Example #1
0
 def test_hashids(self):
     """Tests converting hashes in URLs to IDs. Do not change the values in
     this test.
     """
     assert utils.encode_id(314159) == 'aAPZ9j'
     assert utils.decode_id('aAPZ9j') == 314159
     assert utils.encode_id(11235) == 'b28KJe'
     assert utils.decode_id('b28KJe') == 11235
     self.assertRaises(ValueError, utils.decode_id, 'deadbeef')
Example #2
0
 def test_hashids(self):
     """Tests converting hashes in URLs to IDs. Do not change the values in
     this test.
     """
     assert utils.encode_id(314159) == 'aAPZ9j'
     assert utils.decode_id('aAPZ9j') == 314159
     assert utils.encode_id(11235) == 'b28KJe'
     assert utils.decode_id('b28KJe') == 11235
     self.assertRaises(ValueError, utils.decode_id, 'deadbeef')
Example #3
0
 def test_hashids(self):
     """Tests converting hashes in URLs to IDs. Do not change the values in
     this test.
     """
     assert self.app.url_map.converters['hashid'] == utils.HashidConverter
     assert utils.encode_id(314159) == 'aAPZ9j'
     assert utils.decode_id('aAPZ9j') == 314159
     assert utils.encode_id(11235) == 'b28KJe'
     assert utils.decode_id('b28KJe') == 11235
     self.assertRaises(ValidationError, utils.decode_id, 'deadbeef')
Example #4
0
File: admin.py Project: gratimax/ok
def staff_flag_backup(cid, email, aid):
    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)
    result_page = url_for('.student_assignment_detail', cid=cid,
                          email=email, aid=aid)

    student = User.lookup(email)
    if not student:
        abort(404)
    user_ids = assign.active_user_ids(student.id)

    bid = request.form.get('bid')

    form = forms.CSRFForm()
    if form.validate_on_submit():
        backup = Backup.query.filter_by(id=utils.decode_id(bid),
                                        assignment=assign).one_or_none()
        if not backup:
            flash('{} does not exist'.format(bid, 'error'))
            return redirect(result_page)

        if not backup.flagged:
            result = assign.flag(backup.id, user_ids)
            flash('Flagged backup {} for grading'.format(bid), 'success')
        else:
            result = assign.unflag(backup.id, user_ids)
            flash('Removed grading flag on {}'.format(bid), 'success')

    return redirect(result_page)
Example #5
0
def staff_flag_backup(cid, email, aid):
    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)
    result_page = url_for('.student_assignment_detail', cid=cid,
                          email=email, aid=aid)

    student = User.lookup(email)
    if not student:
        abort(404)
    user_ids = assign.active_user_ids(student.id)

    bid = request.form.get('bid')

    form = forms.CSRFForm()
    if form.validate_on_submit():
        backup = Backup.query.filter_by(id=utils.decode_id(bid),
                                        assignment=assign).one_or_none()
        if not backup:
            flash('{} does not exist'.format(bid, 'error'))
            return redirect(result_page)

        if not backup.flagged:
            result = assign.flag(backup.id, user_ids)
            flash('Flagged backup {} for grading'.format(bid), 'success')
        else:
            result = assign.unflag(backup.id, user_ids)
            flash('Removed grading flag on {}'.format(bid), 'success')

    return redirect(result_page)
