Unify processed case listing with blocked states and package downloads
This commit is contained in:
1 parent
b8c0d7ec40
commit
b42bafb711
8 files changed
+81
-14
No files matched your search
@@ -0,0 +1,31 @@
|
||||
# Bearbeitete Fälle – 2026-10-23
|
||||
|
||||
Die bisher auf terminale Zustände begrenzte Ergebnisliste wurde zur gemeinsamen
|
||||
Liste bearbeiteter Fälle erweitert: OUTPUT_READY, HANDED_OFF, BLOCKED und
|
||||
FAILED_TO_ANALYZE. Filter für Kategorie und Projekt, Suche, Sortierung nach
|
||||
Aktualisierung/Kategorie/Projekt, Routingziel, Sperrgrund und linksseitiger
|
||||
Iconknopf für den bestehenden projektgeschützten Paketdownload. Blockierte Fälle
|
||||
werden nicht als abgeschlossen gewertet. Analysezustände/Budgets unverändert.
|
||||
|
||||
Lokale Validierung:
|
||||
|
||||
```text
|
||||
python -m pytest tests/test_dashboard.py tests/test_web_auth.py tests/test_human_web.py -q
|
||||
26 passed, 1 dependency deprecation warning
|
||||
node tests/dashboard-smoke.mjs
|
||||
PASS, console_errors: [], overflow: false
|
||||
node --check src/viano_bug_analysis/web/case-detail.js
|
||||
PASS
|
||||
```
|
||||
|
||||
Echter Browser prüft die gemeinsame Liste mit Ergebnis und blockiertem Fall,
|
||||
linken Downloadknöpfen, Testerantwort-Hinweis, Kategorieauswahl und bestehender
|
||||
Detailnavigation. Mobile Ansicht visuell kontrolliert. Bestehende Tests sichern
|
||||
projektgebundene Detail-/ZIP-Endpunkte und Anmeldung ab. Routingdaten werden aus
|
||||
dem Manifest übernommen; die Liste scannt nicht separat das Exportverzeichnis.
|
||||
|
||||
Vor Testbeginn Trials 16.939.129.428 Bytes, Fehlerkorpus 2.471.041 Bytes, nur kleine
|
||||
temporäre UI-/Unit-Testdaten. Entwicklungsbudget weiterhin unter 20 GB.
|
||||
Keine LLM-/Cloud-Aufrufe oder Kosten, keine Änderung produktiver Fälle/Dienste.
|
||||
Serverinstallation, HTTPS, echter automatischer Feldtest und Betriebssicherung
|
||||
bleiben Aufgaben auf dem Linux-Zielsystem.
|
||||
+17
-2
@@ -83,8 +83,23 @@ Diagnose sowie Coding- und Human-Dev-Auftrag. Befunde bleiben ausdrücklich
|
||||
KI-Bewertungen mit Belegreferenzen. Das Belegverzeichnis zeigt Herkunft und
|
||||
Abdeckungsinformationen; die vollständigen Recherchepayloads stehen im ZIP.
|
||||
Ein expliziter Auftrag „Kein Auftrag“ wird von einem noch nicht erstellten Dokument
|
||||
unterschieden. Die separate Ergebnisliste enthält abgeschlossene und zur
|
||||
menschlichen Prüfung ausgeleitete Fälle, mit Projektfilter und Suche.
|
||||
unterschieden. Die gemeinsame Liste **Bearbeitete Fälle** enthält Ergebnisse
|
||||
(`OUTPUT_READY`, Ziel Outbox), bereits übergebene Fälle (`HANDED_OFF`), blockierte
|
||||
Fälle (`BLOCKED`) und gescheiterte Analysen (`FAILED_TO_ANALYZE`, Ziel Review).
|
||||
Blockierte Fälle bleiben ausdrücklich unvollständig und können weiterhin auch
|
||||
in der Übersicht wartender Fälle erscheinen. Ein Sperrgrund wird angezeigt;
|
||||
`AWAITING_TEST_FEEDBACK` erscheint als „Wartet auf Testerantwort“.
|
||||
|
||||
Projekt- und Kategoriefilter sowie Suche sind kombinierbar. Die Sortierung erlaubt
|
||||
neueste/älteste Aktualisierung, Kategorie oder Projekt. Links in jeder Zeile lädt
|
||||
ein beschrifteter Iconknopf die vollständige aktuelle Fallakte herunter, auch für
|
||||
blockierte oder gescheiterte Analysen. Der Download ändert keinen Status und löst
|
||||
keine Analyse aus. Die Ablagespalte zeigt das im Manifest gespeicherte Routingziel;
|
||||
es ist keine unabhängige Bestandsprüfung des Outbox-Dateiverzeichnisses. Der Download
|
||||
liest die verwaltete aktuelle Akte und funktioniert auch ohne separate Exportkopie.
|
||||
Angezeigt wird der aktuelle Stand je Fall/Paket. Nach Wiedereröffnung erscheinen
|
||||
aktive Fälle wieder in der laufenden Übersicht; ältere Revisionen bleiben in der
|
||||
Detailansicht erhalten. Berechtigungen und Projektgrenzen gelten auch für Downloads.
|
||||
|
||||
Die Revisionsauswahl liest akzeptierte Artefakte aus der Checkpointhistorie der
|
||||
aktuellen vollständigen Akte. Ergebnisse neuerer Revisionen erscheinen nicht in
|
||||
|
||||
@@ -103,7 +103,9 @@ def case_view(reader):
|
||||
'key':key(manifest['case_id']) + '.' + key(manifest['package_id']),
|
||||
'project_id': manifest['project'].get('id'), 'title': title,
|
||||
'progress': round(sum(complete) / len(complete) * 100), 'calls': calls,
|
||||
'updated_at': manifest['updated_at']}
|
||||
'updated_at': manifest['updated_at'],
|
||||
'destination':manifest.get('routing',{}).get('destination'),
|
||||
'reason':manifest.get('routing',{}).get('reason')}
|
||||
|
||||
|
||||
def aggregate(cases):
|
||||
|
||||
@@ -29,14 +29,23 @@ function panel(title,content,expanded=true){
|
||||
}
|
||||
function renderArchive(){
|
||||
if(!overview)return;
|
||||
const project=$('result-project').value,search=$('result-search').value.trim().toLocaleLowerCase('de-DE');
|
||||
const list=overview.cases.filter(c=>finished.has(c.state)&&(!project||project===c.project_id)&&(!search||`${c.case_id} ${c.title}`.toLocaleLowerCase('de-DE').includes(search))).sort((a,b)=>b.updated_at.localeCompare(a.updated_at));
|
||||
const project=$('result-project').value,search=$('result-search').value.trim().toLocaleLowerCase('de-DE'),status=$('result-status').value,sort=$('result-sort').value;
|
||||
const labels={OUTPUT_READY:'Ergebnis bereit',HANDED_OFF:'Übergeben',BLOCKED:'Blockiert / wartet',FAILED_TO_ANALYZE:'Analyse gescheitert / Review'};
|
||||
const list=overview.cases.filter(c=>(finished.has(c.state)||c.state==='BLOCKED')&&(!status||status===c.state)&&(!project||project===c.project_id)&&(!search||`${c.case_id} ${c.title}`.toLocaleLowerCase('de-DE').includes(search))).sort((a,b)=>{
|
||||
const tie=b.updated_at.localeCompare(a.updated_at)||a.key.localeCompare(b.key);
|
||||
if(sort==='category')return labels[a.state].localeCompare(labels[b.state],'de')||tie;
|
||||
if(sort==='project')return a.project_id.localeCompare(b.project_id,'de')||tie;
|
||||
return sort==='oldest'?-tie:tie;
|
||||
});
|
||||
$('result-count').textContent=`${list.length} ${list.length===1?'Fall':'Fälle'}`;
|
||||
const body=$('result-rows');body.replaceChildren();
|
||||
list.forEach(c=>{const row=document.createElement('tr'),name=document.createElement('td'),link=text('a',c.case_id,'case-link');link.href=`#case/${c.key}`;
|
||||
name.append(link,text('small',c.title));row.append(name,text('td',c.project_id),text('td',({OUTPUT_READY:'Ergebnis bereit',HANDED_OFF:'Übergeben',FAILED_TO_ANALYZE:'Menschliches Review'})[c.state]),text('td',String(c.analysis_revision)),text('td',new Date(c.updated_at).toLocaleString('de-DE')));body.append(row);});
|
||||
if(!list.length){const row=document.createElement('tr'),cell=text('td','Keine abgeschlossenen Fälle für diese Auswahl.','empty');cell.colSpan=5;row.append(cell);body.append(row);}
|
||||
const action=document.createElement('td'),download=text('button','↓','package-icon');download.type='button';download.title=`Fallakte ${c.case_id} herunterladen`;download.setAttribute('aria-label',download.title);download.addEventListener('click',()=>downloadCase(c,download));action.append(download);
|
||||
const category=text('td',labels[c.state]);if(c.reason)category.append(text('small',c.reason==='AWAITING_TEST_FEEDBACK'?'Wartet auf Testerantwort':c.reason));
|
||||
name.append(link,text('small',c.title));row.append(action,name,text('td',c.project_id),category,text('td',({outbox:'Outbox',review:'Review',inbox:'Inbox',cases:'Fallablage'})[c.destination]||'Fallablage'),text('td',String(c.analysis_revision)),text('td',new Date(c.updated_at).toLocaleString('de-DE')));body.append(row);});
|
||||
if(!list.length){const row=document.createElement('tr'),cell=text('td','Keine bearbeiteten Fälle für diese Auswahl.','empty');cell.colSpan=7;row.append(cell);body.append(row);}
|
||||
}
|
||||
function clearDetails(){selectedCase=null;detailHash=null;detailLoading=false;$('result-search').value='';}
|
||||
function clearDetails(){selectedCase=null;detailHash=null;detailLoading=false;$('result-search').value='';$('result-status').value='';$('result-sort').value='newest';$('result-count').textContent='0 Fälle';}
|
||||
async function downloadCase(data,button){
|
||||
const generation=session,credential=token;button.disabled=true;error();
|
||||
try{const response=await fetch(`/v1/cases/${data.key}/package`,{credentials:'same-origin',cache:'no-store'});
|
||||
@@ -113,9 +122,10 @@ function route(){
|
||||
if($('test-page'))$('test-page').hidden=true;
|
||||
const detail=location.hash.startsWith('#case/'),results=location.hash==='#results',submissionPage=location.hash==='#submit';
|
||||
$('overview-page').hidden=detail||results||submissionPage;$('results').hidden=!results;$('case-detail').hidden=!detail;$('submit').hidden=!submissionPage;
|
||||
document.querySelector('header h1').textContent=detail?'Die Fallakte.':results?'Abgeschlossene Fälle.':submissionPage?'Auftrag übergeben.':'Analyse im Blick.';
|
||||
document.querySelector('header h1').textContent=detail?'Die Fallakte.':results?'Bearbeitete Fälle.':submissionPage?'Auftrag übergeben.':'Analyse im Blick.';
|
||||
document.querySelectorAll('nav a').forEach(a=>a.classList.toggle('selected',detail?a.hash==='#cases':a.hash===(results?'#results':submissionPage?'#submit':location.hash==='#cases'?'#cases':'#overview')));
|
||||
if(detail)loadDetail(location.hash);if(results)renderArchive();
|
||||
}
|
||||
$('result-project').addEventListener('change',renderArchive);$('result-search').addEventListener('input',renderArchive);
|
||||
$('result-status').addEventListener('change',renderArchive);$('result-sort').addEventListener('change',renderArchive);
|
||||
window.addEventListener('hashchange',route);route();
|
||||
@@ -4,3 +4,4 @@
|
||||
|
||||
.case-link{color:#4f61c7;font-weight:650;text-decoration:none}.case-link:hover{text-decoration:underline}.detail-panel>summary{font-size:18px;font-weight:650;cursor:pointer}.detail-body{margin-top:20px}.semantic-text{color:#45516a;white-space:pre-wrap;overflow-wrap:anywhere;margin:5px 0 12px}.semantic-fields{display:grid;grid-template-columns:minmax(130px,190px) minmax(0,1fr);gap:12px 22px;margin:0}.semantic-fields dt{font-size:12px;color:#7c8599;font-weight:600}.semantic-fields dd{margin:0;min-width:0}.semantic-fields .semantic-fields{grid-template-columns:130px minmax(0,1fr)}.semantic-list{display:grid;gap:10px}.semantic-item{border-left:2px solid #e6eaf6;padding-left:13px}.statement small{display:block;color:#9299a8}.reference-list{color:#7785ae;font-size:11px;overflow-wrap:anywhere}.hypothesis-card{border:1px solid #e6e9f1;border-radius:9px;padding:20px;margin-bottom:16px}.hypothesis-heading{display:flex;align-items:center;justify-content:space-between;gap:12px}.hypothesis-heading h3{font-size:16px;margin:0}.hypothesis-heading .badge{flex-shrink:0}.hypothesis-card .detail-panel{margin:16px 0 0;padding:14px;box-shadow:none}.hypothesis-card .detail-panel>summary{font-size:13px}.evidence-columns{display:grid;grid-template-columns:1fr 1fr;gap:26px;margin-top:18px}.evidence-columns h4{font-size:12px;color:#68758c}.evidence-columns>div{background:#f7f8fc;padding:16px;border-radius:7px;min-width:0}.detail-controls{display:flex;align-items:center;gap:16px}.detail-controls label{margin:0;min-width:170px}.detail-controls button{margin-top:20px}.history-note{background:#f1f3fd;color:#6472ae;padding:12px;border-radius:6px;font-size:12px}.assignment-status{font-weight:650;color:#34436a}.human-document{white-space:pre-wrap;overflow-wrap:anywhere;font:13px/1.7 system-ui}.next-actions p{color:#5f6d85}#case-detail .panel{scroll-margin-top:20px}@media(max-width:900px){.semantic-fields,.semantic-fields .semantic-fields{grid-template-columns:1fr;gap:4px}.semantic-fields dd{margin-bottom:12px}.detail-controls{flex-wrap:wrap}}@media(max-width:700px){.hypothesis-heading{align-items:flex-start;flex-direction:column}.evidence-columns{grid-template-columns:1fr}.detail-controls{width:100%}.detail-controls button{margin-top:0}.hypothesis-card{padding:14px}.rail{flex-wrap:wrap}.rail nav{flex-wrap:wrap}}
|
||||
#key-projects{border:1px solid var(--line);border-radius:8px;padding:12px;margin-bottom:14px}#key-projects label{display:flex;align-items:center;gap:10px;margin:8px 0}#key-projects input{width:auto;margin:0}#key-list button{margin:10px 8px 0 0}#key-list input{font-family:monospace}
|
||||
#result-rows .package-icon{width:34px;height:34px;padding:0;font-size:22px;line-height:1;border-radius:8px}#result-rows td small{display:block;max-width:320px;overflow-wrap:anywhere;color:var(--muted);font-size:11px}
|
||||
@@ -6,7 +6,7 @@
|
||||
<script src="/assets/case-detail.js" defer></script><script src="/assets/operations.js" defer></script><script src="/assets/test-requests.js" defer></script></head>
|
||||
<body>
|
||||
<aside class="rail"><a class="brand" href="/">V<span>BA</span><small>BugAnalysis</small></a>
|
||||
<nav aria-label="Navigation"><a href="#overview" class="selected">Übersicht</a><a href="#cases">Laufende Fälle</a><a href="#results">Ergebnisse</a><a href="#tests">Testanfragen</a><a href="#submit">Auftrag übergeben</a><a href="#keys">API-Schlüssel</a></nav>
|
||||
<nav aria-label="Navigation"><a href="#overview" class="selected">Übersicht</a><a href="#cases">Laufende Fälle</a><a href="#results">Bearbeitete Fälle</a><a href="#tests">Testanfragen</a><a href="#submit">Auftrag übergeben</a><a href="#keys">API-Schlüssel</a></nav>
|
||||
<div class="rail-bottom">ANALYSEBETRIEB<br><span id="connection">Nicht verbunden</span><button id="logout" type="button" hidden>Abmelden</button></div></aside>
|
||||
<main id="overview">
|
||||
<header><div><div class="eyebrow">BETRIEBSÜBERSICHT</div><h1>Analyse im Blick.</h1><p>Aktuelle Arbeit und messbarer Verbrauch.</p></div><div class="live"><span class="dot"></span><span id="updated">Warte auf Verbindung</span></div></header>
|
||||
@@ -30,9 +30,9 @@
|
||||
<section class="panel" id="cases"><div class="section-head"><div><div class="eyebrow">FALLAKTEN</div><h2>Aktuelle Untersuchungen</h2></div><label class="filter">Projekt <select id="project-filter"><option value="">Alle freigegebenen Projekte</option></select></label></div>
|
||||
<div class="table-wrap"><table><thead><tr><th>Fall</th><th>Projekt</th><th>Zustand</th><th>Revision</th><th>Fortschritt</th></tr></thead><tbody id="case-rows"></tbody></table></div></section>
|
||||
</div>
|
||||
<section class="panel" id="results" hidden><div class="section-head"><div><div class="eyebrow">ERGEBNISSE</div><h2>Abgeschlossene Fälle</h2><p>Diagnosen, Aufträge und Fälle für menschliches Review.</p></div></div>
|
||||
<div class="form-grid"><label>Projekt<select id="result-project"><option value="">Alle freigegebenen Projekte</option></select></label><label>Fall suchen<input id="result-search" type="search" placeholder="Bug-ID oder Fallname"></label></div>
|
||||
<div class="table-wrap"><table><thead><tr><th>Fall</th><th>Projekt</th><th>Ergebnis</th><th>Revision</th><th>Aktualisiert</th></tr></thead><tbody id="result-rows"></tbody></table></div></section>
|
||||
<section class="panel" id="results" hidden><div class="section-head"><div><div class="eyebrow">FALLABLAGE</div><h2>Bearbeitete Fälle</h2><p>Ergebnisse in der Outbox, übergebene, blockierte und gescheiterte Analysen. Blockiert bedeutet noch nicht abgeschlossen.</p></div><span id="result-count" class="badge">0 Fälle</span></div>
|
||||
<div class="form-grid"><label>Projekt<select id="result-project"><option value="">Alle freigegebenen Projekte</option></select></label><label>Kategorie<select id="result-status"><option value="">Alle Kategorien</option><option value="OUTPUT_READY">Ergebnis bereit / Outbox</option><option value="HANDED_OFF">Übergeben</option><option value="BLOCKED">Blockiert / wartet</option><option value="FAILED_TO_ANALYZE">Analyse gescheitert / Review</option></select></label><label>Sortierung<select id="result-sort"><option value="newest">Zuletzt aktualisiert</option><option value="oldest">Älteste zuerst</option><option value="category">Kategorie</option><option value="project">Projekt</option></select></label><label>Fall suchen<input id="result-search" type="search" placeholder="Bug-ID oder Fallname"></label></div>
|
||||
<div class="table-wrap"><table><thead><tr><th><span class="download-heading">Paket</span></th><th>Fall</th><th>Projekt</th><th>Kategorie / Grund</th><th>Ablage</th><th>Revision</th><th>Aktualisiert</th></tr></thead><tbody id="result-rows"></tbody></table></div></section>
|
||||
<section id="case-detail" aria-live="polite" hidden></section>
|
||||
<section class="panel" id="submit" hidden><div class="section-head"><div><div class="eyebrow">EINGANG</div><h2>Auftrag übergeben</h2><p>BugAnalysis erstellt und ergänzt die Fallakte.</p></div></div>
|
||||
<form id="submit-form"><div class="form-grid"><label>Auftragstyp<select id="kind"><option value="new_case">Neue Analyse</option><option value="case_return">Abgeschlossene Akte zurückgeben</option></select></label>
|
||||
|
||||
@@ -69,6 +69,12 @@ try {
|
||||
if(await evaluate("document.getElementById('token-count').textContent")!=='35.300')throw new Error('Project aggregation differs');
|
||||
await evaluate("location.hash='#results'");
|
||||
await until(()=>evaluate("!document.getElementById('results').hidden&&document.querySelector('#result-rows a')"));
|
||||
if(!await evaluate("document.querySelectorAll('#result-rows .package-icon').length===2&&document.getElementById('result-rows').textContent.includes('Wartet auf Testerantwort')"))throw new Error('Processed list missing blocked case or download controls');
|
||||
await evaluate("document.getElementById('result-status').value='BLOCKED';document.getElementById('result-status').dispatchEvent(new Event('change'))");
|
||||
if(!await evaluate("document.querySelectorAll('#result-rows .package-icon').length===1&&document.getElementById('result-rows').textContent.includes('BUG-000020')"))throw new Error('Blocked category filter failed');
|
||||
await evaluate("document.getElementById('result-status').value='OUTPUT_READY';document.getElementById('result-status').dispatchEvent(new Event('change'));document.getElementById('result-sort').value='category';document.getElementById('result-sort').dispatchEvent(new Event('change'))");
|
||||
if(!await evaluate("document.querySelectorAll('#result-rows .package-icon').length===1&&document.getElementById('result-rows').textContent.includes('BUG-000017')"))throw new Error('Outbox category filter failed');
|
||||
await writeFile(path.join(output,'processed-cases.png'),Buffer.from((await send('Page.captureScreenshot',{format:'png'})).data,'base64'));
|
||||
await evaluate("document.querySelector('#result-rows a').click()");
|
||||
await until(()=>evaluate("document.getElementById('case-detail').textContent.includes('SQL-Vorlage mit Platzhaltern')"));
|
||||
if(!await evaluate("document.getElementById('case-detail').textContent.includes('Passende Spawndaten ermitteln')"))throw new Error('Human assignment absent');
|
||||
|
||||
@@ -57,8 +57,10 @@ def test_correction_slice_disjoint_derived_excluded_and_thinking_not_doubled(mon
|
||||
chain.append(chain[0])
|
||||
monkeypatch.setattr(module,'checkpoint_chain',lambda reader:chain)
|
||||
reader=Reader(docs,{'case_id':'BUG-1','package_id':'one','analysis_revision':1,'package_generation':10,
|
||||
'state':'INVESTIGATING_HYPOTHESES','artifacts':{'case':'case'},'project':{'id':'p'},'updated_at':'now'})
|
||||
'state':'INVESTIGATING_HYPOTHESES','artifacts':{'case':'case'},'project':{'id':'p'},'updated_at':'now',
|
||||
'routing':{'destination':'inbox','reason':'AWAITING_TEST_FEEDBACK'}})
|
||||
view=case_view(reader)
|
||||
assert view['destination']=='inbox' and view['reason']=='AWAITING_TEST_FEEDBACK'
|
||||
assert len(view['calls'])==2
|
||||
usage=aggregate([view]);buckets={b['id']:b for b in usage['buckets']}
|
||||
assert buckets['investigation']['input_tokens']==100
|
||||
|
||||
Reference in new issue
Block a user