From 2f5db981a40dc905e585725ff3daa7ba5852c722 Mon Sep 17 00:00:00 2001 From: sstent Date: Sat, 23 Aug 2025 16:07:51 -0700 Subject: [PATCH] working - moved to compose --- .clinerules/CoreDevelopmentRules.md | 381 ++++++++++++++++++ .clinerules/Mandates.md | 10 + .clinerules/planning.md | 33 ++ garminsync/fit_processor/gear_analyzer.py | 73 ++++ garminsync/fit_processor/power_estimator.py | 44 ++ garminsync/web/templates/activity.html | 154 +++++++ .../20240823000000_add_reprocessed_column.py | 31 ++ todo.md | 44 ++ 8 files changed, 770 insertions(+) create mode 100644 .clinerules/CoreDevelopmentRules.md create mode 100644 .clinerules/Mandates.md create mode 100644 .clinerules/planning.md create mode 100644 garminsync/fit_processor/gear_analyzer.py create mode 100644 garminsync/fit_processor/power_estimator.py create mode 100644 garminsync/web/templates/activity.html create mode 100644 migrations/versions/20240823000000_add_reprocessed_column.py create mode 100644 todo.md diff --git a/.clinerules/CoreDevelopmentRules.md b/.clinerules/CoreDevelopmentRules.md new file mode 100644 index 0000000..531d24b --- /dev/null +++ b/.clinerules/CoreDevelopmentRules.md @@ -0,0 +1,381 @@ +# Development Tooling and Workflow Rules + +This document defines the mandatory development workflow, tooling requirements, and compliance rules for all projects. These rules ensure consistent development practices, reproducible builds, and standardized deployment procedures. + +## Core Development Principles + +### Container-First Development + +- **FORBIDDEN**: NEVER update or edit the .env file + +**Rule 1: Always Use Containers** +- **MANDATORY**: Launch all project artifacts as containers, never as local processes +- All applications must run in containerized environments +- No direct execution of local binaries or scripts outside of containers + +**Rule 2: Docker Command** +- **MANDATORY**: Use `docker compose` (new syntax) for all container orchestration +- **FORBIDDEN**: Never use the deprecated `docker-compose` command (old syntax with hyphen) +- All compose operations must use the modern Docker CLI integrated command + +**Rule 3: Docker Compose Version Attribute** +- **FORBIDDEN**: Never use the obsolete `version` attribute in Docker Compose files +- **MANDATORY**: Use modern Docker Compose files without version specification +- The `version` attribute has been deprecated and is no longer required in current Docker Compose specifications + +## Package Management + +### Python Development + +**Rule 4: Python Package Management with Astral UV** +- **MANDATORY**: Manage all Python packages using Astral UV with `pyproject.toml` +- **MANDATORY**: Use `uv sync` for dependency synchronization +- **FORBIDDEN**: Never use `pip` for package installation or management +- All Python dependencies must be declared in `pyproject.toml` and managed through UV +- **Legacy Support**: Poetry support maintained for compatibility where existing + +**Python Development Best Practices**: +- Use Astral UV for dependency management +- Follow PEP 8 coding standards +- Use type hints where applicable +- Structure modules by feature/domain + +### Frontend Development + +**Rule 5: React Package Management** +- **MANDATORY**: For React projects, use `pnpm` as the package manager +- **FORBIDDEN**: Never use `npm` for React project dependency management +- All React dependencies must be managed through pnpm +- **Lock File**: Use `pnpm-lock.yaml` for dependency locking + +**Rule 6: Pre-Build Code Quality Validation** + +**Python Projects**: +- **MANDATORY**: Before building a Python container, run the following commands and fix all issues: + ```bash + ruff format . + ruff check --fix . + ``` +- **MANDATORY**: All ruff formatting and linting errors must be resolved prior to Docker build process +- Code must pass both formatting and linting checks before containerization +- Use ruff for consistent code formatting and quality enforcement + +**Frontend Projects**: +- **MANDATORY**: Before building a React/frontend container, run `pnpm lint` and fix any errors +- **MANDATORY**: Run `pnpm lint --fix` to automatically fix linting issues where possible +- Code quality must be verified before containerization +- All linting errors must be resolved prior to Docker build process +- **MANDATORY**: Run TypeScript type checking before building containers + +**React Development Best Practices**: +- **MANDATORY**: Use TypeScript for all React components and logic +- **MANDATORY**: Use Tailwind CSS for styling +- **MANDATORY**: Use Vite as the build tool +- **MANDATORY**: Follow strict TypeScript configuration +- **MANDATORY**: Use functional components with hooks +- **MANDATORY**: Implement proper component prop typing +- Use modern React patterns (hooks, context, suspense) +- Implement proper error boundaries +- Use consistent naming conventions (PascalCase for components, camelCase for functions) +- Organize imports: React imports first, then third-party, then local imports + + +## Dockerfile Authoring Rules + +**Rule 7: Dockerfile = Build Only** +- **MANDATORY**: The Dockerfile must **only** describe how to **build** the image in the most efficient and smallest way possible. +- **FORBIDDEN**: Any instruction about **how to run** the container (commands, arguments, environment, ports, volumes, networks, restart policies, replicas, resource limits, etc.) must **only** appear in Docker Compose files. +- **MANDATORY**: Prefer **multi‑stage builds** to ensure the final image is minimal. +- **MANDATORY**: Use the **smallest still-supported base image** that satisfies the project's requirements (e.g., `python:3.12-slim`, `alpine`, `distroless`, `ubi-micro`, etc.), and keep it **recent** to receive security patches. +- **MANDATORY**: Remove all build-time tools, caches and temporary files in the final stage. +- **MANDATORY**: Provide a proper `.dockerignore` to keep the build context minimal. +- **RECOMMENDED**: Use BuildKit features such as `--mount=type=cache` to cache package managers (uv/pip/pnpm) during the build. +- **RECOMMENDED**: Pin dependency versions where sensible to ensure reproducible builds. +- **RECOMMENDED**: Run as a non-root user in the final stage when possible. +- **OPTIONAL**: `ENTRYPOINT`/`CMD` can be minimal or omitted; the **effective runtime command must be set in Compose**. + +**Rule 8: Multi-stage Dockerfile Syntax** +- **MANDATORY**: When writing multi-stage Dockerfiles, always use `FROM` and `AS` keywords in **UPPERCASE** +- **MANDATORY**: Stage names should be descriptive and follow consistent naming conventions +- **Example**: `FROM node:18-alpine AS builder` (not `from node:18-alpine as builder`) +- This ensures consistency and follows Docker best practices for multi-stage builds + +## Linting Dockerfiles + +**Rule 9: Dockerfiles must pass `hadolint`** +- **MANDATORY**: All Dockerfiles must be linted with **hadolint** locally +- **MANDATORY**: Any rule suppression must be: + - Declared in a **project-wide `.hadolint.yaml`** with a **short rationale**, **or** + - Inline via `# hadolint ignore=DLXXXX` **with a reference to the issue/PR explaining why**. +- **RECOMMENDED**: Keep the exception list short and reviewed periodically. + +### Sample `.hadolint.yaml` +```yaml +failure-threshold: warning # pipeline fails on warnings and above +ignored: + # Keep this list short, each with a comment explaining *why* it is safe to ignore. + # - DL3008 # Example: apt-get install without --no-install-recommends (document justification) +``` + + +## Container Development Workflow + +### Frontend Container Deployment + +**Rule 10: Frontend Container Deployment** +- **MANDATORY**: Launch frontend applications by rebuilding their Docker image and launching with `docker compose` +- **FORBIDDEN**: Never use `pnpm run` or any local package manager commands to start frontend applications +- Frontend must always be containerized and orchestrated through Docker Compose + +**Rule 11: Frontend Container Build and Test Process** +- **MANDATORY**: To build and test a new version of a frontend container always use: + ```bash + docker compose down FRONTENDNAME + docker compose up -d FRONTENDNAME --build + ``` +- This ensures clean shutdown of existing containers before rebuilding +- Forces fresh build of the frontend container image +- Launches in detached mode for testing + +### Development Workflow Commands + +**Backend Development**: +```bash +cd docker +docker compose down backend +docker compose up -d backend --build +``` + +**Frontend Development**: +```bash +cd docker +docker compose down frontend +docker compose up -d frontend --build +``` + +**Full Stack Development**: +```bash +cd docker +docker compose down +docker compose up -d --build +``` + +**Development Mode Testing**: +```bash +# For backend testing +docker compose exec backend python -m src.main --help + +# For frontend testing +docker compose logs frontend +``` + +## Environment Configuration + +### Centralized Environment Management + +**Rule 12: Root-Level Environment Variables Only** +- **MANDATORY**: All environment variables must be stored in the root `.env` file only +- **FORBIDDEN**: Environment variables in subdirectories (e.g., `frontend/.env`, `src/.env`) +- **MANDATORY**: Use a single `.env.example` template at the root level +- Both backend and frontend applications must read from the root `.env` file +- Docker Compose should mount the root `.env` file to all containers + +**Environment Variable Naming Conventions**: +- **Backend variables**: Use standard naming (e.g., `API_KEY`, `DATABASE_HOST`) +- **Frontend variables**: Prefix with `VITE_` for Vite projects (e.g., `VITE_API_URL`) +- **Docker variables**: Use `COMPOSE_` prefix for Docker Compose settings +- **Shared variables**: Can be used by both backend and frontend (e.g., `APP_ENV`) + +## Database Integration + +**Rule 13: Database Configuration** +- Place database initialization scripts in `/docker/init-scripts/` +- Use environment variables for database configuration +- Implement proper connection pooling +- Follow database naming conventions +- Mount database data as Docker volumes for persistence + +## Testing and Quality Assurance + +**Rule 14: Testing Requirements** +- **MANDATORY**: Run all tests in containerized environments +- Follow testing framework conventions (pytest for Python, Jest for React) +- Include unit, integration, and end-to-end tests +- Test data should be minimal and focused +- Separate test types into different directories + +**Testing Commands**: +```bash +# Python tests +docker compose exec backend python -m pytest tests/ + +# Frontend tests +docker compose exec frontend pnpm test + +# End-to-end tests +docker compose exec e2e pnpm test:e2e +``` + +## Compliance Requirements + +### Mandatory Rules + +**Project Structure**: +- **MANDATORY**: All new code must follow the standardized project structure +- **MANDATORY**: Core backend logic only in `/src/` directory +- **MANDATORY**: Frontend code only in `/frontend/` directory +- **MANDATORY**: All Docker files in `/docker/` directory + +**Package Management**: +- **MANDATORY**: Use UV for Python package management +- **MANDATORY**: Use pnpm for React package management +- **MANDATORY**: Dependencies declared in appropriate configuration files + +**Containerization**: +- **MANDATORY**: Use Docker containers for all deployments +- **MANDATORY**: Frontend applications must be containerized +- **MANDATORY**: Use `docker compose` for orchestration +- **MANDATORY**: Never use obsolete `version` attribute in Docker Compose files +- **MANDATORY**: Use uppercase `FROM` and `AS` in multi-stage Dockerfiles + +**Code Quality**: +- **MANDATORY**: Run linting before building frontend containers +- **MANDATORY**: Resolve all TypeScript errors before deployment +- **MANDATORY**: Follow language-specific coding standards + +### Forbidden Practices + +**Package Management**: +- **FORBIDDEN**: Using `pip` for Python package management +- **FORBIDDEN**: Using `npm` for React projects (use pnpm instead) +- **FORBIDDEN**: Installing packages outside of containerized environments + +**Project Organization**: +- **FORBIDDEN**: Business logic outside `/src/` directory +- **FORBIDDEN**: Frontend code outside `/frontend/` directory +- **FORBIDDEN**: Data files committed to git +- **FORBIDDEN**: Configuration secrets in code +- **FORBIDDEN**: Environment variables in subdirectories + +**Development Workflow**: +- **FORBIDDEN**: Using deprecated `docker-compose` command (use `docker compose`) +- **FORBIDDEN**: Using obsolete `version` attribute in Docker Compose files +- **FORBIDDEN**: Running applications outside of containers +- **FORBIDDEN**: Direct execution of local binaries for production code +- **FORBIDDEN**: Using lowercase `from` and `as` in multi-stage Dockerfiles + +## Deployment Procedures + +### Production Deployment + +**Pre-Deployment Checklist**: +1. All tests passing in containerized environment +2. Linting and type checking completed +3. Environment variables properly configured +4. Database migrations applied +5. Security scan completed + +**Deployment Commands**: +```bash +# Production build +docker compose -f docker-compose.prod.yml build + +# Production deployment +docker compose -f docker-compose.prod.yml up -d + +# Health check +docker compose -f docker-compose.prod.yml ps +``` + +### Development vs Production + +**Development Environment**: +- Use development Docker Compose configuration +- Enable hot reloading where applicable +- Include development tools and debugging utilities +- Use development environment variables + +**Production Environment**: +- Use production-optimized Docker images +- Exclude development dependencies +- Enable production optimizations +- Use production environment variables +- Implement proper logging and monitoring + +## Summary + +These rules ensure: +- **Consistent Development Environment**: All developers use identical containerized setups +- **Modern Tooling**: Latest Docker CLI, UV for Python, pnpm for React +- **Quality Assurance**: Mandatory linting, type checking, and testing +- **Reproducible Builds**: Standardized container build and deployment procedures +- **Security**: Centralized environment management and no secrets in code +- **Maintainability**: Clear separation of concerns and standardized workflows + +**Non-compliance with these rules is not acceptable and must be corrected immediately.** + +## Quick Reference + +### Common Commands + +**Start Development Environment**: +```bash +cd docker && docker compose up -d --build +``` + +**Rebuild Specific Service**: +```bash +docker compose down SERVICE_NAME +docker compose up -d SERVICE_NAME --build +``` + +**View Logs**: +```bash +docker compose logs SERVICE_NAME -f +``` + +**Execute Commands in Container**: +```bash +docker compose exec SERVICE_NAME COMMAND +``` + +**Clean Up**: +```bash +docker compose down +docker system prune -f +``` + +### Package Management Quick Reference + +**Python (UV)**: +```bash +# Add dependency +uv add package_name + +# Sync dependencies +uv sync + +# Remove dependency +uv remove package_name +``` + +**React (pnpm)**: +```bash +# Install dependencies +pnpm install + +# Add dependency +pnpm add package_name + +# Add dev dependency +pnpm add -D package_name + +# Remove dependency +pnpm remove package_name + +# Run linting +pnpm lint + +# Run tests +pnpm test +``` \ No newline at end of file diff --git a/.clinerules/Mandates.md b/.clinerules/Mandates.md new file mode 100644 index 0000000..6db76b2 --- /dev/null +++ b/.clinerules/Mandates.md @@ -0,0 +1,10 @@ + +- use the just_run_* tools via the MCP server +- all installs should be done in the docker container. +- NO installs on the host +- database upgrades should be handled during container server start up +- always rebuild the container before running tests +- if you need clarification return to PLAN mode +- force rereading of the mandates on each cycle +- always track progress of plans in todo.md + \ No newline at end of file diff --git a/.clinerules/planning.md b/.clinerules/planning.md new file mode 100644 index 0000000..9a66d5d --- /dev/null +++ b/.clinerules/planning.md @@ -0,0 +1,33 @@ +# Planning and Phased Approach Guidelines + +## When asked to plan or solve complex problems: + +### 1. Always break down into phases +- Identify 3-5 distinct phases maximum +- Each phase should have clear deliverables +- Phases should build logically on each other +- Include estimated timeframes when relevant + +### 2. Create actionable todo lists +- Use specific, measurable action items +- Start each item with an action verb +- Include who is responsible (if applicable) +- Add priority levels (High/Medium/Low) +- Include dependencies between tasks + +### 3. Structure format +For each phase, provide: +- **Phase Name & Objective** +- **Key Deliverables** +- **Todo List** (with checkboxes) +- **Success Criteria** +- **Estimated Duration** +- **Dependencies/Prerequisites** + +### 4. Planning principles to follow: +- Start with the end goal and work backwards +- Identify critical path items first +- Consider resource constraints and bottlenecks +- Build in buffer time for unexpected issues +- Include review/checkpoint moments +- Make tasks concrete and specific enough that someone else could execute them \ No newline at end of file diff --git a/garminsync/fit_processor/gear_analyzer.py b/garminsync/fit_processor/gear_analyzer.py new file mode 100644 index 0000000..6f13b21 --- /dev/null +++ b/garminsync/fit_processor/gear_analyzer.py @@ -0,0 +1,73 @@ +import numpy as np + +class SinglespeedAnalyzer: + def __init__(self): + self.chainring_options = [38, 46] # teeth + self.common_cogs = list(range(11, 28)) # 11t to 27t rear cogs + self.wheel_circumference_m = 2.096 # 700x25c tire + + def analyze_gear_ratio(self, speed_data, cadence_data, gradient_data): + """Determine most likely singlespeed gear ratio""" + # Validate input parameters + if not speed_data or not cadence_data or not gradient_data: + raise ValueError("Input data cannot be empty") + if len(speed_data) != len(cadence_data) or len(speed_data) != len(gradient_data): + raise ValueError("Input data arrays must be of equal length") + + # Filter for flat terrain segments (gradient < 3%) + flat_indices = [i for i, grad in enumerate(gradient_data) if abs(grad) < 3.0] + flat_speeds = [speed_data[i] for i in flat_indices] + flat_cadences = [cadence_data[i] for i in flat_indices] + + # Only consider data points with sufficient speed (15 km/h) and cadence + valid_indices = [i for i in range(len(flat_speeds)) + if flat_speeds[i] > 4.17 and flat_cadences[i] > 0] # 15 km/h threshold + + if not valid_indices: + return None # Not enough data + + valid_speeds = [flat_speeds[i] for i in valid_indices] + valid_cadences = [flat_cadences[i] for i in valid_indices] + + # Calculate gear ratios from speed and cadence + gear_ratios = [] + for speed, cadence in zip(valid_speeds, valid_cadences): + # Gear ratio = (speed in m/s * 60 seconds/minute) / (cadence in rpm * wheel circumference in meters) + gr = (speed * 60) / (cadence * self.wheel_circumference_m) + gear_ratios.append(gr) + + # Calculate average gear ratio + avg_gear_ratio = sum(gear_ratios) / len(gear_ratios) + + # Find best matching chainring and cog combination + best_fit = None + min_diff = float('inf') + for chainring in self.chainring_options: + for cog in self.common_cogs: + theoretical_ratio = chainring / cog + diff = abs(theoretical_ratio - avg_gear_ratio) + if diff < min_diff: + min_diff = diff + best_fit = (chainring, cog, theoretical_ratio) + + if not best_fit: + return None + + chainring, cog, ratio = best_fit + + # Calculate gear metrics + wheel_diameter_inches = 27.0 # 700c wheel diameter + gear_inches = ratio * wheel_diameter_inches + development_meters = ratio * self.wheel_circumference_m + + # Calculate confidence score (1 - relative error) + confidence = max(0, 1 - (min_diff / ratio)) if ratio > 0 else 0 + + return { + 'estimated_chainring_teeth': chainring, + 'estimated_cassette_teeth': cog, + 'gear_ratio': ratio, + 'gear_inches': gear_inches, + 'development_meters': development_meters, + 'confidence_score': confidence + } diff --git a/garminsync/fit_processor/power_estimator.py b/garminsync/fit_processor/power_estimator.py new file mode 100644 index 0000000..4dfa648 --- /dev/null +++ b/garminsync/fit_processor/power_estimator.py @@ -0,0 +1,44 @@ +import numpy as np + +class PowerEstimator: + def __init__(self): + self.bike_weight_kg = 10.0 # 22 lbs + self.rider_weight_kg = 75.0 # Default assumption + self.drag_coefficient = 0.88 # Road bike + self.frontal_area_m2 = 0.4 # Typical road cycling position + self.rolling_resistance = 0.004 # Road tires + self.drivetrain_efficiency = 0.97 + self.air_density = 1.225 # kg/m³ at sea level, 20°C + + def calculate_power(self, speed_ms, gradient_percent, + air_temp_c=20, altitude_m=0): + """Calculate estimated power using physics model""" + # Validate input parameters + if not isinstance(speed_ms, (int, float)) or speed_ms < 0: + raise ValueError("Speed must be a non-negative number") + if not isinstance(gradient_percent, (int, float)): + raise ValueError("Gradient must be a number") + + # Calculate air density based on temperature and altitude + temp_k = air_temp_c + 273.15 + pressure = 101325 * (1 - 0.0000225577 * altitude_m) ** 5.25588 + air_density = pressure / (287.05 * temp_k) + + # Convert gradient to angle + gradient_rad = np.arctan(gradient_percent / 100.0) + + # Total mass + total_mass = self.bike_weight_kg + self.rider_weight_kg + + # Power components + P_roll = self.rolling_resistance * total_mass * 9.81 * np.cos(gradient_rad) * speed_ms + P_grav = total_mass * 9.81 * np.sin(gradient_rad) * speed_ms + P_aero = 0.5 * air_density * self.drag_coefficient * self.frontal_area_m2 * speed_ms ** 3 + + # Power = (Rolling + Gravity + Aerodynamic) / Drivetrain efficiency + return (P_roll + P_grav + P_aero) / self.drivetrain_efficiency + + def estimate_peak_power(self, power_values, durations): + """Calculate peak power for various durations""" + # This will be implemented in Phase 3 + return {} diff --git a/garminsync/web/templates/activity.html b/garminsync/web/templates/activity.html new file mode 100644 index 0000000..b3f6638 --- /dev/null +++ b/garminsync/web/templates/activity.html @@ -0,0 +1,154 @@ + + + + + + Activity Details - GarminSync + + + + +
+

Activity Details

+ +
+ +
+ +
+

Analysis Metrics

+ + + + + + + + + + +
MetricValue
+
+ +
+ +
+
+ + +
+ + + + + diff --git a/migrations/versions/20240823000000_add_reprocessed_column.py b/migrations/versions/20240823000000_add_reprocessed_column.py new file mode 100644 index 0000000..d892067 --- /dev/null +++ b/migrations/versions/20240823000000_add_reprocessed_column.py @@ -0,0 +1,31 @@ +"""Add reprocessed column + +Revision ID: 20240823000000 +Revises: 20240822165438_add_hr_and_calories_columns +Create Date: 2025-08-23 00:00:00.000000 + +""" +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision = '20240823000000' +down_revision = '20240822165438_add_hr_and_calories_columns' +branch_labels = None +depends_on = None + +def upgrade(): + # Add reprocessed column to activities table + op.add_column('activities', sa.Column('reprocessed', sa.Boolean(), nullable=True, server_default='0')) + + # Set default value for existing records + op.execute("UPDATE activities SET reprocessed = 0 WHERE reprocessed IS NULL") + + # Make the column NOT NULL after setting default values + with op.batch_alter_table('activities') as batch_op: + batch_op.alter_column('reprocessed', existing_type=sa.Boolean(), nullable=False) + +def downgrade(): + # Remove reprocessed column + with op.batch_alter_table('activities') as batch_op: + batch_op.drop_column('reprocessed') diff --git a/todo.md b/todo.md new file mode 100644 index 0000000..df6e76a --- /dev/null +++ b/todo.md @@ -0,0 +1,44 @@ +# Activity Reprocessing Implementation + +## Goal +Add capability to reprocess existing activities to calculate missing metrics like `avg_power` + +## Requirements +- Reprocess all existing activities +- Add web UI button to trigger reprocessing +- Background processing for large jobs +- Progress tracking and status reporting + +## Implementation Phases + +### Phase 1: Database & Infrastructure +- [ ] Add `reprocessed` column to activities table +- [ ] Create migration script for new column +- [ ] Update activity parser to handle reprocessing +- [ ] Add CLI commands for reprocessing + +### Phase 2: CLI & Backend +- [ ] Implement `garminsync reprocess` commands: + - `--all`: Reprocess all activities + - `--missing`: Reprocess activities missing metrics + - `--activity-id`: Reprocess specific activity +- [ ] Add daemon support for reprocessing +- [ ] Create background job system + +### Phase 3: Web UI Integration +- [ ] Add "Reprocess" button to activities page +- [ ] Create API endpoints: + - POST /api/activities/reprocess + - POST /api/activities/{id}/reprocess +- [ ] Implement progress indicators +- [ ] Add real-time status updates via websockets + +### Phase 4: Testing & Optimization +- [ ] Write tests for reprocessing functionality +- [ ] Add pagination for large reprocessing jobs +- [ ] Implement caching for reprocessed activities +- [ ] Performance benchmarks + +## Current Status +*Last updated: 2025-08-23* +⏳ Planning phase - not yet implemented