koleo-cli 0.2.137.2__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 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
@@ -86,7 +81,13 @@ class KoleoAPI:
86
81
 
87
82
  def find_station(self, query: str, language: str = "pl") -> list[ExtendedStationInfo]:
88
83
  # https://koleo.pl/ls?q=tere&language=pl
89
- return self._get_json("/ls", query={"q": query, "language": language})
84
+ return self._get_json("/ls", params={"q": query, "language": language})["stations"]
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
+ )
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
@@ -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]": purchasable,
141
- "query[direct]": direct,
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
@@ -1,3 +1,4 @@
1
+ import re
1
2
  from argparse import ArgumentParser
2
3
  from datetime import datetime, timedelta
3
4
 
@@ -22,7 +23,15 @@ class CLI:
22
23
  ) -> None:
23
24
  self._client = client
24
25
  self._storage = storage
25
- self.console = Console(color_system="standard", no_color=no_color)
26
+ self.no_color = no_color
27
+ self.console = Console(color_system="standard", no_color=no_color, highlight=False)
28
+
29
+ def print(self, text, *args, **kwargs):
30
+ if self.no_color:
31
+ result = re.sub(r'\[[^\]]*\]', '', text)
32
+ print(result)
33
+ else:
34
+ self.console.print(text, *args, **kwargs)
26
35
 
27
36
  @property
28
37
  def client(self) -> KoleoAPI:
@@ -44,9 +53,6 @@ class CLI:
44
53
  def storage(self, storage: Storage):
45
54
  self._storage = storage
46
55
 
47
- def list_stations(self, name: str):
48
- ...
49
-
50
56
  def get_departures(self, station_id: int, date: datetime):
51
57
  cache_id = f"dep-{station_id}-{date.strftime("%Y-%m-%d")}"
52
58
  trains = (
@@ -59,7 +65,7 @@ class CLI:
59
65
  if datetime.fromisoformat(i["departure"]).timestamp() > date.timestamp() # type: ignore
60
66
  ]
61
67
  table = self.trains_on_station_table(trains)
62
- self.console.print(table)
68
+ self.print(table)
63
69
  return table
64
70
 
65
71
  def get_arrivals(self, station_id: int, date: datetime):
@@ -74,21 +80,32 @@ class CLI:
74
80
  if datetime.fromisoformat(i["arrival"]).timestamp() > date.timestamp() # type: ignore
75
81
  ]
76
82
  table = self.trains_on_station_table(trains, type=2)
77
- self.console.print(table)
83
+ self.print(table)
78
84
  return table
79
85
 
80
86
  def full_departures(self, station: str, date: datetime):
81
87
  st = self.get_station(station)
82
- station_info = f"[bold blue]{st["name"]}[/bold blue] ID: {st["id"]}"
83
- self.console.print(station_info)
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]"
89
+ self.print(station_info)
84
90
  self.get_departures(st["id"], date)
85
91
 
86
92
  def full_arrivals(self, station: str, date: datetime):
87
93
  st = self.get_station(station)
88
- station_info = f"[bold blue]{st["name"]}[/bold blue] ID: {st["id"]}"
89
- self.console.print(station_info)
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]"
95
+ self.print(station_info)
90
96
  self.get_arrivals(st["id"], date)
91
97
 
98
+ def find_station(self, query: str | None):
99
+ if query:
100
+ stations = self.client.find_station(query)
101
+ else:
102
+ stations = (
103
+ self.storage.get_cache("stations") or
104
+ self.storage.set_cache("stations", self.client.get_stations())
105
+ )
106
+ for st in stations:
107
+ self.print(f"[bold blue][link=https://koleo.pl/dworzec-pkp/{st["name_slug"]}]{st["name"]}[/bold blue] ID: {st["id"]}[/link]")
108
+
92
109
  def train_info(self, brand: str, name: str, date: datetime):
93
110
  brand = brand.upper().strip()
94
111
  name = name.strip()
@@ -113,7 +130,7 @@ class CLI:
113
130
  train_id = train_calendars["train_calendars"][0]["date_train_map"][date.strftime("%Y-%m-%d")]
114
131
  train_details = self.client.get_train(train_id)
115
132
  brand = next(iter(i for i in brands if i["id"] == train_details["train"]["brand_id"]), {}).get("logo_text", "")
116
- parts = [f"{brand} {train_details["train"]["train_full_name"]}"]
133
+ parts = [f"[red]{brand}[/red] [bold blue]{train_details["train"]["train_full_name"]}[/bold blue]"]
117
134
  route_start = arr_dep_to_dt(train_details["stops"][0]["departure"])
118
135
  route_end = arr_dep_to_dt(train_details["stops"][-1]["arrival"])
119
136
  if route_end.hour < route_start.hour or (route_end.hour==route_start.hour and route_end.minute < route_end.minute):
@@ -131,13 +148,13 @@ class CLI:
131
148
  start = keys[0]
132
149
  for i in range(1, len(keys)):
133
150
  if vehicle_types[keys[i]] != vehicle_types[start]:
134
- parts.append(f"[bold green] {start} - {keys[i]}:[/bold green] {vehicle_types[start]}")
151
+ parts.append(f" {start} - {keys[i]}: [bold green]{vehicle_types[start]}[/bold green]")
135
152
  start = keys[i]
