mirror of
https://git.pumpin.online/funkpower/catena.git
synced 2026-10-23 03:07:13 +00:00
104 lines
3.3 KiB
Python
104 lines
3.3 KiB
Python
"""Apply a subject scheme the way the book pipeline did, for comparison.
|
|||
|
|
|
||
|
|
The four functions below are the originals, copied out of the subjects module
|
||
|
|
verbatim and with the module-level scheme lifted into an argument — importing
|
||
|
|
it opens a database connection and loads a chat client, and neither decides
|
||
|
|
anything. What decides is the arithmetic of the scheme, which is what this
|
||
|
|
compares.
|
||
|
|
|
||
|
|
python3 oracle/subjects.py < cases.ndjson
|
||
|
|
|
||
|
|
One case per line, carrying its own scheme. Writes one JSON answer per line.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import json
|
||
|
|
import re
|
||
|
|
import sys
|
||
|
|
|
||
|
|
_STUDY_TITLE_RE = re.compile(
|
||
|
|
r"\b(companion to|guidebook to|very short introduction|"
|
||
|
|
r"letters of|collected letters|memoirs?\b|autobiograph|"
|
||
|
|
r"\bdiary\b|\bessays\b|\blectures\b|\bspeeches\b|"
|
||
|
|
r"a life of|biography of)\b",
|
||
|
|
re.IGNORECASE,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def canonical_tag(name, scheme):
|
||
|
|
subject_lower = {s.lower(): s for s in scheme["subjects"]}
|
||
|
|
alias_lower = {k.lower(): v for k, v in scheme["aliases"].items()}
|
||
|
|
drop_lower = {d.lower().strip("*[]") for d in scheme["drop"]}
|
||
|
|
t = (name or "").strip()
|
||
|
|
if not t:
|
||
|
|
return None
|
||
|
|
low = t.lower()
|
||
|
|
if low in drop_lower or low.strip("*[]") in drop_lower:
|
||
|
|
return None
|
||
|
|
if low in subject_lower:
|
||
|
|
return subject_lower[low]
|
||
|
|
if low in alias_lower:
|
||
|
|
return alias_lower[low]
|
||
|
|
return None
|
||
|
|
|
||
|
|
|
||
|
|
def expand_parents(subjects, scheme):
|
||
|
|
subject_set = frozenset(scheme["subjects"])
|
||
|
|
chosen = set()
|
||
|
|
for s in subjects:
|
||
|
|
if s not in subject_set:
|
||
|
|
continue
|
||
|
|
chosen.add(s)
|
||
|
|
parent = scheme["parents"].get(s)
|
||
|
|
if parent:
|
||
|
|
chosen.add(parent)
|
||
|
|
return [s for s in scheme["subjects"] if s in chosen]
|
||
|
|
|
||
|
|
|
||
|
|
def sanitize_subjects(subjects, scheme, title=None):
|
||
|
|
blocks = frozenset(scheme["blocks_fiction"])
|
||
|
|
genres = frozenset(scheme["fiction_genres"])
|
||
|
|
chosen = set(subjects)
|
||
|
|
study = bool(title and _STUDY_TITLE_RE.search(title))
|
||
|
|
if chosen & blocks or study:
|
||
|
|
chosen -= genres
|
||
|
|
return [s for s in scheme["subjects"] if s in chosen]
|
||
|
|
|
||
|
|
|
||
|
|
def compose_tags(subjects, scheme, title=None):
|
||
|
|
return sanitize_subjects(expand_parents(subjects, scheme), scheme, title=title)
|
||
|
|
|
||
|
|
|
||
|
|
def needs_classify(existing, scheme):
|
||
|
|
subject_set = frozenset(scheme["subjects"])
|
||
|
|
for raw in existing:
|
||
|
|
c = canonical_tag(raw, scheme)
|
||
|
|
if c and c in subject_set:
|
||
|
|
return False
|
||
|
|
return True
|
||
|
|
|
||
|
|
|
||
|
|
def answer(case):
|
||
|
|
scheme, kind = case["scheme"], case["kind"]
|
||
|
|
if kind == "canonical":
|
||
|
|
return {"subject": canonical_tag(case["tag"], scheme)}
|
||
|
|
if kind == "parents":
|
||
|
|
return {"subjects": expand_parents(case["subjects"], scheme)}
|
||
|
|
if kind == "fiction":
|
||
|
|
return {"subjects": sanitize_subjects(case["subjects"], scheme, title=case.get("title"))}
|
||
|
|
if kind == "compose":
|
||
|
|
return {"subjects": compose_tags(case["subjects"], scheme, title=case.get("title"))}
|
||
|
|
if kind == "needs":
|
||
|
|
return {"needs": needs_classify(case["existing"], scheme)}
|
||
|
|
raise SystemExit(f"no such case: {kind}")
|
||
|
|
|
||
|
|
|
||
|
|
for line in sys.stdin:
|
||
|
|
if not line.strip():
|
||
|
|
continue
|
||
|
|
case = json.loads(line)
|
||
|
|
out = {"name": case["name"], "kind": case["kind"]}
|
||
|
|
out.update(answer(case))
|
||
|
|
sys.stdout.write(json.dumps(out, sort_keys=True) + "\n")
|