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.

@@ -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
+ [![PyPI - Version](https://img.shields.io/pypi/v/koleo-cli.svg)](https://pypi.org/project/koleo-cli)
20
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/koleo-cli.svg)](https://pypi.org/project/koleo-cli)
21
+
22
+ # Koleo CLI
23
+
24
+ ![gif showcasing the functionality](https://github.com/lzgirlcat/koleo-cli/blob/main/koleo-cli.gif?raw=true)
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
+ [![PyPI - Version](https://img.shields.io/pypi/v/koleo-cli.svg)](https://pypi.org/project/koleo-cli)
2
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/koleo-cli.svg)](https://pypi.org/project/koleo-cli)
3
+
4
+ # Koleo CLI
5
+
6
+ ![gif showcasing the functionality](https://github.com/lzgirlcat/koleo-cli/blob/main/koleo-cli.gif?raw=true)
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
+ ```
@@ -37,11 +37,9 @@ class errors:
37
37
  class KoleoForbidden(KoleoAPIException):
38
38
  pass
39
39
 
40
-
41
40
  class KoleoUnauthorized(KoleoAPIException):
42
41
  pass
43
42
 
44
-
45
43
  class KoleoRatelimited(KoleoAPIException):
46
44
  pass
47
45
 
@@ -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, ExtendedBaseStationInfo
11
- from .utils import convert_platform_number, name_to_slug, parse_datetime, arr_dep_to_dt
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=False, max_frames=2)
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'\[[^\]]*\]', '', text)
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.storage.get_cache(cache_id) or
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.storage.get_cache(cache_id) or
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.storage.get_cache("stations") or
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(f"[bold blue][link=https://koleo.pl/dworzec-pkp/{st["name_slug"]}]{st["name"]}[/bold blue] ID: {st["id"]}[/link]")
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: list[str], date: datetime):
108
+ def train_info(self, brand: str, name: str, date: datetime):
110
109
  brand = brand.upper().strip()
111
- if len(name) == 1 and name[0].isnumeric():
112
- number = int(name[0])
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(name.pop(0))
116
- train_name = " ".join(name)
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 (route_end.hour==route_start.hour and route_end.minute < route_end.minute):
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(f"[white] {travel_time.seconds//3600}h{(travel_time.seconds % 3600)/60:.0f}m {speed:^4.1f}km/h [/white]")
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 = [i["id"] for i in api_brands if i["name"].lower().strip() in brands or i["logo_text"].lower().strip() in 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 = [f"[bold blue]{start_station["name"]} → {end_station["name"]} at {date.strftime("%H:%M %d-%m")}[/bold blue]"]
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(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]")
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 if stop["station_id"] == start_station["id"] else
194
- self.storage.get_cache(f"st-{stop['station_id']}")
195
- or self.storage.set_cache(f"st-{stop['station_id']}", self.client.get_station_by_id(stop['station_id']))
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]+=f" [red]{brand}[/red] {train["train_full_name"]}[purple] {stop_station['name']} {position_info}[/purple]"
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 if fs["station_id"] == start_station["id"] else
209
- self.storage.get_cache(f"st-{fs['station_id']}")
210
- or self.storage.set_cache(f"st-{fs['station_id']}", self.client.get_station_by_id(fs['station_id']))
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
- fs_platform = convert_platform_number(fs["platform"]) if fs["platform"] else ""
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']} {f"{fs_platform}/{fs["track"]}" if fs["track"] else fs_platform}[/purple]"
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 if ls["station_id"] == start_station["id"] else
220
- self.storage.get_cache(f"st-{ls['station_id']}")
221
- or self.storage.set_cache(f"st-{ls['station_id']}", self.client.get_station_by_id(ls['station_id']))
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']} {f"{ls_platform}/{ls["track"]}" if ls["track"] else ls_platform}[/purple]"
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(f" {connection_time//3600}h{(connection_time % 3600)/60:.0f}m at [purple]{fs_station['name']}[/purple]")
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"]} {position_info}[/purple]"
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) # type: ignore
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("departures", aliases=["d", "dep", "odjazdy", "o"], help="Allows you to list station departures")
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("arrivals", aliases=["a", "arr", "przyjazdy", "p"], help="Allows you to list station departures")
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", type=str, nargs="+")
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("stations", aliases=["s", "find", "f", "stacje", "ls"], help="Allows you to find stations by their name")
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", default=False
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", default=False
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 # WTF KOLEO!!!!
183
- departure: TimeDict | str # WTF KOLEO!!!!
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 # WTF KOLEO!!!!
200
- departure: TimeDict | str # WTF KOLEO!!!!
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 # WTF KOLEO!!!!
229
- departure: TimeDict | str # WTF KOLEO!!!!
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
+ [![PyPI - Version](https://img.shields.io/pypi/v/koleo-cli.svg)](https://pypi.org/project/koleo-cli)
20
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/koleo-cli.svg)](https://pypi.org/project/koleo-cli)
21
+
22
+ # Koleo CLI
23
+
24
+ ![gif showcasing the functionality](https://github.com/lzgirlcat/koleo-cli/blob/main/koleo-cli.gif?raw=true)
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
+ ```
@@ -14,7 +14,7 @@ def parse_requirements_file(path):
14
14
 
15
15
  setuptools.setup(
16
16
  name="koleo-cli",
17
- version="0.2.137.5",
17
+ version="0.2.137.6",
18
18
  description="Koleo CLI",
19
19
  long_description=long_description(),
20
20
  long_description_content_type="text/markdown",
@@ -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
@@ -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