koleo-cli 0.2.137.3__py3-none-any.whl → 0.2.137.4__py3-none-any.whl
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.
- koleo/api.py +10 -9
- koleo/cli.py +61 -12
- koleo/types.py +53 -0
- koleo/utils.py +8 -1
- {koleo_cli-0.2.137.3.dist-info → koleo_cli-0.2.137.4.dist-info}/METADATA +1 -1
- koleo_cli-0.2.137.4.dist-info/RECORD +12 -0
- koleo_cli-0.2.137.3.dist-info/RECORD +0 -12
- {koleo_cli-0.2.137.3.dist-info → koleo_cli-0.2.137.4.dist-info}/WHEEL +0 -0
- {koleo_cli-0.2.137.3.dist-info → koleo_cli-0.2.137.4.dist-info}/entry_points.txt +0 -0
- {koleo_cli-0.2.137.3.dist-info → koleo_cli-0.2.137.4.dist-info}/top_level.txt +0 -0
koleo/api.py
CHANGED
|
@@ -6,11 +6,6 @@ from requests import PreparedRequest, Response, Session
|
|
|
6
6
|
from koleo.types import *
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
9
|
class errors:
|
|
15
10
|
class KoleoAPIException(Exception):
|
|
16
11
|
status: int
|
|
@@ -88,6 +83,12 @@ class KoleoAPI:
|
|
|
88
83
|
# https://koleo.pl/ls?q=tere&language=pl
|
|
89
84
|
return self._get_json("/ls", params={"q": query, "language": language})["stations"]
|
|
90
85
|
|
|
86
|
+
def get_station_by_id(self, id: int) -> ExtendedBaseStationInfo:
|
|
87
|
+
# https://koleo.pl/api/v2/main/stations/by_id/24000
|
|
88
|
+
return self._get_json(
|
|
89
|
+
f"/api/v2/main/stations/by_id/{id}",
|
|
90
|
+
)
|
|
91
|
+
|
|
91
92
|
def get_station_by_slug(self, slug: str) -> ExtendedBaseStationInfo:
|
|
92
93
|
# https://koleo.pl/api/v2/main/stations/by_slug/inowroclaw
|
|
93
94
|
return self._get_json(
|
|
@@ -132,16 +133,16 @@ class KoleoAPI:
|
|
|
132
133
|
date: datetime,
|
|
133
134
|
direct: bool = False,
|
|
134
135
|
purchasable: bool = False,
|
|
135
|
-
) ->
|
|
136
|
+
) -> list[ConnectionDetail]:
|
|
136
137
|
params = {
|
|
137
138
|
"query[date]": date.strftime("%d-%m-%Y %H:%M:%S"),
|
|
138
139
|
"query[start_station]": start,
|
|
139
140
|
"query[end_station]": end,
|
|
140
|
-
"query[only_purchasable]":
|
|
141
|
-
"query[
|
|
141
|
+
"query[only_purchasable]": str(direct).lower(),
|
|
142
|
+
"query[only_direct]": str(direct).lower(),
|
|
142
143
|
"query[brand_ids][]": brand_ids,
|
|
143
144
|
}
|
|
144
|
-
return self._get_json("/api/v2/main/connections", params=params)
|
|
145
|
+
return self._get_json("/api/v2/main/connections", params=params)["connections"]
|
|
145
146
|
|
|
146
147
|
def get_brands(self) -> list[ApiBrand]:
|
|
147
148
|
# https://koleo.pl/api/v2/main/brands
|
koleo/cli.py
CHANGED
|
@@ -33,7 +33,6 @@ class CLI:
|
|
|
33
33
|
else:
|
|
34
34
|
self.console.print(text, *args, **kwargs)
|
|
35
35
|
|
|
36
|
-
|
|
37
36
|
@property
|
|
38
37
|
def client(self) -> KoleoAPI:
|
|
39
38
|
if not self._client:
|
|
@@ -54,9 +53,6 @@ class CLI:
|
|
|
54
53
|
def storage(self, storage: Storage):
|
|
55
54
|
self._storage = storage
|
|
56
55
|
|
|
57
|
-
def list_stations(self, name: str):
|
|
58
|
-
...
|
|
59
|
-
|
|
60
56
|
def get_departures(self, station_id: int, date: datetime):
|
|
61
57
|
cache_id = f"dep-{station_id}-{date.strftime("%Y-%m-%d")}"
|
|
62
58
|
trains = (
|
|
@@ -89,13 +85,13 @@ class CLI:
|
|
|
89
85
|
|
|
90
86
|
def full_departures(self, station: str, date: datetime):
|
|
91
87
|
st = self.get_station(station)
|
|
92
|
-
station_info = f"[bold blue]{st["name"]}[/bold blue] ID: {st["id"]}"
|
|
88
|
+
station_info = f"[bold blue][link=https://koleo.pl/dworzec-pkp/{st["name_slug"]}/{date.strftime("%Y-%m-%d")}]{st["name"]} at {date.strftime("%d-%m %H:%M")}[/bold blue] ID: {st["id"]}[/link]"
|
|
93
89
|
self.print(station_info)
|
|
94
90
|
self.get_departures(st["id"], date)
|
|
95
91
|
|
|
96
92
|
def full_arrivals(self, station: str, date: datetime):
|
|
97
93
|
st = self.get_station(station)
|
|
98
|
-
station_info = f"[bold blue]{st["name"]}[/bold blue] ID: {st["id"]}"
|
|
94
|
+
station_info = f"[bold blue][link=https://koleo.pl/dworzec-pkp/{st["name_slug"]}/{date.strftime("%Y-%m-%d")}]{st["name"]} at {date.strftime("%d-%m %H:%M")}[/bold blue] ID: {st["id"]}[/link]"
|
|
99
95
|
self.print(station_info)
|
|
100
96
|
self.get_arrivals(st["id"], date)
|
|
101
97
|
|
|
@@ -108,7 +104,7 @@ class CLI:
|
|
|
108
104
|
self.storage.set_cache("stations", self.client.get_stations())
|
|
109
105
|
)
|
|
110
106
|
for st in stations:
|
|
111
|
-
self.print(f"[bold blue]{st["name"]}[/bold blue] ID: {st["id"]}")
|
|
107
|
+
self.print(f"[bold blue][link=https://koleo.pl/dworzec-pkp/{st["name_slug"]}]{st["name"]}[/bold blue] ID: {st["id"]}[/link]")
|
|
112
108
|
|
|
113
109
|
def train_info(self, brand: str, name: str, date: datetime):
|
|
114
110
|
brand = brand.upper().strip()
|
|
@@ -152,13 +148,13 @@ class CLI:
|
|
|
152
148
|
start = keys[0]
|
|
153
149
|
for i in range(1, len(keys)):
|
|
154
150
|
if vehicle_types[keys[i]] != vehicle_types[start]:
|
|
155
|
-
parts.append(f"
|
|
151
|
+
parts.append(f" {start} - {keys[i]}: [bold green]{vehicle_types[start]}[/bold green]")
|
|
156
152
|
start = keys[i]
|
|
157
|
-
parts.append(f"
|
|
153
|
+
parts.append(f" {start} - {keys[-1]}: [bold green]{vehicle_types[start]}[/bold green]")
|
|
158
154
|
self.print("\n".join(parts))
|
|
159
155
|
self.print(self.train_route_table(train_details))
|
|
160
156
|
|
|
161
|
-
def connections(self, start: str, end: str, date: datetime, brands: list[str], direct: bool
|
|
157
|
+
def connections(self, start: str, end: str, date: datetime, brands: list[str], direct: bool, purchasable: bool):
|
|
162
158
|
start_station = self.get_station(start)
|
|
163
159
|
end_station = self.get_station(end)
|
|
164
160
|
brands = [i.lower().strip() for i in brands]
|
|
@@ -178,6 +174,59 @@ class CLI:
|
|
|
178
174
|
direct,
|
|
179
175
|
purchasable
|
|
180
176
|
)
|
|
177
|
+
parts = [f"[bold blue]{start_station["name"]} → {end_station["name"]} at {date.strftime("%H:%M %d-%m")}[/bold blue]"]
|
|
178
|
+
for i in connections:
|
|
179
|
+
arr = arr_dep_to_dt(i["arrival"])
|
|
180
|
+
dep = arr_dep_to_dt(i["departure"])
|
|
181
|
+
travel_time = (arr - dep).seconds
|
|
182
|
+
parts.append(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]")
|
|
183
|
+
if i["constriction_info"]:
|
|
184
|
+
for constriction in i["constriction_info"]:
|
|
185
|
+
parts.append(f" [bold red]- {constriction} [/bold red]")
|
|
186
|
+
if len(i["trains"]) == 1:
|
|
187
|
+
train = i["trains"][0]
|
|
188
|
+
stop = next(iter(i for i in train["stops"] if i["station_id"] == train["start_station_id"]), {})
|
|
189
|
+
stop_station = (
|
|
190
|
+
start_station if stop["station_id"] == start_station["id"] else
|
|
191
|
+
self.storage.get_cache(f"st-{stop['station_id']}")
|
|
192
|
+
or self.storage.set_cache(f"st-{stop['station_id']}", self.client.get_station_by_id(stop['station_id']))
|
|
193
|
+
)
|
|
194
|
+
platform = convert_platform_number(stop["platform"]) if stop["platform"] else ""
|
|
195
|
+
position_info = f"{platform}/{stop["track"]}" if stop["track"] else platform
|
|
196
|
+
brand = next(iter(i for i in api_brands if i["id"] == train["brand_id"]), {}).get("logo_text")
|
|
197
|
+
parts[-1]+=f" [red]{brand}[/red] {train["train_full_name"]}[purple] {stop_station['name']} {position_info}[/purple]"
|
|
198
|
+
else:
|
|
199
|
+
previous_arrival: datetime | None = None
|
|
200
|
+
for train in i["trains"]:
|
|
201
|
+
brand = next(iter(i for i in api_brands if i["id"] == train["brand_id"]), {}).get("logo_text")
|
|
202
|
+
|
|
203
|
+
fs = next(iter(i for i in train["stops"] if i["station_id"] == train["start_station_id"]), {})
|
|
204
|
+
fs_station = (
|
|
205
|
+
start_station if fs["station_id"] == start_station["id"] else
|
|
206
|
+
self.storage.get_cache(f"st-{fs['station_id']}")
|
|
207
|
+
or self.storage.set_cache(f"st-{fs['station_id']}", self.client.get_station_by_id(fs['station_id']))
|
|
208
|
+
)
|
|
209
|
+
fs_platform = convert_platform_number(fs["platform"]) if fs["platform"] else ""
|
|
210
|
+
fs_arr = arr_dep_to_dt(fs["arrival"])
|
|
211
|
+
fs_dep = arr_dep_to_dt(fs["departure"])
|
|
212
|
+
fs_info = f"[bold green]{fs_dep.strftime("%H:%M")} [/bold green][purple]{fs_station['name']} {f"{fs_platform}/{fs["track"]}" if fs["track"] else fs_platform}[/purple]"
|
|
213
|
+
|
|
214
|
+
ls = next(iter(i for i in train["stops"] if i["station_id"] == train["end_station_id"]), {})
|
|
215
|
+
ls_station = (
|
|
216
|
+
start_station if ls["station_id"] == start_station["id"] else
|
|
217
|
+
self.storage.get_cache(f"st-{ls['station_id']}")
|
|
218
|
+
or self.storage.set_cache(f"st-{ls['station_id']}", self.client.get_station_by_id(ls['station_id']))
|
|
219
|
+
)
|
|
220
|
+
ls_platform = convert_platform_number(ls["platform"]) if ls["platform"] else ""
|
|
221
|
+
ls_arr = arr_dep_to_dt(ls["arrival"])
|
|
222
|
+
ls_dep = arr_dep_to_dt(ls["departure"])
|
|
223
|
+
ls_info = f"[bold green]{ls_arr.strftime("%H:%M")} [/bold green][purple]{ls_station['name']} {f"{ls_platform}/{ls["track"]}" if ls["track"] else ls_platform}[/purple]"
|
|
224
|
+
connection_time = (fs_dep - previous_arrival).seconds if previous_arrival else ""
|
|
225
|
+
previous_arrival = ls_arr
|
|
226
|
+
if connection_time:
|
|
227
|
+
parts.append(f" {connection_time//3600}h{(connection_time % 3600)/60:.0f}m at [purple]{fs_station['name']}[/purple]")
|
|
228
|
+
parts.append(f" [red]{brand}[/red] {train["train_full_name"]} {fs_info} - {ls_info}")
|
|
229
|
+
self.print("\n".join(parts))
|
|
181
230
|
|
|
182
231
|
def trains_on_station_table(self, trains: list[TrainOnStationInfo], type: int = 1):
|
|
183
232
|
parts = []
|
|
@@ -272,7 +321,7 @@ def main():
|
|
|
272
321
|
)
|
|
273
322
|
train_route.set_defaults(func=cli.train_info, pass_=["brand", "name", "date"])
|
|
274
323
|
|
|
275
|
-
stations = subparsers.add_parser("stations", aliases=["s", "find", "f", "stacje"], help="Allows you to find stations by their name")
|
|
324
|
+
stations = subparsers.add_parser("stations", aliases=["s", "find", "f", "stacje", "ls"], help="Allows you to find stations by their name")
|
|
276
325
|
stations.add_argument(
|
|
277
326
|
"query",
|
|
278
327
|
help="The station name",
|
|
@@ -313,7 +362,7 @@ def main():
|
|
|
313
362
|
help="whether or not the result should only trains purchasable on koleo",
|
|
314
363
|
action="store_true", default=False
|
|
315
364
|
)
|
|
316
|
-
connections.set_defaults(func=cli.connections, pass_=["start", "end", "brands", "date", "direct" "purchasable"])
|
|
365
|
+
connections.set_defaults(func=cli.connections, pass_=["start", "end", "brands", "date", "direct", "purchasable"])
|
|
317
366
|
|
|
318
367
|
args = parser.parse_args()
|
|
319
368
|
|
koleo/types.py
CHANGED
|
@@ -178,7 +178,60 @@ class TrainDetailResponse(t.TypedDict):
|
|
|
178
178
|
stops: list[TrainStop]
|
|
179
179
|
|
|
180
180
|
|
|
181
|
+
class ConnectionTrainStop(t.TypedDict):
|
|
182
|
+
arrival: TimeDict | str # WTF KOLEO!!!!
|
|
183
|
+
departure: TimeDict | str # WTF KOLEO!!!!
|
|
184
|
+
distance: int
|
|
185
|
+
in_path: bool
|
|
186
|
+
station_id: int
|
|
187
|
+
next_day: bool
|
|
188
|
+
position: int
|
|
189
|
+
train_nr: int
|
|
190
|
+
brand_id: int
|
|
191
|
+
entry_only: bool
|
|
192
|
+
exit_only: bool
|
|
193
|
+
platform: str
|
|
194
|
+
track: str
|
|
195
|
+
on_demand: bool
|
|
196
|
+
|
|
197
|
+
|
|
181
198
|
class ConnectiontrainDetail(TrainDetail):
|
|
199
|
+
arrival: TimeDict | str # WTF KOLEO!!!!
|
|
200
|
+
departure: TimeDict | str # WTF KOLEO!!!!
|
|
201
|
+
stops: list[ConnectionTrainStop]
|
|
202
|
+
bookable: bool
|
|
203
|
+
train_attribute_ids: list[int]
|
|
204
|
+
travel_time: int
|
|
205
|
+
direction: str
|
|
206
|
+
start_station_id: int
|
|
207
|
+
end_station_id: int
|
|
208
|
+
fixed_carriage_composition: bool
|
|
209
|
+
is_option_groups_available: bool
|
|
182
210
|
|
|
211
|
+
|
|
212
|
+
class ErrorDict(t.TypedDict):
|
|
213
|
+
type: str
|
|
214
|
+
value: str
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class ConnectionDetail(t.TypedDict):
|
|
218
|
+
id: int
|
|
219
|
+
distance: int
|
|
220
|
+
purchasable: bool
|
|
221
|
+
purchasable_errors: list[ErrorDict]
|
|
222
|
+
travel_time: int
|
|
223
|
+
changes: int
|
|
224
|
+
needs_document: bool
|
|
225
|
+
brand_ids: list[int]
|
|
226
|
+
start_station_id: int
|
|
227
|
+
end_station_id: int
|
|
183
228
|
arrival: TimeDict | str # WTF KOLEO!!!!
|
|
184
229
|
departure: TimeDict | str # WTF KOLEO!!!!
|
|
230
|
+
bookable: bool
|
|
231
|
+
special_event_slug: str | None
|
|
232
|
+
is_advanced_travel_options: bool
|
|
233
|
+
is_child_birthday_required: bool
|
|
234
|
+
max_passengers_count: bool
|
|
235
|
+
constriction_info: list[str]
|
|
236
|
+
is_estimated_timetable_available: bool
|
|
237
|
+
trains: list[ConnectiontrainDetail]
|
koleo/utils.py
CHANGED
|
@@ -16,6 +16,12 @@ def parse_datetime(s: str):
|
|
|
16
16
|
pass
|
|
17
17
|
if s[0] == "+":
|
|
18
18
|
return datetime.now().replace(hour=0, minute=0) + timedelta(days=int(s[1:]))
|
|
19
|
+
try:
|
|
20
|
+
now = datetime.now()
|
|
21
|
+
dt = datetime.strptime(s, "%d-%m %H:%M")
|
|
22
|
+
return dt.replace(year=now.year)
|
|
23
|
+
except ValueError:
|
|
24
|
+
pass
|
|
19
25
|
return datetime.combine(datetime.now(), datetime.strptime(s, "%H:%M").time())
|
|
20
26
|
|
|
21
27
|
|
|
@@ -58,8 +64,9 @@ NUMERAL_TO_ARABIC = {
|
|
|
58
64
|
"X": 10,
|
|
59
65
|
"XI": 11, # wtf poznań???
|
|
60
66
|
"XII": 12, # just to be safe
|
|
67
|
+
"BUS": "BUS"
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
|
|
64
|
-
def convert_platform_number(number: str) -> int | None:
|
|
71
|
+
def convert_platform_number(number: str) -> int | None | str:
|
|
65
72
|
return NUMERAL_TO_ARABIC.get(number)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
koleo/__init__.py,sha256=N_IkOBZCSPCCw31Hu72CFys707PziGFmXpNVl0CXAz8,47
|
|
2
|
+
koleo/__main__.py,sha256=wu5N2wk8mvBgyvr2ghmQf4prezAe0_i-p123VVreyYc,62
|
|
3
|
+
koleo/api.py,sha256=3pVcAMnCzaDIgakxagmKOZpph8JU5p6agXKGw550rkw,5710
|
|
4
|
+
koleo/cli.py,sha256=o_6tUy8odM-CqSqpEJMJFTGx1QJnPwu-kIXzrjH5VpI,18098
|
|
5
|
+
koleo/storage.py,sha256=l48A8zsP3t77nFZA23dDncoeyV_VlhJ1RvM3xjWug2Q,2001
|
|
6
|
+
koleo/types.py,sha256=PQDDaTF9Q6ZylcOupWrOWg0qWMqRrNhMOE5m_bl8fVo,5219
|
|
7
|
+
koleo/utils.py,sha256=ejJpHnFYDPbxa3Wd_q92cTkwbkXhGvwFGKoGITBSrdY,1615
|
|
8
|
+
koleo_cli-0.2.137.4.dist-info/METADATA,sha256=rBTsWkw7oG5GU5kjGtZktysGFC9smmvohueArb-YE8w,626
|
|
9
|
+
koleo_cli-0.2.137.4.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
10
|
+
koleo_cli-0.2.137.4.dist-info/entry_points.txt,sha256=LtCidkVDq8Zd7-fxpRbys1Xa9LTHMZwXVbdcQEscdes,41
|
|
11
|
+
koleo_cli-0.2.137.4.dist-info/top_level.txt,sha256=AlWdXotkRYzHpFfOBYi6xOXl1H0zq4-tqtZ2XivoWB4,6
|
|
12
|
+
koleo_cli-0.2.137.4.dist-info/RECORD,,
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
koleo/__init__.py,sha256=N_IkOBZCSPCCw31Hu72CFys707PziGFmXpNVl0CXAz8,47
|
|
2
|
-
koleo/__main__.py,sha256=wu5N2wk8mvBgyvr2ghmQf4prezAe0_i-p123VVreyYc,62
|
|
3
|
-
koleo/api.py,sha256=L4Nqcf2dj5IRNijxJ1IeJCf-69o_3FSjvJzLDi1f8s4,5435
|
|
4
|
-
koleo/cli.py,sha256=DAHe1Kqzkg0EGK00kkErBugtz-sdA8FNMpvdeAQoRvM,13767
|
|
5
|
-
koleo/storage.py,sha256=l48A8zsP3t77nFZA23dDncoeyV_VlhJ1RvM3xjWug2Q,2001
|
|
6
|
-
koleo/types.py,sha256=n7eXJfWD9BbCY6pwhPZhrArZsCqNSnGra2-ZSYwxJ58,3916
|
|
7
|
-
koleo/utils.py,sha256=N9ceKXsxC4RrG_W2PJopSL8Dsj44rFs2rDkyzM4m4ek,1428
|
|
8
|
-
koleo_cli-0.2.137.3.dist-info/METADATA,sha256=ONp3qsmOfzWqnTrgWcCGYIZHvuhwvo9l5tdUwrYO4lA,626
|
|
9
|
-
koleo_cli-0.2.137.3.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
10
|
-
koleo_cli-0.2.137.3.dist-info/entry_points.txt,sha256=LtCidkVDq8Zd7-fxpRbys1Xa9LTHMZwXVbdcQEscdes,41
|
|
11
|
-
koleo_cli-0.2.137.3.dist-info/top_level.txt,sha256=AlWdXotkRYzHpFfOBYi6xOXl1H0zq4-tqtZ2XivoWB4,6
|
|
12
|
-
koleo_cli-0.2.137.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|