cadrumo.core.corpus_text module

Import-light text normalisation and extracted-unit resolution for corpora.

exception CorpusAnchorResolutionError(message=None, *, context=None, translated_message=None)[source]

Bases: CoreError

Raised when an extracted corpus sidecar has no unique target unit.

A core-primitive failure, so it inherits CoreError and carries a registered error code rather than deriving from a bare ValueError. Both consumers – the citation lookup and the registry legal-reference reader – catch it by name and immediately wrap it in their own registered error, so nothing depended on the builtin base; what an unregistered root did cost was a structured envelope, leaving an operator a raw interpreter traceback for a bundled-corpus anchor that cannot resolve.

Parameters:
  • message (str | None)

  • context (Mapping[str, object] | None)

  • translated_message (str | None)

Return type:

None

code
normalise_corpus_text(text)[source]

Normalise corpus text for citation-presence checks.

The HTML-tag stripper only matches well-formed tags whose < immediately precedes a tag-name character (letter, slash, or !/?) and whose body is short and contains no spaces — so that bare comparison operators (e.g. < 500 euros and < 3 años that AEAT’s manuals use as math notation) and other unbalanced angle brackets do not inadvertently swallow long spans of prose.

Return type:

str

Parameters:

text (str)

class CorpusRedactionMark(amending_norm, vigencia, tag_start, body_start)[source]

Bases: NamedTuple

One dated redaction a consolidated BOE payload declares, and where it sits.

amending_norm is the norm that PRODUCED this redaction rather than the norm being read, so most marks in a much-amended article name a different document than the one requested. vigencia is the fecha_vigencia attribute as published, YYYYMMDD; it orders redactions and is NOT the effective date a citation should quote.

tag_start is the offset of the <version> opening tag and body_start the offset just past it, so a caller can slice exactly one redaction out of a payload that concatenates them all.

Parameters:
  • amending_norm (str)

  • vigencia (str)

  • tag_start (int)

  • body_start (int)

amending_norm: str

Alias for field number 0

vigencia: str

Alias for field number 1

tag_start: int

Alias for field number 2

body_start: int

Alias for field number 3

corpus_redaction_marks(payload)[source]

Return every dated redaction payload declares, in document order.

Order is reported but deliberately not meaningful. BOE’s open-data article endpoint concatenates redactions oldest first while the consolidated act.php view lists its version selector newest first, so any consumer that inferred “current” from position would be right about one source and silently bundle repealed law from the other. Selection therefore reads the fecha_vigencia attribute, and this function only enumerates.

A payload with no such markup – a consolidated whole-document page, an as-published single-document view, a hand-sliced article – yields an empty tuple, which is the honest answer for a document that declares no redaction history rather than a claim that it holds only one redaction.

Parameters:

payload (str) – The decoded corpus document.

Return type:

tuple[CorpusRedactionMark, ...]

Returns:

One CorpusRedactionMark per <version> element carrying both an id_norma and a fecha_vigencia.

extracted_unit_count(sidecar_path)[source]

Return how many readable units an extracted corpus sidecar carries.

Counts exactly the units resolve_anchored_extracted_unit() would consider – those carrying non-empty text – so a caller reasoning about how a sidecar partitions its source document reasons about the same partition the resolver selects from.

Parameters:

sidecar_path (Path) – Path to the .extracted.json artefact.

Return type:

int

Returns:

The number of units carrying non-empty text.

Raises:

CorpusAnchorResolutionError – If the sidecar cannot be read or declares no units list.

resolve_anchored_extracted_unit(sidecar_path, *, anchor, required_text=(), include_title=False)[source]

Return the one extracted unit that safely represents anchor.

Exact sidecar anchors take precedence. Legacy article slices without a matching persisted fragment remain safe only when their sidecar holds one unit; a requested fragment then cannot select unrelated text. Multi-unit records may use only a unique structural heading that matches the requested anchor. Missing or duplicate candidates raise instead of silently returning a whole document. required_text verifies the selected unit at the legal-reference layer; it cannot select a different one here.

Return type:

str

Parameters: