diff --git a/app/__init__.py b/app/__init__.py index 52aee80..a916e34 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -4,12 +4,14 @@ from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate from flask_bcrypt import Bcrypt from flask_login import LoginManager +from flask_compress import Compress # Initialize Flask extensions db = SQLAlchemy() migrate = Migrate() bcrypt = Bcrypt() login_manager = LoginManager() +compress = Compress() login_manager.login_view = 'auth.login' login_manager.login_message_category = 'info' @@ -26,6 +28,7 @@ def create_app(): migrate.init_app(app, db) bcrypt.init_app(app) login_manager.init_app(app) + compress.init_app(app) # Import models here to avoid circular imports from app.models import User # Import the User model diff --git a/app/routes/main.py b/app/routes/main.py index d1aeab7..cb31264 100644 --- a/app/routes/main.py +++ b/app/routes/main.py @@ -35,12 +35,7 @@ def dashboard(): # Calculate weekly averages via SQL systolic_avg, diastolic_avg, heart_rate_avg = calculate_weekly_summary_sql(current_user.id) - # Badges need paginated or all readings. We'll fetch all readings for badges - # Note: To avoid huge queries, we might want a specific badge query in the future. - # For now, let's fetch current month readings + previous as a rough approximation or keep behavior. - # A generic query for badges: - all_readings = Reading.query.filter_by(user_id=current_user.id).order_by(Reading.timestamp.desc()).all() - badges = calculate_progress_badges(all_readings) + badges = calculate_progress_badges(current_user.id, user_tz) # We will default to showing the list view on initial load page = request.args.get('page', 1, type=int) @@ -248,47 +243,58 @@ def prepare_graph_data(readings): 'heart_rate': [r.heart_rate for r in readings], } -def calculate_progress_badges(readings): - """Generate badges based on user activity and milestones.""" - now = datetime.utcnow().date() +def calculate_progress_badges(user_id, user_tz): + """Generate badges based on user activity and milestones using optimized queries.""" + now_local = datetime.now(user_tz).date() badges = [] - if not readings: + total_readings = Reading.query.filter_by(user_id=user_id).count() + if total_readings == 0: return badges - # Use reversed() instead of re-sorting — readings come in desc order from DB - sorted_readings = list(reversed(readings)) - streak_count = 1 - daily_streak = True + # Fetch only timestamps (highly optimized compared to fetching full objects) + timestamps = db.session.query(Reading.timestamp).filter(Reading.user_id == user_id).order_by(Reading.timestamp.desc()).all() - previous_date = sorted_readings[0].timestamp.date() + streak_count = 0 + if timestamps: + distinct_dates = [] + last_date = None + for (ts,) in timestamps: + local_date = utc.localize(ts).astimezone(user_tz).date() + if local_date != last_date: + distinct_dates.append(local_date) + last_date = local_date - for reading in sorted_readings[1:]: - current_date = reading.timestamp.date() + if distinct_dates: + most_recent_date = distinct_dates[0] + if (now_local - most_recent_date).days <= 1: + streak_count = 1 + current_check_date = most_recent_date + + for d in distinct_dates[1:]: + if (current_check_date - d).days == 1: + streak_count += 1 + current_check_date = d + else: + break - if (current_date - previous_date).days == 1: - streak_count += 1 - elif (current_date - previous_date).days > 1: - daily_streak = False - - previous_date = current_date - - if daily_streak and streak_count >= 1: + if streak_count >= 1: badges.append(f"Current Streak: {streak_count} Days") - if daily_streak and streak_count >= 7: + if streak_count >= 7: badges.append("Logged Every Day for a Week") - - if daily_streak and streak_count >= 30 and previous_date == now: + if streak_count >= 30: badges.append("Monthly Streak") - if all(5 <= r.timestamp.hour < 12 for r in sorted_readings[-7:]): - badges.append("Morning Riser: Logged Readings Every Morning for a Week") - - if all(18 <= r.timestamp.hour <= 23 for r in sorted_readings[-7:]): - badges.append("Night Owl: Logged Readings Every Night for a Week") + last_7_readings = db.session.query(Reading.timestamp).filter(Reading.user_id == user_id).order_by(Reading.timestamp.desc()).limit(7).all() + if len(last_7_readings) == 7: + if all(5 <= utc.localize(ts).astimezone(user_tz).hour < 12 for (ts,) in last_7_readings): + badges.append("Morning Riser: Logged Readings Every Morning for a Week") + + if all(18 <= utc.localize(ts).astimezone(user_tz).hour <= 23 for (ts,) in last_7_readings): + badges.append("Night Owl: Logged Readings Every Night for a Week") milestones = [10, 50, 100, 500, 1000, 5000, 10000] - highest_milestone = max((m for m in milestones if len(readings) >= m), default=None) + highest_milestone = max((m for m in milestones if total_readings >= m), default=None) if highest_milestone: badges.append(f"{highest_milestone} Readings Logged") diff --git a/package.json b/package.json index 5a67906..5f11dfa 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "build": "npx tailwindcss -o ./app/static/css/tailwind.css --minify", - "serve": "npx tailwindcss -o ./app/static/css/tailwind.css --watch" + "serve": "npx tailwindcss -o ./app/static/css/tailwind.css --minify --watch" }, "author": "", "license": "ISC", diff --git a/requirements.txt b/requirements.txt index 5b88314..772bfe9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,7 @@ email-validator==2.2.0 exceptiongroup==1.2.2 flask==3.1.0 Flask-Bcrypt==1.0.1 +Flask-Compress==1.14 Flask-Login==0.6.3 Flask-Migrate==4.0.7 flask-sqlalchemy==3.1.1 diff --git a/tailwind.config.js b/tailwind.config.js index 7cf30a0..0149e95 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -3,9 +3,6 @@ module.exports = { content: ["./app/templates/**/*.html"], theme: { extend: { - fontFamily: { - sans: ['Inter', 'sans-serif'], - }, colors: { primary: { 50: '#f0fdfa',