diff --git a/README.md b/README.md index 3e035d2..94a53cd 100644 --- a/README.md +++ b/README.md @@ -1,79 +1,21 @@ -bitcoin-programm - - Lokales Projekt zur strukturierten Erfassung und Verwaltung von: - - • To-dos - • Kalendereinträgen - • Dokumentation - • später: Bitcoin-Transaktionen und Auswertungen - - Ziel - - Das Projekt dient als zentrales Arbeitsverzeichnis für persönliche Verwaltung und spätere Automatisierung. - - Geplant ist: - - • eine einfache Eingabemaske für To-dos und Kalendertermine - • CSV als einfache Zwischen- und Exportform - • später ein SQLite-Backend für Auswertung und Dashboard - • optional Sync mit Google Calendar oder iCloud Kalender - - Aktueller Fokus - - Der aktuelle Fokus liegt auf: - - 1. Dokumentation - 2. To-do-Erfassung - 3. Kalender-Erfassung - 4. saubere Datenstruktur für späteren Import nach SQLite - - Bitcoin-Tracking kann später ergänzt werden. - - Projektstruktur - - Empfohlene Struktur: - - ─ text - bitcoin-programm/ - ├─ README.md - ├─ docs/ - ├─ data/ - ├─ templates/ - ├─ scripts/ - ├─ sqlite/ - ├─ dashboard/ - └─ notes/ - - Datenprinzip - - • Rohdaten werden getrennt von Verarbeitung gespeichert - • CSV-Dateien dienen als einfache Austausch- und Importform - • SQLite wird später als strukturierte Datenbasis genutzt - • Kalender- und To-do-Daten bleiben nachvollziehbar und versionierbar - - Bedienung - - Geplant ist eine lokale Eingabemaske, mit der Einträge einfacher gepflegt werden können als direkt über CSV. - - Ziele der Maske: - - • Pflichtfelder - • Dropdowns für feste Werte - • weniger Tippfehler - • saubere CSV-Ausgabe - • später einfache Anbindung an SQLite - - Langfristige Erweiterung - - Später sollen möglich sein: - - • Kalender-Synchronisation - • Wiederholungen - • Erinnerungen - • Dashboard-Ansicht - • Bitcoin-Tracking - • Auswertung und Filter - - Hinweise - - Dieses Repo ist bewusst schlank gehalten und wächst schrittweise mit den Anforderungen. +# bitcoin-programm + Lokales Projekt zur strukturierten Erfassung und Verwaltung von: + - To-dos + - Kalendereinträgen + - Dokumentation + - später: Bitcoin-Transaktionen und Auswertungen + ## Ziel + Das Projekt dient als zentrales Arbeitsverzeichnis für persönliche Verwaltung und spätere Automatisierung. + Geplant ist: + - eine einfache Eingabemaske für To-dos und Kalendertermine + - CSV als einfache Zwischen- und Exportform + - später ein SQLite-Backend für Auswertung und Dashboard + - optional Sync mit Google Calendar oder iCloud Kalender + ## Aktueller Fokus + Der aktuelle Fokus liegt auf: + 1. Dokumentation + 2. To-do-Erfassung + 3. Kalender-Erfassung + 4. saubere Datenstruktur für späteren Import nach SQLite + Bitcoin-Tracking kann später ergänzt werden. + \ No newline at end of file diff --git a/app.py b/app.py index 05e51bb..b82777d 100644 --- a/app.py +++ b/app.py @@ -1,56 +1,40 @@ import streamlit as st -import pandas as pd -from pathlib import Path -from datetime import date, time -DATA_DIR = Path("data") -TODO_FILE = DATA_DIR / "todo.csv" -CALENDAR_FILE = DATA_DIR / "calendar.csv" -DATA_DIR.mkdir(parents=True, exist_ok=True) -TODO_COLUMNS = [ - "id", - "titel", - "beschreibung", - "kategorie", - "prioritaet", - "status", - "faellig_am", - "wiederholen", - "notiz", -] -CALENDAR_COLUMNS = [ - "id", - "titel", - "datum", - "uhrzeit", - "dauer_min", - "kalender", - "kategorie", - "erinnerung_min", - "wiederholen", - "notiz", -] -def load_csv(path: Path, columns: list[str]) -> pd.DataFrame: - if path.exists(): - df = pd.read_csv(path) - for col in columns: - if col not in df.columns: - df[col] = "" - return df[columns] - return pd.DataFrame(columns=columns) -def save_csv(df: pd.DataFrame, path: Path) -> None: - df.to_csv(path, index=False) -def next_id(df: pd.DataFrame) -> int: - if df.empty or "id" not in df.columns: - return 1 - try: - return int(df["id"].max()) + 1 - except Exception: - return 1 -st.set_page_config(page_title="Bitcoin-Programm", layout="wide") -st.title("Bitcoin-Programm – To-do & Kalender") -tab1, tab2, tab3 = st.tabs(["To-do anlegen", "Kalendertermin anlegen", "Daten ansehen"]) - # --- To-do Formular --- -with tab1: + import pandas as pd + from pathlib import Path + from datetime import date, time + DATA_DIR = Path("data") + TODO_FILE = DATA_DIR / "todo.csv" + CALENDAR_FILE = DATA_DIR / "calendar.csv" + DATA_DIR.mkdir(parents=True, exist_ok=True) + TODO_COLUMNS = [ + "id","titel","beschreibung","kategorie","prioritaet","status", + "faellig_am","wiederholen","notiz" + ] + CALENDAR_COLUMNS = [ + "id","titel","datum","uhrzeit","dauer_min","kalender","kategorie", + "erinnerung_min","wiederholen","notiz" + ] + def load_csv(path: Path, columns: list[str]) -> pd.DataFrame: + if path.exists(): + df = pd.read_csv(path) + for col in columns: + if col not in df.columns: + df[col] = "" + return df[columns] + return pd.DataFrame(columns=columns) + def save_csv(df: pd.DataFrame, path: Path) -> None: + df.to_csv(path, index=False) + def next_id(df: pd.DataFrame) -> int: + if df.empty or "id" not in df.columns: + return 1 + try: + return int(df["id"].max()) + 1 + except Exception: + return 1 + st.set_page_config(page_title="Bitcoin-Programm", layout="wide") + st.title("Bitcoin-Programm – To-do & Kalender") + tab1, tab2, tab3 = st.tabs(["To-do anlegen", "Kalendertermin anlegen", "Daten ansehen"]) + with tab1: st.subheader("Neues To-do") todo_df = load_csv(TODO_FILE, TODO_COLUMNS) new_todo_id = next_id(todo_df) @@ -82,8 +66,7 @@ with tab1: todo_df = pd.concat([todo_df, new_row], ignore_index=True) save_csv(todo_df, TODO_FILE) st.success("To-do gespeichert.") - # --- Kalender Formular --- -with tab2: + with tab2: st.subheader("Neuen Kalendereintrag") cal_df = load_csv(CALENDAR_FILE, CALENDAR_COLUMNS) new_cal_id = next_id(cal_df) @@ -117,27 +100,17 @@ with tab2: cal_df = pd.concat([cal_df, new_row], ignore_index=True) save_csv(cal_df, CALENDAR_FILE) st.success("Kalendertermin gespeichert.") - # --- Daten ansehen --- -with tab3: + with tab3: st.subheader("Gespeicherte Daten") col1, col2 = st.columns(2) with col1: st.markdown("### To-dos") todo_df = load_csv(TODO_FILE, TODO_COLUMNS) st.dataframe(todo_df, use_container_width=True) + st.download_button("To-do CSV herunterladen", data=todo_df.to_csv(index=False).encode("utf-8"), file_name="todo.csv", mime="text/csv") with col2: st.markdown("### Kalender") cal_df = load_csv(CALENDAR_FILE, CALENDAR_COLUMNS) st.dataframe(cal_df, use_container_width=True) - st.download_button( - "To-do CSV herunterladen", - data=todo_df.to_csv(index=False).encode("utf-8"), - file_name="todo.csv", - mime="text/csv", - ) - st.download_button( - "Kalender CSV herunterladen", - data=cal_df.to_csv(index=False).encode("utf-8"), - file_name="calendar.csv", - mime="text/csv", - ) \ No newline at end of file + st.download_button("Kalender CSV herunterladen", data=cal_df.to_csv(index=False).encode("utf-8"), file_name="calendar.csv", mime="text/csv") + \ No newline at end of file diff --git a/bitcoin_programm_handoff.py b/bitcoin_programm_handoff.py new file mode 100644 index 0000000..10cdab3 --- /dev/null +++ b/bitcoin_programm_handoff.py @@ -0,0 +1,386 @@ +import json +import os +import subprocess +import sys +from pathlib import Path +from urllib.request import Request, urlopen +from urllib.error import HTTPError, URLError +REPO_PATH = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else Path.cwd() +GITEA_BASE = os.environ.get("GITEA_BASE", "https://gitea.schaefinik.xyz") +GITEA_TOKEN = os.environ.get("GITEA_TOKEN", "").strip() +FILES = { + "README.md": """# bitcoin-programm + Lokales Projekt zur strukturierten Erfassung und Verwaltung von: + - To-dos + - Kalendereinträgen + - Dokumentation + - später: Bitcoin-Transaktionen und Auswertungen + ## Ziel + Das Projekt dient als zentrales Arbeitsverzeichnis für persönliche Verwaltung und spätere Automatisierung. + Geplant ist: + - eine einfache Eingabemaske für To-dos und Kalendertermine + - CSV als einfache Zwischen- und Exportform + - später ein SQLite-Backend für Auswertung und Dashboard + - optional Sync mit Google Calendar oder iCloud Kalender + ## Aktueller Fokus + Der aktuelle Fokus liegt auf: + 1. Dokumentation + 2. To-do-Erfassung + 3. Kalender-Erfassung + 4. saubere Datenstruktur für späteren Import nach SQLite + Bitcoin-Tracking kann später ergänzt werden. + """, + "docs/00_overview.md": """# Überblick + Dieses Projekt ist ein lokales Arbeits- und Dokumentationsprojekt für persönliche Organisation. + ## Zweck + Das Projekt soll folgende Bereiche abdecken: + - To-dos + - Kalendertermine + - Projektdokumentation + - später Bitcoin-Tracking + - später SQLite-basierte Auswertung + ## Leitprinzipien + 1. Daten sauber strukturieren + 2. Eingaben einfach halten + 3. CSV als Zwischenformat nutzbar machen + 4. später SQL-Backend ergänzen + 5. lokale Kontrolle behalten + ## Arbeitsweise + ### 1. Erfassung + Neue Einträge werden über eine Eingabemaske erfasst. + ### 2. Speicherung + Die Daten werden in CSV abgelegt und später in SQLite überführt. + ## Zielbild + Am Ende soll das Projekt enthalten: + - eine einfache Eingabemaske + - eine saubere Dokumentation + - exportierbare Daten + - ein lokales Dashboard + - Kalenderanbindung + - To-do-Verwaltung + - später Bitcoin-Verwaltung + ## Status + Der Fokus liegt aktuell auf Dokumentation, Kalender und To-do. Bitcoin-Tracking wird später ergänzt. + """, + "docs/01_requirements.md": """# Anforderungen + ## Hauptziel + Das Projekt soll eine einfache und verlässliche Verwaltung von To-dos und Kalenderterminen ermöglichen. + ## Funktionale Anforderungen + ### To-dos + - To-dos sollen über eine Eingabemaske erfasst werden + - Pflichtfelder müssen vorhanden sein + - feste Werte sollen per Dropdown gewählt werden + - die Daten sollen als CSV exportierbar sein + ### Kalender + - Kalendertermine sollen über eine Eingabemaske erfasst werden + - Datum und Uhrzeit müssen wählbar sein + - Terminarten sollen per Dropdown auswählbar sein + - Wiederholungen sollen unterstützt werden + - Erinnerungen sollen vorbereitet sein + ### Datenhaltung + - CSV als Zwischenspeicher + - später SQLite als Datenbasis + - klare Trennung von Rohdaten und verarbeiteten Daten + ## Nicht-funktionale Anforderungen + - lokale Nutzung ohne Cloud-Zwang + - später synchronisierbar mit Google Calendar oder iCloud + - leicht verständlich und wartbar + - importfreundlich für SQLite + - dokumentierbar und versionierbar + ## UI-Anforderungen + Die Eingabemaske soll enthalten: + - Pflichtfelder + - Dropdowns + - Datumsauswahl + - Uhrzeitauswahl + - Textfelder für Notizen + - klare Trennung zwischen To-do und Kalender + ## Datenqualität + - einheitliche Schreibweisen + - feste Statuswerte + - saubere Datumsformate + - keine freien Sonderformate in Pflichtfeldern + - keine Mischfelder für unterschiedliche Datentypen + """, + "docs/02_todo_calendar.md": """# To-do- und Kalenderkonzept + ## Ziel + Dieses Projekt soll eine einfache Möglichkeit bieten, Aufgaben und Termine zentral zu erfassen. + ## To-do-Felder + - id + - titel + - beschreibung + - kategorie + - prioritaet + - status + - faellig_am + - wiederholen + - notiz + ## Kalender-Felder + - id + - titel + - datum + - uhrzeit + - dauer_min + - kalender + - kategorie + - erinnerung_min + - wiederholen + - notiz + ## Dropdown-Werte + ### Priorität + - Niedrig + - Mittel + - Hoch + ### Status + - Offen + - In Arbeit + - Wartet + - Erledigt + ### Wiederholen + - Nein + - Täglich + - Wöchentlich + - Monatlich + ### Kalender + - Arbeit + - Privat + - Bitcoin + - Todo + ### Kategorie + - Allgemein + - Dokumentation + - Erinnerung + - Termin + - Aufgabe + - Sonstiges + ## Eingaberegeln + - Pflichtfelder müssen ausgefüllt sein + - freie Texte nur dort, wo sie sinnvoll sind + - Datum und Uhrzeit strikt getrennt + - Wiederholungen nur aus erlaubten Werten + - Kalenderziel muss auswählbar sein + ## Workflow + 1. Eintrag über Maske anlegen + 2. Daten als CSV speichern + 3. später nach SQLite importieren + 4. optional an Google Calendar oder iCloud übergeben + """, + "sqlite/schema.sql": """PRAGMA foreign_keys = ON; + CREATE TABLE IF NOT EXISTS todo ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + titel TEXT NOT NULL, + beschreibung TEXT, + kategorie TEXT NOT NULL DEFAULT 'Allgemein', + prioritaet TEXT NOT NULL DEFAULT 'Mittel', + status TEXT NOT NULL DEFAULT 'Offen', + faellig_am TEXT, + wiederholen TEXT NOT NULL DEFAULT 'Nein', + notiz TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + CREATE TABLE IF NOT EXISTS calendar_event ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + titel TEXT NOT NULL, + datum TEXT NOT NULL, + uhrzeit TEXT NOT NULL, + dauer_min INTEGER NOT NULL DEFAULT 60, + kalender TEXT NOT NULL DEFAULT 'Privat', + kategorie TEXT NOT NULL DEFAULT 'Sonstiges', + erinnerung_min INTEGER NOT NULL DEFAULT 30, + wiederholen TEXT NOT NULL DEFAULT 'Nein', + notiz TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + CREATE TABLE IF NOT EXISTS settings ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL + ); + CREATE INDEX IF NOT EXISTS idx_todo_faellig_am ON todo(faellig_am); + CREATE INDEX IF NOT EXISTS idx_todo_status ON todo(status); + CREATE INDEX IF NOT EXISTS idx_calendar_event_datum ON calendar_event(datum); + CREATE INDEX IF NOT EXISTS idx_calendar_event_kalender ON calendar_event(kalender); + """, + "requirements.txt": """streamlit + pandas + """ +} +APP_PY = """import streamlit as st + import pandas as pd + from pathlib import Path + from datetime import date, time + DATA_DIR = Path("data") + TODO_FILE = DATA_DIR / "todo.csv" + CALENDAR_FILE = DATA_DIR / "calendar.csv" + DATA_DIR.mkdir(parents=True, exist_ok=True) + TODO_COLUMNS = [ + "id","titel","beschreibung","kategorie","prioritaet","status", + "faellig_am","wiederholen","notiz" + ] + CALENDAR_COLUMNS = [ + "id","titel","datum","uhrzeit","dauer_min","kalender","kategorie", + "erinnerung_min","wiederholen","notiz" + ] + def load_csv(path: Path, columns: list[str]) -> pd.DataFrame: + if path.exists(): + df = pd.read_csv(path) + for col in columns: + if col not in df.columns: + df[col] = "" + return df[columns] + return pd.DataFrame(columns=columns) + def save_csv(df: pd.DataFrame, path: Path) -> None: + df.to_csv(path, index=False) + def next_id(df: pd.DataFrame) -> int: + if df.empty or "id" not in df.columns: + return 1 + try: + return int(df["id"].max()) + 1 + except Exception: + return 1 + st.set_page_config(page_title="Bitcoin-Programm", layout="wide") + st.title("Bitcoin-Programm – To-do & Kalender") + tab1, tab2, tab3 = st.tabs(["To-do anlegen", "Kalendertermin anlegen", "Daten ansehen"]) + with tab1: + st.subheader("Neues To-do") + todo_df = load_csv(TODO_FILE, TODO_COLUMNS) + new_todo_id = next_id(todo_df) + with st.form("todo_form", clear_on_submit=True): + titel = st.text_input("Titel") + beschreibung = st.text_area("Beschreibung") + kategorie = st.selectbox("Kategorie", ["Allgemein", "Bitcoin", "Dokumentation", "Kalender", "Admin", "Sonstiges"]) + prioritaet = st.selectbox("Priorität", ["Niedrig", "Mittel", "Hoch"]) + status = st.selectbox("Status", ["Offen", "In Arbeit", "Wartet", "Erledigt"]) + faellig_am = st.date_input("Fällig am", value=date.today()) + wiederholen = st.selectbox("Wiederholen", ["Nein", "Täglich", "Wöchentlich", "Monatlich"]) + notiz = st.text_area("Notiz") + submitted = st.form_submit_button("To-do speichern") + if submitted: + if not titel.strip(): + st.error("Bitte mindestens einen Titel eingeben.") + else: + new_row = pd.DataFrame([{ + "id": new_todo_id, + "titel": titel, + "beschreibung": beschreibung, + "kategorie": kategorie, + "prioritaet": prioritaet, + "status": status, + "faellig_am": faellig_am.isoformat(), + "wiederholen": wiederholen, + "notiz": notiz, + }]) + todo_df = pd.concat([todo_df, new_row], ignore_index=True) + save_csv(todo_df, TODO_FILE) + st.success("To-do gespeichert.") + with tab2: + st.subheader("Neuen Kalendereintrag") + cal_df = load_csv(CALENDAR_FILE, CALENDAR_COLUMNS) + new_cal_id = next_id(cal_df) + with st.form("calendar_form", clear_on_submit=True): + titel = st.text_input("Titel") + datum = st.date_input("Datum", value=date.today()) + uhrzeit = st.time_input("Uhrzeit", value=time(9, 0)) + dauer_min = st.number_input("Dauer in Minuten", min_value=5, max_value=1440, value=60, step=5) + kalender = st.selectbox("Kalender", ["Arbeit", "Privat", "Bitcoin", "Todo"]) + kategorie = st.selectbox("Kategorie", ["Meeting", "Aufgabe", "Erinnerung", "Termin", "Sonstiges"]) + erinnerung_min = st.number_input("Erinnerung vorab in Minuten", min_value=0, max_value=10080, value=30, step=5) + wiederholen = st.selectbox("Wiederholen", ["Nein", "Täglich", "Wöchentlich", "Monatlich"]) + notiz = st.text_area("Notiz") + submitted = st.form_submit_button("Kalendertermin speichern") + if submitted: + if not titel.strip(): + st.error("Bitte mindestens einen Titel eingeben.") + else: + new_row = pd.DataFrame([{ + "id": new_cal_id, + "titel": titel, + "datum": datum.isoformat(), + "uhrzeit": uhrzeit.strftime("%H:%M"), + "dauer_min": int(dauer_min), + "kalender": kalender, + "kategorie": kategorie, + "erinnerung_min": int(erinnerung_min), + "wiederholen": wiederholen, + "notiz": notiz, + }]) + cal_df = pd.concat([cal_df, new_row], ignore_index=True) + save_csv(cal_df, CALENDAR_FILE) + st.success("Kalendertermin gespeichert.") + with tab3: + st.subheader("Gespeicherte Daten") + col1, col2 = st.columns(2) + with col1: + st.markdown("### To-dos") + todo_df = load_csv(TODO_FILE, TODO_COLUMNS) + st.dataframe(todo_df, use_container_width=True) + st.download_button("To-do CSV herunterladen", data=todo_df.to_csv(index=False).encode("utf-8"), file_name="todo.csv", mime="text/csv") + with col2: + st.markdown("### Kalender") + cal_df = load_csv(CALENDAR_FILE, CALENDAR_COLUMNS) + st.dataframe(cal_df, use_container_width=True) + st.download_button("Kalender CSV herunterladen", data=cal_df.to_csv(index=False).encode("utf-8"), file_name="calendar.csv", mime="text/csv") + """ +def run(cmd, check=True, capture=True): + return subprocess.run(cmd, cwd=REPO_PATH, check=check, text=True, capture_output=capture) +def git(*args, check=True): + return run(["git", *args], check=check) +def write_file(path, content): + full = REPO_PATH / path + full.parent.mkdir(parents=True, exist_ok=True) + full.write_text(content, encoding="utf-8") + print(f"OK wrote {path}") +def get_fetch_remote(): + remotes = git("remote", "-v").stdout.strip().splitlines() + if not remotes: + raise SystemExit("No git remotes configured") + for line in remotes: + parts = line.split() + if len(parts) >= 3 and parts[2] == "(fetch)": + return parts[0], parts[1] + parts = remotes[0].split() + return parts[0], parts[1] +def get_repo_owner_name(): + _, remote_url = get_fetch_remote() + if remote_url.endswith(".git"): + remote_url = remote_url[:-4] + if "://" in remote_url: + tail = remote_url.split("://", 1)[1] + tail = tail.split("/", 1)[1] + else: + tail = remote_url.split(":", 1)[1] + owner, repo = tail.split("/", 1) + return owner, repo +def main(): + if not REPO_PATH.exists(): + raise SystemExit(f"Repo path not found: {REPO_PATH}") + if not (REPO_PATH / ".git").exists(): + raise SystemExit(f"No .git directory in {REPO_PATH}") + print("Repo path:", REPO_PATH) + print("Branch:", git("branch", "--show-current").stdout.strip()) + print("Status:\n" + (git("status", "--short").stdout.strip() or "(clean)")) + print("Remotes:\n" + git("remote", "-v").stdout.strip()) + remote_name, remote_url = get_fetch_remote() + print(f"Using remote: {remote_name} -> {remote_url}") + owner, repo = get_repo_owner_name() + print(f"Detected repo: {owner}/{repo}") + for path, content in FILES.items(): + write_file(path, content) + write_file("app.py", APP_PY) + py = subprocess.run([sys.executable, "-m", "py_compile", "app.py"], cwd=REPO_PATH, text=True, capture_output=True) + if py.returncode != 0: + print(py.stdout) + print(py.stderr) + raise SystemExit("app.py syntax check failed") + git("add", "README.md", "docs", "sqlite", "requirements.txt", "app.py") + if git("diff", "--cached", "--quiet", check=False).returncode == 0: + print("No changes to commit.") + else: + git("commit", "-m", "docs: add todo-calendar workflow and streamlit form") + print("Committed.") + branch = git("branch", "--show-current").stdout.strip() + git("push", remote_name, branch) + print("Pushed to origin remote:", remote_name) +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/docs/00_overview.md b/docs/00_overview.md index 5ddd574..f9185da 100644 --- a/docs/00_overview.md +++ b/docs/00_overview.md @@ -1,52 +1,32 @@ - - Überblick - - Dieses Projekt ist ein lokales Arbeits- und Dokumentationsprojekt für persönliche Organisation. - - Zweck - - Das Projekt soll folgende Bereiche abdecken: - - • To-dos - • Kalendertermine - • Projektdokumentation - • später Bitcoin-Tracking - • später SQLite-basierte Auswertung - - Leitprinzipien - - 1. Daten sauber strukturieren - 2. Eingaben einfach halten - 3. CSV als Zwischenformat nutzbar machen - 4. später SQL-Backend ergänzen - 5. lokale Kontrolle behalten - - Arbeitsweise - - Es gibt zwei Ebenen: - - 1. Erfassung - - Neue Einträge werden über eine Eingabemaske erfasst. - - 2. Speicherung - - Die Daten werden in CSV abgelegt und später in SQLite überführt. - - Zielbild - - Am Ende soll das Projekt enthalten: - - • eine einfache Eingabemaske - • eine saubere Dokumentation - • exportierbare Daten - • ein lokales Dashboard - • Kalenderanbindung - • To-do-Verwaltung - • später Bitcoin-Verwaltung - - Status - - Der Fokus liegt aktuell auf Dokumentation, Kalender und To-do. - Bitcoin-Tracking wird später ergänzt. - +# Überblick + Dieses Projekt ist ein lokales Arbeits- und Dokumentationsprojekt für persönliche Organisation. + ## Zweck + Das Projekt soll folgende Bereiche abdecken: + - To-dos + - Kalendertermine + - Projektdokumentation + - später Bitcoin-Tracking + - später SQLite-basierte Auswertung + ## Leitprinzipien + 1. Daten sauber strukturieren + 2. Eingaben einfach halten + 3. CSV als Zwischenformat nutzbar machen + 4. später SQL-Backend ergänzen + 5. lokale Kontrolle behalten + ## Arbeitsweise + ### 1. Erfassung + Neue Einträge werden über eine Eingabemaske erfasst. + ### 2. Speicherung + Die Daten werden in CSV abgelegt und später in SQLite überführt. + ## Zielbild + Am Ende soll das Projekt enthalten: + - eine einfache Eingabemaske + - eine saubere Dokumentation + - exportierbare Daten + - ein lokales Dashboard + - Kalenderanbindung + - To-do-Verwaltung + - später Bitcoin-Verwaltung + ## Status + Der Fokus liegt aktuell auf Dokumentation, Kalender und To-do. Bitcoin-Tracking wird später ergänzt. + \ No newline at end of file diff --git a/docs/01_requirements.md b/docs/01_requirements.md new file mode 100644 index 0000000..0ac784c --- /dev/null +++ b/docs/01_requirements.md @@ -0,0 +1,40 @@ +# Anforderungen + ## Hauptziel + Das Projekt soll eine einfache und verlässliche Verwaltung von To-dos und Kalenderterminen ermöglichen. + ## Funktionale Anforderungen + ### To-dos + - To-dos sollen über eine Eingabemaske erfasst werden + - Pflichtfelder müssen vorhanden sein + - feste Werte sollen per Dropdown gewählt werden + - die Daten sollen als CSV exportierbar sein + ### Kalender + - Kalendertermine sollen über eine Eingabemaske erfasst werden + - Datum und Uhrzeit müssen wählbar sein + - Terminarten sollen per Dropdown auswählbar sein + - Wiederholungen sollen unterstützt werden + - Erinnerungen sollen vorbereitet sein + ### Datenhaltung + - CSV als Zwischenspeicher + - später SQLite als Datenbasis + - klare Trennung von Rohdaten und verarbeiteten Daten + ## Nicht-funktionale Anforderungen + - lokale Nutzung ohne Cloud-Zwang + - später synchronisierbar mit Google Calendar oder iCloud + - leicht verständlich und wartbar + - importfreundlich für SQLite + - dokumentierbar und versionierbar + ## UI-Anforderungen + Die Eingabemaske soll enthalten: + - Pflichtfelder + - Dropdowns + - Datumsauswahl + - Uhrzeitauswahl + - Textfelder für Notizen + - klare Trennung zwischen To-do und Kalender + ## Datenqualität + - einheitliche Schreibweisen + - feste Statuswerte + - saubere Datumsformate + - keine freien Sonderformate in Pflichtfeldern + - keine Mischfelder für unterschiedliche Datentypen + \ No newline at end of file diff --git a/docs/02_todo_calendar.md b/docs/02_todo_calendar.md new file mode 100644 index 0000000..7a11174 --- /dev/null +++ b/docs/02_todo_calendar.md @@ -0,0 +1,63 @@ +# To-do- und Kalenderkonzept + ## Ziel + Dieses Projekt soll eine einfache Möglichkeit bieten, Aufgaben und Termine zentral zu erfassen. + ## To-do-Felder + - id + - titel + - beschreibung + - kategorie + - prioritaet + - status + - faellig_am + - wiederholen + - notiz + ## Kalender-Felder + - id + - titel + - datum + - uhrzeit + - dauer_min + - kalender + - kategorie + - erinnerung_min + - wiederholen + - notiz + ## Dropdown-Werte + ### Priorität + - Niedrig + - Mittel + - Hoch + ### Status + - Offen + - In Arbeit + - Wartet + - Erledigt + ### Wiederholen + - Nein + - Täglich + - Wöchentlich + - Monatlich + ### Kalender + - Arbeit + - Privat + - Bitcoin + - Todo + ### Kategorie + - Allgemein + - Dokumentation + - Erinnerung + - Termin + - Aufgabe + - Sonstiges + ## Eingaberegeln + - Pflichtfelder müssen ausgefüllt sein + - freie Texte nur dort, wo sie sinnvoll sind + - Datum und Uhrzeit strikt getrennt + - Wiederholungen nur aus erlaubten Werten + - Kalenderziel muss auswählbar sein + ## Workflow + 1. Eintrag über Maske anlegen + 2. Daten als CSV speichern + 3. später nach SQLite importieren + 4. optional an Google Calendar oder iCloud übergeben + \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 1e51534..bd81dc2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ streamlit -pandas - + pandas + \ No newline at end of file diff --git a/sqlite/schema.sql b/sqlite/schema.sql index cbc0fc6..33c46ae 100644 --- a/sqlite/schema.sql +++ b/sqlite/schema.sql @@ -1,5 +1,4 @@ - ─ sql - PRAGMA foreign_keys = ON; +PRAGMA foreign_keys = ON; CREATE TABLE IF NOT EXISTS todo ( id INTEGER PRIMARY KEY AUTOINCREMENT, titel TEXT NOT NULL, @@ -35,3 +34,4 @@ CREATE INDEX IF NOT EXISTS idx_todo_status ON todo(status); CREATE INDEX IF NOT EXISTS idx_calendar_event_datum ON calendar_event(datum); CREATE INDEX IF NOT EXISTS idx_calendar_event_kalender ON calendar_event(kalender); + \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..3972ddc --- /dev/null +++ b/test.py @@ -0,0 +1,424 @@ + + import json + import os + import subprocess + import sys + from pathlib import Path + from urllib.request import Request, urlopen + from urllib.error import HTTPError, URLError + REPO_PATH = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else Path.cwd() + GITEA_BASE = os.environ.get("GITEA_BASE", "https://gitea.schaefinik.xyz") + GITEA_TOKEN = os.environ.get("GITEA_TOKEN", "").strip() + FILES = { + "README.md": """# bitcoin-programm + Lokales Projekt zur strukturierten Erfassung und Verwaltung von: + - To-dos + - Kalendereinträgen + - Dokumentation + - später: Bitcoin-Transaktionen und Auswertungen + ## Ziel + Das Projekt dient als zentrales Arbeitsverzeichnis für persönliche Verwaltung und spätere Automatisierung. + Geplant ist: + - eine einfache Eingabemaske für To-dos und Kalendertermine + - CSV als einfache Zwischen- und Exportform + - später ein SQLite-Backend für Auswertung und Dashboard + - optional Sync mit Google Calendar oder iCloud Kalender + ## Aktueller Fokus + Der aktuelle Fokus liegt auf: + 1. Dokumentation + 2. To-do-Erfassung + 3. Kalender-Erfassung + 4. saubere Datenstruktur für späteren Import nach SQLite + Bitcoin-Tracking kann später ergänzt werden. + """, + "docs/00_overview.md": """# Überblick + Dieses Projekt ist ein lokales Arbeits- und Dokumentationsprojekt für persönliche Organisation. + ## Zweck + Das Projekt soll folgende Bereiche abdecken: + - To-dos + - Kalendertermine + - Projektdokumentation + - später Bitcoin-Tracking + - später SQLite-basierte Auswertung + ## Leitprinzipien + 1. Daten sauber strukturieren + 2. Eingaben einfach halten + 3. CSV als Zwischenformat nutzbar machen + 4. später SQL-Backend ergänzen + 5. lokale Kontrolle behalten + ## Arbeitsweise + ### 1. Erfassung + Neue Einträge werden über eine Eingabemaske erfasst. + ### 2. Speicherung + Die Daten werden in CSV abgelegt und später in SQLite überführt. + ## Zielbild + Am Ende soll das Projekt enthalten: + - eine einfache Eingabemaske + - eine saubere Dokumentation + - exportierbare Daten + - ein lokales Dashboard + - Kalenderanbindung + - To-do-Verwaltung + - später Bitcoin-Verwaltung + ## Status + Der Fokus liegt aktuell auf Dokumentation, Kalender und To-do. Bitcoin-Tracking wird später ergänzt. + """, + "docs/01_requirements.md": """# Anforderungen + ## Hauptziel + Das Projekt soll eine einfache und verlässliche Verwaltung von To-dos und Kalenderterminen ermöglichen. + ## Funktionale Anforderungen + ### To-dos + - To-dos sollen über eine Eingabemaske erfasst werden + - Pflichtfelder müssen vorhanden sein + - feste Werte sollen per Dropdown gewählt werden + - die Daten sollen als CSV exportierbar sein + ### Kalender + - Kalendertermine sollen über eine Eingabemaske erfasst werden + - Datum und Uhrzeit müssen wählbar sein + - Terminarten sollen per Dropdown auswählbar sein + - Wiederholungen sollen unterstützt werden + - Erinnerungen sollen vorbereitet sein + ### Datenhaltung + - CSV als Zwischenspeicher + - später SQLite als Datenbasis + - klare Trennung von Rohdaten und verarbeiteten Daten + ## Nicht-funktionale Anforderungen + - lokale Nutzung ohne Cloud-Zwang + - später synchronisierbar mit Google Calendar oder iCloud + - leicht verständlich und wartbar + - importfreundlich für SQLite + - dokumentierbar und versionierbar + ## UI-Anforderungen + Die Eingabemaske soll enthalten: + - Pflichtfelder + - Dropdowns + - Datumsauswahl + - Uhrzeitauswahl + - Textfelder für Notizen + - klare Trennung zwischen To-do und Kalender + ## Datenqualität + - einheitliche Schreibweisen + - feste Statuswerte + - saubere Datumsformate + - keine freien Sonderformate in Pflichtfeldern + - keine Mischfelder für unterschiedliche Datentypen + """, + "docs/02_todo_calendar.md": """# To-do- und Kalenderkonzept + ## Ziel + Dieses Projekt soll eine einfache Möglichkeit bieten, Aufgaben und Termine zentral zu erfassen. + ## To-do-Felder + - id + - titel + - beschreibung + - kategorie + - prioritaet + - status + ## Kalender-Felder + • später synchronisierbar mit Google Calendar oder iCloud + • leicht verständlich und wartbar + • importfreundlich für SQLite + • dokumentierbar und versionierbar + + UI-Anforderungen + + Die Eingabemaske soll enthalten: + + • Pflichtfelder + • Dropdowns + • Datumsauswahl + • Uhrzeitauswahl + • Textfelder für Notizen + • klare Trennung zwischen To-do und Kalender + + Datenqualität + + • einheitliche Schreibweisen + • feste Statuswerte + • saubere Datumsformate + • keine freien Sonderformate in Pflichtfeldern + • keine Mischfelder für unterschiedliche Datentypen + + """, + "docs/02_todo_calendar.md": """# To-do- und Kalenderkonzept + + Ziel + + Dieses Projekt soll eine einfache Möglichkeit bieten, Aufgaben und Termine zentral zu erfassen. + + To-do-Felder + + • id + • titel + • beschreibung + • kategorie + • prioritaet + • status + • faellig_am + • wiederholen + • notiz + + Kalender-Felder + + • id + • titel + • datum + • uhrzeit + • dauer_min + • kalender + • kategorie + • erinnerung_min + • wiederholen + • notiz + + Dropdown-Werte + + Priorität + + • Niedrig + • Mittel + • Hoch + + Status + + • Offen + • In Arbeit + Kategorie + + • Pflichtfelder müssen ausgefüllt sein + 2. Daten als CSV speichern + beschreibung TEXT, + faellig_am TEXT, + id INTEGER PRIMARY KEY AUTOINCREMENT, ( + + "requirements.txt": """streamlit + + + return 1 + ig(page_title="Bitcoin-Programm", layout="wide") + st.set_page_config(page_title="Bitcoin-Programm", layout="wide") nt(kalender); + faellig_am = st.date_input("Fällig am", value=date.today()) + + "id": new_todo_id, + "faellig_am": faellig_am.isoformat(), + st.markdown("### To-dos") + st.download_button("Kalender CSV herunterladen", data=cal_df.to_csv(index=False).encode("utf-8"), + tail = tail.split("/", 1)[1] + tail = remote_url.split(":", 1)[1] + prioritaetPTEXTeNOTtNULL DEFAULT 'Mittel', ("utf-8"), + status TEXT NOT NULL DEFAULTt'Offen',und: {REPO_PATH}") + faellig_am TEXT, + wiederholen TEXT NOT NULL DEFAULT 'Nein', {REPO_PATH}") + notiz TEXT, + prcreated_atpTEXT"NOTENULLADEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULTs(datetime('now'))out.strip()) de("utf-8"), + ); "(clean)")) + CREATE TABLE IF NOT EXISTS calendar_event ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + retitelnTEXT NOToNULL,l = get_fetch_remote() + datum TEXT NOT NULL, te_url}") utput=capture) + uhrzeit TEXT NOT NULL, + dauer_min INTEGER NOT NULL DEFAULT}60, + kalender TEXT NOT NULL DEFAULT 'Privat', + fokategorieoTEXTtNOT NULLSDEFAULT)'Sonstiges', + erinnerung_min INTEGER NOT NULL DEFAULT 30, + wiederholen TEXT NOT NULL DEFAULT 'Nein', + notiz TEXT, + pycreated_ateTEXTuNOTsNULLxDEFAULTe(datetime('now')),e", "app.py"], cwd=REPO_PATH, text=True, + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + ); != 0: + CREATE TABLE IF NOT EXISTS settings ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL"app.py syntax check failed") + ); + CREATE"INDEX IFENOTEEXISTS"idx_todo_faellig_ameONitodo(faellig_am);p.py") + CREATE INDEX IF NOT EXISTS idx_todo_status ON todo(status); + CREATE INDEX IF NOT EXISTS idx_calendar_event_datum ON calendar_event(datum); + CREATE INDEX IF NOT EXISTS idx_calendar_event_kalender ON calendar_event(kalender); + """, it("commit", "-m", "docs: add todo-calendar workflow and streamlit form") + "requirements.txt": """streamlit + pandas + """ranch = git("branch", "--show-current").stdout.strip() + } + APP_PY = """import streamlit as st, remote_name) + import pandas as pd + iffromapathlib importnPath + from datetime import date, time + ─ ◉ DATA_DIR = Path("data") e + TODO_FILE = DATA_DIR / "todo.csv" + CALENDAR_FILEz=zDATA_DIRe/a"calendar.csv" + DATA_DIR.modir(parents=True, exist_ok=True)rn dein benannter Remote `bitcoin-programm` + TODO_COLUMNS = [ t a d t e + ─ ( "id","titel","beschreibung","kategorie","prioritaet","status", e + ─ "faellig_am","wiederholen","notiz" e + ]- + CALENDAR_COLUMNSe=u[g / Kontext-Zusammenfassung + P "id","titel","datum","uhrzeit","dauer_min","kalender","kategorie", + ─ ٩ "erinnerung_min","wiederholen","notiz"o-dos, Kalender und später Bitcoin-Tracking {"title + ─ ( ] e : T o e b s L ext=True, {"title + bit def load_csv(pathe Path, columns: list[str]) -> pd.DataFrame: ` chaefinik.xyz/admin/bitcoin` + ─ ( if path.exists(): e + **Sicherdfi=spd.read_csv(path)cht im Chat, nur via `GITEA_TOKEN` in der Host-Umgebung + ─ ( for col in columns: e r u s i r d r pt statt direkter e + if col not in df.columns: + ridf[col]c=i"" lokal ausführen, dann `git status`/Commit/Push prüfen + return df[columns] n o n k r Z nfassung für + ─ ( • return pd.DatmFrame(columns=columns) e E e {"title + def save_csv(df: p .DataFram , path: Path) -> None: + df.to_csv(path, index=False) + ─ (´・def)next_id(df: pd.DataFrame)p->5int:ini none │ 94k/272k │ [████░░░░░░] 35% │ 2h 20m ─ {"title + if df.empty or "id" not in df.columns: + return 1 + try: + return int(df["id"].max()) + 1 + except Exception: + return 1 + st.set_page_config(page_title="Bitcoin-Programm", layout="wide") + st.title("Bitcoin-Programm – To-do & Kalender") + tab1, tab2, tab3 = st.tabs(["To-do anlegen", "Kalendertermin anlegen", "Daten ansehen"]) + with tab1: + st.subheader("Neues To-do") + todo_df = load_csv(TODO_FILE, TODO_COLUMNS) + new_todo_id = next_id(todo_df) + with st.form("todo_form", clear_on_submit=True): + titel = st.text_input("Titel") + beschreibung = st.text_area("Beschreibung") + kategorie = st.selectbox("Kategorie", ["Allgemein", "Bitcoin", "Dokumentation", "Kalender", "Admin", + "Sonstiges"]) + prioritaet = st.selectbox("Priorität", ["Niedrig", "Mittel", "Hoch"]) + status = st.selectbox("Status", ["Offen", "In Arbeit", "Wartet", "Erledigt"]) + faellig_am = st.date_input("Fällig am", value=date.today()) + wiederholen = st.selectbox("Wiederholen", ["Nein", "Täglich", "Wöchentlich", "Monatlich"]) + notiz = st.text_area("Notiz") + submitted = st.form_submit_button("To-do speichern") + if submitted: + if not titel.strip(): + st.error("Bitte mindestens einen Titel eingeben.") + else: + new_row = pd.DataFrame([{ + "id": new_todo_id, + "titel": titel, + "beschreibung": beschreibung, + "kategorie": kategorie, + "prioritaet": prioritaet, + "status": status, + "faellig_am": faellig_am.isoformat(), + "wiederholen": wiederholen, + "notiz": notiz, + }]) + todo_df = pd.concat([todo_df, new_row], ignore_index=True) + save_csv(todo_df, TODO_FILE) + st.success("To-do gespeichert.") + with tab2: + st.subheader("Neuen Kalendereintrag") + cal_df = load_csv(CALENDAR_FILE, CALENDAR_COLUMNS) + new_cal_id = next_id(cal_df) + with st.form("calendar_form", clear_on_submit=True): + titel = st.text_input("Titel") + datum = st.date_input("Datum", value=date.today()) + uhrzeit = st.time_input("Uhrzeit", value=time(9, 0)) + dauer_min = st.number_input("Dauer in Minuten", min_value=5, max_value=1440, value=60, step=5) + kalender = st.selectbox("Kalender", ["Arbeit", "Privat", "Bitcoin", "Todo"]) + kategorie = st.selectbox("Kategorie", ["Meeting", "Aufgabe", "Erinnerung", "Termin", "Sonstiges"]) + erinnerung_min = st.number_input("Erinnerung vorab in Minuten", min_value=0, max_value=10080, + value=30, step=5) + wiederholen = st.selectbox("Wiederholen", ["Nein", "Täglich", "Wöchentlich", "Monatlich"]) + notiz = st.text_area("Notiz") + submitted = st.form_submit_button("Kalendertermin speichern") + if submitted: + if not titel.strip(): + st.error("Bitte mindestens einen Titel eingeben.") + else: + new_row = pd.DataFrame([{ + "id": new_cal_id, + "titel": titel, + "datum": datum.isoformat(), + "uhrzeit": uhrzeit.strftime("%H:%M"), + "dauer_min": int(dauer_min), + "kalender": kalender, + "kategorie": kategorie, + "erinnerung_min": int(erinnerung_min), + "wiederholen": wiederholen, + "notiz": notiz, + }]) + cal_df = pd.concat([cal_df, new_row], ignore_index=True) + save_csv(cal_df, CALENDAR_FILE) + st.success("Kalendertermin gespeichert.") + with tab3: + st.subheader("Gespeicherte Daten") + col1, col2 = st.columns(2) + with col1: + st.markdown("### To-dos") + todo_df = load_csv(TODO_FILE, TODO_COLUMNS) + st.dataframe(todo_df, use_container_width=True) + st.download_button("To-do CSV herunterladen", data=todo_df.to_csv(index=False).encode("utf-8"), + file_name="todo.csv", mime="text/csv") + with col2: + st.markdown("### Kalender") + cal_df = load_csv(CALENDAR_FILE, CALENDAR_COLUMNS) + st.dataframe(cal_df, use_container_width=True) + st.download_button("Kalender CSV herunterladen", data=cal_df.to_csv(index=False).encode("utf-8"), + file_name="calendar.csv", mime="text/csv") + """ + def run(cmd, check=True, capture=True): + return subprocess.run(cmd, cwd=REPO_PATH, check=check, text=True, capture_output=capture) + def git(*args, check=True): + return run(["git", *args], check=check) + def write_file(path, content): + full = REPO_PATH / path + full.parent.mkdir(parents=True, exist_ok=True) + full.write_text(content, encoding="utf-8") + print(f"OK wrote {path}") + def get_fetch_remote(): + remotes = git("remote", "-v").stdout.strip().splitlines() + if not remotes: + raise SystemExit("No git remotes configured") + for line in remotes: + parts = line.split() + if len(parts) >= 3 and parts[2] == "(fetch)": + return parts[0], parts[1] + parts = remotes[0].split() + return parts[0], parts[1] + def get_repo_owner_name(): + _, remote_url = get_fetch_remote() + if remote_url.endswith(".git"): + remote_url = remote_url[:-4] + if "://" in remote_url: + tail = remote_url.split("://", 1)[1] + tail = tail.split("/", 1)[1] + else: + tail = remote_url.split(":", 1)[1] + owner, repo = tail.split("/", 1) + return owner, repo + def main(): + if not REPO_PATH.exists(): + raise SystemExit(f"Repo path not found: {REPO_PATH}") + if not (REPO_PATH / ".git").exists(): + raise SystemExit(f"No .git directory in {REPO_PATH}") + print("Repo path:", REPO_PATH) + print("Branch:", git("branch", "--show-current").stdout.strip()) + print("Status:\n" + (git("status", "--short").stdout.strip() or "(clean)")) + print("Remotes:\n" + git("remote", "-v").stdout.strip()) + remote_name, remote_url = get_fetch_remote() + print(f"Using remote: {remote_name} -> {remote_url}") + owner, repo = get_repo_owner_name() + print(f"Detected repo: {owner}/{repo}") + for path, content in FILES.items(): + write_file(path, content) + write_file("app.py", APP_PY) + py = subprocess.run([sys.executable, "-m", "py_compile", "app.py"], cwd=REPO_PATH, text=True, + capture_output=True) + if py.returncode != 0: + print(py.stdout) + print(py.stderr) + raise SystemExit("app.py syntax check failed") + git("add", "README.md", "docs", "sqlite", "requirements.txt", "app.py") + if git("diff", "--cached", "--quiet", check=False).returncode == 0: + print("No changes to commit.") + else: + git("commit", "-m", "docs: add todo-calendar workflow and streamlit form") + print("Committed.") + branch = git("branch", "--show-current").stdout.strip() + git("push", remote_name, branch) + print("Pushed to origin remote:", remote_name) + if __name__ == "__main__": + main()