"""Main application for paw burn risk assessment.""" import logging import argparse from datetime import datetime, timedelta from typing import Optional import json from config import get_config, AppConfig from models import DatabaseManager from weather_api import create_weather_client from risk_calculator import create_risk_calculator from plotting import create_plotter # Set up logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) class PawRiskApp: """Main application class for paw burn risk assessment.""" def __init__(self, config: Optional[AppConfig] = None): self.config = config or get_config() self.db_manager = DatabaseManager(self.config.database_path) self.weather_client = create_weather_client() self.risk_calculator = create_risk_calculator() self.plotter = create_plotter() def fetch_and_analyze_today(self, location: Optional[str] = None) -> dict: """Fetch weather data and analyze risk for today.""" location = location or self.config.default_location print(f"šŸŒ¤ļø Fetching weather data for {location}...") try: # Fetch complete day weather data weather_hours = self.weather_client.get_full_day_weather(location) if not weather_hours: return {"error": "No weather data available"} print(f"šŸ“Š Retrieved {len(weather_hours)} hours of weather data") # Save weather data to database self.db_manager.save_weather_data(weather_hours) # Calculate risk scores print("🧮 Calculating paw burn risk scores...") risk_scores = self.risk_calculator.calculate_risk_scores(weather_hours) if not risk_scores: return {"error": "Unable to calculate risk scores"} # Save risk scores to database self.db_manager.save_risk_scores(risk_scores) # Generate recommendations recommendations = self.risk_calculator.generate_recommendations(risk_scores) return { "weather_hours": weather_hours, "risk_scores": risk_scores, "recommendations": recommendations, "location": location } except Exception as e: logger.error(f"Error during analysis: {e}") return {"error": str(e)} def format_time(self, dt: datetime) -> str: """Format time based on user's preference.""" if self.config.risk_config.use_24hr_time: return dt.strftime("%H:%M") else: return dt.strftime("%I:%M %p") def print_summary(self, analysis_result: dict): """Print a formatted summary of the analysis.""" if "error" in analysis_result: print(f"āŒ Error: {analysis_result['error']}") return recommendations = analysis_result["recommendations"] location = analysis_result["location"] print("\n" + "="*60) print(f"šŸ• PAW BURN RISK ASSESSMENT - {location.upper()}") print("="*60) # Summary statistics summary = recommendations["summary"] print(f"\nšŸ“ˆ DAILY SUMMARY:") print(f" • Total Hours Analyzed: {summary['total_hours_analyzed']}") print(f" • High Risk Hours: {summary['high_risk_hours']}") print(f" • Maximum Risk Score: {summary['max_risk_score']}/10") print(f" • Average Risk Score: {summary['average_risk_score']}/10") print(f" • Peak Risk Time: {summary['peak_risk_time']}") print(f" • Continuous Risk Periods: {summary['continuous_risk_blocks']}") # Risk periods if recommendations["risk_periods"]: print(f"\nā° HIGH RISK TIME PERIODS:") for period in recommendations["risk_periods"]: print(f" • {period['start']} - {period['end']} ({period['duration_hours']} hours)") # Recommendations print(f"\nšŸ’” RECOMMENDATIONS:") for rec in recommendations["recommendations"]: print(f" {rec}") print("\n" + "="*60) def print_detailed_hourly(self, analysis_result: dict): """Print detailed hourly breakdown.""" if "error" in analysis_result: return weather_hours = analysis_result["weather_hours"] risk_scores = analysis_result["risk_scores"] print("\nšŸ• HOURLY BREAKDOWN:") print("-" * 80) print(f"{'Time':>8} {'Temp':>6} {'UV':>4} {'Condition':>12} {'Risk':>6} {'Shoes':>7}") print("-" * 80) for weather, risk in zip(weather_hours, risk_scores): time_str = self.format_time(weather.datetime) temp_str = f"{weather.temperature_f:.0f}°F" uv_str = f"{weather.uv_index:.1f}" if weather.uv_index else "N/A" condition_short = weather.condition[:12] risk_str = f"{risk.total_score:.1f}" shoes_str = "YES" if risk.recommend_shoes else "no" shoes_color = "āš ļø " if risk.recommend_shoes else "āœ… " print(f"{time_str:>8} {temp_str:>6} {uv_str:>4} {condition_short:>12} " f"{risk_str:>6} {shoes_color}{shoes_str:>5}") def create_plots(self, analysis_result: dict, save_plots: bool = False): """Create and display plots.""" if "error" in analysis_result: return weather_hours = analysis_result["weather_hours"] risk_scores = analysis_result["risk_scores"] recommendations = analysis_result["recommendations"] location = analysis_result["location"] if save_plots: print("\nšŸ“Š Generating and saving visualizations...") else: print("\nšŸ“Š Generating visualizations...") # Create timestamp for file names timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") location_safe = location.replace(" ", "_").replace(",", "") try: # Main risk timeline save_path = f"risk_timeline_{location_safe}_{timestamp}.png" if save_plots else None self.plotter.plot_risk_timeline( risk_scores, weather_hours, threshold=self.config.risk_config.risk_threshold_shoes, save_path=save_path, show=not save_plots ) # Component breakdown save_path = f"risk_components_{location_safe}_{timestamp}.png" if save_plots else None self.plotter.plot_risk_components( risk_scores, save_path=save_path, show=not save_plots ) # Summary dashboard save_path = f"risk_dashboard_{location_safe}_{timestamp}.png" if save_plots else None self.plotter.create_summary_dashboard( risk_scores, weather_hours, recommendations, save_path=save_path, show=not save_plots ) except Exception as e: logger.error(f"Error creating plots: {e}") print(f"āš ļø Error creating plots: {e}") def main(): """Main entry point for the application.""" parser = argparse.ArgumentParser(description="Paw Burn Risk Assessment Tool") parser.add_argument("--location", "-l", type=str, help="Location for weather data (city name, zip code, or coordinates)") parser.add_argument("--detailed", "-d", action="store_true", help="Show detailed hourly breakdown") parser.add_argument("--plot", "-p", action="store_true", help="Show plots") parser.add_argument("--save-plots", "-s", action="store_true", help="Save plots to files") parser.add_argument("--no-recommendations", action="store_true", help="Skip recommendations") parser.add_argument("--config-check", action="store_true", help="Check configuration and exit") args = parser.parse_args() try: # Initialize application print("🐾 Paw Burn Risk Assessment Tool") print("=" * 40) # Check configuration if requested if args.config_check: try: config = get_config() print("āœ… Configuration loaded successfully") print(f"API Key: {'Set' if config.weather_api_key else 'NOT SET'}") print(f"Default Location: {config.default_location}") print(f"Database Path: {config.database_path}") print(f"Risk Threshold: {config.risk_config.risk_threshold_shoes}") print(f"Time Format: {'24-hour' if config.risk_config.use_24hr_time else '12-hour'}") return except Exception as e: print(f"āŒ Configuration error: {e}") return app = PawRiskApp() # Run analysis analysis_result = app.fetch_and_analyze_today(args.location) # Display results if args.detailed: app.print_detailed_hourly(analysis_result) if args.plot or args.save_plots: app.create_plots(analysis_result, save_plots=args.save_plots) # Show summary unless explicitly disabled if not args.no_recommendations: app.print_summary(analysis_result) except KeyboardInterrupt: print("\nšŸ‘‹ Goodbye!") except Exception as e: logger.error(f"Application error: {e}") print(f"āŒ Application error: {e}") if __name__ == "__main__": main()