Files
MapMil/centers/analytics/api/app/schemas.py
T
gitrusprusandCursor 1492576fd9 Add Telegram listener, scheduler, and extended parsers admin UI.
Introduce background scheduling and migrations for analytics ingest, expand parser management and map toolbar UX, and ignore local data/session files from version control.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 20:47:56 +03:00

209 lines
4.8 KiB
Python

from datetime import datetime
from typing import Any, Literal
from pydantic import BaseModel, ConfigDict, Field
ObjectType = Literal["point", "marker", "zone", "other"]
class MapObjectCreate(BaseModel):
name: str = Field(min_length=1, max_length=255)
description: str = ""
type: ObjectType
latitude: float = Field(ge=-90, le=90)
longitude: float = Field(ge=-180, le=180)
created_at: datetime | None = None
class MapObjectRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
name: str
description: str
type: ObjectType
latitude: float
longitude: float
event_id: int | None = None
created_at: datetime
class MapObjectWithEvent(MapObjectRead):
event_date: datetime | None = None
locality: str | None = None
region: str | None = None
topic: str | None = None
source_type: str | None = None
source_url: str | None = None
title: str | None = None
class MapDateBounds(BaseModel):
min: str | None = None
max: str | None = None
class MapFiltersRead(BaseModel):
regions: list[str]
topics: list[str]
source_types: list[str]
available_dates: list[str]
date_bounds: MapDateBounds
class MapObjectUpdate(BaseModel):
name: str | None = Field(default=None, min_length=1, max_length=255)
description: str | None = None
type: ObjectType | None = None
latitude: float | None = Field(default=None, ge=-90, le=90)
longitude: float | None = Field(default=None, ge=-180, le=180)
created_at: datetime | None = None
class ObjectMediaRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
object_id: int
original_name: str
content_type: str
size: int
created_at: datetime
url: str
class EventRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
source_type: str
source_url: str
raw_text: str
title: str
description: str
locality: str
latitude: float | None
longitude: float | None
event_date: datetime | None
ingested_at: datetime
region: str | None
topic: str | None
tags: list[str] | None
metadata: dict[str, Any] | None = Field(validation_alias="metadata_")
class IngestEventItem(BaseModel):
source_type: str = "telegram"
source_url: str
raw_text: str = ""
title: str = ""
description: str = ""
locality: str = ""
latitude: float | None = None
longitude: float | None = None
event_date: datetime | None = None
region: str | None = None
topic: str | None = None
tags: list[str] | None = None
metadata: dict[str, Any] | None = None
class IngestRequest(BaseModel):
job_id: int | None = None
events: list[IngestEventItem] = Field(default_factory=list)
listener: bool = False
class ListenerSubscription(BaseModel):
job_id: int
channel: str
class IngestResponse(BaseModel):
ingested: int
updated: int
skipped: int = 0
map_objects_synced: int
class ParseJobCreate(BaseModel):
source_type: str = "telegram"
source_config: dict[str, Any] = Field(default_factory=dict)
schedule: str | None = None
interval_seconds: int = Field(default=3600, ge=60, le=604800)
is_active: bool = True
class ParseJobUpdate(BaseModel):
source_config: dict[str, Any] | None = None
interval_seconds: int | None = Field(default=None, ge=60, le=604800)
is_active: bool | None = None
class ParseJobRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
source_type: str
source_config: dict[str, Any]
schedule: str | None
interval_seconds: int
is_active: bool
status: str
last_run_at: datetime | None
last_error: str | None
created_at: datetime
class ConsumerCreate(BaseModel):
name: str
regions: list[str] | None = None
topics: list[str] | None = None
date_from: datetime | None = None
class ConsumerUpdate(BaseModel):
name: str | None = None
is_active: bool | None = None
regions: list[str] | None = None
topics: list[str] | None = None
date_from: datetime | None = None
class ConsumerRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
name: str
is_active: bool
created_at: datetime
api_key: str | None = None
regions: list[str] | None = None
topics: list[str] | None = None
date_from: datetime | None = None
class EventListResponse(BaseModel):
items: list[EventRead]
total: int
class AnalyticsSummary(BaseModel):
total_events: int
events_with_coords: int
events_last_24h: int
total_consumers: int
active_consumers: int
total_jobs: int
pending_jobs: int
class TimelinePoint(BaseModel):
date: str
count: int
class TopItem(BaseModel):
name: str
count: int