Skip to content

Commit 97f0caf

Browse files
committed
Parse date-/time-range expressions using the Aika library
https://github.com/panodata/aika
1 parent 033f736 commit 97f0caf

File tree

7 files changed

+60
-54
lines changed

7 files changed

+60
-54
lines changed

.github/workflows/main.yml

+5
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ jobs:
3535
- name: Acquire sources
3636
uses: actions/checkout@v3
3737

38+
- name: Generate locales
39+
if: runner.os == 'Linux'
40+
run: |
41+
sudo apt-get update && sudo apt-get install --yes tzdata locales && sudo locale-gen de_DE.UTF-8
42+
3843
- name: Set up Python
3944
uses: actions/setup-python@v4
4045
with:

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@
1010
- Fix return journey wrt. arrival time
1111
- Process HAFAS remarks
1212
- Add colored terminal output
13+
- Parse date-/time-range expressions using the Aika library

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ and further stops on the trip, and computes travel plan drafts using the [HAFAS]
1919
rex9 travel --from=Oranienburg --to=Stralsund --stops=Fürstenberg,Mildenberg --when=do-di
2020
```
2121

22+
The `--when` argument accepts all date-/time-range expressions as provided by the [Aika]
23+
package, which currently supports English and German.
24+
2225

2326
## Glossary
2427

@@ -46,4 +49,5 @@ poe check
4649
```
4750

4851

52+
[Aika]: https://github.com/panodata/aika
4953
[HAFAS]: https://de.wikipedia.org/wiki/HAFAS

pyproject.toml

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ classifiers = [
4747
]
4848

4949
dependencies = [
50+
"aika==0.1",
5051
"click<=9",
5152
"colorlog<7",
5253
"markdown2<3",
@@ -72,6 +73,7 @@ release = [
7273
]
7374
test = [
7475
"coverage<8",
76+
"freezegun<1.3",
7577
"pytest<8",
7678
]
7779
[tool.setuptools]

rex9/core.py

+4-7
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
import typing as t
77
from textwrap import indent
88

9+
from aika import DaterangeExpression
910
from pyhafas import HafasClient
1011
from pyhafas.profile import DBProfile
1112
from pyhafas.types.fptf import Remark
1213

1314
from rex9.model import TimeMode, TravelJourney, TravelJourneySegment, TravelPlan, TravelPlanSegment
14-
from rex9.util.cli import split_list
15-
from rex9.util.date import format_date_weekday, format_time, next_date_by_weekday
15+
from rex9.util.date import format_date_weekday, format_time
1616
from rex9.util.format import CliFormatter as cf
1717
from rex9.util.pyhafas import query_for
1818

@@ -41,11 +41,8 @@ def make_travelplan(origin: str, destination: str, stops: t.List[str], when: str
4141
location_remote = client.locations(destination)[0]
4242

4343
# Resolve departure and arrival times of home and remote locations.
44-
on_begin, on_end = split_list(when, delimiter="-")
45-
date_begin = next_date_by_weekday(on_begin)
46-
date_end = next_date_by_weekday(on_end, start=date_begin)
47-
datetime_begin = dt.datetime.combine(date_begin, DEFAULT_DEPARTURE_TIME)
48-
datetime_end = dt.datetime.combine(date_end, DEFAULT_ARRIVAL_TIME)
44+
dr = DaterangeExpression(default_start_time=DEFAULT_DEPARTURE_TIME, default_end_time=DEFAULT_ARRIVAL_TIME)
45+
datetime_begin, datetime_end = dr.parse(when)
4946

5047
# Report an intermediate summary.
5148
logger.info(

rex9/util/date.py

-28
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,6 @@
44
import datetime as dt
55
import typing as t
66

7-
import dateutil.relativedelta as dtrel
8-
9-
10-
class WeekdayMap:
11-
days_en = ["MO", "TU", "WE", "TH", "FR", "SA", "SU"]
12-
days_de = ["MO", "DI", "MI", "DO", "FR", "SA", "SO"]
13-
14-
def __init__(self):
15-
self.weekdays = {}
16-
for index, day in enumerate(self.days_en):
17-
self.weekdays[day] = dtrel.weekday(index)
18-
for index, day in enumerate(self.days_de):
19-
self.weekdays[day] = dtrel.weekday(index)
20-
21-
def find(self, label: str):
22-
for key, value in self.weekdays.items():
23-
if key.lower().startswith(label.lower()):
24-
return value
25-
raise KeyError(f"Weekday not found: {label}")
26-
27-
28-
def next_date_by_weekday(weekday: str, start=None):
29-
start = start or dt.date.today()
30-
weekday_map = WeekdayMap()
31-
weekday_dtrel = weekday_map.find(weekday)
32-
delta = dtrel.relativedelta(days=1, weekday=weekday_dtrel)
33-
return start + delta
34-
357

368
def format_date_weekday(datetime: dt.datetime):
379
return f"{datetime} ({datetime.strftime('%A')})"

tests/test_util_date.py

+44-19
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,47 @@
33

44
import datetime as dt
55

6-
import pytest
7-
8-
from rex9.util.date import next_date_by_weekday
9-
10-
11-
def test_weekdaymap_de():
12-
date_value = next_date_by_weekday("do")
13-
assert isinstance(date_value, dt.date)
14-
15-
16-
def test_weekdaymap_en():
17-
date_value = next_date_by_weekday("su")
18-
assert isinstance(date_value, dt.date)
19-
20-
21-
def test_weekdaymap_fail():
22-
with pytest.raises(KeyError) as ex:
23-
next_date_by_weekday("foo")
24-
assert ex.match("Weekday not found: foo")
6+
from aika import DaterangeExpression
7+
from freezegun import freeze_time
8+
9+
TESTDRIVE_DATETIME = "2023-08-17T23:03:17+0200"
10+
11+
12+
@freeze_time(TESTDRIVE_DATETIME)
13+
def test_parse_daterange_relative_months():
14+
dr = DaterangeExpression(
15+
default_start_time=dt.time(hour=9),
16+
default_end_time=dt.time(hour=17),
17+
)
18+
assert (
19+
dr.parse("next March")
20+
== dr.parse("im März")
21+
== (
22+
dt.datetime(2023, 3, 1, 9, 0, 0),
23+
dt.datetime(2023, 3, 31, 17, 0, 0),
24+
)
25+
)
26+
assert (
27+
dr.parse("July to December")
28+
== dr.parse("Juli bis Dezember")
29+
== (
30+
dt.datetime(2023, 7, 1, 9, 0, 0),
31+
dt.datetime(2023, 12, 31, 17, 0, 0),
32+
)
33+
)
34+
35+
36+
@freeze_time(TESTDRIVE_DATETIME)
37+
def test_parse_daterange_relative_weekdays():
38+
dr = DaterangeExpression(
39+
default_start_time=dt.time(hour=9),
40+
default_end_time=dt.time(hour=17),
41+
)
42+
assert (
43+
dr.parse("Sat - Tue")
44+
== dr.parse("Sa-Di")
45+
== (
46+
dt.datetime(2023, 8, 19, 9, 0, 0),
47+
dt.datetime(2023, 8, 22, 17, 0, 0),
48+
)
49+
)

0 commit comments

Comments
 (0)