added activity view

This commit is contained in:
2026-01-09 09:59:36 -08:00
parent c45e41b6a9
commit 55e37fbca8
168 changed files with 8799 additions and 2426 deletions

View File

@@ -6,5 +6,10 @@ from .api_token import APIToken
from .auth_status import AuthStatus
from .weight_record import WeightRecord
from .activity import Activity
from .job import Job
from .health_metric import HealthMetric
from .sync_log import SyncLog
from .sync_log import SyncLog
from .activity_state import GarminActivityState
from .health_state import HealthSyncState
from .scheduled_job import ScheduledJob
from .bike_setup import BikeSetup

View File

@@ -1,4 +1,5 @@
from sqlalchemy import Column, Integer, String, DateTime, Text, LargeBinary
from sqlalchemy import Column, Integer, String, DateTime, Text, LargeBinary, Float, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from ..models import Base
@@ -11,9 +12,34 @@ class Activity(Base):
activity_type = Column(String, nullable=True) # Type of activity (e.g., 'running', 'cycling')
start_time = Column(DateTime, nullable=True) # Start time of the activity
duration = Column(Integer, nullable=True) # Duration in seconds
duration = Column(Integer, nullable=True) # Duration in seconds
# Extended Metrics
distance = Column(Float, nullable=True) # meters
calories = Column(Float, nullable=True) # kcal
avg_hr = Column(Integer, nullable=True) # bpm
max_hr = Column(Integer, nullable=True) # bpm
avg_speed = Column(Float, nullable=True) # m/s
max_speed = Column(Float, nullable=True) # m/s
elevation_gain = Column(Float, nullable=True) # meters
elevation_loss = Column(Float, nullable=True) # meters
avg_cadence = Column(Integer, nullable=True) # rpm/spm
max_cadence = Column(Integer, nullable=True) # rpm/spm
steps = Column(Integer, nullable=True)
aerobic_te = Column(Float, nullable=True) # 0-5
anaerobic_te = Column(Float, nullable=True) # 0-5
avg_power = Column(Integer, nullable=True) # watts
max_power = Column(Integer, nullable=True) # watts
norm_power = Column(Integer, nullable=True) # watts
tss = Column(Float, nullable=True) # Training Stress Score
vo2_max = Column(Float, nullable=True) # ml/kg/min
file_content = Column(LargeBinary, nullable=True) # Activity file content stored in database (base64 encoded)
file_type = Column(String, nullable=True) # File type (.fit, .gpx, .tcx, etc.)
download_status = Column(String, default='pending') # 'pending', 'downloaded', 'failed'
downloaded_at = Column(DateTime, nullable=True) # When downloaded
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
bike_setup_id = Column(Integer, ForeignKey("bike_setups.id"), nullable=True)
bike_setup = relationship("BikeSetup")

View File

@@ -0,0 +1,12 @@
from sqlalchemy import Column, Integer, String, DateTime, func
from ..models import Base
class GarminActivityState(Base):
__tablename__ = "garmin_activity_state"
garmin_activity_id = Column(String, primary_key=True, index=True)
activity_name = Column(String, nullable=True)
activity_type = Column(String, nullable=True)
start_time = Column(DateTime, nullable=True)
sync_status = Column(String, default='new') # 'new', 'updated', 'synced'
last_seen = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())

View File

@@ -0,0 +1,15 @@
from sqlalchemy import Column, Integer, String, DateTime
from sqlalchemy.sql import func
from .base import Base
class BikeSetup(Base):
__tablename__ = "bike_setups"
id = Column(Integer, primary_key=True, index=True)
frame = Column(String, nullable=False)
chainring = Column(Integer, nullable=False)
rear_cog = Column(Integer, nullable=False)
name = Column(String, nullable=True) # Optional, can be derived or user-set
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())

View File

@@ -0,0 +1,16 @@
from sqlalchemy import Column, Integer, String, DateTime, Date, func, UniqueConstraint
from ..models import Base
class HealthSyncState(Base):
__tablename__ = "health_sync_state"
id = Column(Integer, primary_key=True, index=True)
date = Column(Date, nullable=False)
metric_type = Column(String, nullable=False) # 'steps', 'weight', 'sleep', etc.
source = Column(String, nullable=False) #'garmin', 'fitbit'
sync_status = Column(String, default='new') # 'new', 'updated', 'synced'
last_seen = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
__table_args__ = (
UniqueConstraint('date', 'metric_type', 'source', name='uq_health_state'),
)

View File

@@ -0,0 +1,19 @@
from sqlalchemy import Column, Integer, String, DateTime, Text, Boolean, JSON, func
from .base import Base
class Job(Base):
__tablename__ = 'jobs'
id = Column(String, primary_key=True, index=True)
operation = Column(String, nullable=False)
status = Column(String, nullable=False, default='running')
start_time = Column(DateTime(timezone=True), nullable=False)
end_time = Column(DateTime(timezone=True), nullable=True)
progress = Column(Integer, default=0)
message = Column(Text, nullable=True)
result = Column(JSON, nullable=True)
cancel_requested = Column(Boolean, default=False)
paused = Column(Boolean, default=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())

View File

@@ -0,0 +1,20 @@
from sqlalchemy import Column, Integer, String, DateTime, Boolean, Text
from sqlalchemy.sql import func
from .base import Base
class ScheduledJob(Base):
__tablename__ = 'scheduled_jobs'
id = Column(Integer, primary_key=True, index=True)
job_type = Column(String, nullable=False) # e.g. 'fitbit_weight_sync'
name = Column(String, nullable=False)
interval_minutes = Column(Integer, nullable=False, default=60)
params = Column(Text, nullable=True) # JSON string
enabled = Column(Boolean, default=True)
last_run = Column(DateTime(timezone=True), nullable=True)
next_run = Column(DateTime(timezone=True), nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())

View File

@@ -8,6 +8,7 @@ class WeightRecord(Base):
id = Column(Integer, primary_key=True, index=True)
fitbit_id = Column(String, unique=True, nullable=False) # Original Fitbit ID to prevent duplicates
weight = Column(Float, nullable=False) # Weight value
bmi = Column(Float, nullable=True) # BMI value
unit = Column(String, nullable=False) # Unit (e.g., 'kg', 'lbs')
date = Column(DateTime, nullable=False) # Date of measurement
timestamp = Column(DateTime, nullable=False) # Exact timestamp