Skip to content

Commit 57df772

Browse files
authored
Merge branch 'master' into yitbrek/logs
2 parents d506b55 + 56c26c8 commit 57df772

3 files changed

Lines changed: 49 additions & 32 deletions

File tree

app_factory.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
from flask_jwt_extended import JWTManager
44
from src.utils.constants import SERVICE_ACCOUNT_PATH, JWT_SECRET_KEY
55
from datetime import datetime
6-
from flask import Flask, render_template
6+
from flask import Flask, jsonify, render_template
7+
from sqlalchemy import text
78
from graphene import Schema
89
from graphql.utils import schema_printer
910
from src.database import db_session, init_db
@@ -89,6 +90,14 @@ def check_if_token_revoked(jwt_header, jwt_payload: dict) -> bool:
8990
def index():
9091
return render_template("index.html")
9192

93+
@app.route("/health")
94+
def health_check():
95+
try:
96+
db_session.execute(text("SELECT 1"))
97+
return jsonify({"status": "healthy", "database": "connected"}), 200
98+
except Exception:
99+
return jsonify({"status": "unhealthy", "database": "disconnected"}), 503
100+
92101
app.add_url_rule("/graphql", view_func=GraphQLView.as_view("graphql", schema=schema, graphiql=True))
93102

94103
@app.teardown_appcontext

schema.graphql

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,6 @@ type CreateReport {
8585
report: Report
8686
}
8787

88-
scalar Date
89-
9088
scalar DateTime
9189

