python v1 - working list, download issue

This commit is contained in:
2025-08-08 12:48:15 -07:00
parent 760868c98c
commit 1dbd1321ff
3 changed files with 116 additions and 93 deletions

View File

@@ -15,13 +15,18 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
# Copy and install Python dependencies # Copy and install Python dependencies
COPY requirements.txt . COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
# Copy application code # Copy application code
COPY . . COPY garminsync/ ./garminsync/
# Create data directory
RUN mkdir -p /app/data
# Set environment variables from .env file # Set environment variables from .env file
ENV ENV_FILE=/app/.env ENV ENV_FILE=/app/.env
ENV DATA_DIR=/app/data
# Set the entrypoint to run the CLI # Set the entrypoint to run the CLI
ENTRYPOINT ["python", "-m", "garminsync.cli"] ENTRYPOINT ["python", "-m", "garminsync.cli"]

View File

@@ -1,66 +1,72 @@
import os
import typer import typer
from typing_extensions import Annotated
from .config import load_config from .config import load_config
# Initialize environment variables # Initialize environment variables
load_config() load_config()
app = typer.Typer() app = typer.Typer(help="GarminSync - Download Garmin Connect activities", rich_markup_mode=None)
@app.command(name="list") @app.command("list")
def list_activities( def list_activities(
all: bool = typer.Option(False, "--all", help="List all activities"), all_activities: Annotated[bool, typer.Option("--all", help="List all activities")] = False,
missing: bool = typer.Option(False, "--missing", help="List missing activities"), missing: Annotated[bool, typer.Option("--missing", help="List missing activities")] = False,
downloaded: bool = typer.Option(False, "--downloaded", help="List downloaded activities") downloaded: Annotated[bool, typer.Option("--downloaded", help="List downloaded activities")] = False
): ):
""" """List activities based on specified filters"""
List activities based on specified filters
"""
from tqdm import tqdm from tqdm import tqdm
from .database import get_session, Activity from .database import get_session, Activity
from .garmin import GarminClient from .garmin import GarminClient
# Validate input # Validate input
if not any([all, missing, downloaded]): if not any([all_activities, missing, downloaded]):
typer.echo("Error: Please specify at least one filter option (--all, --missing, --downloaded)") typer.echo("Error: Please specify at least one filter option (--all, --missing, --downloaded)")
raise typer.Exit(code=1) raise typer.Exit(code=1)
client = GarminClient() try:
session = get_session() client = GarminClient()
session = get_session()
# Sync database with latest activities
typer.echo("Syncing activities from Garmin Connect...") # Sync database with latest activities
from .database import sync_database typer.echo("Syncing activities from Garmin Connect...")
sync_database(client) from .database import sync_database
sync_database(client)
# Build query based on filters
query = session.query(Activity) # Build query based on filters
query = session.query(Activity)
if all:
pass # Return all activities if all_activities:
elif missing: pass # Return all activities
query = query.filter_by(downloaded=False) elif missing:
elif downloaded: query = query.filter_by(downloaded=False)
query = query.filter_by(downloaded=True) elif downloaded:
query = query.filter_by(downloaded=True)
# Execute query and display results
activities = query.all() # Execute query and display results
if not activities: activities = query.all()
typer.echo("No activities found matching your criteria") if not activities:
return typer.echo("No activities found matching your criteria")
return
# Display results with progress bar
typer.echo(f"Found {len(activities)} activities:") # Display results with progress bar
for activity in tqdm(activities, desc="Listing activities"): typer.echo(f"Found {len(activities)} activities:")
status = "Downloaded" if activity.downloaded else "Missing" for activity in tqdm(activities, desc="Listing activities"):
typer.echo(f"- ID: {activity.activity_id}, Start: {activity.start_time}, Status: {status}") status = "Downloaded" if activity.downloaded else "Missing"
typer.echo(f"- ID: {activity.activity_id}, Start: {activity.start_time}, Status: {status}")
except Exception as e:
typer.echo(f"Error: {str(e)}")
raise typer.Exit(code=1)
finally:
if 'session' in locals():
session.close()
@app.command() @app.command("download")
def download( def download(
missing: bool = typer.Option(False, "--missing", help="Download missing activities") missing: Annotated[bool, typer.Option("--missing", help="Download missing activities")] = False
): ):
""" """Download activities based on specified filters"""
Download activities based on specified filters
"""
from tqdm import tqdm from tqdm import tqdm
from pathlib import Path from pathlib import Path
from .database import get_session, Activity from .database import get_session, Activity
@@ -71,50 +77,61 @@ def download(
typer.echo("Error: Currently only --missing downloads are supported") typer.echo("Error: Currently only --missing downloads are supported")
raise typer.Exit(code=1) raise typer.Exit(code=1)
client = GarminClient() try:
session = get_session() client = GarminClient()
session = get_session()
# Sync database with latest activities
typer.echo("Syncing activities from Garmin Connect...") # Sync database with latest activities
from .database import sync_database typer.echo("Syncing activities from Garmin Connect...")
sync_database(client) from .database import sync_database
sync_database(client)
# Get missing activities
activities = session.query(Activity).filter_by(downloaded=False).all() # Get missing activities
if not activities: activities = session.query(Activity).filter_by(downloaded=False).all()
typer.echo("No missing activities found") if not activities:
return typer.echo("No missing activities found")
return
# Create data directory if it doesn't exist
data_dir = Path(os.getenv("DATA_DIR", "data")) # Create data directory if it doesn't exist
data_dir.mkdir(parents=True, exist_ok=True) data_dir = Path(os.getenv("DATA_DIR", "data"))
data_dir.mkdir(parents=True, exist_ok=True)
# Download activities with progress bar
typer.echo(f"Downloading {len(activities)} missing activities...") # Download activities with progress bar
for activity in tqdm(activities, desc="Downloading"): typer.echo(f"Downloading {len(activities)} missing activities...")
try: for activity in tqdm(activities, desc="Downloading"):
# Download FIT data try:
fit_data = client.download_activity_fit(activity.activity_id) # Download FIT data
fit_data = client.download_activity_fit(activity.activity_id)
# Create filename-safe timestamp
timestamp = activity.start_time.replace(":", "-").replace(" ", "_") # Create filename-safe timestamp
filename = f"activity_{activity.activity_id}_{timestamp}.fit" timestamp = activity.start_time.replace(":", "-").replace(" ", "_")
filepath = data_dir / filename filename = f"activity_{activity.activity_id}_{timestamp}.fit"
filepath = data_dir / filename
# Save file
with open(filepath, "wb") as f: # Save file
f.write(fit_data) with open(filepath, "wb") as f:
f.write(fit_data)
# Update database
activity.filename = str(filepath) # Update database
activity.downloaded = True activity.filename = str(filepath)
session.commit() activity.downloaded = True
session.commit()
except Exception as e:
typer.echo(f"Error downloading activity {activity.activity_id}: {str(e)}") except Exception as e:
session.rollback() typer.echo(f"Error downloading activity {activity.activity_id}: {str(e)}")
session.rollback()
typer.echo("Download completed successfully")
typer.echo("Download completed successfully")
except Exception as e:
typer.echo(f"Error: {str(e)}")
raise typer.Exit(code=1)
finally:
if 'session' in locals():
session.close()
def main():
app()
if __name__ == "__main__": if __name__ == "__main__":
app() main()

View File

@@ -1,5 +1,6 @@
typer>=0.12.3,<0.13.0 typer==0.9.0
click==8.1.7
python-dotenv==1.0.0 python-dotenv==1.0.0
garminconnect==0.2.28 garminconnect==0.2.28
sqlalchemy==2.0.23 sqlalchemy==2.0.23
tqdm==4.66.1 tqdm==4.66.1