v0.0.41: время на конкретный файл в колонке Статус (бэк считает, без общего LLM)
Deploy drhider / validate (push) Canceled after 0s
Deploy drhider / validate (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,40 @@
|
|||||||
|
# v0.0.41 — время на конкретный файл в колонке Статус — 2026-08-19
|
||||||
|
|
||||||
|
**Дата:** 2026-08-19
|
||||||
|
**Версия:** 0.0.40 → 0.0.41
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Суть
|
||||||
|
|
||||||
|
В колонке «Статус» таблицы у всех файлов показывалось ~одинаковое ОБЩЕЕ время.
|
||||||
|
Причина: фронт считал время как разницу `start`→`done`, а LLM — общий для всех
|
||||||
|
файлов (в проходе 1) — попадал в интервал КАЖДОГО файла.
|
||||||
|
|
||||||
|
Теперь бэк считает время на КОНКРЕТНЫЙ файл (extract_text + regex + замена,
|
||||||
|
БЕЗ общего LLM) и передаёт его в `done`-событии.
|
||||||
|
|
||||||
|
## Изменения
|
||||||
|
|
||||||
|
### `drhider/obfuscator.py`
|
||||||
|
- `import time` добавлен.
|
||||||
|
- Заведён `file_times = [0.0] * total`.
|
||||||
|
- Проход 1: `file_times[i] += time.time() - t0` вокруг extract_text + regex.
|
||||||
|
- Проход 2: `file_times[i] += time.time() - t0` вокруг замены.
|
||||||
|
- `progress_cb` расширен до `(phase, idx, total, fname, elapsed)`; в `done`
|
||||||
|
передаётся `round(file_times[i], 2)`.
|
||||||
|
|
||||||
|
### `site/routes/api_bp.py`
|
||||||
|
- `progress(phase, idx, total_, name, elapsed)`; в SSE `done` добавлен `elapsed`.
|
||||||
|
|
||||||
|
### `site/templates/index.html`
|
||||||
|
- В `done`-обработчике: если `d.elapsed > 0` — показать `d.elapsed.toFixed(1)`
|
||||||
|
(время файла от бэка), иначе fallback на старое вычисление.
|
||||||
|
|
||||||
|
### `site/app.py`
|
||||||
|
- `VERSION = "0.0.41"`.
|
||||||
|
|
||||||
|
## Проверка
|
||||||
|
- События: `[start(0,0.0), start(1,0.0), done(0,<время>), done(1,<время>)]`.
|
||||||
|
- `py_compile`, `node --check` — OK.
|
||||||
|
- Тесты: builder 20/20, replacer 10/10, scanner 20/20, zip 28/28.
|
||||||
+11
-5
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
import logging
|
import logging
|
||||||
from typing import Dict, List, Tuple, Callable, Optional
|
from typing import Dict, List, Tuple, Callable, Optional
|
||||||
|
|
||||||
@@ -94,9 +95,9 @@ class TwoPassObfuscator:
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
files: [(filename, content_bytes, content_type), ...]
|
files: [(filename, content_bytes, content_type), ...]
|
||||||
progress_cb: Опциональный коллбек (phase, idx, total, fname),
|
progress_cb: Опциональный коллбек (phase, idx, total, fname, elapsed),
|
||||||
где phase ∈ {"start", "done"}, idx — 0-based индекс.
|
где phase ∈ {"start", "done"}, idx — 0-based индекс,
|
||||||
Вызывается вокруг финальной обработки каждого файла.
|
elapsed — время обработки конкретного файла (сек).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(zip_bytes, csv_string):
|
(zip_bytes, csv_string):
|
||||||
@@ -112,18 +113,21 @@ class TwoPassObfuscator:
|
|||||||
# Извлекаем Markdown из каждого файла
|
# Извлекаем Markdown из каждого файла
|
||||||
all_texts: Dict[str, str] = {}
|
all_texts: Dict[str, str] = {}
|
||||||
total = len(files)
|
total = len(files)
|
||||||
|
file_times: List[float] = [0.0] * total
|
||||||
|
|
||||||
for i, (fname, content, ctype) in enumerate(files):
|
for i, (fname, content, ctype) in enumerate(files):
|
||||||
display_name = os.path.basename(fname) or fname
|
display_name = os.path.basename(fname) or fname
|
||||||
if progress_cb:
|
if progress_cb:
|
||||||
progress_cb("start", i, total, display_name)
|
progress_cb("start", i, total, display_name, 0.0)
|
||||||
|
|
||||||
|
t0 = time.time()
|
||||||
text = extractor.extract_text(fname, content, ctype)
|
text = extractor.extract_text(fname, content, ctype)
|
||||||
all_texts[fname] = text
|
all_texts[fname] = text
|
||||||
|
|
||||||
# Regex-сканирование (быстрое, локальное)
|
# Regex-сканирование (быстрое, локальное)
|
||||||
if text and not text.startswith("[DOC binary"):
|
if text and not text.startswith("[DOC binary"):
|
||||||
scanner.scan_regex(text, self._mapping, self._counters)
|
scanner.scan_regex(text, self._mapping, self._counters)
|
||||||
|
file_times[i] += time.time() - t0
|
||||||
|
|
||||||
# LLM-сканирование (получает уже найденное regex'ом чтобы не дублировать)
|
# LLM-сканирование (получает уже найденное regex'ом чтобы не дублировать)
|
||||||
if self._llm_client:
|
if self._llm_client:
|
||||||
@@ -141,6 +145,7 @@ class TwoPassObfuscator:
|
|||||||
fname = in_fname
|
fname = in_fname
|
||||||
display_name = os.path.basename(in_fname) or in_fname
|
display_name = os.path.basename(in_fname) or in_fname
|
||||||
|
|
||||||
|
t0 = time.time()
|
||||||
obf_content = content # По умолчанию — без изменений
|
obf_content = content # По умолчанию — без изменений
|
||||||
|
|
||||||
if fname.endswith('.doc'):
|
if fname.endswith('.doc'):
|
||||||
@@ -163,9 +168,10 @@ class TwoPassObfuscator:
|
|||||||
obf_content = replaced.encode('utf-8')
|
obf_content = replaced.encode('utf-8')
|
||||||
fname = md_name
|
fname = md_name
|
||||||
|
|
||||||
|
file_times[i] += time.time() - t0
|
||||||
results.append((fname, obf_content))
|
results.append((fname, obf_content))
|
||||||
if progress_cb:
|
if progress_cb:
|
||||||
progress_cb("done", i, total, display_name)
|
progress_cb("done", i, total, display_name, round(file_times[i], 2))
|
||||||
|
|
||||||
# ── Сборка результата ──
|
# ── Сборка результата ──
|
||||||
csv_str = builder.build_mapping_csv(self._mapping)
|
csv_str = builder.build_mapping_csv(self._mapping)
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ if _sys_path_root not in sys.path:
|
|||||||
sys.path.insert(0, _sys_path_root)
|
sys.path.insert(0, _sys_path_root)
|
||||||
|
|
||||||
# Версия приложения (меняется при изменениях)
|
# Версия приложения (меняется при изменениях)
|
||||||
VERSION = "0.0.40"
|
VERSION = "0.0.41"
|
||||||
|
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
|
|||||||
@@ -79,8 +79,8 @@ def process_stream(sid):
|
|||||||
q = queue.Queue()
|
q = queue.Queue()
|
||||||
cancel = threading.Event()
|
cancel = threading.Event()
|
||||||
|
|
||||||
def progress(phase, idx, total_, name):
|
def progress(phase, idx, total_, name, elapsed):
|
||||||
q.put(("progress", phase, idx, name, total_))
|
q.put(("progress", phase, idx, name, total_, elapsed))
|
||||||
|
|
||||||
def worker():
|
def worker():
|
||||||
try:
|
try:
|
||||||
@@ -117,11 +117,11 @@ def process_stream(sid):
|
|||||||
kind = evt[0]
|
kind = evt[0]
|
||||||
|
|
||||||
if kind == "progress":
|
if kind == "progress":
|
||||||
_, phase, idx, name, total_ = evt
|
_, phase, idx, name, total_, elapsed = evt
|
||||||
try:
|
try:
|
||||||
yield (
|
yield (
|
||||||
f"event: {phase}\n"
|
f"event: {phase}\n"
|
||||||
f"data: {json.dumps({'idx': idx, 'name': name, 'total': total_})}\n\n"
|
f"data: {json.dumps({'idx': idx, 'name': name, 'total': total_, 'elapsed': elapsed})}\n\n"
|
||||||
)
|
)
|
||||||
except _disconnect_exceptions():
|
except _disconnect_exceptions():
|
||||||
cancel.set()
|
cancel.set()
|
||||||
|
|||||||
@@ -484,8 +484,11 @@ async function uploadFiles() {
|
|||||||
activeES.addEventListener('done', function(e) {
|
activeES.addEventListener('done', function(e) {
|
||||||
const d = JSON.parse(e.data);
|
const d = JSON.parse(e.data);
|
||||||
clearInterval(fileIntervals[d.idx]);
|
clearInterval(fileIntervals[d.idx]);
|
||||||
const elapsed = ((performance.now() - (fileTimers[d.idx] || performance.now())) / 1000).toFixed(1);
|
// Время на КОНКРЕТНЫЙ файл приходит с бэка (extract_text + замена, без общего LLM)
|
||||||
ss(d.idx, '<span style="color:#22c55e">✓ ' + elapsed + 'с</span>');
|
const sec = (typeof d.elapsed === 'number' && d.elapsed > 0)
|
||||||
|
? d.elapsed.toFixed(1)
|
||||||
|
: ((performance.now() - (fileTimers[d.idx] || performance.now())) / 1000).toFixed(1);
|
||||||
|
ss(d.idx, '<span style="color:#22c55e">✓ ' + sec + 'с</span>');
|
||||||
});
|
});
|
||||||
activeES.addEventListener('complete', function(e) {
|
activeES.addEventListener('complete', function(e) {
|
||||||
activeES.close();
|
activeES.close();
|
||||||
|
|||||||
Reference in New Issue
Block a user