9290
enum DayOfWeekEnum {
@@ -319,7 +317,8 @@ type User {
319317
friendships: [Friendship]
320318
friends: [User]
321319
totalGymDays: Int!
322-
streakStart: Date
320+
streakStart: DateTime
321+
workoutHistory: [Workout]
323322
}
324323

325324
type Workout {

src/schema.py

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import graphene
2+
import base64
23
import os
34
from flask_jwt_extended import create_access_token, create_refresh_token, get_jwt_identity, get_jwt, jwt_required
45
from functools import wraps
5-
from datetime import datetime, timedelta, timezone
6+
from datetime import datetime, timedelta, time, timezone
67
from graphene_sqlalchemy import SQLAlchemyObjectType
78
from graphql import GraphQLError
89
from src.models.capacity import Capacity as CapacityModel
@@ -29,8 +30,10 @@
2930
import requests
3031
from firebase_admin import messaging
3132
import logging
33+
from zoneinfo import ZoneInfo
3234
from sqlalchemy import func, cast, Date
3335

36+
local_tz = ZoneInfo("America/New_York")
3437

3538
def resolve_enum_value(entry):
3639
"""Return the raw value for Enum objects while leaving plain strings untouched."""
@@ -66,7 +69,8 @@ def to_local_time(dt):
6669
return None
6770

6871
# Convert to local timezone (server-local)
69-
return dt_utc.astimezone()
72+
return dt_utc.astimezone(local_tz)
73+
7074

7175
def goal_at(goal_history, window_start_date):
7276
"""
@@ -83,6 +87,7 @@ def goal_at(goal_history, window_start_date):
8387

8488
return goal_history[-1][0]
8589

90+
8691
# MARK: - Gym
8792

8893

@@ -248,6 +253,7 @@ class Meta:
248253
def resolve_effective_at(self, info):
249254
return to_local_time(self.effective_at)
250255

256+
251257
# MARK: - User
252258

253259

@@ -258,18 +264,24 @@ class Meta:
258264
friendships = graphene.List(lambda: Friendship)
259265
friends = graphene.List(lambda: User)
260266
total_gym_days = graphene.Int(
261-
required=True,
262-
description="Get the total number of gym days (unique workout days) for user."
267+
required=True, description="Get the total number of gym days (unique workout days) for user."
263268
)
264-
streak_start = graphene.Date(
265-
description="The start date of the most recent active streak, up until the current date."
269+
streak_start = graphene.DateTime(
270+
description="The start datetime of the most recent active streak (midnight of the day in local timezone), up until the current date."
266271
)
272+
workout_history = graphene.List(lambda: Workout)
273+
274+
def resolve_workout_history(self, info):
275+
query = Workout.get_query(info).filter(WorkoutModel.user_id == self.id).order_by(WorkoutModel.workout_time.desc())
276+
return query.all()
267277

268278
def resolve_total_gym_days(self, info):
269279
return (
270280
Workout.get_query(info)
271281
.filter(WorkoutModel.user_id == self.id)
272-
.with_entities(func.count(func.distinct(cast(WorkoutModel.workout_time, Date)))) # We cast the datetiem object as a Date object to get the unique days
282+
.with_entities(
283+
func.count(func.distinct(cast(WorkoutModel.workout_time, Date)))
284+
) # We cast the datetiem object as a Date object to get the unique days
273285
.scalar()
274286
)
275287

@@ -290,7 +302,7 @@ def resolve_active_streak(self, info):
290302
if not workout_date_rows:
291303
return 0
292304

293-
workout_dates = [row[0] for row in workout_date_rows]
305+
workout_dates = [row[0] for row in workout_date_rows]
294306

295307
goal_hist = (
296308
db_session.query(UserWorkoutGoalHistoryModel.workout_goal, UserWorkoutGoalHistoryModel.effective_at)
@@ -316,7 +328,7 @@ def resolve_active_streak(self, info):
316328

317329
day_iterator = day_pointer
318330
count_in_window = 0
319-
331+
320332
while day_iterator < total_workout_days and workout_dates[day_iterator] >= window_start:
321333
count_in_window += 1
322334
day_iterator += 1
@@ -357,10 +369,7 @@ def resolve_streak_start(self, info):
357369
return None
358370

359371
goal_hist = (
360-
db_session.query(
361-
UserWorkoutGoalHistoryModel.workout_goal,
362-
UserWorkoutGoalHistoryModel.effective_at,
363-
)
372+
db_session.query(UserWorkoutGoalHistoryModel.workout_goal, UserWorkoutGoalHistoryModel.effective_at)
364373
.filter(UserWorkoutGoalHistoryModel.user_id == user.id)
365374
.order_by(UserWorkoutGoalHistoryModel.effective_at.desc())
366375
.all()
@@ -430,8 +439,8 @@ def goal_for_window_start(ws_date):
430439
return None
431440

432441
last_streak_start_date = workout_dates[idx_last_streak_start]
433-
434-
return last_streak_start_date
442+
local_midnight = datetime.combine(last_streak_start_date, time.min, tzinfo=local_tz)
443+
return local_midnight
435444

436445
def resolve_max_streak(self, info):
437446
user = User.get_query(info).filter(UserModel.id == self.id).first()
@@ -450,7 +459,7 @@ def resolve_max_streak(self, info):
450459
if not workout_date_rows:
451460
return 0
452461

453-
workout_dates = [row[0] for row in workout_date_rows]
462+
workout_dates = [row[0] for row in workout_date_rows]
454463

455464
goal_hist = (
456465
db_session.query(UserWorkoutGoalHistoryModel.workout_goal, UserWorkoutGoalHistoryModel.effective_at)
@@ -484,7 +493,7 @@ def resolve_max_streak(self, info):
484493
count_in_window += 1
485494
day_iterator += 1
486495

487-
goal_days = goal_at(goal_hist, window_start)
496+
goal_days = goal_at(goal_hist, window_start)
488497

489498
if count_in_window == 0:
490499
max_met_goal = max(max_met_goal, run_met_goal)
@@ -554,6 +563,7 @@ def resolve_friend(self, info):
554563
def resolve_accepted_at(self, info):
555564
return to_local_time(self.accepted_at)
556565

566+
557567
# MARK: - Giveaway
558568

559569

@@ -703,7 +713,7 @@ def resolve_get_weekly_workout_days(self, info, id):
703713

704714
def resolve_get_all_reports(self, info):
705715
query = ReportModel.query.all()
706-
return query
716+
return query
707717

708718
def resolve_get_hourly_average_capacities_by_facility_id(self, info, facility_id):
709719
valid_facility_ids = [14492437, 8500985, 7169406, 10055021, 2323580, 16099753, 15446768, 12572681]
@@ -831,18 +841,22 @@ def mutate(self, info, name, net_id, email, encoded_image=None):
831841
upload_url = os.getenv("DIGITAL_OCEAN_URL")
832842
if not upload_url:
833843
raise GraphQLError("Upload URL not configured.")
834-
payload = {"bucket": os.getenv("BUCKET_NAME"), "image": encoded_image} # Base64-encoded image string
844+
835845
headers = {"Content-Type": "application/json"}
846+
847+
image_bytes = base64.b64decode(encoded_image)
848+
files = {"image": ("profile.png", image_bytes, "image/png")}
849+
data = {"bucket": os.getenv("BUCKET_NAME")}
836850
try:
837-
response = requests.post(upload_url, json=payload, headers=headers)
851+
response = requests.post(upload_url, files=files, data=data)
838852
response.raise_for_status()
839853
json_response = response.json()
840854
final_photo_url = json_response.get("data")
841855
if not final_photo_url:
842856
raise GraphQLError("No URL returned from upload service.")
843857
except requests.exceptions.RequestException as e:
844858
print(f"Request failed: {e}")
845-
raise GraphQLError("Failed to upload photo.")
859+
raise GraphQLError(f"Failed to upload photo: {e}")
846860

847861
new_user = UserModel(name=name, net_id=net_id, email=email, encoded_image=final_photo_url)
848862
db_session.add(new_user)
@@ -1001,8 +1015,7 @@ class SetWorkoutGoals(graphene.Mutation):
10011015
class Arguments:
10021016
user_id = graphene.Int(required=True, description="The ID of the user.")
10031017
workout_goal = graphene.Int(
1004-
required=True,
1005-
description="The new workout goal for the user in terms of number of days per week.",
1018+
required=True, description="The new workout goal for the user in terms of number of days per week."
10061019
)
10071020

10081021
Output = User
@@ -1045,11 +1058,7 @@ def mutate(self, info, user_id, workout_goal):
10451058
user.workout_goal = workout_goal
10461059

10471060
db_session.add(
1048-
UserWorkoutGoalHistoryModel(
1049-
user_id=user.id,
1050-
workout_goal=workout_goal,
1051-
effective_at=effective_at,
1052-
)
1061+
UserWorkoutGoalHistoryModel(user_id=user.id, workout_goal=workout_goal, effective_at=effective_at)
10531062
)
10541063

10551064
db_session.commit()

0 commit comments

Comments
 (0)