Exemple #1
0
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 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())
Exemple #3
0
def save_assignment(lti=lti):
    assignment_id = request.values.get('assignment_id')
    user, user_id = get_user()
    course_id = get_course_id()
    assignment = Assignment.query.get(assignment_id)
    # Verify exists
    check_resource_exists(assignment, "Assignment", assignment_id)
    # Verify permissions
    if assignment.owner_id != user.id:
        require_course_grader(user, assignment.course_id)
    # Parse new settings
    updates = {}
    if "hidden" in request.values:
        updates["hidden"] = maybe_bool(request.values.get("hidden"))
    if "reviewed" in request.values:
        updates["reviewed"] = maybe_bool(request.values.get("reviewed"))
    if "public" in request.values:
        updates["public"] = maybe_bool(request.values.get("public"))
    if "url" in request.values:
        updates["url"] = request.values.get("url") or None
    if "ip_ranges" in request.values:
        updates["ip_ranges"] = request.values.get("ip_ranges")
    if "name" in request.values:
        updates["name"] = request.values.get("name")
    if "settings" in request.values:
        updates["settings"] = request.values.get("settings")
    # Perform update
    modified = assignment.edit(updates)
    make_log_entry(assignment.id, assignment.version,
                   course_id or assignment.course_id,
                   user.id, "X-Instructor.Settings.Edit", "assignment_settings.blockpy",
                   message=json.dumps(updates))
    return ajax_success({"modified": modified})
Exemple #4
0
def save_student_file(filename, course_id, user):
    submission_id = request.values.get("submission_id")
    code = request.values.get("code")
    submission = Submission.query.get(submission_id)
    # Verify exists
    check_resource_exists(submission, "Submission", submission_id)
    # Verify permissions
    if submission.user_id != user.id:
        require_course_grader(user, submission.course_id)
    # Validate the maximum file size
    if app.config["MAXIMUM_CODE_SIZE"] < len(code):
        return ajax_failure(
            "Maximum size of code exceeded. Current limit is {}, you uploaded {} characters."
            .format(app.config["MAXIMUM_CODE_SIZE"], len(code)))
    # Perform update
    # TODO: What if submission's assignment version conflicts with current assignments' version?
    version_change = submission.assignment.version != submission.assignment_version
    submission.save_code(filename, code)
    make_log_entry(submission.assignment_id,
                   submission.assignment_version,
                   course_id,
                   user.id,
                   "File.Edit",
                   "answer.py",
                   message=code)
    return ajax_success({"version_change": version_change})
Exemple #5
0
def update_submission_status(lti, lti_exception=None):
    # Get parameters
    submission_id = maybe_int(request.values.get("submission_id"))
    status = request.values.get('status')
    course_id = get_course_id()
    user, user_id = get_user()
    submission = Submission.by_id(submission_id)
    # Check resource exists
    check_resource_exists(submission, "Submission", submission_id)
    # Verify permissions
    if submission.user_id != user_id and not user.is_grader(
            submission.course_id):
        return ajax_failure(
            "This is not your submission and you are not a grader in its course."
        )
    # Do action
    success = submission.update_submission_status(status)
    make_log_entry(submission.assignment_id,
                   submission.assignment_version,
                   course_id,
                   user_id,
                   "Submit",
                   "answer.py",
                   category=status,
                   message=str(success))
    return ajax_success({"success": success})
Exemple #6
0
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 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))
Exemple #9
0
def submissions_user(course_id, owner_id):
    ''' List all the users in the course '''
    course_id = int(course_id)
    course = Course.by_id(course_id)
    check_resource_exists(course, "Course", course_id)
    user, user_id = get_user()
    if course_id is not None:
        is_grader = user.is_grader(course_id)
    else:
        is_grader = False
    is_owner = user_id == maybe_int(owner_id)
    if not is_grader and not is_owner:
        return "You are not an instructor or the owner of the assignment!"
    owner = User.by_id(maybe_int(owner_id))
    assignments = natsorted(course.get_submitted_assignments(),
                            key=lambda r: r.name)
    all_subs = Submission.by_student(owner_id, course_id)
    all_subs = {s[0].assignment_id: s for s in all_subs}
    submissions = [all_subs.get(assignment.id, (None, None, assignment))
                   for assignment in assignments]
    return render_template('courses/submissions_user.html',
                           course_id=course_id,
                           assignments=assignments,
                           submissions=submissions,
                           owner=owner,
                           is_instructor=is_grader)
