before claude fix #1

This commit is contained in:
2025-12-23 06:09:34 -08:00
parent c505fb69a6
commit a23fa1b30d
83 changed files with 5682 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
from sqlalchemy.ext.declarative import declarative_base
# Create a base class for all models to inherit from
Base = declarative_base()
# Import all models here to ensure they're registered with the Base
from .config import Configuration
from .api_token import APIToken
from .auth_status import AuthStatus
from .weight_record import WeightRecord
from .activity import Activity
from .health_metric import HealthMetric
from .sync_log import SyncLog

View File

@@ -0,0 +1,19 @@
from sqlalchemy import Column, Integer, String, DateTime, Text, LargeBinary
from sqlalchemy.sql import func
from ..models import Base
class Activity(Base):
__tablename__ = "activities"
id = Column(Integer, primary_key=True, index=True)
garmin_activity_id = Column(String, unique=True, nullable=False) # Original Garmin ID
activity_name = Column(String, nullable=True) # Name of the activity
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
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())

View File

@@ -0,0 +1,23 @@
from sqlalchemy import Column, Integer, String, DateTime
from sqlalchemy.sql import func
from ..models import Base
import json
class APIToken(Base):
__tablename__ = "api_tokens"
id = Column(Integer, primary_key=True, index=True)
token_type = Column(String, nullable=False) # 'fitbit' or 'garmin'
access_token = Column(String, nullable=False) # This should be encrypted in production
refresh_token = Column(String, nullable=True) # This should be encrypted in production
expires_at = Column(DateTime, nullable=False)
scopes = Column(String, nullable=True)
garth_oauth1_token = Column(String, nullable=True) # OAuth1 token for garmin (JSON)
garth_oauth2_token = Column(String, nullable=True) # OAuth2 token for garmin (JSON)
# MFA session fields for garmin
mfa_session_id = Column(String, nullable=True)
mfa_resume_data = Column(String, nullable=True) # JSON blob
mfa_expires_at = Column(DateTime, nullable=True)
last_used = Column(DateTime, nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())

View File

@@ -0,0 +1,17 @@
from sqlalchemy import Column, Integer, String, DateTime, Boolean
from sqlalchemy.sql import func
from ..models import Base
class AuthStatus(Base):
__tablename__ = "auth_status"
id = Column(Integer, primary_key=True, index=True)
service_type = Column(String, nullable=False) # 'fitbit' or 'garmin'
username = Column(String, nullable=True) # Masked username for security display
authenticated = Column(Boolean, default=False) # Whether currently authenticated
token_expires_at = Column(DateTime, nullable=True) # When current token expires
last_login = Column(DateTime, nullable=True) # Last successful login
is_china = Column(Boolean, default=False) # For Garmin - whether using garmin.cn domain
last_check = Column(DateTime, nullable=True) # When status was last checked
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, JSON
from sqlalchemy.sql import func
from datetime import datetime
from ..models import Base
class Configuration(Base):
__tablename__ = "configurations"
id = Column(Integer, primary_key=True, index=True)
fitbit_client_id = Column(String, nullable=True)
fitbit_client_secret = Column(String, nullable=True) # This should be encrypted in production
garmin_username = Column(String, nullable=True)
garmin_password = Column(String, nullable=True) # This should be encrypted in production
sync_settings = Column(JSON, nullable=True) # JSON field for sync preferences
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())

View File

@@ -0,0 +1,17 @@
from sqlalchemy import Column, Integer, String, DateTime, Float, Text
from sqlalchemy.sql import func
from ..models import Base
class HealthMetric(Base):
__tablename__ = "health_metrics"
id = Column(Integer, primary_key=True, index=True)
metric_type = Column(String, nullable=False) # Type of metric (e.g., 'steps', 'heart_rate')
metric_value = Column(Float, nullable=False) # Value of the metric
unit = Column(String, nullable=True) # Unit of measurement
timestamp = Column(DateTime, nullable=False) # When the metric was recorded
date = Column(DateTime, nullable=False) # Date of the metric
source = Column(String, nullable=False) # Source of the metric ('garmin')
detailed_data = Column(Text, nullable=True) # Additional details (stored as JSON string)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())

View File

@@ -0,0 +1,18 @@
from sqlalchemy import Column, Integer, String, DateTime, Text
from sqlalchemy.sql import func
from ..models import Base
class SyncLog(Base):
__tablename__ = "sync_logs"
id = Column(Integer, primary_key=True, index=True)
operation = Column(String, nullable=False) # 'weight_sync', 'activity_archive', 'metrics_download'
status = Column(String, nullable=False) # 'started', 'in_progress', 'completed', 'failed'
message = Column(Text, nullable=True) # Status message or error details
start_time = Column(DateTime, nullable=False)
end_time = Column(DateTime, nullable=True) # When operation completed
records_processed = Column(Integer, default=0) # Number of records processed
records_failed = Column(Integer, default=0) # Number of records failed
user_id = Column(Integer, nullable=True) # Reference to user (if applicable)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())

View File

@@ -0,0 +1,17 @@
from sqlalchemy import Column, Integer, String, DateTime, Float
from sqlalchemy.sql import func
from ..models import Base
class WeightRecord(Base):
__tablename__ = "weight_records"
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
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
sync_status = Column(String, default='unsynced') # 'unsynced', 'synced', 'failed'
garmin_id = Column(String, nullable=True) # ID of record if synced to Garmin
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())