Skip to content

Server-side step calorie calculation#991

Draft
BryceKrispiess wants to merge 7 commits intoCodeWithCJ:mainfrom
BryceKrispiess:main
Draft

Server-side step calorie calculation#991
BryceKrispiess wants to merge 7 commits intoCodeWithCJ:mainfrom
BryceKrispiess:main

Conversation

@BryceKrispiess
Copy link
Contributor

Tip

Help us review and merge your PR faster!
Please ensure you have completed the Checklist below.
For Frontend changes, please run pnpm run validate to check for any errors.
PRs that include tests and clear screenshots are highly preferred!

Description

Server-side step calorie calculation

Previously, step calories were computed client-side on each platform (web/mobile)
independently, leading to inconsistencies. This moves that calculation to the server.

Changes:

  • Server DashboardService.js: GET /api/dashboard/stats now queries pedometer data
    and computes stepCalories using the user's weight/height-based calorie formula,
    returning it alongside the existing dashboard fields.

  • Mobile dashboardApi.ts: New API client that fetches /api/dashboard/stats.

  • Mobile useDailySummary.ts: Hook now fetches stepCalories from the server and
    includes it in the summary. Gracefully falls back to 0 if the endpoint fails.

  • Mobile calculations.ts: calculateEffectiveBurned updated to accept
    server-provided stepCalories instead of computing it locally.

  • Tests: New dashboardApi.test.ts covers the fetch function (request shape, response
    parsing, error handling). useDailySummary.test.ts updated to mock
    fetchDashboardStats and assert stepCalories flows through correctly, including the
    failure fallback.

Related Issue

PR type [X ] Issue [ ] New Feature [ ] Documentation
Linked Issue: #

Checklist

Please check all that apply:

  • [MANDATORY for new feature] Alignment: I have raised a GitHub issue and it was reviewed/approved by maintainers
  • Tests: I have included automated tests for my changes.
  • [MANDATORY for UI changes] Screenshots: I have attached "Before" vs "After" screenshots below.
  • [MANDATORY for Frontend changes] Quality: I have run pnpm run validate (especially for Frontend).
  • Translations: I have only updated the English (en) translation file (if applicable).
  • Architecture: My code follows the existing architecture standards.
  • Database Security: I have updated rls_policies.sql for any new user-specific tables.
  • [MANDATORY - ALL] Integrity & License: I certify this is my own work, free of malicious code(phishing, malware, etc.) and I agree to the License terms.

Screenshots (if applicable)

Before

[Insert screenshot/GIF here]

After

[Insert screenshot/GIF here]

claude and others added 7 commits March 25, 2026 00:31
…across web and mobile

Mobile was using a hardcoded `steps * 0.04` approximation while the server
and web frontend used the more accurate stride-based formula:
  distance(km) × weight(kg) × 0.4

This caused mobile to report nearly double the step calories for a typical
user (400 kcal vs ~200 kcal for 10,000 steps at 70kg/175cm).

- Server: expose `stepCalories` (already computed correctly) in the
  `/api/dashboard/stats` response
- Mobile: add `dashboardApi.ts` + fetch `stepCalories` from server in
  `useDailySummary`; thread value through `DailySummary` type and
  `calculateEffectiveBurned` (removing the local `stepsToCalories` function)

Any future fix to the step calorie formula now only needs to happen once,
on the server.

https://claude.ai/code/session_0199DHyvSiYcgKVWpJ2cj7Wy
…ocessing-Lg5BQ

feat: compute step calories server-side to ensure consistent results …
The previous migration (20260323122741) added steps only to exercise_entries,
but exerciseEntryHistoryService.ts also selects steps from exercise_preset_entries
when building preset workout sessions, causing a runtime crash:
  "column \"steps\" does not exist"

https://claude.ai/code/session_0199DHyvSiYcgKVWpJ2cj7Wy
…ocessing-Lg5BQ

fix: add missing steps column to exercise_preset_entries
- dashboardApi.test.ts: covers fetchDashboardStats GET request, response
  shape (including stepCalories), and error handling
- useDailySummary.test.ts: mock fetchDashboardStats, assert stepCalories
  is included in the summary, and verify graceful fallback to 0 when
  the dashboard stats endpoint fails

https://claude.ai/code/session_0199DHyvSiYcgKVWpJ2cj7Wy
…ocessing-Lg5BQ

test: add tests for server-side step calorie calculation
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly improves the consistency and accuracy of step calorie calculations by centralizing the logic on the server. Previously, client-side calculations could lead to discrepancies across different platforms. By moving this computation to the backend, the system now provides a single, authoritative source for step-related calorie burn, leveraging user-specific weight and height for more precise results. This change streamlines data handling and ensures a more reliable user experience regarding fitness tracking.

