dritter commit, hoffentlich sind grundfunktionen da
This commit is contained in:
@@ -1,79 +1,21 @@
|
||||
bitcoin-programm
|
||||
|
||||
# bitcoin-programm
|
||||
Lokales Projekt zur strukturierten Erfassung und Verwaltung von:
|
||||
|
||||
• To-dos
|
||||
• Kalendereinträgen
|
||||
• Dokumentation
|
||||
• später: Bitcoin-Transaktionen und Auswertungen
|
||||
|
||||
Ziel
|
||||
|
||||
- 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
|
||||
|
||||
- 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.
|
||||
|
||||
@@ -1,35 +1,20 @@
|
||||
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:
|
||||
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:
|
||||
@@ -37,20 +22,19 @@ def load_csv(path: Path, columns: list[str]) -> pd.DataFrame:
|
||||
df[col] = ""
|
||||
return df[columns]
|
||||
return pd.DataFrame(columns=columns)
|
||||
def save_csv(df: pd.DataFrame, path: Path) -> None:
|
||||
def save_csv(df: pd.DataFrame, path: Path) -> None:
|
||||
df.to_csv(path, index=False)
|
||||
def next_id(df: pd.DataFrame) -> int:
|
||||
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:
|
||||
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",
|
||||
)
|
||||
st.download_button("Kalender CSV herunterladen", data=cal_df.to_csv(index=False).encode("utf-8"), file_name="calendar.csv", mime="text/csv")
|
||||
|
||||
@@ -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()
|
||||
+21
-41
@@ -1,52 +1,32 @@
|
||||
|
||||
Überblick
|
||||
|
||||
# Überblick
|
||||
Dieses Projekt ist ein lokales Arbeits- und Dokumentationsprojekt für persönliche Organisation.
|
||||
|
||||
Zweck
|
||||
|
||||
## Zweck
|
||||
Das Projekt soll folgende Bereiche abdecken:
|
||||
|
||||
• To-dos
|
||||
• Kalendertermine
|
||||
• Projektdokumentation
|
||||
• später Bitcoin-Tracking
|
||||
• später SQLite-basierte Auswertung
|
||||
|
||||
Leitprinzipien
|
||||
|
||||
- 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
|
||||
|
||||
## Arbeitsweise
|
||||
### 1. Erfassung
|
||||
Neue Einträge werden über eine Eingabemaske erfasst.
|
||||
|
||||
2. Speicherung
|
||||
|
||||
### 2. Speicherung
|
||||
Die Daten werden in CSV abgelegt und später in SQLite überführt.
|
||||
|
||||
Zielbild
|
||||
|
||||
## 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.
|
||||
- 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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
streamlit
|
||||
pandas
|
||||
pandas
|
||||
|
||||
+2
-2
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user