From 134f3dedb2b6ec245f458c2d30baaa80e6f187af Mon Sep 17 00:00:00 2001 From: Reto Bollinger Date: Wed, 19 Mar 2025 16:28:37 +0100 Subject: [PATCH] Stupid copy and paste of scripts to support french. Btw. docker image needs to be recreated --- lektor/docker/Dockerfile | 1 + lektor/docker/entrypoint.sh | 6 + .../lektordata/project/content/contents+fr.lr | 2 +- .../project/content/termine/contents+fr.lr | 251 ++++++++++++++++++ .../lektordata/project/templates/layout.html | 2 +- .../lektordata/scripts/calendar-fetcher+fr.py | 57 ++++ .../scripts/calendar-fetcher-main+fr.py | 142 ++++++++++ .../scripts/calendar-fetcher-main.py | 2 +- lektor/lektordata/scripts/calendar-fetcher.py | 2 +- lektor/lektordata/scripts/calendarstuff.py | 4 +- 10 files changed, 463 insertions(+), 6 deletions(-) create mode 100644 lektor/lektordata/project/content/termine/contents+fr.lr create mode 100644 lektor/lektordata/scripts/calendar-fetcher+fr.py create mode 100644 lektor/lektordata/scripts/calendar-fetcher-main+fr.py diff --git a/lektor/docker/Dockerfile b/lektor/docker/Dockerfile index 5324fab..db2cd41 100644 --- a/lektor/docker/Dockerfile +++ b/lektor/docker/Dockerfile @@ -5,6 +5,7 @@ RUN apt install -y python3-pip python3-venv pipx curl locales RUN pipx install lektor RUN mkdir -p /opt/lektor/project && mkdir -p /opt/lektor/output RUN sed -i '/de_DE.UTF-8/s/^# //g' /etc/locale.gen && locale-gen +RUN sed -i '/fr_FR.UTF-8/s/^# //g' /etc/locale.gen && locale-gen RUN python3 -m venv /opt/venv diff --git a/lektor/docker/entrypoint.sh b/lektor/docker/entrypoint.sh index 114b848..e3b9c23 100644 --- a/lektor/docker/entrypoint.sh +++ b/lektor/docker/entrypoint.sh @@ -8,4 +8,10 @@ tempfile=$(mktemp) sh -c ". /opt/venv/bin/activate && exec python /opt/lektor/scripts/calendar-fetcher-main.py ${CALENDAR_URL} > $tempfile" mv $tempfile /opt/lektor/project/content/contents.lr +sh -c ". /opt/venv/bin/activate && exec python /opt/lektor/scripts/calendar-fetcher+fr.py ${CALENDAR_URL} > /opt/lektor/project/content/termine/contents+fr.lr" +# TODO As the file reads from the same file as it's output is afterwards piped into this leads to synchronization/buffering issues, we therefore write to a temporaray file and move it to the right place in a subsequent step +tempfile=$(mktemp) +sh -c ". /opt/venv/bin/activate && exec python /opt/lektor/scripts/calendar-fetcher-main+fr.py ${CALENDAR_URL} > $tempfile" +mv $tempfile /opt/lektor/project/content/contents+fr.lr + exec "$@" \ No newline at end of file diff --git a/lektor/lektordata/project/content/contents+fr.lr b/lektor/lektordata/project/content/contents+fr.lr index 04fbdaf..0282f32 100644 --- a/lektor/lektordata/project/content/contents+fr.lr +++ b/lektor/lektordata/project/content/contents+fr.lr @@ -5,7 +5,7 @@ title: Bienvenue au PC Stammertal html:

Notre prochain événement:


-
Samstag 22. März 9:00, Frühlingsschiessen, Bülach
+
samedi 22. mars 9:00, Frühlingsschiessen, Bülach
 
 
diff --git a/lektor/lektordata/project/content/termine/contents+fr.lr b/lektor/lektordata/project/content/termine/contents+fr.lr new file mode 100644 index 0000000..cd5d7c2 --- /dev/null +++ b/lektor/lektordata/project/content/termine/contents+fr.lr @@ -0,0 +1,251 @@ +_model: page +--- +title: Événements +--- +body: + +*
Frühlingsschiessen
  + *
sam. 22. mars 2025
  + *
9:00
  + *
Bülach
  +*
Standreinigung
  + *
sam. 22. mars 2025
  + *
9:00
  + *
 
  +*
freies Training
  + *
lun. 31. mars 2025
  + *
18:00
  + *
Stammheim
  +*
Eulachschiessen
  + *
ven. 4. avril 2025
  + *
16:00
  + *
Winterthur
  +*
Eulachschiessen
  + *
sam. 5. avril 2025
  + *
8:30
  + *
Winterthur
  +*
