aeat.adapters.inbound.pdf._label_regex module

Shared label-anchored regex extraction primitive.

Every casilla-complete extractor under adapters.inbound.declaracion and adapters.inbound.borrador runs essentially the same primitive: for a mapping of casilla_id to compiled pattern, search the PDF’s text stream and return the first match per casilla. This module is the single authoritative implementation; the caller still decides which casillas are in scope through registry extraction profiles or a caller-supplied borrador profile.

The Spanish amount capture group SPANISH_AMOUNT_GROUP is the canonical AEAT printed-amount format. Extractor modules import it and compose per-casilla patterns on top of it.

SPANISH_AMOUNT_GROUP

Capture group for AEAT-printed monetary amounts (Spanish locale).

Matches optional sign, 1-3 leading digits, zero or more groups of <thousands-separator><3 digits> where the separator is . (ASCII full stop), U+00A0 NBSP, or U+202F narrow NBSP, and a mandatory ,<2 digits> decimal tail. The separator class deliberately excludes ASCII space and tab so the regex cannot cross AEAT column-separator whitespace.

TEXT_VALUE_GROUP

Capture group for the last whitespace-delimited token on a line.

pdfplumber collapses AEAT’s column-separator whitespace to single spaces, so the “value-to-the-right-of-the-label” invariant reduces to “the final token on the line”. Multi-token textual values (e.g. "La Rioja") need a richer bbox-anchored primitive instead.

parse_spanish_decimal(raw)[source]

Parse an AEAT-formatted decimal string into a decimal.Decimal.

Accepts the canonical Spanish form 1.234,56, whitespace-separated thousands 1 234,56 (any unicode whitespace including U+00A0 NBSP and U+202F narrow NBSP), and US-style 1234.56. The parser is intentionally more permissive than the regex capture in SPANISH_AMOUNT_GROUP so it can recover values from messy input that has already been delimited by an upstream label match.

Parameters:

raw (str) – Raw numeric substring captured from the PDF.

Return type:

Decimal | None

Returns:

The parsed value, or None if raw is empty, a bare sign, or otherwise unparseable.

class LabelHit(casilla_id, raw_value, decimal_value, match_count)[source]

Bases: object

One successful label-anchored regex match.

Variables:
  • casilla_id – Stable casilla identifier the match is bound to.

  • raw_value – Verbatim substring captured by group 1 of the pattern, with surrounding whitespace stripped.

  • decimal_valueraw_value parsed via parse_spanish_decimal(), or None when the capture is non-numeric.

  • match_count – Number of times the pattern matched text. 1 means the label was unambiguous for this text stream; values greater than 1 mean first-match-wins extraction succeeded but downstream consumers should downgrade confidence or surface an ambiguity warning.

Parameters:
  • casilla_id (CasillaId)

  • raw_value (str)

  • decimal_value (Decimal | None)

  • match_count (int)

casilla_id: TypeAliasType
raw_value: str
decimal_value: Decimal | None
match_count: int
apply_label_regex(text, label_regex_map)[source]

Run each (casilla_id, pattern) regex against text.

First match wins for the raw value; LabelHit.match_count reflects the total number of hits so callers can downgrade confidence when the pattern is ambiguous. Patterns that never match are omitted rather than represented as empty hits, leaving coverage decisions to the calling parser’s registry/profile policy.

Parameters:
  • text (str) – Concatenated text returned by a PDF parser backend.

  • label_regex_map (Mapping[TypeAliasType, Pattern[str]]) – Mapping of casilla identifier to compiled regex. Each pattern must expose group 1 as the value capture.

Return type:

dict[TypeAliasType, LabelHit]

Returns:

A dict keyed by casilla identifier, populated only for patterns that matched at least once. Each value is a LabelHit.