haha yes it works now

This commit is contained in:
Frank Schwenk
2026-06-10 10:38:43 +02:00
parent c9a86f1e0e
commit b5390b8ced
5 changed files with 77 additions and 24 deletions
+23 -15
View File
@@ -22,7 +22,7 @@ MOTIVATION_SYSTEM = (
) )
ORACLE_SYSTEM = ( ORACLE_SYSTEM = (
"Du bist das wöchentliche KI-Orakel einer ADHS-Medikamenten-App. " "Du bist das tägliche KI-Orakel einer ADHS-Medikamenten-App. "
"Passiv-aggressiv, trocken, max 4 Sätze auf Deutsch. Keine medizinischen Ratschläge." "Passiv-aggressiv, trocken, max 4 Sätze auf Deutsch. Keine medizinischen Ratschläge."
) )
@@ -93,8 +93,16 @@ def _motivation_key(day: str) -> str:
return f"motivation:v2:{day}" return f"motivation:v2:{day}"
def _oracle_key(week: str) -> str: def _oracle_key(day: str) -> str:
return f"oracle:v2:{week}" return f"oracle:v3:{day}"
async def _cached_oracle(db: aiosqlite.Connection, day: str) -> str | None:
cached = await _get_cache(db, _oracle_key(day))
if cached:
return cached
week = datetime.strptime(day, "%Y-%m-%d").strftime("%Y-W%W")
return await _get_cache(db, f"oracle:v2:{week}")
async def _cached_motivation(db: aiosqlite.Connection, day: str) -> str | None: async def _cached_motivation(db: aiosqlite.Connection, day: str) -> str | None:
@@ -151,16 +159,16 @@ async def get_roast_of_the_day(db: aiosqlite.Connection, tz: ZoneInfo) -> dict[s
async def generate_oracle( async def generate_oracle(
db: aiosqlite.Connection, db: aiosqlite.Connection,
tz: ZoneInfo, tz: ZoneInfo,
week: str | None = None, day: str | None = None,
*, *,
force: bool = False, force: bool = False,
) -> dict[str, Any]: ) -> dict[str, Any]:
week = week or datetime.now(tz).strftime("%Y-W%W") day = day or datetime.now(tz).strftime("%Y-%m-%d")
key = _oracle_key(week) key = _oracle_key(day)
if not force: if not force:
cached = await _get_cache(db, key) cached = await _cached_oracle(db, day)
if cached: if cached:
return {"text": cached, "source": "cache", "week": week} return {"text": cached, "source": "cache", "day": day}
stats = await compute_stats(db) stats = await compute_stats(db)
days_active = stats["days_active"] days_active = stats["days_active"]
@@ -178,7 +186,7 @@ async def generate_oracle(
else "" else ""
) )
prompt = ( prompt = (
f"Wöchentlicher Orakel-Report. {context}{caveat}" f"Täglicher Orakel-Report für heute. {context}{caveat}"
f"Streak: {stats['streak']} Tage. " f"Streak: {stats['streak']} Tage. "
f"Compliance über {window} Tag(e): {stats['compliance_percent']}%. " f"Compliance über {window} Tag(e): {stats['compliance_percent']}%. "
f"Genommen gesamt: {stats['total_taken']}." f"Genommen gesamt: {stats['total_taken']}."
@@ -197,17 +205,17 @@ async def generate_oracle(
else: else:
source = "ai" source = "ai"
await _set_cache(db, key, text) await _set_cache(db, key, text)
logger.info("Generated oracle for %s (%s)", week, source) logger.info("Generated daily oracle for %s (%s)", day, source)
return {"text": text, "source": source, "week": week} return {"text": text, "source": source, "day": day}
async def get_oracle(db: aiosqlite.Connection, tz: ZoneInfo) -> dict[str, Any]: async def get_oracle(db: aiosqlite.Connection, tz: ZoneInfo) -> dict[str, Any]:
week = datetime.now(tz).strftime("%Y-W%W") day = datetime.now(tz).strftime("%Y-%m-%d")
cached = await _get_cache(db, _oracle_key(week)) cached = await _cached_oracle(db, day)
if cached: if cached:
return {"text": cached, "source": "cache", "week": week} return {"text": cached, "source": "cache", "day": day}
return { return {
"text": "Das Orakel bereitet sich vor. Schau später nochmal rein.", "text": "Das Orakel bereitet sich vor. Schau später nochmal rein.",
"source": "pending", "source": "pending",
"week": week, "day": day,
} }
+2 -2
View File
@@ -37,7 +37,7 @@ async def _ensure_today_motivation() -> None:
await db.close() await db.close()
async def _ensure_weekly_oracle() -> None: async def _ensure_today_oracle() -> None:
config = load_meds_config() config = load_meds_config()
db = await get_db() db = await get_db()
try: try:
@@ -55,7 +55,7 @@ async def lifespan(app: FastAPI):
await db.close() await db.close()
start_scheduler() start_scheduler()
asyncio.create_task(_ensure_today_motivation()) asyncio.create_task(_ensure_today_motivation())
asyncio.create_task(_ensure_weekly_oracle()) asyncio.create_task(_ensure_today_oracle())
yield yield
+5 -5
View File
@@ -49,13 +49,13 @@ async def _generate_daily_motivation() -> None:
await db.close() await db.close()
async def _generate_weekly_oracle() -> None: async def _generate_daily_oracle() -> None:
config = load_meds_config() config = load_meds_config()
db = await get_db() db = await get_db()
try: try:
await generate_oracle(db, config.timezone) await generate_oracle(db, config.timezone)
except Exception: except Exception:
logger.exception("Weekly oracle generation failed") logger.exception("Daily oracle generation failed")
finally: finally:
await db.close() await db.close()
@@ -131,9 +131,9 @@ def start_scheduler() -> None:
) )
scheduler.add_job( scheduler.add_job(
_generate_weekly_oracle, _generate_daily_oracle,
trigger=CronTrigger(day_of_week="mon", hour=3, minute=5, timezone=tz), trigger=CronTrigger(hour=3, minute=5, timezone=tz),
id="weekly-oracle", id="daily-oracle",
replace_existing=True, replace_existing=True,
) )
+46 -1
View File
@@ -8,6 +8,7 @@ import aiosqlite
from server.config_loader import load_meds_config, today_str from server.config_loader import load_meds_config, today_str
from server.db import new_id, utc_now_iso from server.db import new_id, utc_now_iso
from server.slots import compute_status, get_log_for_day, get_snooze_for_day
async def get_first_intake_day(db: aiosqlite.Connection, tz: ZoneInfo) -> str: async def get_first_intake_day(db: aiosqlite.Connection, tz: ZoneInfo) -> str:
@@ -88,11 +89,55 @@ async def day_compliance(db: aiosqlite.Connection, day: str, slot_ids: list[str]
return int(row[0]) >= len(slot_ids) if row else False return int(row[0]) >= len(slot_ids) if row else False
async def _today_streak_state(
db: aiosqlite.Connection,
tz: ZoneInfo,
slot_ids: list[str],
) -> str:
"""Return 'complete', 'failed', or 'pending' for today."""
config = load_meds_config()
day = today_str(tz)
logs = await get_log_for_day(db, day)
slots_by_id = {s.id: s for s in config.slots}
for sid in slot_ids:
entry = logs.get(sid)
if entry and entry["status"] == "missed":
return "failed"
now = datetime.now(tz)
snoozes = await get_snooze_for_day(db, day)
all_taken = True
for sid in slot_ids:
entry = logs.get(sid)
if entry and entry["status"] == "taken":
continue
all_taken = False
slot = slots_by_id[sid]
status = compute_status(slot, now, tz, entry, snoozes.get(sid))
if status == "overdue":
return "failed"
if all_taken:
return "complete"
return "pending"
async def compute_streak(db: aiosqlite.Connection, tz: ZoneInfo) -> int: async def compute_streak(db: aiosqlite.Connection, tz: ZoneInfo) -> int:
config = load_meds_config() config = load_meds_config()
slot_ids = [s.id for s in config.slots] slot_ids = [s.id for s in config.slots]
if not slot_ids:
return 0
today = datetime.now(tz).date()
today_state = await _today_streak_state(db, tz, slot_ids)
if today_state == "failed":
return 0
day = today if today_state == "complete" else today - timedelta(days=1)
streak = 0 streak = 0
day = datetime.now(tz).date()
while True: while True:
day_str = day.isoformat() day_str = day.isoformat()
ok = await day_compliance(db, day_str, slot_ids) ok = await day_compliance(db, day_str, slot_ids)
+1 -1
View File
@@ -14,7 +14,7 @@ from server.db import get_db
async def main() -> None: async def main() -> None:
parser = argparse.ArgumentParser(description="Regenerate cached AI texts") parser = argparse.ArgumentParser(description="Regenerate cached AI texts")
parser.add_argument("--motivation", action="store_true", help="Regenerate today's motivation") parser.add_argument("--motivation", action="store_true", help="Regenerate today's motivation")
parser.add_argument("--oracle", action="store_true", help="Regenerate this week's oracle") parser.add_argument("--oracle", action="store_true", help="Regenerate today's oracle")
parser.add_argument("--force", action="store_true", help="Regenerate even if cache exists") parser.add_argument("--force", action="store_true", help="Regenerate even if cache exists")
args = parser.parse_args() args = parser.parse_args()