Eulachschiessen
  + *
ven. 11. avril 2025
  + *
16:00
  + *
Winterthur
  +*
Eulachschiessen
  + *
sam. 12. avril 2025
  + *
8:30
  + *
Winterthur
  +*
Kreis Winterschiessen
  + *
lun. 14. avril 2025
  + *
18:00
  + *
Stammheim
  +*
Obligatorisches Programm und Kreis Winterschiessen
  + *
lun. 28. avril 2025
  + *
18:00
  + *
Stammheim
  +*
freies Training
  + *
lun. 5. mai 2025
  + *
18:30
  + *
Stammheim
  +*
freies Training
  + *
lun. 12. mai 2025
  + *
18:30
  + *
Stammheim
  +*
Schlossschiessen
  + *
jeu. 15. mai 2025
  + *
17:30
  + *
Wülflingen
  +*
Schlossschiessen
  + *
ven. 16. mai 2025
  + *
17:30
  + *
Wülflingen
  +*
Schlossschiessen
  + *
sam. 17. mai 2025
  + *
9:30
  + *
Wülflingen
  +*
Obligatorisches Programm und freies Training
  + *
lun. 19. mai 2025
  + *
18:30
  + *
Stammheim
  +*
Schlossschiessen
  + *
jeu. 22. mai 2025
  + *
17:30
  + *
Wülflingen
  +*
Schlossschiessen
  + *
ven. 23. mai 2025
  + *
17:30
  + *
Wülflingen
  +*
freies Training
  + *
lun. 26. mai 2025
  + *
18:30
  + *
Stammheim
  +*
Kreis Sommerschiessen
  + *
lun. 2. juin 2025
  + *
18:30
  + *
Stammheim
  +*
Sommer-Schüsse
  + *
ven. 6. juin 2025
  + *
17:00
  + *
Rafz
  +*
Sommer-Schüsse
  + *
sam. 14. juin 2025
  + *
9:00
  + *
Rafz
  +*
Obligatorisches Programm und freies Training
  + *
lun. 16. juin 2025
  + *
18:30
  + *
Stammheim
  +*
freies Training
  + *
lun. 23. juin 2025
  + *
18:30
  + *
Stammheim
  +*
freies Training
  + *
lun. 30. juin 2025
  + *
18:30
  + *
Stammheim
  +*
Bezirks Sommerschiessen
  + *
sam. 5. juillet 2025
  + *
17:00
  + *
Flurlingen
  +*
freies Training
  + *
lun. 7. juillet 2025
  + *
18:30
  + *
Stammheim
  +*
Terassenfest
  + *
ven. 11. juillet 2025
  + *
19:00
  + *
Stammheim
  +*
Bezirks Sommerschiessen
  + *
sam. 12. juillet 2025
  + *
14:00
  + *
Flurlingen
  +*
freies Training
  + *
lun. 14. juillet 2025
  + *
18:30
  + *
Stammheim
  +*
Obligatorisches Programm und freies Training
  + *
lun. 11. août 2025
  + *
18:30
  + *
Stammheim
  +*
ZHKSF Ausbildung Pistole
  + *
jeu. 14. août 2025
  + *
18:30
  + *
 
  +*
Zürcher Kantonalschützenfest
  + *
ven. 15. août 2025
  + *
 
  + *
 
  +*
Zürcher Kantonalschützenfest
  + *
sam. 16. août 2025
  + *
 
  + *
 
  +*
Zürcher Kantonalschützenfest
  + *
dim. 17. août 2025
  + *
 
  + *
 
  +*
KEIN Training
  + *
lun. 18. août 2025
  + *
 
  + *
 
  +*
ZHKSF intern
  + *
jeu. 21. août 2025
  + *
18:30
  + *
 
  +*
Zürcher Kantonalschützenfest
  + *
ven. 22. août 2025
  + *
 
  + *
 
  +*
Zürcher Kantonalschützenfest
  + *
sam. 23. août 2025
  + *
 
  + *
 
  +*
Zürcher Kantonalschützenfest
  + *
dim. 24. août 2025
  + *
 
  + *
 
  +*
Zürcher Kantonalschützenfest
  + *
lun. 25. août 2025
  + *
 
  + *
 
  +*
Zürcher Kantonalschützenfest
  + *
ven. 29. août 2025
  + *
 
  + *
 
  +*
Zürcher Kantonalschützenfest
  + *
sam. 30. août 2025
  + *
 
  + *
 
  +*
Zürcher Kantonalschützenfest
  + *
dim. 31. août 2025
  + *
 
  + *
 
  +*
freies Training
  + *
lun. 1. septembre 2025
  + *
18:30
  + *
Stammheim
  +*
Kreismatch
  + *
lun. 8. septembre 2025
  + *