Example #6
0
def parse_moss_results(base_url, hashed_ids, logger, pattern, template, review_threshold=101):
    run_time = datetime.now()
    match = 0
    # Convert decimal thresholds to percentages
    if review_threshold < 1:
        review_threshold *= 100
    while True:
        r = requests.get('{}/match{}-top.html'.format(base_url, match))
        if r.status_code == 404:
            logger.info('Finished parsing {} results.'.format(match))
            break
        match += 1
        parser = MossParser()
        parser.feed(r.content.decode())
        hashidA, hashidB = parser.ids
        if hashed_ids and hashidA not in hashed_ids and hashidB not in hashed_ids:
            logger.info('Skipping Moss result #{}.'.format(match))
            continue
        submissionA = Backup.query.filter_by(id=decode_id(hashidA)).one_or_none()
        submissionB = Backup.query.filter_by(id=decode_id(hashidB)).one_or_none()

        similarityA, similarityB = parser.similarities
        rangesA, rangesB = parser.ranges[::2], parser.ranges[1::2]
        matchesA = [[int(i) for i in r.split('-')] for r in rangesA]
        matchesB = [[int(i) for i in r.split('-')] for r in rangesB]
        matchesA = recalculate_lines(submissionA, matchesA, pattern)
        matchesB = recalculate_lines(submissionB, matchesB, pattern)
        similarityA = recalculate_similarity(submissionA, matchesA, template)
        similarityB = recalculate_similarity(submissionB, matchesB, template)
        if not hashed_ids or hashidA in hashed_ids:
            result = MossResult(primary=submissionA, secondary=submissionB,
                primary_matches=matchesA, secondary_matches=matchesB,
                tags=['review'] if similarityA >= review_threshold else [], 
                similarity=similarityA, run_time=run_time)
            db.session.add(result)
        if not hashed_ids or hashidB in hashed_ids:
            result = MossResult(primary=submissionB, secondary=submissionA,
                primary_matches=matchesB, secondary_matches=matchesA,
                tags=['review'] if similarityB >= review_threshold else [],
                similarity=similarityB, run_time=run_time)
            db.session.add(result)
        logger.info('Adding Moss result #{}...'.format(match))
    db.session.commit()
Example #7
0
def new_comment():
    if not models.Comment.can(None, current_user, "create"):
        abort(403)

    comment = models.Comment(backup_id=utils.decode_id(
        request.form['backup_id']),
                             author_id=current_user.id,
                             filename=request.form['filename'],
                             line=request.form.get('line', type=int),
                             message=request.form['message'])
    db.session.add(comment)
    db.session.commit()
    return render_template('student/assignment/comment.html', comment=comment)
Example #8
0
def new_comment():
    if not models.Comment.can(None, current_user, "create"):
        abort(403)

    comment = models.Comment(
        backup_id=utils.decode_id(request.form['backup_id']),
        author_id=current_user.id,
        filename=request.form['filename'],
        line=request.form.get('line', type=int),
        message=request.form['message'])
    db.session.add(comment)
    db.session.commit()
    return render_template('student/assignment/comment.html', comment=comment)
Example #9
0
 def add_score(self, user):
     args = self.parse_args()
     try:
         bid = decode_id(args['bid'])
     except (ValueError, TypeError):
         restful.abort(404)
     backup = models.Backup.query.get(bid)
     kind = args['kind'].lower().strip()
     score, message = args['score'], args['message']
     score = make_score(user, backup, score, message, kind)
     if score:
         return {'success': True, 'message': 'OK'}
     return {'success': False, 'message': "Permission error"}
Example #10
0
File: api.py Project: gratimax/ok
 def add_score(self, user):
     args = self.parse_args()
     try:
         bid = decode_id(args['bid'])
     except (ValueError, TypeError):
         restful.abort(404)
     backup = models.Backup.query.get(bid)
     kind = args['kind'].lower().strip()
     score, message = args['score'], args['message']
     score = make_score(user, backup, score, message, kind)
     if score:
         return {'success': True, 'message': 'OK'}
     return {'success': False, 'message': "Permission error"}
