Skip to content

Commit 30058e2

Browse files
authored
Follow through on euring_type (#107)
1 parent 0442e0f commit 30058e2

File tree

6 files changed

+51
-47
lines changed

6 files changed

+51
-47
lines changed

README.md

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ Decoded JSON structure (single record):
9898
### Python Library
9999

100100
```python
101-
from euring import EuringRecord, is_valid_type, TYPE_ALPHABETIC
101+
from euring import EuringRecord, is_valid_euring_type, TYPE_ALPHABETIC
102102

103103
# Decode a record
104104
record = EuringRecord.decode(
@@ -118,28 +118,48 @@ record_json = record.serialize(output_format="json")
118118
record_2020 = record.export("euring2020")
119119

120120
# Validate a value
121-
is_valid = is_valid_type("ABC", TYPE_ALPHABETIC)
121+
is_valid = is_valid_euring_type("ABC", TYPE_ALPHABETIC)
122122
```
123123

124124
Decoded records expose a single `fields` mapping keyed by the stable ASCII
125125
snake_case field `key`. Each field entry includes the official `name`, the raw
126126
`value`, and an `order` index for stable sorting.
127127

128+
### Field keys
129+
130+
For programmatic use, each field also has a stable ASCII [snake_case](https://en.wikipedia.org/wiki/Snake_case) `key`.
131+
132+
The EURING manuals use field names that may include spaces, hyphens, and mixed case. In many programming environments these are awkward to work with (for example when used as object attributes, column names, or identifiers in code). To make decoded output easier to use in Python, JSON, R, and similar tools, the library exposes a normalized ASCII snake_case `key` for every field.
133+
134+
These keys are provided as a practical convenience for developers. They are not part of the formal EURING specification, and consuming systems are free to map them to their own conventions where needed.
135+
136+
## EURING Reference Data
137+
138+
This package ships with EURING reference data in `src/euring/data`.
139+
140+
- All EURING Code tables follow the EURING Manual.
141+
- EURING-published updates for Species, Ringing Schemes, Place Codes, and Circumstances are curated and checked into the package.
142+
- End users do not need to refresh data separately.
143+
128144
## Data definition
129145

130146
EURING vocabulary (as per the manuals):
131147

132148
- Record: one encounter record.
133149
- Field: a single data element within a record.
134150
- Field name: the official EURING name for a field.
135-
- Type: the data type assigned to a field (Alphabetic, Alphanumeric, Integer, Numeric, Text).
151+
- EURING encoding type (`euring_type`): the wire-level constraint assigned to a field
152+
(Alphabetic, Alphanumeric, Integer, Numeric, Text).
153+
- Value type (`value_type`): the intended data type used by this library for a field.
136154
- Code: the coded value stored in a field.
137155
- Code table: the reference table that maps codes to descriptions.
138156
- Column: fixed-width position in EURING2000 records.
139157

140158
EURING uses a record-based format: each record contains a fixed sequence of fields.
141159
The manuals define official field names (with spaces/hyphens), which we preserve for display.
142160

161+
162+
143163
### Encoding vs data from an IT perspective
144164

145165
A EURING record is UTF-8 text that follows an ASCII-era encoding structure
@@ -188,22 +208,6 @@ the EURING format.
188208

189209
This package introduces a signed numeric type (`NumericSigned`) for the EURING2020 fields Latitude and Longitude. `NumericSigned` behaves like `Numeric`, but allows a leading minus sign and explicitly disallows -0. `NumericSigned` is a small, intentional clarification of the generic numeric types. The manuals clearly permit negative Latitude and Longitude in EURING2020, but the generic `Numeric` definition does not describe signed numbers. Making this explicit in the code helps prevent invalid values while staying faithful to the manuals and real-world usage. If a future revision of the specification formally defines signed numeric fields, this implementation can align with it without breaking compatibility.
190210

191-
### Field keys
192-
193-
For programmatic use, each field also has a stable ASCII [snake_case](https://en.wikipedia.org/wiki/Snake_case) `key`.
194-
195-
The EURING manuals use field names that may include spaces, hyphens, and mixed case. In many programming environments these are awkward to work with (for example when used as object attributes, column names, or identifiers in code). To make decoded output easier to use in Python, JSON, R, and similar tools, the library exposes a normalized ASCII snake_case `key` for every field.
196-
197-
These keys are provided as a practical convenience for developers. They are not part of the formal EURING specification, and consuming systems are free to map them to their own conventions where needed.
198-
199-
## EURING Reference Data
200-
201-
This package ships with EURING reference data in `src/euring/data`.
202-
203-
- All EURING Code tables follow the EURING Manual.
204-
- EURING-published updates for Species, Ringing Schemes, Place Codes, and Circumstances are curated and checked into the package.
205-
- End users do not need to refresh data separately.
206-
207211
### Data sources
208212

209213
- Species: <https://www.euring.org/files/documents/EURING_SpeciesCodes_IOC15_1.csv>

example.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
EuringRecord,
99
euring_dms_to_float,
1010
euring_lat_to_dms,
11-
is_valid_type,
11+
is_valid_euring_type,
1212
)
1313

1414

@@ -18,10 +18,10 @@ def main():
1818

1919
# Test type validation
2020
print("\n1. Type Validation:")
21-
print(f"is_alphabetic('ABC'): {is_valid_type('ABC', TYPE_ALPHABETIC)}")
22-
print(f"is_alphabetic('abc'): {is_valid_type('abc', TYPE_ALPHABETIC)}")
23-
print(f"is_integer('123'): {is_valid_type('123', TYPE_INTEGER)}")
24-
print(f"is_integer('12.3'): {is_valid_type('12.3', TYPE_INTEGER)}")
21+
print(f"is_alphabetic('ABC'): {is_valid_euring_type('ABC', TYPE_ALPHABETIC)}")
22+
print(f"is_alphabetic('abc'): {is_valid_euring_type('abc', TYPE_ALPHABETIC)}")
23+
print(f"is_integer('123'): {is_valid_euring_type('123', TYPE_INTEGER)}")
24+
print(f"is_integer('12.3'): {is_valid_euring_type('12.3', TYPE_INTEGER)}")
2525

2626
# Test coordinate conversion
2727
print("\n2. Coordinate Conversion:")

src/euring/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
is_numeric,
4040
is_numeric_signed,
4141
is_text,
42-
is_valid_type,
42+
is_valid_euring_type,
4343
)
4444
from .utils import (
4545
euring_dms_to_float,
@@ -68,7 +68,7 @@
6868
"is_numeric",
6969
"is_numeric_signed",
7070
"is_text",
71-
"is_valid_type",
71+
"is_valid_euring_type",
7272
"EuringException",
7373
"EuringConstraintException",
7474
"EuringTypeException",

src/euring/field_schema.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
TYPE_NUMERIC,
1818
TYPE_NUMERIC_SIGNED,
1919
TYPE_TEXT,
20-
is_valid_type,
20+
is_valid_euring_type,
2121
)
2222

2323
__all__ = [
@@ -87,7 +87,7 @@ def _validate_raw(self, raw: str) -> str | None:
8787
return None
8888
raise EuringConstraintException('Required field, empty value "" is not permitted.')
8989
self._validate_length(raw)
90-
if self.euring_type and not is_valid_type(raw, self.euring_type):
90+
if self.euring_type and not is_valid_euring_type(raw, self.euring_type):
9191
raise EuringTypeException(f'Value "{raw}" is not valid for type {self.euring_type}.')
9292
return raw
9393

@@ -160,7 +160,7 @@ def encode(self, value: Any | None) -> str:
160160
):
161161
str_value = str_value.zfill(self.length)
162162
self._validate_length(str_value)
163-
if self.euring_type and not is_valid_type(str_value, self.euring_type):
163+
if self.euring_type and not is_valid_euring_type(str_value, self.euring_type):
164164
raise EuringTypeException(f'Value "{str_value}" is not valid for type {self.euring_type}.')
165165
return str_value
166166

@@ -183,7 +183,7 @@ def encode_for_format(self, value: Any | None, *, format: str) -> str:
183183
if self.key == "date" and isinstance(value, dt_date):
184184
str_value = value.strftime("%d%m%Y")
185185
self._validate_length(str_value)
186-
if self.euring_type and not is_valid_type(str_value, self.euring_type):
186+
if self.euring_type and not is_valid_euring_type(str_value, self.euring_type):
187187
raise EuringTypeException(f'Value "{str_value}" is not valid for type {self.euring_type}.')
188188
return str_value
189189

@@ -203,7 +203,7 @@ def encode_for_format(self, value: Any | None, *, format: str) -> str:
203203
str_value = str_value.lstrip("0") or "0"
204204

205205
self._validate_length(str_value, ignore_variable_length=ignore_variable_length)
206-
if self.euring_type and not is_valid_type(str_value, self.euring_type):
206+
if self.euring_type and not is_valid_euring_type(str_value, self.euring_type):
207207
raise EuringTypeException(f'Value "{str_value}" is not valid for type {self.euring_type}.')
208208
return str_value
209209

src/euring/types.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,18 +110,18 @@ def is_text(value: str) -> bool:
110110
return _matches(value, RE_TEXT)
111111

112112

113-
def is_valid_type(value: str, type: str) -> bool:
114-
"""Return True if a value matches the specified EURING field type."""
115-
if type == TYPE_ALPHABETIC:
113+
def is_valid_euring_type(value: str, euring_type: str) -> bool:
114+
"""Return True if a value matches the specified EURING encoding type."""
115+
if euring_type == TYPE_ALPHABETIC:
116116
return is_alphabetic(value)
117-
if type == TYPE_ALPHANUMERIC:
117+
if euring_type == TYPE_ALPHANUMERIC:
118118
return is_alphanumeric(value)
119-
if type == TYPE_INTEGER:
119+
if euring_type == TYPE_INTEGER:
120120
return is_integer(value)
121-
if type == TYPE_NUMERIC:
121+
if euring_type == TYPE_NUMERIC:
122122
return is_numeric(value)
123-
if type == TYPE_NUMERIC_SIGNED:
123+
if euring_type == TYPE_NUMERIC_SIGNED:
124124
return is_numeric_signed(value)
125-
if type == TYPE_TEXT:
125+
if euring_type == TYPE_TEXT:
126126
return is_text(value)
127127
return False

tests/test_types.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
is_numeric,
1111
is_numeric_signed,
1212
is_text,
13-
is_valid_type,
13+
is_valid_euring_type,
1414
)
1515

1616

@@ -59,10 +59,10 @@ def test_text(self):
5959
assert not is_text("Hello\x00World")
6060
assert not is_text("Hello|World")
6161

62-
def test_is_valid_type(self):
63-
assert is_valid_type("ABC", TYPE_ALPHABETIC)
64-
assert is_valid_type("123", TYPE_INTEGER)
65-
assert is_valid_type("-12.3", TYPE_NUMERIC_SIGNED)
66-
assert not is_valid_type("-0", TYPE_NUMERIC_SIGNED)
67-
assert not is_valid_type("abc", TYPE_ALPHABETIC)
68-
assert is_valid_type("ABC", "Unknown") is False
62+
def test_is_valid_euring_type(self):
63+
assert is_valid_euring_type("ABC", TYPE_ALPHABETIC)
64+
assert is_valid_euring_type("123", TYPE_INTEGER)
65+
assert is_valid_euring_type("-12.3", TYPE_NUMERIC_SIGNED)
66+
assert not is_valid_euring_type("-0", TYPE_NUMERIC_SIGNED)
67+
assert not is_valid_euring_type("abc", TYPE_ALPHABETIC)
68+
assert is_valid_euring_type("ABC", "Unknown") is False

0 commit comments

Comments
 (0)