koleo-cli 0.2.137.5__tar.gz → 0.2.137.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of koleo-cli might be problematic. Click here for more details.
- koleo_cli-0.2.137.6/PKG-INFO +72 -0
- koleo_cli-0.2.137.6/README.md +54 -0
- {koleo_cli-0.2.137.5 → koleo_cli-0.2.137.6}/koleo/api.py +0 -2
- {koleo_cli-0.2.137.5 → koleo_cli-0.2.137.6}/koleo/cli.py +97 -69
- {koleo_cli-0.2.137.5 → koleo_cli-0.2.137.6}/koleo/storage.py +6 -2
- {koleo_cli-0.2.137.5 → koleo_cli-0.2.137.6}/koleo/types.py +7 -7
- {koleo_cli-0.2.137.5 → koleo_cli-0.2.137.6}/koleo/utils.py +7 -1
- koleo_cli-0.2.137.6/koleo_cli.egg-info/PKG-INFO +72 -0
- {koleo_cli-0.2.137.5 → koleo_cli-0.2.137.6}/setup.py +1 -1
- koleo_cli-0.2.137.5/PKG-INFO +0 -17
- koleo_cli-0.2.137.5/README.md +0 -0
- koleo_cli-0.2.137.5/koleo_cli.egg-info/PKG-INFO +0 -17
- {koleo_cli-0.2.137.5 → koleo_cli-0.2.137.6}/koleo/__init__.py +0 -0
- {koleo_cli-0.2.137.5 → koleo_cli-0.2.137.6}/koleo/__main__.py +0 -0
- {koleo_cli-0.2.137.5 → koleo_cli-0.2.137.6}/koleo_cli.egg-info/SOURCES.txt +0 -0
- {koleo_cli-0.2.137.5 → koleo_cli-0.2.137.6}/koleo_cli.egg-info/dependency_links.txt +0 -0
- {koleo_cli-0.2.137.5 → koleo_cli-0.2.137.6}/koleo_cli.egg-info/entry_points.txt +0 -0
- {koleo_cli-0.2.137.5 → koleo_cli-0.2.137.6}/koleo_cli.egg-info/requires.txt +0 -0
- {koleo_cli-0.2.137.5 → koleo_cli-0.2.137.6}/koleo_cli.egg-info/top_level.txt +0 -0
- {koleo_cli-0.2.137.5 → koleo_cli-0.2.137.6}/pyproject.toml +0 -0
- {koleo_cli-0.2.137.5 → koleo_cli-0.2.137.6}/setup.cfg +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: koleo-cli
|
|
3
|
+
Version: 0.2.137.6
|
|
4
|
+
Summary: Koleo CLI
|
|
5
|
+
Home-page: https://github.com/lzgirlcat/koleo-cli
|
|
6
|
+
Author: Zoey !
|
|
7
|
+
Maintainer-email: cb98uzhd@duck.com
|
|
8
|
+
License: GNU General Public License v3.0
|
|
9
|
+
Project-URL: Source (GitHub), https://github.com/lzgirlcat/koleo-cli
|
|
10
|
+
Project-URL: Issue Tracker, https://github.com/lzgirlcat/koleo-cli/issues
|
|
11
|
+
Keywords: koleo,timetable,trains,rail,poland
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: >=3.12
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: rich==13.7.1
|
|
17
|
+
Requires-Dist: requests==2.31.0
|
|
18
|
+
|
|
19
|
+
[](https://pypi.org/project/koleo-cli)
|
|
20
|
+
[](https://pypi.org/project/koleo-cli)
|
|
21
|
+
|
|
22
|
+
# Koleo CLI
|
|
23
|
+
|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
install via pip by running
|
|
28
|
+
|
|
29
|
+
`pip install koleo-cli`
|
|
30
|
+
|
|
31
|
+
## it currently allows you to:
|
|
32
|
+
- get departures/arrival list for a station
|
|
33
|
+
- get train info given its number and name(pull requests are welcome if you know how to get a train object by just the number)
|
|
34
|
+
- find a station or list all known stations
|
|
35
|
+
- find a connection from station a to b, with filtering by operators
|
|
36
|
+
- save a station as your favourite to quickly check it's departures
|
|
37
|
+
|
|
38
|
+
additionally you can also use the KoleoAPI wrapper directly in your own projects, all returns are fully typed using `typing.TypedDict`
|
|
39
|
+
|
|
40
|
+
## MY(possibly controversial) design choices:
|
|
41
|
+
- platforms and track numbers are shown using arabic numerals instead of roman
|
|
42
|
+
- you can change it by adding `use_roman_numerals: true` to your config.json file
|
|
43
|
+
- most api queries are cached for 24h
|
|
44
|
+
- you can change it by adding `disable_cache: true` to your config.json file
|
|
45
|
+
- the cli.py code is really dirty but printing formatted data is hard :<
|
|
46
|
+
|
|
47
|
+
pull requests are welcome!!
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
usage: koleo [-h] [-c CONFIG] [--nocolor] {departures,d,dep,odjazdy,o,arrivals,a,arr,przyjazdy,p,trainroute,r,tr,t,poc,pociąg,stations,s,find,f,stacje,ls,connections,do,z,szukaj,path} ...
|
|
51
|
+
|
|
52
|
+
Koleo CLI
|
|
53
|
+
|
|
54
|
+
options:
|
|
55
|
+
-h, --help show this help message and exit
|
|
56
|
+
-c CONFIG, --config CONFIG
|
|
57
|
+
Custom config path.
|
|
58
|
+
--nocolor Disable color output and formatting
|
|
59
|
+
|
|
60
|
+
actions:
|
|
61
|
+
{departures,d,dep,odjazdy,o,arrivals,a,arr,przyjazdy,p,trainroute,r,tr,t,poc,pociąg,stations,s,find,f,stacje,ls,connections,do,z,szukaj,path}
|
|
62
|
+
departures (d, dep, odjazdy, o)
|
|
63
|
+
Allows you to list station departures
|
|
64
|
+
arrivals (a, arr, przyjazdy, p)
|
|
65
|
+
Allows you to list station departures
|
|
66
|
+
trainroute (r, tr, t, poc, pociąg)
|
|
67
|
+
Allows you to show the train's route
|
|
68
|
+
stations (s, find, f, stacje, ls)
|
|
69
|
+
Allows you to find stations by their name
|
|
70
|
+
connections (do, z, szukaj, path)
|
|
71
|
+
Allows you to search for connections from a to b
|
|
72
|
+
```
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
[](https://pypi.org/project/koleo-cli)
|
|
2
|
+
[](https://pypi.org/project/koleo-cli)
|
|
3
|
+
|
|
4
|
+
# Koleo CLI
|
|
5
|
+
|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
install via pip by running
|
|
10
|
+
|
|
11
|
+
`pip install koleo-cli`
|
|
12
|
+
|
|
13
|
+
## it currently allows you to:
|
|
14
|
+
- get departures/arrival list for a station
|
|
15
|
+
- get train info given its number and name(pull requests are welcome if you know how to get a train object by just the number)
|
|
16
|
+
- find a station or list all known stations
|
|
17
|
+
- find a connection from station a to b, with filtering by operators
|
|
18
|
+
- save a station as your favourite to quickly check it's departures
|
|
19
|
+
|
|
20
|
+
additionally you can also use the KoleoAPI wrapper directly in your own projects, all returns are fully typed using `typing.TypedDict`
|
|
21
|
+
|
|
22
|
+
## MY(possibly controversial) design choices:
|
|
23
|
+
- platforms and track numbers are shown using arabic numerals instead of roman
|
|
24
|
+
- you can change it by adding `use_roman_numerals: true` to your config.json file
|
|
25
|
+
- most api queries are cached for 24h
|
|
26
|
+
- you can change it by adding `disable_cache: true` to your config.json file
|
|
27
|
+
- the cli.py code is really dirty but printing formatted data is hard :<
|
|
28
|
+
|
|
29
|
+
pull requests are welcome!!
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
usage: koleo [-h] [-c CONFIG] [--nocolor] {departures,d,dep,odjazdy,o,arrivals,a,arr,przyjazdy,p,trainroute,r,tr,t,poc,pociąg,stations,s,find,f,stacje,ls,connections,do,z,szukaj,path} ...
|
|
33
|
+
|
|
34
|
+
Koleo CLI
|
|
35
|
+
|
|
36
|
+
options:
|
|
37
|
+
-h, --help show this help message and exit
|
|
38
|
+
-c CONFIG, --config CONFIG
|
|
39
|
+
Custom config path.
|
|
40
|
+
--nocolor Disable color output and formatting
|
|
41
|
+
|
|
42
|
+
actions:
|
|
43
|
+
{departures,d,dep,odjazdy,o,arrivals,a,arr,przyjazdy,p,trainroute,r,tr,t,poc,pociąg,stations,s,find,f,stacje,ls,connections,do,z,szukaj,path}
|
|
44
|
+
departures (d, dep, odjazdy, o)
|
|
45
|
+
Allows you to list station departures
|
|
46
|
+
arrivals (a, arr, przyjazdy, p)
|
|
47
|
+
Allows you to list station departures
|
|
48
|
+
trainroute (r, tr, t, poc, pociąg)
|
|
49
|
+
Allows you to show the train's route
|
|
50
|
+
stations (s, find, f, stacje, ls)
|
|
51
|
+
Allows you to find stations by their name
|
|
52
|
+
connections (do, z, szukaj, path)
|
|
53
|
+
Allows you to search for connections from a to b
|
|
54
|
+
```
|
|
@@ -7,11 +7,11 @@ from rich.traceback import install
|
|
|
7
7
|
|
|
8
8
|
from .api import KoleoAPI
|
|
9
9
|
from .storage import DEFAULT_CONFIG_PATH, Storage
|
|
10
|
-
from .types import TrainDetailResponse, TrainOnStationInfo
|
|
11
|
-
from .utils import convert_platform_number, name_to_slug, parse_datetime
|
|
10
|
+
from .types import ExtendedBaseStationInfo, TrainDetailResponse, TrainOnStationInfo
|
|
11
|
+
from .utils import RemainderString, arr_dep_to_dt, convert_platform_number, name_to_slug, parse_datetime
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
install(show_locals=
|
|
14
|
+
install(show_locals=True, max_frames=2)
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class CLI:
|
|
@@ -28,7 +28,7 @@ class CLI:
|
|
|
28
28
|
|
|
29
29
|
def print(self, text, *args, **kwargs):
|
|
30
30
|
if self.no_color:
|
|
31
|
-
result = re.sub(r
|
|
31
|
+
result = re.sub(r"\[[^\]]*\]", "", text)
|
|
32
32
|
print(result)
|
|
33
33
|
else:
|
|
34
34
|
self.console.print(text, *args, **kwargs)
|
|
@@ -55,9 +55,8 @@ class CLI:
|
|
|
55
55
|
|
|
56
56
|
def get_departures(self, station_id: int, date: datetime):
|
|
57
57
|
cache_id = f"dep-{station_id}-{date.strftime("%Y-%m-%d")}"
|
|
58
|
-
trains = (
|
|
59
|
-
self.
|
|
60
|
-
self.storage.set_cache(cache_id, self.client.get_departures(station_id, date))
|
|
58
|
+
trains = self.storage.get_cache(cache_id) or self.storage.set_cache(
|
|
59
|
+
cache_id, self.client.get_departures(station_id, date)
|
|
61
60
|
)
|
|
62
61
|
trains = [
|
|
63
62
|
i
|
|
@@ -70,9 +69,8 @@ class CLI:
|
|
|
70
69
|
|
|
71
70
|
def get_arrivals(self, station_id: int, date: datetime):
|
|
72
71
|
cache_id = f"arr-{station_id}-{date.strftime("%Y-%m-%d")}"
|
|
73
|
-
trains = (
|
|
74
|
-
self.
|
|
75
|
-
self.storage.set_cache(cache_id, self.client.get_arrivals(station_id, date))
|
|
72
|
+
trains = self.storage.get_cache(cache_id) or self.storage.set_cache(
|
|
73
|
+
cache_id, self.client.get_arrivals(station_id, date)
|
|
76
74
|
)
|
|
77
75
|
trains = [
|
|
78
76
|
i
|
|
@@ -99,21 +97,23 @@ class CLI:
|
|
|
99
97
|
if query:
|
|
100
98
|
stations = self.client.find_station(query)
|
|
101
99
|
else:
|
|
102
|
-
stations = (
|
|
103
|
-
self.
|
|
104
|
-
self.storage.set_cache("stations", self.client.get_stations())
|
|
100
|
+
stations = self.storage.get_cache("stations") or self.storage.set_cache(
|
|
101
|
+
"stations", self.client.get_stations()
|
|
105
102
|
)
|
|
106
103
|
for st in stations:
|
|
107
|
-
self.print(
|
|
104
|
+
self.print(
|
|
105
|
+
f"[bold blue][link=https://koleo.pl/dworzec-pkp/{st["name_slug"]}]{st["name"]}[/bold blue] ID: {st["id"]}[/link]"
|
|
106
|
+
)
|
|
108
107
|
|
|
109
|
-
def train_info(self, brand: str, name:
|
|
108
|
+
def train_info(self, brand: str, name: str, date: datetime):
|
|
110
109
|
brand = brand.upper().strip()
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
name_parts = name.split(" ")
|
|
111
|
+
if len(name_parts) == 1 and name_parts[0].isnumeric():
|
|
112
|
+
number = int(name_parts[0])
|
|
113
113
|
train_name = ""
|
|
114
114
|
elif len(name) > 1:
|
|
115
|
-
number = int(
|
|
116
|
-
train_name = " ".join(
|
|
115
|
+
number = int(name_parts.pop(0))
|
|
116
|
+
train_name = " ".join(name_parts)
|
|
117
117
|
else:
|
|
118
118
|
raise ValueError("Invalid train name!")
|
|
119
119
|
brands = self.storage.get_cache("brands") or self.storage.set_cache("brands", self.client.get_brands())
|
|
@@ -136,11 +136,15 @@ class CLI:
|
|
|
136
136
|
parts = [f"[red]{brand}[/red] [bold blue]{train_details["train"]["train_full_name"]}[/bold blue]"]
|
|
137
137
|
route_start = arr_dep_to_dt(train_details["stops"][0]["departure"])
|
|
138
138
|
route_end = arr_dep_to_dt(train_details["stops"][-1]["arrival"])
|
|
139
|
-
if route_end.hour < route_start.hour or (
|
|
139
|
+
if route_end.hour < route_start.hour or (
|
|
140
|
+
route_end.hour == route_start.hour and route_end.minute < route_end.minute
|
|
141
|
+
):
|
|
140
142
|
route_end += timedelta(days=1)
|
|
141
143
|
travel_time = route_end - route_start
|
|
142
144
|
speed = train_details["stops"][-1]["distance"] / 1000 / travel_time.seconds * 3600
|
|
143
|
-
parts.append(
|
|
145
|
+
parts.append(
|
|
146
|
+
f"[white] {travel_time.seconds//3600}h{(travel_time.seconds % 3600)/60:.0f}m {speed:^4.1f}km/h [/white]"
|
|
147
|
+
)
|
|
144
148
|
vehicle_types: dict[str, str] = {
|
|
145
149
|
stop["station_display_name"]: stop["vehicle_type"]
|
|
146
150
|
for stop in train_details["stops"]
|
|
@@ -165,24 +169,27 @@ class CLI:
|
|
|
165
169
|
if not brands:
|
|
166
170
|
connection_brands = [i["id"] for i in api_brands]
|
|
167
171
|
else:
|
|
168
|
-
connection_brands = [
|
|
172
|
+
connection_brands = [
|
|
173
|
+
i["id"]
|
|
174
|
+
for i in api_brands
|
|
175
|
+
if i["name"].lower().strip() in brands or i["logo_text"].lower().strip() in brands
|
|
176
|
+
]
|
|
169
177
|
if not connection_brands:
|
|
170
178
|
self.print(f'[bold red]No brands match: "{', '.join(brands)}"[/bold red]')
|
|
171
179
|
exit(2)
|
|
172
180
|
connections = self.client.get_connections(
|
|
173
|
-
start_station["name_slug"],
|
|
174
|
-
end_station["name_slug"],
|
|
175
|
-
connection_brands,
|
|
176
|
-
date,
|
|
177
|
-
direct,
|
|
178
|
-
purchasable
|
|
181
|
+
start_station["name_slug"], end_station["name_slug"], connection_brands, date, direct, purchasable
|
|
179
182
|
)
|
|
180
|
-
parts = [
|
|
183
|
+
parts = [
|
|
184
|
+
f"[bold blue]{start_station["name"]} → {end_station["name"]} at {date.strftime("%H:%M %d-%m")}[/bold blue]"
|
|
185
|
+
]
|
|
181
186
|
for i in connections:
|
|
182
187
|
arr = arr_dep_to_dt(i["arrival"])
|
|
183
188
|
dep = arr_dep_to_dt(i["departure"])
|
|
184
189
|
travel_time = (arr - dep).seconds
|
|
185
|
-
parts.append(
|
|
190
|
+
parts.append(
|
|
191
|
+
f"[bold green][link=https://koleo.pl/travel-options/{i["id"]}]{dep.strftime("%H:%M")} - {arr.strftime("%H:%M")}[/bold green] {travel_time//3600}h{(travel_time % 3600)/60:.0f}m {i['distance']}km: [/link]"
|
|
192
|
+
)
|
|
186
193
|
if i["constriction_info"]:
|
|
187
194
|
for constriction in i["constriction_info"]:
|
|
188
195
|
parts.append(f" [bold red]- {constriction} [/bold red]")
|
|
@@ -190,14 +197,17 @@ class CLI:
|
|
|
190
197
|
train = i["trains"][0]
|
|
191
198
|
stop = next(iter(i for i in train["stops"] if i["station_id"] == train["start_station_id"]), {})
|
|
192
199
|
stop_station = (
|
|
193
|
-
start_station
|
|
194
|
-
|
|
195
|
-
|
|
200
|
+
start_station
|
|
201
|
+
if stop["station_id"] == start_station["id"]
|
|
202
|
+
else self.storage.get_cache(f"st-{stop['station_id']}")
|
|
203
|
+
or self.storage.set_cache(
|
|
204
|
+
f"st-{stop['station_id']}", self.client.get_station_by_id(stop["station_id"])
|
|
205
|
+
)
|
|
196
206
|
)
|
|
197
|
-
platform = convert_platform_number(stop["platform"]) if stop["platform"] else ""
|
|
198
|
-
position_info = f"{platform}/{stop["track"]}" if stop["track"] else platform
|
|
199
207
|
brand = next(iter(i for i in api_brands if i["id"] == train["brand_id"]), {}).get("logo_text")
|
|
200
|
-
parts[-1]+=
|
|
208
|
+
parts[-1] += (
|
|
209
|
+
f" [red]{brand}[/red] {train["train_full_name"]}[purple] {stop_station['name']} {self.format_position(stop["platform"], stop["track"])}[/purple]"
|
|
210
|
+
)
|
|
201
211
|
else:
|
|
202
212
|
previous_arrival: datetime | None = None
|
|
203
213
|
for train in i["trains"]:
|
|
@@ -205,29 +215,35 @@ class CLI:
|
|
|
205
215
|
|
|
206
216
|
fs = next(iter(i for i in train["stops"] if i["station_id"] == train["start_station_id"]), {})
|
|
207
217
|
fs_station = (
|
|
208
|
-
start_station
|
|
209
|
-
|
|
210
|
-
|
|
218
|
+
start_station
|
|
219
|
+
if fs["station_id"] == start_station["id"]
|
|
220
|
+
else self.storage.get_cache(f"st-{fs['station_id']}")
|
|
221
|
+
or self.storage.set_cache(
|
|
222
|
+
f"st-{fs['station_id']}", self.client.get_station_by_id(fs["station_id"])
|
|
223
|
+
)
|
|
211
224
|
)
|
|
212
|
-
|
|
213
|
-
fs_arr = arr_dep_to_dt(fs["arrival"])
|
|
225
|
+
# fs_arr = arr_dep_to_dt(fs["arrival"])
|
|
214
226
|
fs_dep = arr_dep_to_dt(fs["departure"])
|
|
215
|
-
fs_info = f"[bold green]{fs_dep.strftime("%H:%M")} [/bold green][purple]{fs_station['name']} {
|
|
227
|
+
fs_info = f"[bold green]{fs_dep.strftime("%H:%M")} [/bold green][purple]{fs_station['name']} {self.format_position(fs["platform"], fs["track"])}[/purple]"
|
|
216
228
|
|
|
217
229
|
ls = next(iter(i for i in train["stops"] if i["station_id"] == train["end_station_id"]), {})
|
|
218
230
|
ls_station = (
|
|
219
|
-
start_station
|
|
220
|
-
|
|
221
|
-
|
|
231
|
+
start_station
|
|
232
|
+
if ls["station_id"] == start_station["id"]
|
|
233
|
+
else self.storage.get_cache(f"st-{ls['station_id']}")
|
|
234
|
+
or self.storage.set_cache(
|
|
235
|
+
f"st-{ls['station_id']}", self.client.get_station_by_id(ls["station_id"])
|
|
236
|
+
)
|
|
222
237
|
)
|
|
223
|
-
ls_platform = convert_platform_number(ls["platform"]) if ls["platform"] else ""
|
|
224
238
|
ls_arr = arr_dep_to_dt(ls["arrival"])
|
|
225
|
-
ls_dep = arr_dep_to_dt(ls["departure"])
|
|
226
|
-
ls_info = f"[bold green]{ls_arr.strftime("%H:%M")} [/bold green][purple]{ls_station['name']} {
|
|
239
|
+
# ls_dep = arr_dep_to_dt(ls["departure"])
|
|
240
|
+
ls_info = f"[bold green]{ls_arr.strftime("%H:%M")} [/bold green][purple]{ls_station['name']} {self.format_position(ls["platform"], ls["track"])}[/purple]"
|
|
227
241
|
connection_time = (fs_dep - previous_arrival).seconds if previous_arrival else ""
|
|
228
242
|
previous_arrival = ls_arr
|
|
229
243
|
if connection_time:
|
|
230
|
-
parts.append(
|
|
244
|
+
parts.append(
|
|
245
|
+
f" {connection_time//3600}h{(connection_time % 3600)/60:.0f}m at [purple]{fs_station['name']}[/purple]"
|
|
246
|
+
)
|
|
231
247
|
parts.append(f" [red]{brand}[/red] {train["train_full_name"]} {fs_info} - {ls_info}")
|
|
232
248
|
self.print("\n".join(parts))
|
|
233
249
|
|
|
@@ -238,10 +254,8 @@ class CLI:
|
|
|
238
254
|
time = train["departure"] if type == 1 else train["arrival"]
|
|
239
255
|
assert time
|
|
240
256
|
brand = next(iter(i for i in brands if i["id"] == train["brand_id"]), {}).get("logo_text")
|
|
241
|
-
platform = convert_platform_number(train["platform"]) if train["platform"] else ""
|
|
242
|
-
position_info = f"{platform}/{train["track"]}" if train["track"] else platform
|
|
243
257
|
parts.append(
|
|
244
|
-
f"[bold green]{time[11:16]}[/bold green] [red]{brand}[/red] {train["train_full_name"]}[purple] {train["stations"][0]["name"]} {
|
|
258
|
+
f"[bold green]{time[11:16]}[/bold green] [red]{brand}[/red] {train["train_full_name"]}[purple] {train["stations"][0]["name"]} {self.format_position(train["platform"], train["track"])}[/purple]"
|
|
245
259
|
)
|
|
246
260
|
return "\n".join(parts)
|
|
247
261
|
|
|
@@ -250,12 +264,17 @@ class CLI:
|
|
|
250
264
|
for stop in train["stops"]:
|
|
251
265
|
arr = arr_dep_to_dt(stop["arrival"])
|
|
252
266
|
dep = arr_dep_to_dt(stop["departure"])
|
|
253
|
-
platform = convert_platform_number(stop["platform"]) or ""
|
|
254
267
|
parts.append(
|
|
255
|
-
f"[white underline]{stop["distance"] / 1000:^5.1f}km[/white underline] [bold green]{arr.strftime("%H:%M")}[/bold green] - [bold red]{dep.strftime("%H:%M")}[/bold red] [purple]{stop["station_display_name"]} {platform} [/purple]"
|
|
268
|
+
f"[white underline]{stop["distance"] / 1000:^5.1f}km[/white underline] [bold green]{arr.strftime("%H:%M")}[/bold green] - [bold red]{dep.strftime("%H:%M")}[/bold red] [purple]{stop["station_display_name"]} {self.format_position(stop["platform"])} [/purple]"
|
|
256
269
|
)
|
|
257
270
|
return "\n".join(parts)
|
|
258
271
|
|
|
272
|
+
def format_position(self, platform: str, track: str | None = None):
|
|
273
|
+
res = str(convert_platform_number(platform)) or "" if not self.storage.use_roman_numerals else platform
|
|
274
|
+
if track is not None and track != "":
|
|
275
|
+
res += f"/{track}"
|
|
276
|
+
return res
|
|
277
|
+
|
|
259
278
|
def get_station(self, station: str) -> ExtendedBaseStationInfo:
|
|
260
279
|
slug = name_to_slug(station)
|
|
261
280
|
try:
|
|
@@ -266,20 +285,24 @@ class CLI:
|
|
|
266
285
|
self.print(f'[bold red]Station not found: "{station}"[/bold red]')
|
|
267
286
|
exit(2)
|
|
268
287
|
|
|
288
|
+
|
|
269
289
|
def main():
|
|
270
290
|
cli = CLI()
|
|
271
291
|
|
|
272
292
|
parser = ArgumentParser("koleo", description="Koleo CLI")
|
|
273
293
|
parser.add_argument("-c", "--config", help="Custom config path.", default=DEFAULT_CONFIG_PATH)
|
|
274
|
-
parser.add_argument("--nocolor", help="Disable color output", action="store_true", default=False)
|
|
275
|
-
subparsers = parser.add_subparsers(title="actions", required=False)
|
|
294
|
+
parser.add_argument("--nocolor", help="Disable color output and formatting", action="store_true", default=False)
|
|
295
|
+
subparsers = parser.add_subparsers(title="actions", required=False) # type: ignore
|
|
276
296
|
|
|
277
|
-
departures = subparsers.add_parser(
|
|
297
|
+
departures = subparsers.add_parser(
|
|
298
|
+
"departures", aliases=["d", "dep", "odjazdy", "o"], help="Allows you to list station departures"
|
|
299
|
+
)
|
|
278
300
|
departures.add_argument(
|
|
279
301
|
"station",
|
|
280
302
|
help="The station name",
|
|
281
303
|
default=None,
|
|
282
|
-
nargs="
|
|
304
|
+
nargs="*",
|
|
305
|
+
action=RemainderString,
|
|
283
306
|
)
|
|
284
307
|
departures.add_argument(
|
|
285
308
|
"-d",
|
|
@@ -291,12 +314,15 @@ def main():
|
|
|
291
314
|
departures.add_argument("-s", "--save", help="save the station as your default one", action="store_true")
|
|
292
315
|
departures.set_defaults(func=cli.full_departures, pass_=["station", "date"])
|
|
293
316
|
|
|
294
|
-
arrivals = subparsers.add_parser(
|
|
317
|
+
arrivals = subparsers.add_parser(
|
|
318
|
+
"arrivals", aliases=["a", "arr", "przyjazdy", "p"], help="Allows you to list station departures"
|
|
319
|
+
)
|
|
295
320
|
arrivals.add_argument(
|
|
296
321
|
"station",
|
|
297
322
|
help="The station name",
|
|
298
323
|
default=None,
|
|
299
|
-
nargs="
|
|
324
|
+
nargs="*",
|
|
325
|
+
action=RemainderString,
|
|
300
326
|
)
|
|
301
327
|
arrivals.add_argument(
|
|
302
328
|
"-d",
|
|
@@ -314,7 +340,7 @@ def main():
|
|
|
314
340
|
help="Allows you to show the train's route",
|
|
315
341
|
)
|
|
316
342
|
train_route.add_argument("brand", help="The brand name", type=str)
|
|
317
|
-
train_route.add_argument("name", help="The train name",
|
|
343
|
+
train_route.add_argument("name", help="The train name", nargs="+", action=RemainderString)
|
|
318
344
|
train_route.add_argument(
|
|
319
345
|
"-d",
|
|
320
346
|
"--date",
|
|
@@ -324,12 +350,15 @@ def main():
|
|
|
324
350
|
)
|
|
325
351
|
train_route.set_defaults(func=cli.train_info, pass_=["brand", "name", "date"])
|
|
326
352
|
|
|
327
|
-
stations = subparsers.add_parser(
|
|
353
|
+
stations = subparsers.add_parser(
|
|
354
|
+
"stations", aliases=["s", "find", "f", "stacje", "ls", "q"], help="Allows you to find stations by their name"
|
|
355
|
+
)
|
|
328
356
|
stations.add_argument(
|
|
329
357
|
"query",
|
|
330
358
|
help="The station name",
|
|
331
359
|
default=None,
|
|
332
|
-
nargs="
|
|
360
|
+
nargs="*",
|
|
361
|
+
action=RemainderString,
|
|
333
362
|
)
|
|
334
363
|
stations.set_defaults(func=cli.find_station, pass_=["query"])
|
|
335
364
|
|
|
@@ -348,22 +377,21 @@ def main():
|
|
|
348
377
|
default=datetime.now(),
|
|
349
378
|
)
|
|
350
379
|
connections.add_argument(
|
|
351
|
-
"-b",
|
|
352
|
-
"--brands",
|
|
353
|
-
help="Brands to include",
|
|
354
|
-
action="extend", nargs="+", type=str, default=[]
|
|
380
|
+
"-b", "--brands", help="Brands to include", action="extend", nargs="+", type=str, default=[]
|
|
355
381
|
)
|
|
356
382
|
connections.add_argument(
|
|
357
383
|
"-f",
|
|
358
384
|
"--direct",
|
|
359
385
|
help="whether or not the result should only include direct trains",
|
|
360
|
-
action="store_true",
|
|
386
|
+
action="store_true",
|
|
387
|
+
default=False,
|
|
361
388
|
)
|
|
362
389
|
connections.add_argument(
|
|
363
390
|
"-p",
|
|
364
391
|
"--purchasable",
|
|
365
392
|
help="whether or not the result should only trains purchasable on koleo",
|
|
366
|
-
action="store_true",
|
|
393
|
+
action="store_true",
|
|
394
|
+
default=False,
|
|
367
395
|
)
|
|
368
396
|
connections.set_defaults(func=cli.connections, pass_=["start", "end", "brands", "date", "direct", "purchasable"])
|
|
369
397
|
|
|
@@ -27,9 +27,9 @@ T = t.TypeVar("T")
|
|
|
27
27
|
@dataclass
|
|
28
28
|
class Storage:
|
|
29
29
|
favourite_station: str | None = None
|
|
30
|
-
last_searched_connections: list[tuple[str, str]] = field(default_factory=list)
|
|
31
|
-
last_searched_stations: list[str] = field(default_factory=list)
|
|
32
30
|
cache: dict[str, tuple[int, t.Any]] = field(default_factory=dict)
|
|
31
|
+
disable_cache: bool = False
|
|
32
|
+
use_roman_numerals: bool = False
|
|
33
33
|
|
|
34
34
|
def __post_init__(self):
|
|
35
35
|
self._path: str
|
|
@@ -47,6 +47,8 @@ class Storage:
|
|
|
47
47
|
return storage
|
|
48
48
|
|
|
49
49
|
def get_cache(self, id: str) -> t.Any | None:
|
|
50
|
+
if self.disable_cache:
|
|
51
|
+
return
|
|
50
52
|
cache_result = self.cache.get(id)
|
|
51
53
|
if not cache_result:
|
|
52
54
|
return
|
|
@@ -58,6 +60,8 @@ class Storage:
|
|
|
58
60
|
self.save()
|
|
59
61
|
|
|
60
62
|
def set_cache(self, id: str, item: T, ttl: int = 86400) -> T:
|
|
63
|
+
if self.disable_cache:
|
|
64
|
+
return item
|
|
61
65
|
self.cache[id] = (int(time() + ttl), item)
|
|
62
66
|
self.save()
|
|
63
67
|
return item
|
|
@@ -179,8 +179,8 @@ class TrainDetailResponse(t.TypedDict):
|
|
|
179
179
|
|
|
180
180
|
|
|
181
181
|
class ConnectionTrainStop(t.TypedDict):
|
|
182
|
-
arrival: TimeDict | str
|
|
183
|
-
departure: TimeDict | str
|
|
182
|
+
arrival: TimeDict | str # WTF KOLEO!!!!
|
|
183
|
+
departure: TimeDict | str # WTF KOLEO!!!!
|
|
184
184
|
distance: int
|
|
185
185
|
in_path: bool
|
|
186
186
|
station_id: int
|
|
@@ -196,8 +196,8 @@ class ConnectionTrainStop(t.TypedDict):
|
|
|
196
196
|
|
|
197
197
|
|
|
198
198
|
class ConnectiontrainDetail(TrainDetail):
|
|
199
|
-
arrival: TimeDict | str
|
|
200
|
-
departure: TimeDict | str
|
|
199
|
+
arrival: TimeDict | str # WTF KOLEO!!!!
|
|
200
|
+
departure: TimeDict | str # WTF KOLEO!!!!
|
|
201
201
|
stops: list[ConnectionTrainStop]
|
|
202
202
|
bookable: bool
|
|
203
203
|
train_attribute_ids: list[int]
|
|
@@ -225,8 +225,8 @@ class ConnectionDetail(t.TypedDict):
|
|
|
225
225
|
brand_ids: list[int]
|
|
226
226
|
start_station_id: int
|
|
227
227
|
end_station_id: int
|
|
228
|
-
arrival: TimeDict | str
|
|
229
|
-
departure: TimeDict | str
|
|
228
|
+
arrival: TimeDict | str # WTF KOLEO!!!!
|
|
229
|
+
departure: TimeDict | str # WTF KOLEO!!!!
|
|
230
230
|
bookable: bool
|
|
231
231
|
special_event_slug: str | None
|
|
232
232
|
is_advanced_travel_options: bool
|
|
@@ -234,4 +234,4 @@ class ConnectionDetail(t.TypedDict):
|
|
|
234
234
|
max_passengers_count: bool
|
|
235
235
|
constriction_info: list[str]
|
|
236
236
|
is_estimated_timetable_available: bool
|
|
237
|
-
trains: list[ConnectiontrainDetail]
|
|
237
|
+
trains: list[ConnectiontrainDetail]
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from argparse import Action
|
|
1
2
|
from datetime import datetime, time, timedelta
|
|
2
3
|
|
|
3
4
|
from .types import TimeDict
|
|
@@ -64,9 +65,14 @@ NUMERAL_TO_ARABIC = {
|
|
|
64
65
|
"X": 10,
|
|
65
66
|
"XI": 11, # wtf poznań???
|
|
66
67
|
"XII": 12, # just to be safe
|
|
67
|
-
"BUS": "BUS"
|
|
68
|
+
"BUS": "BUS",
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
|
|
71
72
|
def convert_platform_number(number: str) -> int | None | str:
|
|
72
73
|
return NUMERAL_TO_ARABIC.get(number)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class RemainderString(Action):
|
|
77
|
+
def __call__(self, _, namespace, values: list[str], __):
|
|
78
|
+
setattr(namespace, self.dest, " ".join(values) if values else self.default)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: koleo-cli
|
|
3
|
+
Version: 0.2.137.6
|
|
4
|
+
Summary: Koleo CLI
|
|
5
|
+
Home-page: https://github.com/lzgirlcat/koleo-cli
|
|
6
|
+
Author: Zoey !
|
|
7
|
+
Maintainer-email: cb98uzhd@duck.com
|
|
8
|
+
License: GNU General Public License v3.0
|
|
9
|
+
Project-URL: Source (GitHub), https://github.com/lzgirlcat/koleo-cli
|
|
10
|
+
Project-URL: Issue Tracker, https://github.com/lzgirlcat/koleo-cli/issues
|
|
11
|
+
Keywords: koleo,timetable,trains,rail,poland
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: >=3.12
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: rich==13.7.1
|
|
17
|
+
Requires-Dist: requests==2.31.0
|
|
18
|
+
|
|
19
|
+
[](https://pypi.org/project/koleo-cli)
|
|
20
|
+
[](https://pypi.org/project/koleo-cli)
|
|
21
|
+
|
|
22
|
+
# Koleo CLI
|
|
23
|
+
|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
install via pip by running
|
|
28
|
+
|
|
29
|
+
`pip install koleo-cli`
|
|
30
|
+
|
|
31
|
+
## it currently allows you to:
|
|
32
|
+
- get departures/arrival list for a station
|
|
33
|
+
- get train info given its number and name(pull requests are welcome if you know how to get a train object by just the number)
|
|
34
|
+
- find a station or list all known stations
|
|
35
|
+
- find a connection from station a to b, with filtering by operators
|
|
36
|
+
- save a station as your favourite to quickly check it's departures
|
|
37
|
+
|
|
38
|
+
additionally you can also use the KoleoAPI wrapper directly in your own projects, all returns are fully typed using `typing.TypedDict`
|
|
39
|
+
|
|
40
|
+
## MY(possibly controversial) design choices:
|
|
41
|
+
- platforms and track numbers are shown using arabic numerals instead of roman
|
|
42
|
+
- you can change it by adding `use_roman_numerals: true` to your config.json file
|
|
43
|
+
- most api queries are cached for 24h
|
|
44
|
+
- you can change it by adding `disable_cache: true` to your config.json file
|
|
45
|
+
- the cli.py code is really dirty but printing formatted data is hard :<
|
|
46
|
+
|
|
47
|
+
pull requests are welcome!!
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
usage: koleo [-h] [-c CONFIG] [--nocolor] {departures,d,dep,odjazdy,o,arrivals,a,arr,przyjazdy,p,trainroute,r,tr,t,poc,pociąg,stations,s,find,f,stacje,ls,connections,do,z,szukaj,path} ...
|
|
51
|
+
|
|
52
|
+
Koleo CLI
|
|
53
|
+
|
|
54
|
+
options:
|
|
55
|
+
-h, --help show this help message and exit
|
|
56
|
+
-c CONFIG, --config CONFIG
|
|
57
|
+
Custom config path.
|
|
58
|
+
--nocolor Disable color output and formatting
|
|
59
|
+
|
|
60
|
+
actions:
|
|
61
|
+
{departures,d,dep,odjazdy,o,arrivals,a,arr,przyjazdy,p,trainroute,r,tr,t,poc,pociąg,stations,s,find,f,stacje,ls,connections,do,z,szukaj,path}
|
|
62
|
+
departures (d, dep, odjazdy, o)
|
|
63
|
+
Allows you to list station departures
|
|
64
|
+
arrivals (a, arr, przyjazdy, p)
|
|
65
|
+
Allows you to list station departures
|
|
66
|
+
trainroute (r, tr, t, poc, pociąg)
|
|
67
|
+
Allows you to show the train's route
|
|
68
|
+
stations (s, find, f, stacje, ls)
|
|
69
|
+
Allows you to find stations by their name
|
|
70
|
+
connections (do, z, szukaj, path)
|
|
71
|
+
Allows you to search for connections from a to b
|
|
72
|
+
```
|
koleo_cli-0.2.137.5/PKG-INFO
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: koleo-cli
|
|
3
|
-
Version: 0.2.137.5
|
|
4
|
-
Summary: Koleo CLI
|
|
5
|
-
Home-page: https://github.com/lzgirlcat/koleo-cli
|
|
6
|
-
Author: Zoey !
|
|
7
|
-
Maintainer-email: cb98uzhd@duck.com
|
|
8
|
-
License: GNU General Public License v3.0
|
|
9
|
-
Project-URL: Source (GitHub), https://github.com/lzgirlcat/koleo-cli
|
|
10
|
-
Project-URL: Issue Tracker, https://github.com/lzgirlcat/koleo-cli/issues
|
|
11
|
-
Keywords: koleo,timetable,trains,rail,poland
|
|
12
|
-
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Classifier: Operating System :: OS Independent
|
|
14
|
-
Requires-Python: >=3.12
|
|
15
|
-
Description-Content-Type: text/markdown
|
|
16
|
-
Requires-Dist: rich==13.7.1
|
|
17
|
-
Requires-Dist: requests==2.31.0
|
koleo_cli-0.2.137.5/README.md
DELETED
|
File without changes
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: koleo-cli
|
|
3
|
-
Version: 0.2.137.5
|
|
4
|
-
Summary: Koleo CLI
|
|
5
|
-
Home-page: https://github.com/lzgirlcat/koleo-cli
|
|
6
|
-
Author: Zoey !
|
|
7
|
-
Maintainer-email: cb98uzhd@duck.com
|
|
8
|
-
License: GNU General Public License v3.0
|
|
9
|
-
Project-URL: Source (GitHub), https://github.com/lzgirlcat/koleo-cli
|
|
10
|
-
Project-URL: Issue Tracker, https://github.com/lzgirlcat/koleo-cli/issues
|
|
11
|
-
Keywords: koleo,timetable,trains,rail,poland
|
|
12
|
-
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Classifier: Operating System :: OS Independent
|
|
14
|
-
Requires-Python: >=3.12
|
|
15
|
-
Description-Content-Type: text/markdown
|
|
16
|
-
Requires-Dist: rich==13.7.1
|
|
17
|
-
Requires-Dist: requests==2.31.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|