Skip to content

Commit f4426b4

Browse files
committed
Move datetime to StacCommonMetadata.
1 parent 2aa30eb commit f4426b4

File tree

3 files changed

+66
-20
lines changed

3 files changed

+66
-20
lines changed

stac_pydantic/item.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from geojson_pydantic import Feature
44
from pydantic import AnyUrl, ConfigDict, Field, model_serializer, model_validator
5-
from typing_extensions import Self
65

76
from stac_pydantic.links import Links
87
from stac_pydantic.shared import (
@@ -20,20 +19,12 @@ class ItemProperties(StacCommonMetadata):
2019
https://github.com/radiantearth/stac-spec/blob/v1.0.0/item-spec/item-spec.md#properties-object
2120
"""
2221

22+
# Overide the datetime field to be required
2323
datetime: Optional[UtcDatetime]
2424

2525
# Check https://docs.pydantic.dev/dev-v2/migration/#changes-to-config for more information.
2626
model_config = ConfigDict(extra="allow")
2727

28-
@model_validator(mode="after")
29-
def validate_datetime(self) -> Self:
30-
if not self.datetime and (not self.start_datetime or not self.end_datetime):
31-
raise ValueError(
32-
"start_datetime and end_datetime must be specified when datetime is null"
33-
)
34-
35-
return self
36-
3728

3829
class Item(Feature, StacBaseModel):
3930
"""

stac_pydantic/shared.py

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
BaseModel,
1010
ConfigDict,
1111
Field,
12+
model_validator,
1213
)
13-
from typing_extensions import Annotated
14+
from typing_extensions import Annotated, Self
1415

1516
from stac_pydantic.utils import AutoValueEnum
1617

@@ -126,22 +127,49 @@ class Provider(StacBaseModel):
126127

127128
class StacCommonMetadata(StacBaseModel):
128129
"""
129-
https://github.com/radiantearth/stac-spec/blob/v1.0.0/item-spec/common-metadata.md#date-and-time-range
130+
https://github.com/radiantearth/stac-spec/blob/v1.0.0/item-spec/common-metadata.md
130131
"""
131132

133+
# Basic
132134
title: Optional[str] = None
133135
description: Optional[str] = None
134-
start_datetime: Optional[UtcDatetime] = None
135-
end_datetime: Optional[UtcDatetime] = None
136+
# Date and Time
137+
datetime: Optional[UtcDatetime] = None
136138
created: Optional[UtcDatetime] = None
137139
updated: Optional[UtcDatetime] = None
140+
# Date and Time Range
141+
start_datetime: Optional[UtcDatetime] = None
142+
end_datetime: Optional[UtcDatetime] = None
143+
# Provider
144+
providers: Optional[List[Provider]] = None
145+
# Instrument
138146
platform: Optional[str] = None
139147
instruments: Optional[List[str]] = None
140148
constellation: Optional[str] = None
141149
mission: Optional[str] = None
142-
providers: Optional[List[Provider]] = None
143150
gsd: Optional[float] = Field(None, gt=0)
144151

152+
@model_validator(mode="after")
153+
def validate_datetime_or_start_end(self) -> Self:
154+
# When datetime is null, start_datetime and end_datetime must be specified
155+
if not self.datetime and (not self.start_datetime or not self.end_datetime):
156+
raise ValueError(
157+
"start_datetime and end_datetime must be specified when datetime is null"
158+
)
159+
160+
return self
161+
162+
@model_validator(mode="after")
163+
def validate_start_end(self) -> Self:
164+
# Using one of start_datetime or end_datetime requires the use of the other
165+
if (self.start_datetime and not self.end_datetime) or (
166+
not self.start_datetime and self.end_datetime
167+
):
168+
raise ValueError(
169+
"use of start_datetime or end_datetime requires the use of the other"
170+
)
171+
return self
172+
145173

146174
class Asset(StacCommonMetadata):
147175
"""
@@ -157,3 +185,10 @@ class Asset(StacCommonMetadata):
157185
model_config = ConfigDict(
158186
populate_by_name=True, use_enum_values=True, extra="allow"
159187
)
188+
189+
@model_validator(mode="after")
190+
def validate_datetime_or_start_end(self) -> Self:
191+
# Overriding the parent method to avoid requiring datetime or start/end_datetime
192+
# Additional fields MAY be added on the Asset object, but are not required.
193+
# https://github.com/radiantearth/stac-spec/blob/v1.0.0/item-spec/item-spec.md#additional-fields-for-assets
194+
return self

tests/test_models.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from stac_pydantic import Collection, Item, ItemProperties
99
from stac_pydantic.extensions import validate_extensions
1010
from stac_pydantic.links import Link, Links
11-
from stac_pydantic.shared import MimeTypes
11+
from stac_pydantic.shared import MimeTypes, StacCommonMetadata
1212

1313
from .conftest import dict_match, request
1414

@@ -169,10 +169,15 @@ def test_geo_interface() -> None:
169169
"start_datetime": "2024-01-01T00:00:00Z",
170170
"end_datetime": "2024-01-02T00:00:00Z",
171171
},
172+
{
173+
"datetime": "2024-01-01T00:00:00Z",
174+
"start_datetime": "2024-01-01T00:00:00Z",
175+
"end_datetime": "2024-01-02T00:00:00Z",
176+
},
172177
],
173178
)
174-
def test_item_properties_dates(args) -> None:
175-
ItemProperties(**args)
179+
def test_stac_common_dates(args) -> None:
180+
StacCommonMetadata(**args)
176181

177182

178183
@pytest.mark.parametrize(
@@ -183,12 +188,27 @@ def test_item_properties_dates(args) -> None:
183188
{"datetime": None, "end_datetime": "2024-01-01T00:00:00Z"},
184189
],
185190
)
186-
def test_item_properties_no_dates(args) -> None:
191+
def test_stac_common_no_dates(args) -> None:
187192
with pytest.raises(
188193
ValueError,
189194
match="start_datetime and end_datetime must be specified when datetime is null",
190195
):
191-
ItemProperties(**args)
196+
StacCommonMetadata(**args)
197+
198+
199+
@pytest.mark.parametrize(
200+
"args",
201+
[
202+
{"datetime": "2024-01-01T00:00:00Z", "start_datetime": "2024-01-01T00:00:00Z"},
203+
{"datetime": "2024-01-01T00:00:00Z", "end_datetime": "2024-01-01T00:00:00Z"},
204+
],
205+
)
206+
def test_stac_common_start_and_end(args) -> None:
207+
with pytest.raises(
208+
ValueError,
209+
match="use of start_datetime or end_datetime requires the use of the other",
210+
):
211+
StacCommonMetadata(**args)
192212

193213

194214
def test_declared_model() -> None:

0 commit comments

Comments
 (0)