koleo-cli 0.2.137.1__py3-none-any.whl → 0.2.137.3__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.

Potentially problematic release.


This version of koleo-cli might be problematic. Click here for more details.

koleo/__init__.py CHANGED
@@ -1,4 +1,2 @@
1
1
  from .api import KoleoAPI
2
-
3
-
4
- __all__ = ["KoleoAPI"]
2
+ from .types import *
koleo/api.py CHANGED
@@ -1,53 +1,59 @@
1
1
  import typing as t
2
2
  from datetime import datetime
3
3
 
4
- from requests import Session, PreparedRequest, Response
4
+ from requests import PreparedRequest, Response, Session
5
5
 
6
6
  from koleo.types import *
7
7
 
8
8
 
9
- class KoleoAPIException(Exception):
10
- status: int
11
- request: PreparedRequest
12
- response: Response
13
9
 
14
- def __init__(self, response: Response, *args: object) -> None:
15
- super().__init__(*args)
16
- self.status = response.status_code
17
- self.request = response.request
18
- self.response = response
19
10
 
20
- @classmethod
21
- def from_response(cls, response: Response) -> "KoleoAPIException":
22
- if response.status_code == 404:
23
- return KoleoNotFound(response)
24
- elif response.status_code == 401:
25
- return KoleoUnauthorized(response)
26
- elif response.status_code == 403:
27
- return KoleoForbidden(response)
28
- elif response.status_code == 429:
29
- return KoleoRatelimited(response)
30
- else:
31
- return KoleoAPIException(response)
32
11
 
33
12
 
34
- class KoleoNotFound(KoleoAPIException):
35
- pass
36
13
 
14
+ class errors:
15
+ class KoleoAPIException(Exception):
16
+ status: int
17
+ request: PreparedRequest
18
+ response: Response
37
19
 
38
- class KoleoForbidden(KoleoAPIException):
39
- pass
20
+ def __init__(self, response: Response, *args: object) -> None:
21
+ super().__init__(*args)
22
+ self.status = response.status_code
23
+ self.request = response.request
24
+ self.response = response
40
25
 
26
+ @classmethod
27
+ def from_response(cls, response: Response) -> "t.Self":
28
+ if response.status_code == 404:
29
+ return errors.KoleoNotFound(response)
30
+ elif response.status_code == 401:
31
+ return errors.KoleoUnauthorized(response)
32
+ elif response.status_code == 403:
33
+ return errors.KoleoForbidden(response)
34
+ elif response.status_code == 429:
35
+ return errors.KoleoRatelimited(response)
36
+ else:
37
+ return cls(response)
41
38
 
42
- class KoleoUnauthorized(KoleoAPIException):
43
- pass
39
+ class KoleoNotFound(KoleoAPIException):
40
+ pass
44
41
 
42
+ class KoleoForbidden(KoleoAPIException):
43
+ pass
45
44
 
46
- class KoleoRatelimited(KoleoAPIException):
47
- pass
45
+
46
+ class KoleoUnauthorized(KoleoAPIException):
47
+ pass
48
+
49
+
50
+ class KoleoRatelimited(KoleoAPIException):
51
+ pass
48
52
 
49
53
 
50
54
  class KoleoAPI:
55
+ errors = errors
56
+
51
57
  def __init__(self) -> None:
52
58
  self.session = Session()
53
59
  self.base_url = "https://koleo.pl"
@@ -61,20 +67,26 @@ class KoleoAPI:
61
67
  headers = {**self.base_headers, **kwargs.get("headers", {})}
62
68
  r = self.session.get(self.base_url + path, *args, headers=headers, **kwargs)
63
69
  if not r.ok:
64
- raise KoleoAPIException.from_response(r)
70
+ raise errors.KoleoAPIException.from_response(r)
65
71
  return r
66
72
 
67
73
  def _get_json(self, path, *args, **kwargs) -> t.Any:
68
74
  r = self._get(path, *args, **kwargs)
69
- return r.json()
75
+ res = r.json()
76
+ if res is None:
77
+ raise self.errors.KoleoNotFound(r)
78
+ return res
70
79
 
71
80
  def _get_bytes(self, path, *args, **kwargs) -> bytes:
72
81
  r = self._get(path, *args, **kwargs)
73
82
  return r.content
74
83
 
84
+ def get_stations(self) -> list[ExtendedStationInfo]:
85
+ return self._get_json("/api/v2/main/stations")
86
+
75
87
  def find_station(self, query: str, language: str = "pl") -> list[ExtendedStationInfo]:
76
88
  # https://koleo.pl/ls?q=tere&language=pl
77
- return self._get_json("/ls", query={"q": query, "language": language})
89
+ return self._get_json("/ls", params={"q": query, "language": language})["stations"]
78
90
 
79
91
  def get_station_by_slug(self, slug: str) -> ExtendedBaseStationInfo:
80
92
  # https://koleo.pl/api/v2/main/stations/by_slug/inowroclaw
@@ -119,7 +131,7 @@ class KoleoAPI:
119
131
  brand_ids: list[int],
120
132
  date: datetime,
121
133
  direct: bool = False,
122
- purchasable: bool = False
134
+ purchasable: bool = False,
123
135
  ) -> ...:
124
136
  params = {
125
137
  "query[date]": date.strftime("%d-%m-%Y %H:%M:%S"),
@@ -127,7 +139,7 @@ class KoleoAPI:
127
139
  "query[end_station]": end,
128
140
  "query[only_purchasable]": purchasable,
129
141
  "query[direct]": direct,
130
- "query[brand_ids]": brand_ids
142
+ "query[brand_ids][]": brand_ids,
131
143
  }
132
144
  return self._get_json("/api/v2/main/connections", params=params)
133
145
 
koleo/cli.py CHANGED
@@ -1,15 +1,17 @@
1
- from datetime import datetime
1
+ import re
2
2
  from argparse import ArgumentParser
3
+ from datetime import datetime, timedelta
3
4
 
4
5
  from rich.console import Console
5
6
  from rich.traceback import install
6
7
 
7
8
  from .api import KoleoAPI
8
- from .types import TrainOnStationInfo, TrainDetailResponse
9
- from .utils import name_to_slug, parse_datetime, time_dict_to_dt, convert_platform_number
10
- from .storage import Storage, DEFAULT_CONFIG_PATH
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
11
12
 
12
- install(show_locals=True)
13
+
14
+ install(show_locals=False, max_frames=2)
13
15
 
14
16
 
15
17
  class CLI:
@@ -21,7 +23,16 @@ class CLI:
21
23
  ) -> None:
22
24
  self._client = client
23
25
  self._storage = storage
24
- 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)
35
+
25
36
 
26
37
  @property
27
38
  def client(self) -> KoleoAPI:
@@ -43,48 +54,62 @@ class CLI:
43
54
  def storage(self, storage: Storage):
44
55
  self._storage = storage
45
56
 
46
- def list_stations(self, name: str): ...
57
+ def list_stations(self, name: str):
58
+ ...
47
59
 
48
60
  def get_departures(self, station_id: int, date: datetime):
49
- trains = self.client.get_departures(station_id, date)
61
+ cache_id = f"dep-{station_id}-{date.strftime("%Y-%m-%d")}"
62
+ trains = (
63
+ self.storage.get_cache(cache_id) or
64
+ self.storage.set_cache(cache_id, self.client.get_departures(station_id, date))
65
+ )
50
66
  trains = [
51
67
  i
52
68
  for i in trains
53
69
  if datetime.fromisoformat(i["departure"]).timestamp() > date.timestamp() # type: ignore
54
70
  ]
55
71
  table = self.trains_on_station_table(trains)
56
- self.console.print(table)
72
+ self.print(table)
57
73
  return table
58
74
 
