Exemplo n.º 1
0
    def setUp(self):
        super(TestGrading, self).setUp()
        self.setup_course()

        message_dict = {'file_contents': {'backup.py': '1'}, 'analytics': {}}

        self.active_user_ids = [self.user1.id, self.user2.id, self.user3.id]
        self.active_staff = [self.staff1, self.staff2]
        self.active_assignments = [self.assignment, self.assignment2]

        Group.invite(self.user1, self.user2, self.assignment)
        group = Group.lookup(self.user1, self.assignment)
        group.accept(self.user2)

        # Creates 5 submissions for each assignment per user, each spaced two minutes apart

        for assign in self.active_assignments:
            time = assign.due_date - datetime.timedelta(minutes=30)
            for num in range(5):
                for user_id in self.active_user_ids:
                    num += 1
                    time += datetime.timedelta(minutes=2)
                    backup = Backup(submitter_id=user_id,
                        assignment=assign, submit=True)
                    messages = [Message(kind=k, backup=backup,
                        contents=m) for k, m in message_dict.items()]
                    backup.created = time
                    db.session.add_all(messages)
                    db.session.add(backup)
                    # Debugging print if tests fails
                    # print("User {} | Assignment {} | Submission {} | Time {}".format(
                    #     user_id, assign.id, num, time))
        db.session.commit()
Exemplo n.º 2
0
    def setUp(self):
        super(TestGrading, self).setUp()
        self.setup_course()

        message_dict = {'file_contents': {'backup.py': '1'}, 'analytics': {}}

        self.active_user_ids = [self.user1.id, self.user2.id, self.user3.id]
        self.active_staff = [self.staff1, self.staff2]
        self.active_assignments = [self.assignment, self.assignment2]

        Group.invite(self.user1, self.user2, self.assignment)
        group = Group.lookup(self.user1, self.assignment)
        group.accept(self.user2)

        # Creates 5 submissions for each assignment per user, each spaced two minutes apart

        for assign in self.active_assignments:
            time = assign.due_date - datetime.timedelta(minutes=30)
            for num in range(5):
                for user_id in self.active_user_ids:
                    num += 1
                    time += datetime.timedelta(minutes=2)
                    backup = Backup(submitter_id=user_id,
                        assignment=assign, submit=True)
                    messages = [Message(kind=k, backup=backup,
                        contents=m) for k, m in message_dict.items()]
                    backup.created = time
                    db.session.add_all(messages)
                    db.session.add(backup)
                    # Debugging print if tests fails
                    # print("User {} | Assignment {} | Submission {} | Time {}".format(
                    #     user_id, assign.id, num, time))
        db.session.commit()
Exemplo n.º 3
0
    def setUp(self):
        """ Add submissions for 3 users. """
        super(TestRevision, self).setUp()
        self.setup_course()

        message_dict = {'file_contents': {'backup.py': '1'}, 'analytics': {}}

        self.active_user_ids = [self.user1.id, self.user2.id, self.user3.id]

        self.assignment.revisions_allowed = True

        time = self.assignment.due_date # Set to dt.now(), so future subms are late
        for user_id in self.active_user_ids:
            time -= datetime.timedelta(minutes=15)
            backup = Backup(submitter_id=user_id,
                            assignment=self.assignment, submit=True)
            # Revisions are submitted on time.
            backup.created = time
            messages = [Message(kind=k, backup=backup,
                                contents=m) for k, m in message_dict.items()]
            db.session.add_all(messages)
            db.session.add(backup)

        # Put user 3 in a group with user 4
        Group.invite(self.user3, self.user4, self.assignment)
        group = Group.lookup(self.user3, self.assignment)
        group.accept(self.user4)

        okversion = Version(name="ok-client", current_version="v1.5.0",
            download_link="http://localhost/ok")
        db.session.add(okversion)

        db.session.commit()
Exemplo n.º 4
0
def gen_backup(user, assignment):
    seconds_offset = random.randrange(-100000, 100)
    messages = gen_messages(assignment, seconds_offset)
    submit = gen_bool(0.3)
    if submit:
        messages['file_contents']['submit'] = ''
    backup = Backup(
        created=assignment.due_date - datetime.timedelta(seconds=seconds_offset),
        submitter_id=user.id,
        assignment_id=assignment.id,
        submit=submit)
    backup.messages = [Message(kind=k, contents=m) for k, m in messages.items()]
    return backup
