summaryrefslogtreecommitdiff
path: root/src/menu/parse.py
blob: fb45099f1686cfc0d12d28d471d5c3f16a50873c (plain)
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()