Exemple #10
0
def fork_group(lti=lti):
    ''' Adds a group to a course'''
    # Get arguments
    assignment_group_id = int(request.values.get('assignment_group_id'))
    assignment_group = AssignmentGroup.by_id(assignment_group_id)
    is_embedded = ('embed' == request.values.get('menu', "select"))
    # Verify exists
    check_resource_exists(assignment_group, "Assignment Group",
                          assignment_group_id)
    # Verify permissions
    require_course_instructor(g.user, assignment_group.course_id)
    # Perform action
    new_assignment_group = AssignmentGroup.new(
        owner_id=g.user.id,
        course_id=assignment_group.course_id,
        name=assignment_group.name)
    new_assignment_group.forked_id = assignment_group_id
    new_assignment_group.forked_version = assignment_group.version
    # Result
    select_url = get_select_menu_link(new_assignment_group.id,
                                      new_assignment_group.name, is_embedded,
                                      True)
    return jsonify(success=True,
                   id=new_assignment_group.id,
                   name=new_assignment_group.name,
                   select=select_url)
Exemple #11
0
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)
Exemple #12
0
 def get_one(self, review_id):
     user, user_id = get_user()
     review = Review.by_id(review_id)
     check_resource_exists(review, "Review", review_id)
     submission = Submission.by_id(review.submission_id)
     check_resource_exists(submission, "Submission", review.submission_id)
     if submission.user_id != user_id:
         require_course_grader(user, submission.course_id)
     return ajax_success(dict(review=review.encode_json()))
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)
Exemple #14
0
 def delete(self, review_id):
     user, user_id = get_user()
     review = Review.by_id(review_id)
     check_resource_exists(review, "Review", review_id)
     submission = Submission.by_id(review.submission_id)
     check_resource_exists(submission, "Submission", review.submission_id)
     require_course_grader(user, submission.course_id)
     review.delete()
     return ajax_success(dict(success=True))
Exemple #15
0
def get_groups_submissions(group_id, user_id, course_id):
    group = AssignmentGroup.by_id(group_id)
    check_resource_exists(group, "AssignmentGroup", group_id)
    assignments = group.get_assignments()
    submissions = [
        assignment.load(user_id, course_id=course_id)
        for assignment in assignments
    ]
    return group, assignments, submissions
Exemple #16
0
def remove_course(course_id):
    course_id = int(request.values.get('course_id'))
    course = Course.by_id(course_id)
    # Verify exists
    check_resource_exists(course, "Course", course_id)
    # Verify permissions
    require_course_instructor(g.user, course_id)
    # Perform action
    Course.remove(course_id)
    flash('Course removed')
    return redirect(url_for('courses.index'))
Exemple #17
0
def rename_course():
    course_id = int(request.values.get('course_id'))
    course = Course.by_id(course_id)
    # Verify exists
    check_resource_exists(course, "Course", course_id)
    # Verify permissions
    require_course_instructor(g.user, course_id)
    # Perform action
    new_name = request.values.get('name')
    Course.rename(course_id, new_name)
    return jsonify(success=True)
