Skip to content

Commit 6ff8d2a

Browse files
committed
Add summaries for Timestamps Extension
1 parent 5b06a41 commit 6ff8d2a

File tree

2 files changed

+193
-0
lines changed

2 files changed

+193
-0
lines changed

pystac/extensions/timestamps.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
"""
55

66
from datetime import datetime as Datetime
7+
from pystac.summaries import RangeSummary
78
from typing import Dict, Any, Generic, Iterable, Optional, Set, TypeVar, cast
89

910
import pystac
1011
from pystac.extensions.base import (
1112
ExtensionManagementMixin,
1213
PropertiesExtension,
14+
SummariesExtension,
1315
)
1416
from pystac.extensions.hooks import ExtensionHooks
1517
from pystac.utils import datetime_to_str, map_opt, str_to_datetime
@@ -139,6 +141,11 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> "TimestampsExtension[T]":
139141
f"Timestamps extension does not apply to type '{type(obj).__name__}'"
140142
)
141143

144+
@staticmethod
145+
def summaries(obj: pystac.Collection) -> "SummariesTimestampsExtension":
146+
"""Returns the extended summaries object for the given collection."""
147+
return SummariesTimestampsExtension(obj)
148+
142149

143150
class ItemTimestampsExtension(TimestampsExtension[pystac.Item]):
144151
"""A concrete implementation of :class:`TimestampsExtension` on an
@@ -192,6 +199,88 @@ def __repr__(self) -> str:
192199
return "<AssetTimestampsExtension Asset href={}>".format(self.asset_href)
193200

194201

202+
class SummariesTimestampsExtension(SummariesExtension):
203+
"""A concrete implementation of :class:`~SummariesExtension` that extends
204+
the ``summaries`` field of a :class:`~pystac.Collection` to include properties
205+
defined in the :stac-ext:`Timestamps Extension <timestamps>`.
206+
"""
207+
208+
@property
209+
def published(self) -> Optional[RangeSummary[Datetime]]:
210+
"""Get or sets the summary of :attr:`TimestampsExtension.published` values
211+
for this Collection.
212+
"""
213+
214+
return map_opt(
215+
lambda s: RangeSummary(
216+
str_to_datetime(s.minimum), str_to_datetime(s.maximum)
217+
),
218+
self.summaries.get_range(PUBLISHED_PROP),
219+
)
220+
221+
@published.setter
222+
def published(self, v: Optional[RangeSummary[Datetime]]) -> None:
223+
self._set_summary(
224+
PUBLISHED_PROP,
225+
map_opt(
226+
lambda s: RangeSummary(
227+
datetime_to_str(s.minimum), datetime_to_str(s.maximum)
228+
),
229+
v,
230+
),
231+
)
232+
233+
@property
234+
def expires(self) -> Optional[RangeSummary[Datetime]]:
235+
"""Get or sets the summary of :attr:`TimestampsExtension.expires` values
236+
for this Collection.
237+
"""
238+
239+
return map_opt(
240+
lambda s: RangeSummary(
241+
str_to_datetime(s.minimum), str_to_datetime(s.maximum)
242+
),
243+
self.summaries.get_range(EXPIRES_PROP),
244+
)
245+
246+
@expires.setter
247+
def expires(self, v: Optional[RangeSummary[Datetime]]) -> None:
248+
self._set_summary(
249+
EXPIRES_PROP,
250+
map_opt(
251+
lambda s: RangeSummary(
252+
datetime_to_str(s.minimum), datetime_to_str(s.maximum)
253+
),
254+
v,
255+
),
256+
)
257+
258+
@property
259+
def unpublished(self) -> Optional[RangeSummary[Datetime]]:
260+
"""Get or sets the summary of :attr:`TimestampsExtension.unpublished` values
261+
for this Collection.
262+
"""
263+
264+
return map_opt(
265+
lambda s: RangeSummary(
266+
str_to_datetime(s.minimum), str_to_datetime(s.maximum)
267+
),
268+
self.summaries.get_range(UNPUBLISHED_PROP),
269+
)
270+
271+
@unpublished.setter
272+
def unpublished(self, v: Optional[RangeSummary[Datetime]]) -> None:
273+
self._set_summary(
274+
UNPUBLISHED_PROP,
275+
map_opt(
276+
lambda s: RangeSummary(
277+
datetime_to_str(s.minimum), datetime_to_str(s.maximum)
278+
),
279+
v,
280+
),
281+
)
282+
283+
195284
class TimestampsExtensionHooks(ExtensionHooks):
196285
schema_uri: str = SCHEMA_URI
197286
prev_extension_ids: Set[str] = set(["timestamps"])

tests/extensions/test_timestamps.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import pystac
66
from pystac import ExtensionTypeError
7+
from pystac.summaries import RangeSummary
78
from pystac.extensions.timestamps import TimestampsExtension
89
from pystac.utils import get_opt, str_to_datetime, datetime_to_str
910
from tests.utils import TestCases, assert_to_from_dict
@@ -233,3 +234,106 @@ def test_should_raise_exception_when_passing_invalid_extension_object(
233234
TimestampsExtension.ext,
234235
object(),
235236
)
237+
238+
def test_item_repr(self) -> None:
239+
item = pystac.Item.from_file(self.example_uri)
240+
241+
self.assertEqual(
242+
TimestampsExtension.ext(item).__repr__(),
243+
f"<ItemTimestampsExtension Item id={item.id}>",
244+
)
245+
246+
def test_asset_repr(self) -> None:
247+
item = pystac.Item.from_file(self.example_uri)
248+
asset = item.assets["blue"]
249+
250+
self.assertEqual(
251+
TimestampsExtension.ext(asset).__repr__(),
252+
f"<AssetTimestampsExtension Asset href={asset.href}>",
253+
)
254+
255+
256+
class TimestampsSummariesTest(unittest.TestCase):
257+
def setUp(self) -> None:
258+
self.maxDiff = None
259+
260+
@staticmethod
261+
def collection() -> pystac.Collection:
262+
return pystac.Collection.from_file(
263+
TestCases.get_path("data-files/collections/multi-extent.json")
264+
)
265+
266+
def test_published(self) -> None:
267+
collection = self.collection()
268+
summaries_ext = TimestampsExtension.summaries(collection)
269+
published_range = RangeSummary(
270+
str_to_datetime("2020-01-01T00:00:00.000Z"),
271+
str_to_datetime("2020-01-02T00:00:00.000Z"),
272+
)
273+
274+
summaries_ext.published = published_range
275+
276+
self.assertEqual(
277+
summaries_ext.published,
278+
published_range,
279+
)
280+
281+
summaries_dict = collection.to_dict()["summaries"]
282+
283+
self.assertDictEqual(
284+
summaries_dict["published"],
285+
{
286+
"minimum": datetime_to_str(published_range.minimum),
287+
"maximum": datetime_to_str(published_range.maximum),
288+
},
289+
)
290+
291+
def test_expires(self) -> None:
292+
collection = self.collection()
293+
summaries_ext = TimestampsExtension.summaries(collection)
294+
expires_range = RangeSummary(
295+
str_to_datetime("2020-01-01T00:00:00.000Z"),
296+
str_to_datetime("2020-01-02T00:00:00.000Z"),
297+
)
298+
299+
summaries_ext.expires = expires_range
300+
301+
self.assertEqual(
302+
summaries_ext.expires,
303+
expires_range,
304+
)
305+
306+
summaries_dict = collection.to_dict()["summaries"]
307+
308+
self.assertDictEqual(
309+
summaries_dict["expires"],
310+
{
311+
"minimum": datetime_to_str(expires_range.minimum),
312+
"maximum": datetime_to_str(expires_range.maximum),
313+
},
314+
)
315+
316+
def test_unpublished(self) -> None:
317+
collection = self.collection()
318+
summaries_ext = TimestampsExtension.summaries(collection)
319+
unpublished_range = RangeSummary(
320+
str_to_datetime("2020-01-01T00:00:00.000Z"),
321+
str_to_datetime("2020-01-02T00:00:00.000Z"),
322+
)
323+
324+
summaries_ext.unpublished = unpublished_range
325+
326+
self.assertEqual(
327+
summaries_ext.unpublished,
328+
unpublished_range,
329+
)
330+
331+
summaries_dict = collection.to_dict()["summaries"]
332+
333+
self.assertDictEqual(
334+
summaries_dict["unpublished"],
335+
{
336+
"minimum": datetime_to_str(unpublished_range.minimum),
337+
"maximum": datetime_to_str(unpublished_range.maximum),
338+
},
339+
)

0 commit comments

Comments
 (0)