- копирую модуль upload/ из drhider (слои 1-2) - blueprint: параметр sink (drhider-сессия по умолчанию, сверка — DB+парсинг) - upload_bp: contracts_upload_sink = _store_and_parse - routes: регистрирую create_upload_refs_blueprint(cfg, sink=...) - app.py: корень репо в sys.path (для import upload) - History: план переиспользования + ревью Соннета
75 lines
3.8 KiB
JavaScript
75 lines
3.8 KiB
JavaScript
// renderProcTable — 3-секционная таблица во время обработки
|
||
// (готово / текущий / ожидают / пропущены сверх лимита).
|
||
|
||
import { fmtSec } from './fmt_sec.js';
|
||
import { estForFile, DEFAULT_EST_MB_SEC } from './est_for_file.js';
|
||
import { procRow } from './proc_row.js';
|
||
|
||
export function renderProcTable(state, els, cfg) {
|
||
const procState = state.proc.procState;
|
||
const groups = { done: [], current: [], pending: [], over: [] };
|
||
for (let i = 0; i < state.files.length; i++) {
|
||
if (state.overNames.has(state.files[i].name)) { groups.over.push(i); continue; }
|
||
const st = procState[i] ? procState[i].st : 'pending';
|
||
if (st === 'done' || st === 'skipped') groups.done.push(i);
|
||
else if (st === 'current') groups.current.push(i);
|
||
else groups.pending.push(i);
|
||
}
|
||
// Оценка скорости из текущего файла (сек/символ) — для «ожидающих»
|
||
let rate = null;
|
||
for (const i of groups.current) {
|
||
const p = procState[i];
|
||
const cur = (performance.now() - p.t0) / 1000;
|
||
const total = cur + (p.eta != null ? p.eta : 0);
|
||
if (p.chars > 0 && total > 0) rate = total / p.chars;
|
||
}
|
||
const rows = [];
|
||
if (groups.done.length) {
|
||
rows.push('<tr class="grp-row"><td colspan="4">✓ Обработанные (' + groups.done.length + ')</td></tr>');
|
||
for (const i of groups.done) {
|
||
const p = procState[i];
|
||
const txt = p.st === 'skipped'
|
||
? '<span style="color:#c0392b;" title="Не удалось прочитать файл: пустой, повреждённый или скан без текста">не извлечён</span>'
|
||
: '<span style="color:#22c55e;">✓ ' + (p.elapsed ? p.elapsed.toFixed(1) : '0.0') + 'с</span>';
|
||
rows.push(procRow(state, i, txt));
|
||
}
|
||
}
|
||
if (groups.over.length) {
|
||
rows.push('<tr class="grp-row"><td colspan="4">⛔ Пропущены (сверх лимита) (' + groups.over.length + ')</td></tr>');
|
||
for (const i of groups.over) {
|
||
rows.push(procRow(state, i, '<span style="color:#c0392b;">пропущен (лимит)</span>'));
|
||
}
|
||
}
|
||
if (groups.current.length) {
|
||
rows.push('<tr class="grp-row"><td colspan="4">▶ Текущий файл</td></tr>');
|
||
for (const i of groups.current) {
|
||
const p = procState[i];
|
||
const cur = ((performance.now() - p.t0) / 1000).toFixed(1);
|
||
const eta = (p.eta != null) ? ' / ~' + fmtSec(p.eta) : '';
|
||
rows.push(procRow(state, i, '<span style="color:#2563eb;">⏳ ' + cur + 'с' + eta + '</span>'));
|
||
}
|
||
}
|
||
if (groups.pending.length) {
|
||
rows.push('<tr class="grp-row"><td colspan="4">○ Ожидают обработки (' + groups.pending.length + ')</td></tr>');
|
||
for (const i of groups.pending) {
|
||
const p = procState[i] || {};
|
||
// Оценка: LLM (chars x rate) если известна; иначе грубая по размеру/скорости извлечения
|
||
if (rate && !p.est && p.chars > 0) p.est = p.chars * rate;
|
||
if (!p.est) {
|
||
const szMB = (state.files[i] ? state.files[i].size : 0) / 1048576;
|
||
const k = (state.proc && state.proc.procExtractRate) || cfg.estMbSec || DEFAULT_EST_MB_SEC; // сек/МБ
|
||
p.est = Math.max(1, Math.round(szMB * k));
|
||
}
|
||
let txt;
|
||
if (p.st === 'analyzed') txt = '<span style="color:#7d3c98;">анализ ✓</span>';
|
||
else if (p.st === 'current') txt = '<span style="color:#2563eb;">⏳</span>';
|
||
else if (p.est != null) txt = '<span style="color:#999;">~' + fmtSec(p.est) + '</span>';
|
||
else txt = '<span style="color:#999;">—</span>';
|
||
rows.push(procRow(state, i, txt));
|
||
}
|
||
}
|
||
els.tableBody.innerHTML = rows.join('');
|
||
const cntDone = groups.done.length;
|
||
els.countEl.textContent = 'Готово ' + cntDone + ' / ' + state.files.length + ' файлов';
|
||
}
|