mirror of
https://github.com/hansjone/netx.git
synced 2026-10-10 04:40:45 +08:00
Improve biz-compare UX with guided create, sheet toggles, and per-run delete.
Also sticky template field headers and biz-state pause/start endpoints. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
9b3e98ede5
commit
3d2489041a
12 changed files with 669 additions and 155 deletions
|
|
@ -1248,11 +1248,22 @@ def _job_out(j: BizCompareJob) -> dict[str, Any]:
|
|||
"after_batch_id": j.after_batch_id,
|
||||
"mode": j.mode,
|
||||
"status": j.status,
|
||||
"enabled_sheet_ids": _str_list(getattr(j, "enabled_sheet_ids", None)),
|
||||
"note": j.note,
|
||||
"updated_at": j.updated_at.isoformat() + "Z" if j.updated_at else None,
|
||||
}
|
||||
|
||||
|
||||
def _filter_enabled_sheets(
|
||||
sheets_cfg: list[dict[str, Any]], enabled_sheet_ids: list[str] | None
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Empty enabled list → all sheets; else keep matching sheet_id only."""
|
||||
allowed = set(_str_list(enabled_sheet_ids))
|
||||
if not allowed:
|
||||
return sheets_cfg
|
||||
return [s for s in sheets_cfg if sheet_key(s) in allowed]
|
||||
|
||||
|
||||
def list_jobs(db: Session) -> list[dict[str, Any]]:
|
||||
rows = db.query(BizCompareJob).order_by(BizCompareJob.updated_at.desc()).all()
|
||||
return [_job_out(j) for j in rows]
|
||||
|
|
@ -1267,6 +1278,7 @@ def create_job(db: Session, body: dict[str, Any]) -> dict[str, Any]:
|
|||
else:
|
||||
if not db.get(BizCompareTemplate, template_id):
|
||||
raise HTTPException(status_code=404, detail="template_not_found")
|
||||
enabled = _str_list(body.get("enabled_sheet_ids"))
|
||||
j = BizCompareJob(
|
||||
id=uuid4().hex,
|
||||
name=str(body.get("name") or "compare")[:256],
|
||||
|
|
@ -1278,6 +1290,7 @@ def create_job(db: Session, body: dict[str, Any]) -> dict[str, Any]:
|
|||
after_batch_id=str(body.get("after_batch_id") or ""),
|
||||
mode=str(body.get("mode") or "manual")[:16],
|
||||
status="ready",
|
||||
enabled_sheet_ids=enabled,
|
||||
note=str(body.get("note") or "")[:512],
|
||||
created_at=_utcnow(),
|
||||
updated_at=_utcnow(),
|
||||
|
|
@ -1307,6 +1320,8 @@ def update_job(db: Session, job_id: str, body: dict[str, Any]) -> dict[str, Any]
|
|||
):
|
||||
if key in body and body.get(key) is not None:
|
||||
setattr(j, key, str(body.get(key) or ""))
|
||||
if "enabled_sheet_ids" in body:
|
||||
j.enabled_sheet_ids = _str_list(body.get("enabled_sheet_ids"))
|
||||
j.updated_at = _utcnow()
|
||||
db.commit()
|
||||
return _job_out(j)
|
||||
|
|
@ -1328,6 +1343,20 @@ def delete_job(db: Session, job_id: str) -> None:
|
|||
db.commit()
|
||||
|
||||
|
||||
def delete_run(db: Session, run_id: str) -> dict[str, Any]:
|
||||
"""Delete one compare run and its diffs; leave the job intact."""
|
||||
r = db.get(BizCompareRun, run_id)
|
||||
if not r:
|
||||
raise HTTPException(status_code=404, detail="run_not_found")
|
||||
job_id = str(r.job_id or "")
|
||||
db.query(BizCompareDiff).filter(BizCompareDiff.run_id == run_id).delete(
|
||||
synchronize_session=False
|
||||
)
|
||||
db.delete(r)
|
||||
db.commit()
|
||||
return {"ok": True, "job_id": job_id, "run_id": run_id}
|
||||
|
||||
|
||||
def _resolve_after_batch(db: Session, job: BizCompareJob) -> str:
|
||||
if job.mode != "auto":
|
||||
return str(job.after_batch_id or "")
|
||||
|
|
@ -1423,6 +1452,9 @@ def run_compare(db: Session, job_id: str, *, force_after_batch_id: str = "") ->
|
|||
sheets_cfg = template_metrics(tpl)
|
||||
if not sheets_cfg:
|
||||
raise HTTPException(status_code=400, detail="template_has_no_metrics")
|
||||
sheets_cfg = _filter_enabled_sheets(sheets_cfg, getattr(j, "enabled_sheet_ids", None))
|
||||
if not sheets_cfg:
|
||||
raise HTTPException(status_code=400, detail="no_enabled_sheets")
|
||||
|
||||
pmap = _port_map_dict(db, j.mapping_id)
|
||||
norm_rules = template_iface_normalize(tpl)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ def apply_biz_state_schema(conn: Connection) -> None:
|
|||
"CREATE INDEX IF NOT EXISTS ix_biz_state_batch_task_id ON biz_state_batch (task_id)",
|
||||
"CREATE INDEX IF NOT EXISTS ix_biz_state_batch_command_batch_id ON biz_state_batch_command (batch_id)",
|
||||
"CREATE INDEX IF NOT EXISTS ix_biz_state_lldp_neighbor_batch_id ON biz_state_lldp_neighbor (batch_id)",
|
||||
"ALTER TABLE biz_compare_job ADD COLUMN IF NOT EXISTS enabled_sheet_ids JSON DEFAULT '[]'",
|
||||
"CREATE INDEX IF NOT EXISTS ix_biz_compare_job_status ON biz_compare_job (status)",
|
||||
"CREATE INDEX IF NOT EXISTS ix_biz_compare_run_job_id ON biz_compare_run (job_id)",
|
||||
"CREATE INDEX IF NOT EXISTS ix_biz_state_vrf_route_batch_id ON biz_state_vrf_route_summary (batch_id)",
|
||||
|
|
|
|||
|
|
@ -235,6 +235,11 @@ def _replace_items(db: Session, task_id: str, items_in: list[dict[str, Any]]) ->
|
|||
)
|
||||
|
||||
|
||||
def set_task_status(db: Session, task_id: str, status: str) -> dict[str, Any]:
|
||||
"""Set lifecycle status: draft | running | paused | stopped."""
|
||||
return update_task(db, task_id, {"status": status})
|
||||
|
||||
|
||||
def update_task(db: Session, task_id: str, body: dict[str, Any]) -> dict[str, Any]:
|
||||
task = db.get(BizStateTask, task_id)
|
||||
if not task:
|
||||
|
|
|
|||
|
|
@ -174,6 +174,18 @@ def api_patch_task(
|
|||
return svc.update_task(db, task_id, body.model_dump(exclude_unset=True))
|
||||
|
||||
|
||||
@router.post("/tasks/{task_id}/pause")
|
||||
def api_pause_task(task_id: str, db: Session = Depends(get_db)) -> dict[str, Any]:
|
||||
"""Pause periodic schedule; manual collect remains allowed."""
|
||||
return svc.set_task_status(db, task_id, "paused")
|
||||
|
||||
|
||||
@router.post("/tasks/{task_id}/start")
|
||||
def api_start_task(task_id: str, db: Session = Depends(get_db)) -> dict[str, Any]:
|
||||
"""Enable periodic schedule (requires bindings for non–cutover-HF tasks)."""
|
||||
return svc.set_task_status(db, task_id, "running")
|
||||
|
||||
|
||||
@router.delete("/tasks/{task_id}")
|
||||
def api_delete_task(task_id: str, db: Session = Depends(get_db)) -> dict[str, Any]:
|
||||
svc.delete_task(db, task_id)
|
||||
|
|
@ -374,6 +386,8 @@ class CompareJobIn(BaseModel):
|
|||
before_batch_id: str = ""
|
||||
after_batch_id: str = ""
|
||||
mode: str = "manual"
|
||||
# Empty = all template sheets; non-empty = only these sheet_id values
|
||||
enabled_sheet_ids: list[str] = Field(default_factory=list)
|
||||
note: str = ""
|
||||
|
||||
|
||||
|
|
@ -478,6 +492,11 @@ def api_get_run(run_id: str, db: Session = Depends(get_db)) -> dict[str, Any]:
|
|||
return cmp_svc.get_run(db, run_id)
|
||||
|
||||
|
||||
@router.delete("/compare/runs/{run_id}")
|
||||
def api_delete_run(run_id: str, db: Session = Depends(get_db)) -> dict[str, Any]:
|
||||
return cmp_svc.delete_run(db, run_id)
|
||||
|
||||
|
||||
@router.get("/compare/runs/{run_id}/diffs")
|
||||
def api_list_run_diffs(
|
||||
run_id: str,
|
||||
|
|
|
|||
|
|
@ -293,6 +293,8 @@ class BizCompareJob(Base):
|
|||
# manual: fixed after_batch; auto: after_batch_id empty → use latest after task batch
|
||||
mode: Mapped[str] = mapped_column(String(16), default="manual", index=True)
|
||||
status: Mapped[str] = mapped_column(String(32), default="draft", index=True) # draft|ready|auto
|
||||
# Empty = all template sheets; non-empty = only these sheet_id values
|
||||
enabled_sheet_ids: Mapped[list] = mapped_column(_JsonType, default=list)
|
||||
note: Mapped[str] = mapped_column(String(512), default="")
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=utcnow_naive)
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime, default=utcnow_naive)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue