| """Explicit high-coverage rule fallback for declarative sentences."""
|
|
|
| from __future__ import annotations
|
|
|
| import re
|
|
|
| from app.pipeline.nlp import get_nlp
|
|
|
| _SUBJECT_DEPS = frozenset({"nsubj", "nsubjpass", "csubj", "csubjpass"})
|
| _PROTECTED = re.compile(r"ZZPROTECTED[A-Z]+\d+ZZ", re.I)
|
| _QUOTES = frozenset({'"', "“", "”", "‘", "’"})
|
|
|
|
|
| def _lower_continuation(text: str, subject_head) -> str:
|
| value = text.strip()
|
| if not value:
|
| return value
|
| if (
|
| subject_head.pos_ == "PROPN"
|
| or subject_head.text == "I"
|
| ):
|
| return value
|
| return value[:1].lower() + value[1:]
|
|
|
|
|
| def force_cleft_rewrite(text: str) -> str | None:
|
| """Create a subject cleft while retaining the original predicate."""
|
| raw = (text or "").strip()
|
| if (
|
| not raw
|
| or raw.endswith("?")
|
| or any(mark in raw for mark in _QUOTES)
|
| or _PROTECTED.search(raw)
|
| ):
|
| return None
|
|
|
| nlp = get_nlp()
|
| if nlp is None:
|
| return None
|
| try:
|
| doc = nlp(raw)
|
| except Exception:
|
| return None
|
|
|
| root = next((token for token in doc if token.dep_ == "ROOT"), None)
|
| subjects = [token for token in doc if token.dep_ in _SUBJECT_DEPS]
|
| if root is None:
|
| return None
|
|
|
|
|
|
|
| finite_aux = next(
|
| (
|
| token
|
| for token in doc
|
| if token.pos_ == "AUX" and token.i > 0 and token.head.i > token.i
|
| ),
|
| None,
|
| )
|
| if doc[0].tag_ == "VBG" and finite_aux is not None:
|
| subject = doc[0]
|
| start = doc[0].idx
|
| end = finite_aux.idx
|
| else:
|
| if not subjects:
|
| return None
|
| subject = next(
|
| (
|
| token
|
| for token in subjects
|
| if token.head == root or root in tuple(token.ancestors)
|
| ),
|
| subjects[0],
|
| )
|
| subject_tokens = sorted(subject.subtree, key=lambda token: token.i)
|
| if not subject_tokens:
|
| return None
|
| start = subject_tokens[0].idx
|
| last = subject_tokens[-1]
|
| end = last.idx + len(last.text)
|
| prefix = raw[:start].strip()
|
| subject_text = raw[start:end].strip(" ,")
|
| predicate = raw[end:].strip()
|
| predicate = predicate.lstrip(" ,")
|
| terminal = "."
|
| if predicate.endswith(("!", ".")):
|
| terminal = predicate[-1]
|
| predicate = predicate[:-1].rstrip()
|
| if not subject_text or not predicate:
|
| return None
|
| if predicate.lower().startswith(("is it ", "was it ")):
|
| return None
|
|
|
| subject_text = _lower_continuation(subject_text, subject)
|
| core = f"it is {subject_text} that {predicate}{terminal}"
|
| if prefix:
|
| prefix = prefix.rstrip(" ,")
|
| candidate = f"{prefix}, {core}"
|
| else:
|
| candidate = core[:1].upper() + core[1:]
|
| candidate = re.sub(r"\s+", " ", candidate).strip()
|
| if candidate.lower().rstrip(".!?") == raw.lower().rstrip(".!?"):
|
| return None
|
| return candidate
|
|
|