sfeos-helpers 6.7.4__tar.gz → 6.7.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/PKG-INFO +3 -5
  2. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/pyproject.toml +2 -4
  3. sfeos_helpers-6.7.6/stac_fastapi/sfeos_helpers/database/datetime.py +188 -0
  4. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/mappings.py +1 -1
  5. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/version.py +1 -1
  6. sfeos_helpers-6.7.4/stac_fastapi/sfeos_helpers/database/datetime.py +0 -121
  7. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/.gitignore +0 -0
  8. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/README.md +0 -0
  9. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/aggregation/README.md +0 -0
  10. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/aggregation/__init__.py +0 -0
  11. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/aggregation/client.py +0 -0
  12. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/aggregation/format.py +0 -0
  13. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/database/README.md +0 -0
  14. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/database/__init__.py +0 -0
  15. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/database/document.py +0 -0
  16. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/database/index.py +0 -0
  17. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/database/mapping.py +0 -0
  18. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/database/query.py +0 -0
  19. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/database/utils.py +0 -0
  20. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/filter/README.md +0 -0
  21. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/filter/__init__.py +0 -0
  22. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/filter/client.py +0 -0
  23. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/filter/cql2.py +0 -0
  24. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/filter/transform.py +0 -0
  25. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/models/patch.py +0 -0
  26. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/search_engine/__init__.py +0 -0
  27. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/search_engine/base.py +0 -0
  28. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/search_engine/factory.py +0 -0
  29. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/search_engine/index_operations.py +0 -0
  30. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/search_engine/inserters.py +0 -0
  31. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/search_engine/managers.py +0 -0
  32. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/search_engine/selection/__init__.py +0 -0
  33. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/search_engine/selection/base.py +0 -0
  34. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/search_engine/selection/cache_manager.py +0 -0
  35. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/search_engine/selection/factory.py +0 -0
  36. {sfeos_helpers-6.7.4 → sfeos_helpers-6.7.6}/stac_fastapi/sfeos_helpers/search_engine/selection/selectors.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sfeos_helpers
3
- Version: 6.7.4
3
+ Version: 6.7.6
4
4
  Summary: Helper library for the Elasticsearch and Opensearch stac-fastapi backends.
5
5
  Project-URL: Homepage, https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch
6
6
  License: MIT
@@ -9,14 +9,12 @@ Classifier: Intended Audience :: Developers
9
9
  Classifier: Intended Audience :: Information Technology
10
10
  Classifier: Intended Audience :: Science/Research
11
11
  Classifier: License :: OSI Approved :: MIT License
12
- Classifier: Programming Language :: Python :: 3.9
13
- Classifier: Programming Language :: Python :: 3.10
14
12
  Classifier: Programming Language :: Python :: 3.11
15
13
  Classifier: Programming Language :: Python :: 3.12
16
14
  Classifier: Programming Language :: Python :: 3.13
17
15
  Classifier: Programming Language :: Python :: 3.14
18
- Requires-Python: >=3.9
19
- Requires-Dist: stac-fastapi-core==6.7.4
16
+ Requires-Python: >=3.11
17
+ Requires-Dist: stac-fastapi-core==6.7.6
20
18
  Description-Content-Type: text/markdown
21
19
 
22
20
  # sfeos-helpers
@@ -6,15 +6,13 @@ build-backend = "hatchling.build"
6
6
  name = "sfeos_helpers"
7
7
  description = "Helper library for the Elasticsearch and Opensearch stac-fastapi backends."
8
8
  readme = "README.md"
9
- requires-python = ">=3.9"
9
+ requires-python = ">=3.11"
10
10
  license = {text = "MIT"}
11
11
  authors = []
