Skip to content

Commit 8880aef

Browse files
Merge pull request #24 from KNMI/data-type
Add support for Parameter.`data-type`
2 parents df75890 + 23823dc commit 8880aef

File tree

8 files changed

+64
-7
lines changed

8 files changed

+64
-7
lines changed

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,13 @@ c = Collection(
8686
observedProperty=ObservedProperty(
8787
id="https://codes.wmo.int/common/quantity-kind/_windDirection",
8888
label="Wind Direction"
89-
)
89+
),
90+
dataType="integer"
9091
)
9192
})
9293
)
9394

94-
print(c.model_dump_json(indent=2, exclude_none=True))
95+
print(c.model_dump_json(indent=2, exclude_none=True, by_alias=True))
9596
```
9697

9798
Will print
@@ -136,6 +137,7 @@ Will print
136137
"parameter_names": {
137138
"Wind Direction": {
138139
"type": "Parameter",
140+
"data-type": "integer",
139141
"unit": {
140142
"label": "degree true"
141143
},
@@ -148,6 +150,9 @@ Will print
148150
}
149151
```
150152

153+
**IMPORTANT**: The arguments `by_alias=True` to `model_dump_json()` or `model_dump()` is required to get the output as shown above. Without `by_alias=True` the attribute `data-type` will be wrongly outputted as `dataType`. This is due an issue in the [EDR spec](https://github.com/opengeospatial/ogcapi-environmental-data-retrieval/issues/605) and [Pydantic](https://github.com/pydantic/pydantic/issues/8379).
154+
155+
151156
## Contributing
152157

153158
Make an editable install from within the repository root

example.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@
3333
observedProperty=ObservedProperty(
3434
id="https://codes.wmo.int/common/quantity-kind/_windDirection", label="Wind Direction"
3535
),
36+
dataType="integer",
3637
)
3738
}
3839
),
3940
)
4041

41-
print(c.model_dump_json(indent=2, exclude_none=True))
42+
print(c.model_dump_json(indent=2, exclude_none=True, by_alias=True))

src/edr_pydantic/base_model.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ class EdrBaseModel(PydanticBaseModel):
1010
validate_default=True,
1111
validate_assignment=True,
1212
strict=True,
13+
populate_by_name=True,
1314
)

src/edr_pydantic/parameter.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import Literal
44
from typing import Optional
55

6+
from pydantic import Field
67
from pydantic import model_validator
78
from pydantic import RootModel
89

@@ -23,6 +24,7 @@ class Parameter(EdrBaseModel, extra="allow"):
2324
id: Optional[str] = None
2425
label: Optional[str] = None
2526
description: Optional[str] = None
27+
dataType: Optional[Literal["integer", "float", "string"]] = Field(None, alias="data-type") # noqa: N815
2628
unit: Optional[Unit] = None
2729
observedProperty: ObservedProperty # noqa: N815
2830
extent: Optional[Extent] = None

tests/test_data/knmi-example-collections.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@
449449
"tx_dryb_10": {
450450
"type": "Parameter",
451451
"description": "Temperature, air, maximum, 10'",
452+
"data-type": "float",
452453
"unit": {
453454
"label": "degree Celsius",
454455
"symbol": {
@@ -479,6 +480,7 @@
479480
"ww_cor_10": {
480481
"type": "Parameter",
481482
"description": "Weather, validated code, present weather sensor, 10'",
483+
"data-type": "integer",
482484
"unit": {
483485
"label": "weather code",
484486
"symbol": {
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "Parameter",
3+
"data-type": "foobar",
4+
"observedProperty": {
5+
"label": "invalid parameter"
6+
}
7+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"str-parameter": {
3+
"type": "Parameter",
4+
"data-type": "string",
5+
"observedProperty": {
6+
"label": "string parameter"
7+
}
8+
},
9+
"int-parameter": {
10+
"type": "Parameter",
11+
"data-type": "integer",
12+
"observedProperty": {
13+
"label": "int parameter"
14+
}
15+
},
16+
"float-parameter": {
17+
"type": "Parameter",
18+
"data-type": "float",
19+
"observedProperty": {
20+
"label": "float parameter"
21+
}
22+
}
23+
}

tests/test_edr.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import json
22
from pathlib import Path
3-
from typing import Dict
43

54
import pytest
65
from edr_pydantic.capabilities import LandingPageModel
76
from edr_pydantic.collections import Collections
87
from edr_pydantic.collections import Instance
98
from edr_pydantic.extent import Extent
109
from edr_pydantic.extent import Temporal
10+
from edr_pydantic.observed_property import ObservedProperty
1111
from edr_pydantic.parameter import Parameter
12+
from edr_pydantic.parameter import Parameters
1213
from edr_pydantic.unit import Unit
13-
from pydantic import RootModel
1414
from pydantic import ValidationError
1515

1616
happy_cases = [
@@ -19,7 +19,8 @@
1919
("simple-instance.json", Instance),
2020
("landing-page.json", LandingPageModel),
2121
("doc-example-extent.json", Extent),
22-
("parameter-names.json", RootModel[Dict[str, Parameter]]),
22+
("parameter-names.json", Parameters),
23+
("parameter-with-data-type.json", Parameters),
2324
("parameter-with-extent.json", Parameter),
2425
]
2526

@@ -33,13 +34,14 @@ def test_happy_cases(file_name, object_type):
3334
json_string = json.dumps(data, separators=(",", ":"), ensure_ascii=False)
3435

3536
# Round-trip
36-
assert object_type.model_validate_json(json_string).model_dump_json(exclude_none=True) == json_string
37+
assert object_type.model_validate_json(json_string).model_dump_json(exclude_none=True, by_alias=True) == json_string
3738

3839

3940
error_cases = [
4041
("label-or-symbol-unit.json", Unit, r"Either 'label' or 'symbol' should be set"),
4142
("temporal-interval-length1.json", Temporal, r"List should have at least 2 items after validation"),
4243
("temporal-interval-length3.json", Temporal, r"List should have at most 2 items after validation, not 3"),
44+
("parameter-invalid-data-type.json", Parameter, r"Input should be 'integer', 'float' or 'string'"),
4345
]
4446

4547

@@ -53,3 +55,17 @@ def test_error_cases(file_name, object_type, error_message):
5355

5456
with pytest.raises(ValidationError, match=error_message):
5557
object_type.model_validate_json(json_string)
58+
59+
60+
def test_data_type_alias():
61+
p = Parameter(observedProperty=ObservedProperty(label="Wind"), dataType="integer")
62+
63+
# This tests for the current observed Pydantic behavior with model_dump() and the `by_alias` setting
64+
assert (
65+
p.model_dump_json(exclude_none=True)
66+
== '{"type":"Parameter","dataType":"integer","observedProperty":{"label":"Wind"}}'
67+
)
68+
assert (
69+
p.model_dump_json(exclude_none=True, by_alias=True)
70+
== '{"type":"Parameter","data-type":"integer","observedProperty":{"label":"Wind"}}'
71+
)

0 commit comments

Comments
 (0)