From de85f4e30d7b51112f813e0f8695e27457af3d08 Mon Sep 17 00:00:00 2001 From: mike Date: Wed, 19 Nov 2025 17:32:48 +0100 Subject: [PATCH] First Upload --- CP Tagesrechner Jahresplanung/readme.md | 48 + .../tagerechner_win_v0.10.py | 848 ++++++++++++++++++ .../tagesrechner_user-manual.docx | Bin 0 -> 38321 bytes 3 files changed, 896 insertions(+) create mode 100644 CP Tagesrechner Jahresplanung/readme.md create mode 100644 CP Tagesrechner Jahresplanung/tagerechner_win_v0.10.py create mode 100644 CP Tagesrechner Jahresplanung/tagesrechner_user-manual.docx diff --git a/CP Tagesrechner Jahresplanung/readme.md b/CP Tagesrechner Jahresplanung/readme.md new file mode 100644 index 0000000..e32cc20 --- /dev/null +++ b/CP Tagesrechner Jahresplanung/readme.md @@ -0,0 +1,48 @@ +# đŸ—“ïž Termin-Generator fĂŒr Change-Planung +**Version:** v0.10 +**Autor:** Mike Lindner + +--- + +## 📌 Übersicht + +Der Termin-Generator ist ein Desktop-Tool fĂŒr Windows, das automatisiert wiederkehrende Termine fĂŒr ein vollstĂ€ndiges Kalenderjahr erzeugt. +Es wurde speziell fĂŒr **IT-Change-Management**, **Wartungsfenster-Planung** und **regulatorische IT-Rhythmen** entwickelt. + +Typische Beispiele: + +- „Jeden zweiten Mittwoch im Monat um 19:00 Uhr“ +- „Dienstag in der Woche nach dem zweiten Dienstag“ +- „15. oder nĂ€chster Werktag, falls Wochenende“ +- „Nur in ungeraden ISO-Kalenderwochen“ +- „Letzter Kalendertag des Quartals“ +- „Monatlicher Samstag nach dem dritten Donnerstag“ + +Die erzeugten Termine können direkt weiterverarbeitet werden (Excel, Jira, Confluence, Planner, ServiceNow, Outlook etc.). + +--- + +## 🚀 Funktionen + +| Funktion | Status | +|----------|--------| +| Wiederkehrende Termine (tĂ€glich, wöchentlich, monatlich, quartalsweise) | ✔ | +| Berechnung kompletter Serien fĂŒr 1 Jahr | ✔ | +| ISO-Kalenderwochen-Filter (ungerade / gerade) | ✔ | +| Monatsregeln (z. B. „2. Mittwoch“, „Fester Tag“, „Folgetag bei Wochenende“) | ✔ | +| Relative Terminlogik („Mittwoch nach 2. Dienstag“, „+1 Woche“) | ✔ | +| Kopieren der erzeugten Serien in die Zwischenablage | ✔ | +| BedienoberflĂ€che mit selbsterklĂ€renden Beschriftungen und Emojis | ✔ | +| Quartalsfunktionen: letzter Tag im Quartal oder frei definierbar | ✔ | + +--- + +## đŸ–„ïž Systemvoraussetzungen + +- Windows mit **Python 3.x** +- Kein Administrator-Zugriff notwendig + +Optional (fĂŒr automatische Zwischenablage-Kopie): + +```bash +pip install pyperclip diff --git a/CP Tagesrechner Jahresplanung/tagerechner_win_v0.10.py b/CP Tagesrechner Jahresplanung/tagerechner_win_v0.10.py new file mode 100644 index 0000000..51e0745 --- /dev/null +++ b/CP Tagesrechner Jahresplanung/tagerechner_win_v0.10.py @@ -0,0 +1,848 @@ +# tagerechner_win_v0_10.py +# Zweck: Termine (tĂ€glich / wöchentlich / monatlich / quartalsweise) eines Jahres generieren +# v0.10 – Quartalsweise erweitert: +# - Quartalsweise: +# - Option 1: letzter Kalendertag des Quartals +# - Option 2: frei wĂ€hlbarer Kalendertag im Quartalsmonat (z. B. 15.) +# +# v0.09 – UI-Refactor (deutsch, mit Emojis, besser verstĂ€ndliche Bezeichnungen) +# +# Logik: +# - tĂ€glich: jeder Kalendertag +# - wöchentlich: bestimmter Wochentag, optional nur ungerade/gerade Kalenderwochen +# - monatlich: +# 1) 📅 Fester Kalendertag +# 2) 📆 Wiederkehrender Wochentag (z. B. 2. Mittwoch) +# 3) ➡ Kalendertag mit Verschiebung auf nĂ€chsten Werktag (Mo–Fr) +# 4) 🏱 Erster Werktag des Monats (Mo–Fr) +# 5) â†Ș Termin abhĂ€ngig von anderem Datum (relativer Wochentag, mit Wochen-Offset) +# - quartalsweise: +# 1) 🏣 Letzter Kalendertag des Quartals +# 2) 📅 Fester Kalendertag im Quartalsmonat (z. B. 15. im MĂ€rz/Juni/Sept./Dez.) + +VERSION = "v0.10" + +import re +import calendar +from datetime import datetime, timedelta, date + +import tkinter as tk +from tkinter import ttk, messagebox + +try: + import pyperclip + HAS_PYPERCLIP = True +except Exception: + HAS_PYPERCLIP = False + + +# ---------- Hilfsfunktionen ---------- + +def validate_time_format(time_str: str) -> str: + """ + Erwartet HH:MM. Liefert normalisiert HH:MM:SS (Sekunden = :00). + """ + if not re.match(r'^\d{2}:\d{2}$', time_str): + raise ValueError("Uhrzeit bitte als HH:MM angeben.") + h, m = map(int, time_str.split(":")) + if not (0 <= h < 24 and 0 <= m < 60): + raise ValueError("Uhrzeit ungĂŒltig (Stunden 0–23, Minuten 0–59).") + return f"{h:02d}:{m:02d}:00" + + +def nth_weekday_in_month(year: int, month: int, n: int, weekday0: int): + """ + Liefert das Datum des n-ten weekday0 (0=Mo..6=So) im Monat. + Gibt None zurĂŒck, wenn es dieses n-te Vorkommen in diesem Monat nicht gibt. + """ + first_weekday, days_in_month = calendar.monthrange(year, month) # first_weekday: 0=Mo..6=So + delta = (weekday0 - first_weekday) % 7 + day = 1 + delta + 7 * (n - 1) + if day > days_in_month: + return None + return date(year, month, day) + + +def format_datetime_for_output(d: date, time_hhmmss: str) -> str: + return d.strftime("%d.%m.%Y ") + time_hhmmss + + +def iso_week_number(d: date) -> int: + """ISO-Kalenderwoche (1..53).""" + iso = d.isocalendar() + return getattr(iso, "week", iso[1]) + + +def week_parity_ok(d: date, parity: str | None) -> bool: + """ + parity: None | 'odd' | 'even' + """ + if parity is None: + return True + w = iso_week_number(d) + if parity == 'odd': + return (w % 2) == 1 + if parity == 'even': + return (w % 2) == 0 + return True + + +def first_workday_of_month(year: int, month: int) -> date: + """ + 1. Werktag (Mo–Fr) des Monats. + (Feiertage werden NICHT berĂŒcksichtigt.) + """ + d = date(year, month, 1) # 1. des Monats + wd = d.weekday() # Mo=0 .. So=6 + if wd >= 5: # Sa(5) -> +2, So(6) -> +1 + d += timedelta(days=(7 - wd)) + return d + + +def next_workday(d: date) -> date: + """ + NĂ€chster Werktag (Mo–Fr) ab d, inkl. d selbst wenn d ein Werktag ist. + (Feiertage werden NICHT berĂŒcksichtigt.) + """ + while d.weekday() >= 5: # Sa(5), So(6) + d += timedelta(days=1) + return d + + +def weekday_name_de(weekday1: int) -> str: + """Wochentag 1..7 -> deutscher Name.""" + names = { + 1: "Montag", + 2: "Dienstag", + 3: "Mittwoch", + 4: "Donnerstag", + 5: "Freitag", + 6: "Samstag", + 7: "Sonntag", + } + return names.get(weekday1, f"Wochentag {weekday1}") + + +def ordinal_de(n: int) -> str: + """1 -> '1.', 2 -> '2.' etc.""" + return f"{n}." + + +# ---------- Kernlogik ---------- + +def generate_dates(year: int, + frequency: str, + time_hhmmss: str, + weekday_for_weekly: int | None = None, + weekly_iso_week_parity: str | None = None, + monthly_option: int | None = None, + day_of_month: int | None = None, + week_in_month: int | None = None, + weekday_for_monthly: int | None = None, + target_weekday_for_monthly: int | None = None, + relative_week_offset: int = 0, + monthly_iso_week_parity: str | None = None, + quarterly_option: int | None = None) -> list[str]: + """ + frequency: 't' (tĂ€glich), 'w' (wöchentlich), 'm' (monatlich), 'q' (quartalsweise) + weekday_*: 1=Mo .. 7=So (Benutzer-Sicht). Intern 0..6. + monthly_option: + 1 = 📅 Fester Kalendertag + 2 = 📆 Wiederkehrender Wochentag (+ optionaler ISO-KW-Filter) + 3 = ➡ Kalendertag mit Verschiebung auf nĂ€chsten Werktag (Mo–Fr) + 4 = 🏱 Erster Werktag des Monats (Mo–Fr) + 5 = â†Ș Termin abhĂ€ngig von anderem Datum (relativer Wochentag, mit Wochen-Offset) + quarterly_option: + 1 = 🏣 Letzter Kalendertag des Quartals + 2 = 📅 Fester Kalendertag im Quartalsmonat (z. B. 15.) + """ + start_date = date(year, 1, 1) + end_date = date(year, 12, 31) + out = [] + + if frequency == 't': + cur = start_date + while cur <= end_date: + out.append(format_datetime_for_output(cur, time_hhmmss)) + cur += timedelta(days=1) + + elif frequency == 'w': + if weekday_for_weekly is None: + raise ValueError("Bitte einen Wochentag fĂŒr wöchentliche Termine auswĂ€hlen.") + wk = (weekday_for_weekly - 1) % 7 # 0..6 + # erstes Auftreten im Jahr + delta = (wk - start_date.weekday()) % 7 + cur = start_date + timedelta(days=delta) + + # Falls ISO-ParitĂ€t gefordert: auf erste passende Woche vorrĂŒcken + if weekly_iso_week_parity in ('odd', 'even'): + while cur <= end_date and not week_parity_ok(cur, weekly_iso_week_parity): + cur += timedelta(days=7) + step = 14 # ParitĂ€t bleibt bei +14 erhalten + else: + step = 7 + + while cur <= end_date: + if week_parity_ok(cur, weekly_iso_week_parity): + out.append(format_datetime_for_output(cur, time_hhmmss)) + cur += timedelta(days=step) + + elif frequency == 'm': + cur = start_date.replace(day=1) + while cur <= end_date: + if monthly_option == 1: + # 📅 Fester Kalendertag + if day_of_month is None: + raise ValueError("Bitte 'Tag im Monat' angeben (1..31).") + days_in_month = calendar.monthrange(cur.year, cur.month)[1] + if 1 <= day_of_month <= days_in_month: + d = date(cur.year, cur.month, day_of_month) + if d <= end_date: + out.append(format_datetime_for_output(d, time_hhmmss)) + + elif monthly_option == 2: + # 📆 Wiederkehrender Wochentag + optionaler ISO-KW-Filter + if week_in_month is None or weekday_for_monthly is None: + raise ValueError("Bitte Vorkommen (1..5) und Wochentag auswĂ€hlen.") + wk2 = (weekday_for_monthly - 1) % 7 # 0..6 + d = nth_weekday_in_month(cur.year, cur.month, week_in_month, wk2) + if d is not None and d <= end_date: + if week_parity_ok(d, monthly_iso_week_parity): + out.append(format_datetime_for_output(d, time_hhmmss)) + + elif monthly_option == 3: + # ➡ Kalendertag mit Verschiebung auf nĂ€chsten Werktag (Mo–Fr) + if day_of_month is None: + raise ValueError("Bitte 'Kalendertag' angeben (1..31).") + days_in_month = calendar.monthrange(cur.year, cur.month)[1] + if 1 <= day_of_month <= days_in_month: + d = date(cur.year, cur.month, day_of_month) + d = next_workday(d) + if d <= end_date and d.month == cur.month: + out.append(format_datetime_for_output(d, time_hhmmss)) + + elif monthly_option == 4: + # 🏱 Erster Werktag im Monat (Mo–Fr) + d = first_workday_of_month(cur.year, cur.month) + if d <= end_date: + out.append(format_datetime_for_output(d, time_hhmmss)) + + elif monthly_option == 5: + # â†Ș Relativer Wochentag: Ziel-Wochentag nach n-tem Referenz-Wochentag + Wochen-Offset + if week_in_month is None or weekday_for_monthly is None or target_weekday_for_monthly is None: + raise ValueError("FĂŒr 'Termin abhĂ€ngig von anderem Datum' bitte Vorkommen, Referenz- und Ziel-Wochentag angeben.") + base_wk = (weekday_for_monthly - 1) % 7 # Referenz-Wochentag (0..6) + target_wk = (target_weekday_for_monthly - 1) % 7 + ref = nth_weekday_in_month(cur.year, cur.month, week_in_month, base_wk) + if ref is not None and ref <= end_date: + # delta innerhalb derselben Woche + Wochen-Offset in ganzen Wochen + delta_days = (target_wk - base_wk) % 7 + d = ref + timedelta(days=delta_days + 7 * relative_week_offset) + if d.month == cur.month and d <= end_date: + out.append(format_datetime_for_output(d, time_hhmmss)) + + else: + raise ValueError("Bitte eine gĂŒltige Monats-Option wĂ€hlen.") + + # nĂ€chster Monat + if cur.month == 12: + cur = date(cur.year + 1, 1, 1) + else: + cur = date(cur.year, cur.month + 1, 1) + + elif frequency == 'q': + # Quartale: letzter Monat je Quartal = 3, 6, 9, 12 + quarters = [3, 6, 9, 12] + + if quarterly_option is None or quarterly_option == 1: + # 🏣 Letzter Kalendertag des Quartals (wie bisher) + for m in quarters: + days_in_month = calendar.monthrange(year, m)[1] + d = date(year, m, days_in_month) + out.append(format_datetime_for_output(d, time_hhmmss)) + elif quarterly_option == 2: + # 📅 Fester Kalendertag im Quartalsmonat (z. B. 15.) + if day_of_month is None: + raise ValueError("Bitte den Kalendertag im Quartalsmonat angeben (1..31).") + for m in quarters: + days_in_month = calendar.monthrange(year, m)[1] + day = min(day_of_month, days_in_month) # 31 in einem 30-Tage-Monat -> letzter Tag + d = date(year, m, day) + out.append(format_datetime_for_output(d, time_hhmmss)) + else: + raise ValueError("Unbekannte Quartals-Option.") + + else: + raise ValueError("Frequenz unbekannt.") + + return out + + +# ---------- GUI ---------- + +class App(tk.Tk): + def __init__(self): + super().__init__() + + self.title(f"Termin-Generator – Change-Planung {VERSION}") + self.geometry("980x780") + + # ---------- Grund-Layout ---------- + + # Jahr + ttk.Label(self, text="Jahr:").grid(row=0, column=0, padx=10, pady=8, sticky="e") + self.year_var = tk.IntVar(value=datetime.now().year) + self.year_spin = ttk.Spinbox(self, from_=1900, to=2100, textvariable=self.year_var, width=8) + self.year_spin.grid(row=0, column=1, padx=10, pady=8, sticky="w") + + # Frequenz (deutsche Bezeichnungen) + ttk.Label(self, text="Wie oft soll der Termin stattfinden?").grid(row=1, column=0, padx=10, pady=8, sticky="e") + self.frequency_var = tk.StringVar(value="📆 Monatlich") + self.frequency_combo = ttk.Combobox( + self, + textvariable=self.frequency_var, + values=[ + "🔁 TĂ€glich", + "📅 Wöchentlich", + "📆 Monatlich", + "🏣 Quartalsweise (letzter Kalendertag oder fester Tag)" + ], + state='readonly', + width=45 + ) + self.frequency_combo.grid(row=1, column=1, padx=10, pady=8, sticky="w") + self.frequency_combo.bind('<>', self._toggle_by_frequency) + + # Uhrzeit + ttk.Label(self, text="⏱ Uhrzeit (HH:MM):").grid(row=2, column=0, padx=10, pady=8, sticky="e") + self.time_var = tk.StringVar(value="00:00") + self.time_entry = ttk.Entry(self, textvariable=self.time_var, width=8) + self.time_entry.grid(row=2, column=1, padx=10, pady=8, sticky="w") + ttk.Label(self, text="Beispiel: 07:00 oder 18:30").grid(row=2, column=1, padx=100, pady=8, sticky="w") + + # ---------- Wöchentlich ---------- + + self.weekly_frame = ttk.LabelFrame(self, text="📅 Wöchentliche Einstellungen") + self.weekly_weekday_label = ttk.Label(self.weekly_frame, text="Wochentag auswĂ€hlen (1=Mo .. 7=So):") + self.weekly_weekday_var = tk.IntVar(value=2) + self.weekly_weekday_spin = ttk.Spinbox(self.weekly_frame, from_=1, to=7, textvariable=self.weekly_weekday_var, width=6) + + self.weekly_iso_kw_label = ttk.Label(self.weekly_frame, text="EinschrĂ€nkung nach Kalenderwochen:") + self.weekly_iso_kw_var = tk.StringVar(value='Keine EinschrĂ€nkung') + self.weekly_iso_kw_combo = ttk.Combobox( + self.weekly_frame, + textvariable=self.weekly_iso_kw_var, + values=[ + 'Keine EinschrĂ€nkung', + 'Nur in ungeraden Kalenderwochen', + 'Nur in geraden Kalenderwochen' + ], + state='readonly', + width=32 + ) + + self.weekly_hint = ttk.Label( + self.weekly_frame, + text="Hinweis: „Nur in ungeraden/geraden Kalenderwochen“ eignet sich fĂŒr 14-tĂ€gige Rhythmen.", + foreground="grey" + ) + + # ---------- Monatlich ---------- + + self.monthly_frame = ttk.LabelFrame(self, text="📆 Monatliche Terminlogik") + + # Options (Reihenfolge: einfach → komplex) + self.monthly_option_var = tk.IntVar(value=2) # Default: Wiederkehrender Wochentag + + self.rb_m_fixed = ttk.Radiobutton( + self.monthly_frame, + text="📅 Fester Kalendertag (z. B. jeden 15.)", + variable=self.monthly_option_var, + value=1, + command=self._toggle_monthly + ) + self.rb_m_nthwd = ttk.Radiobutton( + self.monthly_frame, + text="📆 Wiederkehrender Wochentag (z. B. 2. Mittwoch im Monat)", + variable=self.monthly_option_var, + value=2, + command=self._toggle_monthly + ) + self.rb_m_followingwd = ttk.Radiobutton( + self.monthly_frame, + text="➡ Kalendertag, ggf. verschoben auf nĂ€chsten Werktag (Mo–Fr)", + variable=self.monthly_option_var, + value=3, + command=self._toggle_monthly + ) + self.rb_m_firstwd = ttk.Radiobutton( + self.monthly_frame, + text="🏱 Erster Werktag des Monats (Mo–Fr)", + variable=self.monthly_option_var, + value=4, + command=self._toggle_monthly + ) + self.rb_m_relative = ttk.Radiobutton( + self.monthly_frame, + text="â†Ș Termin abhĂ€ngig von anderem Datum (z. B. Mittwoch nach 2. Dienstag)", + variable=self.monthly_option_var, + value=5, + command=self._toggle_monthly + ) + + # Felder fĂŒr Optionen 1 & 3 (Kalendertag / Kalendertag + Folgewerktag) + self.day_of_month_label = ttk.Label(self.monthly_frame, text="Tag im Monat (1..31):") + self.day_of_month_var = tk.IntVar(value=1) + self.day_of_month_spin = ttk.Spinbox(self.monthly_frame, from_=1, to=31, textvariable=self.day_of_month_var, width=6) + + # Felder fĂŒr Option 2 & 5 (n-ter / relativer Wochentag) + self.week_in_month_label = ttk.Label(self.monthly_frame, text="Vorkommen im Monat (1..5):") + self.week_in_month_var = tk.IntVar(value=2) + self.week_in_month_spin = ttk.Spinbox(self.monthly_frame, from_=1, to=5, textvariable=self.week_in_month_var, width=6) + + self.monthly_weekday_label = ttk.Label(self.monthly_frame, text="Wochentag (1=Mo .. 7=So):") + self.monthly_weekday_var = tk.IntVar(value=3) + self.monthly_weekday_spin = ttk.Spinbox(self.monthly_frame, from_=1, to=7, textvariable=self.monthly_weekday_var, width=6) + + self.monthly_target_weekday_label = ttk.Label(self.monthly_frame, text="Ziel-Wochentag (1=Mo .. 7=So):") + self.monthly_target_weekday_var = tk.IntVar(value=3) + self.monthly_target_weekday_spin = ttk.Spinbox(self.monthly_frame, from_=1, to=7, textvariable=self.monthly_target_weekday_var, width=6) + + self.monthly_week_offset_label = ttk.Label(self.monthly_frame, text="Wochen-Offset (-4 .. +4):") + self.monthly_week_offset_var = tk.IntVar(value=0) + self.monthly_week_offset_spin = ttk.Spinbox(self.monthly_frame, from_=-4, to=4, textvariable=self.monthly_week_offset_var, width=6) + + # ISO-KW-Filter nur fĂŒr Option 2 + self.iso_kw_filter_label = ttk.Label(self.monthly_frame, text="EinschrĂ€nkung nach Kalenderwochen (optional):") + self.iso_kw_filter_var = tk.StringVar(value='Keine EinschrĂ€nkung') + self.iso_kw_filter_combo = ttk.Combobox( + self.monthly_frame, + textvariable=self.iso_kw_filter_var, + values=[ + 'Keine EinschrĂ€nkung', + 'Nur in ungeraden Kalenderwochen', + 'Nur in geraden Kalenderwochen' + ], + state='readonly', + width=32 + ) + + self.monthly_hint = ttk.Label(self.monthly_frame, text="", foreground="grey") + + # ---------- Quartalsweise ---------- + + self.quarterly_frame = ttk.LabelFrame(self, text="🏣 Quartalsweise Einstellungen") + + self.quarterly_option_var = tk.IntVar(value=1) # 1 = letzter Kalendertag, 2 = fester Tag + self.rb_q_last = ttk.Radiobutton( + self.quarterly_frame, + text="🏣 Letzter Kalendertag des Quartals", + variable=self.quarterly_option_var, + value=1, + command=self._toggle_quarterly + ) + self.rb_q_fixed = ttk.Radiobutton( + self.quarterly_frame, + text="📅 Fester Kalendertag im Quartalsmonat (z. B. 15.)", + variable=self.quarterly_option_var, + value=2, + command=self._toggle_quarterly + ) + + self.quarter_day_label = ttk.Label(self.quarterly_frame, text="Kalendertag im Quartalsmonat (1..31):") + self.quarter_day_var = tk.IntVar(value=15) + self.quarter_day_spin = ttk.Spinbox(self.quarterly_frame, from_=1, to=31, textvariable=self.quarter_day_var, width=6) + + self.quarter_hint = ttk.Label( + self.quarterly_frame, + text="Hinweis: Quartalsmonat = MĂ€rz, Juni, September, Dezember. " + "Ist der Tag grĂ¶ĂŸer als die MonatslĂ€nge (z. B. 31.06.), wird der letzte Tag genommen.", + foreground="grey", + wraplength=750, + justify="left" + ) + + # ---------- Buttons & Ergebnis ---------- + + self.run_btn = ttk.Button(self, text="🧼 Berechnen & in Zwischenablage kopieren", command=self.on_run) + self.clear_btn = ttk.Button(self, text="đŸ§č Liste leeren", command=self.on_clear) + + # Regel-Vorschau + self.preview_var = tk.StringVar(value="Regel-Vorschau: (noch nichts berechnet)") + self.preview_label = ttk.Label(self, textvariable=self.preview_var, foreground="blue") + + # Ergebnis + ttk.Label(self, text="Ergebnis:").grid(row=8, column=0, padx=10, pady=(10, 0), sticky="nw") + self.result_list = tk.Listbox(self, height=16, width=70) + self.result_list.grid(row=8, column=1, padx=10, pady=(10, 0), sticky="nsew") + + # Versionslabel + self.version_label = ttk.Label(self, text=f"Version: {VERSION}", foreground="grey") + self.version_label.grid(row=9, column=0, padx=10, pady=(4, 8), sticky="w") + + # Layout-Konfiguration + self.grid_columnconfigure(1, weight=1) + self.grid_rowconfigure(8, weight=1) + + # Frames platzieren + self.weekly_frame.grid(row=3, column=0, columnspan=2, padx=10, pady=8, sticky="ew") + self.monthly_frame.grid(row=4, column=0, columnspan=2, padx=10, pady=8, sticky="ew") + self.quarterly_frame.grid(row=5, column=0, columnspan=2, padx=10, pady=8, sticky="ew") + + # Wöchentlich-Frame Inhalt + self.weekly_weekday_label.grid(row=0, column=0, padx=10, pady=6, sticky="w") + self.weekly_weekday_spin.grid(row=0, column=1, padx=10, pady=6, sticky="w") + self.weekly_iso_kw_label.grid(row=1, column=0, padx=10, pady=6, sticky="w") + self.weekly_iso_kw_combo.grid(row=1, column=1, padx=10, pady=6, sticky="w") + self.weekly_hint.grid(row=2, column=0, columnspan=2, padx=10, pady=4, sticky="w") + + # Monats-Frame Optionen + self.rb_m_fixed.grid(row=0, column=0, columnspan=3, padx=10, pady=(8, 4), sticky="w") + self.rb_m_nthwd.grid(row=1, column=0, columnspan=3, padx=10, pady=4, sticky="w") + self.rb_m_followingwd.grid(row=2, column=0, columnspan=3, padx=10, pady=4, sticky="w") + self.rb_m_firstwd.grid(row=3, column=0, columnspan=3, padx=10, pady=4, sticky="w") + self.rb_m_relative.grid(row=4, column=0, columnspan=3, padx=10, pady=4, sticky="w") + + self.monthly_hint.grid(row=9, column=0, columnspan=3, padx=10, pady=(4, 8), sticky="w") + + # Quartals-Frame Inhalt + self.rb_q_last.grid(row=0, column=0, columnspan=2, padx=10, pady=(8, 4), sticky="w") + self.rb_q_fixed.grid(row=1, column=0, columnspan=2, padx=10, pady=4, sticky="w") + self.quarter_hint.grid(row=3, column=0, columnspan=3, padx=10, pady=(4, 8), sticky="w") + + # Buttons & Vorschau + self.run_btn.grid(row=6, column=1, padx=10, pady=10, sticky="w") + self.clear_btn.grid(row=6, column=1, padx=10, pady=10, sticky="e") + self.preview_label.grid(row=7, column=0, columnspan=2, padx=10, pady=(4, 0), sticky="w") + + # Initial aktiv/inaktiv setzen + self._toggle_by_frequency(None) + self._toggle_monthly() + self._toggle_quarterly() + + # ---------- Helper: Mapping Frequenz ---------- + + def _get_frequency_code(self) -> str: + val = self.frequency_var.get() + if val.startswith("🔁"): + return 't' + if val.startswith("📅"): + return 'w' + if val.startswith("📆"): + return 'm' + if val.startswith("🏣"): + return 'q' + return 't' + + # ---------- UI-Toggles ---------- + + def _toggle_by_frequency(self, _evt): + f = self._get_frequency_code() + + if f == 'w': + self.weekly_frame.grid() + else: + self.weekly_frame.grid_remove() + + if f == 'm': + self.monthly_frame.grid() + else: + self.monthly_frame.grid_remove() + + if f == 'q': + self.quarterly_frame.grid() + else: + self.quarterly_frame.grid_remove() + + def _hide_monthly_fields(self): + for w in ( + self.day_of_month_label, self.day_of_month_spin, + self.week_in_month_label, self.week_in_month_spin, + self.monthly_weekday_label, self.monthly_weekday_spin, + self.monthly_target_weekday_label, self.monthly_target_weekday_spin, + self.monthly_week_offset_label, self.monthly_week_offset_spin, + self.iso_kw_filter_label, self.iso_kw_filter_combo + ): + w.grid_remove() + + def _toggle_monthly(self): + self._hide_monthly_fields() + opt = int(self.monthly_option_var.get()) + hint = "" + + if opt == 1: + # 📅 Fester Kalendertag + self.day_of_month_label.config(text="Tag im Monat (1..31):") + self.day_of_month_label.grid(row=5, column=0, padx=10, pady=6, sticky="w") + self.day_of_month_spin.grid(row=5, column=1, padx=10, pady=6, sticky="w") + hint = "Beispiel: jeden 15. im Monat." + + elif opt == 2: + # 📆 Wiederkehrender Wochentag + self.week_in_month_label.config(text="Vorkommen im Monat (1..5):") + self.monthly_weekday_label.config(text="Wochentag (1=Mo .. 7=So):") + + self.week_in_month_label.grid(row=5, column=0, padx=10, pady=6, sticky="w") + self.week_in_month_spin.grid(row=5, column=1, padx=10, pady=6, sticky="w") + self.monthly_weekday_label.grid(row=6, column=0, padx=10, pady=6, sticky="w") + self.monthly_weekday_spin.grid(row=6, column=1, padx=10, pady=6, sticky="w") + self.iso_kw_filter_label.grid(row=7, column=0, padx=10, pady=6, sticky="w") + self.iso_kw_filter_combo.grid(row=7, column=1, padx=10, pady=6, sticky="w") + hint = "Beispiel: 2. Mittwoch im Monat. Optional auf ungerade/gerade Kalenderwochen einschrĂ€nkbar." + + elif opt == 3: + # ➡ Kalendertag + Folgewerktag + self.day_of_month_label.config(text="Kalendertag (1..31):") + self.day_of_month_label.grid(row=5, column=0, padx=10, pady=6, sticky="w") + self.day_of_month_spin.grid(row=5, column=1, padx=10, pady=6, sticky="w") + hint = "Wenn der gewĂ€hlte Tag auf Samstag/Sonntag fĂ€llt, wird automatisch der nĂ€chste Werktag verwendet." + + elif opt == 4: + # 🏱 Erster Werktag + hint = "Erster Arbeitstag (Mo–Fr) des Monats. FĂ€llt der 1. auf Sa/So, wird auf den folgenden Montag verschoben." + + elif opt == 5: + # â†Ș Termin abhĂ€ngig von anderem Datum + self.week_in_month_label.config(text="Referenz: Vorkommen im Monat (1..5):") + self.monthly_weekday_label.config(text="Referenz: Wochentag (1=Mo .. 7=So):") + + self.week_in_month_label.grid(row=5, column=0, padx=10, pady=6, sticky="w") + self.week_in_month_spin.grid(row=5, column=1, padx=10, pady=6, sticky="w") + self.monthly_weekday_label.grid(row=6, column=0, padx=10, pady=6, sticky="w") + self.monthly_weekday_spin.grid(row=6, column=1, padx=10, pady=6, sticky="w") + self.monthly_target_weekday_label.grid(row=7, column=0, padx=10, pady=6, sticky="w") + self.monthly_target_weekday_spin.grid(row=7, column=1, padx=10, pady=6, sticky="w") + self.monthly_week_offset_label.grid(row=8, column=0, padx=10, pady=6, sticky="w") + self.monthly_week_offset_spin.grid(row=8, column=1, padx=10, pady=6, sticky="w") + hint = ( + "Beispiele: Mittwoch nach dem 2. Dienstag (Offset=0) " + "oder Dienstag in der Woche nach dem 2. Dienstag (Offset=1)." + ) + + self.monthly_hint.config(text=hint) + + def _toggle_quarterly(self): + opt = int(self.quarterly_option_var.get()) + # Standard: Spinner verstecken + self.quarter_day_label.grid_remove() + self.quarter_day_spin.grid_remove() + + if opt == 2: + # Fester Kalendertag im Quartalsmonat + self.quarter_day_label.grid(row=2, column=0, padx=10, pady=6, sticky="w") + self.quarter_day_spin.grid(row=2, column=1, padx=10, pady=6, sticky="w") + + # ---------- Vorschau-Text ---------- + + def _build_rule_preview(self, + year: int, + frequency_code: str, + time_str: str, + monthly_option: int | None, + day_of_month: int | None, + week_in_month: int | None, + weekday_for_monthly: int | None, + target_weekday_for_monthly: int | None, + relative_week_offset: int, + weekly_weekday: int | None, + weekly_iso_kw_parity: str | None, + monthly_iso_parity: str | None, + quarterly_option: int | None) -> str: + time_short = time_str[:5] # HH:MM + txt = f"Regel-Vorschau: " + + if frequency_code == 't': + txt += f"tĂ€glich um {time_short} im Jahr {year}" + + elif frequency_code == 'w': + if weekly_weekday is None: + return "Regel-Vorschau: wöchentlich (Wochentag nicht gesetzt)" + wname = weekday_name_de(weekly_weekday) + txt += f"wöchentlich am {wname} um {time_short} im Jahr {year}" + if weekly_iso_kw_parity == 'odd': + txt += " (nur in ungeraden Kalenderwochen)" + elif weekly_iso_kw_parity == 'even': + txt += " (nur in geraden Kalenderwochen)" + + elif frequency_code == 'm': + if monthly_option == 1: + if day_of_month is None: + return "Regel-Vorschau: monatlich – fester Kalendertag (Tag nicht gesetzt)" + txt += f"monatlich am {day_of_month}. um {time_short}" + elif monthly_option == 2: + if week_in_month is None or weekday_for_monthly is None: + return "Regel-Vorschau: monatlich – wiederkehrender Wochentag (Angaben unvollstĂ€ndig)" + wname = weekday_name_de(weekday_for_monthly) + txt += f"monatlich am {ordinal_de(week_in_month)} {wname} um {time_short}" + if monthly_iso_parity == 'odd': + txt += " (nur in ungeraden Kalenderwochen)" + elif monthly_iso_parity == 'even': + txt += " (nur in geraden Kalenderwochen)" + elif monthly_option == 3: + if day_of_month is None: + return "Regel-Vorschau: monatlich – Kalendertag mit Folgewerktag (Tag nicht gesetzt)" + txt += ( + f"monatlich am {day_of_month}. bzw. verschoben auf den nĂ€chsten Werktag (Mo–Fr) " + f"um {time_short}" + ) + elif monthly_option == 4: + txt += f"monatlich am ersten Werktag des Monats (Mo–Fr) um {time_short}" + elif monthly_option == 5: + if week_in_month is None or weekday_for_monthly is None or target_weekday_for_monthly is None: + return "Regel-Vorschau: monatlich – Termin abhĂ€ngig von anderem Datum (Angaben unvollstĂ€ndig)" + ref_name = weekday_name_de(weekday_for_monthly) + tgt_name = weekday_name_de(target_weekday_for_monthly) + txt += ( + f"monatlich am {tgt_name} " + f"in Bezug auf den {ordinal_de(week_in_month)} {ref_name} " + f"um {time_short}" + ) + if relative_week_offset == 1: + txt += " (eine Woche nach dem Referenztermin)" + elif relative_week_offset == -1: + txt += " (eine Woche vor dem Referenztermin)" + elif relative_week_offset > 1: + txt += f" ({relative_week_offset} Wochen nach dem Referenztermin)" + elif relative_week_offset < -1: + txt += f" ({abs(relative_week_offset)} Wochen vor dem Referenztermin)" + + elif frequency_code == 'q': + if quarterly_option is None or quarterly_option == 1: + txt += f"quartalsweise am letzten Kalendertag des Quartals um {time_short} im Jahr {year}" + elif quarterly_option == 2: + if day_of_month is None: + return "Regel-Vorschau: quartalsweise – fester Kalendertag im Quartalsmonat (Tag nicht gesetzt)" + txt += ( + f"quartalsweise am {day_of_month}. im Quartalsmonat " + f"(MĂ€rz, Juni, September, Dezember) um {time_short} im Jahr {year}" + ) + else: + txt += "quartalsweise (unbekannte Option)" + + else: + txt += "unbekannte Regel." + + return txt + + # ---------- Aktionen ---------- + + def on_run(self): + try: + year = int(self.year_var.get()) + frequency_code = self._get_frequency_code() + time_str = validate_time_format(self.time_var.get().strip()) + + # gemeinsame Variablen + weekday_for_weekly = None + weekly_iso_parity = None + monthly_option = None + dom = None + wim = None + weekday_for_monthly = None + target_weekday_for_monthly = None + rel_week_offset = 0 + monthly_iso_parity = None + quarterly_option = None + + # Wöchentlich + if frequency_code == 'w': + weekday_for_weekly = int(self.weekly_weekday_var.get()) + sel_w = self.weekly_iso_kw_var.get().strip().lower() + if "ungeraden" in sel_w: + weekly_iso_parity = 'odd' + elif "geraden" in sel_w: + weekly_iso_parity = 'even' + else: + weekly_iso_parity = None + + # Monatlich + if frequency_code == 'm': + monthly_option = int(self.monthly_option_var.get()) + if monthly_option == 1: + dom = int(self.day_of_month_var.get()) + elif monthly_option == 2: + wim = int(self.week_in_month_var.get()) + weekday_for_monthly = int(self.monthly_weekday_var.get()) + sel_m = self.iso_kw_filter_var.get().strip().lower() + if "ungeraden" in sel_m: + monthly_iso_parity = 'odd' + elif "geraden" in sel_m: + monthly_iso_parity = 'even' + else: + monthly_iso_parity = None + elif monthly_option == 3: + dom = int(self.day_of_month_var.get()) + elif monthly_option == 4: + # keine Zusatzfelder + pass + elif monthly_option == 5: + wim = int(self.week_in_month_var.get()) + weekday_for_monthly = int(self.monthly_weekday_var.get()) + target_weekday_for_monthly = int(self.monthly_target_weekday_var.get()) + rel_week_offset = int(self.monthly_week_offset_var.get()) + + # Quartalsweise + if frequency_code == 'q': + quarterly_option = int(self.quarterly_option_var.get()) + if quarterly_option == 2: + dom = int(self.quarter_day_var.get()) + + # Vorschau-Text bauen + preview = self._build_rule_preview( + year=year, + frequency_code=frequency_code, + time_str=time_str, + monthly_option=monthly_option, + day_of_month=dom, + week_in_month=wim, + weekday_for_monthly=weekday_for_monthly, + target_weekday_for_monthly=target_weekday_for_monthly, + relative_week_offset=rel_week_offset, + weekly_weekday=weekday_for_weekly, + weekly_iso_kw_parity=weekly_iso_parity, + monthly_iso_parity=monthly_iso_parity, + quarterly_option=quarterly_option + ) + self.preview_var.set(preview) + + # Termine generieren + dates = generate_dates( + year=year, + frequency=frequency_code, + time_hhmmss=time_str, + weekday_for_weekly=weekday_for_weekly, + weekly_iso_week_parity=weekly_iso_parity, + monthly_option=monthly_option, + day_of_month=dom, + week_in_month=wim, + weekday_for_monthly=weekday_for_monthly, + target_weekday_for_monthly=target_weekday_for_monthly, + relative_week_offset=rel_week_offset, + monthly_iso_week_parity=monthly_iso_parity, + quarterly_option=quarterly_option + ) + + self.result_list.delete(0, tk.END) + for d in dates: + self.result_list.insert(tk.END, d) + + if HAS_PYPERCLIP: + pyperclip.copy("\n".join(dates)) + messagebox.showinfo("Fertig", f"{len(dates)} EintrĂ€ge generiert und in die Zwischenablage kopiert.") + else: + messagebox.showinfo( + "Fertig", + f"{len(dates)} EintrĂ€ge generiert.\n" + f"(Hinweis: pyperclip nicht installiert – nichts kopiert.)" + ) + + except Exception as e: + messagebox.showerror("Fehler", str(e)) + + def on_clear(self): + self.result_list.delete(0, tk.END) + self.preview_var.set("Regel-Vorschau: (noch nichts berechnet)") + + +if __name__ == "__main__": + App().mainloop() diff --git a/CP Tagesrechner Jahresplanung/tagesrechner_user-manual.docx b/CP Tagesrechner Jahresplanung/tagesrechner_user-manual.docx new file mode 100644 index 0000000000000000000000000000000000000000..518c1dbab3a72169f6e99e8178bd74e7c064dff0 GIT binary patch literal 38321 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

oM$Ns*|R&) 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]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 W4h}eg1QIS`f{cJ67Jnk--Twk#FbFIF literal 0 HcmV?d00001