From 5b43bca7ca39662c6a82154404ab708dc1b3262b Mon Sep 17 00:00:00 2001 From: Peter Stockings Date: Tue, 10 Mar 2026 19:34:37 +1100 Subject: [PATCH] Defer loading of profile pic --- app/models.py | 4 +-- app/routes/main.py | 16 +++++++--- app/templates/_layout.html | 12 ------- app/templates/profile.html | 7 +--- ...83e779fcc1_add_user_id_index_on_profile.py | 32 +++++++++++++++++++ tests/test_main.py | 22 ++++++++----- 6 files changed, 60 insertions(+), 33 deletions(-) create mode 100644 migrations/versions/bd83e779fcc1_add_user_id_index_on_profile.py diff --git a/app/models.py b/app/models.py index e948f8c..f826a11 100644 --- a/app/models.py +++ b/app/models.py @@ -11,10 +11,10 @@ class User(UserMixin, db.Model): class Profile(db.Model): id = db.Column(db.Integer, primary_key=True) - user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) + user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False, index=True) name = db.Column(db.String(100)) email = db.Column(db.String(150), unique=True) - profile_pic = db.Column(db.Text) # Store image as a base64 string + profile_pic = db.deferred(db.Column(db.Text)) # Store image as a base64 string, deferred so it doesn't load on every query systolic_threshold = db.Column(db.Integer, default=140) diastolic_threshold = db.Column(db.Integer, default=90) dark_mode = db.Column(db.Boolean, default=False) diff --git a/app/routes/main.py b/app/routes/main.py index cb31264..c76cda9 100644 --- a/app/routes/main.py +++ b/app/routes/main.py @@ -245,15 +245,21 @@ def prepare_graph_data(readings): 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 = [] - total_readings = Reading.query.filter_by(user_id=user_id).count() if total_readings == 0: - return badges + return [] # 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() + return _compute_badges(total_readings, timestamps, user_tz) + +def _compute_badges(total_readings, timestamps, user_tz, now_local=None): + if now_local is None: + now_local = datetime.now(user_tz).date() + badges = [] + + if total_readings == 0: + return badges streak_count = 0 if timestamps: @@ -285,7 +291,7 @@ def calculate_progress_badges(user_id, user_tz): if streak_count >= 30: badges.append("Monthly Streak") - last_7_readings = db.session.query(Reading.timestamp).filter(Reading.user_id == user_id).order_by(Reading.timestamp.desc()).limit(7).all() + last_7_readings = timestamps[:7] 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") diff --git a/app/templates/_layout.html b/app/templates/_layout.html index e1534c4..1ffebca 100644 --- a/app/templates/_layout.html +++ b/app/templates/_layout.html @@ -55,20 +55,8 @@
  • - {% if current_user.profile and current_user.profile.profile_pic %} Profile Picture - {% else %} - -
    - - - -
    - {% endif %}
    - {% if profile.profile_pic %} Profile Picture - {% else %} - Default Profile Picture - {% endif %} + class="w-44 h-44 rounded-full border object-cover shadow">
    diff --git a/migrations/versions/bd83e779fcc1_add_user_id_index_on_profile.py b/migrations/versions/bd83e779fcc1_add_user_id_index_on_profile.py new file mode 100644 index 0000000..c25d4ab --- /dev/null +++ b/migrations/versions/bd83e779fcc1_add_user_id_index_on_profile.py @@ -0,0 +1,32 @@ +"""Add user id index on profile + +Revision ID: bd83e779fcc1 +Revises: 8cfe56a1e597 +Create Date: 2026-03-10 19:33:12.629077 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'bd83e779fcc1' +down_revision = '8cfe56a1e597' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('profile', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_profile_user_id'), ['user_id'], unique=False) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('profile', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_profile_user_id')) + + # ### end Alembic commands ### diff --git a/tests/test_main.py b/tests/test_main.py index 193ddaf..d6dfad8 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -7,13 +7,19 @@ project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) sys.path.insert(0, project_root) from app.models import Reading -from app.routes.main import calculate_progress_badges +from app.routes.main import _compute_badges +from pytz import utc +def calculate_progress_badges_test_helper(readings, test_now=None): + total = len(readings) + timestamps = [(r.timestamp,) for r in sorted(readings, key=lambda x: x.timestamp, reverse=True)] + now_date = test_now.date() if test_now else datetime.utcnow().date() + return _compute_badges(total, timestamps, utc, now_local=now_date) def test_no_readings(): readings = [] - badges = calculate_progress_badges(readings) + badges = calculate_progress_badges_test_helper(readings) assert badges == [] @@ -22,7 +28,7 @@ def test_daily_streak(): readings = [ Reading(timestamp=now - timedelta(days=i)) for i in range(3) ] # 3-day streak - badges = calculate_progress_badges(readings) + badges = calculate_progress_badges_test_helper(readings, test_now=now) assert "Current Streak: 3 Days" in badges @@ -31,7 +37,7 @@ def test_weekly_streak(): readings = [ Reading(timestamp=now - timedelta(days=i)) for i in range(7) ] # 7-day streak - badges = calculate_progress_badges(readings) + badges = calculate_progress_badges_test_helper(readings, test_now=now) assert "Logged Every Day for a Week" in badges @@ -40,7 +46,7 @@ def test_morning_riser(): readings = [ Reading(timestamp=now - timedelta(days=i)) for i in range(7) ] - badges = calculate_progress_badges(readings) + badges = calculate_progress_badges_test_helper(readings, test_now=now) assert "Morning Riser: Logged Readings Every Morning for a Week" in badges @@ -49,7 +55,7 @@ def test_night_owl(): readings = [ Reading(timestamp=now - timedelta(days=i)) for i in range(7) ] - badges = calculate_progress_badges(readings) + badges = calculate_progress_badges_test_helper(readings, test_now=now) assert "Night Owl: Logged Readings Every Night for a Week" in badges @@ -57,7 +63,7 @@ def test_milestones(): readings = [ Reading(timestamp=datetime.utcnow()) for _ in range(50) ] # 50 readings - badges = calculate_progress_badges(readings) + badges = calculate_progress_badges_test_helper(readings) assert "50 Readings Logged" in badges assert "100 Readings Logged" not in badges # Ensure only the highest milestone @@ -67,5 +73,5 @@ def test_no_streak_with_gaps(): readings = [ Reading(timestamp=now - timedelta(days=i * 2)) for i in range(3) ] # Gaps in dates - badges = calculate_progress_badges(readings) + badges = calculate_progress_badges_test_helper(readings, test_now=now) assert "Current Streak" not in badges