mirror of
https://github.com/sstent/FitTrack_ReportGenerator.git
synced 2026-01-26 09:02:26 +00:00
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.
87 lines
3.9 KiB
Python
87 lines
3.9 KiB
Python
import pytest
|
|
import time
|
|
from fastapi.testclient import TestClient
|
|
from api.main import app
|
|
import os
|
|
import zipfile
|
|
import io
|
|
|
|
client = TestClient(app)
|
|
|
|
# Performance Goal SC-002: Analysis of a typical 2-hour workout file MUST complete in under 30 seconds.
|
|
# Performance Goal SC-004: Processing a batch of 100 workout files concurrently without generating errors or significant performance degradation.
|
|
|
|
# Helper to create a dummy FIT file for testing
|
|
def create_dummy_fit_file(file_path, duration_minutes=120):
|
|
# This is a very basic placeholder. A real dummy FIT file would be more complex.
|
|
# For actual performance testing, use a realistic 2-hour FIT file.
|
|
with open(file_path, "w") as f:
|
|
f.write(f"Dummy FIT file for {duration_minutes} minutes\n")
|
|
for i in range(duration_minutes * 60):
|
|
f.write(f"Time: {i}, Power: {200 + (i % 50)}, HR: {120 + (i % 20)}\n")
|
|
|
|
# Helper to create a dummy ZIP file with multiple workout files
|
|
def create_dummy_zip_file(num_files=100):
|
|
zip_buffer = io.BytesIO()
|
|
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf:
|
|
for i in range(num_files):
|
|
file_content = f"Dummy workout file {i}\n".encode('utf-8')
|
|
zf.writestr(f"workout_{i}.fit", file_content)
|
|
zip_buffer.seek(0)
|
|
return zip_buffer
|
|
|
|
|
|
def test_performance_single_workout_analysis(tmp_path):
|
|
# SC-002: Analysis of a typical 2-hour workout file MUST complete in under 30 seconds.
|
|
dummy_fit_file_path = tmp_path / "2_hour_workout.fit"
|
|
create_dummy_fit_file(dummy_fit_file_path, duration_minutes=120)
|
|
|
|
start_time = time.time()
|
|
with open(dummy_fit_file_path, "rb") as f:
|
|
response = client.post(
|
|
"/api/analyze/workout",
|
|
files={"file": ("2_hour_workout.fit", f, "application/octet-stream")},
|
|
data={
|
|
"ftp_value": 250.0
|
|
}
|
|
)
|
|
end_time = time.time()
|
|
elapsed_time = end_time - start_time
|
|
|
|
assert response.status_code == 200
|
|
assert elapsed_time < 30, f"Single workout analysis took {elapsed_time:.2f} seconds, exceeding 30 seconds."
|
|
print(f"Single workout analysis completed in {elapsed_time:.2f} seconds.")
|
|
|
|
# This test is conceptual. True concurrent batch processing performance testing
|
|
# would require a more sophisticated setup (e.g., using a load testing tool like Locust).
|
|
# This test only checks the sequential processing time of a batch.
|
|
def test_performance_batch_analysis_sequential(tmp_path):
|
|
# SC-004: Processing a batch of 100 workout files concurrently without generating errors or significant performance degradation.
|
|
# This test simulates processing 100 files sequentially within the batch endpoint.
|
|
# For true concurrency testing, external load testing tools are recommended.
|
|
num_files = 10
|
|
dummy_zip_content = create_dummy_zip_file(num_files)
|
|
|
|
start_time = time.time()
|
|
response = client.post(
|
|
"/api/analyze/batch",
|
|
files={"zip_file": ("batch_workouts.zip", dummy_zip_content.getvalue(), "application/zip")},
|
|
data={
|
|
"ftp_value": 250.0
|
|
}
|
|
)
|
|
end_time = time.time()
|
|
elapsed_time = end_time - start_time
|
|
|
|
assert response.status_code == 200
|
|
response_json = response.json()
|
|
assert response_json["status"] != "failed"
|
|
assert response_json["total_files"] == num_files
|
|
|
|
# Define a reasonable threshold for sequential processing. This will vary greatly.
|
|
# For 100 files, if each takes ~1 second, then 100 seconds is a rough estimate.
|
|
# This threshold needs to be adjusted based on actual system performance and file complexity.
|
|
expected_max_time = num_files * 5 # e.g., 5 seconds per file as a very rough estimate
|
|
assert elapsed_time < expected_max_time, f"Batch analysis of {num_files} files took {elapsed_time:.2f} seconds, exceeding {expected_max_time} seconds."
|
|
print(f"Batch analysis of {num_files} files completed in {elapsed_time:.2f} seconds.")
|