Админка: колонка «Пользователь» в обработанных документах + FILES.web_account

- бот: @username / имя / tg:id; веб: аккаунт сессии или «аноним»
- новая колонка FILES.web_account (tg:/yandex:/vk:/email:) — пишется в
  _finalize_submit из резолвнутого аккаунта, чтобы у залогиненных веб-юзеров
  атрибуция работала для любого способа входа (раньше только Telegram)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
berkio_admin_gitea
2026-07-16 14:58:30 +00:00
parent c506e9ec24
commit 99dad2f343

View File

@@ -138,7 +138,7 @@ def _convert_doc_to_pdf(src_path: str, dst_path: str) -> None:
def _db_insert_job( def _db_insert_job(
name_or_f, stitching, title, numeration, flip_side, name_or_f, stitching, title, numeration, flip_side,
color, pdfa, quality, original_name, source_size_mb, color, pdfa, quality, original_name, source_size_mb,
ocr=0, web_owner_id=0, ocr=0, web_owner_id=0, web_account=None,
): ):
now = datetime.datetime.now() now = datetime.datetime.now()
date_str = now.strftime("%Y.%m.%d") date_str = now.strftime("%Y.%m.%d")
@@ -178,6 +178,9 @@ def _db_insert_job(
if "web_owner_id" in cols: if "web_owner_id" in cols:
fields.append("web_owner_id") fields.append("web_owner_id")
values.append(int(web_owner_id or 0)) values.append(int(web_owner_id or 0))
if "web_account" in cols:
fields.append("web_account")
values.append(web_account or "")
placeholders = ", ".join(["?"] * len(fields)) placeholders = ", ".join(["?"] * len(fields))
sql = f"INSERT INTO FILES ({', '.join(fields)}) VALUES ({placeholders})" sql = f"INSERT INTO FILES ({', '.join(fields)}) VALUES ({placeholders})"
@@ -409,6 +412,9 @@ def _db_ensure_auth_table():
con.execute("ALTER TABLE FILES ADD COLUMN ocr INTEGER DEFAULT 0") con.execute("ALTER TABLE FILES ADD COLUMN ocr INTEGER DEFAULT 0")
if "web_owner_id" not in have: if "web_owner_id" not in have:
con.execute("ALTER TABLE FILES ADD COLUMN web_owner_id INTEGER DEFAULT 0") con.execute("ALTER TABLE FILES ADD COLUMN web_owner_id INTEGER DEFAULT 0")
if "web_account" not in have:
# аккаунт сессии (tg:/yandex:/vk:/email:) — кто загрузил с сайта
con.execute("ALTER TABLE FILES ADD COLUMN web_account TEXT DEFAULT ''")
except Exception as _mig: except Exception as _mig:
print(f"FILES migrate error: {_mig}") print(f"FILES migrate error: {_mig}")
# Авто-миграция WEB_ACCOUNTS: авто-продление подписки (сохранённый способ оплаты) # Авто-миграция WEB_ACCOUNTS: авто-продление подписки (сохранённый способ оплаты)
@@ -1094,6 +1100,7 @@ def _finalize_submit(job_id, file_path, original_name, stitching, title,
source_size_mb=size_mb, source_size_mb=size_mb,
ocr=ocr, ocr=ocr,
web_owner_id=owner, web_owner_id=owner,
web_account=acc,
) )
except Exception as exc: except Exception as exc:
_safe_remove(file_path) _safe_remove(file_path)
@@ -3205,7 +3212,7 @@ _ADMIN_HTML = """<!doctype html>
<h2 style="margin:0">Обработанные документы <span class="muted" id="docs-totals"></span></h2> <h2 style="margin:0">Обработанные документы <span class="muted" id="docs-totals"></span></h2>
<button class="ghost" onclick="downloadCsv()">Экспорт CSV</button> <button class="ghost" onclick="downloadCsv()">Экспорт CSV</button>
</div> </div>
<div class="scroll"><table><thead><tr><th>Дата</th><th>Файл</th><th class="num">Стр.</th><th class="num">Время</th><th class="num">Исходник</th><th class="num">Результат</th><th>Кач-во</th><th>Сшивка</th><th>OCR</th><th>Источник</th></tr></thead> <div class="scroll"><table><thead><tr><th>Дата</th><th>Файл</th><th class="num">Стр.</th><th class="num">Время</th><th class="num">Исходник</th><th class="num">Результат</th><th>Кач-во</th><th>Сшивка</th><th>OCR</th><th>Источник</th><th>Пользователь</th></tr></thead>
<tbody id="t-docs"></tbody></table></div> <tbody id="t-docs"></tbody></table></div>
</section> </section>
@@ -3425,11 +3432,15 @@ _ADMIN_HTML = """<!doctype html>
document.getElementById('t-docs').innerHTML = docs.length ? docs.map(function(f){ document.getElementById('t-docs').innerHTML = docs.length ? docs.map(function(f){
var web = (f.user_id === wid); var web = (f.user_id === wid);
var srcPill = web ? '<span class="pill web">сайт</span>' : '<span class="pill bot">бот</span>'; var srcPill = web ? '<span class="pill web">сайт</span>' : '<span class="pill bot">бот</span>';
var who = web
? (f.web_account || (f.web_owner_id ? 'tg:'+f.web_owner_id : ''))
: ((f.username && f.username!=='webuser') ? '@'+f.username : (f.first_name || ('tg:'+f.user_id)));
var whoCell = who ? esc(who) : '<span class="muted">аноним</span>';
var dd = (f.date||'') + (f.time ? ' '+String(f.time).slice(0,5) : ''); var dd = (f.date||'') + (f.time ? ' '+String(f.time).slice(0,5) : '');
return '<tr><td>'+esc(dd)+'</td><td class="fname" title="'+esc(f.name)+'">'+esc(f.name)+'</td><td class="num">'+(f.pages||'')+ return '<tr><td>'+esc(dd)+'</td><td class="fname" title="'+esc(f.name)+'">'+esc(f.name)+'</td><td class="num">'+(f.pages||'')+
'</td><td class="num">'+psec(f.secs)+'</td><td class="num">'+size(f.source_size_mb)+'</td><td class="num">'+size(f.result_size_mb)+ '</td><td class="num">'+psec(f.secs)+'</td><td class="num">'+size(f.source_size_mb)+'</td><td class="num">'+size(f.result_size_mb)+
'</td><td>'+esc(f.quality||'')+'</td><td>'+esc(f.stitching||'')+'</td><td>'+(f.ocr?'':'')+'</td><td>'+srcPill+'</td></tr>'; '</td><td>'+esc(f.quality||'')+'</td><td>'+esc(f.stitching||'')+'</td><td>'+(f.ocr?'':'')+'</td><td>'+srcPill+'</td><td>'+whoCell+'</td></tr>';
}).join('') : '<tr><td colspan="10" class="muted">Пока нет документов</td></tr>'; }).join('') : '<tr><td colspan="11" class="muted">Пока нет документов</td></tr>';
} }
function showGate(err){ function showGate(err){
@@ -3743,9 +3754,11 @@ def _admin_collect_stats() -> dict:
# Документы # Документы
cols = {r[1] for r in con.execute("PRAGMA table_info(FILES)")} cols = {r[1] for r in con.execute("PRAGMA table_info(FILES)")}
pages_expr = "COALESCE(NULLIF(pages_total,0), num_pages, 0)" if "pages_total" in cols else "COALESCE(num_pages,0)" pages_expr = "COALESCE(NULLIF(pages_total,0), num_pages, 0)" if "pages_total" in cols else "COALESCE(num_pages,0)"
wa_expr = "COALESCE(web_account,'')" if "web_account" in cols else "''"
docs = [dict(r) for r in con.execute( docs = [dict(r) for r in con.execute(
f"SELECT date, time, COALESCE(NULLIF(original_name,''), name_or_f) AS name, {pages_expr} AS pages, " f"SELECT date, time, COALESCE(NULLIF(original_name,''), name_or_f) AS name, {pages_expr} AS pages, "
"source_size_mb, result_size_mb, quality, stitching, ocr, elapsed_time, user_id, url_res_f " "source_size_mb, result_size_mb, quality, stitching, ocr, elapsed_time, user_id, url_res_f, "
f"username, first_name, COALESCE(web_owner_id,0) AS web_owner_id, {wa_expr} AS web_account "
"FROM FILES ORDER BY rowid DESC LIMIT 150")] "FROM FILES ORDER BY rowid DESC LIMIT 150")]
for _d in docs: for _d in docs:
_d["secs"] = _elapsed_to_sec(_d.get("elapsed_time")) _d["secs"] = _elapsed_to_sec(_d.get("elapsed_time"))