File size: 6,108 Bytes
c91455c
 
46b0269
4361d77
 
 
77b8d27
4361d77
 
 
 
 
 
 
 
77b8d27
 
 
 
4361d77
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46b0269
 
 
 
 
 
 
8de4860
 
 
 
 
4361d77
 
 
 
 
 
 
 
 
 
 
46b0269
c91455c
46b0269
4361d77
dd482d8
4361d77
4dbb8ce
4361d77
cef37ce
4361d77
 
 
 
 
 
 
 
 
 
 
 
 
c91455c
4361d77
 
 
 
2ededba
4361d77
 
 
 
 
 
 
3780bca
4361d77
 
 
 
 
3988090
 
 
 
4361d77
 
 
 
 
 
 
 
 
cc92d5a
c91455c
46b0269
 
4361d77
77b8d27
 
 
cb86629
 
 
4361d77
3988090
cef37ce
c592c1e
4361d77
29a8d28
 
77b8d27
 
 
29a8d28
 
77b8d27
 
 
 
 
 
 
 
29a8d28
 
77b8d27
 
 
 
 
 
 
 
 
c592c1e
46b0269
77b8d27
c91455c
46b0269
77b8d27
 
 
 
 
 
 
 
 
 
3780bca
c592c1e
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
from datetime import datetime, timezone, time, date as dt_date
from sqlmodel import SQLModel, Field, Relationship
from sqlalchemy import Column, DateTime, text, Numeric, func
from enum import Enum
from sqlalchemy.dialects.postgresql import JSONB
from typing import Optional
from decimal import Decimal

class ActivityCategory(str, Enum):
    prayer = "prayer"
    meal = "meal"
    habit = "habit"
    exercise = "exercise"
    productivity = "productivity"

class TransactionCategory(str, Enum):
    INCOME = "income"
    EXPENSE = "expense"

class User(SQLModel, table=True):
    __tablename__ = "users"

    id: int = Field(default=None, primary_key=True)

    phone_number: str = Field(index=True, unique=True)
    email: str = Field(index=True, unique=True)
    hashed_password: str
    name: Optional[str] = None

    timezone: str = "Asia/Karachi"

    created_at: datetime = Field(
        default_factory=lambda: datetime.now(timezone.utc),
        sa_column=Column(DateTime(timezone=True), server_default=text("CURRENT_TIMESTAMP"), nullable=False)
    )
    updated_at: datetime = Field(
    default_factory=lambda: datetime.now(timezone.utc),
    sa_column=Column(
        DateTime(timezone=True), 
        server_default=func.now(), 
        onupdate=func.now(), # SQLAlchemy will now handle this dynamically
        nullable=False
    ))
    activity_entries: list["ActivityEntry"] = Relationship(back_populates="user")
    goals: list["Goal"] = Relationship(back_populates="user")
    day_logs: list["DayLog"] = Relationship(back_populates="user")
    wallet: "Wallet" = Relationship(back_populates="user")
    transactions: list["Transaction"] = Relationship(back_populates="user")

class Goal(SQLModel, table=True):
    __tablename__ = "goals"

    id: int = Field(default=None, primary_key=True)

    user_id: int = Field(
        foreign_key="users.id",
        index=True
    )


    date: dt_date = Field(default_factory=dt_date.today, index=True)
    title: str = Field(index=True)
    description: Optional[str] = None
    total_productivity_hours: Optional[float] = Field(default=0.0)

    active_status: Optional[bool] = Field(default=None)

    activity_entries: list["ActivityEntry"] = Relationship(back_populates="goal")
    user: "User" = Relationship(back_populates="goals")


class DayLog(SQLModel, table=True):
    __tablename__ = "day_logs"

    id: int = Field(default=None, primary_key=True)

    user_id: int = Field(
        foreign_key="users.id",
        index=True
    )

    date: dt_date = Field(default_factory=dt_date.today, index=True)
    bed_time: Optional[time] = None
    sleep_time: Optional[time] = None
    wake_time: Optional[time] = None
    sleep_duration_hours: Optional[float] = None
    total_productivity_min: Optional[float] = Field(default=0.0)
    total_calories: Optional[int] = None
    notes: Optional[str] = None

    user: "User" = Relationship(back_populates="day_logs")
    activity_entries: list["ActivityEntry"] = Relationship(
        back_populates="day_log"
    )
    transactions: list["Transaction"] = Relationship(back_populates="day_log")

class ActivityEntry(SQLModel, table=True):
    __tablename__ = "activity_entries"

    id: int = Field(default=None, primary_key=True)
    user_id: int = Field(
        foreign_key="users.id",
        index=True
    )
    day_log_id: int = Field(
        foreign_key="day_logs.id",
        index=True
    )
    goal_id: Optional[int] = Field(
        default=None,
        foreign_key="goals.id",
        index=True
    )
    goal_name: Optional[str] = None
    date: dt_date = Field(default_factory=dt_date.today, index=True)
    category: ActivityCategory = Field(index=True)
    activity_type_name: str = Field(index=True)
    note: Optional[str] = None

    #for dynamic fields like location, quantity_text, what_i_did, result_text, jamaat,
    #sets, reps, on_time, duration_min, occurred_at, active_status etc.
    details: dict = Field(
        default_factory=dict,
        sa_column=Column(JSONB, server_default='{}')
    )
    user: "User" = Relationship(back_populates="activity_entries")
    goal: Optional["Goal"] = Relationship(back_populates="activity_entries")
    day_log: "DayLog" = Relationship(
        back_populates="activity_entries"
    )

class Wallet(SQLModel, table=True):
    """Stores the running total/balance for the user."""
    __tablename__ = "wallets"

    id: int = Field(default=None, primary_key=True)
    user_id: int = Field(foreign_key="users.id", index=True, unique=True)
    
    # Using Numeric/Decimal for precise money math
    balance: Decimal = Field(default=Decimal("0.00"), sa_column=Column(Numeric(10, 2)))
    
    updated_at: datetime = Field(
        default_factory=lambda: datetime.now(timezone.utc),
        sa_column=Column(DateTime(timezone=True), server_default=text("CURRENT_TIMESTAMP"), onupdate=text("CURRENT_TIMESTAMP"))
    )

    user: "User" = Relationship(back_populates="wallet")
    transactions: list["Transaction"] = Relationship(back_populates="wallet")

class Transaction(SQLModel, table=True):
    """Records every addition (income) and subtraction (expense)."""
    __tablename__ = "transactions"

    id: int = Field(default=None, primary_key=True)
    user_id: int = Field(foreign_key="users.id", index=True) 
    day_log_id: int = Field(foreign_key="day_logs.id", index=True)
    wallet_id: int = Field(foreign_key="wallets.id", index=True)

    date: dt_date = Field(default_factory=dt_date.today, index=True)
    category: TransactionCategory = Field(index=True)
    amount: Decimal = Field(sa_column=Column(Numeric(10, 2)))
    note: Optional[str] = None
    
    # JSONB for the dynamic fields (source, type, litre, dayMode, what_eat)
    details: dict = Field(
        default_factory=dict,
        sa_column=Column(JSONB, server_default='{}')
    )
    # ADDED: Relationships to map back to your other tables
    user: "User" = Relationship(back_populates="transactions")
    day_log: "DayLog" = Relationship(back_populates="transactions")
    wallet: "Wallet" = Relationship(back_populates="transactions")