-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprogress_service.py
More file actions
154 lines (122 loc) · 4.85 KB
/
progress_service.py
File metadata and controls
154 lines (122 loc) · 4.85 KB
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
from datetime import datetime, timedelta, date
from typing import List
import pytz
from app import db
from app.models.user import User
from app.models.user_addiction import UserAddiction
from app.models.daily_log import DailyLog
from app.dto.progress_dto import ProgressResponseDTO, DayEntryDTO
from app.schemas.progress_schema import ProgressResponseSchema
class ProgressService:
# Polish day abbreviations mapping
DAY_ABBREVIATIONS = {
0: 'pon', # Monday
1: 'wt', # Tuesday
2: 'śr', # Wednesday
3: 'czw', # Thursday
4: 'pt', # Friday
5: 'sob', # Saturday
6: 'nd' # Sunday
}
@staticmethod
def get_user_progress_data(username: str, timezone: str = 'Europe/Warsaw') -> ProgressResponseDTO:
"""
Get progress data for a specific user.
Args:
username (str): The username
timezone (str): Timezone for date calculations (default: Europe/Warsaw)
Returns:
ProgressResponseDTO: Progress data
Raises:
Exception: If user or user addiction not found, or other errors
"""
try:
# Get user
user = User.query.filter_by(name=username).first()
if not user:
raise Exception(f"User '{username}' not found")
# Get user's addiction (assuming one active addiction per user)
user_addiction = UserAddiction.query.filter_by(user_id=user.id).first()
if not user_addiction:
raise Exception(f"No addiction found for user '{username}'")
# Get timezone
tz = pytz.timezone(timezone)
# Calculate components
addiction_name = user_addiction.addiction.name
single_day_cost = user_addiction.cost_per_day or 0.0
total_savings = ProgressService._calculate_total_savings(user_addiction)
entries = ProgressService._get_all_entries(user_addiction, tz)
return ProgressResponseDTO(
addiction_name=addiction_name,
single_day_cost=single_day_cost,
total_savings=total_savings,
entries=entries
)
except Exception as e:
raise Exception(f"Progress service error: {str(e)}")
@staticmethod
def _calculate_total_savings(user_addiction: UserAddiction) -> float:
"""
Calculate total savings based on successful days and daily cost.
Args:
user_addiction: UserAddiction instance
Returns:
float: Total savings in PLN
"""
if not user_addiction.cost_per_day:
return 0.0
# Count successful days (relapse = 0)
successful_days = db.session.query(DailyLog).filter(
DailyLog.users_addiction == user_addiction.id,
DailyLog.relapse == 0
).count()
return successful_days * user_addiction.cost_per_day
@staticmethod
def _get_all_entries(user_addiction: UserAddiction, tz: pytz.timezone) -> List[DayEntryDTO]:
"""
Get all day entries from start date to today.
Args:
user_addiction: UserAddiction instance
tz: Timezone for date calculations
Returns:
List[DayEntryDTO]: List of all day entries (newest first)
"""
today = datetime.now(tz).date()
# start_date = datetime.fromtimestamp(user_addiction.start_date, tz).date()
entries = []
# Calculate all days from start to today
current_date = today
while current_date >= date(2025, 5, 1):
date_str = current_date.strftime('%Y-%m-%d')
# Get day of week abbreviation
day_of_week = ProgressService.DAY_ABBREVIATIONS[current_date.weekday()]
# Find daily log for this date
daily_log = DailyLog.query.filter(
DailyLog.users_addiction == user_addiction.id,
DailyLog.date == date_str
).first()
# Determine status
if daily_log is None:
status = 'none'
elif daily_log.relapse == 0:
status = 'success'
else: # relapse == 1
status = 'failure'
entries.append(DayEntryDTO(
date=date_str,
day_of_week=day_of_week,
status=status
))
current_date -= timedelta(days=1)
return entries
@staticmethod
def serialize_progress_response(progress_dto: ProgressResponseDTO) -> dict:
"""
Serialize progress DTO using Marshmallow schema.
Args:
progress_dto: ProgressResponseDTO to serialize
Returns:
dict: Serialized progress data ready for JSON response
"""
schema = ProgressResponseSchema()
return schema.dump(progress_dto.to_dict())