Ejemplo n.º 1
0
Archivo: admin.py Proyecto: gratimax/ok
def assignment_single_queue(cid, aid, uid):
    courses, current_course = get_courses(cid)
    assignment = Assignment.query.filter_by(id=aid, course_id=cid).one_or_none()
    if not Assignment.can(assignment, current_user, 'grade'):
        flash('Insufficient permissions', 'error')
        return abort(401)

    assigned_grader = User.get_by_id(uid)
    if not Assignment.can(assignment, assigned_grader, 'grade'):
        return abort(404)

    page = request.args.get('page', 1, type=int)
    tasks_query = GradingTask.query.filter_by(assignment=assignment,
                                              grader_id=uid)
    queue = (tasks_query.options(db.joinedload('assignment'))
                        .order_by(GradingTask.score_id.asc())
                        .order_by(GradingTask.created.asc())
                        .paginate(page=page, per_page=20))

    remaining = tasks_query.filter_by(score_id=None).count()
    percent_left = (1-(remaining/max(1, queue.total))) * 100

    return render_template('staff/grading/queue.html', courses=courses,
                           current_course=current_course,
                           assignment=assignment, grader=assigned_grader,
                           queue=queue, remaining=remaining,
                           percent_left=percent_left)
Ejemplo n.º 2
0
def assignment_single_queue(cid, aid, uid):
    courses, current_course = get_courses(cid)
    assignment = Assignment.query.filter_by(id=aid, course_id=cid).one_or_none()
    if not Assignment.can(assignment, current_user, 'grade'):
        flash('Insufficient permissions', 'error')
        return abort(401)

    assigned_grader = User.get_by_id(uid)
    if not Assignment.can(assignment, assigned_grader, 'grade'):
        return abort(404)

    page = request.args.get('page', 1, type=int)
    tasks_query = GradingTask.query.filter_by(assignment=assignment,
                                              grader_id=uid)
    queue = (tasks_query.options(db.joinedload('assignment'))
                        .order_by(GradingTask.score_id.asc())
                        .order_by(GradingTask.created.asc())
                        .paginate(page=page, per_page=20))

    remaining = tasks_query.filter_by(score_id=None).count()
    percent_left = (1-(remaining/max(1, queue.total))) * 100

    return render_template('staff/grading/queue.html', courses=courses,
                           current_course=current_course,
                           assignment=assignment, grader=assigned_grader,
                           queue=queue, remaining=remaining,
                           percent_left=percent_left)