Exemplo n.º 5
0
def gen_backup(user, assignment):
    seconds_offset = random.randrange(-100000, 100)
    messages = gen_messages(assignment, seconds_offset)
    submit = gen_bool(0.3)
    if submit:
        messages['file_contents']['submit'] = ''
    backup = Backup(created=assignment.due_date -
                    datetime.timedelta(seconds=seconds_offset),
                    submitter_id=user.id,
                    assignment_id=assignment.id,
                    submit=submit)
    backup.messages = [
        Message(kind=k, contents=m) for k, m in messages.items()
    ]
    return backup
Exemplo n.º 6
0
    def test_files(self):
        backup = Backup(submitter_id=self.user1.id,
                        assignment=self.assignment,
                        submit=True)
        message = Message(kind='file_contents',
                          backup=backup,
                          contents={
                              'hog.py': 'def foo():\n    return',
                              'submit': True
                          })
        db.session.add(message)
        db.session.add(backup)
        db.session.commit()

        # submit should not show up
        assert backup.files() == {'hog.py': 'def foo():\n    return'}
Exemplo n.º 7
0
def download(name, submit, bid, file):
    backup = Backup.query.get(bid)
    if not (backup and Backup.can(backup, current_user, "view")):
        abort(404)
    if backup.submit != submit:
        return redirect(url_for('.download', name=name, submit=backup.submit,
                                bid=bid, file=file))
    try:
        contents = backup.files()[file]
    except KeyError:
        abort(404)
    response = make_response(contents)

    inline = 'raw' in request.args

    content_disposition = "inline" if inline else "attachment"
    response.headers["Content-Disposition"] = ("{0}; filename={1!s}"
                                               .format(content_disposition, file))
    response.headers["Content-Security-Policy"] = "default-src 'none';"
    response.headers["X-Content-Type-Options"] = "nosniff"
    if file.endswith('.ipynb') and not inline:
        # Prevent safari from adding a .txt extension to files
        response.headers["Content-Type"] = "application/octet-stream; charset=UTF-8"
    else:
        response.headers["Content-Type"] = "text/plain; charset=UTF-8"

    return response
Exemplo n.º 8
0
def code(name, submit, bid):
    assign = get_assignment(name)
    backup = Backup.query.get(bid)

    if not (backup and Backup.can(backup, current_user, "view")):
        abort(404)
    if backup.submit != submit:
        return redirect(url_for('.code', name=name, submit=backup.submit, bid=bid))

    diff_type = request.args.get('diff')
    if diff_type not in (None, 'short', 'full'):
        return redirect(url_for('.code', name=name, submit=submit, bid=bid))
    if not assign.files and diff_type:
        return abort(404)

    # sort comments by (filename, line)
    comments = collections.defaultdict(list)
    for comment in backup.comments:
        comments[(comment.filename, comment.line)].append(comment)
    # highlight files and add comments
    files = highlight.diff_files(assign.files, backup.files(), diff_type)
    for filename, source_file in files.items():
        for line in source_file.lines:
            line.comments = comments[(filename, line.line_after)]
    return render_template('student/assignment/code.html',
        course=assign.course, assignment=assign, backup=backup,
        files=files, diff_type=diff_type)
Exemplo n.º 9
0
def flag(name, bid):
    assign = get_assignment(name)
    user_ids = assign.active_user_ids(current_user.id)
    flag = 'flag' in request.form
    next_url = request.form['next']

    backup = models.Backup.query.get(bid)
    if not Backup.can(backup, current_user, "view"):
        abort(404)

    if not assign.active:
        flash('It is too late to change what submission is graded.', 'warning')
    elif flag:
        result = assign.flag(bid, user_ids)
        flash('Flagged submission {}. '.format(result.hashid) +
              'This submission will be used for grading', 'success')
    else:
        result = assign.unflag(bid, user_ids)
        flash('Removed flag from {}. '.format(result.hashid) +
              'The most recent submission will be used for grading.', 'success')

    if is_safe_redirect_url(request, next_url):
        return redirect(next_url)
    else:
        flash("Not a valid redirect", "danger")
        abort(400)
Exemplo n.º 10
0
def edit_backup(bid):
    courses, current_course = get_courses()
    backup = Backup.query.options(db.joinedload('assignment')).get(bid)
    if not backup:
        abort(404)
    if not Backup.can(backup, current_user, 'grade'):
        flash("You do not have permission to score this assignment.",
              "warning")
        abort(401)
    form = forms.SubmissionTimeForm()
    if form.validate_on_submit():
        backup.custom_submission_time = form.get_submission_time(
            backup.assignment)
        db.session.commit()
        flash('Submission time saved', 'success')
        return redirect(url_for('.edit_backup', bid=bid))
    else:
        form.set_submission_time(backup)
    return render_template(
        'staff/grading/edit.html',
        courses=courses,
        current_course=current_course,
        backup=backup,
        student=backup.submitter,
        form=form,
    )
Exemplo n.º 11
0
def code(name, submit, bid):
    assign = get_assignment(name)
    backup = Backup.query.get(bid)

    if not (backup and Backup.can(backup, current_user, "view")):
        abort(404)
    if backup.submit != submit:
        return redirect(
            url_for('.code', name=name, submit=backup.submit, bid=bid))

    diff_type = request.args.get('diff')
    if diff_type not in (None, 'short', 'full'):
        return redirect(url_for('.code', name=name, submit=submit, bid=bid))
    if not assign.files and diff_type:
        return abort(404)

    # sort comments by (filename, line)
    comments = collections.defaultdict(list)
    for comment in backup.comments:
        comments[(comment.filename, comment.line)].append(comment)
    # highlight files and add comments
    files = highlight.diff_files(assign.files, backup.files(), diff_type)
    for filename, lines in files.items():
        for line in lines:
            line.comments = comments[(filename, line.line_after)]
    return render_template('student/assignment/code.html',
                           course=assign.course,
                           assignment=assign,
                           backup=backup,
                           files=files,
                           diff_type=diff_type)
Exemplo n.º 12
0
def flag(name, bid):
    assign = get_assignment(name)
    user_ids = assign.active_user_ids(current_user.id)
    flag = 'flag' in request.form
    next_url = request.form['next']

    backup = models.Backup.query.get(bid)
    if not Backup.can(backup, current_user, "view"):
        abort(404)

    if not assign.active:
        flash('It is too late to change what submission is graded.', 'warning')
    elif flag:
        result = assign.flag(bid, user_ids)
        flash(
            'Flagged submission {}. '.format(result.hashid) +
            'This submission will be used for grading', 'success')
    else:
        result = assign.unflag(bid, user_ids)
        flash(
            'Removed flag from {}. '.format(result.hashid) +
            'The most recent submission will be used for grading.', 'success')

    if is_safe_redirect_url(request, next_url):
        return redirect(next_url)
    else:
        flash("Not a valid redirect", "danger")
        abort(400)
Exemplo n.º 13
0
def download(name, submit, bid, file):
    backup = Backup.query.get(bid)
    if not (backup and Backup.can(backup, current_user, "view")):
        abort(404)
    if backup.submit != submit:
        return redirect(
            url_for('.download',
                    name=name,
                    submit=backup.submit,
                    bid=bid,
                    file=file))
    try:
        contents = backup.files()[file]
    except KeyError:
        abort(404)
    response = make_response(contents)

    content_disposition = "inline" if 'raw' in request.args else "attachment"
    response.headers["Content-Disposition"] = ("{0}; filename={1!s}".format(
        content_disposition, file))
    response.headers["Content-Security-Policy"] = "default-src 'none';"
    response.headers["X-Content-Type-Options"] = "nosniff"
    response.headers["Content-Type"] = "text/plain; charset=UTF-8"

    return response
Exemplo n.º 14
0
def gen_backup(user, assignment):
    messages = {
        'file_contents': {
            'fizzbuzz.py': modified_file,
            'moby_dick': 'Call me Ishmael.'
        },
        'analytics': {}
    }
    submit = gen_bool(0.1)
    if submit:
        messages['file_contents']['submit'] = ''
    backup = Backup(
        created=assignment.due_date -
        datetime.timedelta(seconds=random.randrange(-100000, 100)),
        submitter_id=user.id,
        assignment_id=assignment.id,
        submit=submit)
    backup.messages = [Message(kind=k, contents=m) for k, m in messages.items()]
    return backup
