154 lines
4.6 KiB
Python
154 lines
4.6 KiB
Python
import json
|
|
import os
|
|
from typing import Optional
|
|
|
|
|
|
def load_workouts(file_path: str) -> list:
|
|
try:
|
|
with open(file_path, 'r', encoding='utf-8') as file:
|
|
workouts = json.load(file)
|
|
return workouts if isinstance(workouts, list) else []
|
|
except (FileNotFoundError, json.JSONDecodeError, IOError):
|
|
return []
|
|
|
|
|
|
def filter_by_type(workouts: list, workout_type: str) -> list:
|
|
return [w for w in workouts if w.get('type') == workout_type]
|
|
|
|
|
|
def filter_by_date_range(workouts: list, start_date: str, end_date: str) -> list:
|
|
return [w for w in workouts if start_date <= w.get('date', '') <= end_date]
|
|
|
|
|
|
def total_calories(workouts: list) -> int:
|
|
return sum(w.get('calories', 0) for w in workouts)
|
|
|
|
|
|
def average_duration(workouts: list) -> float:
|
|
if not workouts:
|
|
return 0.0
|
|
total_duration = sum(w.get('duration_min', 0) for w in workouts)
|
|
return total_duration / len(workouts)
|
|
|
|
|
|
def best_distance(workouts: list) -> Optional[dict]:
|
|
distance_workouts = [w for w in workouts if w.get('distance_km', 0) > 0]
|
|
|
|
if not distance_workouts:
|
|
return None
|
|
|
|
best = max(distance_workouts, key=lambda x: x.get('distance_km', 0))
|
|
|
|
return {
|
|
'date': best.get('date'),
|
|
'type': best.get('type'),
|
|
'distance_km': best.get('distance_km')
|
|
}
|
|
|
|
|
|
def calories_per_minute(workout: dict) -> float:
|
|
duration = workout.get('duration_min', 0)
|
|
calories = workout.get('calories', 0)
|
|
|
|
if duration == 0:
|
|
return 0.0
|
|
return calories / duration
|
|
|
|
|
|
def add_workout(workouts: list, date: str, workout_type: str, duration_min: int,
|
|
distance_km: float, calories: int, avg_heart_rate: int = None) -> list:
|
|
valid_types = ['running', 'swimming', 'cycling', 'strength']
|
|
|
|
if workout_type not in valid_types:
|
|
raise ValueError(f"Неверный тип тренировки. Допустимые: {valid_types}")
|
|
|
|
if duration_min <= 0:
|
|
raise ValueError("Длительность должна быть больше 0")
|
|
|
|
if calories <= 0:
|
|
raise ValueError("Калории должны быть больше 0")
|
|
|
|
if distance_km < 0:
|
|
raise ValueError("Дистанция не может быть отрицательной")
|
|
|
|
workout = {
|
|
'date': date,
|
|
'type': workout_type,
|
|
'duration_min': duration_min,
|
|
'distance_km': distance_km,
|
|
'calories': calories
|
|
}
|
|
|
|
if avg_heart_rate is not None:
|
|
workout['avg_heart_rate'] = avg_heart_rate
|
|
|
|
workouts.append(workout)
|
|
return workouts
|
|
|
|
|
|
def get_heart_rate_zones(workouts: list) -> dict:
|
|
zones = {
|
|
'low': 0,
|
|
'moderate': 0,
|
|
'high': 0
|
|
}
|
|
|
|
for workout in workouts:
|
|
heart_rate = workout.get('avg_heart_rate')
|
|
if heart_rate is not None:
|
|
if heart_rate < 120:
|
|
zones['low'] += 1
|
|
elif heart_rate <= 150:
|
|
zones['moderate'] += 1
|
|
else:
|
|
zones['high'] += 1
|
|
|
|
return zones
|
|
|
|
|
|
def generate_monthly_report(workouts: list, year: int, month: int) -> dict:
|
|
month_workouts = []
|
|
for w in workouts:
|
|
date = w.get('date', '')
|
|
if date and date.startswith(f"{year}-{month:02d}"):
|
|
month_workouts.append(w)
|
|
|
|
if not month_workouts:
|
|
return {
|
|
'total_workouts': 0,
|
|
'total_calories': 0,
|
|
'total_distance_km': 0.0,
|
|
'avg_duration_min': 0.0,
|
|
'workouts_by_type': {},
|
|
'most_frequent_type': ''
|
|
}
|
|
|
|
total_workouts = len(month_workouts)
|
|
total_calories = sum(w.get('calories', 0) for w in month_workouts)
|
|
|
|
distance_types = ['running', 'swimming', 'cycling']
|
|
total_distance = sum(w.get('distance_km', 0) for w in month_workouts
|
|
if w.get('type') in distance_types)
|
|
|
|
avg_duration = sum(w.get('duration_min', 0) for w in month_workouts) / total_workouts
|
|
|
|
workouts_by_type = {}
|
|
for w in month_workouts:
|
|
w_type = w.get('type')
|
|
workouts_by_type[w_type] = workouts_by_type.get(w_type, 0) + 1
|
|
|
|
if workouts_by_type:
|
|
max_count = max(workouts_by_type.values())
|
|
most_frequent = [t for t, c in workouts_by_type.items() if c == max_count]
|
|
most_frequent_type = sorted(most_frequent)[0]
|
|
else:
|
|
most_frequent_type = ''
|
|
|
|
return {
|
|
'total_workouts': total_workouts,
|
|
'total_calories': total_calories,
|
|
'total_distance_km': round(total_distance, 2),
|
|
'avg_duration_min': round(avg_duration, 1),
|
|
'workouts_by_type': workouts_by_type,
|
|
'most_frequent_type': most_frequent_type
|
|
} |