Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/fedebotu/k-trains
Browse files Browse the repository at this point in the history
  • Loading branch information
cecy07 committed Sep 19, 2023
2 parents c5c0ce4 + 197fe17 commit 25c4785
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 51 deletions.
79 changes: 79 additions & 0 deletions README-KR.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<div align="center">

# K-Trains 🇰🇷-🚄



[![Streamlit App](https://static.streamlit.io/badges/streamlit_badge_red.svg)](https://k-trains.streamlit.app)[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)[![python_sup](https://img.shields.io/badge/python-3.7+-blue.svg?)](https://www.python.org/downloads/release/python-370/)

<br>
<center>
<img src="assets/ktrains.png" alt="K-Trains" width="300"/>
</center>
<br>
<br>
</div>

> English version [here](README.md)
_SRT가 예약이 가득 차서 수서역으로 티켓이 없으신가요? 아니면 이미 매진된 저렴한 무궁화 티켓 때문에 고민하고 계신가요? K-Trains가 도와드리겠습니다!_

K-Trains는 [Korail](https://www.letskorail.com/)[SRT](https://etk.srail.kr/)의 API에 연결하여 한국의 기차 정보를 얻고 예약하는 Streamlit 앱입니다.



## 사용 방법

### 웹 UI
Streamlit에서 앱을 실행하려면 [여기](https://k-trains.streamlit.app/)를 클릭하세요. UI는 자기 설명적이므로 로그인하고 알림을 받거나 직접 예약하고 싶은 기차를 선택하기만 하면 됩니다. 가능하지 않다면 앱이 사용 가능할 때 알려주고 당신을 위해 예약합니다!

_티켓에 대한 비용은 Korail 또는 SRT 웹사이트/앱에서 시간 내에 지불해야 합니다._

그럼에도 불구하고 수동으로 새로 고침하고 누군가가 티켓을 먼저 예약하지 않을 것이라고 기대하는 것보다는 나을 것입니다 ;)

### 수동 배포

이 응용 프로그램은 Python에서 웹 GUI를 허용하는 [Streamlit](https://streamlit.io/)를 기반으로 합니다. 로컬에서 애플리케이션을 실행하려면 다음 명령을 실행하세요:

```bash
streamlit run app.py
```

웹 브라우저가 자동으로 열려 애플리케이션과 상호 작용할 수 있습니다. 그렇지 않으면 브라우저를 수동으로 열고 http://localhost:8501로 이동할 수 있습니다.

또한 직접 티켓을 예약하기 위해 스크립트를 실행할 수도 있습니다:

```bash
python reserve.py [OPTIONS]
```

### 비밀 및 이메일 API 연결
앱은 이메일 계정 API(특히 Google)에 연결됩니다. 앱을 스스로 배포하려면 다음과 같은 메시지가 표시될 수 있습니다:

```bash
FileNotFoundError: No secrets files found. Valid paths for a secrets.toml file are: C:\Users\nyancat.streamlit\secrets.toml
```
자체 secrets.toml 파일을 관리하기 위해 이 가이드를 따를 수 있습니다. Gmail을 사용하지 않는 경우 email_notify 함수를 수정해야 합니다. 특히 이 줄에서입니다.

## 고지
개발자는 이 애플리케이션의 오용에 대해 책임지지 않습니다. 이 애플리케이션은 교육 목적으로만 사용됩니다. 자기 책임하에 사용하세요!

## 스크린샷
<div align="center">
<br>
<center>
<img src="https://github.com/fedebotu/k-trains/assets/48984123/55ec2078-1034-4e95-b5e2-d15de8478107" alt="k-train-email"/>
</center>
<br>
</div>


## 감사의 말
이 프로젝트는 다음 라이브러리 덕분에 가능했습니다:

Korail: https://github.com/carpedm20/korail2

SRT: https://github.com/ryanking13/SRT

### 피드백
피드백이 있다면 문제를 제기하거나 pull request를 열어주세요!
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
<br>
</div>

> 한국어 버전 [클릭](README-KR.md)

_Tired of not having any ticket to Suseo station because your SRT is fully booked? How about that cheap Mugunghwa ticket that you can't get because it's sold out? K-Trains is here to help you get those tickets!_

Expand Down Expand Up @@ -51,6 +53,9 @@ FileNotFoundError: No secrets files found. Valid paths for a secrets.toml file a
```
You may follow [this guide](https://docs.streamlit.io/streamlit-community-cloud/deploy-your-app/secrets-management) for managing your own `secrets.toml` file. Moreover, if you do not use Gmail, you should modify the `email_notify` function, particularly at [this line](https://github.com/fedebotu/k-trains/blob/1a1f609600f870f09e3ef8fe4e692cc082fdb3cc/ktrains/notify.py#L49C1-L50C1).

## Disclaimer
Developers are not responsible for any misuse of this application. This application is for educational purposes only. Please use it at your own risk!

## Screenshots

<div align="center">
Expand Down
125 changes: 78 additions & 47 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import sys
from copy import deepcopy
import gettext

_ = gettext.gettext
import pandas as pd
import streamlit as st
Expand All @@ -13,20 +14,22 @@
from ktrains.srt.srt import SRT
from ktrains.utils import Stations, save_to_log, LINKS

language = st.sidebar.selectbox(_('Select your language'), ['English', '한국어', 'Italiano','Español'])
if language == 'English':
language = 'en'
elif language == '한국어':
language = 'kr'
elif language == 'Español':
language = 'es'
elif language == 'Italiano':
language = 'it'

language = st.sidebar.selectbox(
_("Select your language"), ["English", "한국어", "Italiano", "Español"]
)
if language == "English":
language = "en"
elif language == "한국어":
language = "kr"
elif language == "Español":
language = "es"
elif language == "Italiano":
language = "it"

try:
localizator = gettext.translation('base', localedir='locales', languages=[language])
localizator.install()
_ = localizator.gettext
localizator = gettext.translation("base", localedir="locales", languages=[language])
localizator.install()
_ = localizator.gettext
except:
pass

Expand Down Expand Up @@ -71,6 +74,7 @@ def check_login():
return True
return False


st.title("K-trains 🇰🇷-🚄")
st.markdown(_("Fork me on:") + f" [{_('GitHub')}]({LINKS['app']['github']})")

Expand All @@ -79,8 +83,8 @@ def check_login():
# Checkbox with two options and an image on top of each
mode = st.selectbox(_("Select railways company"), [_("Korail"), _("SRT")]).lower()
st.session_state.mode = mode.lower()
st.write(_(
"Get your credentials from {} website: [{}]({})").format(
st.write(
_("Get your credentials from {} website: [{}]({})").format(
LINKS[mode]["name"], LINKS[mode]["link"], LINKS[mode]["link"]
)
)
Expand All @@ -98,10 +102,10 @@ def check_login():
else:
st.error(_("Login failed"))
else:
if language == 'kr':
language_sched = 'kor'
if language == "kr":
language_sched = "kor"
else:
language_sched = 'en'
language_sched = "en"

# What happens when logged in
mode = st.session_state.mode
Expand Down Expand Up @@ -143,26 +147,32 @@ def check_login():
date = date.strftime("%Y%m%d")
time = time.strftime("%H%M%S")

table_stations = Stations(mode,language_sched)
table_stations = Stations(mode, language_sched)
if st.button(_("Search")):
trains = ktrains.search_train(dep, arr, date, time, available_only=False)
if trains == []:
st.error("No trains found")
st.session_state.trains = None
else:
#stations.convert_station_name(trains[0].dep_station_name, lang="en")
# stations.convert_station_name(trains[0].dep_station_name, lang="en")
train_list = []
for train in trains:
if language != 'kr':
if language != "kr":
table_lang = "tc"
else:
table_lang = "kr"
train_list.append(
{
"train_no": train.train_number,
"train_type_name": stations.convert_train_name(train.train_name,lang=table_lang),
"dep_name": stations.convert_station_name(train.dep_station_name,lang=table_lang),
"arr_name": stations.convert_station_name(train.arr_station_name,lang=table_lang),
"train_type_name": stations.convert_train_name(
train.train_name, lang=table_lang
),
"dep_name": stations.convert_station_name(
train.dep_station_name, lang=table_lang
),
"arr_name": stations.convert_station_name(
train.arr_station_name, lang=table_lang
),
"dep_time": train.dep_time,
"arr_time": train.arr_time,
"duration": None,
Expand Down Expand Up @@ -242,7 +252,7 @@ def check_login():
header_checkbox_selection_filtered_only=True,
theme="streamlit",
use_checkbox=True,
width="200%"
width="200%",
)

new_df = pd.DataFrame(grid_return["selected_rows"])
Expand All @@ -253,20 +263,27 @@ def check_login():
# st.write(train_codes)

st.header(_("Runner Settings"))
st.write(_(
"The app will automatically reserve and/or notify you when the train is available."
))
st.write(
_(
"The app will automatically reserve and/or notify you when the train is available."
)
)

col1, col2 = st.columns(2)
with col1:
st.subheader(_("Reserve settings"))
st.markdown(_(
"Reserve the train(s) automatically. You will need to reserve in the app/website within a few minutes.")+ f" ([link]({LINKS[mode]['reserve_link']}))"
)
st.markdown(
_(
"Reserve the train(s) automatically. You will need to reserve in the app/website within a few minutes."
)
+ f" ([link]({LINKS[mode]['reserve_link']}))"
)

st.write(_(
"If you do not process the payment, the reservation will be cancelled automatically."
))
st.write(
_(
"If you do not process the payment, the reservation will be cancelled automatically."
)
)
st.number_input(
_("Number of tickets"),
min_value=1,
Expand All @@ -276,12 +293,20 @@ def check_login():
key="num_tickets",
)

st.write(_("Please select the preferred seat type. If both are selected, the app will try to reserve the first available."))
st.write(
_(
"Please select the preferred seat type. If both are selected, the app will try to reserve the first available."
)
)
sub_col1, sub_col2 = st.columns(2)
with sub_col1:
general_seat = st.checkbox(_("General seat"), key="general_seat", value=True)
general_seat = st.checkbox(
_("General seat"), key="general_seat", value=True
)
with sub_col2:
special_seat = st.checkbox(_("Special seat"), key="special_seat", value=True)
special_seat = st.checkbox(
_("Special seat"), key="special_seat", value=True
)
if general_seat and not special_seat:
seat_type = "R"
elif not general_seat and special_seat:
Expand All @@ -291,13 +316,19 @@ def check_login():
with col2:
st.subheader(_("Email notifications settings"))
st.write(_("Notify you when the train is available."))
st.write(_(
"Receivers are the email addresses that will receive notifications. Use commas to separate multiple addresses."
))
st.write(_(
"Note: sender email is ")+f" {LINKS['app']['email']} ."+_(" Be sure to check your spam folder."
))
email_receivers = st.text_input(_("Receivers"), st.session_state.email_receivers)
st.write(
_(
"Receivers are the email addresses that will receive notifications. Use commas to separate multiple addresses."
)
)
st.write(
_("Note: sender email is ")
+ f" {LINKS['app']['email']} ."
+ _(" Be sure to check your spam folder.")
)
email_receivers = st.text_input(
_("Receivers"), st.session_state.email_receivers
)
st.session_state.email_receivers = email_receivers

col1, col2 = st.columns(2)
Expand Down Expand Up @@ -358,9 +389,9 @@ def check_login():

# If running, show log
if st.session_state.running:
st.write(_(
"Hang tight, I'm running... This may take some time, until we find a train!"
))
st.write(
_("Hang tight, I'm running... This may take some time, until we find a train!")
)
st.markdown("---")
st.write(_("Log:"))
with open("log.txt", "r") as f:
Expand Down
5 changes: 3 additions & 2 deletions ktrains/korail/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,7 @@

station_names = {"kor": station_names_korean, "en": station_names_english}


def convert_station_name(station_name, lang="en"):
if lang == "en":
# get index of station_name in station_names_english
Expand Down Expand Up @@ -571,10 +572,11 @@ def convert_station_name(station_name, lang="en"):
"Nuriro",
"Mugunghwa",
"Commuter",
]
]

train_types = {"kor": train_types_korean, "en": train_types_english}


def convert_train_name(train_type, lang):
if lang == "en":
index = train_types_english.index(train_type)
Expand All @@ -584,4 +586,3 @@ def convert_train_name(train_type, lang):
return train_types_english[index]
else:
return train_type

4 changes: 2 additions & 2 deletions ktrains/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ def convert_station_name(self, station_name, lang=None):
raise ValueError(
f"Invalid mode: {self.mode}. Must be one of korail or srt."
)
def convert_train_name(self,train_name,lang=None):

def convert_train_name(self, train_name, lang=None):
if lang is None:
lang = self.lang
if self.mode == "korail":
Expand Down

0 comments on commit 25c4785

Please sign in to comment.