Exemplo n.º 15
0
    def test_files(self):
        backup = Backup(
            submitter_id=self.user1.id,
            assignment=self.assignment,
            submit=True)
        message = Message(
            kind='file_contents',
            backup=backup,
            contents={
                'hog.py': 'def foo():\n    return',
                'submit': True
            })
        db.session.add(message)
        db.session.add(backup)
        db.session.commit()

        # submit should not show up
        assert backup.files() == {
            'hog.py': 'def foo():\n    return'
        }
Exemplo n.º 16
0
def composition(bid):
    backup = Backup.query.get(bid)
    if not (backup and Backup.can(backup, current_user, "grade")):
        abort(404)
    form = forms.CompositionScoreForm()
    existing = Score.query.filter_by(backup=backup, kind="composition").first()
    if existing:
        form.kind.data = "composition"
        form.message.data = existing.message
        form.score.data = existing.score
    return grading_view(backup, form=form)
Exemplo n.º 17
0
def gen_backup(user, assignment):
    messages = {
        'file_contents': {
            'fizzbuzz.py': modified_file,
            'moby_dick': 'Call me Ishmael.'
        },
        'analytics': {}
    }
    submit = gen_bool(0.1)
    if submit:
        messages['file_contents']['submit'] = ''
    backup = Backup(created=assignment.due_date -
                    datetime.timedelta(seconds=random.randrange(-100000, 100)),
                    submitter_id=user.id,
                    assignment_id=assignment.id,
                    submit=submit)
    backup.messages = [
        Message(kind=k, contents=m) for k, m in messages.items()
    ]
    return backup
Exemplo n.º 18
0
Arquivo: admin.py Projeto: gratimax/ok
def composition(bid):
    backup = Backup.query.get(bid)
    if not (backup and Backup.can(backup, current_user, "grade")):
        abort(404)
    form = forms.CompositionScoreForm()
    existing = Score.query.filter_by(backup=backup, kind="composition").first()
    if existing:
        form.kind.data = "composition"
        form.message.data = existing.message
        form.score.data = existing.score
    return grading_view(backup, form=form)
Exemplo n.º 19
0
def score_from_csv(assign_id, rows, kind='total', invalid=None, message=None):
    """
    Job for uploading Scores.

    @param ``rows`` should be a list of records (mappings),
        with labels `email` and `score`
    """
    log = jobs.get_job_logger()
    current_user = jobs.get_current_job().user
    assign = Assignment.query.get(assign_id)

    message = message or '{} score for {}'.format(kind.title(), assign.display_name)

    def log_err(msg):
        log.info('\t!  {}'.format(msg))

    log.info("Uploading scores for {}:\n".format(assign.display_name))

    if invalid:
        log_err('skipping {} invalid entries on lines:'.format(len(invalid)))
        for line in invalid:
            log_err('\t{}'.format(line))
        log.info('')

    success, total = 0, len(rows)
    for i, row in enumerate(rows, start=1):
        try:
            email, score = row['email'], row['score']
            user = User.query.filter_by(email=email).one()

            backup = Backup.query.filter_by(assignment=assign, submitter=user, submit=True).first()
            if not backup:
                backup = Backup.create(submitter=user, assignment=assign, submit=True)

            uploaded_score = Score(grader=current_user, assignment=assign,
                    backup=backup, user=user, score=score, kind=kind, message=message)

            db.session.add(uploaded_score)
            uploaded_score.archive_duplicates()

        except SQLAlchemyError:
            print_exc()
            log_err('error: user with email `{}` does not exist'.format(email))
        else:
            success += 1
        if i % 100 == 0:
            log.info('\nUploaded {}/{} Scores\n'.format(i, total))
    db.session.commit()

    log.info('\nSuccessfully uploaded {} "{}" scores (with {} errors)'.format(success, kind, total - success))

    return '/admin/course/{cid}/assignments/{aid}/scores'.format(
                cid=jobs.get_current_job().course_id, aid=assign_id)
Exemplo n.º 20
0
    def test_backup_owners(self):
        backup = Backup(
            submitter_id=self.user1.id,
            assignment=self.assignment,
            submit=True)
        backup2 = Backup(
            submitter_id=self.user2.id,
            assignment=self.assignment,
            submit=True)
        db.session.add(backup)
        db.session.add(backup2)
        db.session.commit()
        assert backup2.owners() == {self.user2.id}

        Group.invite(self.user1, self.user2, self.assignment)
        Group.invite(self.user1, self.user3, self.assignment)
        group = Group.lookup(self.user1, self.assignment)
        group.accept(self.user2)

        assert backup.owners() == {self.user1.id, self.user2.id}
        assert backup2.owners() == {self.user1.id, self.user2.id}
