diff --git a/pigway_frame_flow/api/app.py b/pigway_frame_flow/api/app.py index 6270c77..b964b28 100644 --- a/pigway_frame_flow/api/app.py +++ b/pigway_frame_flow/api/app.py @@ -124,7 +124,7 @@ def create_app(settings=None, start_worker=True): app = FastAPI( title="Pigway Frame Flow API", docs_url=None, - version="0.1.1", + version="0.1.2", lifespan=lifespan, responses=error_responses, ) @@ -1000,7 +1000,7 @@ def create_app(settings=None, start_worker=True): ) def settings_view(): return SettingsView( - version="0.1.1", + version="0.1.2", mock=settings.mock, write_enabled=settings.write_enabled, api_key_configured=bool(settings.api_key) diff --git a/pigway_frame_flow/extensions/webhooks.py b/pigway_frame_flow/extensions/webhooks.py index 4c6936f..ff9c44f 100644 --- a/pigway_frame_flow/extensions/webhooks.py +++ b/pigway_frame_flow/extensions/webhooks.py @@ -39,12 +39,13 @@ class Webhooks: db = self.service.db for sub in db.all("SELECT * FROM webhook_subscriptions WHERE enabled=1"): types = json.loads(sub["event_types"]) + if not types: + continue + placeholders = ",".join("?" for _ in types) for event in db.all( - "SELECT * FROM events e WHERE created_at>=? AND NOT EXISTS (SELECT 1 FROM webhook_deliveries d WHERE d.event_id=e.id AND d.subscription_id=?) ORDER BY created_at LIMIT 500", - (sub["created_at"], sub["id"]), + f"SELECT * FROM events e WHERE created_at>=? AND event_type IN ({placeholders}) AND NOT EXISTS (SELECT 1 FROM webhook_deliveries d WHERE d.event_id=e.id AND d.subscription_id=?) ORDER BY created_at LIMIT 500", + (sub["created_at"], *types, sub["id"]), ): - if event["event_type"] not in types: - continue db.execute( "INSERT OR IGNORE INTO webhook_deliveries VALUES(?,?,'QUEUED',0,?,NULL)", (sub["id"], event["id"], now_iso()), diff --git a/pigway_frame_flow/models.py b/pigway_frame_flow/models.py index fd814d3..04a86bf 100644 --- a/pigway_frame_flow/models.py +++ b/pigway_frame_flow/models.py @@ -336,7 +336,7 @@ class SettingsView(BaseModel): class Health(BaseModel): status: str - version: str = "0.1.1" + version: str = "0.1.2" class Readiness(BaseModel): @@ -366,7 +366,7 @@ class RestoreRequest(StrictModel): class Version(BaseModel): product: str = "Pigway Frame Flow" - version: str = "0.1.1" + version: str = "0.1.2" api_version: str = "v1" commit: str = "development" build_time: str = "unknown" diff --git a/pigway_frame_flow/scheduler/engine.py b/pigway_frame_flow/scheduler/engine.py index ab07c5b..39cec8a 100644 --- a/pigway_frame_flow/scheduler/engine.py +++ b/pigway_frame_flow/scheduler/engine.py @@ -147,61 +147,72 @@ class Scheduler: "SELECT * FROM jobs WHERE enabled=1 AND next_run<=?", (current.isoformat(),), ): - job = JobInput.model_validate_json(row["payload"]) - slot = row["next_run"] - if self.db.one( - "SELECT * FROM job_runs WHERE job_id=? AND scheduled_at=?", - (row["id"], slot), - ): - continue - # Skip overdue tasks (>5 minutes): avoid an unexpected artwork - # overwrite after downtime. One-time tasks are retired too. - late = (current - datetime.fromisoformat(slot)).total_seconds() > 300 - operation_id = None - if not late: - action = "restore" if job.source.type == "restore" else "display" - payload = ( - {} - if isinstance(job.source, RestoreContent) - else DisplayRequest( - device_id=job.device_id, - content=job.source, - options=Options( - duration=job.display_duration, - restore_previous=job.restore_previous, - priority=job.priority, - cleanup_unstarred=job.cleanup_unstarred, + try: + job = JobInput.model_validate_json(row["payload"]) + slot = row["next_run"] + if self.db.one( + "SELECT * FROM job_runs WHERE job_id=? AND scheduled_at=?", + (row["id"], slot), + ): + continue + # Skip overdue tasks (>5 minutes): avoid an unexpected artwork + # overwrite after downtime. One-time tasks are retired too. + late = ( + current - datetime.fromisoformat(slot) + ).total_seconds() > 300 + operation_id = None + if not late: + action = ( + "restore" if job.source.type == "restore" else "display" + ) + payload = ( + {} + if isinstance(job.source, RestoreContent) + else DisplayRequest( + device_id=job.device_id, + content=job.source, + options=Options( + duration=job.display_duration, + restore_previous=job.restore_previous, + priority=job.priority, + cleanup_unstarred=job.cleanup_unstarred, + ), + ).model_dump(mode="json") + ) + operation_id = self.service.submit( + action, + payload, + job.device_id, + "scheduler", + "schedule-" + uuid4().hex, + "job-" + row["id"] + "-" + slot, + ) + following = next_run(job.schedule, current) + enabled = bool(following) + updated = job.model_copy(update={"enabled": enabled}).model_dump( + mode="json" + ) + with self.db.transaction() as tx: + tx.execute( + "INSERT OR IGNORE INTO job_runs VALUES(?,?,?)", + (row["id"], slot, operation_id), + ) + tx.execute( + "UPDATE jobs SET payload=?,enabled=?,next_run=?,last_run=?,last_result=? WHERE id=?", + ( + encode(updated), + int(enabled), + following, + now_iso(), + "SKIPPED_OVERDUE" if late else operation_id, + row["id"], ), - ).model_dump(mode="json") - ) - operation_id = self.service.submit( - action, - payload, - job.device_id, - "scheduler", - "schedule-" + uuid4().hex, - "job-" + row["id"] + "-" + slot, - ) - following = next_run(job.schedule, current) - enabled = bool(following) - updated = job.model_copy(update={"enabled": enabled}).model_dump( - mode="json" - ) - with self.db.transaction() as tx: - tx.execute( - "INSERT OR IGNORE INTO job_runs VALUES(?,?,?)", - (row["id"], slot, operation_id), - ) - tx.execute( - "UPDATE jobs SET payload=?,enabled=?,next_run=?,last_run=?,last_result=? WHERE id=?", - ( - encode(updated), - int(enabled), - following, - now_iso(), - "SKIPPED_OVERDUE" if late else operation_id, - row["id"], - ), + ) + except Exception: + # Keep this slot pending for retry or the existing overdue policy. + # A single device must not prevent other jobs or queue consumption. + logging.getLogger("frame-flow").error( + "Scheduled job failed; details suppressed" ) for row in self.db.all( "SELECT * FROM playlists WHERE next_run<=?", (current.isoformat(),) diff --git a/pigway_frame_flow/web/index.html b/pigway_frame_flow/web/index.html index 6b9a513..5320e66 100644 --- a/pigway_frame_flow/web/index.html +++ b/pigway_frame_flow/web/index.html @@ -1,3 +1,3 @@