UPDATE
This commit is contained in:
+39
@@ -0,0 +1,39 @@
|
|||||||
|
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_data_upsert import CONTRACT as FARM_DATA_UPSERT_CONTRACT
|
||||||
|
from .fertilization_recommend import CONTRACT as FERTILIZATION_RECOMMEND_CONTRACT
|
||||||
|
from .irrigation_list import CONTRACT as IRRIGATION_LIST_CONTRACT
|
||||||
|
from .irrigation_recommend import CONTRACT as IRRIGATION_RECOMMEND_CONTRACT
|
||||||
|
from .rag_chat import CONTRACT as RAG_CHAT_CONTRACT
|
||||||
|
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_water_need_prediction import CONTRACT as WEATHER_WATER_NEED_PREDICTION_CONTRACT
|
||||||
|
|
||||||
|
ROUTE_CONTRACTS: dict[str, RouteContract] = {
|
||||||
|
contract.path: contract
|
||||||
|
for contract in [
|
||||||
|
RAG_CHAT_CONTRACT,
|
||||||
|
SOILE_MOISTURE_HEATMAP_CONTRACT,
|
||||||
|
SOILE_HEALTH_SUMMARY_CONTRACT,
|
||||||
|
SOILE_ANOMALY_DETECTION_CONTRACT,
|
||||||
|
FARM_DATA_UPSERT_CONTRACT,
|
||||||
|
WEATHER_WATER_NEED_PREDICTION_CONTRACT,
|
||||||
|
ECONOMY_OVERVIEW_CONTRACT,
|
||||||
|
IRRIGATION_LIST_CONTRACT,
|
||||||
|
IRRIGATION_RECOMMEND_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,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
__all__ = ['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,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,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 RootModel
|
||||||
|
|
||||||
|
from .common import 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(RootModel[list[IrrigationMethodSchema]]):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
CONTRACT = RouteContract(
|
||||||
|
method=HTTP_METHOD,
|
||||||
|
path=ROUTE_PATH,
|
||||||
|
request_model=IrrigationListRequest.__name__,
|
||||||
|
response_model=IrrigationListResponse.__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__,
|
||||||
|
)
|
||||||
+50
@@ -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,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,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