Select Git revision
canvas_classes.py

Christopher Bohn authored
canvas_classes.py 39.26 KiB
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
from config import Config
class CanvasSession:
__instance: ClassVar[Canvas] = None
@classmethod
def get_session(cls) -> Canvas:
if cls.__instance is None:
cls.__instance = Canvas(Config.canvas_url, Config.canvas_api_key)
return cls.__instance
# PEOPLE CLASSES
class CanvasUser:
canvas_user: User
# @overload
# def __init__(self, user: int): # by NUID
# super().__init__()
# self.canvas_user = CanvasSession.get_session().get_user(user, 'sis_user_id')
#
# @overload
# def __init__(self, user: User): # by existing gitlab user
# super().__init__()
# self.canvas_user = user
#
def __init__(self, user: Union[int, User]):
super().__init__()
if isinstance(user, int): # by NUID
self.canvas_user = CanvasSession.get_session().get_user(user, 'sis_user_id')
else:
self.canvas_user = user
def get_name(self) -> str:
return self.canvas_user.name
def get_sortable_name(self) -> str:
return self.canvas_user.sortable_name
def get_username(self) -> str:
return self.canvas_user.login_id
def get_canvas_id(self) -> int:
return self.canvas_user.id
def get_nuid(self) -> int:
return int(self.canvas_user.sis_user_id)
def get_email(self) -> str:
return self.canvas_user.email
def __repr__(self) -> str:
username = self.get_username()
return f'{username}'
def __eq__(self, other: "CanvasUser") -> bool:
# if isinstance(other, CanvasUser):
return self.get_username() == other.get_username()
# else:
# return False
def __ne__(self, other: "CanvasUser") -> bool:
return not self.__eq__(other)
def __hash__(self) -> int:
return hash(self.get_name())
"""
// A Canvas user, e.g. a student, teacher, administrator, observer, etc.
{
// The ID of the user.
"id": 2,
// The name of the user.
"name": "Sheldon Cooper",
// The name of the user that is should be used for sorting groups of users, such
// as in the gradebook.
"sortable_name": "Cooper, Sheldon",
// A short name the user has selected, for use in conversations or other less
// formal places through the site.
"short_name": "Shelly",
// The SIS ID associated with the user. This field is only included if the user
// came from a SIS import and has permissions to view SIS information.
"sis_user_id": "SHEL93921",
// The id of the SIS import. This field is only included if the user came from
// a SIS import and has permissions to manage SIS information.
"sis_import_id": 18,
// The integration_id associated with the user. This field is only included if
// the user came from a SIS import and has permissions to view SIS information.
"integration_id": "ABC59802",
// The unique login id for the user. This is what the user uses to log in to
// Canvas.
"login_id": "sheldon@caltech.example.com",
// If avatars are enabled, this field will be included and contain a url to
// retrieve the user's avatar.
"avatar_url": "https://en.gravatar.com/avatar/d8cb8c8cd40ddf0cd05241443a591868?s=80&r=g",
// Optional: This field can be requested with certain API calls, and will return
// a list of the users active enrollments. See the List enrollments API for more
// details about the format of these records.
"enrollments": null,
// Optional: This field can be requested with certain API calls, and will return
// the users primary email address.
"email": "sheldon@caltech.example.com",
// Optional: This field can be requested with certain API calls, and will return
// the users locale in RFC 5646 format.
"locale": "tlh",
// Optional: This field is only returned in certain API calls, and will return a
// timestamp representing the last time the user logged in to canvas.
"last_login": "2012-05-30T17:45:25Z",
// Optional: This field is only returned in certain API calls, and will return
// the IANA time zone name of the user's preferred timezone.
"time_zone": "America/Denver",
// Optional: The user's bio.
"bio": "I like the Muppets."
}
"""
class CanvasUserGroup:
canvas_user_group: Group
def __init__(self, group: Group):
super().__init__()
self.canvas_user_group = group
def get_name(self) -> str:
return self.canvas_user_group.name
def get_number_of_students(self) -> int:
return self.canvas_user_group.members_count
def get_students(self) -> List[CanvasUser]:
canvas_users: Iterable[User] = self.canvas_user_group.get_users()
students: List[CanvasUser] = []
for student in canvas_users:
students.append(CanvasUser(student))
return students
def add_student(self, user: CanvasUser) -> None:
self.canvas_user_group.create_membership(user.get_canvas_id())
def __repr__(self) -> str:
return self.get_name()
"""
{
// The ID of the group.
"id": 17,
// The display name of the group.
"name": "Math Group 1",
// A description of the group. This is plain text.
"description": null,
// Whether or not the group is public. Currently only community groups can be
// made public. Also, once a group has been set to public, it cannot be changed
// back to private.
"is_public": false,
// Whether or not the current user is following this group.
"followed_by_user": false,
// How people are allowed to join the group. For all groups except for
// community groups, the user must share the group's parent course or account.
// For student organized or community groups, where a user can be a member of as
// many or few as they want, the applicable levels are
// 'parent_context_auto_join', 'parent_context_request', and 'invitation_only'.
// For class groups, where students are divided up and should only be part of
// one group of the category, this value will always be 'invitation_only', and
// is not relevant. * If 'parent_context_auto_join', anyone can join and will be
// automatically accepted. * If 'parent_context_request', anyone can request to
// join, which must be approved by a group moderator. * If 'invitation_only',
// only those how have received an invitation my join the group, by accepting
// that invitation.
"join_level": "invitation_only",
// The number of members currently in the group
"members_count": 0,
// The url of the group's avatar
"avatar_url": "https://<canvas>/files/avatar_image.png",
// The course or account that the group belongs to. The pattern here is that
// whatever the context_type is, there will be an _id field named after that
// type. So if instead context_type was 'account', the course_id field would be
// replaced by an account_id field.
"context_type": "Course",
"course_id": 3,
// Certain types of groups have special role designations. Currently, these
// include: 'communities', 'student_organized', and 'imported'. Regular
// course/account groups have a role of null.
"role": null,
// The ID of the group's category.
"group_category_id": 4,
// The SIS ID of the group. Only included if the user has permission to view SIS
// information.
"sis_group_id": "group4a",
// The id of the SIS import if created through SIS. Only included if the user
// has permission to manage SIS information.
"sis_import_id": 14,
// the storage quota for the group, in megabytes
"storage_quota_mb": 50,
// optional: the permissions the user has for the group. returned only for a
// single group and include[]=permissions
"permissions": {"create_discussion_topic":true,"create_announcement":true}
}
"""
class CanvasUserGroupSet: # aka, group_category
canvas_group_category: GroupCategory
def __init__(self, group_category: GroupCategory):
super().__init__()
self.canvas_group_category = group_category
def get_name(self) -> str:
return self.canvas_group_category.name
def get_groups(self) -> List[CanvasUserGroup]:
canvas_groups: Iterable[Group] = self.canvas_group_category.get_groups()
groups: List[CanvasUserGroup] = []
for group in canvas_groups:
groups.append(CanvasUserGroup(group))
return groups
def create_group(self, group_name: str) -> CanvasUserGroup:
canvas_group: Group = self.canvas_group_category.create_group(name=group_name)
return CanvasUserGroup(canvas_group)
def create_groups(self, number_of_groups: int) -> List[CanvasUserGroup]:
base_name: str = self.get_name()
groups: List[CanvasUserGroup] = []
for group_number in range(1, number_of_groups + 1):
canvas_group = self.create_group(f'{base_name} {group_number}')
# groups.append(CanvasUserGroup(canvas_group))
groups.append(canvas_group)
return groups
def __repr__(self) -> str:
return self.get_name()
"""
{
// The ID of the group category.
"id": 17,
// The display name of the group category.
"name": "Math Groups",
// Certain types of group categories have special role designations. Currently,
// these include: 'communities', 'student_organized', and 'imported'. Regular
// course/account group categories have a role of null.
"role": "communities",
// If the group category allows users to join a group themselves, thought they
// may only be a member of one group per group category at a time. Values
// include 'restricted', 'enabled', and null 'enabled' allows students to assign
// themselves to a group 'restricted' restricts them to only joining a group in
// their section null disallows students from joining groups
"self_signup": null,
// Gives instructors the ability to automatically have group leaders assigned.
// Values include 'random', 'first', and null; 'random' picks a student from the
// group at random as the leader, 'first' sets the first student to be assigned
// to the group as the leader
"auto_leader": null,
// The course or account that the category group belongs to. The pattern here is
// that whatever the context_type is, there will be an _id field named after
// that type. So if instead context_type was 'Course', the course_id field would
// be replaced by an course_id field.
"context_type": "Account",
"account_id": 3,
// If self-signup is enabled, group_limit can be set to cap the number of users
// in each group. If null, there is no limit.
"group_limit": null,
// The SIS identifier for the group category. This field is only included if the
// user has permission to manage or view SIS information.
"sis_group_category_id": null,
// The unique identifier for the SIS import. This field is only included if the
// user has permission to manage SIS information.
"sis_import_id": null,
// If the group category has not yet finished a randomly student assignment
// request, a progress object will be attached, which will contain information
// related to the progress of the assignment request. Refer to the Progress API
// for more information
"progress": null
}
"""
# ASSIGNMENT-RELATED CLASSES
class CanvasAssignment:
canvas_assignment: Assignment
def __init__(self, assignment: Assignment):
super().__init__()
self.canvas_assignment = assignment
def get_name(self) -> str:
return self.canvas_assignment.name
def is_quiz(self) -> bool:
return 'online_quiz' in self.canvas_assignment.submission_types
def get_submission_text(self, canvas_user: CanvasUser) -> Optional[str]:
submission: Submission = self.canvas_assignment.get_submission(canvas_user.get_canvas_id())
if submission.submission_type == 'online_text_entry':
return submission.body # TODO: what happens if there is no user submission?
elif submission.submission_type == 'online_url':
return submission.url
elif self.is_quiz():
return 'online quiz'
else:
return None
def get_quiz_response(self, canvas_user: CanvasUser) -> List[Dict[str, str]]:
questions_and_answers: List[Dict[str, str]] = []
questions: List[str] = []
answers: List[str] = []
if self.is_quiz():
quiz_id: int = self.canvas_assignment.quiz_id
course_id: int = self.canvas_assignment.course_id
quiz_submissions: Iterable[QuizSubmission] = CanvasCourse(course_id).canvas_course.get_quiz(
quiz_id).get_submissions() # breaking encapsulation
candidate_submission: List[QuizSubmission] = list(
filter(lambda q: q.user_id == canvas_user.get_canvas_id(), quiz_submissions))
if len(candidate_submission) > 0:
full_questions: List[QuizSubmissionQuestion] = candidate_submission[0].get_submission_questions()
for question in full_questions:
questions.append(question.question_text)
submission: Submission = self.canvas_assignment.get_submission(canvas_user.get_canvas_id(),
include=['submission_history'])
history: List[Submission] = sorted(list(submission.submission_history), key=lambda s: s['submitted_at'],
reverse=True)
if len(history) > 0 and 'submission_data' in history[0]: # TODO: add static typing to this section
submission_data = history[0]['submission_data']
for datum in submission_data:
answers.append(datum['text'])
if len(questions) == len(answers):
for i in range(len(questions)):
questions_and_answers.append({'question': questions[i], 'answer': answers[i]})
else:
questions_and_answers = None
else:
questions_and_answers = None
return questions_and_answers
def __repr__(self) -> str:
return self.get_name()
"""
{
// the ID of the assignment
"id": 4,
// the name of the assignment
"name": "some assignment",
// the assignment description, in an HTML fragment
"description": "<p>Do the following:</p>...",
// The time at which this assignment was originally created
"created_at": "2012-07-01T23:59:00-06:00",
// The time at which this assignment was last modified in any way
"updated_at": "2012-07-01T23:59:00-06:00",
// the due date for the assignment. returns null if not present. NOTE: If this
// assignment has assignment overrides, this field will be the due date as it
// applies to the user requesting information from the API.
"due_at": "2012-07-01T23:59:00-06:00",
// the lock date (assignment is locked after this date). returns null if not
// present. NOTE: If this assignment has assignment overrides, this field will
// be the lock date as it applies to the user requesting information from the
// API.
"lock_at": "2012-07-01T23:59:00-06:00",
// the unlock date (assignment is unlocked after this date) returns null if not
// present NOTE: If this assignment has assignment overrides, this field will be
// the unlock date as it applies to the user requesting information from the
// API.
"unlock_at": "2012-07-01T23:59:00-06:00",
// whether this assignment has overrides
"has_overrides": true,
// (Optional) all dates associated with the assignment, if applicable
"all_dates": null,
// the ID of the course the assignment belongs to
"course_id": 123,
// the URL to the assignment's web page
"html_url": "https://...",
// the URL to download all submissions as a zip
"submissions_download_url": "https://example.com/courses/:course_id/assignments/:id/submissions?zip=1",
// the ID of the assignment's group
"assignment_group_id": 2,
// Boolean flag indicating whether the assignment requires a due date based on
// the account level setting
"due_date_required": true,
// Allowed file extensions, which take effect if submission_types includes
// 'online_upload'.
"allowed_extensions": ["docx", "ppt"],
// An integer indicating the maximum length an assignment's name may be
"max_name_length": 15,
// Boolean flag indicating whether or not Turnitin has been enabled for the
// assignment. NOTE: This flag will not appear unless your account has the
// Turnitin plugin available
"turnitin_enabled": true,
// Boolean flag indicating whether or not VeriCite has been enabled for the
// assignment. NOTE: This flag will not appear unless your account has the
// VeriCite plugin available
"vericite_enabled": true,
// Settings to pass along to turnitin to control what kinds of matches should be
// considered. originality_report_visibility can be 'immediate',
// 'after_grading', 'after_due_date', or 'never' exclude_small_matches_type can
// be null, 'percent', 'words' exclude_small_matches_value: - if type is null,
// this will be null also - if type is 'percent', this will be a number between
// 0 and 100 representing match size to exclude as a percentage of the document
// size. - if type is 'words', this will be number > 0 representing how many
// words a match must contain for it to be considered NOTE: This flag will not
// appear unless your account has the Turnitin plugin available
"turnitin_settings": null,
// If this is a group assignment, boolean flag indicating whether or not
// students will be graded individually.
"grade_group_students_individually": false,
// (Optional) assignment's settings for external tools if submission_types
// include 'external_tool'. Only url and new_tab are included (new_tab defaults
// to false). Use the 'External Tools' API if you need more information about
// an external tool.
"external_tool_tag_attributes": null,
// Boolean indicating if peer reviews are required for this assignment
"peer_reviews": false,
// Boolean indicating peer reviews are assigned automatically. If false, the
// teacher is expected to manually assign peer reviews.
"automatic_peer_reviews": false,
// Integer representing the amount of reviews each user is assigned. NOTE: This
// key is NOT present unless you have automatic_peer_reviews set to true.
"peer_review_count": 0,
// String representing a date the reviews are due by. Must be a date that occurs
// after the default due date. If blank, or date is not after the assignment's
// due date, the assignment's due date will be used. NOTE: This key is NOT
// present unless you have automatic_peer_reviews set to true.
"peer_reviews_assign_at": "2012-07-01T23:59:00-06:00",
// Boolean representing whether or not members from within the same group on a
// group assignment can be assigned to peer review their own group's work
"intra_group_peer_reviews": false,
// The ID of the assignment’s group set, if this is a group assignment. For
// group discussions, set group_category_id on the discussion topic, not the
// linked assignment.
"group_category_id": 1,
// if the requesting user has grading rights, the number of submissions that
// need grading.
"needs_grading_count": 17,
// if the requesting user has grading rights and the
// 'needs_grading_count_by_section' flag is specified, the number of submissions
// that need grading split out by section. NOTE: This key is NOT present unless
// you pass the 'needs_grading_count_by_section' argument as true. ANOTHER
// NOTE: it's possible to be enrolled in multiple sections, and if a student is
// setup that way they will show an assignment that needs grading in multiple
// sections (effectively the count will be duplicated between sections)
"needs_grading_count_by_section": [{"section_id":"123456","needs_grading_count":5},
// {"section_id":"654321","needs_grading_count":0}],
// the sorting order of the assignment in the group
"position": 1,
// (optional, present if Sync Grades to SIS feature is enabled)
"post_to_sis": true,
// (optional, Third Party unique identifier for Assignment)
"integration_id": "12341234",
// (optional, Third Party integration data for assignment)
"integration_data": "12341234",
// For courses using Old Gradebook, indicates whether the assignment is muted.
// For courses using New Gradebook, true if the assignment has any unposted
// submissions, otherwise false. To see the posted status of submissions, check
// the 'posted_attribute' on Submission.
"muted": null,
// the maximum points possible for the assignment
"points_possible": 12,
// the types of submissions allowed for this assignment list containing one or
// more of the following: 'discussion_topic', 'online_quiz', 'on_paper', 'none',
// 'external_tool', 'online_text_entry', 'online_url', 'online_upload'
// 'media_recording'
"submission_types": ["online_text_entry"],
// If true, the assignment has been submitted to by at least one student
"has_submitted_submissions": true,
// The type of grading the assignment receives; one of 'pass_fail', 'percent',
// 'letter_grade', 'gpa_scale', 'points'
"grading_type": "points",
// The id of the grading standard being applied to this assignment. Valid if
// grading_type is 'letter_grade' or 'gpa_scale'.
"grading_standard_id": null,
// Whether the assignment is published
"published": true,
// Whether the assignment's 'published' state can be changed to false. Will be
// false if there are student submissions for the assignment.
"unpublishable": false,
// Whether the assignment is only visible to overrides.
"only_visible_to_overrides": false,
// Whether or not this is locked for the user.
"locked_for_user": false,
// (Optional) Information for the user about the lock. Present when
// locked_for_user is true.
"lock_info": null,
// (Optional) An explanation of why this is locked for the user. Present when
// locked_for_user is true.
"lock_explanation": "This assignment is locked until September 1 at 12:00am",
// (Optional) id of the associated quiz (applies only when submission_types is
// ['online_quiz'])
"quiz_id": 620,
// (Optional) whether anonymous submissions are accepted (applies only to quiz
// assignments)
"anonymous_submissions": false,
// (Optional) the DiscussionTopic associated with the assignment, if applicable
"discussion_topic": null,
// (Optional) Boolean indicating if assignment will be frozen when it is copied.
// NOTE: This field will only be present if the AssignmentFreezer plugin is
// available for your account.
"freeze_on_copy": false,
// (Optional) Boolean indicating if assignment is frozen for the calling user.
// NOTE: This field will only be present if the AssignmentFreezer plugin is
// available for your account.
"frozen": false,
// (Optional) Array of frozen attributes for the assignment. Only account
// administrators currently have permission to change an attribute in this list.
// Will be empty if no attributes are frozen for this assignment. Possible
// frozen attributes are: title, description, lock_at, points_possible,
// grading_type, submission_types, assignment_group_id, allowed_extensions,
// group_category_id, notify_of_update, peer_reviews NOTE: This field will only
// be present if the AssignmentFreezer plugin is available for your account.
"frozen_attributes": ["title"],
// (Optional) If 'submission' is included in the 'include' parameter, includes a
// Submission object that represents the current user's (user who is requesting
// information from the api) current submission for the assignment. See the
// Submissions API for an example response. If the user does not have a
// submission, this key will be absent.
"submission": null,
// (Optional) If true, the rubric is directly tied to grading the assignment.
// Otherwise, it is only advisory. Included if there is an associated rubric.
"use_rubric_for_grading": true,
// (Optional) An object describing the basic attributes of the rubric, including
// the point total. Included if there is an associated rubric.
"rubric_settings": "{"points_possible"=>12}",
// (Optional) A list of scoring criteria and ratings for each rubric criterion.
// Included if there is an associated rubric.
"rubric": null,
// (Optional) If 'assignment_visibility' is included in the 'include' parameter,
// includes an array of student IDs who can see this assignment.
"assignment_visibility": [137, 381, 572],
// (Optional) If 'overrides' is included in the 'include' parameter, includes an
// array of assignment override objects.
"overrides": null,
// (Optional) If true, the assignment will be omitted from the student's final
// grade
"omit_from_final_grade": true,
// Boolean indicating if the assignment is moderated.
"moderated_grading": true,
// The maximum number of provisional graders who may issue grades for this
// assignment. Only relevant for moderated assignments. Must be a positive
// value, and must be set to 1 if the course has fewer than two active
// instructors. Otherwise, the maximum value is the number of active instructors
// in the course minus one, or 10 if the course has more than 11 active
// instructors.
"grader_count": 3,
// The user ID of the grader responsible for choosing final grades for this
// assignment. Only relevant for moderated assignments.
"final_grader_id": 3,
// Boolean indicating if provisional graders' comments are visible to other
// provisional graders. Only relevant for moderated assignments.
"grader_comments_visible_to_graders": true,
// Boolean indicating if provisional graders' identities are hidden from other
// provisional graders. Only relevant for moderated assignments with
// grader_comments_visible_to_graders set to true.
"graders_anonymous_to_graders": true,
// Boolean indicating if provisional grader identities are visible to the final
// grader. Only relevant for moderated assignments.
"grader_names_visible_to_final_grader": true,
// Boolean indicating if the assignment is graded anonymously. If true, graders
// cannot see student identities.
"anonymous_grading": true,
// The number of submission attempts a student can make for this assignment. -1
// is considered unlimited.
"allowed_attempts": 2,
// Whether the assignment has manual posting enabled. Only relevant for courses
// using New Gradebook.
"post_manually": true
}
"""
class CanvasAssignmentGroup:
canvas_assignment_group: AssignmentGroup
def __init__(self, group: AssignmentGroup):
super().__init__()
self.canvas_assignment_group = group
def get_name(self) -> str:
return self.canvas_assignment_group.name
def get_assignments(self) -> List[CanvasAssignment]:
course_id: int = self.canvas_assignment_group.course_id
all_assignments: List[CanvasAssignment] = CanvasCourse(course_id).get_assignments()
assignments: List[CanvasAssignment] = list(
filter(lambda a: a.canvas_assignment.assignment_group_id == self.canvas_assignment_group.id,
all_assignments)) # breaking encapsulation
return assignments
def __repr__(self) -> str:
return self.get_name()
"""
{
// the id of the Assignment Group
"id": 1,
// the name of the Assignment Group
"name": "group2",
// the position of the Assignment Group
"position": 7,
// the weight of the Assignment Group
"group_weight": 20,
// the sis source id of the Assignment Group
"sis_source_id": "1234",
// the integration data of the Assignment Group
"integration_data": {"5678":"0954"},
// the assignments in this Assignment Group (see the Assignment API for a
// detailed list of fields)
"assignments": [],
// the grading rules that this Assignment Group has
"rules": null
}
"""
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
class CanvasCourse:
canvas_course: Course
def __init__(self, course_id: int):
self.canvas_course = CanvasSession.get_session().get_course(course_id)
def get_all_users(self) -> List[CanvasUser]:
canvas_users: Iterable[User] = self.canvas_course.get_users()
users: List[CanvasUser] = []
for user in canvas_users:
users.append(CanvasUser(user))
return users
def get_students(self) -> List[CanvasUser]:
canvas_users: Iterable[User] = self.canvas_course.get_users(enrollment_type=['student'])
students: List[CanvasUser] = []
for user in canvas_users:
students.append(CanvasUser(user))
return students
def get_instructional_team(self) -> List[CanvasUser]:
canvas_users: Iterable[User] = self.canvas_course.get_users(enrollment_type=['teacher', 'ta'])
instructors: List[CanvasUser] = []
for user in canvas_users:
instructors.append(CanvasUser(user))
return instructors
def get_all_user_groups(self) -> List[CanvasUserGroup]:
canvas_groups: Iterable[Group] = self.canvas_course.get_groups()
groups: List[CanvasUserGroup] = []
for group in canvas_groups:
groups.append(CanvasUserGroup(group))
return groups
def get_user_groupsets(self) -> List[CanvasUserGroupSet]:
canvas_group_categories: Iterable[GroupCategory] = self.canvas_course.get_group_categories()
group_sets: List[CanvasUserGroupSet] = []
for group_category in canvas_group_categories:
group_sets.append(CanvasUserGroupSet(group_category))
return group_sets
def create_user_groupset(self, groupset_name: str) -> CanvasUserGroupSet:
group_category: GroupCategory = self.canvas_course.create_group_category(groupset_name)
return CanvasUserGroupSet(group_category)
def get_assignment_groups(self) -> List[CanvasAssignmentGroup]:
canvas_assignment_groups: Iterable[AssignmentGroup] = self.canvas_course.get_assignment_groups(
include=['assignments'])
assignment_groups: List[CanvasAssignmentGroup] = []
for assignment_group in canvas_assignment_groups:
assignment_groups.append(CanvasAssignmentGroup(assignment_group))
return assignment_groups
def get_assignments(self) -> List[CanvasAssignment]:
canvas_assignments: Iterable[Assignment] = self.canvas_course.get_assignments()
assignments: List[CanvasAssignment] = []
for assignment in canvas_assignments:
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}'
"""
{
// the unique identifier for the course
"id": 370663,
// the SIS identifier for the course, if defined. This field is only included if
// the user has permission to view SIS information.
"sis_course_id": null,
// the UUID of the course
"uuid": "WvAHhY5FINzq5IyRIJybGeiXyFkG3SqHUPb7jZY5",
// the integration identifier for the course, if defined. This field is only
// included if the user has permission to view SIS information.
"integration_id": null,
// the unique identifier for the SIS import. This field is only included if the
// user has permission to manage SIS information.
"sis_import_id": 34,
// the full name of the course
"name": "InstructureCon 2012",
// the course code
"course_code": "INSTCON12",
// the current state of the course one of 'unpublished', 'available',
// 'completed', or 'deleted'
"workflow_state": "available",
// the account associated with the course
"account_id": 81259,
// the root account associated with the course
"root_account_id": 81259,
// the enrollment term associated with the course
"enrollment_term_id": 34,
// the grading standard associated with the course
"grading_standard_id": 25,
// the grade_passback_setting set on the course
"grade_passback_setting": "nightly_sync",
// the date the course was created.
"created_at": "2012-05-01T00:00:00-06:00",
// the start date for the course, if applicable
"start_at": "2012-06-01T00:00:00-06:00",
// the end date for the course, if applicable
"end_at": "2012-09-01T00:00:00-06:00",
// the course-set locale, if applicable
"locale": "en",
// A list of enrollments linking the current user to the course. for student
// enrollments, grading information may be included if include[]=total_scores
"enrollments": null,
// optional: the total number of active and invited students in the course
"total_students": 32,
// course calendar
"calendar": null,
// the type of page that users will see when they first visit the course -
// 'feed': Recent Activity Dashboard - 'wiki': Wiki Front Page - 'modules':
// Course Modules/Sections Page - 'assignments': Course Assignments List -
// 'syllabus': Course Syllabus Page other types may be added in the future
"default_view": "feed",
// optional: user-generated HTML for the course syllabus
"syllabus_body": "<p>syllabus html goes here</p>",
// optional: the number of submissions needing grading returned only if the
// current user has grading rights and include[]=needs_grading_count
"needs_grading_count": 17,
// optional: the enrollment term object for the course returned only if
// include[]=term
"term": null,
// optional: information on progress through the course returned only if
// include[]=course_progress
"course_progress": null,
// weight final grade based on assignment group percentages
"apply_assignment_group_weights": true,
// optional: the permissions the user has for the course. returned only for a
// single course and include[]=permissions
"permissions": {"create_discussion_topic":true,"create_announcement":true},
"is_public": true,
"is_public_to_auth_users": true,
"public_syllabus": true,
"public_syllabus_to_auth": true,
// optional: the public description of the course
"public_description": "Come one, come all to InstructureCon 2012!",
"storage_quota_mb": 5,
"storage_quota_used_mb": 5,
"hide_final_grades": false,
"license": "Creative Commons",
"allow_student_assignment_edits": false,
"allow_wiki_comments": false,
"allow_student_forum_attachments": false,
"open_enrollment": true,
"self_enrollment": false,
"restrict_enrollments_to_course_dates": false,
"course_format": "online",
// optional: this will be true if this user is currently prevented from viewing
// the course because of date restriction settings
"access_restricted_by_date": false,
// The course's IANA time zone name.
"time_zone": "America/Denver",
// optional: whether the course is set as a Blueprint Course (blueprint fields
// require the Blueprint Courses feature)
"blueprint": true,
// optional: Set of restrictions applied to all locked course objects
"blueprint_restrictions": {"content":true,"points":true,"due_dates":false,"availability_dates":false},
// optional: Sets of restrictions differentiated by object type applied to
// locked course objects
"blueprint_restrictions_by_object_type": {"assignment":{"content":true,"points":true},"wiki_page":{"content":true}}
}
"""
if __name__ == '__main__':
pass