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>
148 lines
5.7 KiB
Python
148 lines
5.7 KiB
Python
from datetime import datetime, timezone
|
|
|
|
from sqlalchemy import (
|
|
JSON,
|
|
Boolean,
|
|
DateTime,
|
|
Float,
|
|
ForeignKey,
|
|
Integer,
|
|
String,
|
|
Text,
|
|
UniqueConstraint,
|
|
)
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
|
|
from .database import Base
|
|
|
|
|
|
class MapObject(Base):
|
|
__tablename__ = "map_objects"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
|
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
description: Mapped[str] = mapped_column(Text, default="")
|
|
type: Mapped[str] = mapped_column(String(50), nullable=False)
|
|
latitude: Mapped[float] = mapped_column(Float, nullable=False)
|
|
longitude: Mapped[float] = mapped_column(Float, nullable=False)
|
|
event_id: Mapped[int | None] = mapped_column(
|
|
ForeignKey("events.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
index=True,
|
|
)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
default=lambda: datetime.now(timezone.utc),
|
|
)
|
|
|
|
media: Mapped[list["ObjectMedia"]] = relationship(
|
|
back_populates="object",
|
|
cascade="all, delete-orphan",
|
|
)
|
|
event: Mapped["Event | None"] = relationship(back_populates="map_object")
|
|
|
|
|
|
class ObjectMedia(Base):
|
|
__tablename__ = "object_media"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
|
object_id: Mapped[int] = mapped_column(
|
|
ForeignKey("map_objects.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
index=True,
|
|
)
|
|
stored_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
original_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
content_type: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
size: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
default=lambda: datetime.now(timezone.utc),
|
|
)
|
|
|
|
object: Mapped["MapObject"] = relationship(back_populates="media")
|
|
|
|
|
|
class Event(Base):
|
|
__tablename__ = "events"
|
|
__table_args__ = (UniqueConstraint("source_url", name="uq_events_source_url"),)
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
|
source_type: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
|
|
source_url: Mapped[str] = mapped_column(String(512), nullable=False)
|
|
raw_text: Mapped[str] = mapped_column(Text, default="")
|
|
title: Mapped[str] = mapped_column(String(512), default="")
|
|
description: Mapped[str] = mapped_column(Text, default="")
|
|
locality: Mapped[str] = mapped_column(String(255), default="")
|
|
latitude: Mapped[float | None] = mapped_column(Float, nullable=True)
|
|
longitude: Mapped[float | None] = mapped_column(Float, nullable=True)
|
|
event_date: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
ingested_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
default=lambda: datetime.now(timezone.utc),
|
|
index=True,
|
|
)
|
|
region: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
|
|
topic: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
|
|
tags: Mapped[list | None] = mapped_column(JSON, nullable=True)
|
|
metadata_: Mapped[dict | None] = mapped_column("metadata", JSON, nullable=True)
|
|
|
|
map_object: Mapped["MapObject | None"] = relationship(
|
|
back_populates="event",
|
|
uselist=False,
|
|
)
|
|
|
|
|
|
class ParseJob(Base):
|
|
__tablename__ = "parse_jobs"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
|
source_type: Mapped[str] = mapped_column(String(50), nullable=False)
|
|
source_config: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
|
|
schedule: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
|
interval_seconds: Mapped[int] = mapped_column(Integer, default=3600, nullable=False)
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
|
status: Mapped[str] = mapped_column(String(50), default="pending", index=True)
|
|
last_run_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
last_error: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
default=lambda: datetime.now(timezone.utc),
|
|
)
|
|
|
|
|
|
class Consumer(Base):
|
|
__tablename__ = "consumers"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
|
name: Mapped[str] = mapped_column(String(255), nullable=False, unique=True)
|
|
api_key_hash: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
default=lambda: datetime.now(timezone.utc),
|
|
)
|
|
|
|
filter: Mapped["ConsumerFilter | None"] = relationship(
|
|
back_populates="consumer",
|
|
uselist=False,
|
|
cascade="all, delete-orphan",
|
|
)
|
|
|
|
|
|
class ConsumerFilter(Base):
|
|
__tablename__ = "consumer_filters"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
|
consumer_id: Mapped[int] = mapped_column(
|
|
ForeignKey("consumers.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
unique=True,
|
|
)
|
|
regions: Mapped[list | None] = mapped_column(JSON, nullable=True)
|
|
topics: Mapped[list | None] = mapped_column(JSON, nullable=True)
|
|
min_access_level: Mapped[int] = mapped_column(Integer, default=0)
|
|
date_from: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
|
|
consumer: Mapped["Consumer"] = relationship(back_populates="filter")
|