Exemplo n.º 21
0
def download(name, submit, bid, file):
    backup = Backup.query.get(bid)
    if not (backup and Backup.can(backup, current_user, "view")):
        abort(404)
    if backup.submit != submit:
        return redirect(url_for('.download', name=name, submit=backup.submit,
                                bid=bid, file=file))
    try:
        contents = backup.files()[file]
    except KeyError:
        abort(404)
    response = make_response(contents)
    response.headers["Content-Disposition"] = "attachment; filename={0!s}".format(file)
    return response
Exemplo n.º 22
0
    def setUp(self):
        """ Add submissions for 3 users. """
        super(TestRevision, self).setUp()
        self.setup_course()

        message_dict = {'file_contents': {'backup.py': '1'}, 'analytics': {}}

        self.active_user_ids = [self.user1.id, self.user2.id, self.user3.id]

        self.assignment.revisions_allowed = True

        time = self.assignment.due_date  # Set to dt.now(), so future subms are late
        for user_id in self.active_user_ids:
            time -= datetime.timedelta(minutes=15)
            backup = Backup(submitter_id=user_id,
                            assignment=self.assignment,
                            submit=True)
            # Revisions are submitted on time.
            backup.created = time
            messages = [
                Message(kind=k, backup=backup, contents=m)
                for k, m in message_dict.items()
            ]
            db.session.add_all(messages)
            db.session.add(backup)

        # Put user 3 in a group with user 4
        Group.invite(self.user3, self.user4, self.assignment)
        group = Group.lookup(self.user3, self.assignment)
        group.accept(self.user4)

        okversion = Version(name="ok-client",
                            current_version="v1.5.0",
                            download_link="http://localhost/ok")
        db.session.add(okversion)

        db.session.commit()
Exemplo n.º 23
0
Arquivo: admin.py Projeto: gratimax/ok
def grading(bid):
    backup = Backup.query.get(bid)
    if not (backup and Backup.can(backup, current_user, "grade")):
        abort(404)

    form = forms.GradeForm()
    existing = Score.query.filter_by(backup=backup).first()

    if existing and existing.kind in GRADE_TAGS:
        form = forms.GradeForm(kind=existing.kind)
        form.kind.data = existing.kind
        form.message.data = existing.message
        form.score.data = existing.score

    return grading_view(backup, form=form)
Exemplo n.º 24
0
def autograde_backup(bid):
    backup = Backup.query.options(db.joinedload('assignment')).get(bid)
    if not backup:
        abort(404)
    if not Backup.can(backup, current_user, 'grade'):
        flash("You do not have permission to score this assignment.", "warning")
        abort(401)

    form = forms.CSRFForm()
    if form.validate_on_submit():
        try:
            autograder.autograde_backup(backup)
            flash('Submitted to the autograder', 'success')
        except ValueError as e:
            flash(str(e), 'error')
    return redirect(url_for('.grading', bid=bid))
Exemplo n.º 25
0
def grading(bid):
    backup = Backup.query.get(bid)
    if not (backup and Backup.can(backup, current_user, "grade")):
        abort(404)

    form = forms.GradeForm()
    existing = [s for s in backup.scores if not s.archived]
    first_score = existing[0] if existing else None

    if first_score and first_score.kind in GRADE_TAGS:
        form = forms.GradeForm(kind=first_score.kind)
        form.kind.data = first_score.kind
        form.message.data = first_score.message
        form.score.data = first_score.score

    return grading_view(backup, form=form)
Exemplo n.º 26
0
 def _make_assignment(self, uids, assignment):
     # create a submission every 15 minutes
     message_dict = {'file_contents': {'backup.py': '1'}, 'analytics': {}}
     time = assignment.due_date
     for _ in range(20):
         for user_id in uids:
             time -= datetime.timedelta(minutes=15)
             backup = Backup(submitter_id=user_id,
                             assignment=assignment,
                             submit=True)
             messages = [
                 Message(kind=k, backup=backup, contents=m)
                 for k, m in message_dict.items()
             ]
             db.session.add_all(messages)
             db.session.add(backup)
     db.session.commit()
