This project is a running performance tracking application, consisting of a Flutter-based mobile frontend and a Flask-based Python backend. It allows users to record their running activities, view historical data, and track progress towards their fitness goals.
Visualize Readme Website : Runalyze README đź”—
Technical Project Report : Runalyze đź”—
- Features
- Directory Structure
- Setup Instructions
- Contributors
- Database Schema
- API Endpoints
- Frontend Architecture
- User Authentication: Secure user registration and login.
- Activity Tracking: Record running distance, duration, pace, and split times.
- Historical Data: View past workout records by date.
- Goal Tracking: Monitor goal achievement status (completed/missed) for each activity.
- Personalized Goals: Backend logic to update user performance traits and goals based on activity.
- Responsive UI: Flutter frontend designed for various screen sizes.
jnoctis-hackathon_physical_app/
├── README.md
├── physicalapp/
│ ├── analysis_options.yaml
│ ├── pubspec.yaml
│ ├── .metadata
│ ├── android/...
│ ├── assets/...
│ ├── ios/...
│ ├── lib/ (frontend code)
│ │ ├── instruction.dart
│ │ ├── login.dart
│ │ ├── main.dart
│ │ ├── signup.dart
│ │ ├── pages/
│ │ │ ├── analysis.dart
│ │ │ ├── history.dart
│ │ │ ├── history_day.dart
│ │ │ ├── run.dart
│ │ │ └── running_result.dart
│ │ └── utils/
│ │ ├── calculate.dart
│ │ ├── time_format.dart
│ │ └── titles.dart
│ ├── linux/...
│ ├── macos/...
│ ├── test/...
│ ├── web/...
│ └── windows/...
└── physicalbackend/ (backend code, batch file)
├── database.py
├── run_backend.bat
├── run_server.ps1
└── server.py
- Python 3.8+
- pip (Python package installer)
-
Navigate to the
physicalbackend/directory:cd jnoctis-hackathon_physical_app/physicalbackend/ -
(Recommended) Create and activate a virtual environment:
python -m venv venv # Windows: .\venv\Scripts\activate # macOS/Linux: source venv/bin/activate -
Install required Python packages:
pip install Flask Flask-Cors Flask-SQLAlchemy
The backend uses a SQLite database (site.db) by default. It needs to be initialized before running the server.
-
Ensure your virtual environment is activated.
-
Set the
FLASK_APPenvironment variable:# Windows: set FLASK_APP=server.py # macOS/Linux: export FLASK_APP=server.py -
Run the database initialization command:
flask init-dbNote: If you modify your database models, you may need to delete
site.dband re-runflask init-db. Deletingsite.dbwill erase all existing data.
-
Windows (using Command Prompt/CMD):
@echo off echo Setting FLASK_APP environment variable... set FLASK_APP=server.py echo Initializing database... flask init-db echo Starting Flask server... python server.py pause -
Windows (using PowerShell):
$env:FLASK_APP="server.py" flask init-db python .\server.py -
Manual Run (for other OS or direct control): Ensure your virtual environment is activated and
FLASK_APPis set, then run:python server.pyThe server typically runs on
http://127.0.0.1:5000. (default)
- Flutter SDK
- Android Studio / Xcode
- Code editor (e.g., we use VS Code)
-
Navigate to the
physicalapp/directory:cd jnoctis-hackathon_physical_app/physicalapp/ -
Get Flutter dependencies:
flutter pub get -
Create
.envfile: In thephysicalapp/directory, create a file named.envand add your backend API base URL:BASE_URL=http://127.0.0.1:5000Important: Ensure this URL matches where your Flask backend is running.
-
Ensure you are in the
physicalapp/directory and Flutter dependencies are installed. -
Connect a device or start an emulator/simulator.
-
Run the Flutter application:
flutter runThe app will launch on your connected device or emulator.
✨
| Member | Role | Responsibilities |
|---|---|---|
| sapt36 | Developer / Backend & History | History Record Frontend, Backend Integration, Backend Design, Backend Logic Development, Setup & Testing, Database Construction, This README |
| Sankonsky | Developer / Running & GPS | GPS Connection, Main Running Function Backend, Acceleration Test Function, Frontend Integration, Frontend Display Debugging |
| JNoctis | Developer / Frontend Design & Integration | Main Page Frontend Design, Integration and Aesthetic Refinement |
| WuKobeWu | Developer / Analysis & App Packaging | Analysis Frontend, Backend Integration, New Database Fields, Backend Design, iOS App Packaging, Demo App |
| Ashley | Project Integration & Requirements Management | Requirement Elicitation, Presentation Design, Video Editing, Full App Architecture, Market Research, Video/Audio Recording, Real-device Testing (Non-coding) |
The backend uses Flask-SQLAlchemy to manage the database. The database schema is defined in physicalbackend/database.py and includes the following models:
Represents a user of the application.
| Field Name | Data Type / Attributes | Description |
|---|---|---|
id |
Integer, Primary Key | Unique identifier for the user |
username |
String, Unique, Not Null | User's unique username |
password_hash |
String, Not Null | Hashed password for secure storage |
third_party_id |
String, Unique, Nullable | Optional ID for third-party authentication (e.g., Google, Facebook) |
created_at |
DateTime | Timestamp when the user account was created (UTC) |
activities |
Relationship (One-to-many) | One-to-many relationship with Activity records |
Stores details of each running activity.
| Field Name | Data Type / Attributes | Description |
|---|---|---|
id |
Integer, Primary Key | Unique identifier for the activity |
user_id |
Integer, Foreign Key | Links to the User who performed the activity |
start_time |
DateTime, Not Null | The exact start time of the activity |
duration_seconds |
Integer, Not Null | Total duration of the activity in seconds |
distance_km |
Float, Not Null | Total distance covered in kilometers |
end_latitude |
Float, Nullable | Latitude at the end of the activity |
end_longitude |
Float, Nullable | Longitude at the end of the activity |
average_pace_seconds_per_km |
Integer, Not Null | Average pace in seconds per kilometer |
split_paces_json |
Text, Nullable | JSON string storing a list of dictionaries, where each dictionary contains km and paceSeconds for individual kilometers |
goal_state |
String, Nullable | The state of the goal for this activity (e.g., 'completed', 'missed', 'None') |
goal_dist |
Float, Nullable | The target distance for the goal (if any) |
goal_pace |
Integer, Nullable | The target pace in seconds per km for the goal (if any) |
Stores user-specific traits and personalized goal information.
| Field Name | Data Type / Attributes | Description |
|---|---|---|
id |
Integer, Primary Key | Unique identifier for the trait record |
user_id |
Integer, Foreign Key, Unique, Not Null | Links to the User this trait belongs to |
user_type |
String, Nullable | User's type, possibly determined from a questionnaire |
long_goal |
MutableDict / JSON, Nullable | A dictionary storing long-term goals |
curr_goal |
MutableDict / JSON, Nullable | A dictionary storing current active goals |
usually_quit |
Boolean, Default False | Indicates if the user usually quits runs |
now_quit |
Boolean, Default False | Indicates if the user quit the current run |
believe_ai |
Boolean, Default True | Indicates if the user believes in AI recommendations |
The Flask backend provides the following RESTful API endpoints:
-
Endpoint:
/register -
Method:
POST -
Description: Registers a new user.
-
Request Example:
{ "username": "newuser", "password": "securepassword123", "third_party_id": "optional_third_party_id" } -
Response Example (Success):
{ "message": "User registered successfully" } -
Error Responses:
400 Bad Request:{"message": "Username and password are required"}409 Conflict:{"message": "Username already exists"}409 Conflict:{"message": "Third-party ID already linked to an account"}
-
Endpoint:
/login -
Method:
POST -
Description: Authenticates user credentials and logs in.
-
Request Example:
{ "username": "testuser", "password": "testpassword" } -
Response Example (Success):
{ "message": "Login successful", "user_id": 1 } -
Error Responses:
401 Unauthorized:{"message": "Invalid credentials"}
-
Endpoint:
/activities -
Method:
POST -
Description: Records a single running activity.
-
Request Example:
{ "user_id": 1, "start_time": "2025-06-27T10:00:00", "duration_seconds": 1800, "distance_km": 5.0, "end_latitude": 25.0330, "end_longitude": 121.5645, "average_pace_seconds_per_km": 360.0, "split_paces": [350, 365, 355, 360, 370], "goal_state": true, "goal_dist": 5.0, "goal_pace": 370 } -
Response Example (Success):
{ "message": "Activity added successfully", "activity_id": 101 } -
Error Responses:
400 Bad Request:{"message": "Missing field: [field_name]"}400 Bad Request:{"message": "Invalid date format for start_time. Use ISO format (YYYY-MM-DDTHH:MM:SS)"}404 Not Found:{"message": "User not found or not authenticated"}500 Internal Server Error:{"message": "Error adding activity: [error_details]"}
-
Endpoint:
/activities/<int:user_id> -
Method:
GET -
Description: Retrieves all running activity records for a specified user, ordered by time in descending order.
-
URL Parameters:
user_id(Integer): Unique identifier for the user.
-
Response Example (Success):
[ { "id": 101, "user_id": 1, "start_time": "2025-06-27T10:00:00", "duration_seconds": 1800, "distance_km": 5.0, "end_latitude": 25.0330, "end_longitude": 121.5645, "average_pace_seconds_per_km": 360.0, "split_paces": [350, 365, 355, 360, 370], "goal_state": true, "goal_dist": 5.0, "goal_pace": 370 }, { "id": 100, "user_id": 1, "start_time": "2025-06-26T09:30:00", "duration_seconds": 1200, "distance_km": 3.0, "end_latitude": 25.0300, "end_longitude": 121.5600, "average_pace_seconds_per_km": 400.0, "split_paces": [390, 410, 400], "goal_state": false, "goal_dist": 4.0, "goal_pace": 390 } ] -
Error Responses:
404 Not Found:{"message": "User not found"}
-
Endpoint:
/activities/past_week/<int:user_id> -
Method:
GET -
Description: Retrieves running activity records for the past seven days for a specified user, ordered by time in descending order.
-
URL Parameters:
user_id(Integer): Unique identifier for the user.
-
Response Example (Success): (Similar to
get_user_activities, but only includes data from the past seven days)[ { "id": 101, "user_id": 1, "start_time": "2025-06-27T10:00:00", "duration_seconds": 1800, "distance_km": 5.0, "end_latitude": 25.0330, "end_longitude": 121.5645, "average_pace_seconds_per_km": 360.0, "split_paces": [350, 365, 355, 360, 370], "goal_state": true, "goal_dist": 5.0, "goal_pace": 370 } ] -
Error Responses:
404 Not Found:{"message": "User not found"}
-
Endpoint:
/activities_by_date/<int:user_id>/<string:date_str> -
Method:
GET -
Description: Retrieves all running activity records for a specified user on a specific date, ordered by time in descending order.
-
URL Parameters:
user_id(Integer): Unique identifier for the user.date_str(String): Date string inYYYY-MM-DDformat.
-
Request Example:
/activities_by_date/1/2025-06-27 -
Response Example (Success): (Similar to
get_user_activities, but only includes data for the specified date)[ { "id": 101, "user_id": 1, "start_time": "2025-06-27T10:00:00", "duration_seconds": 1800, "distance_km": 5.0, "end_latitude": 25.0330, "end_longitude": 121.5645, "average_pace_seconds_per_km": 360.0, "split_paces": [350, 365, 355, 360, 370], "goal_state": true, "goal_dist": 5.0, "goal_pace": 370 } ] -
Error Responses:
400 Bad Request:{"message": "Invalid date format. Use ISO-MM-DD"}404 Not Found:{"message": "User not found"}
-
Endpoint:
/finish_questionare -
Method:
POST -
Description: After a user completes the questionnaire, their long-term and current running goals and other traits are set based on their answers.
-
Request Example:
{ "user_id": 1, "g1": "Faster speed (Additional: distance=10, speed=4.5)", "g2": "Yes (Additional: distance=21, speed=4)", "h1": "Within a week", "h2": "3~10km", "h3": "5~7", "h4": "kg (Additional: weight=75)", "m1": "No", "m2": "Yes" }- g1: Running motivation ('Faster speed', 'Longer distance', 'Healthier shape'). Can include additional parameters in
(Additional: ...)format. - g2: Whether there is a long-term goal ('Yes', 'No'). If 'Yes', can include additional parameters in
(Additional: ...)format, such asdistance(target distance),speed(target pace in min/km),goal(target distance, used for 'Longer distance' type),weight(target weight). - h1: Time since last run ('More than a month', 'Within a month', 'Within a week').
- h2: Usual running distance ('Less than 3km', '3~10km', 'More than 10km').
- h3: Usual running pace ('Less than 5', '5~7', 'More than 7'), in min/km.
- h4: Current weight. Can include additional parameters in
(Additional: weight=...)format. - m1: Whether habitually gives up ('Yes', 'No').
- m2: Whether believes in AI recommendations ('Yes', 'No').
- g1: Running motivation ('Faster speed', 'Longer distance', 'Healthier shape'). Can include additional parameters in
-
Response Example (Success):
{ "message": "Trait created successfully" } -
Error Responses:
400 Bad Request:{"error": "Missing user_id"}
-
Endpoint:
/goal/<int:user_id> -
Method:
GET -
Description: Retrieves the current running goal (distance and pace) for a specified user.
-
URL Parameters:
user_id(Integer): Unique identifier for the user.
-
Response Example (Success):
{ "goal_dist": 5.0, "goal_pace": 360.0 } -
Response Example (Questionnaire not completed):
{ "goal_dist": -1, "goal_pace": -1 } -
Error Responses:
404 Not Found:{"message": "User not found"}
-
Endpoint:
/user_type/<int:user_id> -
Method:
GET -
Description: Retrieves the user's type, their current weight, and their running frequency goal.
-
URL Parameters:
user_id(Integer): Unique identifier for the user.
-
Response Example (Success):
{ "user_type": "faster", "weight": 70.0, "freq": 3.0 } -
Error Responses:
404 Not Found:{"message": "User not found"}404 Not Found:{"message": "Trait not found"}
The Flutter frontend (physicalapp/lib/) structure:
| File Name | Description |
|---|---|
main.dart |
The entry point of the application, handling environment variable loading, app theme, routing, and main navigation flow. It directs users to the login or instruction page based on questionnaire completion. |
pages/history.dart |
Displays a calendar view of user's past activities, coloring dates based on activity goal status, and allowing viewing detailed activities for a selected date. |
pages/history_day.dart |
Shows detailed running activity records for a specific date, including goal status, key metrics, and split paces, with support for swiping through multiple activities. |
pages/run.dart |
The core live run tracking screen, handling location tracking, timer, metric calculation, split times, pace alerts (audio feedback), progress bar, and data submission upon run completion. |
pages/running_result.dart |
Displays a detailed summary of a single completed running activity, including date, distance, average pace, total time, and goal status. |
pages/analysis.dart |
Serves as the "Analysis" tab, retrieving and displaying user-specific info (type, weight, frequency), weekly running statistics, and generating motivational titles and habit stability progress based on user data. |
login.dart |
User login interface, handling credential submission, session management, and displaying error messages. |
signup.dart |
New user registration interface, handling the registration process, input validation, and navigation. |
instruction.dart |
Guides users through a series of questions to classify their running goals and habits, dynamically displaying input fields, collecting answers, and submitting to the backend for personalized goal setting. |
utils/time_format.dart |
Contains utility functions for formatting time and pace values specifically for the app's UI, e.g., converting seconds to HH:MM:SS or MM'SS" format. |
utils/calculate.dart |
Contains utility functions for performing calculations related to running data, e.g., calculating the average of a list of double values. |
utils/titles.dart |
Contains utility functions for generating dynamic titles and messages based on user data and activity metrics, used in the analysis page for personalized feedback. |


