Add filtered map API, creamy-caprice-style toolbar (dates, layers, search), Yandex ru tiles, and store Telegram session in MapMil/data instead of mounting SocialParser. Co-authored-by: Cursor <cursoragent@cursor.com>
192 lines
4.3 KiB
Python
192 lines
4.3 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)
|
|
|
|
|
|
class IngestResponse(BaseModel):
|
|
ingested: int
|
|
updated: int
|
|
map_objects_synced: int
|
|
|
|
|
|
class ParseJobCreate(BaseModel):
|
|
source_type: str = "telegram"
|
|
source_config: dict[str, Any] = Field(default_factory=dict)
|
|
schedule: str | None = None
|
|
|
|
|
|
class ParseJobRead(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: int
|
|
source_type: str
|
|
source_config: dict[str, Any]
|
|
schedule: str | None
|
|
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
|