From 7e3538e7457eb26542ab5912a5014835f9725c2f Mon Sep 17 00:00:00 2001 From: "Soren (Molty)" Date: Mon, 4 May 2026 13:14:44 +1000 Subject: [PATCH] quick backup --- servicem8_inspector.py | 114 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/servicem8_inspector.py b/servicem8_inspector.py index d82f326..f2f29a0 100644 --- a/servicem8_inspector.py +++ b/servicem8_inspector.py @@ -46,6 +46,7 @@ def html_page(title: str, body: str) -> HTMLResponse: Webhook form responses Polled form responses Polled quote templates + Apply runs Generated materials """ @@ -206,6 +207,7 @@ def dashboard():
  • Browse polled form responses
  • Browse parsed polled Quote Template responses
  • Browse poll runs
  • +
  • Browse dry-run/apply runs
  • Browse generated job-material state
  • @@ -540,6 +542,89 @@ def form_response_detail(row_id: int): return html_page(f"Form response {row_id}", body) +@app.get("/poll/apply-runs", response_class=HTMLResponse) +def list_apply_runs(page: int = Query(1, ge=1)): + offset = (page - 1) * PAGE_SIZE + try: + with closing(get_poll_conn()) as conn: + rows = conn.execute( + """ + select id, form_response_uuid, job_uuid, mode, started_at, finished_at, + desired_count, created_count, status, error + from quote_template_apply_runs + order by id desc + limit ? offset ? + """, + (PAGE_SIZE, offset), + ).fetchall() + except sqlite3.Error as e: + return html_page("Apply runs", f"
    Apply-run table unavailable: {escape(str(e))}
    ") + + table_rows = [] + for row in rows: + table_rows.append( + f"{row['id']}" + f"{escape(row['mode'] or '')}{escape(row['status'] or '')}" + f"{escape(row['form_response_uuid'])}" + f"{escape(row['job_uuid'] or '')}{escape(row['started_at'] or '')}{escape(row['finished_at'] or '')}" + f"{row['desired_count']}{row['created_count']}{escape((row['error'] or '')[:160])}" + ) + + body = f""" + + + {''.join(table_rows) or ""} +
    IDModeStatusForm response UUIDJob UUIDStartedFinishedDesiredCreatedError
    No apply runs found yet.
    + + """ + return html_page("Apply runs", body) + + +@app.get("/poll/apply-runs/{run_id}", response_class=HTMLResponse) +def apply_run_detail(run_id: int): + try: + with closing(get_poll_conn()) as conn: + run = conn.execute("select * from quote_template_apply_runs where id = ?", (run_id,)).fetchone() + rows = conn.execute( + "select * from quote_template_apply_run_rows where run_id = ? order by row_index asc", + (run_id,), + ).fetchall() + except sqlite3.Error as e: + return html_page("Apply run", f"
    Apply-run table unavailable: {escape(str(e))}
    ") + if not run: + raise HTTPException(status_code=404, detail="Apply run not found") + + table_rows = [] + for row in rows: + payload = parse_json_field(row['api_payload_json'], {}) or {} + table_rows.append( + f"{row['row_index']}{escape(row['action'] or '')}{escape(row['kind'] or '')}" + f"{escape(row['name'] or '')}{escape(row['job_material_uuid'] or '')}" + f"{escape(row['source_question'] or '')}{escape(row['error'] or '')}" + f"
    API payload
    {escape(pretty_json(payload))}
    " + ) + + body = f""" +
    +
    Run ID
    {run['id']}
    +
    Mode
    {escape(run['mode'] or '')}
    +
    Status
    {escape(run['status'] or '')}
    +
    Form response UUID
    {escape(run['form_response_uuid'])}
    +
    Job UUID
    {escape(run['job_uuid'] or '')}
    +
    Started
    {escape(run['started_at'] or '')}
    +
    Finished
    {escape(run['finished_at'] or '')}
    +
    Desired
    {run['desired_count']}
    +
    Created
    {run['created_count']}
    +
    Error
    {escape(run['error'] or '')}
    +
    +

    Rows

    {''.join(table_rows) or ""}
    #ActionKindNameCreated UUIDSource questionError
    No rows recorded.
    + """ + return html_page(f"Apply run {run_id}", body) + + @app.get("/poll/runs", response_class=HTMLResponse) def list_poll_runs(page: int = Query(1, ge=1)): offset = (page - 1) * PAGE_SIZE @@ -749,6 +834,22 @@ def polled_quote_template_detail(form_response_uuid: str): f"{escape(item.get('source_question', ''))}" ) + dry_run_cmd = f"/opt/webhooks/apply_polled_quote_template_jobmaterials.py --uuid {row['form_response_uuid']} --pretty" + apply_cmd = f"/opt/webhooks/apply_polled_quote_template_jobmaterials.py --uuid {row['form_response_uuid']} --apply --pretty" + try: + with closing(get_poll_conn()) as conn: + recent_runs = conn.execute( + "select id, mode, status, started_at, finished_at, desired_count, created_count, error from quote_template_apply_runs where form_response_uuid = ? order by id desc limit 8", + (row['form_response_uuid'],), + ).fetchall() + except sqlite3.Error: + recent_runs = [] + recent_run_rows = [] + for run in recent_runs: + recent_run_rows.append( + f"{run['id']}{escape(run['mode'] or '')}{escape(run['status'] or '')}{escape(run['started_at'] or '')}{escape(run['finished_at'] or '')}{run['desired_count']}{run['created_count']}{escape((run['error'] or '')[:120])}" + ) + body = f"""
    Form response UUID
    {escape(row['form_response_uuid'])}
    @@ -763,6 +864,19 @@ def polled_quote_template_detail(form_response_uuid: str):
    Error
    {escape(row['process_error'] or '')}
    Raw polled response
    Open raw polled form response
    +
    +

    Selective apply commands

    +
    +

    Dry-run first:

    +
    {escape(dry_run_cmd)}
    +

    Apply to ServiceM8 only after checking the dry-run:

    +
    {escape(apply_cmd)}
    +
    +
    +
    +

    Recent dry-run/apply runs for this response

    + {''.join(recent_run_rows) or ""}
    IDModeStatusStartedFinishedDesiredCreatedError
    No dry-run/apply runs yet.
    +

    Desired jobMaterial rows

    {''.join(material_rows) or ""}
    SortKindNameMaterial UUIDSource question
    No desired jobMaterial rows.

    Parsed JSON

    {escape(pretty_json(parsed))}
    """