Files
eventlens/backend/app/models.py
T
2026-04-18 14:23:24 +02:00

169 lines
6.4 KiB
Python

from datetime import datetime
from enum import Enum
from sqlalchemy import Boolean, DateTime, Enum as SqlEnum, ForeignKey, Integer, JSON
from sqlalchemy import String, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base
class WatchType(str, Enum):
artist = "artist"
event = "event"
class RegionScope(str, Enum):
hamburg = "hamburg"
germany = "germany"
class NotificationType(str, Enum):
discovery = "discovery"
reminder = "reminder"
class NotificationStatus(str, Enum):
sent = "sent"
skipped = "skipped"
failed = "failed"
class ProviderStatusType(str, Enum):
ok = "ok"
blocked = "blocked"
error = "error"
class SourceStatusType(str, Enum):
pending = "pending"
ok = "ok"
no_match = "no_match"
error = "error"
class WatchItem(Base):
__tablename__ = "watch_items"
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
name: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
watch_type: Mapped[WatchType] = mapped_column(SqlEnum(WatchType), nullable=False)
region_scope: Mapped[RegionScope] = mapped_column(SqlEnum(RegionScope), nullable=False)
notes: Mapped[str | None] = mapped_column(Text, nullable=True)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
created_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow, nullable=False
)
updated_at: Mapped[datetime] = mapped_column(
DateTime,
default=datetime.utcnow,
onupdate=datetime.utcnow,
nullable=False,
)
tracked_events: Mapped[list["TrackedEvent"]] = relationship(
back_populates="watch_item", cascade="all, delete-orphan"
)
sources: Mapped[list["WatchSource"]] = relationship(
back_populates="watch_item", cascade="all, delete-orphan"
)
class WatchSource(Base):
__tablename__ = "watch_sources"
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
watch_item_id: Mapped[int] = mapped_column(ForeignKey("watch_items.id"), nullable=False)
label: Mapped[str | None] = mapped_column(String(255), nullable=True)
url: Mapped[str] = mapped_column(String(1024), nullable=False)
parser_type: Mapped[str] = mapped_column(String(50), default="auto", nullable=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
last_status: Mapped[SourceStatusType] = mapped_column(
SqlEnum(SourceStatusType), default=SourceStatusType.pending, nullable=False
)
last_message: Mapped[str | None] = mapped_column(Text, nullable=True)
last_checked_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
created_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow, nullable=False
)
updated_at: Mapped[datetime] = mapped_column(
DateTime,
default=datetime.utcnow,
onupdate=datetime.utcnow,
nullable=False,
)
watch_item: Mapped[WatchItem] = relationship(back_populates="sources")
class TrackedEvent(Base):
__tablename__ = "tracked_events"
__table_args__ = (
UniqueConstraint("watch_item_id", "source", "external_id", name="uq_watch_event"),
)
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
watch_item_id: Mapped[int] = mapped_column(ForeignKey("watch_items.id"), nullable=False)
source: Mapped[str] = mapped_column(String(50), nullable=False, default="ticketmaster")
external_id: Mapped[str] = mapped_column(String(255), nullable=False)
title: Mapped[str] = mapped_column(String(255), nullable=False)
matched_term: Mapped[str] = mapped_column(String(255), nullable=False)
venue_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
city: Mapped[str | None] = mapped_column(String(120), nullable=True)
country_code: Mapped[str | None] = mapped_column(String(10), nullable=True)
event_date: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
ticket_url: Mapped[str | None] = mapped_column(String(1024), nullable=True)
image_url: Mapped[str | None] = mapped_column(String(1024), nullable=True)
is_ticket_purchased: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
purchased_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
discovery_notified_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
reminder_notified_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
first_seen_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow, nullable=False
)
last_seen_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow, nullable=False
)
raw_payload: Mapped[dict | None] = mapped_column(JSON, nullable=True)
watch_item: Mapped[WatchItem] = relationship(back_populates="tracked_events")
notifications: Mapped[list["NotificationLog"]] = relationship(
back_populates="tracked_event", cascade="all, delete-orphan"
)
class NotificationLog(Base):
__tablename__ = "notification_logs"
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
tracked_event_id: Mapped[int] = mapped_column(
ForeignKey("tracked_events.id"), nullable=False
)
notification_type: Mapped[NotificationType] = mapped_column(
SqlEnum(NotificationType), nullable=False
)
status: Mapped[NotificationStatus] = mapped_column(
SqlEnum(NotificationStatus), nullable=False
)
message: Mapped[str] = mapped_column(Text, nullable=False)
created_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow, nullable=False
)
tracked_event: Mapped[TrackedEvent] = relationship(back_populates="notifications")
class ProviderStatus(Base):
__tablename__ = "provider_statuses"
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
provider_name: Mapped[str] = mapped_column(String(50), unique=True, nullable=False)
status: Mapped[ProviderStatusType] = mapped_column(
SqlEnum(ProviderStatusType), nullable=False
)
message: Mapped[str] = mapped_column(Text, nullable=False)
last_checked_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow, nullable=False
)
last_success_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)