Zlovoblachko's picture
Add application file
339f372
from datetime import datetime
from typing import Optional
from sqlalchemy import BigInteger, String, ForeignKey, Integer, DateTime, Boolean
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
from sqlalchemy.ext.asyncio import AsyncAttrs, async_sessionmaker, create_async_engine
from app.config.config import DATABASE_URL
engine = create_async_engine(DATABASE_URL) # подключение и создание БД
async_session = async_sessionmaker(engine, expire_on_commit=False)
class Base(AsyncAttrs, DeclarativeBase):
"""Base class for all models"""
pass
class User(Base):
"""User model for storing telegram user data"""
__tablename__ = 'users'
id: Mapped[int] = mapped_column(primary_key=True)
tg_id: Mapped[int] = mapped_column(BigInteger, unique=True, nullable=False)
name: Mapped[str] = mapped_column(String(100), nullable=True)
login: Mapped[str] = mapped_column(String(100), nullable=True)
contact: Mapped[str] = mapped_column(String(100), nullable=True)
subscription_status: Mapped[str] = mapped_column(
String(20), default='inactive')
# Relationships
test_attempts = relationship("TestAttempt", back_populates="user")
feedback = relationship("Feedback", back_populates="user")
class Service(Base):
"""Service model for storing available services"""
__tablename__ = 'services'
id: Mapped[int] = mapped_column(primary_key=True)
service_name: Mapped[str] = mapped_column(String(100), unique=True, nullable=False)
service_description: Mapped[str] = mapped_column(String(500))
service_price: Mapped[int] = mapped_column(BigInteger, nullable=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
# Relationships
feedback = relationship("Feedback", back_populates="service")
class Test(Base):
"""Test model for storing quiz/test information"""
__tablename__ = 'tests'
id: Mapped[int] = mapped_column(primary_key=True)
test_name: Mapped[str] = mapped_column(String(100), unique=True, nullable=False)
test_type: Mapped[str] = mapped_column(String(20), nullable=False)
test_description: Mapped[str] = mapped_column(String(250))
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
completion_message: Mapped[Optional[str]] = mapped_column(String(1000))
# Relationships
questions = relationship("TestQuestion", back_populates="test", cascade="all, delete-orphan")
results = relationship("TestResult", back_populates="test", cascade="all, delete-orphan")
attempts = relationship("TestAttempt", back_populates="test")
class TestQuestion(Base):
"""Model for storing test questions"""
__tablename__ = 'test_questions'
id: Mapped[int] = mapped_column(primary_key=True)
test_id: Mapped[int] = mapped_column(ForeignKey('tests.id', ondelete='CASCADE'), nullable=False)
question_content: Mapped[str] = mapped_column(String(150), nullable=False)
question_variants: Mapped[str] = mapped_column(String(500), nullable=False) # JSON string
question_points: Mapped[str] = mapped_column(String(100), nullable=False) # JSON string
# Relationships
test = relationship("Test", back_populates="questions")
class TestResult(Base):
"""Model for storing possible test results"""
__tablename__ = 'test_results'
id: Mapped[int] = mapped_column(primary_key=True)
test_id: Mapped[int] = mapped_column(ForeignKey('tests.id', ondelete='CASCADE'), nullable=False)
min_points: Mapped[int] = mapped_column(Integer, nullable=False)
max_points: Mapped[int] = mapped_column(Integer, nullable=False)
result_text: Mapped[str] = mapped_column(String(1000), nullable=False)
# Relationships
test = relationship("Test", back_populates="results")
class TestAttempt(Base):
"""Model for storing user test attempts"""
__tablename__ = 'test_attempts'
id: Mapped[int] = mapped_column(primary_key=True)
user_id: Mapped[int] = mapped_column(ForeignKey('users.id', ondelete='CASCADE'), nullable=False)
test_id: Mapped[int] = mapped_column(ForeignKey('tests.id', ondelete='CASCADE'), nullable=False)
score: Mapped[int] = mapped_column(Integer, nullable=True)
result: Mapped[Optional[str]] = mapped_column(String(1000), nullable=True)
completed_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.now, nullable=True)
# Relationships
user = relationship("User", back_populates="test_attempts")
test = relationship("Test", back_populates="attempts")
class TestAnswer(Base):
"""Stores individual answers for each question"""
__tablename__ = 'test_answers'
id: Mapped[int] = mapped_column(primary_key=True)
attempt_id: Mapped[int] = mapped_column(ForeignKey('test_attempts.id', ondelete='CASCADE'))
question_id: Mapped[int] = mapped_column(ForeignKey('test_questions.id', ondelete='CASCADE'))
answer_given: Mapped[str] = mapped_column(String(500))
points_earned: Mapped[int] = mapped_column(Integer, nullable=True)
class LeadMagnet(Base):
"""Model for storing lead magnets/content triggers"""
__tablename__ = 'lead_magnets'
id: Mapped[int] = mapped_column(primary_key=True)
trigger: Mapped[str] = mapped_column(String(50), unique=True, nullable=False)
content: Mapped[str] = mapped_column(String(500), nullable=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
class Feedback(Base):
"""Model for storing user feedback"""
__tablename__ = 'feedback'
id: Mapped[int] = mapped_column(primary_key=True)
user_id: Mapped[int] = mapped_column(ForeignKey('users.id', ondelete='CASCADE'), nullable=False)
service_id: Mapped[int] = mapped_column(ForeignKey('services.id', ondelete='CASCADE'), nullable=False)
rating: Mapped[int] = mapped_column(Integer, nullable=False)
review: Mapped[str] = mapped_column(String(1000))
is_new: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
# Relationships
user = relationship("User", back_populates="feedback")
service = relationship("Service", back_populates="feedback")
async def async_main():
"""Initialize database tables"""
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)