dritter commit, hoffentlich sind grundfunktionen da

This commit is contained in:
Dominik Schäfer
2026-08-26 06:44:54 +07:00
parent 57d4d4d8f0
commit 5ab4fc5123
9 changed files with 1011 additions and 203 deletions
+20 -78
View File
@@ -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
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.
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.
+41 -68
View File
@@ -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",
)
st.download_button("Kalender CSV herunterladen", data=cal_df.to_csv(index=False).encode("utf-8"), file_name="calendar.csv", mime="text/csv")
+386
View File
@@ -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()
+31 -51
View File
@@ -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.
+40
View File
@@ -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
+63
View File
@@ -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
View File
@@ -1,3 +1,3 @@
streamlit
pandas
pandas
+2 -2
View File
@@ -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);
+424
View File
@@ -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()