From fc869c60df8e3ff490349f3cd53396554fe38c47 Mon Sep 17 00:00:00 2001 From: stud203998 Date: Tue, 14 Apr 2026 22:02:46 +0300 Subject: [PATCH] Last version --- data/report.json | 25 +++++ data/students.json | 38 +++++++ src/main.py | 267 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 330 insertions(+) create mode 100644 data/report.json diff --git a/data/report.json b/data/report.json new file mode 100644 index 0000000..accd321 --- /dev/null +++ b/data/report.json @@ -0,0 +1,25 @@ +{ + "total_students": 6, + "average_grade_all": 82.83, + "top_student": { + "id": 4, + "name": "Елена Смирнова", + "courses": [ + "Программирование на Python", + "Базы данных" + ], + "grades": [ + 100.0, + 98.0, + 97.0 + ], + "avg_grade": 98.33333333333333 + }, + "course_popularity": { + "Математика": 3, + "Программирование на Python": 4, + "Физика": 3, + "Английский язык": 2, + "Базы данных": 1 + } +} \ No newline at end of file diff --git a/data/students.json b/data/students.json index e69de29..f5209e0 100644 --- a/data/students.json +++ b/data/students.json @@ -0,0 +1,38 @@ +[ + { + "id": 1, + "name": "Иван Петров", + "courses": ["Математика", "Программирование на Python", "Физика"], + "grades": [85.0, 92.0, 78.0] + }, + { + "id": 2, + "name": "Мария Сидорова", + "courses": ["Программирование на Python", "Английский язык"], + "grades": [95.0, 88.0, 91.0] + }, + { + "id": 3, + "name": "Алексей Иванов", + "courses": ["Математика", "Физика"], + "grades": [72.0, 68.0, 74.0] + }, + { + "id": 4, + "name": "Елена Смирнова", + "courses": ["Программирование на Python", "Базы данных"], + "grades": [100.0, 98.0, 97.0] + }, + { + "id": 5, + "name": "Дмитрий Козлов", + "courses": ["Математика"], + "grades": [60.0, 65.0] + }, + { + "id": 6, + "name": "Ольга Новикова", + "courses": ["Программирование на Python", "Английский язык", "Физика"], + "grades": [88.0, 94.0, 82.0, 90.0] + } +] \ No newline at end of file diff --git a/src/main.py b/src/main.py index e69de29..b4dbcd4 100644 --- a/src/main.py +++ b/src/main.py @@ -0,0 +1,267 @@ +import json +import csv +import os +from typing import List, Dict, Any + +# ---------- 1. Загрузка данных ---------- +def load_students(filepath: str) -> List[Dict[str, Any]]: + """ + Загружает данные из файла (CSV или JSON). + Возвращает список словарей студентов. + """ + if not os.path.exists(filepath): + raise FileNotFoundError(f"Файл {filepath} не найден.") + + if filepath.endswith('.json'): + with open(filepath, 'r', encoding='utf-8') as f: + data = json.load(f) + # Приведение к единому формату (список словарей) + if isinstance(data, dict) and 'students' in data: + return data['students'] + return data + + elif filepath.endswith('.csv'): + students = [] + with open(filepath, 'r', encoding='utf-8') as f: + reader = csv.DictReader(f) + for row in reader: + # Преобразование полей + student = { + 'id': int(row['id']), + 'name': row['name'], + 'courses': row['courses'].split(';') if row['courses'] else [], + 'grades': [float(x) for x in row['grades'].split(';')] if row['grades'] else [] + } + students.append(student) + return students + else: + raise ValueError("Неподдерживаемый формат файла. Используйте .json или .csv") + +# ---------- 2. Валидация одного студента ---------- +def validate_student_data(student: Dict[str, Any]) -> bool: + """ + Проверяет корректность данных студента. + Возвращает True, если все поля валидны. + """ + required_keys = {'id', 'name', 'courses', 'grades'} + if not required_keys.issubset(student.keys()): + return False + + # Проверка id + if not isinstance(student['id'], int) or student['id'] <= 0: + return False + + # Проверка имени + if not isinstance(student['name'], str) or len(student['name'].strip()) == 0: + return False + + # Проверка курсов (должен быть список строк) + if not isinstance(student['courses'], list): + return False + for course in student['courses']: + if not isinstance(course, str): + return False + + # Проверка оценок (список чисел от 0 до 100) + if not isinstance(student['grades'], list): + return False + for grade in student['grades']: + if not isinstance(grade, (int, float)) or not (0 <= grade <= 100): + return False + + return True + +# ---------- 3. Средний балл ---------- +def calculate_average_grade(grades: List[float]) -> float: + """ + Возвращает среднее арифметическое списка оценок. + Для пустого списка возвращает 0.0. + """ + if not grades: + return 0.0 + return sum(grades) / len(grades) + +# ---------- 4. Добавление студента ---------- +def add_student(students: List[Dict[str, Any]], student: Dict[str, Any]) -> List[Dict[str, Any]]: + """ + Добавляет нового студента, если id уникален. + Возвращает обновлённый список. + """ + if any(s['id'] == student['id'] for s in students): + print(f"Предупреждение: студент с id={student['id']} уже существует. Добавление отменено.") + return students + students.append(student) + return students + +# ---------- 5. Удаление студента ---------- +def remove_student(students: List[Dict[str, Any]], student_id: int) -> List[Dict[str, Any]]: + """ + Удаляет студента по id. Возвращает новый список. + """ + new_list = [s for s in students if s['id'] != student_id] + if len(new_list) == len(students): + print(f"Предупреждение: студент с id={student_id} не найден.") + return new_list + +# ---------- 6. Топ-студенты по порогу ---------- +def find_top_performers(students: List[Dict[str, Any]], threshold: float) -> List[Dict[str, Any]]: + """ + Возвращает список студентов со средним баллом >= threshold, + отсортированный по убыванию среднего балла. + """ + # Вычисляем средний балл для каждого студента + students_with_avg = [] + for s in students: + avg = calculate_average_grade(s['grades']) + students_with_avg.append((avg, s)) + # Фильтруем и сортируем + filtered = [(avg, s) for avg, s in students_with_avg if avg >= threshold] + filtered.sort(key=lambda x: x[0], reverse=True) + return [s for _, s in filtered] + +# ---------- 7. Студенты по курсу ---------- +def get_students_by_course(students: List[Dict[str, Any]], course_name: str) -> List[Dict[str, Any]]: + """ + Возвращает список студентов, изучающих указанный курс. + """ + return [s for s in students if course_name in s['courses']] + +# ---------- 8. Генерация отчёта ---------- +def generate_report(students: List[Dict[str, Any]]) -> Dict[str, Any]: + """ + Формирует статистический отчёт. + """ + if not students: + return { + "total_students": 0, + "average_grade_all": 0.0, + "top_student": None, + "course_popularity": {} + } + + total = len(students) + # Средний балл всех студентов + all_avgs = [calculate_average_grade(s['grades']) for s in students] + avg_all = sum(all_avgs) / total if total else 0.0 + + # Лучший студент + max_avg = max(all_avgs) + top_student = next(s for s in students if calculate_average_grade(s['grades']) == max_avg) + + # Популярность курсов + course_pop = {} + for s in students: + for c in s['courses']: + course_pop[c] = course_pop.get(c, 0) + 1 + + return { + "total_students": total, + "average_grade_all": round(avg_all, 2), + "top_student": top_student, + "course_popularity": course_pop + } + +# ---------- 9. Сортировка студентов по среднему баллу ---------- +def sort_students_by_grade(students: List[Dict[str, Any]], reverse: bool = False) -> List[Dict[str, Any]]: + """ + Возвращает новый список, отсортированный по среднему баллу. + reverse=True — по убыванию, иначе по возрастанию. + """ + return sorted(students, key=lambda s: calculate_average_grade(s['grades']), reverse=reverse) + +# ---------- 10. Сохранение отчёта в JSON ---------- +def save_report(report: Dict[str, Any], filepath: str) -> None: + """ + Сохраняет отчёт в файл в формате JSON. + """ + # Преобразуем top_student в сериализуемый словарь (исключаем функции) + serializable_report = report.copy() + if 'top_student' in serializable_report and serializable_report['top_student'] is not None: + # Создаём копию без лишних полей + top = serializable_report['top_student'].copy() + serializable_report['top_student'] = top + with open(filepath, 'w', encoding='utf-8') as f: + json.dump(serializable_report, f, indent=2, ensure_ascii=False) + +# ---------- Основная функция main() ---------- +def main(): + # Убедимся, что папка data существует + os.makedirs("data", exist_ok=True) + data_file = "data/students.json" + + # Если файла нет, создаём пример данных + if not os.path.exists(data_file): + example_students = [ + {"id": 1, "name": "Иван Петров", "courses": ["Математика", "Программирование на Python", "Физика"], "grades": [85, 92, 78]}, + {"id": 2, "name": "Мария Сидорова", "courses": ["Программирование на Python", "Английский язык"], "grades": [95, 88, 91]}, + {"id": 3, "name": "Алексей Иванов", "courses": ["Математика", "Физика"], "grades": [72, 68, 74]}, + {"id": 4, "name": "Елена Смирнова", "courses": ["Программирование на Python", "Базы данных"], "grades": [100, 98, 97]}, + {"id": 5, "name": "Дмитрий Козлов", "courses": ["Математика"], "grades": [60, 65]} + ] + with open(data_file, 'w', encoding='utf-8') as f: + json.dump(example_students, f, indent=2, ensure_ascii=False) + print(f"Создан пример файла {data_file}") + + # 1. Загрузка + print("1. Загрузка данных...") + students = load_students(data_file) + print(f"Загружено студентов: {len(students)}") + + # 2. Валидация и фильтрация некорректных + print("\n2. Валидация данных...") + valid_students = [] + for s in students: + if validate_student_data(s): + valid_students.append(s) + else: + print(f"Предупреждение: студент {s.get('name', '?')} (id={s.get('id')}) содержит некорректные данные и будет пропущен.") + students = valid_students + print(f"После валидации осталось: {len(students)} студентов.") + + # 3. Добавление поля avg_grade + print("\n3. Вычисление среднего балла для каждого студента...") + for s in students: + s['avg_grade'] = calculate_average_grade(s['grades']) + print(f"{s['name']}: средний балл = {s['avg_grade']:.2f}") + + # 4. Топ-студенты с порогом 85.0 + print("\n4. Топ-студенты (средний балл >= 85.0):") + top = find_top_performers(students, 85.0) + for s in top: + print(f" {s['name']} — {s['avg_grade']:.2f}") + + # 5. Студенты курса "Программирование на Python" + course_name = "Программирование на Python" + print(f"\n5. Студенты, изучающие курс «{course_name}»:") + python_students = get_students_by_course(students, course_name) + for s in python_students: + print(f" {s['name']}") + + # 6. Генерация и сохранение отчёта + print("\n6. Генерация отчёта...") + report = generate_report(students) + report_path = "data/report.json" + save_report(report, report_path) + print(f"Отчёт сохранён в {report_path}") + + # 7. Демонстрация add_student и remove_student + print("\n7. Демонстрация add_student и remove_student:") + new_student = {"id": 99, "name": "Тест Тестов", "courses": ["Программирование на Python"], "grades": [90, 85]} + students = add_student(students, new_student) + print(f"После добавления нового студента: {len(students)} студентов.") + students = remove_student(students, 2) # удалим Марию Сидорову + print(f"После удаления студента с id=2: {len(students)} студентов.") + # Восстановим удалённого для чистоты (необязательно) + students = add_student(students, {"id": 2, "name": "Мария Сидорова", "courses": ["Программирование на Python", "Английский язык"], "grades": [95, 88, 91]}) + print(f"Восстановили Марию: {len(students)} студентов.") + + # 8. Сортировка по среднему баллу и вывод первых 5 + print("\n8. Первые 5 студентов при сортировке по убыванию среднего балла:") + sorted_students = sort_students_by_grade(students, reverse=True) + for s in sorted_students[:5]: + print(f" {s['name']} — {calculate_average_grade(s['grades']):.2f}") + + print("\nРабота программы завершена.") + +if __name__ == "__main__": + main() \ No newline at end of file