Ejemplo n.º 3
0
def start_moss_job(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.MossSubmissionForm()
    if form.validate_on_submit():
        job = jobs.enqueue_job(
            moss.submit_to_moss,
            description='Moss Upload for {}'.format(assign.display_name),
            course_id=cid,
            user_id=current_user.id,
            assignment_id=assign.id,
            moss_id=form.moss_userid.data,
            file_regex=form.file_regex.data or '*',
            language=form.language.data)
        return redirect(url_for('.course_job', cid=cid, job_id=job.id))
    else:
        return render_template(
            'staff/jobs/moss.html',
            courses=courses,
            current_course=current_course,
            assignment=assign,
            form=form,
        )
Ejemplo n.º 4
0
def start_github_search(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.GithubSearchRecentForm()
    if form.validate_on_submit():
        job = jobs.enqueue_job(
            github_search.search_similar_repos,
            description='Github Search for {}'.format(assign.display_name),
            course_id=cid,
            user_id=current_user.id,
            assignment_id=assign.id,
            keyword=form.keyword.data,
            template_name=form.template_name.data,
            access_token=form.access_token.data,
            weeks_past=form.weeks_past.data,
            language=form.language.data,
            issue_title=form.issue_title.data,
            issue_body=form.issue_body.data)
        return redirect(url_for('.course_job', cid=cid, job_id=job.id))
    else:
        return render_template(
            'staff/jobs/github_search.html',
            courses=courses,
            current_course=current_course,
            assignment=assign,
            form=form,
        )
Ejemplo n.º 5
0
def staff_group_add(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'):
        flash('Cannot access assignment', 'error')
        return abort(404)

    form = forms.StaffAddGroupFrom()
    result_page = url_for('.student_assignment_detail', cid=cid,
                          email=email, aid=aid)

    student = User.lookup(email)
    if not student:
        return abort(404)

    if form.validate_on_submit():
        target = User.lookup(form.email.data)
        if not target or not target.is_enrolled(cid):
            flash("This user is not enrolled", 'warning')
            return redirect(result_page)
        try:
            Group.force_add(current_user, student, target, assign)
        except BadRequest as e:
            flash("Error: {}".format(str(e.description)), 'error')
            return redirect(result_page)

    return redirect(result_page)
Ejemplo n.º 6
0
def assignment_stats(cid, aid):
    courses, current_course = get_courses(cid)
    assign = Assignment.query.filter_by(id=aid, course_id=cid).one_or_none()
    if not Assignment.can(assign, current_user, 'edit'):
        flash('Insufficient permissions', 'error')
        return abort(401)

    stats = Assignment.assignment_stats(assign.id)

    submissions = [d for d in stats.pop('raw_data')]

    pie_chart = pygal.Pie(half_pie=True,
                          disable_xml_declaration=True,
                          style=CleanStyle,
                          inner_radius=.5,
                          legend_at_bottom=True)
    pie_chart.title = 'Students submission status'
    pie_chart.add('Students with Submissions', stats['students_with_subm'])
    pie_chart.add('Students with Backups', stats['students_with_backup'])
    pie_chart.add('Not Started', stats['students_no_backup'])

    return render_template('staff/course/assignment/assignment.stats.html',
                           assignment=assign,
                           subm_chart=pie_chart,
                           courses=courses,
                           stats=stats,
                           submissions=submissions,
                           current_course=current_course)
Ejemplo n.º 7
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)
Ejemplo n.º 8
0
def autograde(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)
    auth_token = get_token_if_valid()
    form = forms.AutogradeForm()
    if form.validate_on_submit():
        if hasattr(form, 'token') and form.token.data:
            token = form.token.data
        else:
            token = auth_token

        autopromotion = form.autopromote.data
        try:
            autograde_assignment(assign, form.autograder_id.data,
                                 token, autopromotion=autopromotion)
            flash('Submitted to the autograder', 'success')
        except ValueError as e:
            flash(str(e), 'error')

    if not form.token.data and auth_token:
        form.token.data = auth_token[0]

    if not form.autograder_id.data and assign.autograding_key:
        form.autograder_id.data = assign.autograding_key

    return render_template('staff/grading/autograde.html',
                           current_course=current_course,
                           assignment=assign, form=form)
Ejemplo n.º 9
0
Archivo: admin.py Proyecto: gratimax/ok
def autograde(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)
    auth_token = get_token_if_valid()
    form = forms.AutogradeForm()
    if form.validate_on_submit():
        if hasattr(form, 'token') and form.token.data:
            token = form.token.data
        else:
            token = auth_token

        autopromotion = form.autopromote.data
        try:
            autograde_assignment(assign, form.autograder_id.data,
                                 token, autopromotion=autopromotion)
            flash('Submitted to the autograder', 'success')
        except ValueError as e:
            flash(str(e), 'error')

    if not form.token.data and auth_token:
        form.token.data = auth_token[0]

    if not form.autograder_id.data and assign.autograding_key:
        form.autograder_id.data = assign.autograding_key

    return render_template('staff/grading/autograde.html',
                           current_course=current_course,
                           assignment=assign, form=form)
Ejemplo n.º 10
0
Archivo: admin.py Proyecto: gratimax/ok
def staff_group_add(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'):
        flash('Cannot access assignment', 'error')
        return abort(404)

    form = forms.StaffAddGroupFrom()
    result_page = url_for('.student_assignment_detail', cid=cid,
                          email=email, aid=aid)

    student = User.lookup(email)
    if not student:
        return abort(404)

    if form.validate_on_submit():
        target = User.lookup(form.email.data)
        if not target or not target.is_enrolled(cid):
            flash("This user is not enrolled", 'warning')
            return redirect(result_page)
        try:
            Group.force_add(current_user, student, target, assign)
        except BadRequest as e:
            flash("Error: {}".format(str(e.description)), 'error')
            return redirect(result_page)

    return redirect(result_page)
Ejemplo n.º 11
0
def staff_group_remove(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)

    student = User.lookup(email)
    if not student:
        abort(404)

    result_page = url_for('.student_assignment_detail',
                          cid=cid,
                          email=email,
                          aid=aid)

    form = forms.CSRFForm()
    if form.validate_on_submit():
        target = User.lookup(request.form['target'])
        if not target:
            flash('{} does not exist'.format(request.form['target']), 'error')
            return redirect(result_page)
        try:
            Group.force_remove(current_user, student, target, assign)
        except BadRequest as e:
            flash("Error: {}".format(str(e.description)), 'error')
    return redirect(result_page)
Ejemplo n.º 12
0
Archivo: admin.py Proyecto: gratimax/ok
def assignment_queues(cid, aid):
    courses, current_course = get_courses(cid)
    assignment = Assignment.query.filter_by(id=aid, course_id=cid).one_or_none()
    if not Assignment.can(assignment, current_user, 'grade'):
        flash('Insufficient permissions', 'error')
        return abort(401)

    queues = GradingTask.get_staff_tasks(assignment.id)

    incomplete = [q.grader.email for q in queues if q.completed != q.total]
    complete = [q.grader.email for q in queues if q.completed == q.total]

    mailto_link = "mailto://{0}?subject={1}&body={2}&cc={3}".format(
        current_user.email,
        "{0} grading queue is not finished".format(assignment.display_name),
        "Queue Link: {0}".format(url_for('admin.grading_tasks', _external=True)),
        ','.join(incomplete)
    )

    remaining = len(incomplete)
    percent_left = (1-(remaining/max(1, len(queues)))) * 100

    if current_user.email in incomplete:
        flash("Hmm... You aren't finished with your queue.", 'info')
    elif current_user.email in complete:
        flash("Nice! You are all done with your queue", 'success')
    else:
        flash("You don't have a queue for this assignment", 'info')

    return render_template('staff/grading/overview.html', courses=courses,
                           current_course=current_course,
                           assignment=assignment, queues=queues,
                           incomplete=incomplete, mailto=mailto_link,
                           remaining=remaining, percent_left=percent_left)
Ejemplo n.º 13
0
Archivo: admin.py Proyecto: gratimax/ok
def templates(cid, aid):
    courses, current_course = get_courses(cid)
    assignment = Assignment.query.filter_by(id=aid, course_id=cid).one_or_none()
    if not Assignment.can(assignment, current_user, 'edit'):
        flash('Insufficient permissions', 'error')
        return abort(401)

    form = forms.AssignmentTemplateForm()

    if assignment.course != current_course:
        return abort(401)

    if form.validate_on_submit():
        files = request.files.getlist("template_files")
        if files:
            templates = {}
            for template in files:
                templates[template.filename] = str(template.read(), 'latin1')
            assignment.files = templates
        cache.delete_memoized(Assignment.name_to_assign_info)
        db.session.commit()
        flash("Templates Uploaded", "success")

    # TODO: Use same student facing code rendering/highlighting
    return render_template('staff/course/assignment.template.html',
                           assignment=assignment, form=form, courses=courses,
                           current_course=current_course)
Ejemplo n.º 14
0
Archivo: admin.py Proyecto: 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)
Ejemplo n.º 15
0
def templates(cid, aid):
    courses, current_course = get_courses(cid)
    assignment = Assignment.query.filter_by(id=aid, course_id=cid).one_or_none()
    if not Assignment.can(assignment, current_user, 'edit'):
        flash('Insufficient permissions', 'error')
        return abort(401)

    form = forms.AssignmentTemplateForm()

    if assignment.course != current_course:
        return abort(401)

    if form.validate_on_submit():
        files = request.files.getlist("template_files")
        if files:
            templates = {}
            for template in files:
                templates[template.filename] = str(template.read(), 'latin1')
            assignment.files = templates
        cache.delete_memoized(Assignment.name_to_assign_info)
        db.session.commit()
        flash("Templates Uploaded", "success")

    # TODO: Use same student facing code rendering/highlighting
    return render_template('staff/course/assignment/assignment.template.html',
                           assignment=assignment, form=form, courses=courses,
                           current_course=current_course)
Ejemplo n.º 16
0
def assignment_queues(cid, aid):
    courses, current_course = get_courses(cid)
    assignment = Assignment.query.filter_by(id=aid, course_id=cid).one_or_none()
    if not Assignment.can(assignment, current_user, 'grade'):
        flash('Insufficient permissions', 'error')
        return abort(401)

    queues = GradingTask.get_staff_tasks(assignment.id)

    incomplete = [q.grader.email for q in queues if q.completed != q.total]
    complete = [q.grader.email for q in queues if q.completed == q.total]

    mailto_link = "mailto://{0}?subject={1}&body={2}&cc={3}".format(
        current_user.email,
        "{0} grading queue is not finished".format(assignment.display_name),
        "Queue Link: {0}".format(url_for('admin.grading_tasks', _external=True)),
        ','.join(incomplete)
    )

    remaining = len(incomplete)
    percent_left = (1-(remaining/max(1, len(queues)))) * 100

    if current_user.email in incomplete:
        flash("Hmm... You aren't finished with your queue.", 'info')
    elif current_user.email in complete:
        flash("Nice! You are all done with your queue", 'success')
    else:
        flash("You don't have a queue for this assignment", 'info')

    return render_template('staff/grading/overview.html', courses=courses,
                           current_course=current_course,
                           assignment=assignment, queues=queues,
                           incomplete=incomplete, mailto=mailto_link,
                           remaining=remaining, percent_left=percent_left)
Ejemplo n.º 17
0
def student_assignment_detail(cid, email, aid):
    courses, current_course = get_courses(cid)
    page = request.args.get('page', 1, type=int)

    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)

    student = User.lookup(email)
    if not student.is_enrolled(cid):
        flash("This user is not enrolled", 'warning')

    assignment_stats = assign.user_status(student, staff_view=True)

    user_ids = assign.active_user_ids(student.id)

    latest = assignment_stats.final_subm or assign.backups(user_ids).first()

    stats = {
        'num_backups': assign.backups(user_ids).count(),
        'num_submissions': assign.submissions(user_ids).count(),
        'current_q': None,
        'attempts': None,
        'latest': latest,
        'analytics': latest and latest.analytics()
    }

    backups = (Backup.query.options(
        db.joinedload('scores'), db.joinedload('submitter')).filter(
            Backup.submitter_id.in_(user_ids),
            Backup.assignment_id == assign.id).order_by(
                Backup.flagged.desc(), Backup.submit.desc(),
                Backup.created.desc()))

    paginate = backups.paginate(page=page, per_page=15)

    if stats['analytics']:
        stats['current_q'] = stats['analytics'].get('question')
        stats['attempts'] = (stats['analytics'].get('history',
                                                    {}).get('all_attempts'))

    return render_template('staff/student/assignment.html',
                           courses=courses,
                           current_course=current_course,
                           student=student,
                           assignment=assign,
                           add_member_form=forms.StaffAddGroupFrom(),
                           paginate=paginate,
                           csrf_form=forms.CSRFForm(),
                           stats=stats,
                           assign_status=assignment_stats)
Ejemplo n.º 18
0
Archivo: admin.py Proyecto: gratimax/ok
def student_assignment_detail(cid, email, aid):
    courses, current_course = get_courses(cid)
    page = request.args.get('page', 1, type=int)

    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)

    student = User.lookup(email)
    if not student.is_enrolled(cid):
        flash("This user is not enrolled", 'warning')

    assignment_stats = assign.user_status(student)

    user_ids = assign.active_user_ids(student.id)

    latest = assignment_stats.final_subm or assign.backups(user_ids).first()

    stats = {
        'num_backups': assign.backups(user_ids).count(),
        'num_submissions': assign.submissions(user_ids).count(),
        'current_q': None,
        'attempts': None,
        'latest': latest,
        'analytics': latest and latest.analytics()
    }

    backups = (Backup.query.options(db.joinedload('scores'),
                                    db.joinedload('submitter'))
                     .filter(Backup.submitter_id.in_(user_ids),
                             Backup.assignment_id == assign.id)
                     .order_by(Backup.flagged.desc(), Backup.submit.desc(),
                               Backup.created.desc()))

    paginate = backups.paginate(page=page, per_page=15)

    if stats['analytics']:
        stats['current_q'] = stats['analytics'].get('question')
        stats['attempts'] = (stats['analytics'].get('history', {})
                                               .get('all_attempts'))

    return render_template('staff/student/assignment.html',
                           courses=courses, current_course=current_course,
                           student=student, assignment=assign,
                           add_member_form=forms.StaffAddGroupFrom(),
                           paginate=paginate,
                           csrf_form=forms.CSRFForm(),
                           upload_form=forms.UploadSubmissionForm(),
                           stats=stats,
                           assign_status=assignment_stats)
