from datetime import datetime, timedelta from html import escape from typing import Any, TypedDict from urllib.parse import quote WIKI_ARTICLE_BASE = "https://www.thomas-krenn.com/de/wiki/" WIKI_HOME_URL = "https://www.thomas-krenn.com/de/wiki/Hauptseite" ORG_NAME = "Thomas-Krenn.AG" LINE = "=" * 78 SUBLINE = "-" * 78 class DisplayOptions(TypedDict): show_date: bool show_user: bool show_category: bool DEFAULT_DISPLAY: DisplayOptions = { "show_date": True, "show_user": True, "show_category": True, } def parse_display_options( show_date: str | None = None, show_user: str | None = None, show_category: str | None = None, ) -> DisplayOptions: return { "show_date": show_date == "true", "show_user": show_user == "true", "show_category": show_category == "true", } def filter_articles( articles: list[dict[str, Any]], category_filter: str | None = None, ) -> list[dict[str, Any]]: if not category_filter: return articles wanted = category_filter.strip().lower() return [ a for a in articles if any(c.lower() == wanted for c in a.get("categories", [])) ] def split_articles_by_type(articles: list[dict[str, Any]]) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: sorted_articles = sorted(articles, key=lambda a: a.get("timestamp", ""), reverse=True) new_articles: list[dict[str, Any]] = [] edited_articles: list[dict[str, Any]] = [] new_titles: set[str] = set() for item in sorted_articles: title = item.get("title", "") if not title or item.get("type") != "new": continue if title not in new_titles: new_articles.append(item) new_titles.add(title) seen_edited: set[str] = set() for item in sorted_articles: title = item.get("title", "") if not title or title in new_titles or title in seen_edited: continue edited_articles.append(item) seen_edited.add(title) return new_articles, edited_articles def create_subject(days: int, category: str = "") -> str: base = f"Interner Wiki-Newsletter – {ORG_NAME} ({days} Tage)" if category.strip(): return f"{base} | Kategorie: {category.strip()}" return base def create_plain_text( articles: list[dict[str, Any]], days: int, display: DisplayOptions | None = None, category: str = "", ) -> str: display = display or DEFAULT_DISPLAY new_articles, edited_articles = split_articles_by_type(articles) period_start, period_end = _period_range(days) created_at = datetime.now().strftime("%d.%m.%Y %H:%M") lines = [ LINE, f"{ORG_NAME.upper()} – INTERNER WIKI-NEWSLETTER", LINE, f"Zeitraum: {period_start} bis {period_end} ({days} Tage)", f"Erstellt am: {created_at}", ] if category.strip(): lines.append(f"Kategorie: {category.strip()}") lines.extend( [ "", "Liebe Kolleginnen und Kollegen,", "", "im Thomas-Krenn-Wiki wurden im ausgewählten Zeitraum folgende Artikel", "neu veröffentlicht bzw. aktualisiert:", "", ] ) if not new_articles and not edited_articles: lines.extend( [ SUBLINE, "HINWEIS", SUBLINE, "Im gewählten Zeitraum wurden keine passenden Wiki-Änderungen gefunden.", "", _plain_footer(new_articles, edited_articles), ] ) return "\n".join(lines).strip() lines.extend(_plain_section("NEUE ARTIKEL", new_articles, display, empty_text="Keine neuen Artikel im Zeitraum.")) lines.append("") lines.extend( _plain_section( "BEARBEITETE ARTIKEL", edited_articles, display, empty_text="Keine bearbeiteten Artikel im Zeitraum.", ) ) lines.append("") lines.append(_plain_footer(new_articles, edited_articles)) return "\n".join(lines).strip() def create_html( articles: list[dict[str, Any]], days: int, display: DisplayOptions | None = None, category: str = "", ) -> str: display = display or DEFAULT_DISPLAY new_articles, edited_articles = split_articles_by_type(articles) period_start, period_end = _period_range(days) created_at = datetime.now().strftime("%d.%m.%Y %H:%M") subject = escape(create_subject(days, category)) category_row = "" if category.strip(): category_row = f'
Kategorie: {escape(category.strip())}
' if not new_articles and not edited_articles: body = 'Im gewählten Zeitraum wurden keine passenden Wiki-Änderungen gefunden.
' else: body = "\n".join( [ _html_section("Neue Artikel", new_articles, display, "Keine neuen Artikel im Zeitraum."), _html_section("Bearbeitete Artikel", edited_articles, display, "Keine bearbeiteten Artikel im Zeitraum."), ] ) summary = _html_summary(new_articles, edited_articles) return f"""{escape(empty_text)}
') return "\n".join(blocks) blocks.append('|
Zusammenfassung Neue Artikel: {len(new_articles)} Bearbeitete Artikel: {len(edited_articles)} Gesamt: {total} |