106 lines
3.3 KiB
Python
106 lines
3.3 KiB
Python
"""
|
|
Database models for users, settings, and reports
|
|
"""
|
|
import uuid
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
from sqlalchemy import String, Text, DateTime, ForeignKey, JSON
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
from sqlalchemy.dialects.postgresql import UUID
|
|
|
|
from .database import Base
|
|
|
|
|
|
class User(Base):
|
|
"""User model for storing Google OAuth users"""
|
|
__tablename__ = "users"
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True),
|
|
primary_key=True,
|
|
default=uuid.uuid4
|
|
)
|
|
google_id: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
|
|
email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
|
|
name: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
|
avatar_url: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime,
|
|
default=datetime.utcnow,
|
|
nullable=False
|
|
)
|
|
last_login_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
|
|
|
|
# Relationships
|
|
settings: Mapped[Optional["UserSettings"]] = relationship(
|
|
"UserSettings",
|
|
back_populates="user",
|
|
uselist=False,
|
|
cascade="all, delete-orphan"
|
|
)
|
|
reports: Mapped[list["Report"]] = relationship(
|
|
"Report",
|
|
back_populates="user",
|
|
cascade="all, delete-orphan"
|
|
)
|
|
|
|
|
|
class UserSettings(Base):
|
|
"""User settings (encrypted API keys, custom base URLs)"""
|
|
__tablename__ = "user_settings"
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True),
|
|
primary_key=True,
|
|
default=uuid.uuid4
|
|
)
|
|
user_id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True),
|
|
ForeignKey("users.id", ondelete="CASCADE"),
|
|
unique=True,
|
|
nullable=False
|
|
)
|
|
# Store settings as encrypted JSON string
|
|
encrypted_settings: Mapped[str] = mapped_column(Text, nullable=False)
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
DateTime,
|
|
default=datetime.utcnow,
|
|
onupdate=datetime.utcnow,
|
|
nullable=False
|
|
)
|
|
|
|
# Relationship
|
|
user: Mapped["User"] = relationship("User", back_populates="settings")
|
|
|
|
|
|
class Report(Base):
|
|
"""Analysis report storage"""
|
|
__tablename__ = "reports"
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True),
|
|
primary_key=True,
|
|
default=uuid.uuid4
|
|
)
|
|
user_id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True),
|
|
ForeignKey("users.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
index=True
|
|
)
|
|
ticker: Mapped[str] = mapped_column(String(20), nullable=False)
|
|
market_type: Mapped[str] = mapped_column(String(10), nullable=False) # us, twse, tpex
|
|
analysis_date: Mapped[str] = mapped_column(String(10), nullable=False) # YYYY-MM-DD
|
|
# Store full result as JSONB
|
|
result: Mapped[dict] = mapped_column(JSON, nullable=False)
|
|
language: Mapped[Optional[str]] = mapped_column(String(10), nullable=True)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime,
|
|
default=datetime.utcnow,
|
|
nullable=False,
|
|
index=True
|
|
)
|
|
|
|
# Relationship
|
|
user: Mapped["User"] = relationship("User", back_populates="reports")
|