Exemplo n.º 27
0
def submit_assignment(name):
    assign = get_assignment(name)
    group = Group.lookup(current_user, assign)
    user_ids = assign.active_user_ids(current_user.id)
    fs = assign.final_submission(user_ids)

    if not assign.uploads_enabled:
        flash("This assignment cannot be submitted online", 'warning')
        return redirect(url_for('.assignment', name=assign.name))
    if not assign.active:
        flash("It's too late to submit this assignment", 'warning')
        return redirect(url_for('.assignment', name=assign.name))

    form = UploadSubmissionForm()
    if form.validate_on_submit():
        backup = Backup(
            submitter=current_user,
            assignment=assign,
            submit=True,
        )
        if form.upload_files.upload_backup_files(backup):
            db.session.add(backup)
            db.session.commit()
            if assign.autograding_key:
                try:
                    submit_continous(backup)
                except ValueError as e:
                    logger.warning('Web submission did not autograde',
                                   exc_info=True)
                    flash('Did not send to autograder: {}'.format(e),
                          'warning')
            flash('Uploaded submission', 'success')
            return redirect(
                url_for(
                    '.code',
                    name=assign.name,
                    submit=backup.submit,
                    bid=backup.id,
                ))

    return render_template('student/assignment/submit.html',
                           assignment=assign,
                           group=group,
                           course=assign.course,
                           form=form)
Exemplo n.º 28
0
def download(name, submit, bid, file):
    backup = Backup.query.get(bid)
    if not (backup and Backup.can(backup, current_user, "view")):
        abort(404)
    if backup.submit != submit:
        return redirect(
            url_for('.download',
                    name=name,
                    submit=backup.submit,
                    bid=bid,
                    file=file))
    try:
        contents = backup.files()[file]
    except KeyError:
        abort(404)
    response = make_response(contents)
    response.headers[
        "Content-Disposition"] = "attachment; filename={0!s}".format(file)
    return response
Exemplo n.º 29
0
    def _add_file(self, filename, contents):
        self.setup_course()

        email = '*****@*****.**'
        self.login(email)
        self.user = User.lookup(email)

        self.backup = Backup(submitter=self.user,
                             assignment=self.assignment,
                             submit=True)

        self.message = Message(backup=self.backup,
                               contents={
                                   filename: contents,
                                   'submit': True
                               },
                               kind='file_contents')

        db.session.add(self.backup)
        db.session.add(self.message)
        db.session.commit()
Exemplo n.º 30
0
def download(name, submit, bid, file):
    backup = Backup.query.get(bid)
    if not (backup and Backup.can(backup, current_user, "view")):
        abort(404)
    if backup.submit != submit:
        return redirect(url_for('.download', name=name, submit=backup.submit,
                                bid=bid, file=file))
    try:
        contents = backup.files()[file]
    except KeyError:
        abort(404)
    response = make_response(contents)

    content_disposition = "inline" if 'raw' in request.args else "attachment"
    response.headers["Content-Disposition"] = ("{0}; filename={1!s}"
                                               .format(content_disposition, file))
    response.headers["Content-Security-Policy"] = "default-src 'none';"
    response.headers["X-Content-Type-Options"] = "nosniff"
    response.headers["Content-Type"] = "text/plain; charset=UTF-8"

    return response
Exemplo n.º 31
0
    def test_backup_owners(self):
        backup = Backup(submitter_id=self.user1.id,
                        assignment=self.assignment,
                        submit=True)
        backup2 = Backup(submitter_id=self.user2.id,
                         assignment=self.assignment,
                         submit=True)
        db.session.add(backup)
        db.session.add(backup2)
        db.session.commit()
        assert backup2.owners() == {self.user2.id}

        Group.invite(self.user1, self.user2, self.assignment)
        Group.invite(self.user1, self.user3, self.assignment)
        group = Group.lookup(self.user1, self.assignment)
        group.accept(self.user2)

        assert backup.owners() == {self.user1.id, self.user2.id}
        assert backup2.owners() == {self.user1.id, self.user2.id}
