def export_submissions(): assignment_id = int(request.values.get('assignment_id')) assignment = Assignment.by_id(assignment_id) course_id = get_course_id(True) user, user_id = get_user() # Verify exists check_resource_exists(assignment, "Assignment", assignment_id) # Verify permissions assignment = Assignment.by_id(int(assignment_id)) if course_id is None or not user.is_instructor(int(course_id)): return "You are not an instructor or the owner of the assignment!" # Get data suas = Submission.by_assignment(assignment_id, course_id) submissions = [sua[0] for sua in suas] users = [sua[1] for sua in suas] bundle = export_zip(assignments=[assignment], submissions=submissions, users=users) filename = assignment.get_filename(extension='.zip') return Response(bundle, mimetype='application/zip', headers={ 'Content-Disposition': 'attachment;filename={}'.format(filename) })
def get_assignments(): assignment_ids = request.values.get('assignment_ids', "") course_id = get_course_id() user, user_id = get_user() # TODO: verify that they have the permissions to see these assignments assignments, groups = [], [] errors = [] if not assignment_ids: course: Course = Course.by_id(course_id) check_resource_exists(course, "Course", course_id) grouped_assignments = natsorted( course.get_submitted_assignments_grouped(), key=lambda r: (r.AssignmentGroup.name if r.AssignmentGroup is not None else None, r.Assignment.name)) assignments = [a.Assignment.encode_json() for a in grouped_assignments] groups = [ a.AssignmentGroup.encode_json() if a.AssignmentGroup is not None else None for a in grouped_assignments ] else: for assignment_id in assignment_ids.split(","): if not assignment_id.isdigit(): errors.append(f"Unknown Assignment ID: {assignment_id!r}") assignment_id = int(assignment_id) # With Course Role Information assignment = Assignment.by_id(assignment_id) check_resource_exists(assignment, "Assignment", assignment_id) assignments.append(assignment.encode_json()) return ajax_success( dict(assignments=assignments, errors=errors, groups=groups))
def dump_logs(): assignment_id = int(request.values.get('assignment_id')) course_id = int(request.values.get('course_id')) assignment = Assignment.by_id(assignment_id) user, user_id = get_user() # Verify exists check_resource_exists(assignment, "Assignment", assignment_id) # Verify permissions if not user.is_grader(course_id): return "You are not a grader in this course." # Get data suas = Submission.by_assignment(assignment_id, course_id) data = { 'assignment': assignment.encode_json(), 'submissions': [{ 'user': u.encode_json(), 'submission': sub.encode_json(), 'history': Log.get_history(course_id, assignment_id, u.id), 'reviews': sub.get_reviews() } for (sub, u, assign) in suas] } filename = assignment.get_filename() + '_submissions.json' return Response(json.dumps(data), mimetype='application/json', headers={ 'Content-Disposition': 'attachment;filename={}'.format(filename) })
def get_assignment(lti=lti): assignment_id = int(request.values.get('assignment_id')) assignment = Assignment.by_id(assignment_id) is_embedded = ('embed' == request.values.get('menu', "select")) # Verify exists check_resource_exists(assignment, "Assignment", assignment_id) # Perform action select_url = get_select_menu_link(assignment.id, assignment.title(), is_embedded, False) return jsonify(success=True, redirect=url_for('assignments.load', assignment_id=assignment.id), id=assignment.id, name=assignment.name, type=assignment.type, instructions=strip_tags(assignment.instructions)[:255], title=assignment.title(), view=url_for('assignments.load', assignment_id=assignment.id, embed=is_embedded), select=select_url, edit=url_for('assignments.load', assignment_id=assignment.id, course_id=assignment.course_id), date_modified=assignment.pretty_date_modified())
def load_assignment(lti=lti): # Get arguments assignment_id = int(request.values.get('assignment_id')) assignment = Assignment.by_id(assignment_id) course_id = get_course_id(True) user, user_id = get_user() # Verify exists check_resource_exists(assignment, "Assignment", assignment_id) # Verify permissions if course_id is None: editor_information = assignment.for_read_only_editor(user_id) else: editor_information = assignment.for_editor(user_id, course_id) browser_info = repr({ 'platform': request.user_agent.platform, 'browser': request.user_agent.browser, 'version': request.user_agent.version, 'language': request.user_agent.language, 'user_agent': request.user_agent.string }) # Log the event if user is not None: make_log_entry(assignment_id, assignment.version, course_id, user_id, 'Session.Start', message=browser_info) # Verify passcode, if necessary if assignment.passcode_fails(request.values.get('passcode')): return ajax_failure("Passcode {!r} rejected".format( request.values.get("passcode"))) return ajax_success(editor_information)
def watch(): assignment_list = request.values.get('assignments', '') assignments = [int(aid) for aid in assignment_list.split(',') if len(aid) > 0] course_id = request.values.get('course_id', g.course.id if 'course' in g else None) if course_id == None or course_id == "": return ajax_failure("No Course ID given!") if g.user is None or not g.user.is_instructor(int(course_id)): return ajax_failure("You are not an instructor in this assignments' course.") update = request.values.get('update', 'false') == "true" if update: data = [] for aid in assignments: submissions = Submission.by_assignment(aid, int(course_id)) completions = sum([int(sua[0].correct) for sua in submissions]) workings = Submission.get_latest(aid, int(course_id)) histories = [process_history([h['time'] for h in sua[0].get_history()]) for sua in submissions] touches = [int(sua[0].version) for sua in submissions] feedbacks = [l[0] for l in Log.calculate_feedbacks(aid, course_id)] data.append({'id': aid, 'Completions': completions, 'Workings': workings, 'Time': histories, 'Touches': touches, 'Feedbacks': feedbacks}) return jsonify(success=True, data=data) else: assignments = [Assignment.by_id(aid) for aid in assignments] return render_template('blockpy/watch.html', course_id=course_id, assignments=assignments, assignment_list=assignment_list)
def load_assignment(lti=lti): # Get arguments assignment_id = int(request.values.get('assignment_id')) assignment = Assignment.by_id(assignment_id) student_id = maybe_int(request.values.get('user_id')) course_id = get_course_id(True) user, user_id = get_user() force_download = maybe_bool(request.values.get('force_download', "false")) # Verify exists check_resource_exists(assignment, "Assignment", assignment_id) # Verify permissions if user_id != student_id and not user.is_grader(course_id): return ajax_failure( "Only graders can see submissions for other people.") if course_id is None: editor_information = assignment.for_read_only_editor(student_id) else: editor_information = assignment.for_editor(student_id, course_id) browser_info = json.dumps({ 'platform': request.user_agent.platform, 'browser': request.user_agent.browser, 'version': request.user_agent.version, 'language': request.user_agent.language, 'user_agent': request.user_agent.string }) # Log the event if user is not None: if user_id != student_id: make_log_entry(assignment_id, assignment.version, course_id, user_id, 'X-Submission.Get', message=str(student_id)) else: make_log_entry(assignment_id, assignment.version, course_id, user_id, 'Session.Start', message=browser_info) # Verify passcode, if necessary if assignment.passcode_fails(request.values.get('passcode')): return ajax_failure("Passcode {!r} rejected".format( request.values.get("passcode"))) if force_download: student_filename = User.by_id(student_id).get_filename("") filename = assignment.get_filename( "") + "_" + student_filename + '_submission.json' return Response(json.dumps(editor_information), mimetype='application/json', headers={ 'Content-Disposition': 'attachment;filename={}'.format(filename) }) else: return ajax_success(editor_information)
def remove_assignment(lti=None): assignment_id = int(request.values.get('assignment_id')) assignment = Assignment.by_id(assignment_id) # Verify exists check_resource_exists(assignment, "Assignment", assignment_id) # Verify permissions require_course_instructor(g.user, assignment.course_id) Assignment.remove(assignment.id) return jsonify(success=True)
def move_course(lti=None): assignment_id = int(request.values.get('assignment_id')) new_course_id = int(request.values.get('new_course_id')) assignment = Assignment.by_id(int(assignment_id)) # Verify exists check_resource_exists(assignment, "Assignment", assignment_id) # Verify permissions require_course_instructor(g.user, assignment.course_id) require_course_instructor(g.user, new_course_id) # Perform action assignment.move_course(new_course_id) return jsonify(success=True)
def load_assignment_give_feedback(): ''' Very random function necessary for syncing with JN - we need to expose the 'on_run' field from assignments in public courses. TODO: Do this for public courses only, not just private ones :return: ''' assignment_id = request.values.get('assignment_id', None) if assignment_id is None: return ajax_failure("No Assignment ID given!") assignment = Assignment.by_id(assignment_id) return jsonify(success=True, give_feedback=assignment.on_run)
def get_assignments(): assignment_ids = request.values.get('assignment_ids', "") course_id = get_course_id() user, user_id = get_user() # TODO: verify that they have the permissions to see this file assignments = [] for assignment_id in assignment_ids.split(","): if not assignment_id.isdigit(): return ajax_failure(f"Unknown Assignment ID: {assignment_id!r}") assignment_id = int(assignment_id) # With Course Role Information assignment = Assignment.by_id(assignment_id) check_resource_exists(assignment, "Assignment", assignment_id) assignments.append(assignment.encode_json()) return ajax_success(dict(assignments=assignments))
def update_submission(self, score, correct): was_changed = self.score != score or self.correct != correct self.score = int(round(100 * score)) self.correct = correct assignment = Assignment.by_id(self.assignment_id) if assignment.reviewed: self.submission_status = SubmissionStatuses.IN_PROGRESS self.grading_status = GradingStatuses.NOT_READY elif self.correct: self.submission_status = SubmissionStatuses.COMPLETED self.grading_status = GradingStatuses.FULLY_GRADED else: self.submission_status = SubmissionStatuses.SUBMITTED self.grading_status = GradingStatuses.PENDING db.session.commit() return was_changed
def encode_json(self): group = AssignmentGroup.by_id(self.assignment_group_id) group_url = group.url if group else None assignment = Assignment.by_id(self.assignment_id) assignment_url = assignment.url if assignment else None return { '_schema_version': 1, 'assignment_group_id': self.assignment_group_id, 'assignment_group_url': group_url, 'assignment_id': self.assignment_id, 'assignment_url': assignment_url, 'position': self.position, 'id': self.id, 'date_modified': datetime_to_string(self.date_modified), 'date_created': datetime_to_string(self.date_created) }
def export(): assignment_id = int(request.values.get('assignment_id')) assignment = Assignment.by_id(assignment_id) course_id = get_course_id(True) user, user_id = get_user() # Verify exists check_resource_exists(assignment, "Assignment", assignment_id) # Verify permissions bundle = export_bundle(assignments=[assignment]) filename = assignment.get_filename() return Response(json.dumps(bundle), mimetype='application/json', headers={ 'Content-Disposition': 'attachment;filename={}'.format(filename) })
def load_editor(lti, editor_information): assignment = Assignment.by_id(editor_information['current_assignment_id']) position = editor_information['assignments'].index(assignment) if position is None: return "No assignment found for this user/course/assignment (maze game):" + repr( editor_information) if not editor_information['submissions']: return "No submission found for this user/course/assignment (maze game):" + repr( editor_information) return render_template( 'maze/maze.html', ip=request.remote_addr, level=assignment.name, submission=editor_information['submissions'][position], assignment=assignment, **editor_information)
def export(): user = load_api_user() assignment_url = request.json.get("assignment_url") assignment_id = request.json.get("assignment_id") if assignment_id or assignment_url: if assignment_url: assignment = Assignment.by_url(assignment_url) else: assignment = Assignment.by_id(assignment_id) check_resource_exists(assignment, "Assignment", assignment_id or assignment_url) if not user.is_instructor(assignment.course_id): return abort(400, "Not an instructor in this assignments' course.") return json.dumps(export_bundle(assignments=[assignment])) group_id = request.json.get("assignment_group_id") if group_id: assignment_group = AssignmentGroup.by_id(group_id) assignments = assignment_group.get_assignments() memberships = assignment_group.get_memberships() if not user.is_instructor(assignment_group.course_id): return abort( 400, "Not an instructor in this assignment group's course.") for assignment in assignments: if not user.is_instructor(assignment.course_id): return abort(400, "Not an instructor in this assignments' course.") bundle = export_bundle(groups=[assignment_group], assignments=assignments, memberships=memberships) return json.dumps(bundle) course_id = request.json.get("course_id") if course_id: if not user.is_instructor(course_id): return abort(400, "Not an instructor in this course.") course = Course.by_id(course_id) groups = course.get_assignment_groups() assignment_and_memberships = course.get_assignments() assignments = [a for a, m in assignment_and_memberships] memberships = [m for a, m in assignment_and_memberships] bundle = export_bundle(groups=groups, assignments=assignments, memberships=memberships) return json.dumps(bundle) abort(400, "No assignment_id or assignment_group_id given")
def browse_history(): # Get parameters course_id = maybe_int(request.values.get('course_id')) assignment_id = maybe_int(request.values.get('assignment_id')) student_id = maybe_int(request.values.get('user_id')) page_offset = maybe_int(request.values.get('page_offset', 0)) embed = maybe_bool(request.values.get('embed')) user, user_id = get_user() # Get resources assignment = Assignment.by_id(assignment_id) student = User.by_id(student_id) course = Course.by_id(course_id) # Verify user can see the submission if user_id != student_id and not user.is_grader(course_id): return ajax_failure("Only graders can see logs for other people.") history = Log.get_history(course_id, assignment_id, student_id, page_offset, HISTORY_PAGE_LIMIT) return render_template('blockpy/browse_history.html', assignment=assignment, student=student, course=course, history=history, embed=embed)
def move_membership(lti=None): # Get arguments assignment_id = int(request.values.get('assignment_id')) old_group_id = int(request.values.get('old_group_id')) new_group_id = int(request.values.get('new_group_id')) assignment = Assignment.by_id(assignment_id) # Verify exists check_resource_exists(assignment, "Assignment", assignment_id) # Verify permissions require_course_instructor(g.user, assignment.course_id) # Verify permissions if new_group_id != -1: new_assignment_group = AssignmentGroup.by_id(new_group_id) require_course_instructor(g.user, new_assignment_group.course_id) if old_group_id != -1: old_assignment_group = AssignmentGroup.by_id(old_group_id) require_course_instructor(g.user, old_assignment_group.course_id) # Perform action AssignmentGroupMembership.move_assignment(assignment_id, new_group_id) # Result return jsonify(success=True)
def parse_assignment_load(): # Lookup Code assignment_group_id = parse_lookup_code() # Assignment Group ID if assignment_group_id is None: assignment_group_id = maybe_int( request.args.get('assignment_group_id')) # Exact "url" code for group if assignment_group_id is None: assignment_group_id = AssignmentGroup.id_by_url( request.args.get('assignment_group_url')) # Assignment ID current_assignment_id = maybe_int(request.args.get('assignment_id')) # Exact "url" code for assignment if current_assignment_id is None: current_assignment_id = Assignment.id_by_url( request.args.get('assignment_url')) # User user = g.get('user', None) user_id = user.id if user else None # Course ID of the user course_id = maybe_int(request.args.get('course_id', None)) if course_id is None: course_id = int(g.course.id) if 'course' in g and g.course else None # LTI submission URL new_submission_url = request.form.get('lis_result_sourcedid', None) # Embedded? embed = request.values.get('embed', 'false').lower() == 'true' # Get group assignment_group = AssignmentGroup.by_id(assignment_group_id) # Get assignments if assignment_group is None: assignment = Assignment.by_id(current_assignment_id) if assignment: assignments = [assignment] else: assignments = [] else: assignments = assignment_group.get_assignments() # Potentially adjust assignment_id if current_assignment_id is None and assignments: current_assignment_id = assignments[0].id # Get submissions if user_id is None or course_id is None: submissions = [] else: submissions = [ assignment.load_or_new_submission(user_id, course_id, new_submission_url, assignment_group_id) for assignment in assignments ] # Determine the users' role in relation to this information role = user.determine_role(assignments, submissions) if user else "anonymous" if role in ("student", "anonymous"): # Check for any IP locked assignments for assignment in assignments: if not assignment.is_allowed(request.remote_addr): return abort( 403, "You cannot access this assignment from your current location: " + request.remote_addr) # Check passcode passcode_protected = False for assignment in assignments: if assignment.has_passcode() and not passcode_protected: passcode_protected = True # Combine the submissions and assignments group = list(zip(assignments, submissions)) # Okay we've got everything return dict(group=group, assignment_group=assignment_group, assignments=assignments, submissions=submissions, assignment_group_id=assignment_group_id, current_assignment_id=current_assignment_id, user=user, user_id=user_id, role=role, course_id=course_id, embed=embed, passcode_protected=passcode_protected)