Exemple #18
0
def get_submission_image(lti=lti):
    submission_id = int(request.values.get('submission_id'))
    relative_image_path = 'uploads/submission_blocks/{}.png'.format(submission_id)
    submission = Submission.query.get(submission_id)
    user, user_id = get_user()
    # Check exists
    check_resource_exists(submission, "Submission", submission_id)
    # Check permissions
    if submission.user_id != user_id:
        require_course_grader(user, submission.course_id)
    # Do action
    return app.send_static_file(relative_image_path)
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)
Exemple #20
0
def update_grading_status(lti, lti_exception=None):
    submission_id = maybe_int(request.values.get("submission_id"))
    # TODO: Pretty sure multiple assignments are broken for grading
    assignment_group_id = maybe_int(request.values.get('assignment_group_id'))
    new_grading_status = request.values.get("new_grading_status")
    user, user_id = get_user()
    submission = Submission.by_id(submission_id)
    # Check resource exists
    check_resource_exists(submission, "Submission", submission_id)
    # Verify permissions
    if not user.is_grader(submission.course_id):
        return ajax_failure(
            "This is not your submission and you are not a grader in its course."
        )
    submission.update_grading_status(new_grading_status)
    if submission.grading_status != GradingStatuses.FULLY_GRADED:
        return ajax_success({'new_status': new_grading_status})
    # Do action
    if assignment_group_id is None:
        assignment_group_id = submission.assignment_group_id
    error = "Generic LTI Failure - perhaps not logged into LTI session?"
    try:
        success, score = lti_post_grade(lti, submission, None,
                                        assignment_group_id,
                                        submission.user_id,
                                        submission.course_id)
    except LTIPostMessageException as e:
        success = False
        error = str(e)
    if success:
        make_log_entry(submission.assignment_id,
                       submission.assignment_version,
                       submission.course_id,
                       user_id,
                       "X-Submission.LMS",
                       "answer.py",
                       message=str(score))
        return ajax_success({"submitted": True, "new_status": "FullyGraded"})
    else:
        submission.update_grading_status(GradingStatuses.FAILED)
        make_log_entry(submission.assignment_id,
                       submission.assignment_version,
                       submission.course_id,
                       user_id,
                       "X-Submission.LMS.Failure",
                       "answer.py",
                       message=error)
        return ajax_failure({
            "submitted": False,
            "message": error,
            "new_status": "Failed"
        })
Exemple #21
0
 def get_all(self):
     submission_id = maybe_int(request.values.get('submission_id'))
     user, user_id = get_user()
     if submission_id is None:
         reviews = Review.get_generic_reviews()
     else:
         submission = Submission.by_id(submission_id)
         check_resource_exists(submission, "Submission", submission_id)
         reviews = Review.get_for_submission(submission_id)
         if submission.user_id != user_id:
             require_course_grader(user, submission.course_id)
     return ajax_success(
         dict(reviews=[review.encode_json() for review in reviews]))
Exemple #22
0
 def put(self, review_id):
     user, user_id = get_user()
     review = Review.by_id(review_id)
     check_resource_exists(review, "Review", review_id)
     submission = Submission.by_id(review.submission_id)
     check_resource_exists(submission, "Submission", review.submission_id)
     require_course_grader(user, submission.course_id)
     review_data = request.json.copy()
     del review_data['id']
     fix_nullables(review_data)
     review_data['author_id'] = user_id
     edited_review = review.edit(review_data)
     return ajax_success(dict(review=edited_review.encode_json()))
Exemple #23
0
def update_submission_status():
    submission_id = maybe_int(request.values.get("submission_id"))
    new_submission_status = request.values.get("new_submission_status")
    user, user_id = get_user()
    submission = Submission.by_id(submission_id)
    # Check resource exists
    check_resource_exists(submission, "Submission", submission_id)
    # Verify permissions
    if not user.is_grader(submission.course_id):
        return ajax_failure(
            "You are not a grader in this submission's course.")
    submission.update_submission_status(new_submission_status)
    return ajax_success({'new_status': new_submission_status})
Exemple #24
0
def get_image():
    submission_id = int(request.values.get('submission_id'))
    directory = request.values.get('directory')
    relative_image_path = 'uploads/{}/{}.png'.format(directory, submission_id)
    submission = Submission.query.get(submission_id)
    user, user_id = get_user()
    # Check exists
    check_resource_exists(submission, "Submission", submission_id)
    # Check permissions
    if submission.user_id != user_id and not user.is_grader(submission.course_id):
        return ajax_failure("This is not your submission and you are not a grader in its course.")
    # Do action
    return app.send_static_file(relative_image_path)
