quick backup
This commit is contained in:
@@ -46,6 +46,7 @@ def html_page(title: str, body: str) -> HTMLResponse:
|
|||||||
<a href='/form-responses'>Webhook form responses</a>
|
<a href='/form-responses'>Webhook form responses</a>
|
||||||
<a href='/poll/form-responses'>Polled form responses</a>
|
<a href='/poll/form-responses'>Polled form responses</a>
|
||||||
<a href='/poll/quote-template'>Polled quote templates</a>
|
<a href='/poll/quote-template'>Polled quote templates</a>
|
||||||
|
<a href='/poll/apply-runs'>Apply runs</a>
|
||||||
<a href='/generated-materials'>Generated materials</a>
|
<a href='/generated-materials'>Generated materials</a>
|
||||||
</nav>
|
</nav>
|
||||||
"""
|
"""
|
||||||
@@ -206,6 +207,7 @@ def dashboard():
|
|||||||
<li><a href='/poll/form-responses'>Browse polled form responses</a></li>
|
<li><a href='/poll/form-responses'>Browse polled form responses</a></li>
|
||||||
<li><a href='/poll/quote-template'>Browse parsed polled Quote Template responses</a></li>
|
<li><a href='/poll/quote-template'>Browse parsed polled Quote Template responses</a></li>
|
||||||
<li><a href='/poll/runs'>Browse poll runs</a></li>
|
<li><a href='/poll/runs'>Browse poll runs</a></li>
|
||||||
|
<li><a href='/poll/apply-runs'>Browse dry-run/apply runs</a></li>
|
||||||
<li><a href='/generated-materials'>Browse generated job-material state</a></li>
|
<li><a href='/generated-materials'>Browse generated job-material state</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -540,6 +542,89 @@ def form_response_detail(row_id: int):
|
|||||||
return html_page(f"Form response {row_id}", body)
|
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"<div class='card'>Apply-run table unavailable: {escape(str(e))}</div>")
|
||||||
|
|
||||||
|
table_rows = []
|
||||||
|
for row in rows:
|
||||||
|
table_rows.append(
|
||||||
|
f"<tr><td><a href='/poll/apply-runs/{row['id']}'>{row['id']}</a></td>"
|
||||||
|
f"<td>{escape(row['mode'] or '')}</td><td>{escape(row['status'] or '')}</td>"
|
||||||
|
f"<td><a href='/poll/quote-template/{escape(row['form_response_uuid'])}'>{escape(row['form_response_uuid'])}</a></td>"
|
||||||
|
f"<td>{escape(row['job_uuid'] or '')}</td><td>{escape(row['started_at'] or '')}</td><td>{escape(row['finished_at'] or '')}</td>"
|
||||||
|
f"<td>{row['desired_count']}</td><td>{row['created_count']}</td><td>{escape((row['error'] or '')[:160])}</td></tr>"
|
||||||
|
)
|
||||||
|
|
||||||
|
body = f"""
|
||||||
|
<table>
|
||||||
|
<thead><tr><th>ID</th><th>Mode</th><th>Status</th><th>Form response UUID</th><th>Job UUID</th><th>Started</th><th>Finished</th><th>Desired</th><th>Created</th><th>Error</th></tr></thead>
|
||||||
|
<tbody>{''.join(table_rows) or "<tr><td colspan='10'>No apply runs found yet.</td></tr>"}</tbody>
|
||||||
|
</table>
|
||||||
|
<div class='pagination'>
|
||||||
|
{f"<a href='{link_with_params('/poll/apply-runs', page=page-1)}'>← Prev</a>" if page > 1 else ''}
|
||||||
|
<a href='{link_with_params('/poll/apply-runs', page=page+1)}'>Next →</a>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
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"<div class='card'>Apply-run table unavailable: {escape(str(e))}</div>")
|
||||||
|
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"<tr><td>{row['row_index']}</td><td>{escape(row['action'] or '')}</td><td>{escape(row['kind'] or '')}</td>"
|
||||||
|
f"<td>{escape(row['name'] or '')}</td><td>{escape(row['job_material_uuid'] or '')}</td>"
|
||||||
|
f"<td>{escape(row['source_question'] or '')}</td><td>{escape(row['error'] or '')}</td></tr>"
|
||||||
|
f"<tr><td></td><td colspan='6'><details><summary>API payload</summary><pre>{escape(pretty_json(payload))}</pre></details></td></tr>"
|
||||||
|
)
|
||||||
|
|
||||||
|
body = f"""
|
||||||
|
<div class='card summary-grid'>
|
||||||
|
<div><strong>Run ID</strong></div><div>{run['id']}</div>
|
||||||
|
<div><strong>Mode</strong></div><div>{escape(run['mode'] or '')}</div>
|
||||||
|
<div><strong>Status</strong></div><div>{escape(run['status'] or '')}</div>
|
||||||
|
<div><strong>Form response UUID</strong></div><div><a href='/poll/quote-template/{escape(run['form_response_uuid'])}'>{escape(run['form_response_uuid'])}</a></div>
|
||||||
|
<div><strong>Job UUID</strong></div><div>{escape(run['job_uuid'] or '')}</div>
|
||||||
|
<div><strong>Started</strong></div><div>{escape(run['started_at'] or '')}</div>
|
||||||
|
<div><strong>Finished</strong></div><div>{escape(run['finished_at'] or '')}</div>
|
||||||
|
<div><strong>Desired</strong></div><div>{run['desired_count']}</div>
|
||||||
|
<div><strong>Created</strong></div><div>{run['created_count']}</div>
|
||||||
|
<div><strong>Error</strong></div><div>{escape(run['error'] or '')}</div>
|
||||||
|
</div>
|
||||||
|
<div class='section'><h2>Rows</h2><table><thead><tr><th>#</th><th>Action</th><th>Kind</th><th>Name</th><th>Created UUID</th><th>Source question</th><th>Error</th></tr></thead><tbody>{''.join(table_rows) or "<tr><td colspan='7'>No rows recorded.</td></tr>"}</tbody></table></div>
|
||||||
|
"""
|
||||||
|
return html_page(f"Apply run {run_id}", body)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/poll/runs", response_class=HTMLResponse)
|
@app.get("/poll/runs", response_class=HTMLResponse)
|
||||||
def list_poll_runs(page: int = Query(1, ge=1)):
|
def list_poll_runs(page: int = Query(1, ge=1)):
|
||||||
offset = (page - 1) * PAGE_SIZE
|
offset = (page - 1) * PAGE_SIZE
|
||||||
@@ -749,6 +834,22 @@ def polled_quote_template_detail(form_response_uuid: str):
|
|||||||
f"<td>{escape(item.get('source_question', ''))}</td></tr>"
|
f"<td>{escape(item.get('source_question', ''))}</td></tr>"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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"<tr><td><a href='/poll/apply-runs/{run['id']}'>{run['id']}</a></td><td>{escape(run['mode'] or '')}</td><td>{escape(run['status'] or '')}</td><td>{escape(run['started_at'] or '')}</td><td>{escape(run['finished_at'] or '')}</td><td>{run['desired_count']}</td><td>{run['created_count']}</td><td>{escape((run['error'] or '')[:120])}</td></tr>"
|
||||||
|
)
|
||||||
|
|
||||||
body = f"""
|
body = f"""
|
||||||
<div class='card summary-grid'>
|
<div class='card summary-grid'>
|
||||||
<div><strong>Form response UUID</strong></div><div>{escape(row['form_response_uuid'])}</div>
|
<div><strong>Form response UUID</strong></div><div>{escape(row['form_response_uuid'])}</div>
|
||||||
@@ -763,6 +864,19 @@ def polled_quote_template_detail(form_response_uuid: str):
|
|||||||
<div><strong>Error</strong></div><div>{escape(row['process_error'] or '')}</div>
|
<div><strong>Error</strong></div><div>{escape(row['process_error'] or '')}</div>
|
||||||
<div><strong>Raw polled response</strong></div><div><a href='/poll/form-responses/{escape(row['form_response_uuid'])}'>Open raw polled form response</a></div>
|
<div><strong>Raw polled response</strong></div><div><a href='/poll/form-responses/{escape(row['form_response_uuid'])}'>Open raw polled form response</a></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class='section'>
|
||||||
|
<h2>Selective apply commands</h2>
|
||||||
|
<div class='card'>
|
||||||
|
<p><strong>Dry-run first:</strong></p>
|
||||||
|
<pre>{escape(dry_run_cmd)}</pre>
|
||||||
|
<p><strong>Apply to ServiceM8 only after checking the dry-run:</strong></p>
|
||||||
|
<pre>{escape(apply_cmd)}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class='section'>
|
||||||
|
<h2>Recent dry-run/apply runs for this response</h2>
|
||||||
|
<table><thead><tr><th>ID</th><th>Mode</th><th>Status</th><th>Started</th><th>Finished</th><th>Desired</th><th>Created</th><th>Error</th></tr></thead><tbody>{''.join(recent_run_rows) or "<tr><td colspan='8'>No dry-run/apply runs yet.</td></tr>"}</tbody></table>
|
||||||
|
</div>
|
||||||
<div class='section'><h2>Desired jobMaterial rows</h2><table><thead><tr><th>Sort</th><th>Kind</th><th>Name</th><th>Material UUID</th><th>Source question</th></tr></thead><tbody>{''.join(material_rows) or "<tr><td colspan='5'>No desired jobMaterial rows.</td></tr>"}</tbody></table></div>
|
<div class='section'><h2>Desired jobMaterial rows</h2><table><thead><tr><th>Sort</th><th>Kind</th><th>Name</th><th>Material UUID</th><th>Source question</th></tr></thead><tbody>{''.join(material_rows) or "<tr><td colspan='5'>No desired jobMaterial rows.</td></tr>"}</tbody></table></div>
|
||||||
<div class='section'><h2>Parsed JSON</h2><pre>{escape(pretty_json(parsed))}</pre></div>
|
<div class='section'><h2>Parsed JSON</h2><pre>{escape(pretty_json(parsed))}</pre></div>
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user