136
- parts.append(f"[bold green] {start} - {keys[-1]}:[/bold green] {vehicle_types[start]}")
137
- self.console.print("\n".join(parts))
138
- self.console.print(self.train_route_table(train_details))
153
+ parts.append(f" {start} - {keys[-1]}: [bold green]{vehicle_types[start]}[/bold green]")
154
+ self.print("\n".join(parts))
155
+ self.print(self.train_route_table(train_details))
139
156
 
140
- def connections(self, start: str, end: str, date: datetime, brands: list[str], direct: bool = False, purchasable: bool = False):
157
+ def connections(self, start: str, end: str, date: datetime, brands: list[str], direct: bool, purchasable: bool):
141
158
  start_station = self.get_station(start)
142
159
  end_station = self.get_station(end)
143
160
  brands = [i.lower().strip() for i in brands]
@@ -147,7 +164,7 @@ class CLI:
147
164
  else:
148
165
  connection_brands = [i["id"] for i in api_brands if i["name"].lower().strip() in brands or i["logo_text"].lower().strip() in brands]
149
166
  if not connection_brands:
150
- self.console.print(f'[bold red]No brands match: "{', '.join(brands)}"[/bold red]')
167
+ self.print(f'[bold red]No brands match: "{', '.join(brands)}"[/bold red]')
151
168
  exit(2)
152
169
  connections = self.client.get_connections(
153
170
  start_station["name_slug"],
@@ -157,6 +174,59 @@ class CLI:
157
174
  direct,
158
175
  purchasable
159
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))
160
230
 
161
231
  def trains_on_station_table(self, trains: list[TrainOnStationInfo], type: int = 1):
162
232
  parts = []
@@ -168,7 +238,7 @@ class CLI:
168
238
  platform = convert_platform_number(train["platform"]) if train["platform"] else ""
169
239
  position_info = f"{platform}/{train["track"]}" if train["track"] else platform
170
240
  parts.append(
171
- f"[bold green]{time[11:16]}[/bold green] {brand} {train["train_full_name"]}[purple] {train["stations"][0]["name"]} {position_info}[/purple]"
241
+ f"[bold green]{time[11:16]}[/bold green] [red]{brand}[/red] {train["train_full_name"]}[purple] {train["stations"][0]["name"]} {position_info}[/purple]"
172
242
  )
173
243
  return "\n".join(parts)
174
244
 
@@ -190,7 +260,7 @@ class CLI:
190
260
  f"st-{slug}", self.client.get_station_by_slug(slug)
191
261
  )
192
262
  except self.client.errors.KoleoNotFound:
193
- self.console.print(f'[bold red]Station not found: "{station}"[/bold red]')
263
+ self.print(f'[bold red]Station not found: "{station}"[/bold red]')
194
264
  exit(2)
195
265
 
196
266
  def main():
@@ -251,9 +321,18 @@ def main():
251
321
  )
252
322
  train_route.set_defaults(func=cli.train_info, pass_=["brand", "name", "date"])
253
323
 
324
+ stations = subparsers.add_parser("stations", aliases=["s", "find", "f", "stacje", "ls"], help="Allows you to find stations by their name")
325
+ stations.add_argument(
326
+ "query",
327
+ help="The station name",
328
+ default=None,
329
+ nargs="?",
330
+ )
331
+ stations.set_defaults(func=cli.find_station, pass_=["query"])
332
+
254
333
  connections = subparsers.add_parser(
255
334
  "connections",
256
- aliases=["do", "z", "szukaj", "path", "find"],
335
+ aliases=["do", "z", "szukaj", "path"],
257
336
  help="Allows you to search for connections from a to b",
258
337
  )
259
338
  connections.add_argument("start", help="The starting station", type=str)
@@ -283,7 +362,7 @@ def main():
283
362
  help="whether or not the result should only trains purchasable on koleo",
284
363
  action="store_true", default=False
285
364
  )
286
- 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"])
287
366
 
288
367
  args = parser.parse_args()
289
368
 
@@ -291,6 +370,7 @@ def main():
291
370
  client = KoleoAPI()
292
371
  cli.client, cli.storage = client, storage
293
372
  cli.console.no_color = args.nocolor
373
+ cli.no_color = args.nocolor
294
374
  if hasattr(args, "station") and args.station is None:
295
375
  args.station = storage.favourite_station
296
376
  elif hasattr(args, "station") and args.save:
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: koleo-cli
3
- Version: 0.2.137.2
3
+ Version: 0.2.137.4
4
4
  Summary: Koleo CLI
5
5
  Home-page: https://github.com/lzgirlcat/koleo-cli
6
6
  Author: Zoey !
@@ -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=4qMx7pyfX_DR830BzfzwfWnmuiRQyvvWO42KwAl8Lqo,5422
4
- koleo/cli.py,sha256=raS5fkz42YY3gVJXpoExid1Dkc99vmM-hPgTvHsUZGM,12742
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.2.dist-info/METADATA,sha256=f980X6ifdRtYmDoq6-8lHz_Quz7pm4RZUlOI66OKVKw,626
9
- koleo_cli-0.2.137.2.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
10
- koleo_cli-0.2.137.2.dist-info/entry_points.txt,sha256=LtCidkVDq8Zd7-fxpRbys1Xa9LTHMZwXVbdcQEscdes,41
11
- koleo_cli-0.2.137.2.dist-info/top_level.txt,sha256=AlWdXotkRYzHpFfOBYi6xOXl1H0zq4-tqtZ2XivoWB4,6
12
- koleo_cli-0.2.137.2.dist-info/RECORD,,