Phase 3: VISUAL feed and M-Files drawings in the worker
Migration 0004 (VISUAL and drawing caches, go-live ignored list, resource map, job tables, order-derived columns with provenance; work order key includes the operation sequence). The real worker on APScheduler with settings re-read each fire, pause, run now, advisory locks, job_runs and a heartbeat. The visual-feed job (five VisualKit reads, COPY and MERGE cache, suspect-batch guard, the apply in one transaction: drafts, change orders, retire-moved, work order gone, closed and cancelled observed, projects and units from order links, auto-release) and the fingerprint. The drawing-lookup job (queue, budget, download on version change, sha256 into assets, decision table, staging for rev approval, drawing-origin change orders). Clerical From VISUAL panel, flags, rev approval, hand uploads, change-order review; Admin Feed settings and resource map. Synthetic fixture generator and a fixture-driven worker in CI with a Playwright scenario. Docs updated. Verified: 3249 server tests and 13 Playwright scenarios on the docker-1 CI stack, 76 frontend tests, doctor at 0004; one feed and one drawing run against VISUAL DEV and the vault from the developer machine (counts in the changelog). Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
"""`packetstation` command line: app, worker, migrate, doctor, version, healthcheck."""
|
||||
"""`packetstation` command line: app, worker, job, fixtures, migrate, doctor, version, healthcheck."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -55,7 +55,52 @@ def cmd_worker(_: argparse.Namespace) -> int:
|
||||
_setup_logging(s.log_level)
|
||||
guard.startup_checks(s)
|
||||
beat = Path(os.environ.get("WORKER_BEAT_FILE") or s.worker_beat_file)
|
||||
return worker.run(beat, s.ps_build)
|
||||
return worker.run(beat, s.ps_build, settings=s)
|
||||
|
||||
|
||||
def cmd_job_run(a: argparse.Namespace) -> int:
|
||||
"""Run one job once in the foreground (trigger `cli`) and print its job_runs row as JSON."""
|
||||
from . import guard
|
||||
from .worker.jobs import load_jobs
|
||||
from .worker.runs import run_job
|
||||
from .worker.scheduler import Scheduler
|
||||
|
||||
s = _settings()
|
||||
_setup_logging(s.log_level)
|
||||
guard.startup_checks(s)
|
||||
jobs = load_jobs()
|
||||
if a.name not in jobs:
|
||||
print(f"packetstation job run: no job {a.name!r}; jobs: {', '.join(sorted(jobs))}", file=sys.stderr)
|
||||
return 2
|
||||
sched = Scheduler(s, jobs=jobs)
|
||||
try:
|
||||
row = run_job(sched, a.name, "cli")
|
||||
except Exception as e:
|
||||
from .worker.redaction import redact
|
||||
|
||||
print(f"packetstation job run: {redact(f'{type(e).__name__}: {e}')}", file=sys.stderr)
|
||||
return 1
|
||||
print(json.dumps(row, indent=2, default=str))
|
||||
return 1 if row.get("status") == "failed" else 0
|
||||
|
||||
|
||||
def cmd_fixtures_use(a: argparse.Namespace) -> int:
|
||||
"""Point the fixture sources at state N: writes DIR/state. Never on STACK=prod."""
|
||||
if os.environ.get("STACK", "").strip().lower() == "prod":
|
||||
print("packetstation fixtures: refused on STACK=prod", file=sys.stderr)
|
||||
return 2
|
||||
if a.state < 1:
|
||||
print("packetstation fixtures use: the state is a number from 1", file=sys.stderr)
|
||||
return 2
|
||||
d = Path(a.dir or os.environ.get("FIXTURE_DIR") or "/fixtures")
|
||||
if not (d / f"feed.{a.state}.json").is_file():
|
||||
print(f"packetstation fixtures use: {d / f'feed.{a.state}.json'} not found", file=sys.stderr)
|
||||
return 1
|
||||
tmp = d / "state.tmp"
|
||||
tmp.write_text(str(a.state) + "\n", encoding="utf-8")
|
||||
tmp.replace(d / "state")
|
||||
print(f"fixture state {a.state} in {d}")
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_migrate(a: argparse.Namespace) -> int:
|
||||
@@ -148,6 +193,17 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
sub = ap.add_subparsers(dest="cmd", required=True)
|
||||
sub.add_parser("app", help="Run the web app (uvicorn on 0.0.0.0:8000)").set_defaults(func=cmd_app)
|
||||
sub.add_parser("worker", help="Run the background worker").set_defaults(func=cmd_worker)
|
||||
j = sub.add_parser("job", help="Worker jobs")
|
||||
jsub = j.add_subparsers(dest="job_cmd", required=True)
|
||||
jr = jsub.add_parser("run", help="Run one job once now and print its run as JSON (exit 1 when it failed)")
|
||||
jr.add_argument("name", help="visual-feed, drawing-lookup or housekeeping")
|
||||
jr.set_defaults(func=cmd_job_run)
|
||||
f = sub.add_parser("fixtures", help="Synthetic feed fixtures (CI and tests)")
|
||||
fsub = f.add_subparsers(dest="fixtures_cmd", required=True)
|
||||
fu = fsub.add_parser("use", help="Point the fixture sources at state N (writes DIR/state)")
|
||||
fu.add_argument("state", type=int)
|
||||
fu.add_argument("--dir", help="The fixture directory (default FIXTURE_DIR or /fixtures)")
|
||||
fu.set_defaults(func=cmd_fixtures_use)
|
||||
m = sub.add_parser("migrate", help="Apply pending SQL migrations as the migrate role")
|
||||
m.add_argument(
|
||||
"--bootstrap-roles",
|
||||
|
||||
Reference in New Issue
Block a user