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')
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')
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)
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()
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)
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)
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"}
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)
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
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)
def to_python(self, value): try: return utils.decode_id(value) except (TypeError, ValueError) as e: raise ValidationError(str(e))
def format(self, value): if type(value) == int: return encode_id(value) else: return decode_id(value)