12
12
  classifiers = [
13
13
  "Intended Audience :: Developers",
14
14
  "Intended Audience :: Information Technology",
15
15
  "Intended Audience :: Science/Research",
16
- "Programming Language :: Python :: 3.9",
17
- "Programming Language :: Python :: 3.10",
18
16
  "Programming Language :: Python :: 3.11",
19
17
  "Programming Language :: Python :: 3.12",
20
18
  "Programming Language :: Python :: 3.13",
@@ -31,7 +29,7 @@ keywords = [
31
29
  ]
32
30
  dynamic = ["version"]
33
31
  dependencies = [
34
- "stac-fastapi.core==6.7.4",
32
+ "stac-fastapi.core==6.7.6",
35
33
  ]
36
34
 
37
35
  [project.urls]
@@ -0,0 +1,188 @@
1
+ """Elasticsearch/OpenSearch-specific datetime utilities.
2
+
3
+ This module provides datetime utility functions specifically designed for
4
+ Elasticsearch and OpenSearch query formatting.
5
+ """
6
+
7
+ import logging
8
+ import re
9
+ from datetime import date
10
+ from datetime import datetime as datetime_type
11
+ from datetime import timezone
12
+ from typing import Dict, Optional, Union
13
+
14
+ from stac_fastapi.core.utilities import get_bool_env
15
+ from stac_fastapi.types.rfc3339 import DateTimeType
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ def return_date(
21
+ interval: Optional[Union[DateTimeType, str]],
22
+ ) -> Dict[str, Optional[str]]:
23
+ """
24
+ Convert a date interval to an Elasticsearch/OpenSearch query format.
25
+
26
+ This function converts a date interval (which may be a datetime, a tuple of one or two datetimes,
27
+ a string representing a datetime or range, or None) into a dictionary for filtering
28
+ search results with Elasticsearch/OpenSearch.
29
+
30
+ This function ensures the output dictionary contains 'gte' and 'lte' keys,
31
+ even if they are set to None, to prevent KeyError in the consuming logic.
32
+
33
+ Args:
34
+ interval (Optional[Union[DateTimeType, str]]): The date interval, which might be a single datetime,
35
+ a tuple with one or two datetimes, a string, or None.
36
+
37
+ Returns:
38
+ dict: A dictionary representing the date interval for use in filtering search results,
39
+ always containing 'gte' and 'lte' keys.
40
+ """
41
+ result: Dict[str, Optional[str]] = {"gte": None, "lte": None}
42
+ use_datetime_nanos = get_bool_env("USE_DATETIME_NANOS", default=True)
43
+ if interval is None:
44
+ return result
45
+
46
+ if use_datetime_nanos:
47
+ MIN_DATE_NANOS = datetime_type(1970, 1, 1, tzinfo=timezone.utc)
48
+ MAX_DATE_NANOS = datetime_type(
49
+ 2262, 4, 11, 23, 47, 16, 854775, tzinfo=timezone.utc
50
+ )
51
+
52
+ if isinstance(interval, str):
53
+ if "/" in interval:
54
+ parts = interval.split("/")
55
+ result["gte"] = (
56
+ parts[0] if parts[0] != ".." else MIN_DATE_NANOS.isoformat()
57
+ )
58
+ result["lte"] = (
59
+ parts[1]
60
+ if len(parts) > 1 and parts[1] != ".."
61
+ else MAX_DATE_NANOS.isoformat()
62
+ )
63
+ else:
64
+ converted_time = interval if interval != ".." else None
65
+ result["gte"] = result["lte"] = converted_time
66
+ return result
67
+
68
+ if isinstance(interval, datetime_type):
69
+ dt_utc = (
70
+ interval.astimezone(timezone.utc)
71
+ if interval.tzinfo
72
+ else interval.replace(tzinfo=timezone.utc)
73
+ )
74
+ if dt_utc < MIN_DATE_NANOS:
75
+ dt_utc = MIN_DATE_NANOS
76
+ elif dt_utc > MAX_DATE_NANOS:
77
+ dt_utc = MAX_DATE_NANOS
78
+ datetime_iso = dt_utc.isoformat().replace("+00:00", "Z")
79
+ result["gte"] = result["lte"] = datetime_iso
80
+ elif isinstance(interval, tuple):
81
+ start, end = interval
82
+ # Ensure datetimes are converted to UTC and formatted with 'Z'
83
+ if start:
84
+ start_utc = (
85
+ start.astimezone(timezone.utc)
86
+ if start.tzinfo
87
+ else start.replace(tzinfo=timezone.utc)
88
+ )
89
+ if start_utc < MIN_DATE_NANOS:
90
+ start_utc = MIN_DATE_NANOS
91
+ elif start_utc > MAX_DATE_NANOS:
92
+ start_utc = MAX_DATE_NANOS
93
+ result["gte"] = start_utc.isoformat().replace("+00:00", "Z")
94
+ if end:
95
+ end_utc = (
96
+ end.astimezone(timezone.utc)
97
+ if end.tzinfo
98
+ else end.replace(tzinfo=timezone.utc)
99
+ )
100
+ if end_utc < MIN_DATE_NANOS:
101
+ end_utc = MIN_DATE_NANOS
102
+ elif end_utc > MAX_DATE_NANOS:
103
+ end_utc = MAX_DATE_NANOS
104
+ result["lte"] = end_utc.isoformat().replace("+00:00", "Z")
105
+
106
+ return result
107
+
108
+ else:
109
+ if isinstance(interval, str):
110
+ if "/" in interval:
111
+ parts = interval.split("/")
112
+ result["gte"] = (
113
+ parts[0]
114
+ if parts[0] != ".."
115
+ else datetime_type.min.isoformat() + "Z"
116
+ )
117
+ result["lte"] = (
118
+ parts[1]
119
+ if len(parts) > 1 and parts[1] != ".."
120
+ else datetime_type.max.isoformat() + "Z"
121
+ )
122
+ else:
123
+ converted_time = interval if interval != ".." else None
124
+ result["gte"] = result["lte"] = converted_time
125
+ return result
126
+
127
+ if isinstance(interval, datetime_type):
128
+ datetime_iso = interval.isoformat()
129
+ result["gte"] = result["lte"] = datetime_iso
130
+ elif isinstance(interval, tuple):
131
+ start, end = interval
132
+ # Ensure datetimes are converted to UTC and formatted with 'Z'
133
+ if start:
134
+ result["gte"] = start.isoformat().replace("+00:00", "Z")
135
+ if end:
136
+ result["lte"] = end.isoformat().replace("+00:00", "Z")
137
+
138
+ return result
139
+
140
+
141
+ def extract_date(date_str: str) -> date:
142
+ """Extract date from ISO format string.
143
+
144
+ Args:
145
+ date_str: ISO format date string
146
+
147
+ Returns:
148
+ A date object extracted from the input string.
149
+ """
150
+ date_str = date_str.replace("Z", "+00:00")
151
+ return datetime_type.fromisoformat(date_str).date()
152
+
153
+
154
+ def extract_first_date_from_index(index_name: str) -> date:
155
+ """Extract the first date from an index name containing date patterns.
156
+
157
+ Searches for date patterns (YYYY-MM-DD) within the index name string
158
+ and returns the first found date as a date object.
159
+
160
+ Args:
161
+ index_name: Index name containing date patterns.
162
+
163
+ Returns:
164
+ A date object extracted from the first date pattern found in the index name.
165
+
166
+ """
167
+ date_pattern = r"\d{4}-\d{2}-\d{2}"
168
+ match = re.search(date_pattern, index_name)
169
+
170
+ if not match:
171
+ logger.error(f"No date pattern found in index name: '{index_name}'")
172
+ raise ValueError(
173
+ f"No date pattern (YYYY-MM-DD) found in index name: '{index_name}'"
174
+ )
175
+
176
+ date_string = match.group(0)
177
+
178
+ try:
179
+ extracted_date = datetime_type.strptime(date_string, "%Y-%m-%d").date()
180
+ return extracted_date
181
+ except ValueError as e:
182
+ logger.error(
183
+ f"Invalid date format found in index name '{index_name}': "
184
+ f"'{date_string}' - {str(e)}"
185
+ )
186
+ raise ValueError(
187
+ f"Invalid date format in index name '{index_name}': '{date_string}'"
188
+ ) from e
@@ -142,7 +142,7 @@ ES_ITEMS_MAPPINGS = {
142
142
  "type": "object",
143
143
  "properties": {
144
144
  # Common https://github.com/radiantearth/stac-spec/blob/master/item-spec/common-metadata.md
145
- "datetime": {"type": "date"},
145
+ "datetime": {"type": "date_nanos"},
146
146
  "start_datetime": {"type": "date"},
147
147
  "end_datetime": {"type": "date"},
148
148
  "created": {"type": "date"},
@@ -1,2 +1,2 @@
1
1
  """library version."""
2
- __version__ = "6.7.4"
2
+ __version__ = "6.7.6"
@@ -1,121 +0,0 @@
1
- """Elasticsearch/OpenSearch-specific datetime utilities.
2
-
3
- This module provides datetime utility functions specifically designed for
4
- Elasticsearch and OpenSearch query formatting.
5
- """
6
-
7
- import logging
8
- import re
9
- from datetime import date
10
- from datetime import datetime as datetime_type
11
- from typing import Dict, Optional, Union
12
-
13
- from stac_fastapi.types.rfc3339 import DateTimeType
14
-
15
- logger = logging.getLogger(__name__)
16
-
17
-
18
- def return_date(
19
- interval: Optional[Union[DateTimeType, str]],
20
- ) -> Dict[str, Optional[str]]:
21
- """
22
- Convert a date interval to an Elasticsearch/OpenSearch query format.
23
-
24
- This function converts a date interval (which may be a datetime, a tuple of one or two datetimes,
25
- a string representing a datetime or range, or None) into a dictionary for filtering
26
- search results with Elasticsearch/OpenSearch.
27
-
28
- This function ensures the output dictionary contains 'gte' and 'lte' keys,
29
- even if they are set to None, to prevent KeyError in the consuming logic.
30
-
31
- Args:
32
- interval (Optional[Union[DateTimeType, str]]): The date interval, which might be a single datetime,
33
- a tuple with one or two datetimes, a string, or None.
34
-
35
- Returns:
36
- dict: A dictionary representing the date interval for use in filtering search results,
37
- always containing 'gte' and 'lte' keys.
38
- """
39
- result: Dict[str, Optional[str]] = {"gte": None, "lte": None}
40
-
41
- if interval is None:
42
- return result
43
-
44
- if isinstance(interval, str):
45
- if "/" in interval:
46
- parts = interval.split("/")
47
- result["gte"] = (
48
- parts[0] if parts[0] != ".." else datetime_type.min.isoformat() + "Z"
49
- )
50
- result["lte"] = (
51
- parts[1]
52
- if len(parts) > 1 and parts[1] != ".."
53
- else datetime_type.max.isoformat() + "Z"
54
- )
55
- else:
56
- converted_time = interval if interval != ".." else None
57
- result["gte"] = result["lte"] = converted_time
58
- return result
59
-
60
- if isinstance(interval, datetime_type):
61
- datetime_iso = interval.isoformat()
62
- result["gte"] = result["lte"] = datetime_iso
63
- elif isinstance(interval, tuple):
64
- start, end = interval
65
- # Ensure datetimes are converted to UTC and formatted with 'Z'
66
- if start:
67
- result["gte"] = start.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
68
- if end:
69
- result["lte"] = end.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
70
-
71
- return result
72
-
73
-
74
- def extract_date(date_str: str) -> date:
75
- """Extract date from ISO format string.
76
-
77
- Args:
78
- date_str: ISO format date string
79
-
80
- Returns:
81
- A date object extracted from the input string.
82
- """
83
- date_str = date_str.replace("Z", "+00:00")
84
- return datetime_type.fromisoformat(date_str).date()
85
-
86
-
87
- def extract_first_date_from_index(index_name: str) -> date:
88
- """Extract the first date from an index name containing date patterns.
89
-
90
- Searches for date patterns (YYYY-MM-DD) within the index name string
91
- and returns the first found date as a date object.
92
-
93
- Args:
94
- index_name: Index name containing date patterns.
95
-
96
- Returns:
97
- A date object extracted from the first date pattern found in the index name.
98
-
99
- """
100
- date_pattern = r"\d{4}-\d{2}-\d{2}"
101
- match = re.search(date_pattern, index_name)
102
-
103
- if not match:
104
- logger.error(f"No date pattern found in index name: '{index_name}'")
105
- raise ValueError(
106
- f"No date pattern (YYYY-MM-DD) found in index name: '{index_name}'"
107
- )
108
-
109
- date_string = match.group(0)
110
-
111
- try:
112
- extracted_date = datetime_type.strptime(date_string, "%Y-%m-%d").date()
113
- return extracted_date
114
- except ValueError as e:
115
- logger.error(
116
- f"Invalid date format found in index name '{index_name}': "
117
- f"'{date_string}' - {str(e)}"
118
- )
119
- raise ValueError(
120
- f"Invalid date format in index name '{index_name}': '{date_string}'"
121
- ) from e
File without changes
File without changes