Ejemplo n.º 19
0
def autograde(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.CSRFForm()
    if form.validate_on_submit():
        try:
            autograder.autograde_assignment(assign)
            flash('Submitted to the autograder', 'success')
        except ValueError as e:
            flash(str(e), 'error')
    return redirect(url_for('.assignment', cid=cid, aid=aid))
Ejemplo n.º 20
0
def staff_submit_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)
    # TODO: DRY - Unify with student upload code - should just be a function
    form = forms.UploadSubmissionForm()
    if form.validate_on_submit():
        files = request.files.getlist("upload_files")
        if files:
            templates = assign.files
            messages = {'file_contents': {}}
            for upload in files:
                data = upload.read()
                if len(data) > 2097152:
                    # File is too large (over 2 MB)
                    flash(("{} is over the maximum file size limit of 2MB"
                           .format(upload.filename)),
                          'danger')
                    return redirect(result_page)
                messages['file_contents'][upload.filename] = str(data, 'latin1')
            if templates:
                missing = []
                for template in templates:
                    if template not in messages['file_contents']:
                        missing.append(template)
                if missing:
                    flash(("Missing files: {}. The following files are required: {}"
                           .format(', '.join(missing), ', '.join([t for t in templates]))
                           ), 'danger')
                    return redirect(result_page)
            # use student, not current_user
            backup = ok_api.make_backup(student, assign.id, messages, True)
            if form.flag_submission.data:
                assign.flag(backup.id, user_ids)
            if assign.autograding_key:
                try:
                    submit_continous(backup)
                except ValueError as e:
                    flash('Did not send to autograder: {}'.format(e), 'warning')

            flash("Uploaded submission (ID: {})".format(backup.hashid), 'success')
            return redirect(result_page)