59
75
  def get_arrivals(self, station_id: int, date: datetime):
60
- trains = self.client.get_arrivals(station_id, date)
76
+ cache_id = f"arr-{station_id}-{date.strftime("%Y-%m-%d")}"
77
+ trains = (
78
+ self.storage.get_cache(cache_id) or
79
+ self.storage.set_cache(cache_id, self.client.get_arrivals(station_id, date))
80
+ )
61
81
  trains = [
62
82
  i
63
83
  for i in trains
64
84
  if datetime.fromisoformat(i["arrival"]).timestamp() > date.timestamp() # type: ignore
65
85
  ]
66
86
  table = self.trains_on_station_table(trains, type=2)
67
- self.console.print(table)
87
+ self.print(table)
68
88
  return table
69
89
 
70
90
  def full_departures(self, station: str, date: datetime):
71
- slug = name_to_slug(station)
72
- st = self.storage.get_cache(f"st-{slug}") or self.storage.set_cache(
73
- f"st-{slug}", self.client.get_station_by_slug(slug)
74
- )
91
+ st = self.get_station(station)
75
92
  station_info = f"[bold blue]{st["name"]}[/bold blue] ID: {st["id"]}"
76
- self.console.print(station_info)
93
+ self.print(station_info)
77
94
  self.get_departures(st["id"], date)
78
95
 
79
96
  def full_arrivals(self, station: str, date: datetime):
80
- slug = name_to_slug(station)
81
- st = self.storage.get_cache(f"st-{slug}") or self.storage.set_cache(
82
- f"st-{slug}", self.client.get_station_by_slug(slug)
83
- )
97
+ st = self.get_station(station)
84
98
  station_info = f"[bold blue]{st["name"]}[/bold blue] ID: {st["id"]}"
85
- self.console.print(station_info)
99
+ self.print(station_info)
86
100
  self.get_arrivals(st["id"], date)
87
101
 
102
+ def find_station(self, query: str | None):
103
+ if query:
104
+ stations = self.client.find_station(query)
105
+ else:
106
+ stations = (
107
+ self.storage.get_cache("stations") or
108
+ self.storage.set_cache("stations", self.client.get_stations())
109
+ )
110
+ for st in stations:
111
+ self.print(f"[bold blue]{st["name"]}[/bold blue] ID: {st["id"]}")
112
+
88
113
  def train_info(self, brand: str, name: str, date: datetime):
89
114
  brand = brand.upper().strip()
90
115
  name = name.strip()
@@ -96,15 +121,27 @@ class CLI:
96
121
  number = int(number)
97
122
  else:
98
123
  raise ValueError("Invalid train name!")
124
+ brands = self.storage.get_cache("brands") or self.storage.set_cache("brands", self.client.get_brands())
125
+ if brand not in [i["name"] for i in brands]:
126
+ res = {i["logo_text"]: i["name"] for i in brands}.get(brand)
127
+ if not res:
128
+ raise ValueError("Invalid brand name!")
129
+ brand = res
99
130
  cache_id = f"tc-{brand}-{number}-{name}"
100
131
  train_calendars = self.storage.get_cache(cache_id) or self.storage.set_cache(
101
132
  cache_id, self.client.get_train_calendars(brand, number, train_name)
102
133
  )
103
- brands = self.storage.get_cache("brands") or self.storage.set_cache("brands", self.client.get_brands())
104
134
  train_id = train_calendars["train_calendars"][0]["date_train_map"][date.strftime("%Y-%m-%d")]
105
135
  train_details = self.client.get_train(train_id)