Exemplo n.º 32
0
def staff_submit_backup(cid, email, aid):
    courses, current_course = get_courses(cid)
    assign = Assignment.query.filter_by(id=aid, course_id=cid).one_or_none()
    if not assign or not Assignment.can(assign, current_user, 'grade'):
        return abort(404)
    student = User.lookup(email)
    if not student:
        abort(404)
    user_ids = assign.active_user_ids(student.id)
    # TODO: DRY - Unify with student upload code - should just be a function
    form = forms.StaffUploadSubmissionForm()
    if form.validate_on_submit():
        backup = Backup(
            submitter=student,
            creator=current_user,
            assignment=assign,
            submit=True,
            custom_submission_time=form.get_submission_time(assign),
        )
        if form.upload_files.upload_backup_files(backup):
            db.session.add(backup)
            db.session.commit()
            if assign.autograding_key:
                try:
                    autograder.submit_continous(backup)
                except ValueError as e:
                    flash('Did not send to autograder: {}'.format(e),
                          'warning')
            flash('Uploaded submission'.format(backup.hashid), 'success')
            return redirect(url_for('.grading', bid=backup.id))
    return render_template(
        'staff/student/submit.html',
        current_course=current_course,
        courses=courses,
        student=student,
        assignment=assign,
        upload_form=form,
    )
Exemplo n.º 33
0
Arquivo: admin.py Projeto: gratimax/ok
def grade(bid):
    """ Used as a form submission endpoint. """
    backup = Backup.query.options(db.joinedload('assignment')).get(bid)
    if not backup:
        abort(404)
    if not Backup.can(backup, current_user, 'grade'):
        flash("You do not have permission to score this assignment.", "warning")
        abort(401)

    form = forms.GradeForm()
    score_kind = form.kind.data.strip().lower()
    is_composition = (score_kind == "composition")
    # TODO: Form should include redirect url instead of guessing based off tag

    if is_composition:
        form = forms.CompositionScoreForm()

    if not form.validate_on_submit():
        return grading_view(backup, form=form)

    score = Score(backup=backup, grader=current_user,
                  assignment_id=backup.assignment_id)
    form.populate_obj(score)
    db.session.add(score)
    db.session.commit()

    # Archive old scores of the same kind
    score.archive_duplicates()

    next_page = None
    flash_msg = "Added a {0} {1} score.".format(score.score, score_kind)

    # Find GradingTasks applicable to this score
    tasks = backup.grading_tasks
    for task in tasks:
        task.score = score
        cache.delete_memoized(User.num_grading_tasks, task.grader)

    db.session.commit()

    if len(tasks) == 1:
        # Go to next task for the current task queue if possible.
        task = tasks[0]
        next_task = task.get_next_task()
        next_route = '.composition' if is_composition else '.grading'
        # Handle case when the task is on the users queue
        if next_task:
            flash_msg += (" There are {0} tasks left. Here's the next submission:"
                          .format(task.remaining))
            next_page = url_for(next_route, bid=next_task.backup_id)
        else:
            flash_msg += " All done with grading for {}".format(backup.assignment.name)
            next_page = url_for('.grading_tasks')
    else:
        # TODO: Send task id or redirect_url in the grading form
        # For now, default to grading tasks
        next_page = url_for('.grading_tasks')

    flash(flash_msg, 'success')

    if not next_page:
        next_page = url_for('.assignment_queues', aid=backup.assignment_id,
                            cid=backup.assignment.course_id)
    return redirect(next_page)
Exemplo n.º 34
0
 def restore_object(self, attrs, instance=None):
     attrs[u'user'] = self.context.get('request').user
     backup = Backup(**attrs)
     if backup.file:
         backup.file.name = backup.name
     return backup
Exemplo n.º 35
0
def grade(bid):
    """ Used as a form submission endpoint. """
    backup = Backup.query.options(db.joinedload('assignment')).get(bid)
    if not backup:
        abort(404)
    if not Backup.can(backup, current_user, 'grade'):
        flash("You do not have permission to score this assignment.", "warning")
        abort(401)

    form = forms.GradeForm()
    score_kind = form.kind.data.strip().lower()
    is_composition = (score_kind == "composition")
    # TODO: Form should include redirect url instead of guessing based off tag

    if is_composition:
        form = forms.CompositionScoreForm()

    if not form.validate_on_submit():
        return grading_view(backup, form=form)

    score = Score(backup=backup, grader=current_user,
                  assignment_id=backup.assignment_id)
    form.populate_obj(score)
    db.session.add(score)
    db.session.commit()

    # Archive old scores of the same kind
    score.archive_duplicates()

    next_page = None
    flash_msg = "Added a {0} {1} score.".format(score.score, score_kind)

    # Find GradingTasks applicable to this score
    tasks = backup.grading_tasks
    for task in tasks:
        task.score = score
        cache.delete_memoized(User.num_grading_tasks, task.grader)

    db.session.commit()

    if len(tasks) == 1:
        # Go to next task for the current task queue if possible.
        task = tasks[0]
        next_task = task.get_next_task()
        next_route = '.composition' if is_composition else '.grading'
        # Handle case when the task is on the users queue
        if next_task:
            flash_msg += (" There are {0} tasks left. Here's the next submission:"
                          .format(task.remaining))
            next_page = url_for(next_route, bid=next_task.backup_id)
        else:
            flash_msg += " All done with grading for {}".format(backup.assignment.name)
            next_page = url_for('.grading_tasks')
    else:
        # TODO: Send task id or redirect_url in the grading form
        # For now, default to grading tasks
        next_page = url_for('.grading_tasks')

    flash(flash_msg, 'success')

    if not next_page:
        next_page = url_for('.assignment_queues', aid=backup.assignment_id,
                            cid=backup.assignment.course_id)
    return redirect(next_page)