Ejemplo n.º 21
0
Archivo: admin.py Proyecto: gratimax/ok
def staff_submit_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)
    # TODO: DRY - Unify with student upload code - should just be a function
    form = forms.UploadSubmissionForm()
    if form.validate_on_submit():
        files = request.files.getlist("upload_files")
        if files:
            templates = assign.files
            messages = {'file_contents': {}}
            for upload in files:
                data = upload.read()
                if len(data) > 2097152:
                    # File is too large (over 2 MB)
                    flash(("{} is over the maximum file size limit of 2MB"
                           .format(upload.filename)),
                          'danger')
                    return redirect(result_page)
                messages['file_contents'][upload.filename] = str(data, 'latin1')
            if templates:
                missing = []
                for template in templates:
                    if template not in messages['file_contents']:
                        missing.append(template)
                if missing:
                    flash(("Missing files: {}. The following files are required: {}"
                           .format(', '.join(missing), ', '.join([t for t in templates]))
                           ), 'danger')
                    return redirect(result_page)
            # use student, not current_user
            backup = ok_api.make_backup(student, assign.id, messages, True)
            if form.flag_submission.data:
                assign.flag(backup.id, user_ids)
            if assign.autograding_key:
                try:
                    submit_continous(backup)
                except ValueError as e:
                    flash('Did not send to autograder: {}'.format(e), 'warning')

            flash("Uploaded submission (ID: {})".format(backup.hashid), 'success')
            return redirect(result_page)
