feat: Initial implementation of FitTrack Report Generator

This commit introduces the initial version of the FitTrack Report Generator, a FastAPI application for analyzing workout files.

Key features include:
- Parsing of FIT, TCX, and GPX workout files.
- Analysis of power, heart rate, speed, and elevation data.
- Generation of summary reports and charts.
- REST API for single and batch workout analysis.

The project structure has been set up with a `src` directory for core logic, an `api` directory for the FastAPI application, and a `tests` directory for unit, integration, and contract tests.

The development workflow is configured to use Docker and modern Python tooling.
This commit is contained in:
2025-10-11 09:54:13 -07:00
parent 6643a64ff0
commit 9e0bd322d3
152 changed files with 25695 additions and 49 deletions

View File

@@ -0,0 +1,58 @@
import pytest
from unittest.mock import MagicMock, patch, mock_open
from src.core.file_parser import GpxParser, WorkoutData, WorkoutMetadata
from datetime import datetime, timedelta
@pytest.fixture
def mock_gpxpy_parse():
with patch('gpxpy.parse') as mock_parse:
mock_gpx = MagicMock()
mock_parse.return_value = mock_gpx
# Mock GPX data
mock_gpx.time = datetime(2023, 1, 1, 10, 0, 0)
mock_gpx.get_moving_data.return_value.moving_time = 3600
mock_point1 = MagicMock()
mock_point1.time = datetime(2023, 1, 1, 10, 0, 0)
mock_point1.latitude = 40.0
mock_point1.longitude = -105.0
mock_point1.elevation = 1600.0
mock_point2 = MagicMock()
mock_point2.time = datetime(2023, 1, 1, 10, 1, 0)
mock_point2.latitude = 40.1
mock_point2.longitude = -105.1
mock_point2.elevation = 1610.0
mock_segment = MagicMock()
mock_segment.points = [mock_point1, mock_point2]
mock_track = MagicMock()
mock_track.segments = [mock_segment]
mock_gpx.tracks = [mock_track]
yield mock_parse
def test_gpx_parser_initialization():
parser = GpxParser("dummy.gpx")
assert parser.file_path == "dummy.gpx"
def test_gpx_parser_parse_method_returns_workout_data(mock_gpxpy_parse):
# Mock the open function as well, since GpxParser directly opens the file
with patch('builtins.open', mock_open(read_data="<gpx></gpx>")):
parser = GpxParser("dummy.gpx")
workout_data = parser.parse()
mock_gpxpy_parse.assert_called_once() # gpxpy.parse is called
assert isinstance(workout_data, WorkoutData)
assert isinstance(workout_data.metadata, WorkoutMetadata)
assert workout_data.metadata.file_type == "GPX"
assert workout_data.metadata.start_time == datetime(2023, 1, 1, 10, 0, 0)
assert workout_data.metadata.duration == timedelta(seconds=3600)
assert not workout_data.time_series_data.empty
assert "latitude" in workout_data.time_series_data.columns
assert "longitude" in workout_data.time_series_data.columns
assert "elevation" in workout_data.time_series_data.columns