18:00
  + *
Stammheim
  +*
Orientierungslauf Stammerberg (kein Schiessbetrieb!)
  + *
sam. 13. septembre 2025
  + *
 
  + *
Unterstammheim
  +*
Kreismatch
  + *
lun. 15. septembre 2025
  + *
18:00
  + *
Stammheim
  +*
Schwaderlohschiessen
  + *
sam. 20. septembre 2025
  + *
13:30
  + *
Alterswilen
  +*
freies Training
  + *
lun. 22. septembre 2025
  + *
18:00
  + *
Stammheim
  +*
Schwaderlohschiessen (unser Schiesstag)
  + *
mar. 23. septembre 2025
  + *
17:30
  + *
Alterswilen
  +*
Schwaderlohschiessen
  + *
sam. 27. septembre 2025
  + *
8:00
  + *
Alterswilen
  +*
Schwaderlohschiessen
  + *
dim. 28. septembre 2025
  + *
10:00
  + *
Alterswilen
  +*
Endschiessen
  + *
lun. 29. septembre 2025
  + *
17:00
  + *
Stammheim
  +*
Endschiessen
  + *
lun. 6. octobre 2025
  + *
17:00
  + *
Stammheim
  +*
Niklausschiessen
  + *
sam. 25. octobre 2025
  + *
9:00
  + *
Diessenhofen
  +*
Niklausschiessen
  + *
dim. 26. octobre 2025
  + *
9:00
  + *
Diessenhofen
  +*
Absenden
  + *
ven. 31. octobre 2025
  + *
19:00
  + *
 
  +*
Niklausschiessen
  + *
sam. 1. novembre 2025
  + *
9:00
  + *
Diessenhofen
  +*
Appenzeller Lupi Meisterschaft
  + *
dim. 11. janvier 2026
  + *
9:00
  + *
 
  +*
Generalversammlung
  + *
jeu. 5. février 2026
  + *
19:00
  + *
 
  + +--- +_template: page.html + + diff --git a/lektor/lektordata/project/templates/layout.html b/lektor/lektordata/project/templates/layout.html index df56f54..481b769 100644 --- a/lektor/lektordata/project/templates/layout.html +++ b/lektor/lektordata/project/templates/layout.html @@ -39,7 +39,7 @@ ['/termine', 'Événements'], ['/vorstand', 'Direction'], ['/about', 'À propos'], - ['/kontakt', 'Contactez nous'] + ['/kontakt', 'Contactez-nous'] ] } %} diff --git a/lektor/lektordata/scripts/calendar-fetcher+fr.py b/lektor/lektordata/scripts/calendar-fetcher+fr.py new file mode 100644 index 0000000..3a3f313 --- /dev/null +++ b/lektor/lektordata/scripts/calendar-fetcher+fr.py @@ -0,0 +1,57 @@ +from datetime import datetime, date, timezone +import locale +import sys +from calendarstuff import get_events + + +def fetch_upcoming_events(ics_url): + + events = get_events(ics_url, 'fr_FR.UTF-8') + + for event in events: + + start = event.get('dtstart').dt + out_summary = event.get('summary') + location = event.get('location', 'No location specified') + + out_startdate = start.strftime("%a %-d. %B %Y") + + # Format output based on whether it's an all-day event + if isinstance(start, date) and not isinstance(start, datetime): + out_starttime = " " + else: + out_starttime = start.strftime('%-H:%M') + + if location != 'No location specified': + out_location = location + else: + out_location = " " + + print(f"*
{out_summary}
 ") + print(f" *
{out_startdate}
 ") + print(f" *
{out_starttime}
 ") + print(f" *
{out_location}
 ") + + +if __name__ == "__main__": + + ics_url = sys.argv[1] + + # ics_url = "https://backoffice.pc-stammertal.ch/remote.php/dav/public-calendars/RqLX5wj25aY6cpnP?export" + + print("_model: page") + print("---") + print("title: Événements") + print("---") + print("body:") + print("") + # print("
Kalender Abonnieren
") + # print("") + # print("
\"Link
") + # print("") + fetch_upcoming_events(ics_url) + print("") + print("---") + print("_template: page.html") + print("") + print("") \ No newline at end of file diff --git a/lektor/lektordata/scripts/calendar-fetcher-main+fr.py b/lektor/lektordata/scripts/calendar-fetcher-main+fr.py new file mode 100644 index 0000000..66978dd --- /dev/null +++ b/lektor/lektordata/scripts/calendar-fetcher-main+fr.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 + +import sys +from pathlib import Path +from datetime import datetime, date, timezone +from typing import Optional, NamedTuple +from dataclasses import dataclass +from bs4 import BeautifulSoup +import locale +from calendarstuff import get_events + + +@dataclass +class EventDetails: + weekday: str + date: str + time: str + summary: str + location: str + + def to_html(self) -> str: + return ( + f'
{self.weekday} ' + f'{self.date}{self.time}, {self.summary}' + f'{self.location}
' + ) + +class EventProcessor: + def __init__(self, ics_url: str, content_file: str): + self.ics_url = ics_url + self.content_file = Path(content_file) + self.fallback_html = ( + '
Malheureusement inconnu, mais ' + 'demande à la direction qui devrait le savoir
' + ) + + @property + def fallback_content(self) -> str: + return f"""_model: htmlpage +--- +title: Bienvenue au PC Stammertal +--- +html: + +

