koleo-cli 0.2.137.8__tar.gz → 0.2.137.10__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.8/koleo_cli.egg-info → koleo_cli-0.2.137.10}/PKG-INFO +1 -1
- {koleo_cli-0.2.137.8 → koleo_cli-0.2.137.10}/koleo/cli.py +101 -5
- {koleo_cli-0.2.137.8 → koleo_cli-0.2.137.10}/koleo/storage.py +11 -2
- {koleo_cli-0.2.137.8 → koleo_cli-0.2.137.10/koleo_cli.egg-info}/PKG-INFO +1 -1
- {koleo_cli-0.2.137.8 → koleo_cli-0.2.137.10}/setup.py +1 -1
- {koleo_cli-0.2.137.8 → koleo_cli-0.2.137.10}/LICENSE +0 -0
- {koleo_cli-0.2.137.8 → koleo_cli-0.2.137.10}/README.md +0 -0
- {koleo_cli-0.2.137.8 → koleo_cli-0.2.137.10}/koleo/__init__.py +0 -0
- {koleo_cli-0.2.137.8 → koleo_cli-0.2.137.10}/koleo/__main__.py +0 -0
- {koleo_cli-0.2.137.8 → koleo_cli-0.2.137.10}/koleo/api.py +0 -0
- {koleo_cli-0.2.137.8 → koleo_cli-0.2.137.10}/koleo/types.py +0 -0
- {koleo_cli-0.2.137.8 → koleo_cli-0.2.137.10}/koleo/utils.py +0 -0
- {koleo_cli-0.2.137.8 → koleo_cli-0.2.137.10}/koleo_cli.egg-info/SOURCES.txt +0 -0
- {koleo_cli-0.2.137.8 → koleo_cli-0.2.137.10}/koleo_cli.egg-info/dependency_links.txt +0 -0
- {koleo_cli-0.2.137.8 → koleo_cli-0.2.137.10}/koleo_cli.egg-info/entry_points.txt +0 -0
- {koleo_cli-0.2.137.8 → koleo_cli-0.2.137.10}/koleo_cli.egg-info/requires.txt +0 -0
- {koleo_cli-0.2.137.8 → koleo_cli-0.2.137.10}/koleo_cli.egg-info/top_level.txt +0 -0
- {koleo_cli-0.2.137.8 → koleo_cli-0.2.137.10}/pyproject.toml +0 -0
- {koleo_cli-0.2.137.8 → koleo_cli-0.2.137.10}/setup.cfg +0 -0
|
@@ -6,7 +6,7 @@ from rich.console import Console
|
|
|
6
6
|
|
|
7
7
|
from .api import KoleoAPI
|
|
8
8
|
from .storage import DEFAULT_CONFIG_PATH, Storage
|
|
9
|
-
from .types import ExtendedBaseStationInfo, TrainDetailResponse, TrainOnStationInfo
|
|
9
|
+
from .types import ExtendedBaseStationInfo, TrainDetailResponse, TrainOnStationInfo, TrainCalendar
|
|
10
10
|
from .utils import RemainderString, arr_dep_to_dt, convert_platform_number, name_to_slug, parse_datetime
|
|
11
11
|
|
|
12
12
|
|
|
@@ -89,6 +89,37 @@ class CLI:
|
|
|
89
89
|
self.print(station_info)
|
|
90
90
|
self.get_arrivals(st["id"], date)
|
|
91
91
|
|
|
92
|
+
def all_trains(self, station: str, date: datetime):
|
|
93
|
+
st = self.get_station(station)
|
|
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)
|
|
96
|
+
arr_cache_id = f"arr-{st['id']}-{date.strftime("%Y-%m-%d")}"
|
|
97
|
+
dep_cache_id = f"dep-{st['id']}-{date.strftime("%Y-%m-%d")}"
|
|
98
|
+
arrivals = self.storage.get_cache(arr_cache_id) or self.storage.set_cache(
|
|
99
|
+
arr_cache_id, self.client.get_arrivals(st['id'], date)
|
|
100
|
+
)
|
|
101
|
+
departures = self.storage.get_cache(dep_cache_id) or self.storage.set_cache(
|
|
102
|
+
dep_cache_id, self.client.get_departures(st['id'], date)
|
|
103
|
+
)
|
|
104
|
+
trains = sorted(
|
|
105
|
+
[(i, 1) for i in departures] + [(i, 2) for i in arrivals],
|
|
106
|
+
key=lambda train: datetime.fromisoformat(train[0]["departure"] if train[1] == 1 else train[0]["arrival"]).timestamp()
|
|
107
|
+
)
|
|
108
|
+
trains = [
|
|
109
|
+
(i, type)
|
|
110
|
+
for i, type in trains
|
|
111
|
+
if datetime.fromisoformat(i["departure"] if type == 1 else i["arrival"]).timestamp() > date.timestamp() # type: ignore
|
|
112
|
+
]
|
|
113
|
+
brands = self.storage.get_cache("brands") or self.storage.set_cache("brands", self.client.get_brands())
|
|
114
|
+
parts = []
|
|
115
|
+
for train, type in trains:
|
|
116
|
+
time = f"[bold green]{train['departure'][11:16]}[/bold green]" if type == 1 else f"[bold yellow]{train['arrival'][11:16]}[/bold yellow]"
|
|
117
|
+
brand = next(iter(i for i in brands if i["id"] == train["brand_id"]), {}).get("logo_text")
|
|
118
|
+
parts.append(
|
|
119
|
+
f"{time} [red]{brand}[/red] {train["train_full_name"]}[purple] {train["stations"][0]["name"]} {self.format_position(train["platform"], train["track"])}[/purple]"
|
|
120
|
+
)
|
|
121
|
+
self.print("\n".join(parts))
|
|
122
|
+
|
|
92
123
|
def find_station(self, query: str | None):
|
|
93
124
|
if query:
|
|
94
125
|
stations = self.client.find_station(query)
|
|
@@ -101,7 +132,7 @@ class CLI:
|
|
|
101
132
|
f"[bold blue][link=https://koleo.pl/dworzec-pkp/{st["name_slug"]}]{st["name"]}[/bold blue] ID: {st["id"]}[/link]"
|
|
102
133
|
)
|
|
103
134
|
|
|
104
|
-
def
|
|
135
|
+
def get_train_calendars(self, brand: str, name: str) -> list[TrainCalendar]:
|
|
105
136
|
brand = brand.upper().strip()
|
|
106
137
|
name_parts = name.split(" ")
|
|
107
138
|
if len(name_parts) == 1 and name_parts[0].isnumeric():
|
|
@@ -126,21 +157,47 @@ class CLI:
|
|
|
126
157
|
except self.client.errors.KoleoNotFound:
|
|
127
158
|
self.print(f'[bold red]Train not found: nr={number}, name="{train_name}"[/bold red]')
|
|
128
159
|
exit(2)
|
|
129
|
-
|
|
160
|
+
return train_calendars["train_calendars"]
|
|
161
|
+
|
|
162
|
+
def train_calendar(self, brand: str, name: str):
|
|
163
|
+
train_calendars = self.get_train_calendars(brand, name)
|
|
164
|
+
brands = self.storage.get_cache("brands") or self.storage.set_cache("brands", self.client.get_brands())
|
|
165
|
+
for calendar in train_calendars:
|
|
166
|
+
brand = next(iter(i for i in brands if i["id"] == calendar["trainBrand"]), {}).get("logo_text", "")
|
|
167
|
+
parts = [f"[red]{brand}[/red] [bold blue]{calendar['train_nr']}{" "+ v if (v:=calendar.get("train_name")) else ""}[/bold blue]:"]
|
|
168
|
+
for k, v in calendar["date_train_map"].items():
|
|
169
|
+
parts.append(f" [bold green]{k}[/bold green]: [purple]{v}[/purple]")
|
|
170
|
+
self.print("\n".join(parts))
|
|
171
|
+
|
|
172
|
+
def train_info(self, brand: str, name: str, date: datetime):
|
|
173
|
+
train_calendars = self.get_train_calendars(brand, name)
|
|
174
|
+
if not (train_id:=train_calendars[0]["date_train_map"].get(date.strftime("%Y-%m-%d"))):
|
|
175
|
+
self.print(f"[bold red]This train doesn't run on the selected date: {date.strftime("%Y-%m-%d")}[/bold red]")
|
|
176
|
+
exit(2)
|
|
177
|
+
self.train_detail(train_id)
|
|
178
|
+
|
|
179
|
+
def train_detail(self, train_id: int):
|
|
130
180
|
train_details = self.client.get_train(train_id)
|
|
181
|
+
brands = self.storage.get_cache("brands") or self.storage.set_cache("brands", self.client.get_brands())
|
|
131
182
|
brand = next(iter(i for i in brands if i["id"] == train_details["train"]["brand_id"]), {}).get("logo_text", "")
|
|
183
|
+
|
|
132
184
|
parts = [f"[red]{brand}[/red] [bold blue]{train_details["train"]["train_full_name"]}[/bold blue]"]
|
|
185
|
+
parts.append(f" {train_details["train"]["run_desc"]}")
|
|
186
|
+
|
|
133
187
|
route_start = arr_dep_to_dt(train_details["stops"][0]["departure"])
|
|
134
188
|
route_end = arr_dep_to_dt(train_details["stops"][-1]["arrival"])
|
|
189
|
+
|
|
135
190
|
if route_end.hour < route_start.hour or (
|
|
136
191
|
route_end.hour == route_start.hour and route_end.minute < route_end.minute
|
|
137
192
|
):
|
|
138
193
|
route_end += timedelta(days=1)
|
|
194
|
+
|
|
139
195
|
travel_time = route_end - route_start
|
|
140
196
|
speed = train_details["stops"][-1]["distance"] / 1000 / travel_time.seconds * 3600
|
|
141
197
|
parts.append(
|
|
142
198
|
f"[white] {travel_time.seconds//3600}h{(travel_time.seconds % 3600)/60:.0f}m {speed:^4.1f}km/h [/white]"
|
|
143
199
|
)
|
|
200
|
+
|
|
144
201
|
vehicle_types: dict[str, str] = {
|
|
145
202
|
stop["station_display_name"]: stop["vehicle_type"]
|
|
146
203
|
for stop in train_details["stops"]
|
|
@@ -330,10 +387,30 @@ def main():
|
|
|
330
387
|
arrivals.add_argument("-s", "--save", help="save the station as your default one", action="store_true")
|
|
331
388
|
arrivals.set_defaults(func=cli.full_arrivals, pass_=["station", "date"])
|
|
332
389
|
|
|
390
|
+
all_trains = subparsers.add_parser(
|
|
391
|
+
"all", aliases=["w", "wszystkie", "all_trains", "pociagi"], help="Allows you to list all station trains"
|
|
392
|
+
)
|
|
393
|
+
all_trains.add_argument(
|
|
394
|
+
"station",
|
|
395
|
+
help="The station name",
|
|
396
|
+
default=None,
|
|
397
|
+
nargs="*",
|
|
398
|
+
action=RemainderString,
|
|
399
|
+
)
|
|
400
|
+
all_trains.add_argument(
|
|
401
|
+
"-d",
|
|
402
|
+
"--date",
|
|
403
|
+
help="the date",
|
|
404
|
+
type=lambda s: parse_datetime(s),
|
|
405
|
+
default=datetime.now(),
|
|
406
|
+
)
|
|
407
|
+
all_trains.add_argument("-s", "--save", help="save the station as your default one", action="store_true")
|
|
408
|
+
all_trains.set_defaults(func=cli.all_trains, pass_=["station", "date"])
|
|
409
|
+
|
|
333
410
|
train_route = subparsers.add_parser(
|
|
334
411
|
"trainroute",
|
|
335
412
|
aliases=["r", "tr", "t", "poc", "pociąg"],
|
|
336
|
-
help="Allows you to
|
|
413
|
+
help="Allows you to check the train's route",
|
|
337
414
|
)
|
|
338
415
|
train_route.add_argument("brand", help="The brand name", type=str)
|
|
339
416
|
train_route.add_argument("name", help="The train name", nargs="+", action=RemainderString)
|
|
@@ -346,6 +423,23 @@ def main():
|
|
|
346
423
|
)
|
|
347
424
|
train_route.set_defaults(func=cli.train_info, pass_=["brand", "name", "date"])
|
|
348
425
|
|
|
426
|
+
train_calendar = subparsers.add_parser(
|
|
427
|
+
"traincalendar",
|
|
428
|
+
aliases=["kursowanie", "tc", "k"],
|
|
429
|
+
help="Allows you to check what days the train runs on",
|
|
430
|
+
)
|
|
431
|
+
train_calendar.add_argument("brand", help="The brand name", type=str)
|
|
432
|
+
train_calendar.add_argument("name", help="The train name", nargs="+", action=RemainderString)
|
|
433
|
+
train_calendar.set_defaults(func=cli.train_calendar, pass_=["brand", "name"])
|
|
434
|
+
|
|
435
|
+
train_detail = subparsers.add_parser(
|
|
436
|
+
"traindetail",
|
|
437
|
+
aliases=["td", "tid", "id", "idpoc"],
|
|
438
|
+
help="Allows you to show the train's route given it's koleo ID",
|
|
439
|
+
)
|
|
440
|
+
train_detail.add_argument("train_id", help="The koleo ID", type=int)
|
|
441
|
+
train_detail.set_defaults(func=cli.train_detail, pass_=["train_id"])
|
|
442
|
+
|
|
349
443
|
stations = subparsers.add_parser(
|
|
350
444
|
"stations", aliases=["s", "find", "f", "stacje", "ls", "q"], help="Allows you to find stations by their name"
|
|
351
445
|
)
|
|
@@ -402,7 +496,6 @@ def main():
|
|
|
402
496
|
args.station = storage.favourite_station
|
|
403
497
|
elif hasattr(args, "station") and args.save:
|
|
404
498
|
storage.favourite_station = args.station
|
|
405
|
-
storage.save()
|
|
406
499
|
if not hasattr(args, "func"):
|
|
407
500
|
if storage.favourite_station:
|
|
408
501
|
cli.full_departures(storage.favourite_station, datetime.now())
|
|
@@ -411,3 +504,6 @@ def main():
|
|
|
411
504
|
exit()
|
|
412
505
|
else:
|
|
413
506
|
args.func(**{k: v for k, v in args.__dict__.items() if k in getattr(args, "pass_", [])})
|
|
507
|
+
|
|
508
|
+
if storage._dirty:
|
|
509
|
+
storage.save()
|
|
@@ -33,6 +33,7 @@ class Storage:
|
|
|
33
33
|
|
|
34
34
|
def __post_init__(self):
|
|
35
35
|
self._path: str
|
|
36
|
+
self._dirty = False
|
|
36
37
|
|
|
37
38
|
@classmethod
|
|
38
39
|
def load(cls, *, path: str = DEFAULT_CONFIG_PATH) -> t.Self:
|
|
@@ -57,13 +58,13 @@ class Storage:
|
|
|
57
58
|
return item
|
|
58
59
|
else:
|
|
59
60
|
self.cache.pop(id)
|
|
60
|
-
self.
|
|
61
|
+
self._dirty = True
|
|
61
62
|
|
|
62
63
|
def set_cache(self, id: str, item: T, ttl: int = 86400) -> T:
|
|
63
64
|
if self.disable_cache:
|
|
64
65
|
return item
|
|
65
66
|
self.cache[id] = (int(time() + ttl), item)
|
|
66
|
-
self.
|
|
67
|
+
self._dirty = True
|
|
67
68
|
return item
|
|
68
69
|
|
|
69
70
|
def save(self):
|
|
@@ -72,4 +73,12 @@ class Storage:
|
|
|
72
73
|
if not ospath.exists(dir):
|
|
73
74
|
makedirs(dir)
|
|
74
75
|
with open(self._path, "w+") as f:
|
|
76
|
+
self.clean()
|
|
75
77
|
dump(asdict(self), f, indent=True)
|
|
78
|
+
|
|
79
|
+
def clean(self):
|
|
80
|
+
now = time()
|
|
81
|
+
copy = self.cache.copy()
|
|
82
|
+
self.cache = {k: data for k, data in copy.items() if data[0] > now}
|
|
83
|
+
if copy != self.cache:
|
|
84
|
+
self._dirty = True
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|