Last version
This commit is contained in:
parent
fab3f1f6e4
commit
fc869c60df
25
data/report.json
Normal file
25
data/report.json
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
@ -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]
|
||||
}
|
||||
]
|
||||
267
src/main.py
267
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()
|
||||
Loading…
Reference in New Issue
Block a user