Initial import: eCW Charge Validation bot and results Portal
Snapshot of the working project as of 2026-10-23 (local 2d4d4fc), one commit with no earlier history: the tracked files were de-identified first (synthetic claim ids and patient data only; see Context.md section 7). Credentials, run logs, screenshots, claim reports and client workbooks are gitignored and not included. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
ba15a574ab
205 files changed
+56772
No files matched your search
@@ -0,0 +1,126 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Run {{ run.run_id }}{% endblock %}
|
||||
{% block head %}{% if run.status == "Running" %}<meta http-equiv="refresh" content="5">{% endif %}{% endblock %}
|
||||
{% block style %}
|
||||
.st { display: inline-block; padding: 2px 9px; border-radius: 10px;
|
||||
font-weight: 600; font-size: 12px; background: var(--head); }
|
||||
.st.Done { background: var(--pass-bg); color: var(--pass); }
|
||||
.st.Run-Error, .st.Ended-without-a-record { background: var(--fail-bg);
|
||||
color: var(--fail); }
|
||||
.st.Stopped { background: var(--review-bg); color: var(--review); }
|
||||
.st.Running { background: var(--hilite); color: var(--accent); }
|
||||
dl.facts { display: grid; grid-template-columns: 150px 1fr; gap: 6px 12px;
|
||||
margin: 0; }
|
||||
dl.facts dt { color: var(--muted); }
|
||||
dl.facts dd { margin: 0; }
|
||||
.card.error { border-left: 5px solid var(--fail); }
|
||||
.card.warn { border-left: 5px solid var(--review-bar); }
|
||||
table.plain { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||||
table.plain th, table.plain td { text-align: left; padding: 6px 8px;
|
||||
border-bottom: 1px solid var(--line);
|
||||
vertical-align: top; }
|
||||
table.plain th { color: #3b4757; font-size: 12px; text-transform: uppercase;
|
||||
letter-spacing: .4px; background: var(--head); }
|
||||
td.t { white-space: nowrap; color: var(--muted); width: 90px; }
|
||||
.ok { color: var(--pass); font-weight: 600; }
|
||||
.no { color: var(--fail); font-weight: 600; }
|
||||
pre.tb { background: var(--head); padding: 10px; border-radius: 6px;
|
||||
white-space: pre-wrap; font-size: 12px; margin: 6px 0 0; }
|
||||
ul.claims { columns: 4; margin: 0; padding-left: 18px; }
|
||||
tr.k-run_error td, tr.k-claim_error td { color: var(--fail); }
|
||||
tr.k-warning td { color: var(--review); }
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<h1>Run {{ run.started | usdate }} <span class="st {{ run.status | replace(' ', '-') }}">{{ run.status }}</span></h1>
|
||||
<p class="sub">{% if run.status == "Running" %}Still running — this page refreshes every 5 seconds.{% else %}Ran {{ run.duration_s | dur }}.{% endif %}</p>
|
||||
|
||||
<div class="card">
|
||||
<dl class="facts">
|
||||
<dt>Started By</dt><dd>{{ run.started_by }}</dd>
|
||||
<dt>Source</dt><dd>{{ run.source | capitalize }}</dd>
|
||||
<dt>Mode</dt><dd>{{ run.mode }}{% if run.test_sheet %} · claims from {{ run.test_sheet }}{% endif %}</dd>
|
||||
<dt>Started</dt><dd>{{ run.started | usdate }}</dd>
|
||||
<dt>Ended</dt><dd>{{ run.ended | usdate }}</dd>
|
||||
<dt>Result</dt><dd>{{ run.status }}{% if run.exit_code is not none %} (exit {{ run.exit_code }}){% endif %}{% if run.error_step %} — {{ run.error_step }}: {{ run.error_message }}{% endif %}</dd>
|
||||
{% if run.status == "Stopped" %}<dt>Stopped By</dt><dd>{{ run.stopped_by or "—" }}</dd>{% endif %}
|
||||
<dt>Claims</dt><dd>{{ run.claims_done }} of {{ run.claims_total }} checked ·
|
||||
{{ run.pass }} Pass · {{ run.fail }} Fail · {{ run.review }} Review{% if run.not_found %} · {{ run.not_found }} not found{% endif %}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{% if run.run_errors %}
|
||||
<div class="card error">
|
||||
<h2>Run Error</h2>
|
||||
{% for e in run.run_errors %}
|
||||
<p><strong>{{ e.t[11:19] }}</strong> · {{ e.step }} · exit {{ e.exit_code }} — {{ e.message }}</p>
|
||||
{% if e.traceback %}<details><summary>Traceback</summary><pre class="tb">{{ e.traceback }}</pre></details>{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card">
|
||||
<h2>eCW Login Attempts{% if run.login_attempts %} · {{ run.login_total_s | dur }} in all{% endif %}</h2>
|
||||
{% if run.login_attempts %}
|
||||
<table class="plain">
|
||||
<thead><tr><th>#</th><th>Start</th><th>End</th><th>Took</th><th>2FA mail wait</th><th>Result</th><th>Steps</th></tr></thead>
|
||||
<tbody>
|
||||
{% for a in run.login_attempts %}
|
||||
<tr>
|
||||
<td>{{ a.attempt }}</td>
|
||||
<td>{{ a.start | usdate }}</td>
|
||||
<td>{{ a.end | usdate }}</td>
|
||||
<td>{{ a.duration_s | dur }}</td>
|
||||
<td>{{ a.twofa_wait_s | dur if a.twofa_wait_s is not none else "—" }}</td>
|
||||
<td class="{{ 'ok' if a.result == 'ok' else 'no' }}">{{ a.result_label }}{% if a.message %} — {{ a.message }}{% endif %}</td>
|
||||
<td>{% for label, time in a.step_rows %}{{ time }} {{ label }}{% if not loop.last %}<br>{% endif %}{% endfor %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}<p class="note">No eCW login in this run.</p>{% endif %}
|
||||
</div>
|
||||
|
||||
{% if run.claim_errors %}
|
||||
<div class="card error">
|
||||
<h2>Claim Errors ({{ run.claim_errors | length }})</h2>
|
||||
<p class="note">The bot could not check these claims; the Charge Summary shows them as Review.</p>
|
||||
<table class="plain">
|
||||
<thead><tr><th>Time</th><th>Claim</th><th>Step</th><th>Message</th></tr></thead>
|
||||
<tbody>{% for e in run.claim_errors %}<tr><td class="t">{{ e.t[11:19] }}</td><td>{{ e.claim }}</td><td>{{ e.step }}</td><td>{{ e.message }}</td></tr>{% endfor %}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if run.not_reached %}
|
||||
<div class="card error">
|
||||
<h2>Not Reached ({{ run.not_reached | length }})</h2>
|
||||
<p class="note">Queued, but the run ended before it got to them.</p>
|
||||
<ul class="claims">{% for c in run.not_reached %}<li>{{ c }}</li>{% endfor %}</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if run.warnings %}
|
||||
<div class="card warn">
|
||||
<h2>Warnings ({{ run.warnings | length }})</h2>
|
||||
<table class="plain">
|
||||
<thead><tr><th>Time</th><th>Kind</th><th>Claim</th><th>Message</th></tr></thead>
|
||||
<tbody>{% for e in run.warnings %}<tr><td class="t">{{ e.t[11:19] }}</td><td>{{ e.code }}</td><td>{{ e.claim or "" }}</td><td>{{ e.message }}</td></tr>{% endfor %}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if run.screenshots %}
|
||||
<div class="card">
|
||||
<h2>Failure screenshots</h2>
|
||||
<ul>{% for e in run.screenshots %}<li>{{ e.t[11:19] }} <a href="/runs/{{ run.run_id }}/shot/{{ loop.index0 }}" target="_blank" rel="noopener">{{ e.tag }}</a></li>{% endfor %}</ul>
|
||||
<p class="note">Each one's page HTML, when saved, is beside it in Screenshots\ (open it there, not here).</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card">
|
||||
<h2>Timeline</h2>
|
||||
<table class="plain">
|
||||
<tbody>{% for e in run.timeline %}<tr class="k-{{ e.kind }}"><td class="t">{{ e.time }}</td><td>{{ e.text }}</td></tr>{% endfor %}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in new issue
Block a user