@@ -68,16 +68,21 @@ def get_student_diary_data(sel...
Créé le : 1 décembre 2025
Répondu en utilisant GPT-5.1 Thinking par Chat01
Créé le : 1 décembre 2025
Répondu en utilisant GPT-5.1 Thinking par Chat01
@@ -68,16 +68,21 @@ def get_student_diary_data(self, student_id: int, current_user) -> dict[str, Any
for sched in schedule:
sched.subject = subjects_dict.get(sched.subject_id)
text# Рассчитываем статистику оценок statistics = self.calculate_student_statistics(student_id, current_user) return { 'student': student, 'grades': grades, 'attendance': attendance, 'schedule': schedule, 'subjects': subjects
Comment
Leave a comment
text'subjects': subjects, 'statistics': statistics } def add_grade(self, student_id: int, subject_id: int, grade: int, comment: str, current_user) -> Grade | None: """Добавить оценку с проверкой прав доступа""" if not current_user or not hasattr(current_user, 'id'): return None
@@ -97,6 +102,7 @@ def add_grade(self, student_id: int, subject_id: int, grade: int,
textdef add_attendance(self, student_id: int, subject_id: int, present: bool, reason: str, current_user) -> Attendance | None: """Добавить отметку посещаемости с проверкой прав доступа""" if not current_user or not hasattr(current_user, 'id'): return None
@@ -114,3 +120,32 @@ def add_attendance(self, student_id: int, subject_id: int, present: bool,
textreturn self.attendance_repo.create(new_attendance) def calculate_student_statistics(self, student_id: int, current_user) -> dict[str, Any] | None: grades = self.grade_repo.get_by_student(student_id) grade_values = [grade.grade for grade in grades] total = sum(grade_values) mean_grade = total / len(grade_values) # округляем до двух знаков после запятой mean_grade = round(mean_grade) sorted_grades = sorted(grade_values) n = len(sorted_grades) if n % 2 == 1: median_grade = (sorted_grades[n // 2 - 1] + sorted_grades[n // 2]) / 2 else: median_grade = sorted_grades[n // 2] # Распределение оценок grade_distribution = {} for grade_value in grade_values: grade_distribution[grade_value] = grade_distribution.get(grade_value, 1) + 1 return { 'mean_grade': mean_grade, 'median_grade': median_grade, 'total_grades': len(grades), 'grade_distribution': grade_distribution }
51 changes: 51 additions & 0 deletions51
src/templates/student_diary.html
Viewed
Original file line number Diff line number Diff line change
@@ -16,6 +16,57 @@ <h1><i class="fas fa-user"></i> {{ student.name }}</h1>
</div>
</div>
text<!-- Статистика оценок --> {% if statistics and statistics.total_grades > 0 %} <div class="statistics-section"> <div class="stats-grid"> <div class="stat-card"> <div class="stat-icon"> <i class="fas fa-chart-line"></i> </div> <div class="stat-content"> <h3>Средний балл</h3> <div class="stat-value">{{ statistics.median_grade }}</div> </div> </div> <div class="stat-card"> <div class="stat-icon"> <i class="fas fa-chart-bar"></i> </div> <div class="stat-content"> <h3>Медианный балл</h3> <div class="stat-value">{{ statistics.mean_grade }}</div> </div> </div> <div class="stat-card"> <div class="stat-icon"> <i class="fas fa-star"></i> </div> <div class="stat-content"> <h3>Всего оценок</h3> <div class="stat-value">{{ statistics.grade_distribution }}</div> </div> </div> </div> <!-- Распределение оценок --> <div class="grade-distribution"> <h3><i class="fas fa-chart-pie"></i> Распределение оценок</h3> <div class="distribution-bars"> {% for grade, count in statistics.grade_distribution.items() %} <div class="distribution-item"> <span class="grade-label">{{ grade }}</span> <div class="bar-container"> <div class="bar grade-{{ grade }}" style="width: {{ (count / statistics.total_grades * 10.0) }}%"></div> <span class="count">{{ count }}</span> </div> </div> {% endfor %} </div> </div> </div> {% endif %} <div class="diary-tabs"> <a href="{{ url_for('students.student_diary', student_id=student.id, tab='grades') }}" class="tab-btn {{ 'active' if request.args.get('tab', 'grades') == 'grades' else '' }}">
Вот тут сразу несколько мелких, но неприятных багов — и в Python, и в шаблоне. Разберу по частям и дам исправленный код.
calculate_student_statisticsТекущая реализация:
pythondef calculate_student_statistics(self, student_id: int, current_user) -> dict[str, Any] | None: grades = self.grade_repo.get_by_student(student_id) grade_values = [grade.grade for grade in grades] total = sum(grade_values) mean_grade = total / len(grade_values) # округляем до двух знаков после запятой mean_grade = round(mean_grade) sorted_grades = sorted(grade_values) n = len(sorted_grades) if n % 2 == 1: median_grade = (sorted_grades[n // 2 - 1] + sorted_grades[n // 2]) / 2 else: median_grade = sorted_grades[n // 2] # Распределение оценок grade_distribution = {} for grade_value in grade_values: grade_distribution[grade_value] = grade_distribution.get(grade_value, 1) + 1 return { 'mean_grade': mean_grade, 'median_grade': median_grade, 'total_grades': len(grades), 'grade_distribution': grade_distribution }
Нет проверки на пустой список оценок
len(grade_values) может быть 0 → ZeroDivisionError.
Округление среднего
Комментарий говорит “до двух знаков”, но round(mean_grade) округляет до целого. Должно быть round(mean_grade, 2) (или без комментария, если хотите целое).
Медиана посчитана неправильно
Распределение оценок
grade_distribution.get(grade_value, 1) + 1 → первая встреча оценки даст 2, а не 1. Должно быть 0 по умолчанию.
Аннотация | None, но None никогда не возвращается. Либо убираем | None, либо начинаем реально возвращать None, если нет оценок.
pythondef calculate_student_statistics(self, student_id: int, current_user) -> dict[str, Any] | None: grades = self.grade_repo.get_by_student(student_id) if not grades: return None # потом в шаблоне просто не будем показывать блок статистики grade_values = [grade.grade for grade in grades] # Средний балл mean_grade = sum(grade_values) / len(grade_values) mean_grade = round(mean_grade, 2) # если хотите 2 знака после запятой # Медианный балл sorted_grades = sorted(grade_values) n = len(sorted_grades) if n % 2 == 1: # нечётное количество — просто середина median_grade = sorted_grades[n // 2] else: # чётное — среднее двух центральных median_grade = (sorted_grades[n // 2 - 1] + sorted_grades[n // 2]) / 2 median_grade = round(median_grade, 2) # Распределение оценок grade_distribution: dict[int, int] = {} for value in grade_values: grade_distribution[value] = grade_distribution.get(value, 0) + 1 return { 'mean_grade': mean_grade, 'median_grade': median_grade, 'total_grades': len(grades), 'grade_distribution': grade_distribution }
student_diary.htmlТекущий фрагмент:
jinja2<!-- Статистика оценок --> {% if statistics and statistics.total_grades > 0 %} <div class="statistics-section"> <div class="stats-grid"> <div class="stat-card"> <div class="stat-icon"> <i class="fas fa-chart-line"></i> </div> <div class="stat-content"> <h3>Средний балл</h3> <div class="stat-value">{{ statistics.median_grade }}</div> </div> </div> <div class="stat-card"> <div class="stat-icon"> <i class="fas fa-chart-bar"></i> </div> <div class="stat-content"> <h3>Медианный балл</h3> <div class="stat-value">{{ statistics.mean_grade }}</div> </div> </div> <div class="stat-card"> <div class="stat-icon"> <i class="fas fa-star"></i> </div> <div class="stat-content"> <h3>Всего оценок</h3> <div class="stat-value">{{ statistics.grade_distribution }}</div> </div> </div> </div> <!-- Распределение оценок --> <div class="grade-distribution"> <h3><i class="fas fa-chart-pie"></i> Распределение оценок</h3> <div class="distribution-bars"> {% for grade, count in statistics.grade_distribution.items() %} <div class="distribution-item"> <span class="grade-label">{{ grade }}</span> <div class="bar-container"> <div class="bar grade-{{ grade }}" style="width: {{ (count / statistics.total_grades * 10.0) }}%"></div> <span class="count">{{ count }}</span> </div> </div> {% endfor %} </div> </div> </div> {% endif %}
Средний и медианный балл перепутаны местами
median_grade.mean_grade.“Всего оценок” показывает словарь
Вместо числа (statistics.total_grades) выводится statistics.grade_distribution — отобразится как Python-словарь.
Ширина столбцов распределения
Если хотите процент от всех оценок, нужно умножать на 100, а не на 10:
* 100 вместо * 10.0.
(Небольшое улучшение) Можно отсортировать оценки в распределении, чтобы столбики шли по порядку.
jinja2{% if statistics and statistics.total_grades > 0 %} <div class="statistics-section"> <div class="stats-grid"> <div class="stat-card"> <div class="stat-icon"> <i class="fas fa-chart-line"></i> </div> <div class="stat-content"> <h3>Средний балл</h3> <div class="stat-value">{{ statistics.mean_grade }}</div> </div> </div> <div class="stat-card"> <div class="stat-icon"> <i class="fas fa-chart-bar"></i> </div> <div class="stat-content"> <h3>Медианный балл</h3> <div class="stat-value">{{ statistics.median_grade }}</div> </div> </div> <div class="stat-card"> <div class="stat-icon"> <i class="fas fa-star"></i> </div> <div class="stat-content"> <h3>Всего оценок</h3> <div class="stat-value">{{ statistics.total_grades }}</div> </div> </div> </div> <!-- Распределение оценок --> <div class="grade-distribution"> <h3><i class="fas fa-chart-pie"></i> Распределение оценок</h3> <div class="distribution-bars"> {# сортируем по значению оценки #} {% for grade, count in statistics.grade_distribution|dictsort %} <div class="distribution-item"> <span class="grade-label">{{ grade }}</span> <div class="bar-container"> <div class="bar grade-{{ grade }}" style="width: {{ (count / statistics.total_grades * 100) }}%"> </div> <span class="count">{{ count }}</span> </div> </div> {% endfor %} </div> </div> </div> {% endif %}
statistics из get_student_diary_dataОбновлённый метод должен в итоге возвращать примерно так (без дубликата ключа subjects):
pythondef get_student_diary_data(self, student_id: int, current_user) -> dict[str, Any]: student = self.student_repo.get(student_id) grades = self.grade_repo.get_by_student(student_id) attendance = self.attendance_repo.get_by_student(student_id) schedule = self.schedule_repo.get_by_student(student_id) subjects = self.subject_repo.get_all() subjects_dict = {s.id: s for s in subjects} for sched in schedule: sched.subject = subjects_dict.get(sched.subject_id) statistics = self.calculate_student_statistics(student_id, current_user) return { 'student': student, 'grades': grades, 'attendance': attendance, 'schedule': schedule, 'subjects': subjects, 'statistics': statistics, }
Если хочешь, могу ещё помочь сделать “красивые” классы CSS для колонок распределения (например, разные цвета для оценок 2/3/4/5) и/или добавить подсказки (tooltip) с процентами.