diff --git a/api/canvas_classes.py b/api/canvas_classes.py index 69b0de55f588b86dbb68e506bb666d4d99da2b31..2ad19a6f495397e82c46629cc2941ea228d08e02 100644 --- a/api/canvas_classes.py +++ b/api/canvas_classes.py @@ -3,8 +3,10 @@ from typing import ClassVar, Dict, Iterable, List, Optional, Union from canvasapi import Canvas from canvasapi.assignment import Assignment, AssignmentGroup from canvasapi.course import Course +from canvasapi.exceptions import ResourceDoesNotExist from canvasapi.group import Group, GroupCategory from canvasapi.quiz import QuizSubmission, QuizSubmissionQuestion +from canvasapi.section import Section from canvasapi.submission import Submission from canvasapi.user import User @@ -631,6 +633,90 @@ class CanvasAssignmentGroup: """ +class CanvasCourseSection: + canvas_section: Section + + def __init__(self, section: Section, course: "CanvasCourse"): + super().__init__() + self.canvas_section = section + self.course = course + + def get_students(self) -> List[CanvasUser]: + # course_students: List[CanvasUser] = None + course_students: List[CanvasUser] = self.course.get_students() + enrollments = self.canvas_section.get_enrollments() + students: List[CanvasUser] = [] + for enrollment in enrollments: + if enrollment.role == 'StudentEnrollment': + user_json = enrollment.user + """ + try: + user = self.course.canvas_course.get_user(int(user_json['sis_user_id']), + 'sis_user_id') # TODO re-encapsulate + students.append(CanvasUser(user)) + except ResourceDoesNotExist: + if course_students is None: + course_students = self.course.get_students() + candidates = list(filter(lambda c: c.get_username() == user_json['login_id'], course_students)) + if len(candidates) == 1: + students.append(candidates[0]) + print(f'WARNING: Canvas user {user_json["name"]} located indirectly ' + f'(could not directly locate Canvas REST resource). Adding to list.') + elif len(candidates) > 1: + print(f'ERROR: Indirectly found {len(candidates)} canvas users for {user_json["name"]} ' + f'using the "unique" login {user_json["login_id"]}. Adding none of them. ' + f'(Could not directly locate Canvas REST resource)') + else: + print(f'ERROR: Could not locate canvas user: {user_json["name"]}') + """ + candidates = list(filter(lambda c: c.get_username() == user_json['login_id'], course_students)) + if len(candidates) == 1: + students.append(candidates[0]) + elif len(candidates) > 1: + print(f'ERROR: Indirectly found {len(candidates)} canvas users for {user_json["name"]} ' + f'using the "unique" login {user_json["login_id"]}. Adding none of them.') + else: + print(f'ERROR: Could not locate canvas user: {user_json["name"]}') + return students + + def __repr__(self) -> str: + return self.canvas_section.name + + +""" +{ + // The unique identifier for the section. + "id": 1, + // The name of the section. + "name": "Section A", + // The sis id of the section. This field is only included if the user has + // permission to view SIS information. + "sis_section_id": "s34643", + // Optional: The integration ID of the section. This field is only included if + // the user has permission to view SIS information. + "integration_id": "3452342345", + // The unique identifier for the SIS import if created through SIS. This field + // is only included if the user has permission to manage SIS information. + "sis_import_id": 47, + // The unique Canvas identifier for the course in which the section belongs + "course_id": 7, + // The unique SIS identifier for the course in which the section belongs. This + // field is only included if the user has permission to view SIS information. + "sis_course_id": "7", + // the start date for the section, if applicable + "start_at": "2012-06-01T00:00:00-06:00", + // the end date for the section, if applicable + "end_at": null, + // Restrict user enrollments to the start and end dates of the section + "restrict_enrollments_to_section_dates": null, + // The unique identifier of the original course of a cross-listed section + "nonxlist_course_id": null, + // optional: the total number of active and invited students in the section + "total_students": 13 +} +""" + + # THE COURSE ITSELF @@ -694,6 +780,13 @@ class CanvasCourse: assignments.append(CanvasAssignment(assignment)) return assignments + def get_sections(self): + canvas_sections: Iterable[Section] = self.canvas_course.get_sections() + sections: List[CanvasCourseSection] = [] + for section in canvas_sections: + sections.append(CanvasCourseSection(section, self)) + return sections + def __repr__(self) -> str: return f'{self.canvas_course.course_code}: {self.canvas_course.name}' diff --git a/create_canvas_roster.py b/create_canvas_roster.py new file mode 100644 index 0000000000000000000000000000000000000000..54928b0b74fa9c75a8d60da756f6dbc43630e918 --- /dev/null +++ b/create_canvas_roster.py @@ -0,0 +1,19 @@ +import csv + +from api.canvas_classes import * + +if __name__ == '__main__': + canvas_course_id: int = int(input("What is the Canvas course ID? ")) + filename: str = input("What file do you want to save the roster to? ") + course: CanvasCourse = CanvasCourse(canvas_course_id) + # students: List[CanvasUser] = course.get_students() + fields = ['SortableName', 'ReadableName', 'Section', 'NUID', 'Username', 'Email'] + with open(filename, mode='w') as csv_file: + # writer = csv.DictWriter(csv_file, fieldnames=fields) + # writer.writeheader() + writer = csv.writer(csv_file) + writer.writerow(fields) + for section in list(sorted(course.get_sections(), key=lambda s: s.__repr__())): + for student in list(sorted(section.get_students(), key=lambda s: s.get_sortable_name())): + writer.writerow([student.get_sortable_name(), student.get_name(), section, + student.get_nuid(), student.get_username(), student.get_email()])