UPDATE
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
from .common import RouteContract
|
||||
from .crop_simulation_current_farm_chart import CONTRACT as CROP_SIMULATION_CURRENT_FARM_CHART_CONTRACT
|
||||
from .crop_simulation_growth import CONTRACT as CROP_SIMULATION_GROWTH_CONTRACT
|
||||
from .crop_simulation_growth_status import CONTRACT as CROP_SIMULATION_GROWTH_STATUS_CONTRACT
|
||||
from .crop_simulation_harvest_prediction import CONTRACT as CROP_SIMULATION_HARVEST_PREDICTION_CONTRACT
|
||||
from .crop_simulation_yield_prediction import CONTRACT as CROP_SIMULATION_YIELD_PREDICTION_CONTRACT
|
||||
from .economy_overview import CONTRACT as ECONOMY_OVERVIEW_CONTRACT
|
||||
from .farm_alerts import CONTRACTS as FARM_ALERTS_CONTRACTS
|
||||
from .farm_data_upsert import CONTRACT as FARM_DATA_UPSERT_CONTRACT
|
||||
from .farm_detail import CONTRACT as FARM_DETAIL_CONTRACT
|
||||
from .farm_parameter import CONTRACT as FARM_PARAMETER_CONTRACT
|
||||
from .fertilization_recommend import CONTRACT as FERTILIZATION_RECOMMEND_CONTRACT
|
||||
from .irrigation_methods import CONTRACTS as IRRIGATION_METHOD_CONTRACTS
|
||||
from .irrigation_recommend import CONTRACT as IRRIGATION_RECOMMEND_CONTRACT
|
||||
from .irrigation_water_stress import CONTRACT as IRRIGATION_WATER_STRESS_CONTRACT
|
||||
from .pest_disease import CONTRACTS as PEST_DISEASE_CONTRACTS
|
||||
from .plant import CONTRACTS as PLANT_CONTRACTS
|
||||
from .rag_chat import CONTRACT as RAG_CHAT_CONTRACT
|
||||
from .soil_data import CONTRACTS as SOIL_DATA_CONTRACTS
|
||||
from .soile_anomaly_detection import CONTRACT as SOILE_ANOMALY_DETECTION_CONTRACT
|
||||
from .soile_health_summary import CONTRACT as SOILE_HEALTH_SUMMARY_CONTRACT
|
||||
from .soile_moisture_heatmap import CONTRACT as SOILE_MOISTURE_HEATMAP_CONTRACT
|
||||
from .weather_farm_card import CONTRACT as WEATHER_FARM_CARD_CONTRACT
|
||||
from .weather_water_need_prediction import CONTRACT as WEATHER_WATER_NEED_PREDICTION_CONTRACT
|
||||
|
||||
ALL_ROUTE_CONTRACTS: list[RouteContract] = [
|
||||
RAG_CHAT_CONTRACT,
|
||||
*FARM_ALERTS_CONTRACTS,
|
||||
*SOIL_DATA_CONTRACTS,
|
||||
SOILE_MOISTURE_HEATMAP_CONTRACT,
|
||||
SOILE_HEALTH_SUMMARY_CONTRACT,
|
||||
SOILE_ANOMALY_DETECTION_CONTRACT,
|
||||
FARM_DATA_UPSERT_CONTRACT,
|
||||
FARM_DETAIL_CONTRACT,
|
||||
FARM_PARAMETER_CONTRACT,
|
||||
WEATHER_FARM_CARD_CONTRACT,
|
||||
WEATHER_WATER_NEED_PREDICTION_CONTRACT,
|
||||
ECONOMY_OVERVIEW_CONTRACT,
|
||||
*PLANT_CONTRACTS,
|
||||
*PEST_DISEASE_CONTRACTS,
|
||||
*IRRIGATION_METHOD_CONTRACTS,
|
||||
IRRIGATION_RECOMMEND_CONTRACT,
|
||||
IRRIGATION_WATER_STRESS_CONTRACT,
|
||||
FERTILIZATION_RECOMMEND_CONTRACT,
|
||||
CROP_SIMULATION_GROWTH_CONTRACT,
|
||||
CROP_SIMULATION_GROWTH_STATUS_CONTRACT,
|
||||
CROP_SIMULATION_CURRENT_FARM_CHART_CONTRACT,
|
||||
CROP_SIMULATION_HARVEST_PREDICTION_CONTRACT,
|
||||
CROP_SIMULATION_YIELD_PREDICTION_CONTRACT,
|
||||
]
|
||||
|
||||
ROUTE_CONTRACTS: dict[str, RouteContract] = {
|
||||
f'{contract.method} {contract.path}': contract
|
||||
for contract in ALL_ROUTE_CONTRACTS
|
||||
}
|
||||
|
||||
__all__ = ['ALL_ROUTE_CONTRACTS', 'ROUTE_CONTRACTS', 'RouteContract']
|
||||
@@ -0,0 +1,32 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Generic, TypeAlias, TypeVar
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
JsonValue: TypeAlias = Any
|
||||
JsonObject: TypeAlias = dict[str, Any]
|
||||
JsonList: TypeAlias = list[Any]
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
|
||||
class SchemaModel(BaseModel):
|
||||
model_config = ConfigDict(extra='allow', populate_by_name=True)
|
||||
|
||||
|
||||
class ApiEnvelope(SchemaModel, Generic[T]):
|
||||
code: int
|
||||
msg: str
|
||||
data: T
|
||||
|
||||
|
||||
class RouteContract(SchemaModel):
|
||||
method: str
|
||||
path: str
|
||||
request_model: str
|
||||
response_model: str
|
||||
|
||||
|
||||
class EmptyRequest(SchemaModel):
|
||||
pass
|
||||
@@ -0,0 +1,42 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, JsonObject, JsonValue, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'POST'
|
||||
ROUTE_PATH = '/api/crop-simulation/current-farm-chart/'
|
||||
|
||||
|
||||
class CropSimulationCurrentFarmChartRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
plant_name: str | None = None
|
||||
|
||||
|
||||
class CropSimulationCurrentFarmChartResponseData(SchemaModel):
|
||||
farm_uuid: str | None = None
|
||||
plant_name: str | None = None
|
||||
engine: str | None = None
|
||||
model_name: str | None = None
|
||||
scenario_id: int | None = None
|
||||
simulation_warning: str | None = None
|
||||
categories: list[str] = Field(default_factory=list)
|
||||
series: JsonValue | None = None
|
||||
summary: JsonObject = Field(default_factory=dict)
|
||||
current_state: JsonObject = Field(default_factory=dict)
|
||||
metrics: JsonObject = Field(default_factory=dict)
|
||||
daily_output: JsonObject = Field(default_factory=dict)
|
||||
|
||||
|
||||
class CropSimulationCurrentFarmChartResponse(ApiEnvelope[CropSimulationCurrentFarmChartResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=CropSimulationCurrentFarmChartRequest.__name__,
|
||||
response_model=CropSimulationCurrentFarmChartResponse.__name__,
|
||||
)
|
||||
@@ -0,0 +1,46 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import Field, model_validator
|
||||
|
||||
from .common import ApiEnvelope, JsonObject, JsonValue, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'POST'
|
||||
ROUTE_PATH = '/api/crop-simulation/growth/'
|
||||
|
||||
|
||||
class CropSimulationGrowthRequest(SchemaModel):
|
||||
plant_name: str
|
||||
dynamic_parameters: list[str] = Field(min_length=1)
|
||||
farm_uuid: UUID | None = None
|
||||
weather: JsonValue | None = None
|
||||
soil_parameters: JsonObject | None = None
|
||||
site_parameters: JsonObject | None = None
|
||||
crop_parameters: JsonObject | None = None
|
||||
agromanagement: JsonObject | None = None
|
||||
page_size: int | None = Field(default=None, ge=1, le=50)
|
||||
|
||||
@model_validator(mode='after')
|
||||
def validate_farm_or_weather(self) -> 'CropSimulationGrowthRequest':
|
||||
if self.farm_uuid is None and self.weather is None:
|
||||
raise ValueError('Either farm_uuid or weather must be provided.')
|
||||
return self
|
||||
|
||||
|
||||
class CropSimulationGrowthResponseData(SchemaModel):
|
||||
task_id: str
|
||||
status_url: str
|
||||
plant_name: str
|
||||
|
||||
|
||||
class CropSimulationGrowthResponse(ApiEnvelope[CropSimulationGrowthResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=CropSimulationGrowthRequest.__name__,
|
||||
response_model=CropSimulationGrowthResponse.__name__,
|
||||
)
|
||||
@@ -0,0 +1,59 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, JsonObject, JsonValue, JsonList, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'GET'
|
||||
ROUTE_PATH = '/api/crop-simulation/growth/<task_id>/status/'
|
||||
|
||||
|
||||
class CropSimulationGrowthStatusRequest(SchemaModel):
|
||||
task_id: str
|
||||
page: int | None = Field(default=None, ge=1)
|
||||
page_size: int | None = Field(default=None, ge=1)
|
||||
|
||||
|
||||
class CropSimulationPagination(SchemaModel):
|
||||
page: int
|
||||
page_size: int
|
||||
total_items: int
|
||||
total_pages: int
|
||||
has_next: bool
|
||||
has_previous: bool
|
||||
|
||||
|
||||
class CropSimulationGrowthResult(SchemaModel):
|
||||
plant_name: str | None = None
|
||||
dynamic_parameters: list[str] = Field(default_factory=list)
|
||||
engine: str | None = None
|
||||
model_name: str | None = None
|
||||
scenario_id: int | None = None
|
||||
simulation_warning: str | None = None
|
||||
summary_metrics: JsonObject = Field(default_factory=dict)
|
||||
stage_timeline: JsonList = Field(default_factory=list)
|
||||
stages_page: JsonList = Field(default_factory=list)
|
||||
pagination: CropSimulationPagination | None = None
|
||||
daily_records_count: int | None = None
|
||||
default_page_size: int | None = None
|
||||
|
||||
|
||||
class CropSimulationGrowthStatusResponseData(SchemaModel):
|
||||
task_id: str
|
||||
status: str
|
||||
message: str | None = None
|
||||
progress: JsonObject = Field(default_factory=dict)
|
||||
result: CropSimulationGrowthResult | None = None
|
||||
error: str | None = None
|
||||
|
||||
|
||||
class CropSimulationGrowthStatusResponse(ApiEnvelope[CropSimulationGrowthStatusResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=CropSimulationGrowthStatusRequest.__name__,
|
||||
response_model=CropSimulationGrowthStatusResponse.__name__,
|
||||
)
|
||||
@@ -0,0 +1,37 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, JsonObject, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'POST'
|
||||
ROUTE_PATH = '/api/crop-simulation/harvest-prediction/'
|
||||
|
||||
|
||||
class CropSimulationHarvestPredictionRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
plant_name: str | None = None
|
||||
|
||||
|
||||
class CropSimulationHarvestPredictionResponseData(SchemaModel):
|
||||
date: str
|
||||
dateFormatted: str
|
||||
daysUntil: int
|
||||
description: str | None = None
|
||||
optimalWindowStart: str | None = None
|
||||
optimalWindowEnd: str | None = None
|
||||
gddDetails: JsonObject = Field(default_factory=dict)
|
||||
|
||||
|
||||
class CropSimulationHarvestPredictionResponse(ApiEnvelope[CropSimulationHarvestPredictionResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=CropSimulationHarvestPredictionRequest.__name__,
|
||||
response_model=CropSimulationHarvestPredictionResponse.__name__,
|
||||
)
|
||||
@@ -0,0 +1,41 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, JsonObject, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'POST'
|
||||
ROUTE_PATH = '/api/crop-simulation/yield-prediction/'
|
||||
|
||||
|
||||
class CropSimulationYieldPredictionRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
plant_name: str | None = None
|
||||
|
||||
|
||||
class CropSimulationYieldPredictionResponseData(SchemaModel):
|
||||
farm_uuid: str
|
||||
plant_name: str | None = None
|
||||
predictedYieldTons: float | None = None
|
||||
predictedYieldRaw: float | None = None
|
||||
unit: str | None = None
|
||||
sourceUnit: str | None = None
|
||||
simulationEngine: str | None = None
|
||||
simulationModel: str | None = None
|
||||
scenarioId: int | None = None
|
||||
simulationWarning: str | None = None
|
||||
supportingMetrics: JsonObject = Field(default_factory=dict)
|
||||
|
||||
|
||||
class CropSimulationYieldPredictionResponse(ApiEnvelope[CropSimulationYieldPredictionResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=CropSimulationYieldPredictionRequest.__name__,
|
||||
response_model=CropSimulationYieldPredictionResponse.__name__,
|
||||
)
|
||||
@@ -0,0 +1,47 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'POST'
|
||||
ROUTE_PATH = '/api/economy/overview/'
|
||||
|
||||
|
||||
class EconomyOverviewRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
|
||||
|
||||
class EconomyDataItem(SchemaModel):
|
||||
title: str
|
||||
value: str
|
||||
subtitle: str | None = None
|
||||
avatarIcon: str | None = None
|
||||
avatarColor: str | None = None
|
||||
|
||||
|
||||
class ChartSeriesItem(SchemaModel):
|
||||
name: str
|
||||
data: list[float] = Field(default_factory=list)
|
||||
|
||||
|
||||
class EconomyOverviewResponseData(SchemaModel):
|
||||
farm_uuid: str
|
||||
source: str | None = None
|
||||
economicData: list[EconomyDataItem] = Field(default_factory=list)
|
||||
chartSeries: list[ChartSeriesItem] = Field(default_factory=list)
|
||||
chartCategories: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class EconomyOverviewResponse(ApiEnvelope[EconomyOverviewResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=EconomyOverviewRequest.__name__,
|
||||
response_model=EconomyOverviewResponse.__name__,
|
||||
)
|
||||
@@ -0,0 +1,90 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, JsonObject, JsonValue, RouteContract, SchemaModel
|
||||
|
||||
|
||||
class FarmAlertsRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
sensor_uuid: UUID | None = None
|
||||
query: str | None = None
|
||||
|
||||
|
||||
class FarmAlertNotificationSchema(SchemaModel):
|
||||
id: int | None = None
|
||||
farm_uuid: str | None = None
|
||||
endpoint: str | None = None
|
||||
level: Literal['danger', 'warning', 'info'] | str
|
||||
title: str
|
||||
message: str
|
||||
suggested_action: str | None = None
|
||||
source_alert_id: str | None = None
|
||||
source_metric_type: str | None = None
|
||||
payload: JsonObject = Field(default_factory=dict)
|
||||
created_at: str | None = None
|
||||
updated_at: str | None = None
|
||||
|
||||
|
||||
class FarmAlertsTimelineItem(SchemaModel):
|
||||
timestamp: str | None = None
|
||||
level: Literal['danger', 'warning', 'info'] | str
|
||||
title: str
|
||||
description: str | None = None
|
||||
source_alert_id: str | None = None
|
||||
source_metric_type: str | None = None
|
||||
|
||||
|
||||
class FarmAlertsTrackerResponseData(SchemaModel):
|
||||
farm_uuid: str
|
||||
service_id: str | None = None
|
||||
knowledge_base: str | None = None
|
||||
tone_file: str | None = None
|
||||
tracker: JsonObject = Field(default_factory=dict)
|
||||
headline: str
|
||||
overview: str | None = None
|
||||
status_level: Literal['danger', 'warning', 'info'] | str
|
||||
notifications: list[FarmAlertNotificationSchema] = Field(default_factory=list)
|
||||
raw_llm_response: str | None = None
|
||||
structured_context: JsonObject = Field(default_factory=dict)
|
||||
|
||||
|
||||
class FarmAlertsTimelineResponseData(SchemaModel):
|
||||
farm_uuid: str
|
||||
service_id: str | None = None
|
||||
knowledge_base: str | None = None
|
||||
tone_file: str | None = None
|
||||
tracker: JsonObject = Field(default_factory=dict)
|
||||
headline: str
|
||||
overview: str | None = None
|
||||
timeline: list[FarmAlertsTimelineItem] = Field(default_factory=list)
|
||||
notifications: list[FarmAlertNotificationSchema] = Field(default_factory=list)
|
||||
raw_llm_response: str | None = None
|
||||
structured_context: JsonObject = Field(default_factory=dict)
|
||||
|
||||
|
||||
class FarmAlertsTrackerResponse(ApiEnvelope[FarmAlertsTrackerResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
class FarmAlertsTimelineResponse(ApiEnvelope[FarmAlertsTimelineResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACTS = [
|
||||
RouteContract(
|
||||
method='POST',
|
||||
path='/api/farm-alerts/tracker/',
|
||||
request_model=FarmAlertsRequest.__name__,
|
||||
response_model=FarmAlertsTrackerResponse.__name__,
|
||||
),
|
||||
RouteContract(
|
||||
method='POST',
|
||||
path='/api/farm-alerts/timeline/',
|
||||
request_model=FarmAlertsRequest.__name__,
|
||||
response_model=FarmAlertsTimelineResponse.__name__,
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,53 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import Field, model_validator
|
||||
|
||||
from .common import ApiEnvelope, JsonObject, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'POST'
|
||||
ROUTE_PATH = '/api/farm-data/'
|
||||
|
||||
|
||||
class FarmBoundaryCorner(SchemaModel):
|
||||
lat: float
|
||||
lon: float
|
||||
|
||||
|
||||
class FarmDataUpsertRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
farm_boundary: JsonObject
|
||||
sensor_key: str | None = 'sensor-7-1'
|
||||
sensor_payload: JsonObject | None = None
|
||||
plant_ids: list[int] = Field(default_factory=list)
|
||||
irrigation_method_id: int | None = None
|
||||
|
||||
@model_validator(mode='after')
|
||||
def validate_payload_sources(self) -> 'FarmDataUpsertRequest':
|
||||
if not self.sensor_payload and not self.plant_ids and self.irrigation_method_id is None:
|
||||
raise ValueError('At least one of sensor_payload, plant_ids or irrigation_method_id must be provided.')
|
||||
return self
|
||||
|
||||
|
||||
class FarmDataUpsertResponseData(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
center_location_id: int | None = None
|
||||
weather_forecast_id: int | None = None
|
||||
sensor_payload: JsonObject = Field(default_factory=dict)
|
||||
plant_ids: list[int] = Field(default_factory=list)
|
||||
irrigation_method_id: int | None = None
|
||||
created_at: str | None = None
|
||||
updated_at: str | None = None
|
||||
|
||||
|
||||
class FarmDataUpsertResponse(ApiEnvelope[FarmDataUpsertResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=FarmDataUpsertRequest.__name__,
|
||||
response_model=FarmDataUpsertResponse.__name__,
|
||||
)
|
||||
@@ -0,0 +1,113 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, JsonObject, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'GET'
|
||||
ROUTE_PATH = '/api/farm-data/<farm_uuid>/detail/'
|
||||
|
||||
|
||||
class FarmDetailRequest(SchemaModel):
|
||||
farm_uuid: str
|
||||
|
||||
|
||||
class FarmCenterLocationSchema(SchemaModel):
|
||||
id: int
|
||||
lat: float
|
||||
lon: float
|
||||
farm_boundary: JsonObject = Field(default_factory=dict)
|
||||
|
||||
|
||||
class WeatherForecastDetailSchema(SchemaModel):
|
||||
id: int
|
||||
forecast_date: str | None = None
|
||||
temperature_min: float | None = None
|
||||
temperature_max: float | None = None
|
||||
temperature_mean: float | None = None
|
||||
precipitation: float | None = None
|
||||
precipitation_probability: float | None = None
|
||||
humidity_mean: float | None = None
|
||||
wind_speed_max: float | None = None
|
||||
et0: float | None = None
|
||||
weather_code: int | None = None
|
||||
|
||||
|
||||
class FarmSoilDepthSchema(SchemaModel):
|
||||
depth_label: str
|
||||
bdod: float | None = None
|
||||
cec: float | None = None
|
||||
cfvo: float | None = None
|
||||
clay: float | None = None
|
||||
nitrogen: float | None = None
|
||||
ocd: float | None = None
|
||||
ocs: float | None = None
|
||||
phh2o: float | None = None
|
||||
sand: float | None = None
|
||||
silt: float | None = None
|
||||
soc: float | None = None
|
||||
wv0010: float | None = None
|
||||
wv0033: float | None = None
|
||||
wv1500: float | None = None
|
||||
|
||||
|
||||
class FarmSoilPayloadSchema(SchemaModel):
|
||||
resolved_metrics: JsonObject = Field(default_factory=dict)
|
||||
metric_sources: JsonObject = Field(default_factory=dict)
|
||||
depths: list[FarmSoilDepthSchema] = Field(default_factory=list)
|
||||
|
||||
|
||||
class FarmPlantSchema(SchemaModel):
|
||||
id: int
|
||||
name: str
|
||||
light: str | None = None
|
||||
watering: str | None = None
|
||||
soil: str | None = None
|
||||
temperature: str | None = None
|
||||
growth_stage: str | None = None
|
||||
planting_season: str | None = None
|
||||
harvest_time: str | None = None
|
||||
spacing: str | None = None
|
||||
fertilizer: str | None = None
|
||||
created_at: str | None = None
|
||||
updated_at: str | None = None
|
||||
|
||||
|
||||
class FarmIrrigationMethodSchema(SchemaModel):
|
||||
id: int
|
||||
name: str
|
||||
category: str | None = None
|
||||
description: str | None = None
|
||||
water_efficiency_percent: float | None = None
|
||||
water_pressure_required: str | None = None
|
||||
flow_rate: str | None = None
|
||||
coverage_area: str | None = None
|
||||
soil_type: str | None = None
|
||||
climate_suitability: str | None = None
|
||||
created_at: str | None = None
|
||||
updated_at: str | None = None
|
||||
|
||||
|
||||
class FarmDetailResponseData(SchemaModel):
|
||||
center_location: FarmCenterLocationSchema
|
||||
weather: WeatherForecastDetailSchema | None = None
|
||||
sensor_payload: JsonObject = Field(default_factory=dict)
|
||||
soil: FarmSoilPayloadSchema
|
||||
plant_ids: list[int] = Field(default_factory=list)
|
||||
plants: list[FarmPlantSchema] = Field(default_factory=list)
|
||||
irrigation_method_id: int | None = None
|
||||
irrigation_method: FarmIrrigationMethodSchema | None = None
|
||||
created_at: str | None = None
|
||||
updated_at: str | None = None
|
||||
|
||||
|
||||
class FarmDetailResponse(ApiEnvelope[FarmDetailResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=FarmDetailRequest.__name__,
|
||||
response_model=FarmDetailResponse.__name__,
|
||||
)
|
||||
@@ -0,0 +1,41 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, JsonObject, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'POST'
|
||||
ROUTE_PATH = '/api/farm-data/parameters/'
|
||||
|
||||
|
||||
class FarmParameterRequest(SchemaModel):
|
||||
sensor_key: str = 'sensor-7-1'
|
||||
code: str
|
||||
name_fa: str
|
||||
unit: str | None = ''
|
||||
data_type: str | None = 'float'
|
||||
metadata: JsonObject = Field(default_factory=dict)
|
||||
|
||||
|
||||
class FarmParameterResponseData(SchemaModel):
|
||||
id: int
|
||||
sensor_key: str
|
||||
code: str
|
||||
name_fa: str
|
||||
unit: str | None = None
|
||||
data_type: str | None = None
|
||||
metadata: JsonObject = Field(default_factory=dict)
|
||||
created_at: str | None = None
|
||||
action: str
|
||||
|
||||
|
||||
class FarmParameterResponse(ApiEnvelope[FarmParameterResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=FarmParameterRequest.__name__,
|
||||
response_model=FarmParameterResponse.__name__,
|
||||
)
|
||||
@@ -0,0 +1,49 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'POST'
|
||||
ROUTE_PATH = '/api/fertilization/recommend/'
|
||||
|
||||
|
||||
class FertilizationRecommendRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
sensor_uuid: UUID | None = None
|
||||
plant_name: str | None = None
|
||||
growth_stage: str | None = None
|
||||
query: str | None = None
|
||||
|
||||
|
||||
class FertilizationSection(SchemaModel):
|
||||
type: Literal['recommendation', 'list', 'warning', 'info']
|
||||
title: str
|
||||
icon: str | None = None
|
||||
content: str | None = None
|
||||
items: list[str] = Field(default_factory=list)
|
||||
fertilizerType: str | None = None
|
||||
amount: str | None = None
|
||||
applicationMethod: str | None = None
|
||||
timing: str | None = None
|
||||
validityPeriod: str | None = None
|
||||
expandableExplanation: str | None = None
|
||||
|
||||
|
||||
class FertilizationRecommendResponseData(SchemaModel):
|
||||
sections: list[FertilizationSection] = Field(default_factory=list)
|
||||
|
||||
|
||||
class FertilizationRecommendResponse(ApiEnvelope[FertilizationRecommendResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=FertilizationRecommendRequest.__name__,
|
||||
response_model=FertilizationRecommendResponse.__name__,
|
||||
)
|
||||
@@ -0,0 +1,39 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, EmptyRequest, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'GET'
|
||||
ROUTE_PATH = '/api/irrigation/'
|
||||
|
||||
|
||||
class IrrigationListRequest(EmptyRequest):
|
||||
pass
|
||||
|
||||
|
||||
class IrrigationMethodSchema(SchemaModel):
|
||||
id: int
|
||||
name: str
|
||||
category: str | None = None
|
||||
description: str | None = None
|
||||
water_efficiency_percent: float | None = None
|
||||
water_pressure_required: str | None = None
|
||||
flow_rate: str | None = None
|
||||
coverage_area: str | None = None
|
||||
soil_type: str | None = None
|
||||
climate_suitability: str | None = None
|
||||
created_at: str | None = None
|
||||
updated_at: str | None = None
|
||||
|
||||
|
||||
class IrrigationListResponse(ApiEnvelope[list[IrrigationMethodSchema]]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=IrrigationListRequest.__name__,
|
||||
response_model=IrrigationListResponse.__name__,
|
||||
)
|
||||
@@ -0,0 +1,91 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, EmptyRequest, JsonObject, JsonValue, RouteContract, SchemaModel
|
||||
|
||||
|
||||
class IrrigationMethodPayload(SchemaModel):
|
||||
name: str
|
||||
category: str | None = None
|
||||
description: str | None = None
|
||||
water_efficiency_percent: float | None = None
|
||||
water_pressure_required: str | None = None
|
||||
flow_rate: str | None = None
|
||||
coverage_area: str | None = None
|
||||
soil_type: str | None = None
|
||||
climate_suitability: str | None = None
|
||||
|
||||
|
||||
class IrrigationMethodPartialPayload(SchemaModel):
|
||||
name: str | None = None
|
||||
category: str | None = None
|
||||
description: str | None = None
|
||||
water_efficiency_percent: float | None = None
|
||||
water_pressure_required: str | None = None
|
||||
flow_rate: str | None = None
|
||||
coverage_area: str | None = None
|
||||
soil_type: str | None = None
|
||||
climate_suitability: str | None = None
|
||||
|
||||
|
||||
class IrrigationMethodSchema(IrrigationMethodPayload):
|
||||
id: int
|
||||
created_at: str | None = None
|
||||
updated_at: str | None = None
|
||||
|
||||
|
||||
class IrrigationMethodDetailRequest(SchemaModel):
|
||||
pk: int
|
||||
|
||||
|
||||
class IrrigationMethodListResponse(ApiEnvelope[list[IrrigationMethodSchema]]):
|
||||
pass
|
||||
|
||||
|
||||
class IrrigationMethodDetailResponse(ApiEnvelope[IrrigationMethodSchema]):
|
||||
pass
|
||||
|
||||
|
||||
class IrrigationMethodDeleteResponse(ApiEnvelope[JsonValue | None]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACTS = [
|
||||
RouteContract(
|
||||
method='GET',
|
||||
path='/api/irrigation/',
|
||||
request_model=EmptyRequest.__name__,
|
||||
response_model=IrrigationMethodListResponse.__name__,
|
||||
),
|
||||
RouteContract(
|
||||
method='POST',
|
||||
path='/api/irrigation/',
|
||||
request_model=IrrigationMethodPayload.__name__,
|
||||
response_model=IrrigationMethodDetailResponse.__name__,
|
||||
),
|
||||
RouteContract(
|
||||
method='GET',
|
||||
path='/api/irrigation/<pk>/',
|
||||
request_model=IrrigationMethodDetailRequest.__name__,
|
||||
response_model=IrrigationMethodDetailResponse.__name__,
|
||||
),
|
||||
RouteContract(
|
||||
method='PUT',
|
||||
path='/api/irrigation/<pk>/',
|
||||
request_model=IrrigationMethodPayload.__name__,
|
||||
response_model=IrrigationMethodDetailResponse.__name__,
|
||||
),
|
||||
RouteContract(
|
||||
method='PATCH',
|
||||
path='/api/irrigation/<pk>/',
|
||||
request_model=IrrigationMethodPartialPayload.__name__,
|
||||
response_model=IrrigationMethodDetailResponse.__name__,
|
||||
),
|
||||
RouteContract(
|
||||
method='DELETE',
|
||||
path='/api/irrigation/<pk>/',
|
||||
request_model=IrrigationMethodDetailRequest.__name__,
|
||||
response_model=IrrigationMethodDeleteResponse.__name__,
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,49 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'POST'
|
||||
ROUTE_PATH = '/api/irrigation/recommend/'
|
||||
|
||||
|
||||
class IrrigationRecommendRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
sensor_uuid: UUID | None = None
|
||||
plant_name: str | None = None
|
||||
growth_stage: str | None = None
|
||||
irrigation_method_name: str | None = None
|
||||
query: str | None = None
|
||||
|
||||
|
||||
class IrrigationSection(SchemaModel):
|
||||
type: Literal['recommendation', 'list', 'warning', 'info']
|
||||
title: str
|
||||
icon: str | None = None
|
||||
content: str | None = None
|
||||
items: list[str] = Field(default_factory=list)
|
||||
frequency: str | None = None
|
||||
amount: str | None = None
|
||||
timing: str | None = None
|
||||
validityPeriod: str | None = None
|
||||
expandableExplanation: str | None = None
|
||||
|
||||
|
||||
class IrrigationRecommendResponseData(SchemaModel):
|
||||
sections: list[IrrigationSection] = Field(default_factory=list)
|
||||
|
||||
|
||||
class IrrigationRecommendResponse(ApiEnvelope[IrrigationRecommendResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=IrrigationRecommendRequest.__name__,
|
||||
response_model=IrrigationRecommendResponse.__name__,
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, JsonObject, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'POST'
|
||||
ROUTE_PATH = '/api/irrigation/water-stress/'
|
||||
|
||||
|
||||
class IrrigationWaterStressRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
sensor_uuid: UUID | None = None
|
||||
|
||||
|
||||
class IrrigationWaterStressResponseData(SchemaModel):
|
||||
farm_uuid: str
|
||||
waterStressIndex: int | float
|
||||
level: Literal['low', 'medium', 'high'] | str
|
||||
sourceMetric: JsonObject = Field(default_factory=dict)
|
||||
|
||||
|
||||
class IrrigationWaterStressResponse(ApiEnvelope[IrrigationWaterStressResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=IrrigationWaterStressRequest.__name__,
|
||||
response_model=IrrigationWaterStressResponse.__name__,
|
||||
)
|
||||
@@ -0,0 +1,118 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, JsonObject, JsonValue, RouteContract, SchemaModel
|
||||
|
||||
|
||||
class PestDiseaseDetectionRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
sensor_uuid: UUID | None = None
|
||||
plant_name: str | None = None
|
||||
query: str | None = None
|
||||
image_urls: list[str] = Field(default_factory=list)
|
||||
image: str | None = None
|
||||
images: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class PestDiseaseDetectionResponseData(SchemaModel):
|
||||
has_issue: bool
|
||||
category: Literal['no_issue', 'pest', 'disease', 'nutrient_stress', 'abiotic_stress', 'unknown'] | str
|
||||
confidence: float | None = None
|
||||
severity: Literal['low', 'medium', 'high'] | str
|
||||
summary: str
|
||||
detected_signs: list[str] = Field(default_factory=list)
|
||||
possible_causes: list[str] = Field(default_factory=list)
|
||||
immediate_actions: list[str] = Field(default_factory=list)
|
||||
reasoning: list[str] = Field(default_factory=list)
|
||||
farm_uuid: str | None = None
|
||||
knowledge_base: str | None = None
|
||||
tone_file: str | None = None
|
||||
raw_response: str | None = None
|
||||
|
||||
|
||||
class PestDiseaseRiskRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
sensor_uuid: UUID | None = None
|
||||
plant_name: str | None = None
|
||||
growth_stage: str | None = None
|
||||
query: str | None = None
|
||||
|
||||
|
||||
class RiskBlock(SchemaModel):
|
||||
score: float | None = None
|
||||
level: Literal['low', 'medium', 'high'] | str | None = None
|
||||
likely_conditions: list[str] = Field(default_factory=list)
|
||||
reasoning: list[str] = Field(default_factory=list)
|
||||
statsLabel: str | None = None
|
||||
|
||||
|
||||
class PestDiseaseRiskResponseData(SchemaModel):
|
||||
summary: str
|
||||
forecast_window: str | None = None
|
||||
overall_risk: Literal['low', 'medium', 'high'] | str
|
||||
disease_risk: RiskBlock = Field(default_factory=RiskBlock)
|
||||
pest_risk: RiskBlock = Field(default_factory=RiskBlock)
|
||||
key_drivers: list[str] = Field(default_factory=list)
|
||||
recommended_actions: list[str] = Field(default_factory=list)
|
||||
farm_context: JsonObject = Field(default_factory=dict)
|
||||
farm_uuid: str | None = None
|
||||
knowledge_base: str | None = None
|
||||
tone_file: str | None = None
|
||||
raw_response: str | None = None
|
||||
|
||||
|
||||
class PestDiseaseRiskSummaryRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
sensor_uuid: UUID | None = None
|
||||
|
||||
|
||||
class PestDiseaseRiskSummaryDrivers(SchemaModel):
|
||||
keyDrivers: list[str] = Field(default_factory=list)
|
||||
summary: str | None = None
|
||||
forecastWindow: str | None = None
|
||||
source: str | None = None
|
||||
|
||||
|
||||
class PestDiseaseRiskSummaryResponseData(SchemaModel):
|
||||
farm_uuid: str
|
||||
diseaseRisk: RiskBlock = Field(default_factory=RiskBlock)
|
||||
pestRisk: RiskBlock = Field(default_factory=RiskBlock)
|
||||
drivers: PestDiseaseRiskSummaryDrivers = Field(default_factory=PestDiseaseRiskSummaryDrivers)
|
||||
|
||||
|
||||
class PestDiseaseDetectionResponse(ApiEnvelope[PestDiseaseDetectionResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
class PestDiseaseRiskResponse(ApiEnvelope[PestDiseaseRiskResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
class PestDiseaseRiskSummaryResponse(ApiEnvelope[PestDiseaseRiskSummaryResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACTS = [
|
||||
RouteContract(
|
||||
method='POST',
|
||||
path='/api/pest-disease/detect/',
|
||||
request_model=PestDiseaseDetectionRequest.__name__,
|
||||
response_model=PestDiseaseDetectionResponse.__name__,
|
||||
),
|
||||
RouteContract(
|
||||
method='POST',
|
||||
path='/api/pest-disease/risk/',
|
||||
request_model=PestDiseaseRiskRequest.__name__,
|
||||
response_model=PestDiseaseRiskResponse.__name__,
|
||||
),
|
||||
RouteContract(
|
||||
method='POST',
|
||||
path='/api/pest-disease/risk-summary/',
|
||||
request_model=PestDiseaseRiskSummaryRequest.__name__,
|
||||
response_model=PestDiseaseRiskSummaryResponse.__name__,
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,107 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, EmptyRequest, JsonObject, JsonValue, RouteContract, SchemaModel
|
||||
|
||||
|
||||
class PlantPayload(SchemaModel):
|
||||
name: str
|
||||
light: str | None = None
|
||||
watering: str | None = None
|
||||
soil: str | None = None
|
||||
temperature: str | None = None
|
||||
growth_stage: str | None = None
|
||||
planting_season: str | None = None
|
||||
harvest_time: str | None = None
|
||||
spacing: str | None = None
|
||||
fertilizer: str | None = None
|
||||
|
||||
|
||||
class PlantPartialPayload(SchemaModel):
|
||||
name: str | None = None
|
||||
light: str | None = None
|
||||
watering: str | None = None
|
||||
soil: str | None = None
|
||||
temperature: str | None = None
|
||||
growth_stage: str | None = None
|
||||
planting_season: str | None = None
|
||||
harvest_time: str | None = None
|
||||
spacing: str | None = None
|
||||
fertilizer: str | None = None
|
||||
|
||||
|
||||
class PlantRecord(PlantPayload):
|
||||
id: int
|
||||
created_at: str | None = None
|
||||
updated_at: str | None = None
|
||||
|
||||
|
||||
class PlantListResponse(ApiEnvelope[list[PlantRecord]]):
|
||||
pass
|
||||
|
||||
|
||||
class PlantDetailResponse(ApiEnvelope[PlantRecord]):
|
||||
pass
|
||||
|
||||
|
||||
class PlantDeleteResponse(ApiEnvelope[JsonValue | None]):
|
||||
pass
|
||||
|
||||
|
||||
class PlantDetailRequest(SchemaModel):
|
||||
pk: int
|
||||
|
||||
|
||||
class PlantFetchInfoRequest(SchemaModel):
|
||||
name: str
|
||||
|
||||
|
||||
class PlantFetchInfoResponse(ApiEnvelope[JsonObject]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACTS = [
|
||||
RouteContract(
|
||||
method='GET',
|
||||
path='/api/plants/',
|
||||
request_model=EmptyRequest.__name__,
|
||||
response_model=PlantListResponse.__name__,
|
||||
),
|
||||
RouteContract(
|
||||
method='POST',
|
||||
path='/api/plants/',
|
||||
request_model=PlantPayload.__name__,
|
||||
response_model=PlantDetailResponse.__name__,
|
||||
),
|
||||
RouteContract(
|
||||
method='GET',
|
||||
path='/api/plants/<pk>/',
|
||||
request_model=PlantDetailRequest.__name__,
|
||||
response_model=PlantDetailResponse.__name__,
|
||||
),
|
||||
RouteContract(
|
||||
method='PUT',
|
||||
path='/api/plants/<pk>/',
|
||||
request_model=PlantPayload.__name__,
|
||||
response_model=PlantDetailResponse.__name__,
|
||||
),
|
||||
RouteContract(
|
||||
method='PATCH',
|
||||
path='/api/plants/<pk>/',
|
||||
request_model=PlantPartialPayload.__name__,
|
||||
response_model=PlantDetailResponse.__name__,
|
||||
),
|
||||
RouteContract(
|
||||
method='DELETE',
|
||||
path='/api/plants/<pk>/',
|
||||
request_model=PlantDetailRequest.__name__,
|
||||
response_model=PlantDeleteResponse.__name__,
|
||||
),
|
||||
RouteContract(
|
||||
method='POST',
|
||||
path='/api/plants/fetch-info/',
|
||||
request_model=PlantFetchInfoRequest.__name__,
|
||||
response_model=PlantFetchInfoResponse.__name__,
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,50 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, JsonObject, JsonValue, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'POST'
|
||||
ROUTE_PATH = '/api/rag/chat/'
|
||||
|
||||
|
||||
class RagChatRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
query: str | None = None
|
||||
message: str | None = None
|
||||
history: list[JsonObject] | str | None = None
|
||||
image_urls: list[str] = Field(default_factory=list)
|
||||
image: str | None = None
|
||||
images: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class RagChatSection(SchemaModel):
|
||||
type: Literal['recommendation', 'list', 'warning', 'info', 'summary']
|
||||
title: str
|
||||
icon: str | None = None
|
||||
content: str | None = None
|
||||
items: list[str] = Field(default_factory=list)
|
||||
primaryAction: str | None = None
|
||||
timing: str | None = None
|
||||
validityPeriod: str | None = None
|
||||
expandableExplanation: str | None = None
|
||||
metadata: JsonValue | None = None
|
||||
|
||||
|
||||
class RagChatResponseData(SchemaModel):
|
||||
sections: list[RagChatSection] = Field(default_factory=list)
|
||||
|
||||
|
||||
class RagChatResponse(ApiEnvelope[RagChatResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=RagChatRequest.__name__,
|
||||
response_model=RagChatResponse.__name__,
|
||||
)
|
||||
@@ -0,0 +1,124 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, JsonObject, JsonValue, RouteContract, SchemaModel
|
||||
|
||||
|
||||
class SoilDataCoordinatesRequest(SchemaModel):
|
||||
lat: float
|
||||
lon: float
|
||||
|
||||
|
||||
class SoilDepthDataSchema(SchemaModel):
|
||||
depth_label: str
|
||||
bdod: float | None = None
|
||||
cec: float | None = None
|
||||
cfvo: float | None = None
|
||||
clay: float | None = None
|
||||
nitrogen: float | None = None
|
||||
ocd: float | None = None
|
||||
ocs: float | None = None
|
||||
phh2o: float | None = None
|
||||
sand: float | None = None
|
||||
silt: float | None = None
|
||||
soc: float | None = None
|
||||
wv0010: float | None = None
|
||||
wv0033: float | None = None
|
||||
wv1500: float | None = None
|
||||
|
||||
|
||||
class SoilLocationPayload(SchemaModel):
|
||||
source: str
|
||||
id: int
|
||||
lon: float
|
||||
lat: float
|
||||
depths: list[SoilDepthDataSchema] = Field(default_factory=list)
|
||||
|
||||
|
||||
class SoilTaskQueuedResponseData(SchemaModel):
|
||||
source: str = 'task'
|
||||
task_id: str
|
||||
lon: float
|
||||
lat: float
|
||||
status_url: str | None = None
|
||||
|
||||
|
||||
class SoilTaskStatusResponseData(SchemaModel):
|
||||
task_id: str
|
||||
status: str
|
||||
message: str | None = None
|
||||
progress: JsonObject = Field(default_factory=dict)
|
||||
result: JsonValue | None = None
|
||||
error: str | None = None
|
||||
|
||||
|
||||
class NdviHealthRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
|
||||
|
||||
class NdviHealthDataItem(SchemaModel):
|
||||
title: str
|
||||
value: JsonValue | None = None
|
||||
color: str
|
||||
icon: str
|
||||
|
||||
|
||||
class NdviHealthResponseData(SchemaModel):
|
||||
ndviIndex: float | None = None
|
||||
mean_ndvi: float | None = None
|
||||
ndvi_map: JsonObject = Field(default_factory=dict)
|
||||
vegetation_health_class: str | None = None
|
||||
observation_date: str | None = None
|
||||
satellite_source: str | None = None
|
||||
healthData: list[NdviHealthDataItem] = Field(default_factory=list)
|
||||
|
||||
|
||||
class SoilDataResponse(ApiEnvelope[SoilLocationPayload]):
|
||||
pass
|
||||
|
||||
|
||||
class SoilTaskQueuedResponse(ApiEnvelope[SoilTaskQueuedResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
class SoilTaskStatusResponse(ApiEnvelope[SoilTaskStatusResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
class NdviHealthResponse(ApiEnvelope[NdviHealthResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACTS = [
|
||||
RouteContract(
|
||||
method='GET',
|
||||
path='/api/soil-data/',
|
||||
request_model=SoilDataCoordinatesRequest.__name__,
|
||||
response_model=SoilDataResponse.__name__,
|
||||
),
|
||||
RouteContract(
|
||||
method='POST',
|
||||
path='/api/soil-data/',
|
||||
request_model=SoilDataCoordinatesRequest.__name__,
|
||||
response_model=SoilDataResponse.__name__,
|
||||
),
|
||||
RouteContract(
|
||||
method='GET',
|
||||
path='/api/soil-data/tasks/<task_id>/status/',
|
||||
request_model='SoilTaskStatusRequest',
|
||||
response_model=SoilTaskStatusResponse.__name__,
|
||||
),
|
||||
RouteContract(
|
||||
method='POST',
|
||||
path='/api/soil-data/ndvi-health/',
|
||||
request_model=NdviHealthRequest.__name__,
|
||||
response_model=NdviHealthResponse.__name__,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
class SoilTaskStatusRequest(SchemaModel):
|
||||
task_id: str
|
||||
@@ -0,0 +1,42 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, JsonObject, JsonList, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'POST'
|
||||
ROUTE_PATH = '/api/soile/anomaly-detection/'
|
||||
|
||||
|
||||
class SoileAnomalyDetectionRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
|
||||
|
||||
class SoileAnomalyDetectionResponseData(SchemaModel):
|
||||
farm_uuid: str
|
||||
summary: str
|
||||
explanation: str | None = None
|
||||
likely_cause: str | None = None
|
||||
recommended_action: str | None = None
|
||||
monitoring_priority: Literal['low', 'medium', 'high', 'urgent'] | str
|
||||
confidence: float | None = None
|
||||
generated_at: str | None = None
|
||||
anomalies: JsonList = Field(default_factory=list)
|
||||
interpretation: JsonObject = Field(default_factory=dict)
|
||||
knowledge_base: str | None = None
|
||||
raw_response: str | None = None
|
||||
|
||||
|
||||
class SoileAnomalyDetectionResponse(ApiEnvelope[SoileAnomalyDetectionResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=SoileAnomalyDetectionRequest.__name__,
|
||||
response_model=SoileAnomalyDetectionResponse.__name__,
|
||||
)
|
||||
@@ -0,0 +1,37 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, JsonObject, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'POST'
|
||||
ROUTE_PATH = '/api/soile/health-summary/'
|
||||
|
||||
|
||||
class SoileHealthSummaryRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
|
||||
|
||||
class SoileHealthSummaryResponseData(SchemaModel):
|
||||
farm_uuid: str
|
||||
healthScore: int | float
|
||||
profileSource: str | None = None
|
||||
healthScoreDetails: JsonObject = Field(default_factory=dict)
|
||||
healthLanguage: JsonObject = Field(default_factory=dict)
|
||||
avgSoilMoisture: int | float | None = None
|
||||
avgSoilMoistureRaw: float | None = None
|
||||
avgSoilMoistureStatus: str | None = None
|
||||
|
||||
|
||||
class SoileHealthSummaryResponse(ApiEnvelope[SoileHealthSummaryResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=SoileHealthSummaryRequest.__name__,
|
||||
response_model=SoileHealthSummaryResponse.__name__,
|
||||
)
|
||||
@@ -0,0 +1,41 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, JsonObject, JsonList, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'POST'
|
||||
ROUTE_PATH = '/api/soile/moisture-heatmap/'
|
||||
|
||||
|
||||
class SoileMoistureHeatmapRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
|
||||
|
||||
class SoileMoistureHeatmapResponseData(SchemaModel):
|
||||
farm_uuid: str
|
||||
location: JsonObject = Field(default_factory=dict)
|
||||
current_sensor: JsonObject = Field(default_factory=dict)
|
||||
soil_profile: JsonList = Field(default_factory=list)
|
||||
timestamp: str | None = None
|
||||
grid_resolution: JsonObject = Field(default_factory=dict)
|
||||
grid_cells: JsonList = Field(default_factory=list)
|
||||
sensor_points: JsonList = Field(default_factory=list)
|
||||
quality_legend: JsonObject = Field(default_factory=dict)
|
||||
depth_layers: JsonList = Field(default_factory=list)
|
||||
model_metadata: JsonObject = Field(default_factory=dict)
|
||||
summary: JsonObject = Field(default_factory=dict)
|
||||
|
||||
|
||||
class SoileMoistureHeatmapResponse(ApiEnvelope[SoileMoistureHeatmapResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=SoileMoistureHeatmapRequest.__name__,
|
||||
response_model=SoileMoistureHeatmapResponse.__name__,
|
||||
)
|
||||
@@ -0,0 +1,41 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'POST'
|
||||
ROUTE_PATH = '/api/weather/farm-card/'
|
||||
|
||||
|
||||
class WeatherFarmCardRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
|
||||
|
||||
class WeatherChartData(SchemaModel):
|
||||
labels: list[str] = Field(default_factory=list)
|
||||
series: list[list[float]] = Field(default_factory=list)
|
||||
|
||||
|
||||
class WeatherFarmCardResponseData(SchemaModel):
|
||||
condition: str
|
||||
temperature: float | int
|
||||
unit: str
|
||||
humidity: float | int
|
||||
windSpeed: float | int
|
||||
windUnit: str
|
||||
chartData: WeatherChartData = Field(default_factory=WeatherChartData)
|
||||
|
||||
|
||||
class WeatherFarmCardResponse(ApiEnvelope[WeatherFarmCardResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=WeatherFarmCardRequest.__name__,
|
||||
response_model=WeatherFarmCardResponse.__name__,
|
||||
)
|
||||
@@ -0,0 +1,46 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .common import ApiEnvelope, JsonList, RouteContract, SchemaModel
|
||||
|
||||
HTTP_METHOD = 'POST'
|
||||
ROUTE_PATH = '/api/weather/water-need-prediction/'
|
||||
|
||||
|
||||
class WeatherWaterNeedPredictionRequest(SchemaModel):
|
||||
farm_uuid: UUID
|
||||
|
||||
|
||||
class WaterNeedInsight(SchemaModel):
|
||||
summary: str | None = None
|
||||
irrigation_outlook: str | None = None
|
||||
recommended_action: str | None = None
|
||||
risk_note: str | None = None
|
||||
confidence: float | None = None
|
||||
|
||||
|
||||
class WeatherWaterNeedPredictionResponseData(SchemaModel):
|
||||
farm_uuid: str
|
||||
totalNext7Days: float | None = None
|
||||
unit: str | None = None
|
||||
categories: list[str] = Field(default_factory=list)
|
||||
series: JsonList = Field(default_factory=list)
|
||||
dailyBreakdown: JsonList = Field(default_factory=list)
|
||||
insight: WaterNeedInsight = Field(default_factory=WaterNeedInsight)
|
||||
knowledge_base: str | None = None
|
||||
raw_response: str | None = None
|
||||
|
||||
|
||||
class WeatherWaterNeedPredictionResponse(ApiEnvelope[WeatherWaterNeedPredictionResponseData]):
|
||||
pass
|
||||
|
||||
|
||||
CONTRACT = RouteContract(
|
||||
method=HTTP_METHOD,
|
||||
path=ROUTE_PATH,
|
||||
request_model=WeatherWaterNeedPredictionRequest.__name__,
|
||||
response_model=WeatherWaterNeedPredictionResponse.__name__,
|
||||
)
|
||||
Reference in New Issue
Block a user