-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathproduct.py
156 lines (111 loc) · 4.36 KB
/
product.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
"""
this file shows how to define products and their parameters first and then schema models
for fully OpenAPI typed endpoints.
Tried making it more reusable with pydantic's `create_model` method, but:
1. code became less readable
2. lost typing support for these dynamically created models (at least in VSCode)
Therefore showing the _explicit_ way here, even if the models feel anemic. Could write
an code generator for this chore.
"""
from dataclasses import dataclass
from typing import Annotated, Generic, Literal, Type, Union
from annotated_types import Ge, Le
from geojson_pydantic import Feature, FeatureCollection, Point, Polygon
from geojson_pydantic.features import Geom
from pydantic import BaseModel, ConfigDict
from stapi_fastapi.models.product import Product
from stapi_fastapi.types.datetime_interval import DatetimeInterval
from stapi_fastapi.types.filter import CQL2Filter
# Step 0a: define some mixins, should be moved to `stapi_fastapi` TODO
class OpportunityPropertiesMixin(BaseModel):
datetime: DatetimeInterval
class OpportunityRequestGenericMixin(BaseModel):
"""
base mixin allowing additional attributes for when no explicit OpenAPI is
desired (maybe that shouldn't be a thing, but expect implementors to do the
models? TODO).
"""
filter: CQL2Filter | None = None
model_config = ConfigDict(extra="allow")
class OpportunityRequestMixin(OpportunityRequestGenericMixin, Generic[Geom]):
"""
base requiring being explicit.
"""
geometry: Geom
model_config = ConfigDict(extra="forbid")
# Step 0b: define a helper container to keep things tidy
@dataclass
class ProductSchema:
product: Product
opportunity_schema: Type[Feature]
# Step 1: For each product, define parameters model
class Product0Parameters(BaseModel):
max_off_nadir: Annotated[float, Ge(0), Le(45)] = 30
class Product1Parameters(BaseModel):
max_off_nadir: Annotated[float, Ge(0), Le(30)] = 15
# Step 2a: Define opportunity properties models for each product parameter model.
class Product0OpportunityProperties(Product0Parameters, OpportunityPropertiesMixin):
product_id: Literal["tle1"] = "tle1" # would be nice to enfore presence of this?
class Product1OpportunityProperties(Product1Parameters, OpportunityPropertiesMixin):
product_id: Literal["tle2"] = "tle2"
# Step 2b: Define opportunity request model for each product parameter model; and their
# union
class Product0OpportunityRequest(
Product0OpportunityProperties, OpportunityRequestMixin[Point]
):
""""""
class Product1OpportunityRequest(
Product1OpportunityProperties, OpportunityRequestMixin[Point]
):
""""""
OpportunityRequest = Union[Product0OpportunityRequest, Product1OpportunityRequest]
# Step 2c: Define opportunity model for each product parameter model. Special trick: use
# additional attributes for the response vs request.
class Product0OpportunityResponseProperties(Product0OpportunityProperties):
azimuth: float
incidence: float
off_nadir: float
sun_azimuth: float
sun_elevation: float
class Product0Opportunity(Feature[Polygon, Product0OpportunityResponseProperties]):
type: Literal["Feature"] = "Feature"
class Product1OpportunityResponseProperties(Product1OpportunityProperties):
azimuth: float
incidence: float
off_nadir: float
sun_azimuth: float
sun_elevation: float
class Product1Opportunity(Feature[Polygon, Product1OpportunityResponseProperties]):
type: Literal["Feature"] = "Feature"
# Step 2d: Define opportunity collections union using all opportunity models
class Product0OpportunityCollection(FeatureCollection[Product0Opportunity]):
""""""
class Product1OpportunityCollection(FeatureCollection[Product1Opportunity]):
""""""
OpportunityCollection = Union[
Product0OpportunityCollection,
Product1OpportunityCollection,
]
# Step 3: Define products
PRODUCTS = (
ProductSchema(
product=Product(
id="tle1",
title="TLE defined satellite",
license="",
links=[],
parameters=Product0Parameters,
),
opportunity_schema=Product0Opportunity,
),
ProductSchema(
product=Product(
id="tle2",
title="Another TLE defined satellite",
license="",
links=[],
parameters=Product1Parameters,
),
opportunity_schema=Product1Opportunity,
),
)