v1.0.171: ▶ Сравнить группу via SSE + classify timer/progress

This commit is contained in:
2026-06-24 13:55:22 +04:00
parent e0cff8d85f
commit cbdfc7a8c1
3 changed files with 64 additions and 15 deletions
+60 -13
View File
@@ -197,7 +197,28 @@ function showClassifyBtn() {
async function runClassify() { async function runClassify() {
var btn = document.getElementById('classifyBtn'); var btn = document.getElementById('classifyBtn');
btn.disabled = true; btn.disabled = true;
btn.innerHTML = '<span class="spinner"></span> Классификация...'; btn.innerHTML = '<span class="spinner"></span> Классификация... 0с';
var start = Date.now();
var timer = setInterval(function() {
btn.innerHTML = '<span class="spinner"></span> Классификация... ' + Math.round((Date.now() - start) / 1000) + 'с';
}, 1000);
// Poll progress каждые 2с
var pollTimer = setInterval(async function() {
try {
var r = await fetch(VM_API + '/api/batch-progress?batch=' + batchId);
var d = await r.json();
if (d.ok && d.counts) {
var done = (d.counts.classified || 0) + (d.counts.failed || 0);
var total = d.total || 0;
btn.innerHTML = '<span class="spinner"></span> Классификация... ' + done + '/' + total + ' (' + Math.round((Date.now() - start) / 1000) + 'с)';
if (done >= total && total > 0) {
clearInterval(pollTimer);
}
}
} catch(e) {}
}, 2000);
try { try {
var resp = await fetch(VM_API + '/api/classify-batch', { var resp = await fetch(VM_API + '/api/classify-batch', {
@@ -206,14 +227,18 @@ async function runClassify() {
body: JSON.stringify({batch_id: batchId}) body: JSON.stringify({batch_id: batchId})
}); });
var data = await resp.json(); var data = await resp.json();
clearInterval(timer);
clearInterval(pollTimer);
if (data.ok) { if (data.ok) {
btn.innerHTML = '✓ ' + data.done + '/' + data.total + ' классифицировано'; btn.innerHTML = '✓ ' + data.done + '/' + data.total + ' классифицировано (' + Math.round((Date.now() - start) / 1000) + 'с)';
loadGroups(); loadGroups();
} else { } else {
btn.innerHTML = '✗ ' + (data.error || 'ошибка'); btn.innerHTML = '✗ ' + (data.error || 'ошибка');
btn.disabled = false; btn.disabled = false;
} }
} catch(e) { } catch(e) {
clearInterval(timer);
clearInterval(pollTimer);
btn.innerHTML = '✗ ' + e.message; btn.innerHTML = '✗ ' + e.message;
btn.disabled = false; btn.disabled = false;
} }
@@ -283,27 +308,49 @@ window.runCompareForGroup = async function(gi) {
var btn = event.target; var btn = event.target;
btn.disabled = true; btn.disabled = true;
btn.innerHTML = '<span class="spinner"></span> Применяю группу...'; btn.innerHTML = '<span class="spinner"></span> Применяю...';
// 1. Apply groups (creates contracts + supplements)
var ar = await fetch(VM_API + '/api/apply-groups', { var ar = await fetch(VM_API + '/api/apply-groups', {
method: 'POST', method: 'POST',
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
body: JSON.stringify({batch_id: batchId, groups: [group]}) body: JSON.stringify({batch_id: batchId, groups: [group]})
}); });
var ad = await ar.json(); var ad = await ar.json();
if (!ad.ok) { btn.innerHTML = '✗ ' + ad.error; btn.disabled = false; return; } if (!ad.ok || !ad.contract_ids || !ad.contract_ids.length) {
btn.innerHTML = '✗ ошибка'; btn.disabled = false; return;
}
// 2. Get the created contract_id from supplements var cid = ad.contract_ids[0];
var sr = await fetch(VM_API + '/api/supplements?contract_id=&batch=' + batchId);
// Actually need contract_id — let's get it from groups data
btn.innerHTML = '<span class="spinner"></span> Сравнение...'; btn.innerHTML = '<span class="spinner"></span> Сравнение...';
// Use EventSource for process-v2 — but we need contract_id var diffCard = document.getElementById('diffCard');
// For now: use the old compare flow with contractId var diffBody = document.getElementById('diffBody');
btn.innerHTML = '✓ Готово — обновите страницу и нажмите «Общее сравнение»'; var diffStatus = document.getElementById('diffStatus');
btn.disabled = false; diffCard.style.display = 'block';
diffBody.innerHTML = '';
diffStatus.textContent = '⏳ 0.0с';
var start = Date.now();
var timer = setInterval(function() {
diffStatus.textContent = '⏳ ' + ((Date.now() - start) / 1000).toFixed(1) + 'с';
}, 200);
var es = new EventSource(VM_API + '/process-v2?contract_id=' + cid);
es.onmessage = function(e) {
var d = JSON.parse(e.data);
if (d.type === 'done') {
clearInterval(timer); es.close();
diffStatus.textContent = '✓ ' + d.total_time_s + 'с';
btn.innerHTML = '✓ Готово'; btn.disabled = false;
} else if (d.type === 'applied') {
diffBody.innerHTML += '<div style="font-size:12px;margin-top:4px;">+' + (d.summary.added||0) + ' ~' + (d.summary.updated||0) + ' -' + (d.summary.deleted||0) + '</div>';
} else if (d.type === 'error') {
clearInterval(timer); es.close();
diffBody.innerHTML += '<div style="color:var(--destructive);">✗ ' + d.message + '</div>';
btn.innerHTML = '✗'; btn.disabled = false;
}
};
es.onerror = function() { clearInterval(timer); es.close(); btn.innerHTML = '✗ соединение'; btn.disabled = false; };
}; };
function uploadFile(file, onProgress) { function uploadFile(file, onProgress) {
+3 -1
View File
@@ -108,6 +108,7 @@ def apply_groups(batch_id, groups_data):
Возвращает {ok, created: количество созданных supplements}. Возвращает {ok, created: количество созданных supplements}.
""" """
created = 0 created = 0
contract_ids = []
for g in groups_data: for g in groups_data:
contract_number = g.get("contract_number", "") contract_number = g.get("contract_number", "")
if contract_number == "__unresolved__": if contract_number == "__unresolved__":
@@ -118,6 +119,7 @@ def apply_groups(batch_id, groups_data):
# Create contract # Create contract
c = db_contracts.insert(contract_number, counterparty) c = db_contracts.insert(contract_number, counterparty)
contract_id = c["id"] contract_id = c["id"]
contract_ids.append(contract_id)
# Create supplements in order # Create supplements in order
for i, d in enumerate(docs): for i, d in enumerate(docs):
@@ -126,4 +128,4 @@ def apply_groups(batch_id, groups_data):
db_supplements.insert(contract_id, doc_id, supp_type) db_supplements.insert(contract_id, doc_id, supp_type)
created += 1 created += 1
return {"ok": True, "created": created} return {"ok": True, "created": created, "contract_ids": contract_ids}
+1 -1
View File
@@ -75,7 +75,7 @@
<body> <body>
<div class="topbar"> <div class="topbar">
<img src="/nubes-logo.svg" alt="Nubes"> <img src="/nubes-logo.svg" alt="Nubes">
<span class="title">Сверка договоров — LLM AI-driven Event Sourcing <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.0.170 — Lucee</span></span> <span class="title">Сверка договоров — LLM AI-driven Event Sourcing <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.0.171 — Lucee</span></span>
</div> </div>
<div class="content"> <div class="content">