Exemple #25
0
def remove_group(lti=lti):
    ''' Removes a group from a course'''
    assignment_group_id = int(request.values.get('assignment_group_id'))
    assignment_group = AssignmentGroup.by_id(assignment_group_id)
    # Verify exists
    check_resource_exists(assignment_group, "Assignment Group",
                          assignment_group_id)
    # Verify permissions
    require_course_instructor(g.user, assignment_group.course_id)
    # Perform action
    AssignmentGroup.remove(assignment_group.id)
    # Result
    return jsonify(success=True)
Exemple #26
0
 def post(self):
     user, user_id = get_user()
     submission_id = maybe_int(request.values.get('submission_id'))
     submission = Submission.by_id(submission_id)
     check_resource_exists(submission, "Submission", submission_id)
     require_course_grader(user, submission.course_id)
     review_data = request.values.copy()
     del review_data['id']
     review_data['author_id'] = user_id
     review_data['submission_version'] = submission.version
     review_data['assignment_version'] = submission.assignment_version
     fix_nullables(review_data)
     new_review = Review.new(review_data)
     return ajax_success(dict(review=new_review.encode_json()))
Exemple #27
0
def save_instructor_file(course_id, user, filename):
    assignment_id = request.values.get("assignment_id")
    code = request.values.get("code")
    assignment = Assignment.query.get(assignment_id)
    # Verify exists
    check_resource_exists(assignment, "Assignment", assignment_id)
    # Verify permissions
    if assignment.owner_id != user.id:
        require_course_grader(user, assignment.course_id)
    # Perform update
    assignment.save_file(filename, code)
    make_log_entry(assignment_id, assignment.version, course_id, user.id,
                   "X-Instructor.File.Edit", filename, message=code)
    return ajax_success({})
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))
Exemple #29
0
def edit_settings(lti=lti):
    # Get arguments
    assignment_group_id = int(request.values.get('assignment_group_id'))
    assignment_group = AssignmentGroup.by_id(assignment_group_id)
    ip_ranges = request.values.get('ip_ranges')
    passcode = request.values.get('passcode')
    # Verify exists
    check_resource_exists(assignment_group, "Assignment Group",
                          assignment_group_id)
    # Verify permissions
    require_course_instructor(g.user, assignment_group.course_id)
    # Perform action
    if request.method == 'POST':
        if ip_ranges is not None:
            for assignment in assignment_group.get_assignments():
                assignment.edit(dict(ip_ranges=ip_ranges))
                assignment.update_setting("passcode", passcode)
        return redirect(request.url)
    # Result
    else:
        assignments = assignment_group.get_assignments()
        passcode = assignments[0].get_setting("passcode", "")
        existing_ip_ranges = [
            assignment.ip_ranges for assignment in assignments
        ]
        merged_duplicates = set(existing_ip_ranges)
        warning = ""
        if len(merged_duplicates) == 1:
            ip_ranges = merged_duplicates.pop()
        elif merged_duplicates:
            ip_ranges = existing_ip_ranges[0]
            warning = "This assignment has multiple IP ranges: <pre>{}</pre>".format(
                "\n".join(existing_ip_ranges))
        return '''
            <!doctype html>
            <title>Edit Assignment Group Settings</title>
            <h1>Edit Assignment Group Settings</h1>
            <p>Assignment: {group_name}</p>
            <p>{warning}</p>
            <form action="" method=post>
              <p>IP Ranges: <input type=text name=ip_ranges value="{ip_ranges}"><br>
                 Passcode: <input type=text name=passcode value="{passcode}"><br>
                 <input type=submit value=Change>
            </form>
            '''.format(group_name=assignment_group.name,
                       ip_ranges=ip_ranges if ip_ranges else "",
                       passcode=passcode if passcode else "",
                       warning=warning)
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)
                    })