Example #11
0
def assign_grading(cid, 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'):
        flash('Cannot access assignment', 'error')
        return abort(404)

    form = forms.CreateTaskForm()
    course_staff = sorted(current_course.get_staff(), key=lambda x: x.role)
    details = lambda e: "{0} - ({1})".format(e.user.email, e.role)
    form.staff.choices = [(utils.encode_id(e.user_id), details(e))
                          for e in course_staff]

    if not form.staff.data:
        # Select all by default
        form.staff.default = [u[0] for u in form.staff.choices]
        form.process()

    if form.validate_on_submit():
        # TODO: Use worker job for this (this is query intensive)
        selected_users = []
        for hash_id in form.staff.data:
            user = User.get_by_id(utils.decode_id(hash_id))
            if user and user.is_enrolled(cid, roles=STAFF_ROLES):
                selected_users.append(user)

        # Available backups
        data = assign.course_submissions()
        backups = set(b['backup']['id'] for b in data if b['backup'])
        students = set(b['user']['id'] for b in data if b['backup'])
        no_submissions = set(b['user']['id'] for b in data if not b['backup'])

        tasks = GradingTask.create_staff_tasks(backups, selected_users, aid,
                                               cid, form.kind.data,
                                               form.only_unassigned.data)

        num_with_submissions = len(students) - len(no_submissions)
        flash(("Created {0} tasks ({1} students) for {2} staff.".format(
            len(tasks), num_with_submissions, len(selected_users))), "success")
        return redirect(url_for('.assignment', cid=cid, aid=aid))

    # Return template with options for who has to grade.
    return render_template('staff/grading/assign_tasks.html',
                           current_course=current_course,
                           assignment=assign,
                           form=form)
Example #12
0
    def get(self, user, key=None):
        if key is None:
            restful.abort(405)
        try:
            bid = decode_id(key)
        except (ValueError, TypeError):
            restful.abort(404)

        backup = self.model.query.filter_by(id=bid).first()
        if not backup:
            if user.is_admin:
                return restful.abort(404)
            return restful.abort(403)
        if not self.model.can(backup, user, 'view'):
            return restful.abort(403)
        backup.group = [models.User.get_by_id(uid) for uid in backup.owners()]
        return backup
Example #13
0
    def get(self, user, key=None):
        if key is None:
            restful.abort(405)
        try:
            bid = decode_id(key)
        except (ValueError, TypeError):
            restful.abort(404)

        backup = self.model.query.filter_by(id=bid).first()
        if not backup:
            if user.is_admin:
                return restful.abort(404)
            return restful.abort(403)
        if not self.model.can(backup, user, 'view'):
            return restful.abort(403)
        backup.group = [models.User.get_by_id(uid) for uid in backup.owners()]
        return backup
Example #14
0
File: admin.py Project: gratimax/ok
def assign_grading(cid, 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'):
        flash('Cannot access assignment', 'error')
        return abort(404)

    form = forms.CreateTaskForm()
    course_staff = sorted(current_course.get_staff(), key=lambda x: x.role)
    details = lambda e: "{0} - ({1})".format(e.user.email, e.role)
    form.staff.choices = [(utils.encode_id(e.user_id), details(e))
                          for e in course_staff]

    if not form.staff.data:
        # Select all by default
        form.staff.default = [u[0] for u in form.staff.choices]
        form.process()

    if form.validate_on_submit():
        # TODO: Use worker job for this (this is query intensive)
        selected_users = []
        for hash_id in form.staff.data:
            user = User.get_by_id(utils.decode_id(hash_id))
            if user and user.is_enrolled(cid, roles=STAFF_ROLES):
                selected_users.append(user)

        # Available backups:
        students, backups, no_submissions = assign.course_submissions()

        tasks = GradingTask.create_staff_tasks(backups, selected_users, aid, cid,
                                               form.kind.data, form.only_unassigned.data)

        num_with_submissions = len(students) - len(no_submissions)
        flash(("Created {0} tasks ({1} students) for {2} staff."
               .format(len(tasks), num_with_submissions, len(selected_users))),
              "success")
        return redirect(url_for('.assignment', cid=cid, aid=aid))

    # Return template with options for who has to grade.
    return render_template('staff/grading/assign_tasks.html',
                           current_course=current_course, assignment=assign,
                           form=form)
Example #15
0
 def to_python(self, value):
     try:
         return utils.decode_id(value)
     except (TypeError, ValueError) as e:
         raise ValidationError(str(e))
Example #16
0
 def to_python(self, value):
     try:
         return utils.decode_id(value)
     except (TypeError, ValueError) as e:
         raise ValidationError(str(e))
Example #17
0
 def format(self, value):
     if type(value) == int:
         return encode_id(value)
     else:
         return decode_id(value)
Example #18
0
File: api.py Project: gratimax/ok
 def format(self, value):
     if type(value) == int:
         return encode_id(value)
     else:
         return decode_id(value)