Skip to content

Commit d904e31

Browse files
committed
IW search works
1 parent e541439 commit d904e31

File tree

6 files changed

+108
-72
lines changed

6 files changed

+108
-72
lines changed

src/planet/backends.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from stapi_fastapi.routers.product_router import ProductRouter
2929

3030
from client import Client
31-
from conversions import planet_order_to_stapi_order, stapi_opportunity_payload_to_planet_iw_search
31+
import conversions
3232

3333

3434
async def mock_get_orders(
@@ -64,7 +64,7 @@ async def get_order(order_id: str, request: Request) -> ResultE[Maybe[Order]]:
6464
"""
6565
try:
6666
return Success(
67-
Maybe.from_optional(planet_order_to_stapi_order(Client(request).get_order(order_id)))
67+
Maybe.from_optional(conversions.planet_order_to_stapi_order(Client(request).get_order(order_id)))
6868
)
6969
except Exception as e:
7070
return Failure(e)
@@ -140,11 +140,11 @@ async def search_opportunities(
140140
request: Request,
141141
) -> ResultE[tuple[list[Opportunity], Maybe[str]]]:
142142
try:
143-
iw_request = stapi_opportunity_payload_to_planet_iw_search(product_router.product, search)
143+
iw_request = conversions.stapi_opportunity_payload_to_planet_iw_search(product_router.product, search)
144144
imaging_windows = Client(request).get_imaging_windows(iw_request)
145145

146146
opportunities = [
147-
imaging_window_to_opportunity(iw, search)
147+
conversions.planet_iw_to_stapi_opportunity(iw, product_router.product, search)
148148
for iw
149149
in imaging_windows
150150
]

src/planet/client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ def get_imaging_windows(self, payload: dict) -> dict:
3838
"Header 'location' not found: %s, status %s, body %s" % (
3939
list(r.headers.keys()), r.status_code, r.text)
4040
)
41-
poll_url = r.headers['location']
41+
poll_url = f"{Settings().api_domain}{r.headers['location']}"
4242

4343
while True:
44+
print('polling', poll_url)
4445
r = requests.get(poll_url, headers=self.headers)
4546
r.raise_for_status()
4647
status = r.json()['status']

src/planet/conversions.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from pydantic import AwareDatetime
44

5+
from planet.models import PlanetOpportunityProperties, OffNadirAngleRange, PlanetSatelliteType
56
from stapi_fastapi import Product
67
from stapi_fastapi.models.order import Order, OrderProperties, OrderSearchParameters, OrderStatus, OrderStatusCode
78
from stapi_fastapi.models.opportunity import (
@@ -82,16 +83,25 @@ def stapi_opportunity_payload_to_planet_iw_search(product: Product, search: Oppo
8283
"geometry": search.geometry.dict(),
8384
}
8485

85-
def planet_iw_to_stapi_opportunity(iw: dict, search: OpportunityPayload) -> Opportunity:
86+
def planet_iw_to_stapi_opportunity(iw: dict, product: Product, search: OpportunityPayload) -> Opportunity:
87+
88+
print(iw)
89+
90+
properties = PlanetOpportunityProperties(
91+
product_id=product.id,
92+
datetime=(iw['start_time'], iw['end_time']),
93+
off_nadir_angle=OffNadirAngleRange(
94+
minimum=iw['off_nadir_angle_min'],
95+
maximum=iw['off_nadir_angle_max']),
96+
satellite_type=PlanetSatelliteType(iw['satellite_type']),
97+
title='Planet Assured Imaging Window @ ' + iw['start_time'],
98+
)
99+
if 'cloud_forecast' in iw and len(cf := iw['cloud_forecast']) > 0:
100+
properties.cloud_forecast = cf[0].get('prediction')
101+
86102
return Opportunity(
87103
id=iw["id"],
104+
type="Feature",
88105
geometry=search.geometry,
89-
properties={
90-
'title': 'Planet Assured Imaging Window @ ' + iw['start_time'],
91-
'datetime': f"{iw['start_time']}/{iw['end_time']}",
92-
'product_id': search.product_id,
93-
'constraints': {
94-
'off_nadir': [iw['start_off_nadir'], iw['end_off_nadir']],
95-
'cloud_cover': iw['cloud_forecast'][0]['prediction']
96-
}
97-
})
106+
properties=properties
107+
)

src/planet/models.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
from typing import Any, Literal, Self
2+
from enum import Enum
3+
4+
5+
from pydantic import BaseModel, Field, model_validator
6+
7+
from stapi_fastapi.models.opportunity import (
8+
Opportunity,
9+
OpportunityCollection,
10+
OpportunityProperties,
11+
OpportunitySearchRecord,
12+
)
13+
from stapi_fastapi.models.order import (
14+
Order,
15+
OrderParameters,
16+
OrderStatus,
17+
)
18+
from stapi_fastapi.models.product import (
19+
Product,
20+
Provider,
21+
ProviderRole,
22+
)
23+
24+
class PlanetSatelliteType(str, Enum):
25+
SKYSAT = 'SKYSAT'
26+
PELICAN = 'PELICAN'
27+
TANAGER = 'TANAGER'
28+
29+
class PlanetProductConstraints(BaseModel):
30+
off_nadir: float
31+
32+
33+
class OffNadirAngleRange(BaseModel):
34+
minimum: float = Field(ge=0, le=90)
35+
maximum: float = Field(ge=0, le=90)
36+
37+
@model_validator(mode="after")
38+
def validate_range(self) -> Self:
39+
if self.minimum > self.maximum:
40+
raise ValueError("range minimum cannot be greater than maximum")
41+
return self
42+
43+
44+
class PlanetOpportunityProperties(OpportunityProperties):
45+
off_nadir_angle: OffNadirAngleRange
46+
satellite_type: PlanetSatelliteType
47+
# off_nadir start/end
48+
# sat_elevation_angle
49+
# sat_azimuth_angle
50+
# sat_azimuth_angle start/end
51+
# sun_elevation_angle
52+
# solar_zenith_angle
53+
# low_light
54+
# ground_sample_distance
55+
# assured_tasking_tier
56+
# conflicting_orders
57+
# quota_priority_multiplier
58+
# cloud_forecast
59+
# sensitivity_mode
60+
61+
62+
class PlanetOrderParameters(OrderParameters):
63+
s3_path: str | None = None
64+
65+
66+
67+
provider_planet = Provider(
68+
name="Planet",
69+
description="A provider for Test data",
70+
roles=[ProviderRole.producer], # Example role
71+
url="https://www.planet.com", # Must be a valid URL
72+
)

src/planet/settings.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
from pydantic_settings import BaseSettings
55

66
API_DOMAIN = "https://api.planet.com"
7-
API_BASE_URL = "https://api.staging.planet-labs.com"
8-
API_BASE_URL = "https://api.planet.com"
97

108

119
class LogLevel(Enum):

src/planet/shared.py

Lines changed: 10 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@
3030
ProviderRole,
3131
)
3232

33+
from models import (
34+
PlanetProductConstraints,
35+
PlanetOpportunityProperties,
36+
PlanetOrderParameters,
37+
provider_planet
38+
)
39+
3340
from backends import (
3441
mock_create_order,
3542
mock_get_opportunity_collection,
@@ -90,38 +97,9 @@ def put_opportunity_collection(self, collection: OpportunityCollection) -> None:
9097

9198

9299

93-
class MyProductConstraints(BaseModel):
94-
off_nadir: int
95-
96-
97-
class OffNadirRange(BaseModel):
98-
minimum: int = Field(ge=0, le=45)
99-
maximum: int = Field(ge=0, le=45)
100-
101-
@model_validator(mode="after")
102-
def validate_range(self) -> Self:
103-
if self.minimum > self.maximum:
104-
raise ValueError("range minimum cannot be greater than maximum")
105-
return self
106-
107100

108-
class MyOpportunityProperties(OpportunityProperties):
109-
off_nadir: OffNadirRange
110-
vehicle_id: list[Literal[1, 2, 5, 7, 8]]
111-
platform: Literal["platform_id"]
112101

113102

114-
class MyOrderParameters(OrderParameters):
115-
s3_path: str | None = None
116-
117-
118-
provider_planet = Provider(
119-
name="Planet",
120-
description="A provider for Test data",
121-
roles=[ProviderRole.producer], # Example role
122-
url="https://www.planet.com", # Must be a valid URL
123-
)
124-
125103

126104
product_test_planet_sync_opportunity = Product(
127105
id="INT-003001:Assured Tasking",
@@ -135,31 +113,8 @@ class MyOrderParameters(OrderParameters):
135113
search_opportunities=search_opportunities,
136114
search_opportunities_async=None,
137115
get_opportunity_collection=None,
138-
constraints=MyProductConstraints,
139-
opportunity_properties=MyOpportunityProperties,
140-
order_parameters=MyOrderParameters,
116+
constraints=PlanetProductConstraints,
117+
opportunity_properties=PlanetOpportunityProperties,
118+
order_parameters=PlanetOrderParameters,
141119
)
142120

143-
144-
def create_mock_opportunity() -> Opportunity:
145-
now = datetime.now(timezone.utc) # Use timezone-aware datetime
146-
start = now
147-
end = start + timedelta(days=5)
148-
149-
# Create a list of mock opportunities for the given product
150-
return Opportunity(
151-
id=str(uuid4()),
152-
type="Feature",
153-
geometry=Point(
154-
type="Point",
155-
coordinates=Position2D(longitude=0.0, latitude=0.0),
156-
),
157-
properties=MyOpportunityProperties(
158-
product_id="xyz123",
159-
datetime=(start, end),
160-
off_nadir=OffNadirRange(minimum=20, maximum=22),
161-
vehicle_id=[1],
162-
platform="platform_id",
163-
other_thing="abcd1234", # type: ignore
164-
),
165-
)

0 commit comments

Comments
 (0)