def get_assignments_by_section(course_id, sections_names=None, assignments_names=None): """ Retrieving assignments by sections. """ # Retrieve the contents of the course sections = core_course_get_contents(course_id) if sections_names is not None: # Filter out section names sections = [ section for section in sections if section["name"] not in sections_names ] found_sections = set(section["name"] for section in sections) missing_sections = set(sections_names).difference(found_sections) if missing_sections: logger.error("Could not find the following sections: %s", list(missing_sections)) if assignments_names is not None: # Handle missing assignments found_assignments = set([ module["name"] for section in sections for module in section["modules"] ]) missing_assignments = set(assignments_names).difference( found_assignments) if missing_assignments: logger.error("Could not find the following assignments: %s", missing_assignments) assignment_id_to_section = {} # Looking through the course contents trying to locate the assignment for section in sections: for module in section["modules"]: # Filter only assignments in moodle if module["modname"] != "assign": continue # Filter assignments by name if assignments_names and module["name"] not in assignments_names: continue assignment_id_to_section[module["id"]] = section["name"] assignments = get_assignments(course_id, list(assignment_id_to_section.keys())) assignments_by_section = defaultdict(list) for assignment in assignments: section_name = assignment_id_to_section[assignment.cmid] assignments_by_section[section_name].append(assignment) return assignments_by_section
def status_report(course_id): """ Generates a short report of the students for a specific course. Returns a list of StudentStatus tuples. """ assignments = get_assignments(course_id) users_map = get_students(course_id) submissions_by_user = Counter() last_submission_by_user = defaultdict( lambda: SubmissionTuple(name="Nothing", timestamp=0)) for assignment in assignments: for submission in assignment.submissions: user_name = users_map[submission.user_id] submissions_by_user[user_name] += 1 if (last_submission_by_user[user_name].timestamp < submission.timestamp): last_submission_by_user[user_name] = SubmissionTuple( name=assignment.name, timestamp=submission.timestamp) student_statuses = [] for user, submission_count in submissions_by_user.items(): student_statuses.append( StudentStatus(user, submission_count, last_submission_by_user[user].name)) student_statuses = sorted( student_statuses, key=lambda student_status: student_status.total_submissions, ) return student_statuses
def export_submissions(course_id, download_folder): """ Downloads all submissions from a given course """ assignments = get_assignments(course_id) users_map = get_students(course_id) for assignment in assignments: for submission in assignment.submissions: download_submission( assignment.name, users_map[submission.user_id], submission, download_folder, )
def export_materials(course_id, folder): """ Downloads all the materials from a course to a given folder """ # Put assignments into a dict to find easily assigns = {assign.uid: assign for assign in get_assignments(course_id)} sections = core_course_get_contents(course_id) for section in sections: section_folder = Path(folder) / Path(section["name"]) for module in section["modules"]: module_name = module["name"] module_type = module["modname"] if module_type not in ("resource", "folder", "assign", "url"): if module_type not in ["feedback", "forum"]: logger.warning("Skipped export from unknown module '%s'", module_type) continue # This is one of the known modules - create the section section_folder.mkdir(parents=True, exist_ok=True) if module_type in ("resource", "folder"): download_folder = section_folder # If its a folder, create a subfolder if module_type == "folder": download_folder = download_folder / Path(module_name) download_folder.mkdir(parents=True, exist_ok=True) for resource in module["contents"]: download_file(resource["fileurl"], download_folder) elif module_type == "assign": # If module is an assignment - download attachments and description assign = assigns[module["instance"]] if len(assign.description) > 0: description_file = section_folder / Path( assign.name).with_suffix(".txt") description_file.write_text(assign.description) for attachment in assign.attachments: download_file(attachment, section_folder) elif module_type == "url": url_file = section_folder / Path(f"{module_name}_url.txt") # Assuming a url module can only have 1 url inside url_file.write_text(module["contents"][0]["fileurl"])
def submissions_statistics(course_id, is_verbose=False, download_folder=None): """ Returns a dictionary describing the status of ungraded exercises in the course. The dictionary looks like this: ``` { 'total_submissions': 10, 'total_ungraded': 5, 'total_resubmissions': 2, 'exercises': { 'assign1': { 'submissions': 2, 'ungraded': 2, 'resubmissions': 0} }, ... } } ``` If download_folder is set, downloads the ungraded exercises """ logger.info("Showing ungraded submissions for course %s", course_id) total_submissions = 0 total_ungraded = 0 total_resubmissions = 0 assignments_statistics = {} assignments = get_assignments(course_id) users_map = get_students(course_id) for assignment in assignments: current_assignment_submissions_amount = len(assignment.submissions) current_assignment_ungraded = assignment.ungraded() current_assignment_ungraded_amount = len(current_assignment_ungraded) current_assignment_resubmissions_amount = 0 ungraded_ignored = [] for submission in current_assignment_ungraded: if submission.resubmitted: current_assignment_resubmissions_amount += 1 if submission.user_id in STUDENTS_TO_IGNORE.keys(): ungraded_ignored.append(STUDENTS_TO_IGNORE[submission.user_id]) if download_folder is not None: download_submission( assignment.name, users_map[submission.user_id], submission, download_folder, ) total_submissions += current_assignment_submissions_amount total_ungraded += current_assignment_ungraded_amount total_resubmissions += current_assignment_resubmissions_amount # Print total stats about this assignment if is_verbose and len(ungraded_ignored) != 0: logger.info( "Ignored %s submissions for assignment '%s' (CMID %s, ID %s): %s", len(ungraded_ignored), assignment.name, assignment.cmid, assignment.uid, ungraded_ignored, ) amount_ungraded_not_ignored = current_assignment_ungraded_amount - len( ungraded_ignored) if amount_ungraded_not_ignored != 0: logger.info( "Total ungraded for assignment [%s] (CMID %s, ID %s): %s/%s", assignment.name, assignment.cmid, assignment.uid, amount_ungraded_not_ignored, len(assignment.submissions), ) assignments_statistics[assignment.name] = { "submissions": current_assignment_submissions_amount, "ungraded": amount_ungraded_not_ignored, "resubmissions": current_assignment_resubmissions_amount, } return { "total_submissions": total_submissions, "total_ungraded": total_ungraded, "total_resubmissions": total_resubmissions, "exercises": assignments_statistics, }