|
1 | 1 | from datetime import datetime as dt |
2 | | -from typing import Any, Dict, List, Optional, Tuple, Union, cast |
| 2 | +from typing import Any, Dict, List, Optional, Union |
3 | 3 |
|
4 | 4 | from geojson_pydantic.geometries import ( |
5 | 5 | GeometryCollection, |
|
10 | 10 | Point, |
11 | 11 | Polygon, |
12 | 12 | ) |
13 | | -from pydantic import AfterValidator, BaseModel, Field, TypeAdapter, model_validator |
| 13 | +from pydantic import AfterValidator, BaseModel, Field, model_validator |
14 | 14 | from typing_extensions import Annotated |
15 | 15 |
|
16 | 16 | from stac_pydantic.api.extensions.fields import FieldsExtension |
17 | 17 | from stac_pydantic.api.extensions.query import Operator |
18 | 18 | from stac_pydantic.api.extensions.sort import SortExtension |
19 | | -from stac_pydantic.shared import BBox, UtcDatetime |
| 19 | +from stac_pydantic.shared import ( |
| 20 | + BBox, |
| 21 | + str_to_datetimes, |
| 22 | + validate_bbox, |
| 23 | + validate_datetime, |
| 24 | +) |
20 | 25 |
|
21 | 26 | Intersection = Union[ |
22 | 27 | Point, |
|
28 | 33 | GeometryCollection, |
29 | 34 | ] |
30 | 35 |
|
31 | | -SearchDatetime = TypeAdapter(Optional[UtcDatetime]) |
32 | | - |
33 | | - |
34 | | -def validate_bbox(v: Optional[BBox]) -> Optional[BBox]: |
35 | | - """Validate BBOX value.""" |
36 | | - if v: |
37 | | - # Validate order |
38 | | - if len(v) == 4: |
39 | | - xmin, ymin, xmax, ymax = cast(Tuple[int, int, int, int], v) |
40 | | - |
41 | | - elif len(v) == 6: |
42 | | - xmin, ymin, min_elev, xmax, ymax, max_elev = cast( |
43 | | - Tuple[int, int, int, int, int, int], v |
44 | | - ) |
45 | | - if max_elev < min_elev: |
46 | | - raise ValueError( |
47 | | - "Maximum elevation must greater than minimum elevation" |
48 | | - ) |
49 | | - else: |
50 | | - raise ValueError("Bounding box must have 4 or 6 coordinates") |
51 | | - |
52 | | - # Validate against WGS84 |
53 | | - if xmin < -180 or ymin < -90 or xmax > 180 or ymax > 90: |
54 | | - raise ValueError("Bounding box must be within (-180, -90, 180, 90)") |
55 | | - |
56 | | - if ymax < ymin: |
57 | | - raise ValueError("Maximum latitude must be greater than minimum latitude") |
58 | | - |
59 | | - return v |
60 | | - |
61 | | - |
62 | | -def str_to_datetimes(value: str) -> List[Optional[dt]]: |
63 | | - # Split on "/" and replace no value or ".." with None |
64 | | - values = [v if v and v != ".." else None for v in value.split("/")] |
65 | | - |
66 | | - # Cast because pylance gets confused by the type adapter and annotated type |
67 | | - dates = cast( |
68 | | - List[Optional[dt]], |
69 | | - [ |
70 | | - # Use the type adapter to validate the datetime strings, strict is necessary |
71 | | - # due to pydantic issues #8736 and #8762 |
72 | | - SearchDatetime.validate_strings(v, strict=True) if v else None |
73 | | - for v in values |
74 | | - ], |
75 | | - ) |
76 | | - return dates |
77 | | - |
78 | | - |
79 | | -def validate_datetime(v: Optional[str]) -> Optional[str]: |
80 | | - """Validate Datetime value.""" |
81 | | - if v is not None: |
82 | | - dates = str_to_datetimes(v) |
83 | | - |
84 | | - # If there are more than 2 dates, it's invalid |
85 | | - if len(dates) > 2: |
86 | | - raise ValueError( |
87 | | - "Invalid datetime range. Too many values. Must match format: {begin_date}/{end_date}" |
88 | | - ) |
89 | | - |
90 | | - # If there is only one date, duplicate to use for both start and end dates |
91 | | - if len(dates) == 1: |
92 | | - dates = [dates[0], dates[0]] |
93 | | - |
94 | | - # If there is a start and end date, check that the start date is before the end date |
95 | | - if dates[0] and dates[1] and dates[0] > dates[1]: |
96 | | - raise ValueError( |
97 | | - "Invalid datetime range. Begin date after end date. " |
98 | | - "Must match format: {begin_date}/{end_date}" |
99 | | - ) |
100 | | - |
101 | | - return v |
102 | | - |
103 | 36 |
|
104 | 37 | class Search(BaseModel): |
105 | 38 | """ |
|
0 commit comments