This commit is contained in:
team3
2026-07-06 14:44:20 +02:00
parent f4c5116abb
commit cc27e53b9e
20 changed files with 504 additions and 267 deletions

View File

@@ -32,8 +32,46 @@ def _norm_title(s: str) -> str:
return s.casefold()
# Bracket-aware separator handling: a ' — ' (or a dash that _norm_dash would normalize) INSIDE
# ()[]{} is part of the title, not a title/description separator. Without this a spaced dash within
# a math title truncates it: 'Aε-Algorithmus (Güte 1+ε, Laufzeit O(n³)) — …' → 'Aε-Algorithmus (Güte…'.
_BRACKETS = {"(": ")", "[": "]", "{": "}"}
_CLOSERS = {v: k for k, v in _BRACKETS.items()}
def _unclosed(s: str) -> bool:
"""True if s has a dangling opening bracket ()[]{} — the symptom of a mid-bracket cut. Extra
closers (more ')' than '(') do NOT count; only an unclosed opener signals a truncated title."""
depth = 0
for c in s:
if c in _BRACKETS:
depth += 1
elif c in _CLOSERS and depth > 0:
depth -= 1
return depth > 0
def _split_top(s: str, sep: str) -> str:
"""First bracket-depth-0 segment of s split on sep; whole s if sep never occurs at depth 0."""
depth = 0
for i, c in enumerate(s):
if c in _BRACKETS:
depth += 1
elif c in _CLOSERS and depth > 0:
depth -= 1
elif depth == 0 and s.startswith(sep, i):
return s[:i]
return s
def _title(entry: str) -> str:
return entry.split("")[0].strip() or entry
"""Title = text before the first TOP-LEVEL ''. A separator inside ()[]{} does not split
(keeps math titles intact); if the extracted title has an unclosed bracket (malformed source),
keep the whole entry rather than emit a truncated title."""
title = _split_top(entry, "")
if _unclosed(title):
return entry.strip() or entry
return title.strip() or entry
def clean_title(s: str) -> str:
@@ -74,6 +112,9 @@ def _resolve_title(idx: dict[str, int], t: str) -> int | None:
return idx.get(_norm_title(t)) or idx.get(_norm_title(_title(t)))
_DASH_SEP_RE = re.compile(r"\s*[‒–—―]\s+|\s+[‒–—―]\s*")
def _norm_dash(s: str) -> str:
"""Dash variants (en/em/figure/bar) with whitespace on AT LEAST ONE side → uniform separator ''.
Some models (especially non-western ones) use an en-dash "" instead of the em-dash; without
@@ -81,8 +122,12 @@ def _norm_dash(s: str) -> str:
space ("Titel —Beschreibung" / "Titel— Beschreibung") also breaks the split and leaks the source
filename into the description — so a dash with a space on either side is repaired too. The ASCII
hyphen "-" is deliberately NOT in the class (would split "n - 1"/"3-SAT"); requiring ≥1 surrounding
space keeps glued compounds like "Backtracking—Verfahren" and number ranges like "1215" untouched."""
return re.sub(r"\s*[‒–—―]\s+|\s+[‒–—―]\s*", "", s)
space keeps glued compounds like "Backtracking—Verfahren" and number ranges like "1215" untouched.
A qualifying dash INSIDE an open bracket ()[]{} is left untouched — it is part of the title, not a
separator (else 'Algorithmus (Güte 1+ε O(n³)) — …' would split mid-parenthetical)."""
def _repl(m):
return m.group(0) if _unclosed(s[:m.start()]) else ""
return _DASH_SEP_RE.sub(_repl, s)
def _parse_selection(text: str) -> dict[int, str]: