1- """Item crud client."""
1+ """Core client."""
22import logging
33import re
44from datetime import datetime as datetime_type
1515from pygeofilter .backends .cql2_json import to_cql2
1616from pygeofilter .parsers .cql2_text import parse as parse_cql2_text
1717from stac_pydantic .links import Relations
18- from stac_pydantic .shared import MimeTypes
18+ from stac_pydantic .shared import BBox , MimeTypes
1919from stac_pydantic .version import STAC_VERSION
2020
2121from stac_fastapi .core .base_database_logic import BaseDatabaseLogic
3838from stac_fastapi .types .conformance import BASE_CONFORMANCE_CLASSES
3939from stac_fastapi .types .extension import ApiExtension
4040from stac_fastapi .types .requests import get_base_url
41+ from stac_fastapi .types .rfc3339 import DateTimeType
4142from stac_fastapi .types .search import BaseSearchPostRequest
4243from stac_fastapi .types .stac import Collection , Collections , Item , ItemCollection
4344
@@ -244,8 +245,8 @@ async def get_collection(self, collection_id: str, **kwargs) -> Collection:
244245 async def item_collection (
245246 self ,
246247 collection_id : str ,
247- bbox : Optional [List [ NumType ] ] = None ,
248- datetime : Union [ str , datetime_type , None ] = None ,
248+ bbox : Optional [BBox ] = None ,
249+ datetime : Optional [ DateTimeType ] = None ,
249250 limit : int = 10 ,
250251 token : str = None ,
251252 ** kwargs ,
@@ -254,8 +255,8 @@ async def item_collection(
254255
255256 Args:
256257 collection_id (str): The identifier of the collection to read items from.
257- bbox (Optional[List[NumType] ]): The bounding box to filter items by.
258- datetime (Union[str, datetime_type, None ]): The datetime range to filter items by.
258+ bbox (Optional[BBox ]): The bounding box to filter items by.
259+ datetime (Optional[DateTimeType ]): The datetime range to filter items by.
259260 limit (int): The maximum number of items to return. The default value is 10.
260261 token (str): A token used for pagination.
261262 request (Request): The incoming request.
@@ -349,53 +350,64 @@ async def get_item(self, item_id: str, collection_id: str, **kwargs) -> Item:
349350 return self .item_serializer .db_to_stac (item , base_url )
350351
351352 @staticmethod
352- def _return_date (interval_str ):
353+ def _return_date (
354+ interval : Optional [Union [DateTimeType , str ]]
355+ ) -> Dict [str , Optional [str ]]:
353356 """
354- Convert a date interval string into a dictionary for filtering search results .
357+ Convert a date interval.
355358
356- The date interval string should be formatted as either a single date or a range of dates separated
357- by "/". The date format should be ISO-8601 (YYYY-MM-DDTHH:MM:SSZ). If the interval string is a
358- single date, it will be converted to a dictionary with a single "eq" key whose value is the date in
359- the ISO-8601 format. If the interval string is a range of dates, it will be converted to a
360- dictionary with "gte" (greater than or equal to) and "lte" (less than or equal to) keys. If the
361- interval string is a range of dates with ".." instead of "/", the start and end dates will be
362- assigned default values to encompass the entire possible date range.
359+ (which may be a datetime, a tuple of one or two datetimes a string
360+ representing a datetime or range, or None) into a dictionary for filtering
361+ search results with Elasticsearch.
362+
363+ This function ensures the output dictionary contains 'gte' and 'lte' keys,
364+ even if they are set to None, to prevent KeyError in the consuming logic.
363365
364366 Args:
365- interval_str (str): The date interval string to be converted.
367+ interval (Optional[Union[DateTimeType, str]]): The date interval, which might be a single datetime,
368+ a tuple with one or two datetimes, a string, or None.
366369
367370 Returns:
368- dict: A dictionary representing the date interval for use in filtering search results.
371+ dict: A dictionary representing the date interval for use in filtering search results,
372+ always containing 'gte' and 'lte' keys.
369373 """
370- intervals = interval_str .split ("/" )
371- if len (intervals ) == 1 :
372- datetime = f"{ intervals [0 ][0 :19 ]} Z"
373- return {"eq" : datetime }
374- else :
375- start_date = intervals [0 ]
376- end_date = intervals [1 ]
377- if ".." not in intervals :
378- start_date = f"{ start_date [0 :19 ]} Z"
379- end_date = f"{ end_date [0 :19 ]} Z"
380- elif start_date != ".." :
381- start_date = f"{ start_date [0 :19 ]} Z"
382- end_date = "2200-12-01T12:31:12Z"
383- elif end_date != ".." :
384- start_date = "1900-10-01T00:00:00Z"
385- end_date = f"{ end_date [0 :19 ]} Z"
386- else :
387- start_date = "1900-10-01T00:00:00Z"
388- end_date = "2200-12-01T12:31:12Z"
374+ result : Dict [str , Optional [str ]] = {"gte" : None , "lte" : None }
389375
390- return {"lte" : end_date , "gte" : start_date }
376+ if interval is None :
377+ return result
378+
379+ if isinstance (interval , str ):
380+ if "/" in interval :
381+ parts = interval .split ("/" )
382+ result ["gte" ] = parts [0 ] if parts [0 ] != ".." else None
383+ result ["lte" ] = (
384+ parts [1 ] if len (parts ) > 1 and parts [1 ] != ".." else None
385+ )
386+ else :
387+ converted_time = interval if interval != ".." else None
388+ result ["gte" ] = result ["lte" ] = converted_time
389+ return result
390+
391+ if isinstance (interval , datetime_type ):
392+ datetime_iso = interval .isoformat ()
393+ result ["gte" ] = result ["lte" ] = datetime_iso
394+ elif isinstance (interval , tuple ):
395+ start , end = interval
396+ # Ensure datetimes are converted to UTC and formatted with 'Z'
397+ if start :
398+ result ["gte" ] = start .strftime ("%Y-%m-%dT%H:%M:%S.%f" )[:- 3 ] + "Z"
399+ if end :
400+ result ["lte" ] = end .strftime ("%Y-%m-%dT%H:%M:%S.%f" )[:- 3 ] + "Z"
401+
402+ return result
391403
392404 async def get_search (
393405 self ,
394406 request : Request ,
395407 collections : Optional [List [str ]] = None ,
396408 ids : Optional [List [str ]] = None ,
397- bbox : Optional [List [ NumType ] ] = None ,
398- datetime : Optional [Union [ str , datetime_type ] ] = None ,
409+ bbox : Optional [BBox ] = None ,
410+ datetime : Optional [DateTimeType ] = None ,
399411 limit : Optional [int ] = 10 ,
400412 query : Optional [str ] = None ,
401413 token : Optional [str ] = None ,
@@ -411,8 +423,8 @@ async def get_search(
411423 Args:
412424 collections (Optional[List[str]]): List of collection IDs to search in.
413425 ids (Optional[List[str]]): List of item IDs to search for.
414- bbox (Optional[List[NumType] ]): Bounding box to search in.
415- datetime (Optional[Union[str, datetime_type] ]): Filter items based on the datetime field.
426+ bbox (Optional[BBox ]): Bounding box to search in.
427+ datetime (Optional[DateTimeType ]): Filter items based on the datetime field.
416428 limit (Optional[int]): Maximum number of results to return.
417429 query (Optional[str]): Query string to filter the results.
418430 token (Optional[str]): Access token to use when searching the catalog.
@@ -459,7 +471,6 @@ async def get_search(
459471 "direction" : "desc" if sort [0 ] == "-" else "asc" ,
460472 }
461473 )
462- print (sort_param )
463474 base_args ["sortby" ] = sort_param
464475
465476 if filter :
0 commit comments