File size: 6,481 Bytes
339f372
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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)