Files
AgapHost/mood/src/sync.py
alvis d37801806d 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>
2026-07-30 04:42:58 +00:00

51 lines
1.9 KiB
Python

"""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)