commit 3bf10a7461688acaaa201e82be2afecfb9cf7a0a Author: stud203788 Date: Thu Apr 16 20:33:55 2026 +0300 реализованы все функции и основной сценарий diff --git a/data/workouts.json b/data/workouts.json new file mode 100644 index 0000000..e685921 --- /dev/null +++ b/data/workouts.json @@ -0,0 +1,9 @@ +[ + { + "date": "2026-04-28", + "type": "swimming", + "duration_min": 1200, + "distance_km": 2.0, + "calories": 120 + } +] \ No newline at end of file diff --git a/fitness_tracker.py b/fitness_tracker.py new file mode 100644 index 0000000..3599dd1 --- /dev/null +++ b/fitness_tracker.py @@ -0,0 +1,154 @@ +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 + } \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..d99c3b0 --- /dev/null +++ b/main.py @@ -0,0 +1,175 @@ +from fitness_tracker import * +import os + + +def save_workouts(file_path: str, workouts: list) -> None: + try: + os.makedirs(os.path.dirname(file_path), exist_ok=True) + + with open(file_path, 'w', encoding='utf-8') as file: + json.dump(workouts, file, indent=2, ensure_ascii=False) + print(f"\nДанные сохранены в {file_path}") + except IOError as e: + print(f"\nОшибка при сохранении: {e}") + + +def main(): + print("=" * 60) + print("ФИТНЕС ТРЕКЕР - АНАЛИЗ ТРЕНИРОВОК") + print("=" * 60) + + FILE_PATH = "data/workouts.json" + + workouts = load_workouts(FILE_PATH) + print(f"\nЗагружено {len(workouts)} тренировок") + + print("\n" + "=" * 60) + print("ОБЩАЯ СТАТИСТИКА") + print("=" * 60) + + print(f"\nВсего тренировок: {len(workouts)}") + + total_cals = total_calories(workouts) + print(f"Всего калорий: {total_cals}") + + avg_dur = average_duration(workouts) + print(f"Средняя длительность: {avg_dur:.1f} минут") + + best = best_distance(workouts) + if best: + print(f"Лучшая дистанция: {best['distance_km']} км ({best['type']}, {best['date']})") + else: + print("Лучшая дистанция: нет тренировок с дистанцией") + + print("\n" + "=" * 60) + print("ФИЛЬТРАЦИЯ ПО ТИПУ ТРЕНИРОВКИ") + print("=" * 60) + + valid_types = ['running', 'swimming', 'cycling', 'strength'] + while True: + workout_type = input("\nВведите тип тренировки (running/swimming/cycling/strength): ").lower() + if workout_type in valid_types: + break + print(f"Неверный тип. Допустимые: {valid_types}") + + filtered = filter_by_type(workouts, workout_type) + print(f"\nТренировок типа '{workout_type}': {len(filtered)}") + + print("\n" + "=" * 60) + print("МЕСЯЧНЫЙ ОТЧЁТ") + print("=" * 60) + + while True: + try: + year = int(input("\nВведите год (например, 2024): ")) + month = int(input("Введите месяц (1-12): ")) + if 1 <= month <= 12: + break + print("Месяц должен быть от 1 до 12") + except ValueError: + print("Введите целое число") + + report = generate_monthly_report(workouts, year, month) + + print(f"\nОТЧЁТ ЗА {year}-{month:02d}") + print("-" * 40) + print(f"Всего тренировок: {report['total_workouts']}") + print(f"Всего калорий: {report['total_calories']}") + print(f"Общая дистанция: {report['total_distance_km']} км") + print(f"Средняя длительность: {report['avg_duration_min']} мин") + print("\nРаспределение по типам:") + for w_type, count in report['workouts_by_type'].items(): + print(f" {w_type}: {count}") + print(f"\nСамый частый тип: {report['most_frequent_type'] if report['most_frequent_type'] else 'нет данных'}") + + month_workouts = [w for w in workouts if w.get('date', '').startswith(f"{year}-{month:02d}")] + hr_zones = get_heart_rate_zones(month_workouts) + + print("\nПУЛЬСОВЫЕ ЗОНЫ (за месяц)") + print("-" * 40) + print(f"Низкая (< 120 bpm): {hr_zones['low']} тренировок") + print(f"Средняя (120-150 bpm): {hr_zones['moderate']} тренировок") + print(f"Высокая (> 150 bpm): {hr_zones['high']} тренировок") + + print("\n" + "=" * 60) + print("ДОБАВЛЕНИЕ НОВОЙ ТРЕНИРОВКИ") + print("=" * 60) + + add_new = input("\nДобавить новую тренировку? (да/нет): ").lower() + + if add_new in ['да', 'yes', 'y', 'д']: + print("\nВведите данные новой тренировки:") + + while True: + date = input("Дата (ГГГГ-ММ-ДД): ") + if len(date) == 10 and date[4] == '-' and date[7] == '-': + break + print("Неверный формат даты. Используйте ГГГГ-ММ-ДД") + + while True: + w_type = input("Тип (running/swimming/cycling/strength): ").lower() + if w_type in valid_types: + break + print(f"Неверный тип. Допустимые: {valid_types}") + + while True: + try: + duration = int(input("Длительность (минуты): ")) + if duration > 0: + break + print("Длительность должна быть больше 0") + except ValueError: + print("Введите целое число") + + while True: + try: + distance = float(input("Дистанция (км): ")) + if distance >= 0: + break + print("Дистанция не может быть отрицательной") + except ValueError: + print("Введите число") + + while True: + try: + calories = int(input("Калории: ")) + if calories > 0: + break + print("Калории должны быть больше 0") + except ValueError: + print("Введите целое число") + + hr_input = input("Средний пульс (опционально, Enter чтобы пропустить): ") + avg_heart_rate = int(hr_input) if hr_input.strip() else None + + try: + workouts = add_workout(workouts, date, w_type, duration, distance, calories, avg_heart_rate) + print(f"\nТренировка успешно добавлена") + except ValueError as e: + print(f"\nОшибка: {e}") + + save_workouts(FILE_PATH, workouts) + + print("\n" + "=" * 60) + print("ДЕМОНСТРАЦИЯ ФУНКЦИИ calories_per_minute") + print("=" * 60) + + if workouts: + first_workout = workouts[0] + intensity = calories_per_minute(first_workout) + print(f"\nПервая тренировка в списке:") + print(f"Дата: {first_workout.get('date')}") + print(f"Тип: {first_workout.get('type')}") + print(f"Длительность: {first_workout.get('duration_min')} мин") + print(f"Калории: {first_workout.get('calories')}") + print(f"\nИнтенсивность: {intensity:.2f} калорий в минуту") + else: + print("\nНет тренировок для демонстрации") + + print("\n" + "=" * 60) + print("ПРОГРАММА ЗАВЕРШЕНА") + print("=" * 60) + + +if __name__ == "__main__": + main() \ No newline at end of file