ui update and some backend functionality adding in accordance with research on adhd and ux design
This commit is contained in:
@@ -8,6 +8,7 @@ that can be called from API routes, bot commands, or scheduler.
|
||||
import uuid
|
||||
from datetime import datetime, date
|
||||
import core.postgres as postgres
|
||||
import core.tz as tz
|
||||
|
||||
|
||||
def start_session(routine_id, user_uuid):
|
||||
@@ -159,9 +160,13 @@ def clone_template(template_id, user_uuid):
|
||||
return routine
|
||||
|
||||
|
||||
STREAK_MILESTONES = {3, 7, 14, 21, 30, 60, 90, 100, 365}
|
||||
|
||||
|
||||
def _update_streak(user_uuid, routine_id):
|
||||
"""Update streak after completing a session. Resets if day was missed."""
|
||||
today = date.today()
|
||||
"""Update streak after completing a session. Resets if day was missed.
|
||||
Returns the updated streak dict with optional 'milestone' key."""
|
||||
today = tz.user_today()
|
||||
|
||||
streak = postgres.select_one(
|
||||
"routine_streaks",
|
||||
@@ -169,6 +174,7 @@ def _update_streak(user_uuid, routine_id):
|
||||
)
|
||||
|
||||
if not streak:
|
||||
new_streak_val = 1
|
||||
new_streak = {
|
||||
"id": str(uuid.uuid4()),
|
||||
"user_uuid": user_uuid,
|
||||
@@ -177,7 +183,10 @@ def _update_streak(user_uuid, routine_id):
|
||||
"longest_streak": 1,
|
||||
"last_completed_date": today.isoformat(),
|
||||
}
|
||||
return postgres.insert("routine_streaks", new_streak)
|
||||
result = postgres.insert("routine_streaks", new_streak)
|
||||
if new_streak_val in STREAK_MILESTONES:
|
||||
result["milestone"] = new_streak_val
|
||||
return result
|
||||
|
||||
last_completed = streak.get("last_completed_date")
|
||||
if last_completed:
|
||||
@@ -187,24 +196,28 @@ def _update_streak(user_uuid, routine_id):
|
||||
if days_diff == 0:
|
||||
return streak
|
||||
elif days_diff == 1:
|
||||
new_streak = streak["current_streak"] + 1
|
||||
new_streak_val = streak["current_streak"] + 1
|
||||
else:
|
||||
new_streak = 1
|
||||
new_streak_val = 1
|
||||
else:
|
||||
new_streak = 1
|
||||
new_streak_val = 1
|
||||
|
||||
longest = max(streak["longest_streak"], new_streak)
|
||||
longest = max(streak["longest_streak"], new_streak_val)
|
||||
|
||||
postgres.update(
|
||||
"routine_streaks",
|
||||
{
|
||||
"current_streak": new_streak,
|
||||
"current_streak": new_streak_val,
|
||||
"longest_streak": longest,
|
||||
"last_completed_date": today.isoformat(),
|
||||
},
|
||||
{"id": streak["id"]}
|
||||
)
|
||||
return streak
|
||||
|
||||
result = {**streak, "current_streak": new_streak_val, "longest_streak": longest}
|
||||
if new_streak_val in STREAK_MILESTONES:
|
||||
result["milestone"] = new_streak_val
|
||||
return result
|
||||
|
||||
|
||||
def calculate_streak(user_uuid, routine_id):
|
||||
@@ -217,8 +230,14 @@ def calculate_streak(user_uuid, routine_id):
|
||||
|
||||
|
||||
def get_active_session(user_uuid):
|
||||
"""Get user's currently active session."""
|
||||
return postgres.select_one(
|
||||
"""Get user's currently active or paused session."""
|
||||
session = postgres.select_one(
|
||||
"routine_sessions",
|
||||
{"user_uuid": user_uuid, "status": "active"}
|
||||
)
|
||||
if not session:
|
||||
session = postgres.select_one(
|
||||
"routine_sessions",
|
||||
{"user_uuid": user_uuid, "status": "paused"}
|
||||
)
|
||||
return session
|
||||
|
||||
39
core/tz.py
Normal file
39
core/tz.py
Normal file
@@ -0,0 +1,39 @@
|
||||
"""
|
||||
core/tz.py - Timezone-aware date/time helpers
|
||||
|
||||
The frontend sends X-Timezone-Offset (minutes from UTC, same sign as
|
||||
JavaScript's getTimezoneOffset — positive means behind UTC).
|
||||
These helpers convert server UTC to the user's local date/time.
|
||||
"""
|
||||
|
||||
from datetime import datetime, date, timezone, timedelta
|
||||
|
||||
|
||||
def _get_offset_minutes():
|
||||
"""Read the timezone offset header from the current Flask request.
|
||||
Returns 0 if outside a request context or header is absent."""
|
||||
try:
|
||||
import flask
|
||||
return int(flask.request.headers.get("X-Timezone-Offset", 0))
|
||||
except (ValueError, TypeError, RuntimeError):
|
||||
# RuntimeError: outside of request context
|
||||
return 0
|
||||
|
||||
|
||||
def _offset_to_tz(offset_minutes):
|
||||
"""Convert JS-style offset (positive = behind UTC) to a timezone object."""
|
||||
return timezone(timedelta(minutes=-offset_minutes))
|
||||
|
||||
|
||||
def user_now(offset_minutes=None):
|
||||
"""Current datetime in the user's timezone.
|
||||
If offset_minutes is provided, uses that instead of the request header."""
|
||||
if offset_minutes is None:
|
||||
offset_minutes = _get_offset_minutes()
|
||||
tz = _offset_to_tz(offset_minutes)
|
||||
return datetime.now(tz)
|
||||
|
||||
|
||||
def user_today(offset_minutes=None):
|
||||
"""Current date in the user's timezone."""
|
||||
return user_now(offset_minutes).date()
|
||||
Reference in New Issue
Block a user