Add two-stage run verification and 24-hour Activity filter
All checks were successful
Deploy / deploy (push) Successful in 57s

Status now reads "Sent" when the confirmation click lands, since that
only means MassPrice accepted the run request, not that it finished —
their own processing is async and takes anywhere from ~1 to ~22
minutes in observed practice. A new "Complete" column tracks that
separately: run_rule() captures a watermark (the target rule's current
top data-activitylogid on /Activity/List, not the displayed timestamp
string, to sidestep fragile date/timezone parsing) before submitting,
and scheduler.py schedules a one-off verify_run() job
VERIFY_DELAY_MINUTES later to check whether a new "Complete" entry
appeared past that watermark.

rebuild_jobs() now only clears recurring "rule-*" jobs, not the one-off
"verify-*" jobs, so editing a schedule can't cancel a verification
that's already in flight for an earlier run.

Also scopes the Activity page to the last 24 hours per request.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H6TgffxTvYhqxykAbVQg2v
This commit is contained in:
Barely Removable 2026-08-31 21:49:45 -07:00
parent 988db5d5c4
commit 8b58dadc5a
8 changed files with 249 additions and 20 deletions

14
app.py
View file

@ -1,5 +1,6 @@
import hmac
import os
from datetime import datetime, timedelta, timezone
from functools import wraps
from flask import Flask, jsonify, redirect, render_template, request, session, url_for
@ -8,7 +9,7 @@ import automation
import scheduler
import storage
APP_VERSION = "0.6.0"
APP_VERSION = "0.7.0"
APP_VERSION_DATE = "2026-09-01"
app = Flask(__name__)
@ -127,11 +128,20 @@ def api_session_import():
return jsonify({"ok": True})
def _parse_ts(value):
try:
return datetime.fromisoformat(value)
except (TypeError, ValueError):
return None
@app.route("/activity")
@login_required
def activity():
history = storage.load("run_history.json", [])
return render_template("activity.html", history=history)
cutoff = datetime.now(timezone.utc) - timedelta(hours=24)
recent = [e for e in history if (_parse_ts(e.get("timestamp")) or cutoff) >= cutoff]
return render_template("activity.html", history=recent)
@app.route("/api/rules/refresh", methods=["POST"])