- SQL migration 101_user_custom_pois.sql: table with GEOGRAPHY(POINT) + GIST index - Pydantic schemas: CustomPoiCreate / CustomPoiUpdate / CustomPoiOut - Service custom_pois.py: CRUD + get_overlaps_for_scoring (1km radius, psycopg v3 CAST) - API /api/v1/custom-pois: POST(201)/GET/PATCH/DELETE, X-Session-Id header auth - Scoring in analyze_parcel: custom POI block after OSM POI loop, decay weight * max(0, 1 - dist/1000), custom_poi_score_items in response - Tests: CRUD + validation (weight/lon/lat 422) + scoring integration (mock-based) - Vault: Schema_User_Custom_Pois.md + Endpoint_Parcel_Analyze v3.9 section
60 lines
2.2 KiB
Python
60 lines
2.2 KiB
Python
"""Pydantic schemas для user_custom_pois (#254).
|
||
|
||
CustomPoiCreate — тело POST-запроса.
|
||
CustomPoiUpdate — тело PATCH-запроса (все поля опциональны).
|
||
CustomPoiOut — ответ API (включает id, created_at, updated_at).
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from datetime import datetime
|
||
|
||
from pydantic import BaseModel, Field
|
||
|
||
# Диапазон весов кастомных POI шире системных ([-5, 5]),
|
||
# чтобы пользователь мог сильно усилить или исключить POI.
|
||
_CUSTOM_WEIGHT_MIN: float = -5.0
|
||
_CUSTOM_WEIGHT_MAX: float = 5.0
|
||
|
||
|
||
class CustomPoiCreate(BaseModel):
|
||
name: str = Field(..., min_length=1, max_length=200, description="Отображаемое название точки")
|
||
category: str | None = Field(None, max_length=100, description="Произвольная категория")
|
||
weight: float = Field(
|
||
...,
|
||
ge=_CUSTOM_WEIGHT_MIN,
|
||
le=_CUSTOM_WEIGHT_MAX,
|
||
description="Вес точки в scoring [-5, 5]",
|
||
)
|
||
lon: float = Field(..., ge=-180.0, le=180.0, description="Долгота WGS-84")
|
||
lat: float = Field(..., ge=-90.0, le=90.0, description="Широта WGS-84")
|
||
parcel_cad: str | None = Field(
|
||
None,
|
||
max_length=100,
|
||
description="Кадастровый номер участка; NULL = глобальная для пользователя",
|
||
)
|
||
notes: str | None = Field(None, max_length=2000, description="Примечания")
|
||
|
||
|
||
class CustomPoiUpdate(BaseModel):
|
||
name: str | None = Field(None, min_length=1, max_length=200)
|
||
category: str | None = None
|
||
weight: float | None = Field(None, ge=_CUSTOM_WEIGHT_MIN, le=_CUSTOM_WEIGHT_MAX)
|
||
lon: float | None = Field(None, ge=-180.0, le=180.0)
|
||
lat: float | None = Field(None, ge=-90.0, le=90.0)
|
||
parcel_cad: str | None = Field(None, max_length=100)
|
||
notes: str | None = None
|
||
|
||
|
||
class CustomPoiOut(BaseModel):
|
||
id: int
|
||
user_id: str
|
||
parcel_cad: str | None
|
||
name: str
|
||
category: str | None
|
||
weight: float
|
||
lon: float
|
||
lat: float
|
||
notes: str | None
|
||
created_at: datetime
|
||
updated_at: datetime
|