from __future__ import annotations from datetime import datetime from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship from sqlalchemy.sql import func from sqlalchemy import String, ForeignKey, BigInteger, UniqueConstraint, Text, DateTime from sqlalchemy.dialects.postgresql import ARRAY import enum from typing import List, Optional class Base(DeclarativeBase): pass class Sessions(Base): __tablename__ = "sessions" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) code: Mapped[str] = mapped_column(String(8), unique=True, nullable=False) patient_id: Mapped[int] = mapped_column( ForeignKey("patients.id", ondelete="CASCADE"), nullable=False) patient: Mapped["Patients"] = relationship(back_populates="sessions") sent_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) consulted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True) doctor_id: Mapped[int] = mapped_column(ForeignKey("doctors.id")) session_status_history: Mapped[List["SessionStatusHistory"]] = relationship( back_populates="sessions", cascade="all, delete-orphan") session_date_time_history: Mapped[List["SessionDateTimeHistory"]] = relationship( back_populates="sessions", cascade="all, delete-orphan") paid_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True) class SessionDateTimeHistory(Base): __tablename__ = "session_date_time_history" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) sessions_id: Mapped[int] = mapped_column(ForeignKey("sessions.id", ondelete="CASCADE"), nullable=False) sessions: Mapped["Sessions"] = relationship(back_populates="session_date_time_history") updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) consultation_date_time: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True) who_updated: Mapped[Optional[str]] = mapped_column(String(50), nullable=True) class SessionStatusHistory(Base): __tablename__ = "session_status_history" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) sessions_id: Mapped[int] = mapped_column(ForeignKey("sessions.id", ondelete="CASCADE"), nullable=False) sessions: Mapped["Sessions"] = relationship(back_populates="session_status_history") updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) status: Mapped[str] = mapped_column(String(50), nullable=False, default="pending") who_updated: Mapped[Optional[str]] = mapped_column(String(50), nullable=True) class Admins(Base): __tablename__ = "admins" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) telegram_id: Mapped[int] = mapped_column(BigInteger, unique=True, nullable=False) created_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True) available_payment_methods: Mapped[Optional[List[str]]] = mapped_column( ARRAY(String), nullable=True) class Patients(Base): __tablename__ = "patients" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) telegram_id: Mapped[int] = mapped_column(BigInteger, unique=True, nullable=False) phone: Mapped[Optional[str]] = mapped_column(nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True) accepted_terms: Mapped[bool] = mapped_column(default=False, nullable=True) sessions: Mapped[List["Sessions"]] = relationship( back_populates="patient", cascade="all, delete-orphan") class Doctors(Base): __tablename__ = "doctors" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) code: Mapped[str] = mapped_column(unique=True, nullable=True) time_zone: Mapped[Optional[str]] = mapped_column(nullable=True) telegram_id: Mapped[int] = mapped_column(BigInteger, unique=True, nullable=False) name: Mapped[str] = mapped_column(nullable=False) available_formats: Mapped[Optional[List[str]] ] = mapped_column(ARRAY(String), nullable=True) is_active: Mapped[bool] = mapped_column(default=False, nullable=False) is_verified: Mapped[bool] = mapped_column(default=False, nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) referral_code_id: Mapped[int] = mapped_column( ForeignKey("referral_codes.id"), nullable=False) specialties: Mapped[List[str]] = mapped_column( ARRAY(String), nullable=False, default=list) referral: Mapped["ReferralCode"] = relationship( back_populates="doctor", uselist=False) sessions: Mapped[List["Sessions"]] = relationship() form_links: Mapped[List["FormLink"]] = relationship( back_populates="doctor", cascade="all, delete-orphan") payment_methods: Mapped[List["PaymentMethod"]] = relationship( back_populates="doctor", cascade="all, delete-orphan") verification_requests: Mapped[List["VerificationRequests"]] = relationship( back_populates="doctor", cascade="all, delete-orphan") def __repr__(self) -> str: return f"" class ReferralCode(Base): __tablename__ = "referral_codes" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) code: Mapped[str] = mapped_column(unique=True, nullable=False) is_used: Mapped[bool] = mapped_column(default=False, nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) used_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True) doctor: Mapped[Optional["Doctors"]] = relationship( back_populates="referral", uselist=False) def __repr__(self) -> str: return f"" class VerificationRequests(Base): __tablename__ = "verification_requests" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) doctor_id: Mapped[int] = mapped_column( ForeignKey("doctors.id", ondelete="CASCADE"), unique=True, nullable=False) code: Mapped[str] = mapped_column(unique=True, nullable=False) sent_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) reviewed_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True) status: Mapped[str] = mapped_column(default=False, nullable=False) # Связь поправлена — связь с doctor через doctor_id doctor: Mapped["Doctors"] = relationship( back_populates="verification_requests") def __repr__(self) -> str: return f"" class FormLink(Base): __tablename__ = "form_links" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) doctor_id: Mapped[int] = mapped_column( ForeignKey("doctors.id", ondelete="CASCADE"), unique=True, nullable=False) url: Mapped[str] = mapped_column(nullable=False) label: Mapped[Optional[str]] = mapped_column(nullable=True) is_active: Mapped[bool] = mapped_column(default=True, nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) doctor: Mapped["Doctors"] = relationship(back_populates="form_links") class PaymentMethod(Base): __tablename__ = "payment_methods" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) doctor_id: Mapped[int] = mapped_column( ForeignKey("doctors.id", ondelete="CASCADE"), unique=True, nullable=False) method: Mapped[str] = mapped_column(nullable=False) details: Mapped[Optional[str]] = mapped_column(nullable=True) payment_api_key: Mapped[str] = mapped_column(nullable=False) is_active: Mapped[bool] = mapped_column(default=True, nullable=False) is_primary: Mapped[bool] = mapped_column(default=True, nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) doctor: Mapped["Doctors"] = relationship(back_populates="payment_methods") class PaymentsRegistered(Base): __tablename__ = "payments_registered" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) code: Mapped[str] = mapped_column(String(8), unique=True, nullable=False) mapped: Mapped[bool] = mapped_column(nullable=True) class NotificationType(enum.Enum): PAYMENT_RECEIVED = "payment_received" FIRST_REMINDER_SENT = "first_reminder_sent" class SessionNotification(Base): __tablename__ = "session_notifications" __table_args__ = ( UniqueConstraint("session_id", "type", name="uq_session_notification_once"), ) id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) session_id: Mapped[int] = mapped_column(ForeignKey("sessions.id", ondelete="CASCADE"), nullable=False) patient_id: Mapped[int] = mapped_column(ForeignKey("patients.id", ondelete="CASCADE"), nullable=False) type: Mapped[NotificationType] = mapped_column(nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) sent_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True) last_error: Mapped[Optional[str]] = mapped_column(Text, nullable=True)