104 lines
5.2 KiB
Python
104 lines
5.2 KiB
Python
#!/usr/bin/env python3
|
||
"""exp12 — реконструкция per-chunk инъекционных блоков глоссария из приёмочного store.
|
||
|
||
$0. Воспроизводит ровно то, что боевой редактор A1 (glm-5 mono) видел:
|
||
- editorConstraintBlock (renderEditorConstraintBlock, memory.go): CONFIRMED dst-формы,
|
||
строки «- «dst»», дедуп по dst. Заголовок editorConstraintHeader.
|
||
- bilingualGlossaryBlock (renderGlossaryBlock, memory.go): «src → dst» (+ ⟨проверить⟩
|
||
для не-CONFIRMED). Заголовок glossaryBlockHeader. Для билингв-армов (редактор видит src).
|
||
|
||
Активные ключи чанка = injected_ids(chunk) ∪ injected_ids(chunk0) для chunk>0 (аппрокс sticky
|
||
A5 внутри главы; выбранные главы — 2-чанковые, sticky несёт ровно активные chunk0).
|
||
Ключ injected_id = «src\x1fsense\x1fsince\x1funtil» → строка glossary по (src,sense,since,until).
|
||
CONFIRMED ⇔ status='approved' и окно since/until валидно для главы.
|
||
|
||
Выход: diag/blocks.json {(ch/chunk): {editor_constraint, bilingual_glossary, keys[]}}
|
||
"""
|
||
from __future__ import annotations
|
||
import json, sqlite3
|
||
from pathlib import Path
|
||
|
||
DIAG = Path("/home/ubuntu/books/gu-zhenren/diag")
|
||
DB = DIAG / "store-copy.db"
|
||
SEL = [5, 7, 14] # пре-регистрированный выбор (exp12_extract.py)
|
||
|
||
EDITOR_HEADER = ("КАНОНИЧЕСКИЕ ПЕРЕВОДЫ имён и терминов (в черновике эти сущности должны быть "
|
||
"переданы ИМЕННО этими формами — приводи к ним любые расхождения, склоняя по "
|
||
"контексту; не вводи иных вариантов и не меняй ничего другого):")
|
||
GLOSS_HEADER = ("ГЛОССАРИЙ (используй эти утверждённые переводы имён и терминов последовательно; "
|
||
"строки с пометкой ⟨проверить⟩ — неподтверждённые кандидаты):")
|
||
|
||
|
||
def main() -> None:
|
||
con = sqlite3.connect(DB)
|
||
con.row_factory = sqlite3.Row
|
||
# glossary lookup by (src,sense,since,until)
|
||
gl = {}
|
||
for r in con.execute("SELECT src,sense,since_ch,until_ch,dst,status FROM glossary"):
|
||
gl[(r["src"], r["sense"], r["since_ch"], r["until_ch"])] = (r["dst"], r["status"])
|
||
|
||
# injected_ids per selected chunk
|
||
inj = {}
|
||
for r in con.execute(
|
||
"SELECT chapter,chunk_idx,injected_ids FROM retrieval_state WHERE chapter IN (%s)"
|
||
% ",".join(map(str, SEL))
|
||
):
|
||
inj[(r["chapter"], r["chunk_idx"])] = json.loads(r["injected_ids"] or "[]")
|
||
con.close()
|
||
|
||
def keydec(k: str):
|
||
src, sense, since, until = k.split("\x1f")
|
||
return (src, sense, int(since), int(until))
|
||
|
||
out = {}
|
||
for ch in SEL:
|
||
chunks = sorted(c for (cc, c) in inj if cc == ch)
|
||
chunk0 = inj.get((ch, 0), [])
|
||
for c in chunks:
|
||
active = list(inj.get((ch, c), []))
|
||
if c > 0: # sticky: carry chunk0's exact matches (A5 scene inertia within chapter)
|
||
for k in chunk0:
|
||
if k not in active:
|
||
active.append(k)
|
||
# map to records
|
||
recs = []
|
||
for k in active:
|
||
src, sense, since, until = keydec(k)
|
||
dst, status = gl.get((src, sense, since, until), ("", "auto"))
|
||
if not dst.strip():
|
||
continue
|
||
confirmed = (status == "approved") # windows all 0/0 here → spoiler-valid
|
||
recs.append({"src": src, "dst": dst.strip(), "confirmed": confirmed})
|
||
|
||
# editor constraint block: CONFIRMED only, dedup by dst
|
||
seen, elines = set(), []
|
||
for r in recs:
|
||
if not r["confirmed"] or r["dst"] in seen:
|
||
continue
|
||
seen.add(r["dst"]); elines.append(f"- «{r['dst']}»")
|
||
editor_block = (EDITOR_HEADER + "\n" + "\n".join(elines)) if elines else ""
|
||
|
||
# bilingual glossary block: src → dst (+ ⟨проверить⟩ if not confirmed)
|
||
glines = []
|
||
for r in recs:
|
||
line = f"{r['src']} → {r['dst']}"
|
||
if not r["confirmed"]:
|
||
line += " ⟨проверить⟩"
|
||
glines.append(line)
|
||
gloss_block = (GLOSS_HEADER + "\n" + "\n".join(glines)) if glines else ""
|
||
|
||
out[f"{ch}/{c}"] = {
|
||
"editor_constraint": editor_block,
|
||
"bilingual_glossary": gloss_block,
|
||
"n_constraint_terms": len(elines),
|
||
"n_gloss_terms": len(glines),
|
||
}
|
||
|
||
(DIAG / "blocks.json").write_text(json.dumps(out, ensure_ascii=False, indent=1), encoding="utf-8")
|
||
for k, v in out.items():
|
||
print(f"\n=== chunk {k}: {v['n_constraint_terms']} constraint / {v['n_gloss_terms']} gloss terms ===")
|
||
print(v["editor_constraint"][:400])
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|