Highlights

  • Server-Side Step Calorie Calculation: The calculation of step calories has been moved from client-side (web/mobile) to the server, ensuring consistency and accuracy across platforms.
  • New Mobile API Client: A new API client (dashboardApi.ts) was introduced on the mobile side to fetch dashboard statistics, including the server-computed step calories.
  • Updated Daily Summary Hook: The useDailySummary.ts hook now fetches stepCalories from the server and gracefully falls back to 0 if the API call fails.
  • Refactored Calorie Calculation Logic: The calculateEffectiveBurned function in calculations.ts was updated to accept server-provided stepCalories directly, removing the client-side stepsToCalories conversion.
  • Database Migration: A new database migration adds a steps column to the exercise_preset_entries table on the server, aligning with the new data requirements.
  • Comprehensive Testing: New tests were added for the dashboardApi client, and existing useDailySummary tests were updated to cover the new server-side stepCalories integration and error handling.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces server-computed stepCalories to replace the client-side stepsToCalories approximation. This involves adding a new dashboardApi to fetch these statistics, updating the useDailySummary hook to integrate this data, and modifying the calculateEffectiveBurned function to prioritize this more accurate stepCalories value. A review comment suggests improving type safety and consistency in the useDailySummary hook's error handling for fetchDashboardStats by returning a full default DashboardStats object on failure.

fetchFoodEntries(date),
fetchExerciseEntries(date),
fetchWaterIntake(date).catch(() => ({ water_ml: 0 })),
fetchDashboardStats(date).catch(() => ({ stepCalories: 0 })),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For better type safety and consistency with how other failed fetches are handled (like fetchWaterIntake), it's a good practice for the .catch block to return an object that conforms to the DashboardStats interface, even if it's just a default/fallback version. This makes the code more robust for future modifications where other properties of dashboardStats might be used.

A default DashboardStats object could be defined and reused, perhaps in dashboardApi.ts.

Suggested change
fetchDashboardStats(date).catch(() => ({ stepCalories: 0 })),
fetchDashboardStats(date).catch(() => ({ eaten: 0, burned: 0, remaining: 0, goal: 0, net: 0, progress: 0, steps: 0, stepCalories: 0, bmr: 0, unit: 'kcal' })),

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The extra zeroed fields don't matter since the hook only reads stepCalories anyways. There's no real type issue if the shape allows partials.

@BryceKrispiess BryceKrispiess marked this pull request as draft March 25, 2026 03:21
@CodeWithCJ
Copy link
Owner

Steps shouldn't be in preset table.
Could you discuss with us before you start any change specially for new enhancements.
Dashboard service is currently used by homepage . Not sure if changing that would cause any issues to homepage widget.
I want to be caution on mobile app related change. @apedley could you check it out..

@CodeWithCJ
Copy link
Owner

@Sim-sat

@BryceKrispiess
Copy link
Contributor Author

@CodeWithCJ Will do in the future. I had talked with @apedley about helping out with some of the app development. I know he had some tweaks for this so I moved to draft for now.

@BryceKrispiess
Copy link
Contributor Author

@CodeWithCJ should the server endpoint be fixed to query the right table, or should steps be removed from dashboardStats entirely and stay client-side?

Copy link
Contributor

@apedley apedley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The core idea and direction is good, but there are some problems I've made comments for. Also:

  • dashboard fetch is done in useDailySummary instead of it's own query so theres no way to invalidate or refresh just those stats.
  • the big one: we now call 10 api endpoints for the dashboard screen and displaying the same data. if this is meant to consolidate the api hits, then more data will need to be added to the /api/dashboard/stats endpoint and we can remove the others. What is interesting about that endpoint though is it's not used for any of our code, just an external dashboard integration. so just verify how complete/correct the data it returns is. the other option is to add stepCalories to one of the existing api calls

unit: string;
}

export const fetchDashboardStats = async (date: string): Promise<DashboardStats> => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should encodeURIComponent the date to be sure

@@ -0,0 +1,22 @@
import { apiFetch } from './apiClient';

export interface DashboardStats {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a shared package (just for web and mobile right now) in the /shared/src/ folder that uses zod to validate and then infer types. We are moving new code to that pattern


beforeEach(() => {
jest.resetAllMocks();
(globalThis as any).fetch = mockFetch;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the actual implementation uses apiFetch in apiClient.ts so this isn't testing the real code path

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants