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 streamlit as st
import pandas as pd import pandas as pd
from pathlib import Path from pathlib import Path
from datetime import date, time from datetime import date, time
DATA_DIR = Path("data") DATA_DIR = Path("data")
TODO_FILE = DATA_DIR / "todo.csv" TODO_FILE = DATA_DIR / "todo.csv"
CALENDAR_FILE = DATA_DIR / "calendar.csv" CALENDAR_FILE = DATA_DIR / "calendar.csv"
DATA_DIR.mkdir(parents=True, exist_ok=True) DATA_DIR.mkdir(parents=True, exist_ok=True)
TODO_COLUMNS = [ TODO_COLUMNS = [
"id", "id","titel","beschreibung","kategorie","prioritaet","status",
"titel", "faellig_am","wiederholen","notiz"
"beschreibung", ]
"kategorie", CALENDAR_COLUMNS = [
"prioritaet", "id","titel","datum","uhrzeit","dauer_min","kalender","kategorie",
"status", "erinnerung_min","wiederholen","notiz"
"faellig_am", ]
"wiederholen", def load_csv(path: Path, columns: list[str]) -> pd.DataFrame:
"notiz", if path.exists():
] df = pd.read_csv(path)
CALENDAR_COLUMNS = [ for col in columns:
"id", if col not in df.columns:
"titel", df[col] = ""
"datum", return df[columns]
"uhrzeit", return pd.DataFrame(columns=columns)
"dauer_min", def save_csv(df: pd.DataFrame, path: Path) -> None:
"kalender", df.to_csv(path, index=False)
"kategorie", def next_id(df: pd.DataFrame) -> int:
"erinnerung_min", if df.empty or "id" not in df.columns:
"wiederholen", return 1
"notiz", try:
] return int(df["id"].max()) + 1
def load_csv(path: Path, columns: list[str]) -> pd.DataFrame: except Exception:
if path.exists(): return 1
df = pd.read_csv(path) st.set_page_config(page_title="Bitcoin-Programm", layout="wide")
for col in columns: st.title("Bitcoin-Programm – To-do & Kalender")
if col not in df.columns: tab1, tab2, tab3 = st.tabs(["To-do anlegen", "Kalendertermin anlegen", "Daten ansehen"])
df[col] = "" with tab1:
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:
st.subheader("Neues To-do") st.subheader("Neues To-do")
todo_df = load_csv(TODO_FILE, TODO_COLUMNS) todo_df = load_csv(TODO_FILE, TODO_COLUMNS)
new_todo_id = next_id(todo_df) new_todo_id = next_id(todo_df)
@@ -82,8 +66,7 @@ with tab1:
todo_df = pd.concat([todo_df, new_row], ignore_index=True) todo_df = pd.concat([todo_df, new_row], ignore_index=True)
save_csv(todo_df, TODO_FILE) save_csv(todo_df, TODO_FILE)
st.success("To-do gespeichert.") st.success("To-do gespeichert.")
# --- Kalender Formular --- with tab2:
with tab2:
st.subheader("Neuen Kalendereintrag") st.subheader("Neuen Kalendereintrag")
cal_df = load_csv(CALENDAR_FILE, CALENDAR_COLUMNS) cal_df = load_csv(CALENDAR_FILE, CALENDAR_COLUMNS)
new_cal_id = next_id(cal_df) new_cal_id = next_id(cal_df)
@@ -117,27 +100,17 @@ with tab2:
cal_df = pd.concat([cal_df, new_row], ignore_index=True) cal_df = pd.concat([cal_df, new_row], ignore_index=True)
save_csv(cal_df, CALENDAR_FILE) save_csv(cal_df, CALENDAR_FILE)
st.success("Kalendertermin gespeichert.") st.success("Kalendertermin gespeichert.")
# --- Daten ansehen --- with tab3:
with tab3:
st.subheader("Gespeicherte Daten") st.subheader("Gespeicherte Daten")
col1, col2 = st.columns(2) col1, col2 = st.columns(2)
with col1: with col1:
st.markdown("### To-dos") st.markdown("### To-dos")
todo_df = load_csv(TODO_FILE, TODO_COLUMNS) todo_df = load_csv(TODO_FILE, TODO_COLUMNS)
st.dataframe(todo_df, use_container_width=True) 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: with col2:
st.markdown("### Kalender") st.markdown("### Kalender")
cal_df = load_csv(CALENDAR_FILE, CALENDAR_COLUMNS) cal_df = load_csv(CALENDAR_FILE, CALENDAR_COLUMNS)
st.dataframe(cal_df, use_container_width=True) st.dataframe(cal_df, use_container_width=True)
st.download_button( st.download_button("Kalender CSV herunterladen", data=cal_df.to_csv(index=False).encode("utf-8"), file_name="calendar.csv", mime="text/csv")
"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",
)
+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
Überblick Dieses Projekt ist ein lokales Arbeits- und Dokumentationsprojekt für persönliche Organisation.
## Zweck
Dieses Projekt ist ein lokales Arbeits- und Dokumentationsprojekt für persönliche Organisation. Das Projekt soll folgende Bereiche abdecken:
- To-dos
Zweck - Kalendertermine
- Projektdokumentation
Das Projekt soll folgende Bereiche abdecken: - später Bitcoin-Tracking
- später SQLite-basierte Auswertung
• To-dos ## Leitprinzipien
• Kalendertermine 1. Daten sauber strukturieren
• Projektdokumentation 2. Eingaben einfach halten
• später Bitcoin-Tracking 3. CSV als Zwischenformat nutzbar machen
• später SQLite-basierte Auswertung 4. später SQL-Backend ergänzen
5. lokale Kontrolle behalten
Leitprinzipien ## Arbeitsweise
### 1. Erfassung
1. Daten sauber strukturieren Neue Einträge werden über eine Eingabemaske erfasst.
2. Eingaben einfach halten ### 2. Speicherung
3. CSV als Zwischenformat nutzbar machen Die Daten werden in CSV abgelegt und später in SQLite überführt.
4. später SQL-Backend ergänzen ## Zielbild
5. lokale Kontrolle behalten Am Ende soll das Projekt enthalten:
- eine einfache Eingabemaske
Arbeitsweise - eine saubere Dokumentation
- exportierbare Daten
Es gibt zwei Ebenen: - ein lokales Dashboard
- Kalenderanbindung
1. Erfassung - To-do-Verwaltung
- später Bitcoin-Verwaltung
Neue Einträge werden über eine Eingabemaske erfasst. ## Status
Der Fokus liegt aktuell auf Dokumentation, Kalender und To-do. Bitcoin-Tracking wird später ergänzt.
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 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 ( CREATE TABLE IF NOT EXISTS todo (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
titel TEXT NOT NULL, 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_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_datum ON calendar_event(datum);
CREATE INDEX IF NOT EXISTS idx_calendar_event_kalender ON calendar_event(kalender); 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()