Show durable reservation acquisition progress in download tasks
This commit is contained in:
1 parent
698d91367c
commit
db42a5e087
16 files changed
+757
-23
No files matched your search
@@ -0,0 +1,47 @@
|
||||
"""Private, best-effort acquisition observations, separate from source evidence."""
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from .capture_job import atomic_json, fingerprint
|
||||
|
||||
|
||||
VERSION = "arr-acquisition-progress/v1"
|
||||
PHASES = frozenset({"queued", "searching", "fetching", "verifying", "completed", "failed", "interrupted"})
|
||||
PUBLIC_FIELDS = frozenset({"phase", "completed", "total", "percent", "updated_at"})
|
||||
|
||||
|
||||
def snapshot(phase, completed=0, total=None, *, updated_at=None):
|
||||
percent = (100 if phase == "completed" else 0 if phase == "queued" else
|
||||
min(99, completed * 100 // total) if total else 99 if total == 0 else None)
|
||||
return validate({"phase": phase, "completed": completed, "total": total, "percent": percent,
|
||||
"updated_at": updated_at or datetime.now(timezone.utc).isoformat()})
|
||||
|
||||
|
||||
def validate(value):
|
||||
"""Return only the fixed privacy-safe public contract; reject malformed data."""
|
||||
if type(value) is not dict or set(value) != PUBLIC_FIELDS or value["phase"] not in PHASES:
|
||||
raise ValueError("invalid acquisition progress")
|
||||
completed, total, percent = value["completed"], value["total"], value["percent"]
|
||||
if type(completed) is not int or not 0 <= completed <= 10000000:
|
||||
raise ValueError("invalid acquisition count")
|
||||
if total is not None and (type(total) is not int or not completed <= total <= 10000000):
|
||||
raise ValueError("invalid acquisition total")
|
||||
if percent is not None and (type(percent) is not int or not 0 <= percent <= 100):
|
||||
raise ValueError("invalid acquisition percent")
|
||||
if value["phase"] == "completed":
|
||||
if total is None or completed != total or percent != 100:
|
||||
raise ValueError("incomplete acquisition")
|
||||
elif percent == 100:
|
||||
raise ValueError("premature acquisition completion")
|
||||
stamp = value["updated_at"]
|
||||
if type(stamp) is not str or len(stamp) > 40 or datetime.fromisoformat(stamp).tzinfo is None:
|
||||
raise ValueError("invalid acquisition timestamp")
|
||||
return dict(value)
|
||||
|
||||
|
||||
def publish(folder, identity, phase, completed=0, total=None):
|
||||
"""An observation failure must never change the source collection outcome."""
|
||||
try:
|
||||
atomic_json(folder / "progress.json", {"version": VERSION, "identity_sha256": fingerprint(identity),
|
||||
"progress": snapshot(phase, completed, total)}, replace=True)
|
||||
except Exception:
|
||||
pass
|
||||
Reference in new issue
Block a user