22from typing import Any
33
44from sqlmodel import Session , select
5-
65from app .core .security import get_password_hash , verify_password
76from app .models import Item , ItemCreate , User , UserCreate , UserUpdate
7+ from app .models import Workout , Exercise , PersonalBest , PersonalBestCreate
8+ from datetime import date
89
10+ # Define metric keys for exercises we want to track personal bests for
11+ TRACKED_EXERCISES = {
12+ "bench press" : "bench-press" ,
13+ "squat" : "squat" ,
14+ "deadlift" : "deadlift" ,
15+ "push-ups" : "pushups" ,
16+ "pull-ups" : "pullups" ,
17+ # extend as needed
18+ }
919
1020def create_user (* , session : Session , user_create : UserCreate ) -> User :
1121 db_obj = User .model_validate (
@@ -52,3 +62,71 @@ def create_item(*, session: Session, item_in: ItemCreate, owner_id: uuid.UUID) -
5262 session .commit ()
5363 session .refresh (db_item )
5464 return db_item
65+
66+
67+
68+ #TODO test, create_or_update_personal_best will upsert only if the new value is strictly better.
69+ # Two getters: one for all metrics, one for a single metric.
70+
71+ def create_or_update_personal_best (
72+ * , session : Session , user_id : uuid .UUID , pb_in : PersonalBestCreate
73+ ) -> PersonalBest :
74+ # see if user already has a PB on this metric
75+ stmt = select (PersonalBest ).where (
76+ PersonalBest .user_id == user_id ,
77+ PersonalBest .metric == pb_in .metric
78+ )
79+ existing = session .exec (stmt ).one_or_none ()
80+
81+ # update if new value is "better" (you define the logic per metric)
82+ if existing :
83+ if pb_in .value > existing .value :
84+ existing .value = pb_in .value
85+ existing .date = pb_in .date
86+ session .add (existing )
87+ else :
88+ existing = PersonalBest .model_validate (pb_in , update = {"user_id" : user_id })
89+ session .add (existing )
90+
91+ session .commit ()
92+ session .refresh (existing )
93+ return existing
94+
95+ def get_personal_bests (
96+ * , session : Session , user_id : uuid .UUID
97+ ) -> list [PersonalBest ]:
98+ stmt = select (PersonalBest ).where (PersonalBest .user_id == user_id )
99+ return session .exec (stmt ).all ()
100+
101+ def get_personal_best (
102+ * , session : Session , user_id : uuid .UUID , metric : str
103+ ) -> PersonalBest | None :
104+ stmt = select (PersonalBest ).where (
105+ PersonalBest .user_id == user_id ,
106+ PersonalBest .metric == metric
107+ )
108+ return session .exec (stmt ).one_or_none ()
109+
110+ def update_personal_bests_after_workout (* , session : Session , workout : Workout ):
111+ user_id = workout .user_id
112+
113+ # Fetch all exercises associated with this workout
114+ exercises = workout .exercises
115+
116+ for exercise in exercises :
117+ name = exercise .name .lower ()
118+ metric_key = TRACKED_EXERCISES .get (name )
119+ if not metric_key :
120+ #
121+ continue # skip exercises we don't track
122+
123+ # Use weight * reps as performance metric for now
124+ value = (exercise .weight or 0 ) * (exercise .reps or 0 )
125+ if value <= 0 :
126+ #continue
127+ value = 0
128+
129+ pb_in = PersonalBestCreate (metric = metric_key , value = value , date = date .today ())
130+
131+ from app .crud import create_or_update_personal_best # to avoid circular imports
132+ create_or_update_personal_best (session = session , user_id = user_id , pb_in = pb_in )
0 commit comments