114 lines
5.7 KiB
Python
114 lines
5.7 KiB
Python
"""Last presentation pass over every accepted ZP Wiki answer (RAG and GraphRAG).
|
|
|
|
It changes wording only, never facts:
|
|
- sources are written as full links, never as numbers ("[1]");
|
|
- notices that explain the system instead of answering ("Vedúci je určený
|
|
podľa autora profilovej stránky…", "Profil obsahuje aj záznamy bez úplného
|
|
dátumu…", "Rok nedopĺňam…") are dropped;
|
|
- "this detail is not recorded" lines stay only when the answer has nothing
|
|
else; then one plain sentence says so;
|
|
- technical labels ("Názov / téma zo zdroja:") become plain words.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
DROP = [
|
|
r"Vedúci je určený podľa autora profilovej stránky[^\n]*",
|
|
r"Profil obsahuje aj záznamy bez úplného dátumu[^\n]*",
|
|
r"Rok nedopĺňam z roku práce\.?",
|
|
r"Údaje sú z indexovan[^\n]*",
|
|
r"Indexovan[éý][^\n]*",
|
|
r"Zobrazený je obmedzený výber[^\n]*",
|
|
]
|
|
PHASE_TAG = re.compile(r"\s+\((?:zaznamenaný výsledok|priebežný stav|plán|zadanie|podklad|navrhovaný cieľ|téma alebo názov|"
|
|
r"úloha označená ako splnená)\)(?=\s*$)", re.M)
|
|
# Students whose only match is an assignment/plan or reading: one line at the end instead of a block each.
|
|
SIDE = {"téma je v zadaní, téme alebo pláne práce; zaznamenaný výsledok k nej sa nenašiel.":
|
|
"Téma je len v zadaní alebo pláne, bez zaznamenaného výsledku",
|
|
"záznamy dokladajú len štúdium alebo poznámky k téme, nie vlastnú realizáciu.":
|
|
"Len štúdium alebo poznámky k téme"}
|
|
|
|
|
|
BULLET = re.compile(r"^- (?P<work>[^;\n:]+?); (?P<phase>[^;:\n]+?)(?:; záznam (?P<date>[^:\n]+?))?: (?P<text>.*)$", re.M)
|
|
LABEL_PREFIX = re.compile(r"^(?:-\s+|\*(?:Názov|Meno)[^*]*\*:\s*|(?:Názov|Téma)\s*:\s*)+")
|
|
|
|
|
|
def _bullet(match: re.Match) -> str:
|
|
text = LABEL_PREFIX.sub("", match.group("text").strip()).strip()
|
|
if not text or text.startswith("#"):
|
|
return "" # a bare heading ("## Diplomový projekt 2") says nothing
|
|
if re.fullmatch(r"(?:Vedúci\w*:\s*)?(?:(?:Ing|Mgr|doc|prof|Bc|RNDr)\.\s*)*[A-ZÁ-Ž]\w+\s+[A-ZÁ-Ž]\w+(?:,\s*PhD\.?)?\.?", text):
|
|
return "" # only the supervisor's name, already the subject of the answer
|
|
date = (match.group("date") or "").replace("(rok neuvedený)", "").strip(" :()")
|
|
return f"- {match.group('work').strip()}" + (f" ({date})" if date else "") + f": {text}"
|
|
|
|
|
|
def _collapse_side_people(blocks: list[str]) -> list[str]:
|
|
groups, current = [], []
|
|
for block in blocks:
|
|
if re.fullmatch(r"\*\*[^*\n]+\*\*", block) and current:
|
|
groups.append(current)
|
|
current = []
|
|
current.append(block)
|
|
if current:
|
|
groups.append(current)
|
|
kept, side = [], {}
|
|
for group in groups:
|
|
head = re.fullmatch(r"\*\*([^*\n]+)\*\*", group[0])
|
|
label = next((v for k, v in SIDE.items() for b in group if b.endswith(k)), None)
|
|
if head and label:
|
|
side.setdefault(label, []).append(head.group(1))
|
|
else:
|
|
kept += group
|
|
return kept + [f"{label}: {', '.join(names)}." for label, names in side.items()]
|
|
MISSING = re.compile(r"(?:požadovaný údaj nie je v tomto zápise doložený|v podkladoch chýbajú doložené údaje|"
|
|
r"Požadované údaje k tomuto stretnutiu nie sú v dostupnom výbere doložené)", re.I)
|
|
RENAME = [
|
|
(r"Názov / téma zo zdroja:\s*(?:\*Názov [^*]*\*:\s*)?", "Téma: "),
|
|
(r"Vedúci práce \(autor profilu študenta\):", "Vedúci práce:"),
|
|
(r"Konzultant práce \(zo zdroja\):", "Konzultant:"),
|
|
]
|
|
INLINE_CITE = re.compile(r"\s*(?:\[\d+\])+(?!\(|:)")
|
|
REFERENCE_LINE = re.compile(r"^\[\d+\]:\s*\S+\s*$", re.M)
|
|
SOURCES = re.compile(r"^Zdroj(?:e)?:\s*(.+)$", re.M)
|
|
|
|
|
|
def _links(line: str) -> list[str]:
|
|
urls = re.findall(r"\]\((https?://[^)\s]+)\)", line) or re.findall(r"https?://[^\s,)\]]+", line)
|
|
return list(dict.fromkeys(urls))
|
|
|
|
|
|
def finalize(text: str, source_urls: list[str] | None = None) -> str:
|
|
if not isinstance(text, str) or not text.strip():
|
|
return text
|
|
out = REFERENCE_LINE.sub("", text)
|
|
sources, label = [], None
|
|
for match in SOURCES.finditer(out):
|
|
sources += _links(match.group(1))
|
|
label = label or match.group(0).split(":", 1)[0]
|
|
out = SOURCES.sub("", out)
|
|
out = INLINE_CITE.sub("", out)
|
|
for pattern in DROP:
|
|
out = re.sub(pattern, "", out)
|
|
for pattern, replacement in RENAME:
|
|
out = re.sub(pattern, replacement, out)
|
|
out = PHASE_TAG.sub("", out)
|
|
out = BULLET.sub(_bullet, out)
|
|
out = re.sub(r"^- Téma alebo názov doložený pri tej istej práci:\s*(?:\*[^*]*\*:\s*)?", "- Téma: ", out, flags=re.M)
|
|
out = re.sub(r"(?m)^(\s*- [^:\n]+:)\s+-\s+", r"\1 ", out) # "Diplomový projekt: - Rozpoznávanie…'
|
|
blocks = _collapse_side_people([b.strip() for b in re.split(r"\n\s*\n", out) if b.strip()])
|
|
substantive = [b for b in blocks if not MISSING.search(b) and not re.fullmatch(r"\*\*[^*]+\*\*", b)
|
|
and not re.fullmatch(r"(?:\*\*[^*]+\*\*\s*)?(?:Posledný záznam[^\n]*|Zadanie a ciele:)", b)]
|
|
if substantive:
|
|
blocks = [b for b in blocks if not MISSING.search(b)]
|
|
blocks = [b for i, b in enumerate(blocks) # a heading left without its content
|
|
if not (re.fullmatch(r"[A-ZÁ-Ž][^\n]{0,40}:", b) and (i + 1 == len(blocks) or blocks[i + 1].startswith("**")))]
|
|
else:
|
|
heads = [b for b in blocks if re.fullmatch(r"\*\*[^*]+\*\*", b) or b.startswith("**")]
|
|
blocks = heads[:1] + ["ZP Wiki k tomu nemá záznam."]
|
|
sources = list(dict.fromkeys(sources)) # rewrite the answer's own source line, never add one
|
|
if sources:
|
|
blocks.append((label or "Zdroje") + ": " + ", ".join(sources))
|
|
return "\n\n".join(blocks)
|