Exemplo n.º 36
0
def submit_assignment(name):
    # TODO: Unify student & staff upload.
    assign = get_assignment(name)
    group = Group.lookup(current_user, assign)
    user_ids = assign.active_user_ids(current_user.id)
    fs = assign.final_submission(user_ids)
    if not assign.uploads_enabled:
        flash("This assignment cannot be submitted online", 'warning')
        return redirect(url_for('.assignment', name=assign.name))

    extension = None # No need for an extension
    if not assign.active:
        extension = Extension.get_extension(current_user, assign)
        if not extension:
            flash("It's too late to submit this assignment", 'warning')
            return redirect(url_for('.assignment', name=assign.name))

    if request.method == "POST":
        backup = Backup.create(
            submitter=current_user,
            assignment=assign,
            submit=True,
        )
        assignment = backup.assignment
        if extension:
            backup.custom_submission_time = extension.custom_submission_time

        templates = assignment.files or []
        files = {}

        def extract_file_index(file_ind):
            """ Get the index of of file objects. Used because
            request.files.getlist() does not handle uniquely indexed
            lists.
            >>> extract_file_index('file[12'])
            12
            """
            brace_loc = file_ind.find('[')
            index_str = file_ind[brace_loc+1:-1]
            return int(index_str)


        # A list of one element lists
        sorted_uploads = sorted(list(request.files.items()),
                                key=lambda x: extract_file_index(x[0]))
        uploads = [v[1] for v in sorted_uploads]
        full_path_names = list(request.form.listvalues())[0]

        template_files = assign.files or []
        file_names = [os.path.split(f)[1] for f in full_path_names]
        missing = [t for t in template_files if t not in file_names]
        if missing:
            return jsonify({
                'error': ('Missing files: {}. The following files are required: {}'
                          .format(', '.join(missing), ', '.join(template_files)))
            }), 400

        backup_folder_postfix = time.time()

        for full_path, upload in zip(full_path_names, uploads):
            data = upload.read()
            if len(data) > MAX_UPLOAD_FILE_SIZE:  # file is too large (over 25 MB)
                return jsonify({
                  'error': ('{} is larger than the maximum file size of {} MB'
                            .format(full_path, MAX_UPLOAD_FILE_SIZE/1024/1024))
                }), 400
            try:
                files[full_path] = str(data, 'utf-8')
            except UnicodeDecodeError:
                upload.stream.seek(0) # We've already read data, so reset before uploading
                dest_folder = "uploads/{}/{}/{}/".format(assign.name, current_user.id, backup_folder_postfix)
                bin_file = ExternalFile.upload(upload.stream, current_user.id, full_path,
                                               staff_file=False, prefix=dest_folder,
                                               course_id=assign.course.id,
                                               backup=backup, assignment_id=assign.id)
                db.session.add(bin_file)

        message = Message(kind='file_contents', contents=files)
        backup.messages.append(message)

        db.session.add(backup)
        db.session.commit()

        # Send to continuous autograder
        if assign.autograding_key and assign.continuous_autograding:
            try:
                submit_continuous(backup)
            except ValueError as e:
                flash('Did not send to autograder: {}'.format(e), 'warning')

        return jsonify({
            'backup': backup.hashid,
            'url': url_for('.code', name=assign.name, submit=backup.submit,
                           bid=backup.id)
        })

    return render_template('student/assignment/submit.html', assignment=assign,
                           group=group, course=assign.course)