mirror of
https://github.com/sstent/AICyclingCoach.git
synced 2026-03-23 13:25:31 +00:00
change to TUI
This commit is contained in:
@@ -1,72 +0,0 @@
|
||||
# Multi-stage build for container-first development
|
||||
FROM python:3.11-slim-bullseye AS builder
|
||||
|
||||
# Set environment variables
|
||||
ENV PYTHONDONTWRITEBYTECODE 1
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
# Install system dependencies for building
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends gcc libpq-dev && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Install Python dependencies
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Runtime stage
|
||||
FROM python:3.11-slim-bullseye AS runtime
|
||||
|
||||
# Set environment variables
|
||||
ENV PYTHONDONTWRITEBYTECODE 1
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
# Install runtime system dependencies only
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends libpq5 && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy installed packages from builder stage
|
||||
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
|
||||
COPY --from=builder /usr/local/bin /usr/local/bin
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Create entrypoint script for migration handling
|
||||
RUN echo '#!/bin/bash\n\
|
||||
set -e\n\
|
||||
\n\
|
||||
# Run database migrations synchronously\n\
|
||||
echo "Running database migrations..."\n\
|
||||
python -m alembic upgrade head\n\
|
||||
\n\
|
||||
# Verify migration success\n\
|
||||
echo "Verifying migration status..."\n\
|
||||
python -m alembic current\n\
|
||||
\n\
|
||||
# Start the application\n\
|
||||
echo "Starting application..."\n\
|
||||
exec "$@"' > /app/entrypoint.sh && \
|
||||
chmod +x /app/entrypoint.sh
|
||||
|
||||
# Create non-root user and logs directory
|
||||
RUN useradd -m appuser && \
|
||||
mkdir -p /app/logs && \
|
||||
chown -R appuser:appuser /app
|
||||
USER appuser
|
||||
|
||||
# Expose application port
|
||||
EXPOSE 8000
|
||||
|
||||
# Use entrypoint for migration automation
|
||||
ENTRYPOINT ["/app/entrypoint.sh"]
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
@@ -1,6 +1,6 @@
|
||||
[alembic]
|
||||
script_location = alembic
|
||||
sqlalchemy.url = postgresql+asyncpg://postgres:password@db:5432/cycling
|
||||
sqlalchemy.url = sqlite+aiosqlite:///data/cycling_coach.db
|
||||
|
||||
[loggers]
|
||||
keys = root
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
from logging.config import fileConfig
|
||||
from sqlalchemy import engine_from_config, pool
|
||||
from sqlalchemy.ext.asyncio import AsyncEngine
|
||||
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
|
||||
from alembic import context
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Add app directory to path
|
||||
sys.path.append(os.getcwd())
|
||||
# Add backend directory to path
|
||||
backend_dir = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(backend_dir))
|
||||
|
||||
# Import base and models
|
||||
from app.models.base import Base
|
||||
from app.config import settings
|
||||
from backend.app.models.base import Base
|
||||
from backend.app.config import settings
|
||||
|
||||
# Import all models to ensure they're registered
|
||||
from backend.app.models import *
|
||||
|
||||
config = context.config
|
||||
fileConfig(config.config_file_name)
|
||||
@@ -19,12 +24,13 @@ target_metadata = Base.metadata
|
||||
|
||||
def run_migrations_offline():
|
||||
"""Run migrations in 'offline' mode."""
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
url = settings.DATABASE_URL
|
||||
context.configure(
|
||||
url=url,
|
||||
target_metadata=target_metadata,
|
||||
literal_binds=True,
|
||||
dialect_opts={"paramstyle": "named"},
|
||||
render_as_batch=True, # Important for SQLite
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
@@ -32,21 +38,28 @@ def run_migrations_offline():
|
||||
|
||||
async def run_migrations_online():
|
||||
"""Run migrations in 'online' mode."""
|
||||
connectable = AsyncEngine(
|
||||
engine_from_config(
|
||||
config.get_section(config.config_ini_section),
|
||||
prefix="sqlalchemy.",
|
||||
poolclass=pool.NullPool,
|
||||
future=True,
|
||||
url=settings.DATABASE_URL,
|
||||
)
|
||||
# Ensure data directory exists
|
||||
data_dir = Path("data")
|
||||
data_dir.mkdir(exist_ok=True)
|
||||
|
||||
connectable = create_async_engine(
|
||||
settings.DATABASE_URL,
|
||||
poolclass=pool.NullPool,
|
||||
connect_args={"check_same_thread": False} if "sqlite" in settings.DATABASE_URL else {}
|
||||
)
|
||||
|
||||
async with connectable.connect() as connection:
|
||||
await connection.run_sync(do_run_migrations)
|
||||
|
||||
await connectable.dispose()
|
||||
|
||||
def do_run_migrations(connection):
|
||||
context.configure(connection=connection, target_metadata=target_metadata)
|
||||
context.configure(
|
||||
connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
render_as_batch=True, # Important for SQLite ALTER TABLE support
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
BIN
backend/app/__pycache__/config.cpython-313.pyc
Normal file
BIN
backend/app/__pycache__/config.cpython-313.pyc
Normal file
Binary file not shown.
BIN
backend/app/__pycache__/database.cpython-313.pyc
Normal file
BIN
backend/app/__pycache__/database.cpython-313.pyc
Normal file
Binary file not shown.
@@ -1,11 +1,20 @@
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
class Settings(BaseSettings):
|
||||
DATABASE_URL: str
|
||||
GPX_STORAGE_PATH: str
|
||||
AI_MODEL: str = "openrouter/auto"
|
||||
API_KEY: str
|
||||
# Database settings
|
||||
DATABASE_URL: str = "sqlite+aiosqlite:///data/cycling_coach.db"
|
||||
|
||||
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
|
||||
# File storage settings
|
||||
GPX_STORAGE_PATH: str = "data/gpx"
|
||||
|
||||
# AI settings
|
||||
AI_MODEL: str = "deepseek/deepseek-r1"
|
||||
OPENROUTER_API_KEY: str = ""
|
||||
|
||||
# Garmin settings
|
||||
GARMIN_USERNAME: str = ""
|
||||
GARMIN_PASSWORD: str = ""
|
||||
|
||||
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
|
||||
|
||||
settings = Settings()
|
||||
@@ -1,10 +1,19 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
||||
from sqlalchemy.orm import declarative_base, sessionmaker
|
||||
|
||||
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql+asyncpg://postgres:password@db:5432/cycling")
|
||||
# Use SQLite database in data directory
|
||||
DATA_DIR = Path("data")
|
||||
DATABASE_PATH = DATA_DIR / "cycling_coach.db"
|
||||
DATABASE_URL = os.getenv("DATABASE_URL", f"sqlite+aiosqlite:///{DATABASE_PATH}")
|
||||
|
||||
engine = create_async_engine(
|
||||
DATABASE_URL,
|
||||
echo=False, # Set to True for SQL debugging
|
||||
connect_args={"check_same_thread": False} if "sqlite" in DATABASE_URL else {}
|
||||
)
|
||||
|
||||
engine = create_async_engine(DATABASE_URL, echo=True)
|
||||
AsyncSessionLocal = sessionmaker(
|
||||
bind=engine,
|
||||
class_=AsyncSession,
|
||||
@@ -15,4 +24,19 @@ Base = declarative_base()
|
||||
|
||||
async def get_db() -> AsyncSession:
|
||||
async with AsyncSessionLocal() as session:
|
||||
yield session
|
||||
yield session
|
||||
|
||||
async def init_db():
|
||||
"""Initialize the database by creating all tables."""
|
||||
# Ensure data directory exists
|
||||
DATA_DIR.mkdir(exist_ok=True)
|
||||
|
||||
# Import all models to ensure they are registered
|
||||
from .models import (
|
||||
user, rule, plan, plan_rule, workout,
|
||||
analysis, route, section, garmin_sync_log, prompt
|
||||
)
|
||||
|
||||
# Create all tables
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
@@ -1,7 +1,7 @@
|
||||
from fastapi import Depends
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.database import get_db
|
||||
from app.services.ai_service import AIService
|
||||
from backend.app.database import get_db
|
||||
from backend.app.services.ai_service import AIService
|
||||
from typing import AsyncGenerator
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ from .route import Route
|
||||
from .section import Section
|
||||
from .rule import Rule
|
||||
from .plan import Plan
|
||||
from .plan_rule import PlanRule
|
||||
from .plan_rule import plan_rules
|
||||
from .user import User
|
||||
from .workout import Workout
|
||||
from .analysis import Analysis
|
||||
|
||||
BIN
backend/app/models/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
backend/app/models/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
backend/app/models/__pycache__/analysis.cpython-313.pyc
Normal file
BIN
backend/app/models/__pycache__/analysis.cpython-313.pyc
Normal file
Binary file not shown.
BIN
backend/app/models/__pycache__/base.cpython-313.pyc
Normal file
BIN
backend/app/models/__pycache__/base.cpython-313.pyc
Normal file
Binary file not shown.
BIN
backend/app/models/__pycache__/garmin_sync_log.cpython-313.pyc
Normal file
BIN
backend/app/models/__pycache__/garmin_sync_log.cpython-313.pyc
Normal file
Binary file not shown.
BIN
backend/app/models/__pycache__/plan.cpython-313.pyc
Normal file
BIN
backend/app/models/__pycache__/plan.cpython-313.pyc
Normal file
Binary file not shown.
BIN
backend/app/models/__pycache__/plan_rule.cpython-313.pyc
Normal file
BIN
backend/app/models/__pycache__/plan_rule.cpython-313.pyc
Normal file
Binary file not shown.
BIN
backend/app/models/__pycache__/prompt.cpython-313.pyc
Normal file
BIN
backend/app/models/__pycache__/prompt.cpython-313.pyc
Normal file
Binary file not shown.
BIN
backend/app/models/__pycache__/route.cpython-313.pyc
Normal file
BIN
backend/app/models/__pycache__/route.cpython-313.pyc
Normal file
Binary file not shown.
BIN
backend/app/models/__pycache__/rule.cpython-313.pyc
Normal file
BIN
backend/app/models/__pycache__/rule.cpython-313.pyc
Normal file
Binary file not shown.
BIN
backend/app/models/__pycache__/section.cpython-313.pyc
Normal file
BIN
backend/app/models/__pycache__/section.cpython-313.pyc
Normal file
Binary file not shown.
BIN
backend/app/models/__pycache__/user.cpython-313.pyc
Normal file
BIN
backend/app/models/__pycache__/user.cpython-313.pyc
Normal file
Binary file not shown.
BIN
backend/app/models/__pycache__/workout.cpython-313.pyc
Normal file
BIN
backend/app/models/__pycache__/workout.cpython-313.pyc
Normal file
Binary file not shown.
@@ -1,4 +1,5 @@
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey, JSON, Boolean, DateTime, func
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey, JSON, Boolean, DateTime
|
||||
from sqlalchemy.orm import relationship
|
||||
from .base import BaseModel
|
||||
|
||||
@@ -13,7 +14,7 @@ class Analysis(BaseModel):
|
||||
suggestions = Column(JSON)
|
||||
approved = Column(Boolean, default=False)
|
||||
created_plan_id = Column(Integer, ForeignKey('plans.id'))
|
||||
approved_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
approved_at = Column(DateTime, default=datetime.utcnow) # Changed from server_default=func.now()
|
||||
|
||||
# Relationships
|
||||
workout = relationship("Workout", back_populates="analyses")
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
from datetime import datetime
|
||||
from uuid import UUID, uuid4
|
||||
from sqlalchemy import Column, DateTime
|
||||
from sqlalchemy import Column, Integer, DateTime
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
class BaseModel(Base):
|
||||
__abstract__ = True
|
||||
|
||||
id = Column(PG_UUID(as_uuid=True), primary_key=True, default=uuid4)
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
from sqlalchemy import Column, Integer, ForeignKey
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy import Column, Integer, ForeignKey, JSON
|
||||
from sqlalchemy.orm import relationship
|
||||
from .base import BaseModel
|
||||
|
||||
class Plan(BaseModel):
|
||||
__tablename__ = "plans"
|
||||
|
||||
jsonb_plan = Column(JSONB, nullable=False)
|
||||
jsonb_plan = Column(JSON, nullable=False) # Changed from JSONB to JSON for SQLite compatibility
|
||||
version = Column(Integer, nullable=False)
|
||||
parent_plan_id = Column(Integer, ForeignKey('plans.id'), nullable=True)
|
||||
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
from sqlalchemy import Column, Integer, ForeignKey
|
||||
from sqlalchemy.orm import relationship
|
||||
from .base import BaseModel
|
||||
from sqlalchemy import Column, Integer, ForeignKey, Table
|
||||
from .base import Base
|
||||
|
||||
class PlanRule(BaseModel):
|
||||
__tablename__ = "plan_rules"
|
||||
|
||||
plan_id = Column(Integer, ForeignKey('plans.id'), primary_key=True)
|
||||
rule_id = Column(Integer, ForeignKey('rules.id'), primary_key=True)
|
||||
|
||||
plan = relationship("Plan", back_populates="rules")
|
||||
rule = relationship("Rule", back_populates="plans")
|
||||
# Association table for many-to-many relationship between plans and rules
|
||||
plan_rules = Table(
|
||||
'plan_rules', Base.metadata,
|
||||
Column('plan_id', Integer, ForeignKey('plans.id'), primary_key=True),
|
||||
Column('rule_id', Integer, ForeignKey('rules.id'), primary_key=True)
|
||||
)
|
||||
@@ -1,7 +1,12 @@
|
||||
from .base import BaseModel
|
||||
from sqlalchemy import Column, String
|
||||
from sqlalchemy.orm import relationship
|
||||
from .base import BaseModel
|
||||
|
||||
class User(BaseModel):
|
||||
__tablename__ = "users"
|
||||
|
||||
plans = relationship("Plan", back_populates="user")
|
||||
username = Column(String(100), nullable=False, unique=True)
|
||||
email = Column(String(255), nullable=True)
|
||||
|
||||
# Note: Relationship removed as Plan model doesn't have user_id field
|
||||
# plans = relationship("Plan", back_populates="user")
|
||||
@@ -1,9 +1,9 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.database import get_db
|
||||
from app.models.workout import Workout
|
||||
from app.models.plan import Plan
|
||||
from app.models.garmin_sync_log import GarminSyncLog
|
||||
from backend.app.database import get_db
|
||||
from backend.app.models.workout import Workout
|
||||
from backend.app.models.plan import Plan
|
||||
from backend.app.models.garmin_sync_log import GarminSyncLog
|
||||
from sqlalchemy import select, desc
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import APIRouter, Query, HTTPException
|
||||
from fastapi.responses import FileResponse
|
||||
from app.services.export_service import ExportService
|
||||
from backend.app.services.export_service import ExportService
|
||||
from pathlib import Path
|
||||
import logging
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from fastapi import APIRouter, Depends, BackgroundTasks
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.dependencies import verify_api_key
|
||||
from app.services.workout_sync import WorkoutSyncService
|
||||
from app.database import get_db
|
||||
from backend.app.dependencies import verify_api_key
|
||||
from backend.app.services.workout_sync import WorkoutSyncService
|
||||
from backend.app.database import get_db
|
||||
|
||||
router = APIRouter(dependencies=[Depends(verify_api_key)])
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from fastapi import APIRouter, UploadFile, File, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.database import get_db
|
||||
from app.services.gpx import parse_gpx, store_gpx_file
|
||||
from app.schemas.gpx import RouteCreate, Route as RouteSchema
|
||||
from app.models import Route
|
||||
from backend.app.database import get_db
|
||||
from backend.app.services.gpx import parse_gpx, store_gpx_file
|
||||
from backend.app.schemas.gpx import RouteCreate, Route as RouteSchema
|
||||
from backend.app.models import Route
|
||||
import os
|
||||
|
||||
router = APIRouter(prefix="/gpx", tags=["GPX Routes"])
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import PlainTextResponse, JSONResponse
|
||||
from app.services.health_monitor import HealthMonitor
|
||||
from backend.app.services.health_monitor import HealthMonitor
|
||||
from prometheus_client import generate_latest, CONTENT_TYPE_LATEST, Gauge
|
||||
from pathlib import Path
|
||||
import json
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import APIRouter, UploadFile, File, Form, HTTPException
|
||||
from fastapi.responses import JSONResponse
|
||||
from app.services.import_service import ImportService
|
||||
from backend.app.services.import_service import ImportService
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from app.database import get_db
|
||||
from app.models.plan import Plan as PlanModel
|
||||
from app.models.rule import Rule
|
||||
from app.schemas.plan import PlanCreate, Plan as PlanSchema, PlanGenerationRequest, PlanGenerationResponse
|
||||
from app.dependencies import get_ai_service
|
||||
from app.services.ai_service import AIService
|
||||
from backend.app.database import get_db
|
||||
from backend.app.models.plan import Plan as PlanModel
|
||||
from backend.app.models.rule import Rule
|
||||
from backend.app.schemas.plan import PlanCreate, Plan as PlanSchema, PlanGenerationRequest, PlanGenerationResponse
|
||||
from backend.app.dependencies import get_ai_service
|
||||
from backend.app.services.ai_service import AIService
|
||||
from uuid import UUID, uuid4
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
|
||||
@@ -3,10 +3,10 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from typing import List
|
||||
|
||||
from app.database import get_db
|
||||
from app.models.prompt import Prompt
|
||||
from app.schemas.prompt import Prompt as PromptSchema, PromptCreate, PromptUpdate
|
||||
from app.services.prompt_manager import PromptManager
|
||||
from backend.app.database import get_db
|
||||
from backend.app.models.prompt import Prompt
|
||||
from backend.app.schemas.prompt import Prompt as PromptSchema, PromptCreate, PromptUpdate
|
||||
from backend.app.services.prompt_manager import PromptManager
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from app.database import get_db
|
||||
from app.models.rule import Rule
|
||||
from app.schemas.rule import RuleCreate, Rule as RuleSchema, NaturalLanguageRuleRequest, ParsedRuleResponse
|
||||
from app.dependencies import get_ai_service
|
||||
from app.services.ai_service import AIService
|
||||
from backend.app.database import get_db
|
||||
from backend.app.models.rule import Rule
|
||||
from backend.app.schemas.rule import RuleCreate, Rule as RuleSchema, NaturalLanguageRuleRequest, ParsedRuleResponse
|
||||
from backend.app.dependencies import get_ai_service
|
||||
from backend.app.services.ai_service import AIService
|
||||
from uuid import UUID
|
||||
from typing import List
|
||||
|
||||
|
||||
@@ -3,17 +3,17 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from typing import List
|
||||
|
||||
from app.database import get_db
|
||||
from app.models.workout import Workout
|
||||
from app.models.analysis import Analysis
|
||||
from app.models.garmin_sync_log import GarminSyncLog
|
||||
from app.models.plan import Plan
|
||||
from app.schemas.workout import Workout as WorkoutSchema, WorkoutSyncStatus, WorkoutMetric
|
||||
from app.schemas.analysis import Analysis as AnalysisSchema
|
||||
from app.schemas.plan import Plan as PlanSchema
|
||||
from app.services.workout_sync import WorkoutSyncService
|
||||
from app.services.ai_service import AIService
|
||||
from app.services.plan_evolution import PlanEvolutionService
|
||||
from backend.app.database import get_db
|
||||
from backend.app.models.workout import Workout
|
||||
from backend.app.models.analysis import Analysis
|
||||
from backend.app.models.garmin_sync_log import GarminSyncLog
|
||||
from backend.app.models.plan import Plan
|
||||
from backend.app.schemas.workout import Workout as WorkoutSchema, WorkoutSyncStatus, WorkoutMetric
|
||||
from backend.app.schemas.analysis import Analysis as AnalysisSchema
|
||||
from backend.app.schemas.plan import Plan as PlanSchema
|
||||
from backend.app.services.workout_sync import WorkoutSyncService
|
||||
from backend.app.services.ai_service import AIService
|
||||
from backend.app.services.plan_evolution import PlanEvolutionService
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
BIN
backend/app/services/__pycache__/ai_service.cpython-313.pyc
Normal file
BIN
backend/app/services/__pycache__/ai_service.cpython-313.pyc
Normal file
Binary file not shown.
BIN
backend/app/services/__pycache__/garmin.cpython-313.pyc
Normal file
BIN
backend/app/services/__pycache__/garmin.cpython-313.pyc
Normal file
Binary file not shown.
BIN
backend/app/services/__pycache__/plan_evolution.cpython-313.pyc
Normal file
BIN
backend/app/services/__pycache__/plan_evolution.cpython-313.pyc
Normal file
Binary file not shown.
BIN
backend/app/services/__pycache__/prompt_manager.cpython-313.pyc
Normal file
BIN
backend/app/services/__pycache__/prompt_manager.cpython-313.pyc
Normal file
Binary file not shown.
BIN
backend/app/services/__pycache__/workout_sync.cpython-313.pyc
Normal file
BIN
backend/app/services/__pycache__/workout_sync.cpython-313.pyc
Normal file
Binary file not shown.
@@ -3,8 +3,8 @@ import asyncio
|
||||
from typing import Dict, Any, List, Optional
|
||||
import httpx
|
||||
import json
|
||||
from app.services.prompt_manager import PromptManager
|
||||
from app.models.workout import Workout
|
||||
from backend.app.services.prompt_manager import PromptManager
|
||||
from backend.app.models.workout import Workout
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -2,8 +2,8 @@ import json
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
import zipfile
|
||||
from app.database import SessionLocal
|
||||
from app.models import Route, Rule, Plan
|
||||
from backend.app.database import SessionLocal
|
||||
from backend.app.models import Route, Rule, Plan
|
||||
import tempfile
|
||||
import logging
|
||||
import shutil
|
||||
|
||||
@@ -3,7 +3,7 @@ import uuid
|
||||
import logging
|
||||
from fastapi import UploadFile, HTTPException
|
||||
import gpxpy
|
||||
from app.config import settings
|
||||
from backend.app.config import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ from datetime import datetime
|
||||
import logging
|
||||
from typing import Dict, Any
|
||||
from sqlalchemy import text
|
||||
from app.database import get_db
|
||||
from app.models.garmin_sync_log import GarminSyncLog, SyncStatus
|
||||
from backend.app.database import get_db
|
||||
from backend.app.models.garmin_sync_log import GarminSyncLog, SyncStatus
|
||||
import requests
|
||||
from app.config import settings
|
||||
from backend.app.config import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -43,12 +43,12 @@ class HealthMonitor:
|
||||
|
||||
def _get_sync_queue_size(self) -> int:
|
||||
"""Get number of pending sync operations"""
|
||||
from app.models.garmin_sync_log import GarminSyncLog, SyncStatus
|
||||
from backend.app.models.garmin_sync_log import GarminSyncLog, SyncStatus
|
||||
return GarminSyncLog.query.filter_by(status=SyncStatus.PENDING).count()
|
||||
|
||||
def _count_pending_analyses(self) -> int:
|
||||
"""Count workouts needing analysis"""
|
||||
from app.models.workout import Workout
|
||||
from backend.app.models.workout import Workout
|
||||
return Workout.query.filter_by(analysis_status='pending').count()
|
||||
|
||||
def _check_database(self) -> str:
|
||||
|
||||
@@ -3,8 +3,8 @@ import zipfile
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
from datetime import datetime
|
||||
from app.database import SessionLocal
|
||||
from app.models import Route, Rule, Plan
|
||||
from backend.app.database import SessionLocal
|
||||
from backend.app.models import Route, Rule, Plan
|
||||
import shutil
|
||||
import logging
|
||||
from sqlalchemy import and_
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from app.services.ai_service import AIService
|
||||
from app.models.analysis import Analysis
|
||||
from app.models.plan import Plan
|
||||
from backend.app.services.ai_service import AIService
|
||||
from backend.app.models.analysis import Analysis
|
||||
from backend.app.models.plan import Plan
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, update, func
|
||||
from app.models.prompt import Prompt
|
||||
from backend.app.models.prompt import Prompt
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, desc
|
||||
from app.services.garmin import GarminService, GarminAPIError, GarminAuthError
|
||||
from app.models.workout import Workout
|
||||
from app.models.garmin_sync_log import GarminSyncLog
|
||||
from app.models.garmin_sync_log import GarminSyncLog
|
||||
from backend.app.services.garmin import GarminService, GarminAPIError, GarminAuthError
|
||||
from backend.app.models.workout import Workout
|
||||
from backend.app.models.garmin_sync_log import GarminSyncLog
|
||||
from backend.app.models.garmin_sync_log import GarminSyncLog
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
from typing import Dict, Any
|
||||
|
||||
@@ -16,7 +16,7 @@ from typing import Optional
|
||||
backend_dir = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(backend_dir))
|
||||
|
||||
from app.database import get_database_url
|
||||
from backend.app.database import get_database_url
|
||||
|
||||
class DatabaseManager:
|
||||
"""Handles database backup and restore operations."""
|
||||
|
||||
@@ -18,7 +18,7 @@ from alembic import command
|
||||
from alembic.migration import MigrationContext
|
||||
from alembic.script import ScriptDirectory
|
||||
from sqlalchemy import create_engine, text
|
||||
from app.database import get_database_url
|
||||
from backend.app.database import get_database_url
|
||||
|
||||
class MigrationChecker:
|
||||
"""Validates migration compatibility and integrity."""
|
||||
|
||||
@@ -17,7 +17,7 @@ from alembic import command
|
||||
from alembic.migration import MigrationContext
|
||||
from alembic.script import ScriptDirectory
|
||||
import sqlalchemy as sa
|
||||
from app.database import get_database_url
|
||||
from backend.app.database import get_database_url
|
||||
|
||||
def get_alembic_config():
|
||||
"""Get Alembic configuration."""
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from app.main import app
|
||||
from app.database import get_db, Base
|
||||
from backend.app.main import app
|
||||
from backend.app.database import get_db, Base
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, patch, MagicMock
|
||||
from app.services.ai_service import AIService, AIServiceError
|
||||
from app.models.workout import Workout
|
||||
from backend.app.services.ai_service import AIService, AIServiceError
|
||||
from backend.app.models.workout import Workout
|
||||
import json
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, patch
|
||||
from app.services.garmin import GarminService
|
||||
from app.models.garmin_sync_log import GarminSyncStatus
|
||||
from backend.app.services.garmin import GarminService
|
||||
from backend.app.models.garmin_sync_log import GarminSyncStatus
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
from app.services.plan_evolution import PlanEvolutionService
|
||||
from app.models.plan import Plan
|
||||
from app.models.analysis import Analysis
|
||||
from backend.app.services.plan_evolution import PlanEvolutionService
|
||||
from backend.app.models.plan import Plan
|
||||
from backend.app.models.analysis import Analysis
|
||||
from datetime import datetime
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
from app.services.workout_sync import WorkoutSyncService
|
||||
from app.models.workout import Workout
|
||||
from app.models.garmin_sync_log import GarminSyncLog
|
||||
from backend.app.services.workout_sync import WorkoutSyncService
|
||||
from backend.app.models.workout import Workout
|
||||
from backend.app.models.garmin_sync_log import GarminSyncLog
|
||||
from datetime import datetime, timedelta
|
||||
import asyncio
|
||||
|
||||
|
||||
Reference in New Issue
Block a user