services: add mood, moodtracker, overleaf, personal-sensing; update ollama
Compose and supporting code for four services that had been running or prototyped without their config tracked here, per the repo convention that agap_git holds the compose + config while application source lives in each service's own Gitea repo. Only placeholder credentials are included: mood/.env.example and moodtracker/.env.example ship dummy values, and overleaf/variables.env carries app name and feature flags only. Real values stay in Vaultwarden. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
50
mood/src/sync.py
Normal file
50
mood/src/sync.py
Normal file
@@ -0,0 +1,50 @@
|
||||
"""Sync orchestration: read moodtracker's SQLite file -> idempotent upsert.
|
||||
|
||||
Single stream ('moodtracker_entries'), cursor = highest source_id ingested so
|
||||
far. Each run re-checks a small overlap of already-synced ids (config.OVERLAP_ROWS)
|
||||
as a cheap safety net, then upserts anything with id > cursor - overlap.
|
||||
"""
|
||||
import time
|
||||
|
||||
from . import config, store
|
||||
from .mood_source import connect_source, fetch_entries_since
|
||||
|
||||
STREAM_KEY = "moodtracker_entries"
|
||||
|
||||
|
||||
def run_sync(conn, source_db_path=None):
|
||||
"""One sync pass. Returns a counts dict. Never raises — errors are
|
||||
recorded in ingest_runs/sync_state and returned in totals['errors']."""
|
||||
source_db_path = source_db_path or config.SOURCE_DB_PATH
|
||||
run_id = store.start_run(conn)
|
||||
totals = {"entries": 0, "errors": []}
|
||||
try:
|
||||
last = store.get_last_synced_id(conn, STREAM_KEY)
|
||||
since = max(0, last - config.OVERLAP_ROWS)
|
||||
source_conn = connect_source(source_db_path)
|
||||
try:
|
||||
rows = fetch_entries_since(source_conn, since)
|
||||
finally:
|
||||
source_conn.close()
|
||||
n = store.upsert_entries(conn, rows)
|
||||
totals["entries"] = n
|
||||
max_id = max((r["id"] for r in rows), default=last)
|
||||
store.set_sync_state(conn, STREAM_KEY, max(max_id, last))
|
||||
store.finish_run(conn, run_id, "ok", entries=n)
|
||||
except Exception as e: # noqa: BLE001 - isolate failures, keep the loop alive
|
||||
totals["errors"].append(str(e))
|
||||
store.set_sync_state(
|
||||
conn, STREAM_KEY, store.get_last_synced_id(conn, STREAM_KEY),
|
||||
status="error", error=str(e),
|
||||
)
|
||||
store.finish_run(conn, run_id, "error", entries=0, error=str(e))
|
||||
return totals
|
||||
|
||||
|
||||
def sync_loop(conn, source_db_path=None, interval=None, once=False):
|
||||
interval = interval or config.SYNC_INTERVAL_SECONDS
|
||||
while True:
|
||||
yield run_sync(conn, source_db_path)
|
||||
if once:
|
||||
return
|
||||
time.sleep(interval)
|
||||
Reference in New Issue
Block a user