Ejemplo n.º 22
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)
Ejemplo n.º 23
0
def export_scores(cid, aid):
    courses, current_course = get_courses(cid)
    assign = Assignment.query.filter_by(id=aid, course_id=cid).one_or_none()
    if not Assignment.can(assign, current_user, 'export'):
        flash('Insufficient permissions', 'error')
        return abort(401)
    query = (Score.query.options(db.joinedload('backup')).filter_by(
        assignment=assign, archived=False))

    custom_items = ('time', 'is_late', 'email', 'group')
    items = custom_items + Enrollment.export_items + Score.export_items

    def generate_csv():
        """ Generate csv export of scores for assignment.
        Num Queries: ~2N queries for N scores.
        """
        # Yield Column Info as first row
        yield ','.join(items) + '\n'
        for score in query:
            csv_file = StringIO()
            csv_writer = csv.DictWriter(csv_file, fieldnames=items)
            submitters = score.backup.enrollment_info()
            group = [s.user.email for s in submitters]
            time_str = utils.local_time(score.backup.created, current_course)
            for submitter in submitters:
                data = {
                    'email': submitter.user.email,
                    'time': time_str,
                    'is_late': score.backup.is_late,
                    'group': group
                }
                data.update(submitter.export)
                data.update(score.export)
                csv_writer.writerow(data)
            yield csv_file.getvalue()

    file_name = "{0}.csv".format(assign.name.replace('/', '-'))
    disposition = 'attachment; filename={0}'.format(file_name)

    # TODO: Remove. For local performance testing.
    # return render_template('staff/index.html', data=list(generate_csv()))
    return Response(stream_with_context(generate_csv()),
                    mimetype='text/csv',
                    headers={'Content-Disposition': disposition})