106
- brand = next(iter(i for i in brands if i["id"] == train_details["train"]["brand_id"]), {}).get("name", "")
107
- parts = [f"{brand} {train_details["train"]["train_full_name"]}"]
136
+ brand = next(iter(i for i in brands if i["id"] == train_details["train"]["brand_id"]), {}).get("logo_text", "")
137
+ parts = [f"[red]{brand}[/red] [bold blue]{train_details["train"]["train_full_name"]}[/bold blue]"]
138
+ route_start = arr_dep_to_dt(train_details["stops"][0]["departure"])
139
+ route_end = arr_dep_to_dt(train_details["stops"][-1]["arrival"])
140
+ if route_end.hour < route_start.hour or (route_end.hour==route_start.hour and route_end.minute < route_end.minute):
141
+ route_end += timedelta(days=1)
142
+ travel_time = route_end - route_start
143
+ speed = train_details["stops"][-1]["distance"] / 1000 / travel_time.seconds * 3600
144
+ parts.append(f"[white] {travel_time.seconds//3600}h{(travel_time.seconds % 3600)/60:.0f}m {speed:^4.1f}km/h [/white]")
108
145
  vehicle_types: dict[str, str] = {
109
146
  stop["station_display_name"]: stop["vehicle_type"]
110
147
  for stop in train_details["stops"]
@@ -118,17 +155,29 @@ class CLI:
118
155
  parts.append(f"[bold green] {start} - {keys[i]}:[/bold green] {vehicle_types[start]}")
119
156
  start = keys[i]
120
157
  parts.append(f"[bold green] {start} - {keys[-1]}:[/bold green] {vehicle_types[start]}")
121
- self.console.print("\n".join(parts))
122
- self.console.print(self.train_route_table(train_details))
158
+ self.print("\n".join(parts))
159
+ self.print(self.train_route_table(train_details))
123
160
 
124
- def route(self, start: str, end: str, date: datetime, direct: bool = False, purchasable: bool = False):
125
- slug = name_to_slug(station)
126
- st = self.storage.get_cache(f"st-{slug}") or self.storage.set_cache(
127
- f"st-{slug}", self.client.get_station_by_slug(slug)
161
+ def connections(self, start: str, end: str, date: datetime, brands: list[str], direct: bool = False, purchasable: bool = False):
162
+ start_station = self.get_station(start)
163
+ end_station = self.get_station(end)
164
+ brands = [i.lower().strip() for i in brands]
165
+ api_brands = self.storage.get_cache("brands") or self.storage.set_cache("brands", self.client.get_brands())
166
+ if not brands:
167
+ connection_brands = [i["id"] for i in api_brands]
168
+ else:
169
+ connection_brands = [i["id"] for i in api_brands if i["name"].lower().strip() in brands or i["logo_text"].lower().strip() in brands]
170
+ if not connection_brands:
171
+ self.print(f'[bold red]No brands match: "{', '.join(brands)}"[/bold red]')
172
+ exit(2)
173
+ connections = self.client.get_connections(
174
+ start_station["name_slug"],
175
+ end_station["name_slug"],
176
+ connection_brands,
177
+ date,
178
+ direct,
179
+ purchasable
128
180
  )
129
- station_info = f"[bold blue]{st["name"]}[/bold blue] ID: {st["id"]}"
130
- self.console.print(station_info)
131
- self.get_arrivals(st["id"], date)
132
181
 
133
182
  def trains_on_station_table(self, trains: list[TrainOnStationInfo], type: int = 1):
134
183
  parts = []
@@ -136,23 +185,34 @@ class CLI:
136
185
  for train in trains:
137
186
  time = train["departure"] if type == 1 else train["arrival"]
138
187
  assert time
139
- brand = next(iter(i for i in brands if i["id"] == train["brand_id"]), {}).get("name")
188
+ brand = next(iter(i for i in brands if i["id"] == train["brand_id"]), {}).get("logo_text")
189
+ platform = convert_platform_number(train["platform"]) if train["platform"] else ""
190
+ position_info = f"{platform}/{train["track"]}" if train["track"] else platform
140
191
  parts.append(
141
- f"[bold green]{time[11:16]}[/bold green] {brand} {train["train_full_name"]}[purple] {train["stations"][0]["name"]} [/purple]"
192
+ f"[bold green]{time[11:16]}[/bold green] [red]{brand}[/red] {train["train_full_name"]}[purple] {train["stations"][0]["name"]} {position_info}[/purple]"
142
193
  )
143
194
  return "\n".join(parts)
144
195
 
145
196
  def train_route_table(self, train: TrainDetailResponse):
146
197
  parts = []
147
198
  for stop in train["stops"]:
148
- arr = time_dict_to_dt(stop["arrival"])
149
- dep = time_dict_to_dt(stop["departure"])
199
+ arr = arr_dep_to_dt(stop["arrival"])
200
+ dep = arr_dep_to_dt(stop["departure"])
150
201
  platform = convert_platform_number(stop["platform"]) or ""
151
202
  parts.append(
152
203
  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]"
153
204
  )
154
205
  return "\n".join(parts)
155
206
 
207
+ def get_station(self, station: str) -> ExtendedBaseStationInfo:
208
+ slug = name_to_slug(station)
209
+ try:
210
+ return self.storage.get_cache(f"st-{slug}") or self.storage.set_cache(
211
+ f"st-{slug}", self.client.get_station_by_slug(slug)
212
+ )
213
+ except self.client.errors.KoleoNotFound:
214
+ self.print(f'[bold red]Station not found: "{station}"[/bold red]')
215
+ exit(2)
156
216
 
157
217
  def main():
158
218
  cli = CLI()
@@ -160,9 +220,9 @@ def main():
160
220
  parser = ArgumentParser("koleo", description="Koleo CLI")
161
221
  parser.add_argument("-c", "--config", help="Custom config path.", default=DEFAULT_CONFIG_PATH)
162
222
  parser.add_argument("--nocolor", help="Disable color output", action="store_true", default=False)
163
- subparsers = parser.add_subparsers(title="actions", required=False)
223
+ subparsers = parser.add_subparsers(title="actions", required=False) # type: ignore
164
224
 
165
- departures = subparsers.add_parser("departures", aliases=["d", "dep"], help="Allows you to list station departures")
225
+ departures = subparsers.add_parser("departures", aliases=["d", "dep", "odjazdy", "o"], help="Allows you to list station departures")
166
226
  departures.add_argument(
167
227
  "station",
168
228
  help="The station name",
@@ -179,7 +239,7 @@ def main():
179
239
  departures.add_argument("-s", "--save", help="save the station as your default one", action="store_true")
180
240
  departures.set_defaults(func=cli.full_departures, pass_=["station", "date"])
181
241
 
182
- arrivals = subparsers.add_parser("arrivals", aliases=["a", "arr"], help="Allows you to list station departures")
242
+ arrivals = subparsers.add_parser("arrivals", aliases=["a", "arr", "przyjazdy", "p"], help="Allows you to list station departures")
183
243
  arrivals.add_argument(
184
244
  "station",
185
245
  help="The station name",
@@ -198,7 +258,7 @@ def main():
198
258
 
199
259
  train_route = subparsers.add_parser(
200
260
  "trainroute",
201
- aliases=["r", "tr", "t"],
261
+ aliases=["r", "tr", "t", "poc", "pociąg"],
202
262
  help="Allows you to show the train's route",
203
263
  )
204
264
  train_route.add_argument("brand", help="The brand name", type=str)
@@ -212,12 +272,56 @@ def main():
212
272
  )
213
273
  train_route.set_defaults(func=cli.train_info, pass_=["brand", "name", "date"])
214
274
 
275
+ stations = subparsers.add_parser("stations", aliases=["s", "find", "f", "stacje"], help="Allows you to find stations by their name")
276
+ stations.add_argument(
277
+ "query",
278
+ help="The station name",
279
+ default=None,
280
+ nargs="?",
281
+ )
282
+ stations.set_defaults(func=cli.find_station, pass_=["query"])
283
+
284
+ connections = subparsers.add_parser(
285
+ "connections",
286
+ aliases=["do", "z", "szukaj", "path"],
287
+ help="Allows you to search for connections from a to b",
288
+ )
289
+ connections.add_argument("start", help="The starting station", type=str)
290
+ connections.add_argument("end", help="The end station", type=str)
291
+ connections.add_argument(
292
+ "-d",
293
+ "--date",
294
+ help="the date",
295
+ type=lambda s: parse_datetime(s),
296
+ default=datetime.now(),
297
+ )
298
+ connections.add_argument(
299
+ "-b",
300
+ "--brands",
301
+ help="Brands to include",
302
+ action="extend", nargs="+", type=str, default=[]
303
+ )
304
+ connections.add_argument(
305
+ "-f",
306
+ "--direct",
307
+ help="whether or not the result should only include direct trains",
308
+ action="store_true", default=False
309
+ )
310
+ connections.add_argument(
311
+ "-p",
312
+ "--purchasable",
313
+ help="whether or not the result should only trains purchasable on koleo",
314
+ action="store_true", default=False
315
+ )
316
+ connections.set_defaults(func=cli.connections, pass_=["start", "end", "brands", "date", "direct" "purchasable"])
317
+
215
318
  args = parser.parse_args()
216
319
 
217
320
  storage = Storage.load(path=args.config)
218
321
  client = KoleoAPI()
219
322
  cli.client, cli.storage = client, storage
220
323
  cli.console.no_color = args.nocolor
324
+ cli.no_color = args.nocolor
221
325
  if hasattr(args, "station") and args.station is None:
222
326
  args.station = storage.favourite_station
223
327
  elif hasattr(args, "station") and args.save:
koleo/storage.py CHANGED
@@ -1,9 +1,10 @@
1
1
  import typing as t
2
- from time import time
3
- from dataclasses import dataclass, asdict, field
4
- from sys import platform
2
+ from dataclasses import asdict, dataclass, field
5
3
  from json import dump, load
6
- from os import makedirs, path as ospath
4
+ from os import makedirs
5
+ from os import path as ospath
6
+ from sys import platform
7
+ from time import time
7
8
 
8
9
 
9
10
  def get_adequate_config_path():
@@ -42,7 +43,7 @@ class Storage:
42
43
  else:
43
44
  data = {}
44
45
  storage = cls(**data)
45
- storage._path = path
46
+ storage._path = expanded
46
47
  return storage
47
48
 
48
49
  def get_cache(self, id: str) -> t.Any | None:
@@ -62,10 +63,9 @@ class Storage:
62
63
  return item
63
64
 
64
65
  def save(self):
65
- expanded = ospath.expanduser(self._path)
66
- dir = ospath.dirname(expanded)
66
+ dir = ospath.dirname(self._path)
67
67
  if dir:
68
68
  if not ospath.exists(dir):
69
69
  makedirs(dir)
70
- with open(expanded, "w+") as f:
71
- dump(asdict(self), f)
70
+ with open(self._path, "w+") as f:
71
+ dump(asdict(self), f, indent=True)
koleo/types.py CHANGED
@@ -12,6 +12,7 @@ class ExtendedBaseStationInfo(BaseStationInfo):
12
12
  hits: int
13
13
  version: str # "A", "B"
14
14
  is_group: bool
15
+ city: str | None
15
16
  region: str
16
17
  country: str
17
18
  latitude: float
@@ -175,3 +176,9 @@ class TrainStop(t.TypedDict):
175
176
  class TrainDetailResponse(t.TypedDict):
176
177
  train: TrainDetail
177
178
  stops: list[TrainStop]
179
+
180
+
181
+ class ConnectiontrainDetail(TrainDetail):
182
+
183
+ arrival: TimeDict | str # WTF KOLEO!!!!
184
+ departure: TimeDict | str # WTF KOLEO!!!!
koleo/utils.py CHANGED
@@ -1,11 +1,11 @@
1
- from datetime import datetime, time
1
+ from datetime import datetime, time, timedelta
2
2
 
3
3
  from .types import TimeDict
4
4
 
5
5
 
6
6
  def parse_datetime(s: str):
7
- now = datetime.today()
8
7
  try:
8
+ now = datetime.now()
9
9
  dt = datetime.strptime(s, "%d-%m")
10
10
  return dt.replace(year=now.year, hour=0, minute=0)
11
11
  except ValueError:
@@ -14,12 +14,16 @@ def parse_datetime(s: str):
14
14
  return datetime.strptime(s, "%Y-%m-%d").replace(hour=0, minute=0)
15
15
  except ValueError:
16
16
  pass
17
- return datetime.combine(now, datetime.strptime(s, "%H:%M").time())
17
+ if s[0] == "+":
18
+ return datetime.now().replace(hour=0, minute=0) + timedelta(days=int(s[1:]))
19
+ return datetime.combine(datetime.now(), datetime.strptime(s, "%H:%M").time())
18
20
 
19
21
 
20
- def time_dict_to_dt(s: TimeDict):
22
+ def arr_dep_to_dt(i: TimeDict | str):
23
+ if isinstance(i, str):
24
+ return datetime.fromisoformat(i)
21
25
  now = datetime.today()
22
- return datetime.combine(now, time(s["hour"], s["minute"], s["second"]))
26
+ return datetime.combine(now, time(i["hour"], i["minute"], i["second"]))
23
27
 
24
28
 
25
29
  TRANSLITERATIONS = {
@@ -52,8 +56,8 @@ NUMERAL_TO_ARABIC = {
52
56
  "VIII": 8,
53
57
  "IX": 9,
54
58
  "X": 10,
55
- "XI": 11, # wtf poznań???
56
- "XII": 12 # just to be safe
59
+ "XI": 11, # wtf poznań???
60
+ "XII": 12, # just to be safe
57
61
  }
58
62
 
59
63
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: koleo-cli
3
- Version: 0.2.137.1
3
+ Version: 0.2.137.3
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=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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (74.1.2)
2
+ Generator: setuptools (75.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,12 +0,0 @@
1
- koleo/__init__.py,sha256=ChPpKyc4vrcgWj-Uk_ZlDw32s_7iFpE3f1FTAd6zxPY,51
2
- koleo/__main__.py,sha256=wu5N2wk8mvBgyvr2ghmQf4prezAe0_i-p123VVreyYc,62
3
- koleo/api.py,sha256=uzaOSD_voMRoIxKRuh-RqGKGcJDyAUh3UtOvchW-skc,5052
4
- koleo/cli.py,sha256=ARSDB6bLYf0c2gSkY_A_b4BoniaMBamCfCUByZsSl5o,9365
5
- koleo/storage.py,sha256=uCh6edwizAuw1z_Ti5AXvDan2pJAJBSobCVmYw096F8,2015
6
- koleo/types.py,sha256=8mAsRdNh3jMJwAV5KnHVvDQAekhs0N70EVem4p_w18o,3760
7
- koleo/utils.py,sha256=vywC_2q_quCdk23GPYXzm6oteCYwHcS3AVwK2pYv1NQ,1224
8
- koleo_cli-0.2.137.1.dist-info/METADATA,sha256=MXri6goTJkWZs2XgcsdSm-MHie9-53nWXCleo4XVIJU,626
9
- koleo_cli-0.2.137.1.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
10
- koleo_cli-0.2.137.1.dist-info/entry_points.txt,sha256=LtCidkVDq8Zd7-fxpRbys1Xa9LTHMZwXVbdcQEscdes,41
11
- koleo_cli-0.2.137.1.dist-info/top_level.txt,sha256=AlWdXotkRYzHpFfOBYi6xOXl1H0zq4-tqtZ2XivoWB4,6
12
- koleo_cli-0.2.137.1.dist-info/RECORD,,