def fetch(args, metadata): course = canvas.Course() what = args[0].rstrip(os.sep) args = args[1:] if os.sep in what: partition = os.path.split(what) what = partition[0] args.insert(0, partition[1]) args = list(map(lambda s: s.rstrip(os.sep), args)) if what == "students": fetch_students(course) elif what == "groups": fetch_groups(course) elif what == "group": fetch_group(course, " ".join(args)) elif what == "subs": if len(args) == 0: fetch_all_subs(course) else: fetch_subs(course, " ".join(args), deep=True, metadata=metadata == True) else: raise Exception("Don't yet know how to fetch {}.".format(str(args)))
def add_students_to_sections_random(section_names): course = canvas.Course() can = canvas.Canvas() section_ids = { section_name: find_section_id(section_name) for section_name in section_names } users_all = can.list_students(course.id) random.shuffle(users_all) users_slice_start = 0 for section_name, n_sections_remaining in zip( section_names, range(len(section_ids), 0, -1)): n_users_remaining = len(users_all) - users_slice_start section_size = n_users_remaining // n_sections_remaining section_id = section_ids[section_name] section_users = users_all[users_slice_start:users_slice_start + section_size] print('Section {} (id: {}) gets {} students: {}'.format( repr(section_name), section_id, section_size, ', '.join(u['name'] for u in section_users))) for user in section_users: can.add_section_member(section_id, user['id']) users_slice_start += section_size
def grade(args): args, _ = grade_args_parser().parse_known_args(args) _check_grade(args.grade) _check_filepaths(args.attachments) if args.comment: comment = args.comment elif args.comment_file: _check_filepaths([args.comment_file]) with open(args.comment_file) as f: comment = f.read() elif args.attachments: comment = "See attached file{}.".format( "" if len(args.attachments) == 1 else "s") else: comment = None course = canvas.Course() assignment = canvas.Assignment(course) submission = canvas.Submission() student_ids = submission.student_ids if args.one: student_ids = student_ids[:1] for sub in assignment.submissions(): if sub['user_id'] in student_ids: current_grade = sub['grade'] if current_grade == grade: print('Already graded.') course.canvas.show_verification_urls( course.id, assignment.id, sub['user_id']) return for student_id in student_ids: assignment.give_feedback(student_id, args.grade, comment, args.attachments, use_post=True)
def find_user(user_name): course = canvas.Course() can = canvas.Canvas() users = can.list_students(course.id) # Hack to remove duplicates. users_found = list(filter(lambda user: user_name == user['name'] or user_name == user['login_id'][:6], users)) users_found = list(set(tuple(x.items()) for x in users_found)) users_found = [{k: v for k, v in x} for x in users_found] if len(users_found) == 1: print('Found; id: {}, sis_user_id: {}, sis_login_id: {}, name: {}'.format( users_found[0]['id'], users_found[0]['sis_user_id'], users_found[0]['sis_login_id'], users_found[0]['name'])) elif len(users_found) > 1: print('{} users found:'.format(len(users_found))) for user in users_found: print(user) sys.exit(1) else: print('No users found. Guesses:') users.sort(key=lambda user: levenshtein.ratio(user_name, user['name']), reverse=True) for user in users[:10]: print('{:.2%} match; id: {}, sis_user_id: {}, sis_login_id: {}, name: {}'.format( levenshtein.ratio(user_name, user['name']), user['id'], user['sis_user_id'], user['sis_login_id'], user['name'])) sys.exit(1)
def set_group_members(group_name, user_names): course = canvas.Course() can = canvas.Canvas() groups_all = can.groups_in_course(course.id) groups = list(filter(lambda x: x['name'] == group_name, groups_all)) if len(groups) < 1: raise Exception('no group of that name') else: group_id = groups[0]['id'] users_all = can.list_students(course.id) user_ids = [] for user_name in user_names: users = list(filter(lambda x: x['name'] == user_name, users_all)) if len(users) < 1: raise Exception('no user of that name') else: user_id = users[0]['id'] user_ids.append(user_id) can.add_group_members(group_id, user_ids) fetch_groups(course) # a bit silly, and very slow
def grade(args): args, more_attachments = grade_args_parser().parse_known_args(args) args.attachments += more_attachments _check_grade(args.grade) _check_filepaths(args.attachments) if args.comment: comment = args.comment elif args.comment_file: _check_filepaths([args.comment_file]) with open(args.comment_file) as f: comment = f.read() elif args.attachments: comment = "See attached file{}.".format("" if len(args.attachments) == 1 else "s") else: comment = None course = canvas.Course() assignment = canvas.Assignment(course) submission = canvas.Submission() student_ids = submission.student_ids if args.one: student_ids = student_ids[:1] elif args.kuid is not None: users_found = _find_user(args.kuid) if len(users_found) == 1: user_id = users_found[0]['id'] student_ids = [user_id] elif len(users_found) > 1: print( "Kuid is too ambiguous! Found multiple users with the kuid {}:." .format(args.kuid)) for user in users_found: print('- id: {}, login_id: {}, kuid: {}, name: {}'.format( user['id'], user['login_id'], user['kuid'], user['name'])) sys.exit(1) else: print("Found no users with the kuid {}!".format(args.kuid)) sys.exit(1) for sub in assignment.submissions(): if sub['user_id'] in student_ids: current_grade = sub['grade'] if current_grade == grade: print('Already graded.') course.canvas.show_verification_urls(course.id, assignment.id, sub['user_id']) return for student_id in student_ids: assignment.give_feedback(student_id, args.grade, comment, args.attachments, use_post=True)
def create(subs_path, category_name, groups_file): canvas = canvaslib.Canvas() course = canvaslib.Course() # Get mapping from abc123 to Canvas User ID. user_ids = get_user_id_mapping(canvas, course) # Extract groups. with open(groups_file) as f: contents = f.read().strip() groups = [line.split(' ') for line in contents.split('\n')] # Update Canvas. create_groups_on_canvas(canvas, course, category_name, user_ids, groups) # Update locally. groupsubs_path = subs_path.replace('subs/', 'groupsubs/') os.makedirs(groupsubs_path, exist_ok=True) for group_members, group_number in zip(groups, range(len(groups))): groupsub_path = os.path.join(groupsubs_path, '{:03d}'.format(group_number)) group_members_submitted = [] total_count = 0 for abc123 in group_members: pattern = os.path.join(subs_path, abc123) + '_*' subpaths = glob.glob(pattern) # There should be 0 or 1 directories that match. # 0 if, for some reason, a KU ID specified in a group.txt doesn't # have a submission. (This can be for other reasons than that it # is an invalid KU ID, or that that KU ID isn't subscribed to the # course. Example: cbh239 is on Advanced Programming 2017 and was # mentioned in a group.txt, they didn't submit anything # themselves for assignment 1, but they don't have an empty # directory like other students. if len(subpaths) > 1: raise Exception('Wait! Two submissions match {}: {}'.format( abc123, ", ".join(subpaths))) elif len(subpaths) == 1: subpath = subpaths[0] ignore_files = [".staffeli.yml", "group.txt"] files = [ f for f in os.listdir(subpath) if f not in ignore_files ] if len(files) > 0: group_members_submitted.append(subpath) if len(group_members_submitted) != 1: raise Exception('Wait! Multiple submissions from {}: {}'.format( ", ".join(group_members), ", ".join(group_members_submitted))) submitter_path = group_members_submitted[0] os.symlink(os.path.join('..', '..', submitter_path), groupsub_path) return 0
def find_section_id(section_name): course = canvas.Course() can = canvas.Canvas() sections_all = can.course_sections(course.id) sections = list(filter(lambda x: x['name'] == section_name, sections_all)) if len(sections) < 1: raise Exception('no section of that name') else: section_id = sections[0]['id'] return section_id
def _find_user(user_name): course = canvas.Course() users = course.list_students().json # Hack to remove duplicates. users_found = list( filter( lambda user: user_name == user['name'] or user_name == user[ 'login_id'][:6], users)) users_found = list(set(tuple(x.items()) for x in users_found)) users_found = [{k: v for k, v in x} for x in users_found] return users_found
def delete_all_groups(group_category_name): course = canvas.Course() can = canvas.Canvas() categories_all = can.group_categories(course.id) categories = list( filter(lambda x: x['name'] == group_category_name, categories_all)) if len(categories) < 1: raise Exception('no category of that name') else: group_category_id = categories[0]['id'] can.delete_all_groups(group_category_id) fetch_groups(course) # a bit silly, and very slow
def add_member_to_section(section_name, user_name): course = canvas.Course() can = canvas.Canvas() section_id = find_section_id(section_name) users_all = can.list_students(course.id) users = list(filter(lambda x: (x['name'] == user_name or x['login_id'][:6] == user_name), users_all)) if len(users) < 1: raise Exception('no user of that name') else: user_id = users[0]['id'] can.add_section_member(section_id, user_id)
def grade(args): args, remargs = grade_args_parser().parse_known_args(args) grade = _check_grade(args.grade) filepaths = _check_filepaths(remargs) message = args.message course = canvas.Course() assignment = canvas.Assignment(course) submission = canvas.Submission() for student_id in submission.student_ids: assignment.give_feedback(student_id, grade, message, filepaths, use_post=True)
def fetch(args, metadata): course = canvas.Course() what = args[0] args = args[1:] if '/' in what: partition = what.partition('/') what = partition[0] args.insert(0, partition[2]) if what == "students": fetch_students(course) elif what == "groups": fetch_groups(course) elif what == "group": fetch_group(course, " ".join(args)) elif what == "subs": if len(args) == 0: fetch_all_subs(course) else: fetch_subs(course, " ".join(args).strip('/'), deep=True, metadata=metadata == True) else: raise Exception("Don't yet know how to fetch {}.".format(str(args)))
def groupsplit(args): split_according_to_groups(canvas.Course(), args[0], args[1])
#!/usr/bin/env python3 # # Split into sections. import sys import re import os import staffeli.canvas as canvas course = canvas.Course() can = canvas.Canvas() def create(groupsubs_path, groups_file, *section_names): sections_path = groupsubs_path.replace('groupsubs/', 'sections/') with open(groups_file) as f: contents = f.read().strip() groups = [line.split(' ') for line in contents.split('\n')] groups_distributed = [] all_sections = can.list_sections(course.id) for name in section_names: section = next(filter(lambda sec: sec['name'] == name, all_sections)) m = re.match(r'Class (.+)', name) if m is not None: name = m.group(1).lower()
def sectsplit(name, subs=None, splits=None): """Split submissions according to sections.""" subs = subs if subs else "subs" splits = splits if splits else "splits" split_by_section(canvas.Course(), name, subs, splits)