Ejemplo n.º 24
0
def export_assignment(assignment_id, anonymized):
    """ Generate a zip file of submissions from enrolled students.

    Final Submission: One submission per student/group
        Zip Strucutre: cal-cs61a../[email protected]@b.com/abc12d/hog.py
    Anonymized: Submission without identifying info
        Zip Strucutre: cal-cs61a../{hash}/hog.py
    """
    logger = jobs.get_job_logger()

    assignment = Assignment.query.get(assignment_id)
    requesting_user = jobs.get_current_job().user

    if not assignment:
        logger.warning("No assignment found")
        raise Exception("No Assignment")

    if not Assignment.can(assignment, requesting_user, "download"):
        raise Exception("{} does not have enough permission"
                        .format(requesting_user.email))
    if anonymized:
        logger.info("Starting anonymized submission export")
    else:
        logger.info("Starting final submission export")
    course = assignment.course
    with io.BytesIO() as bio:
        # Get a handle to the in-memory zip in append mode
        with zipfile.ZipFile(bio, "w", zipfile.ZIP_DEFLATED, False) as zf:
            zf.external_attr = 0o655 << 16
            export_loop(bio, zf, logger, assignment, anonymized)
            created_time = local_time(dt.datetime.now(), course, fmt='%m-%d-%I-%M-%p')
            zip_name = '{}_{}.zip'.format(assignment.name.replace('/', '-'), created_time)

        bio.seek(0)
        # Close zf handle to finish writing zipfile
        logger.info("Uploading...")
        upload = ExternalFile.upload(bio, user_id=requesting_user.id, name=zip_name,
                                     course_id=course.id,
                                     prefix='jobs/exports/{}/'.format(course.offering))

    logger.info("Saved as: {0}".format(upload.object_name))
    msg = "/files/{0}".format(encode_id(upload.id))
    return msg
