mirror of
https://github.com/sstent/FitTrack_GarminSync.git
synced 2026-01-26 17:12:00 +00:00
feat: Add --debug option to CLI for verbose output
This commit introduces a global option to the GarminSync CLI, providing verbose logging and diagnostic information for troubleshooting. Key changes include: - Implemented a to manage and propagate the debug flag across CLI commands. - Refactored in to accept and utilize the debug flag, enabling detailed logging of HTTP requests and responses. - Updated CLI commands (, ) to access the from the . - Resolved circular import by extracting into a dedicated module. - Configured for Poetry-based dependency management. - Addressed various type hinting issues and linting warnings across the CLI codebase to maintain code quality.
This commit is contained in:
@@ -1,13 +1,11 @@
|
||||
import os
|
||||
import yaml # type: ignore[import-untyped]
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
class ConfigManager:
|
||||
"""Configuration management utilities for YAML config"""
|
||||
|
||||
|
||||
def __init__(self, config_path: Optional[Path] = None):
|
||||
if config_path is None:
|
||||
# Use default location in user's home directory
|
||||
@@ -16,18 +14,18 @@ class ConfigManager:
|
||||
else:
|
||||
self.config_path = config_path
|
||||
self.config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
self.config = self._load_config()
|
||||
|
||||
def _load_config(self) -> Dict[str, Any]:
|
||||
"""Load configuration from YAML file"""
|
||||
if self.config_path.exists():
|
||||
with open(self.config_path, "r") as f:
|
||||
with open(self.config_path, 'r') as f:
|
||||
return yaml.safe_load(f) or {}
|
||||
else:
|
||||
# Return default configuration
|
||||
default_config = {
|
||||
"api_base_url": "https://api.garmin.com",
|
||||
"api_base_url": "http://localhost:8001", # Default to local GarminSync service
|
||||
"default_timeout": 30,
|
||||
"output_format": "table", # Options: table, json, csv
|
||||
"remember_login": True,
|
||||
@@ -37,7 +35,7 @@ class ConfigManager:
|
||||
|
||||
def _save_config(self, config: Dict[str, Any]) -> None:
|
||||
"""Save configuration to YAML file"""
|
||||
with open(self.config_path, "w") as f:
|
||||
with open(self.config_path, 'w') as f:
|
||||
yaml.dump(config, f)
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
@@ -52,4 +50,4 @@ class ConfigManager:
|
||||
def update(self, updates: Dict[str, Any]) -> None:
|
||||
"""Update multiple configuration values"""
|
||||
self.config.update(updates)
|
||||
self._save_config(self.config)
|
||||
self._save_config(self.config)
|
||||
@@ -1,27 +1,28 @@
|
||||
import csv
|
||||
import json
|
||||
import csv
|
||||
from io import StringIO
|
||||
from typing import Any, Dict, List, Mapping, Set, Union
|
||||
from typing import List, Dict, Any, Union, Set, Mapping, cast # Import Set, Mapping, and cast
|
||||
from csv import DictWriter # Removed CsvWriter from import
|
||||
|
||||
|
||||
def format_output(data: Any, format_type: str = "table") -> str:
|
||||
def format_output(data: Union[Dict, List, Any], output_format: str = "table") -> str:
|
||||
"""Format output in multiple formats (JSON, table, CSV)"""
|
||||
|
||||
if format_type.lower() == "json":
|
||||
|
||||
if output_format.lower() == "json":
|
||||
return json.dumps(data, indent=2, default=str)
|
||||
|
||||
elif format_type.lower() == "csv":
|
||||
|
||||
elif output_format.lower() == "csv":
|
||||
return _format_as_csv(data)
|
||||
|
||||
elif format_type.lower() == "table":
|
||||
|
||||
elif output_format.lower() == "table":
|
||||
return _format_as_table(data)
|
||||
|
||||
|
||||
else:
|
||||
# Default to table format
|
||||
return _format_as_table(data)
|
||||
|
||||
|
||||
def _format_as_table(data: Any) -> str:
|
||||
def _format_as_table(data: Union[Dict, List, Any]) -> str:
|
||||
"""Format data as a human-readable table"""
|
||||
if isinstance(data, dict):
|
||||
# Format dictionary as key-value pairs
|
||||
@@ -29,70 +30,71 @@ def _format_as_table(data: Any) -> str:
|
||||
for key, value in data.items():
|
||||
lines.append(f"{key:<20} | {value}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
elif isinstance(data, list):
|
||||
if not data:
|
||||
return "No data to display"
|
||||
|
||||
|
||||
if isinstance(data[0], dict):
|
||||
# Format list of dictionaries as a table
|
||||
if not data[0]:
|
||||
return "No data to display"
|
||||
|
||||
|
||||
headers = list(data[0].keys())
|
||||
# Create header row
|
||||
header_line = " | ".join(f"{h:<15}" for h in headers)
|
||||
separator = "-+-".join("-" * 15 for _ in headers)
|
||||
|
||||
|
||||
# Create data rows
|
||||
rows = [header_line, separator]
|
||||
for item in data:
|
||||
row = " | ".join(f"{str(item.get(h, '')):<15}" for h in headers)
|
||||
rows.append(row)
|
||||
|
||||
|
||||
return "\n".join(rows)
|
||||
else:
|
||||
# Format simple list
|
||||
return "\n".join(str(item) for item in data)
|
||||
|
||||
|
||||
else:
|
||||
# For other types, just convert to string
|
||||
return str(data)
|
||||
|
||||
|
||||
def _format_as_csv(data: Any) -> str:
|
||||
def _format_as_csv(data: Union[Dict, List, Any]) -> str:
|
||||
"""Format data as CSV"""
|
||||
if isinstance(data, dict):
|
||||
# Convert single dict to list with one item for CSV processing
|
||||
data = [data]
|
||||
|
||||
|
||||
if isinstance(data, list) and data and isinstance(data[0], dict):
|
||||
# Format list of dictionaries as CSV
|
||||
output = StringIO()
|
||||
if data:
|
||||
fieldnames: Set[str] = set()
|
||||
fieldnames: List[str] = [] # Initialize as List[str]
|
||||
unique_fieldnames: Set[str] = set() # Use Set for uniqueness
|
||||
for row in data:
|
||||
fieldnames.update(row.keys())
|
||||
fieldnames = sorted(list(fieldnames))
|
||||
|
||||
writer_csv: csv.DictWriter = csv.DictWriter(output, fieldnames=fieldnames)
|
||||
writer_csv.writeheader()
|
||||
unique_fieldnames.update(row.keys())
|
||||
fieldnames = sorted(list(unique_fieldnames)) # Convert to list and sort
|
||||
|
||||
writer: DictWriter[Any] = csv.DictWriter(output, fieldnames=fieldnames) # Explicitly type writer
|
||||
writer.writeheader()
|
||||
for row in data:
|
||||
writer_csv.writerow({k: v for k, v in row.items() if k in fieldnames})
|
||||
|
||||
writer.writerow(cast(Mapping[str, Any], {k: v for k, v in row.items() if k in fieldnames})) # Cast to Mapping[str, Any]
|
||||
|
||||
return output.getvalue()
|
||||
|
||||
|
||||
elif isinstance(data, list):
|
||||
# Format simple list as CSV with one column
|
||||
output = StringIO()
|
||||
writer_csv: csv.writer = csv.writer(output)
|
||||
simple_writer = csv.writer(output) # Removed type hint CsvWriter
|
||||
for item in data:
|
||||
writer_csv.writerow([item])
|
||||
simple_writer.writerow([item])
|
||||
return output.getvalue()
|
||||
|
||||
|
||||
else:
|
||||
# For other types, just convert to string and put in one cell
|
||||
output = StringIO()
|
||||
writer_csv: csv.writer = csv.writer(output)
|
||||
writer_csv.writerow([str(data)])
|
||||
return output.getvalue()
|
||||
simple_writer = csv.writer(output) # Removed type hint CsvWriter
|
||||
simple_writer.writerow([str(data)])
|
||||
return output.getvalue()
|
||||
Reference in New Issue
Block a user