1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
import datetime
import xml.etree.ElementTree as ET
import re
def parse(xml_menu: str) -> dict[datetime.date, str]:
"""Parse the given xml menu and return a (date -> entry) map."""
root = ET.fromstring(xml_menu)
item_nodes: list[ET.Element] = root.findall("./channel/item")
def getchildren(node: ET.Element) -> tuple[ET.Element, ET.Element]:
title = node.find("title")
desc = node.find("description")
if title is None or desc is None:
raise ValueError("title and description of item should be defined")
return (title, desc)
item_children: map[tuple[ET.Element, ET.Element]] = map(getchildren, item_nodes)
def gettext(node: ET.Element) -> str:
text = node.text
if text is None:
raise ValueError(f"{node.tag} should contain text")
return text
items = map(lambda item: (parse_weekday_date(gettext(item[0])), gettext(item[1])), item_children)
return dict(items)
def parse_from_file(xml_file_path: str) -> dict[datetime.date, str]:
"""Parse the menu from the given xml file and return a (date -> entry) map.
Throws an exception if file reading is not succesful."""
with open(xml_file_path, 'r', encoding="utf-8") as f:
xml_menu = f.read()
return parse(xml_menu)
def parse_weekday_date(date: str) -> datetime.date:
"""Parse the given date of the form 'WD DD.MM.YYYY' and return the corresponding date object.
The weekday should be in finnish format.
>>> parse_weekday_date("su 26.1.2026")
datetime.date(2026, 1, 26)
Returns a string indicating an error if no weekday
is present or if the date is of the wrong format.
>>> parse_weekday_date("mon 16.2.2026")
'päiväyksen ensimmäinen sanan pitäisi olla viikonpäivän lyhenne'
Parsing the date after the weekday is done as in `parse_date`.
"""
(head, tail) = date.split(maxsplit = 1)
if head not in ["ma", "ti", "ke", "to", "pe", "la", "su"]:
raise ValueError("päiväyksen ensimmäinen sanan pitäisi olla viikonpäivän lyhenne")
return parse_date(tail)
def parse_date(date: str) -> datetime.date:
"""Parse the given date of the form 'DD.MM.YYYY' and return the corresponding date object.
>>> parse_date("16.2.2026")
datetime.date(2026, 2, 16)
Returns a string indicating an error if the date is of the wrong format.
>>> parse_date("bogus")
'päiväyksen tulee olla muotoa PP.KK.VVVV'
Nonexistent dates are handled:
>>> parse_date("31.2.2024")
'päivän tulee olla olemassa ollut tai oleva päivä väliltä 01.01.0001-31.12.9999'
"""
date_pat = re.compile("([0-9]{1,2})\\.([0-9]{1,2})\\.([0-9]{4})")
date_match = date_pat.fullmatch(date)
if date_match is None:
raise ValueError("päiväyksen tulee olla muotoa PP.KK.VVVV")
(day, month, year) = date_match.groups()
try:
date_ = datetime.date(int(year), int(month), int(day))
return date_
except:
raise ValueError(f"päivän tulee olla olemassa ollut tai oleva päivä väliltä 01.01.{datetime.MINYEAR:04d}-31.12.{datetime.MAXYEAR}")
if __name__ == "__main__":
import doctest
doctest.testmod()
|