Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

- Add stricter Place Code validation (#99).
- Parse raw values to values for all fields (#98).
- Add constraints and tests for Direction (#97).
- Improve support for Distance, Direction and Elapsed Time (#96).
Expand Down
12 changes: 12 additions & 0 deletions src/euring/codes.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import re
from collections.abc import Callable, Mapping
from datetime import date
from typing import Any
Expand Down Expand Up @@ -200,6 +201,17 @@ def parse_direction(value: str) -> int | None:
return parsed


_PLACE_CODE_RE = re.compile(r"^[A-Z]{2}([A-Z]{2}|[0-9]{2}|--)$")


def parse_place_code(value: str) -> str:
"""Validate the place code pattern (AA##, AAAA, or AA--)."""
value_str = f"{value}"
if not _PLACE_CODE_RE.match(value_str):
raise EuringConstraintException(f'Value "{value}" is not a valid place code format.')
return value_str


def _parse_decimal_coordinate(value: str, *, max_abs: int, max_decimals: int, field_name: str) -> float:
"""Parse and validate a decimal latitude/longitude string."""
try:
Expand Down
13 changes: 10 additions & 3 deletions src/euring/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
parse_latitude,
parse_longitude,
parse_old_greater_coverts,
parse_place_code,
)
from .field_schema import EuringField, EuringFormattedField, EuringLookupField
from .types import (
Expand Down Expand Up @@ -173,8 +174,13 @@
lookup=LOOKUP_ACCURACY_OF_DATE,
),
EuringField(name="Time", key="time", type_name=TYPE_ALPHANUMERIC, length=4),
EuringLookupField(
name="Place Code", key="place_code", type_name=TYPE_ALPHANUMERIC, length=4, lookup=lookup_place_code
EuringFormattedField(
name="Place Code",
key="place_code",
type_name=TYPE_ALPHANUMERIC,
length=4,
parser=parse_place_code,
lookup=lookup_place_code,
),
EuringFormattedField(
name="Geographical Co-ordinates",
Expand Down Expand Up @@ -337,12 +343,13 @@
required=False,
parser=parse_longitude,
),
EuringLookupField(
EuringFormattedField(
name="Current Place Code",
key="current_place_code",
type_name=TYPE_ALPHANUMERIC,
length=4,
required=False,
parser=parse_place_code,
lookup=lookup_place_code,
),
EuringField(name="More Other Marks", key="more_other_marks", type_name=TYPE_ALPHABETIC, required=False),
Expand Down
11 changes: 11 additions & 0 deletions tests/test_decoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
parse_direction,
parse_geographical_coordinates,
parse_old_greater_coverts,
parse_place_code,
)
from euring.exceptions import EuringConstraintException, EuringTypeException
from euring.fields import EURING_FIELDS
Expand Down Expand Up @@ -180,6 +181,16 @@ def test_parse_direction_allows_hyphens(self):
assert parse_direction("---") is None
assert parse_direction("-") is None

def test_parse_place_code_allows_country_unknown_subdivision(self):
assert parse_place_code("NL--") == "NL--"

def test_parse_place_code_allows_numeric_subdivision(self):
assert parse_place_code("ES00") == "ES00"

def test_parse_place_code_rejects_invalid_pattern(self):
with pytest.raises(EuringConstraintException):
parse_place_code("N1--")

def test_parse_direction_rejects_out_of_range(self):
with pytest.raises(EuringConstraintException):
parse_direction("360")
Expand Down