diff --git a/analyze_grades.py b/analyze_grades.py index 32c86bc0cc630c476e39c29737f3dc490c8b3084..aa2e9e20f3d40d550e6de94ec531d6ccd03fc324 100644 --- a/analyze_grades.py +++ b/analyze_grades.py @@ -3,7 +3,7 @@ import datetime import statistics import sys -from typing import List, Optional, Dict, Set +from typing import List, Optional, Dict, Set, Tuple from api.canvas_classes import CanvasAssignment, CanvasAssignmentGroup, CanvasCourse, CanvasUser from common_functions import select_from_list @@ -49,6 +49,94 @@ def create_semester_filter() -> str: return f'1{str(year)[-2:]}{month}' # introducing a y2.1k problem +def select_students_for_material_collection(assignment, student_subset) -> Tuple[CanvasUser, CanvasUser, CanvasUser]: + points_possible: float = assignment.get_points_possible() + grade_thresholds: Dict[str, float] = { + 'A': 0.93 * points_possible, + 'A-': 0.90 * points_possible, + 'B+': 0.87 * points_possible, + 'B': 0.83 * points_possible, + 'B-': 0.80 * points_possible, + 'C+': 0.77 * points_possible, + 'C': 0.73 * points_possible, + 'C-': 0.70 * points_possible + } + a_student: Optional[CanvasUser] = None + b_student: Optional[CanvasUser] = None + c_student: Optional[CanvasUser] = None + candidates_for_material_collection: Set[CanvasUser] = \ + {student for student in student_subset + if assignment.get_score(student) is not None + and assignment.get_score(student) >= grade_thresholds['A']} + if len(candidates_for_material_collection) == 0: + candidates_for_material_collection = \ + {student for student in student_subset + if assignment.get_score(student) is not None + and assignment.get_score(student) >= grade_thresholds['A-']} + if len(candidates_for_material_collection) > 0: + a_student = candidates_for_material_collection.pop() + candidates_for_material_collection = \ + {student for student in student_subset if assignment.get_score(student) is not None + and grade_thresholds['B'] <= assignment.get_score(student) < grade_thresholds['B+']} + if len(candidates_for_material_collection) == 0: + candidates_for_material_collection = \ + {student for student in student_subset if assignment.get_score(student) is not None + and grade_thresholds['B-'] <= assignment.get_score(student) < grade_thresholds['A-']} + if len(candidates_for_material_collection) > 0: + b_student = candidates_for_material_collection.pop() + candidates_for_material_collection = \ + {student for student in student_subset if assignment.get_score(student) is not None + and grade_thresholds['C'] <= assignment.get_score(student) < grade_thresholds['C+']} + if len(candidates_for_material_collection) == 0: + candidates_for_material_collection = \ + {student for student in student_subset if assignment.get_score(student) is not None + and grade_thresholds['C-'] <= assignment.get_score(student) < grade_thresholds['B-']} + if len(candidates_for_material_collection) > 0: + c_student = candidates_for_material_collection.pop() + return a_student, b_student, c_student + + +def print_sample_scores(assignment: CanvasAssignment, majors: Set[Major], + major_partitions: Dict[Major, Set[CanvasUser]], + students: Optional[List[CanvasUser]] = None) -> None: + points_possible: float = assignment.get_points_possible() + a_student: Optional[CanvasUser] = None + b_student: Optional[CanvasUser] = None + c_student: Optional[CanvasUser] = None + for major in majors: + student_subset: Set[CanvasUser] = major_partitions[major] if students is None \ + else major_partitions[major].intersection(students) + selected_students: Set[CanvasUser] = set() + if len(student_subset) == 0: + print(f'{major.name}: no students.') + elif len(student_subset) <= 30: + print(f'{major.name} has {len(student_subset)} students: reporting scores for all students.') + selected_students = student_subset + a_student, b_student, c_student = select_students_for_material_collection(assignment, student_subset) + else: + print( + f'{major.name} has {len(student_subset)} students: reporting scores for 30 randomly-selected students.') + # let's make sure there are an 'A', a 'B', and a 'C' student in the mix + a_student, b_student, c_student = select_students_for_material_collection(assignment, student_subset) + for student in (a_student, b_student, c_student): + if student is not None: + student_subset.remove(student) + selected_students.add(student) + while len(selected_students) < 30: + student = student_subset.pop() + selected_students.add(student) + i = 1 + for student in selected_students: + print(f'Student {i:>2} -- raw score: {assignment.get_score(student)}' + f'\tscaled score: {100 * assignment.get_score(student) / points_possible}') + print('Collect sample materials --') + print(f'"A" student: {a_student.get_name()}') + print(f'"B" student: {b_student.get_name()}') + print(f'"C" student: {c_student.get_name()}') + i += 1 + print() + + def print_statistics_for_some_majors(assignment: CanvasAssignment, majors: Set[Major], major_partitions: Dict[Major, Set[CanvasUser]], students: Optional[List[CanvasUser]] = None) -> None: @@ -76,6 +164,8 @@ def print_statistics(assignment: CanvasAssignment, major_partitions: Dict[Major, non_computing_majors: Set[Major] = {major for major in major_partitions.keys() if not major.is_computing_major} print_statistics_for_some_majors(assignment, computing_majors, major_partitions) print_statistics_for_some_majors(assignment, non_computing_majors, major_partitions) + print() + print_sample_scores(assignment, computing_majors, major_partitions) def print_statistics_by_section(course: CanvasCourse, assignment: CanvasAssignment, @@ -95,6 +185,8 @@ def print_statistics_by_section(course: CanvasCourse, assignment: CanvasAssignme print_statistics_for_some_majors(assignment, computing_majors, major_partitions, students) print_statistics_for_some_majors(assignment, non_computing_majors, major_partitions, students) print() + print_sample_scores(assignment, computing_majors, major_partitions, students) + print() def assess_assignments(course: CanvasCourse, major_partitions: Dict[Major, Set[CanvasUser]],