Ejemplo n.º 25
0
Archivo: admin.py Proyecto: gratimax/ok
def new_assignment(cid):
    courses, current_course = get_courses(cid)
    if not Assignment.can(None, current_user, 'create'):
        flash('Insufficient permissions', 'error')
        return abort(401)

    form = forms.AssignmentForm(course=current_course)
    if form.validate_on_submit():
        model = Assignment(course_id=cid, creator_id=current_user.id)
        form.populate_obj(model)
        db.session.add(model)
        db.session.commit()
        cache.delete_memoized(Assignment.name_to_assign_info)

        flash("Assignment created successfully.", "success")
        return redirect(url_for(".course_assignments", cid=cid))

    return render_template('staff/course/assignment.new.html', form=form,
                           courses=courses, current_course=current_course)
Ejemplo n.º 26
0
Archivo: admin.py Proyecto: gratimax/ok
def assignment_stats(cid, aid):
    courses, current_course = get_courses(cid)
    assign = Assignment.query.filter_by(id=aid, course_id=cid).one_or_none()
    if not Assignment.can(assign, current_user, 'edit'):
        flash('Insufficient permissions', 'error')
        return abort(401)
    stats = Assignment.assignment_stats(assign.id, detailed=True)

    pie_chart = pygal.Pie(half_pie=True, disable_xml_declaration=True,
                          style=CleanStyle,
                          inner_radius=.5, legend_at_bottom=True)
    pie_chart.title = 'Students submission status'
    pie_chart.add('Students with Submissions', stats['students_submitted'])
    pie_chart.add('Not Submitted', stats['students_nosubmit'])

    return render_template('staff/course/assignment.stats.html',
                           assignment=assign, subm_chart=pie_chart,
                           courses=courses, stats=stats,
                           current_course=current_course)
Ejemplo n.º 27
0
Archivo: admin.py Proyecto: 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)
Ejemplo n.º 28
0
Archivo: admin.py Proyecto: gratimax/ok
def export_scores(cid, aid):
    courses, current_course = get_courses(cid)
    assign = Assignment.query.filter_by(id=aid, course_id=cid).one_or_none()
    if not Assignment.can(assign, current_user, 'export'):
        flash('Insufficient permissions', 'error')
        return abort(401)
    query = (Score.query.options(db.joinedload('backup'))
                  .filter_by(assignment=assign, archived=False))

    custom_items = ('time', 'is_late', 'email', 'group')
    items = custom_items + Enrollment.export_items + Score.export_items

    def generate_csv():
        """ Generate csv export of scores for assignment.
        Num Queries: ~2N queries for N scores.
        """
        # Yield Column Info as first row
        yield ','.join(items) + '\n'
        for score in query:
            csv_file = StringIO()
            csv_writer = csv.DictWriter(csv_file, fieldnames=items)
            submitters = score.backup.enrollment_info()
            group = [s.user.email for s in submitters]
            time_str = utils.local_time(score.backup.created, course)
            for submitter in submitters:
                data = {'email': submitter.user.email,
                        'time': time_str,
                        'is_late': score.backup.is_late,
                        'group': group}
                data.update(submitter.export)
                data.update(score.export)
                csv_writer.writerow(data)
            yield csv_file.getvalue()

    file_name = "{0}.csv".format(assign.name.replace('/', '-'))
    disposition = 'attachment; filename={0}'.format(file_name)

    # TODO: Remove. For local performance testing.
    # return render_template('staff/index.html', data=list(generate_csv()))
    return Response(stream_with_context(generate_csv()), mimetype='text/csv',
                    headers={'Content-Disposition': disposition})
Ejemplo n.º 29
0
def publish_scores(cid, aid):
    courses, current_course = get_courses(cid)
    assign = Assignment.query.filter_by(id=aid, course_id=cid).one_or_none()
    if not Assignment.can(assign, current_user, 'publish_scores'):
        flash('Insufficient permissions', 'error')
        abort(401)

    form = forms.PublishScores(obj=assign)
    if form.validate_on_submit():
        assign.published_scores = form.published_scores.data
        db.session.commit()
        flash(
            "Saved published scores for {}".format(assign.display_name),
            "success",
        )
        return redirect(url_for('.publish_scores', cid=cid, aid=aid))
    return render_template('staff/course/assignment/assignment.publish.html',
                           assignment=assign,
                           form=form,
                           courses=courses,
                           current_course=current_course)
