mirror of
https://github.com/ChrisSewell/LeosShoes.git
synced 2025-07-01 01:57:28 -04:00
258 lines
9.1 KiB
Python
258 lines
9.1 KiB
Python
"""Data models for weather and risk assessment."""
|
|
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
from typing import Optional, List
|
|
import sqlite3
|
|
import json
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
@dataclass
|
|
class WeatherHour:
|
|
"""Represents weather data for a single hour."""
|
|
datetime: datetime
|
|
temperature_f: float
|
|
uv_index: Optional[float]
|
|
condition: str
|
|
is_forecast: bool = False
|
|
|
|
def to_dict(self) -> dict:
|
|
"""Convert to dictionary for JSON serialization."""
|
|
return {
|
|
'datetime': self.datetime.isoformat(),
|
|
'temperature_f': self.temperature_f,
|
|
'uv_index': self.uv_index,
|
|
'condition': self.condition,
|
|
'is_forecast': self.is_forecast
|
|
}
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: dict) -> 'WeatherHour':
|
|
"""Create from dictionary."""
|
|
return cls(
|
|
datetime=datetime.fromisoformat(data['datetime']),
|
|
temperature_f=data['temperature_f'],
|
|
uv_index=data.get('uv_index'),
|
|
condition=data['condition'],
|
|
is_forecast=data.get('is_forecast', False)
|
|
)
|
|
|
|
@dataclass
|
|
class RiskScore:
|
|
"""Represents a risk score for a specific hour."""
|
|
datetime: datetime
|
|
temperature_score: float
|
|
uv_score: float
|
|
condition_score: float
|
|
accumulated_heat_score: float
|
|
surface_recovery_score: float
|
|
total_score: float
|
|
recommend_shoes: bool
|
|
|
|
def to_dict(self) -> dict:
|
|
"""Convert to dictionary for JSON serialization."""
|
|
return {
|
|
'datetime': self.datetime.isoformat(),
|
|
'temperature_score': self.temperature_score,
|
|
'uv_score': self.uv_score,
|
|
'condition_score': self.condition_score,
|
|
'accumulated_heat_score': self.accumulated_heat_score,
|
|
'surface_recovery_score': self.surface_recovery_score,
|
|
'total_score': self.total_score,
|
|
'recommend_shoes': self.recommend_shoes
|
|
}
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: dict) -> 'RiskScore':
|
|
"""Create from dictionary."""
|
|
return cls(
|
|
datetime=datetime.fromisoformat(data['datetime']),
|
|
temperature_score=data['temperature_score'],
|
|
uv_score=data['uv_score'],
|
|
condition_score=data['condition_score'],
|
|
accumulated_heat_score=data['accumulated_heat_score'],
|
|
surface_recovery_score=data['surface_recovery_score'],
|
|
total_score=data['total_score'],
|
|
recommend_shoes=data['recommend_shoes']
|
|
)
|
|
|
|
class DatabaseManager:
|
|
"""Manages database operations for weather and risk data."""
|
|
|
|
def __init__(self, db_path: str):
|
|
self.db_path = db_path
|
|
self.init_database()
|
|
|
|
def init_database(self):
|
|
"""Initialize database tables."""
|
|
conn = sqlite3.connect(self.db_path)
|
|
try:
|
|
cursor = conn.cursor()
|
|
|
|
# Weather data table
|
|
cursor.execute('''
|
|
CREATE TABLE IF NOT EXISTS weather_data (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
datetime TEXT UNIQUE,
|
|
temperature_f REAL,
|
|
uv_index REAL,
|
|
condition TEXT,
|
|
is_forecast BOOLEAN,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
)
|
|
''')
|
|
|
|
# Risk scores table
|
|
cursor.execute('''
|
|
CREATE TABLE IF NOT EXISTS risk_scores (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
datetime TEXT UNIQUE,
|
|
temperature_score REAL,
|
|
uv_score REAL,
|
|
condition_score REAL,
|
|
accumulated_heat_score REAL,
|
|
surface_recovery_score REAL,
|
|
total_score REAL,
|
|
recommend_shoes BOOLEAN,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
)
|
|
''')
|
|
|
|
# Create indexes for better performance
|
|
cursor.execute('CREATE INDEX IF NOT EXISTS idx_weather_datetime ON weather_data(datetime)')
|
|
cursor.execute('CREATE INDEX IF NOT EXISTS idx_risk_datetime ON risk_scores(datetime)')
|
|
|
|
conn.commit()
|
|
logger.info("Database initialized successfully")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error initializing database: {e}")
|
|
raise
|
|
finally:
|
|
conn.close()
|
|
|
|
def save_weather_data(self, weather_hours: List[WeatherHour]):
|
|
"""Save weather data to database."""
|
|
conn = sqlite3.connect(self.db_path)
|
|
try:
|
|
cursor = conn.cursor()
|
|
|
|
for hour in weather_hours:
|
|
cursor.execute('''
|
|
INSERT OR REPLACE INTO weather_data
|
|
(datetime, temperature_f, uv_index, condition, is_forecast)
|
|
VALUES (?, ?, ?, ?, ?)
|
|
''', (
|
|
hour.datetime.isoformat(),
|
|
hour.temperature_f,
|
|
hour.uv_index,
|
|
hour.condition,
|
|
hour.is_forecast
|
|
))
|
|
|
|
conn.commit()
|
|
logger.info(f"Saved {len(weather_hours)} weather records")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error saving weather data: {e}")
|
|
raise
|
|
finally:
|
|
conn.close()
|
|
|
|
def save_risk_scores(self, risk_scores: List[RiskScore]):
|
|
"""Save risk scores to database."""
|
|
conn = sqlite3.connect(self.db_path)
|
|
try:
|
|
cursor = conn.cursor()
|
|
|
|
for score in risk_scores:
|
|
cursor.execute('''
|
|
INSERT OR REPLACE INTO risk_scores
|
|
(datetime, temperature_score, uv_score, condition_score,
|
|
accumulated_heat_score, surface_recovery_score, total_score, recommend_shoes)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
''', (
|
|
score.datetime.isoformat(),
|
|
score.temperature_score,
|
|
score.uv_score,
|
|
score.condition_score,
|
|
score.accumulated_heat_score,
|
|
score.surface_recovery_score,
|
|
score.total_score,
|
|
score.recommend_shoes
|
|
))
|
|
|
|
conn.commit()
|
|
logger.info(f"Saved {len(risk_scores)} risk score records")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error saving risk scores: {e}")
|
|
raise
|
|
finally:
|
|
conn.close()
|
|
|
|
def get_weather_data(self, start_date: datetime, end_date: datetime) -> List[WeatherHour]:
|
|
"""Retrieve weather data for a date range."""
|
|
conn = sqlite3.connect(self.db_path)
|
|
try:
|
|
cursor = conn.cursor()
|
|
cursor.execute('''
|
|
SELECT datetime, temperature_f, uv_index, condition, is_forecast
|
|
FROM weather_data
|
|
WHERE datetime BETWEEN ? AND ?
|
|
ORDER BY datetime
|
|
''', (start_date.isoformat(), end_date.isoformat()))
|
|
|
|
results = []
|
|
for row in cursor.fetchall():
|
|
results.append(WeatherHour(
|
|
datetime=datetime.fromisoformat(row[0]),
|
|
temperature_f=row[1],
|
|
uv_index=row[2],
|
|
condition=row[3],
|
|
is_forecast=bool(row[4])
|
|
))
|
|
|
|
return results
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error retrieving weather data: {e}")
|
|
raise
|
|
finally:
|
|
conn.close()
|
|
|
|
def get_risk_scores(self, start_date: datetime, end_date: datetime) -> List[RiskScore]:
|
|
"""Retrieve risk scores for a date range."""
|
|
conn = sqlite3.connect(self.db_path)
|
|
try:
|
|
cursor = conn.cursor()
|
|
cursor.execute('''
|
|
SELECT datetime, temperature_score, uv_score, condition_score,
|
|
accumulated_heat_score, surface_recovery_score, total_score, recommend_shoes
|
|
FROM risk_scores
|
|
WHERE datetime BETWEEN ? AND ?
|
|
ORDER BY datetime
|
|
''', (start_date.isoformat(), end_date.isoformat()))
|
|
|
|
results = []
|
|
for row in cursor.fetchall():
|
|
results.append(RiskScore(
|
|
datetime=datetime.fromisoformat(row[0]),
|
|
temperature_score=row[1],
|
|
uv_score=row[2],
|
|
condition_score=row[3],
|
|
accumulated_heat_score=row[4],
|
|
surface_recovery_score=row[5],
|
|
total_score=row[6],
|
|
recommend_shoes=bool(row[7])
|
|
))
|
|
|
|
return results
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error retrieving risk scores: {e}")
|
|
raise
|
|
finally:
|
|
conn.close() |