Notre prochain événement:


+{self.fallback_html} +
+  
+  
+  
+
+

...et ne manquez pas non plus :

+27. Zürcher Kantonalschützenfest 2025 + +--- +_template: + +page.html +""" + + def setup_locale(self) -> None: + try: + locale.setlocale(locale.LC_TIME, 'de_DE.UTF-8') + except locale.Error as e: + print(f"Warning: Failed to set locale: {e}", file=sys.stderr) + + def format_event_time(self, start: datetime | date) -> str: + return "" if isinstance(start, date) and not isinstance(start, datetime) else start.strftime(" %-H:%M") + + def get_next_event(self) -> Optional[EventDetails]: + try: + + events = get_events(self.ics_url, 'fr_FR.UTF-8') + + event = events[0] + start = event.get('dtstart').dt + + return EventDetails( + weekday=start.strftime("%A"), + date=start.strftime("%-d. %B"), + time=self.format_event_time(start), + summary=event.get('summary', ''), + location=f", {event.get('location')}" if event.get('location') else "" + ) + + except (StopIteration, AttributeError) as e: + print(f"No upcoming events found: {e}", file=sys.stderr) + return None + + def read_current_content(self) -> tuple[str, str]: + try: + content = self.content_file.read_text() + soup = BeautifulSoup(content, 'html.parser') + events = soup.find_all('div', {'class': 'nextevent'}) + return content, str(events[0]) if len(events) == 1 else "" + + except (IOError, IndexError) as e: + print(f"Error reading content file: {e}", file=sys.stderr) + return "", "" + + def process(self) -> str: + self.setup_locale() + + content, source_str = self.read_current_content() + if not content: + return self.fallback_content + + if len(source_str) > 0 and source_str in content: + event = self.get_next_event() + if not event: + return content.replace(source_str, self.fallback_html).rstrip() + + return content.replace(source_str, event.to_html()).rstrip() + + return self.fallback_content + +def main() -> None: + if len(sys.argv) != 2: + print(f"Usage: {sys.argv[0]} ", file=sys.stderr) + sys.exit(1) + + processor = EventProcessor( + ics_url=sys.argv[1], + content_file="/opt/lektor/project/content/contents+fr.lr" + ) + + print(processor.process(), end='') + print("") + print("") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/lektor/lektordata/scripts/calendar-fetcher-main.py b/lektor/lektordata/scripts/calendar-fetcher-main.py index d259c9c..95b3d41 100644 --- a/lektor/lektordata/scripts/calendar-fetcher-main.py +++ b/lektor/lektordata/scripts/calendar-fetcher-main.py @@ -80,7 +80,7 @@ page.html def get_next_event(self) -> Optional[EventDetails]: try: - events = get_events(self.ics_url) + events = get_events(self.ics_url, 'de_DE.UTF-8') event = events[0] start = event.get('dtstart').dt diff --git a/lektor/lektordata/scripts/calendar-fetcher.py b/lektor/lektordata/scripts/calendar-fetcher.py index 85b3e7f..eb401e6 100644 --- a/lektor/lektordata/scripts/calendar-fetcher.py +++ b/lektor/lektordata/scripts/calendar-fetcher.py @@ -6,7 +6,7 @@ from calendarstuff import get_events def fetch_upcoming_events(ics_url): - events = get_events(ics_url) + events = get_events(ics_url, 'de_DE.UTF-8') for event in events: diff --git a/lektor/lektordata/scripts/calendarstuff.py b/lektor/lektordata/scripts/calendarstuff.py index d844994..9860ff0 100644 --- a/lektor/lektordata/scripts/calendarstuff.py +++ b/lektor/lektordata/scripts/calendarstuff.py @@ -56,9 +56,9 @@ def split_multiday_events(events): return split_events -def get_events(ics_url): +def get_events(ics_url, tgt_locale): # Set German locale - locale.setlocale(locale.LC_TIME, 'de_DE.UTF-8') + locale.setlocale(locale.LC_TIME, tgt_locale) response = requests.get(ics_url) calendar = Calendar.from_ical(response.content)