Ejemplo n.º 30
0
def assignment_timeline(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'):
        flash('Cannot access assignment', 'error')
        return abort(404)

    student = User.lookup(email)
    if not student.is_enrolled(cid):
        flash("This user is not enrolled", 'warning')

    stats = assign.user_timeline(student.id)

    return render_template('staff/student/assignment.timeline.html',
                           courses=courses,
                           current_course=current_course,
                           student=student,
                           assignment=assign,
                           submitters=stats['submitters'],
                           timeline=stats['timeline'])
Ejemplo n.º 31
0
def new_assignment(cid):
    courses, current_course = get_courses(cid)
    if not Assignment.can(None, current_user, 'create'):
        flash('Insufficient permissions', 'error')
        return abort(401)

    form = forms.AssignmentForm(course=current_course)
    if form.validate_on_submit():
        model = Assignment(course_id=cid, creator_id=current_user.id)
        form.populate_obj(model)
        db.session.add(model)
        db.session.commit()
        cache.delete_memoized(Assignment.name_to_assign_info)

        flash("Assignment created successfully.", "success")
        if form.visible.data:
            return redirect(url_for(".templates", cid=cid, aid=model.id))
        return redirect(url_for(".course_assignments", cid=cid))

    return render_template('staff/course/assignment/assignment.new.html', form=form,
                           courses=courses, current_course=current_course)
Ejemplo 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,
    )
Ejemplo n.º 33
0
Archivo: admin.py Proyecto: gratimax/ok
def assignment(cid, aid):
    courses, current_course = get_courses(cid)
    assign = Assignment.query.filter_by(id=aid, course_id=cid).one_or_none()
    if not assign:
        return abort(404)
    if not Assignment.can(assign, current_user, 'edit'):
        flash('Insufficient permissions', 'error')
        return abort(401)

    form = forms.AssignmentUpdateForm(obj=assign, course=current_course)
    stats = Assignment.assignment_stats(assign.id)

    if form.validate_on_submit():
        # populate_obj converts back to UTC
        form.populate_obj(assign)
        assign.creator_id = current_user.id
        cache.delete_memoized(Assignment.name_to_assign_info)
        db.session.commit()
        flash("Assignment edited successfully.", "success")

    return render_template('staff/course/assignment.html', assignment=assign,
                           form=form, courses=courses, stats=stats,
                           current_course=current_course)
Ejemplo n.º 34
0
def assignment(cid, aid):
    courses, current_course = get_courses(cid)
    assign = Assignment.query.filter_by(id=aid, course_id=cid).one_or_none()
    if not assign:
        return abort(404)
    if not Assignment.can(assign, current_user, 'edit'):
        flash('Insufficient permissions', 'error')
        return abort(401)

    form = forms.AssignmentUpdateForm(obj=assign, course=current_course)
    stats = Assignment.assignment_stats(assign.id)

    if form.validate_on_submit():
        # populate_obj converts back to UTC
        form.populate_obj(assign)
        assign.creator_id = current_user.id
        cache.delete_memoized(Assignment.name_to_assign_info)
        db.session.commit()
        flash("Assignment edited successfully.", "success")

    return render_template('staff/course/assignment/assignment.html', assignment=assign,
                           form=form, courses=courses, stats=stats,
                           current_course=current_course)
Ejemplo n.º 35
0
Archivo: admin.py Proyecto: gratimax/ok
def staff_group_remove(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)

    student = User.lookup(email)
    if not student:
        abort(404)

    result_page = url_for('.student_assignment_detail', cid=cid,
                          email=email, aid=aid)

    form = forms.CSRFForm()
    if form.validate_on_submit():
        target = User.lookup(request.form['target'])
        if not target:
            flash('{} does not exist'.format(request.form['target']), 'error')
            return redirect(result_page)
        try:
            Group.force_remove(current_user, student, target, assign)
        except BadRequest as e:
            flash("Error: {}".format(str(e.description)), 'error')
    return redirect(result_page)