コード例 #1
0
ファイル: ranking.py プロジェクト: pombredanne/crowdranker
def interpolate_grades():
    """This function interpolates the specified final grades."""
    c = db.venue(request.args(0)) or redirect(URL('default', 'index'))
    props = db(db.user_properties.user == auth.user.email).select().first()
    if not access.can_manage(c, props):
	session.flash = T('Not authorized')
	redirect(URL('ranking', 'view_final_grades', args=[c.id]))
    grades = db(db.grades.venue_id == c.id).select(db.grades.id, db.grades.assigned_grade, orderby=db.grades.percentile).as_list()
    if len(grades) == 0:
	return
    # Fixes the lower end.
    if grades[0]['assigned_grade'] is None:
	last_assigned_grade = 0.0
	db(db.grades.id == grades[0]['id']).update(assigned_grade = 0.0)
    else:
	last_assigned_grade = grades[0]['assigned_grade']
    last_assigned_idx = 0
    # Interpolates the rest.
    for i, g in enumerate(grades):
	grade = g['assigned_grade']
	if grade is not None:
	    # Interpolates from previous to this one.
	    for k in range(last_assigned_idx + 1, i):
		new_grade = last_assigned_grade + (grade - last_assigned_grade) * (k - last_assigned_idx) / (i - last_assigned_idx)
		db(db.grades.id == grades[k]['id']).update(assigned_grade = new_grade)
	    last_assigned_idx = i
	    last_assigned_grade = grade
    db.commit()
    session.flash = T('The grades have been interpolated.')
    redirect(URL('ranking', 'view_final_grades', args=[c.id]))
コード例 #2
0
def rank_by_grades():
    c = db.venue(request.args(0)) or redirect(URL('default', 'index'))
    props = db(db.user_properties.user == get_user_email()).select().first()
    if not access.can_manage(c, props):
        session.flash = T('You cannot evaluate contributors for this venue')
        redirect(URL('default', 'index'))
    ranker.rank_by_grades(c.id)
    db.commit()
    session.flash = T('Grades are computed')
    redirect(URL('venues', 'view_venue_research', args=[c.id]))
コード例 #3
0
ファイル: rating.py プロジェクト: erikaeriga/crowdranker
def assign_reviewers():
    c = db.venue(request.args(0)) or redirect('default', 'index')
    props = db(db.user_properties.email == auth.user.email).select().first()
    if props == None:
        session.flash = T('You cannot assign reviewers to this venue.')
        redirect(URL('default', 'index'))
    if not access.can_manage(c, props):
        session.flash = T('You cannot assign reviewers to this venue.')
        redirect(URL('default', 'index'))
    # These are the users that can be called upon reviewing the submissions.
    managed_user_lists = util.get_list(props.managed_user_lists)
    # Constrains the user lists to those managed by the user.
    list_q = (db.user_list.id.belongs(managed_user_lists))
    # The default is the list of users that can review the venue.
    if c.rate_constraint is None:
	form = SQLFORM.factory(
	    Field('users', requires = IS_IN_DB(db(list_q), 'user_list.id', '%(name)s')),
	    Field('number_of_reviews_per_user', 'integer'),
	    Field('incremental', 'boolean'),
	    )
    else:
	form = SQLFORM.factory(
	    Field('users', default = c.rate_constraint,
		  requires = IS_IN_DB(db(list_q), 'user_list.id', '%(name)s')),
	    Field('number_of_reviews_per_user', 'integer'),
	    Field('incremental', 'boolean'),
	    )	
    if form.process().accepted:
        if (util.is_none(form.vars.users) or form.vars.number_of_reviews_per_user == 0 
                or (form.vars.number_of_reviews_per_user < 0 and not form.vars.incremental)):
            session.flash = T('No reviewing duties added.')
            redirect(URL('venues', 'managed_index'))
	n = c.number_of_submissions_per_reviewer
	c.update_record(number_of_submissions_per_reviewer = n + form.vars.number_of_reviews_per_user)
        # TODO(luca): this should be implemented in terms of a job queue
        user_list = db.user_list(form.vars.users)
        for m in user_list.email_list:
            current_asgn = db((db.reviewing_duties.user_email == m) &
                (db.reviewing_duties.venue_id == c.id)).select().first()
            if current_asgn == None:
                # Creates a new one.
                db.reviewing_duties.insert(user_email = m, venue_id = c.id, 
                    num_reviews = form.vars.number_of_reviews_per_user)
            else:
                # Update an existing one
                if form.vars.incremental:
                    new_number = max(0, current_asgn.num_reviews + form.vars.number_of_reviews_per_user)
                else:
                    new_number = form.vars.number_of_reviews_per_user
                current_asgn.update_record(num_reviews = new_number)
        db.commit()
        session.flash = T('The reviewing duties have been assigned.')
        redirect(URL('venues', 'managed_index'))
    venue_form = SQLFORM(db.venue, record=c, readonly=True)
    return dict(venue=c, form=form, vform=venue_form)
コード例 #4
0
def reset_grades():
    """This function resets the final grades to None."""
    c = db.venue(request.args(0)) or redirect(URL('default', 'index'))
    props = db(db.user_properties.user == get_user_email()).select().first()
    if not access.can_manage(c, props):
        session.flash = T('Not authorized')
        redirect(URL('ranking', 'view_grades', args=[c.id]))
    db(db.grades.venue_id == c.id).update(assigned_grade=None)
    db.commit()
    session.flash = T('The grades have been cleared.')
    redirect(URL('ranking', 'view_grades', args=[c.id]))
コード例 #5
0
ファイル: ranking.py プロジェクト: lawrancej/crowdranker
def reset_grades():
    """This function resets the final grades to None."""
    c = db.venue(request.args(0)) or redirect(URL('default', 'index'))
    props = db(db.user_properties.user == get_user_email()).select().first()
    if not access.can_manage(c, props):
        session.flash = T('Not authorized')
        redirect(URL('ranking', 'view_grades', args=[c.id]))
    db(db.grades.venue_id == c.id).update(assigned_grade = None)
    db.commit()
    session.flash = T('The grades have been cleared.')
    redirect(URL('ranking', 'view_grades', args=[c.id]))
コード例 #6
0
def release_grades():
    c = db.venue(request.args(0)) or redirect(URL('default', 'index'))
    visible = (request.args(1) == 'True')
    props = db(db.user_properties.user == get_user_email()).select().first()
    if not access.can_manage(c, props):
        session.flash = T('Not authorized')
        redirect(URL('ranking', 'view_grades', args=[c.id]))
    db(db.venue.id == c.id).update(grades_released=visible)
    db.commit()
    if visible:
        session.flash = T('The grades are now visible to students.')
    else:
        session.flash = T('The grades are no longer visible to students.')
    redirect(URL('ranking', 'view_grades', args=[c.id]))
コード例 #7
0
ファイル: ranking.py プロジェクト: lawrancej/crowdranker
def release_grades():
    c = db.venue(request.args(0)) or redirect(URL('default', 'index'))
    visible = (request.args(1) == 'True')
    props = db(db.user_properties.user == get_user_email()).select().first()
    if not access.can_manage(c, props):
        session.flash = T('Not authorized')
        redirect(URL('ranking', 'view_grades', args=[c.id]))
    db(db.venue.id == c.id).update(grades_released = visible)
    db.commit()
    if visible:
        session.flash = T('The grades are now visible to students.')
    else:
        session.flash = T('The grades are no longer visible to students.')
    redirect(URL('ranking', 'view_grades', args=[c.id]))
コード例 #8
0
ファイル: ranking.py プロジェクト: pombredanne/crowdranker
def view_final_grades():
    """This function shows the final grade of each user.
    """
    c = db.venue(request.args(0)) or redirect(URL('default', 'index'))
    props = db(db.user_properties.user == auth.user.email).select().first()
    if not access.can_view_ratings(c, props):
	session.flash = T('You do not have access to the final grades for this venue.')
	redirect(URL('venues', 'view_venue', args=[c.id]))
    # Checking that final grades are recent and don't need recomputation.
    venue_row = db(db.venue.id == c.id).select().first()
    grades_date = venue_row.latest_grades_date
    if grades_date is None:
        session.flash = T('Grades are not updated/computed. Please compute final grades')
        redirect(URL('venues', 'view_venue', args=[c.id]))
    # Okay, final grades are computed and are updated.
    # Prepares the query for the grid.
    if is_user_admin():
	db.grades.reputation.readable = True
    db.grades.user.represent = represent_user_by_submission_feedback
    db.grades.venue_id.readable = False
    db.grades.submission_percentile.label = T('Submission')
    db.grades.percentile.label = T('Overall')
    link_list = []
    if access.can_manage(c, props):
	db.grades.assigned_grade.writable = True
	db.grades.assigned_grade.comment = T('Assign the desired grade to a few users, '
					     'then automatically fill-in the remaining '
	                                     'grades via interpolation. ')
	is_editable = True
	link_list.append(A(T('Interpolate final grades'), 
			   _href=URL('ranking', 'interpolate_grades', args=[c.id], user_signature=True)))
	link_list.append(A(T('Clear final grades'), 
			   _href=URL('ranking', 'reset_grades', args=[c.id], user_signature=True)))
    else:
	db.grades.assigned_grade.writable = False
	is_editable = False
    q = (db.grades.venue_id == c.id)
    grid = SQLFORM.grid(q,
	args=request.args[:1],
	user_signature=False, details=False,
	create=False, editable=is_editable, deletable=False,
	fields=[db.grades.user, db.grades.submission_percentile, db.grades.venue_id,
		db.grades.n_ratings, db.grades.accuracy,
		db.grades.reputation, db.grades.percentile, db.grades.assigned_grade ],
	)
    title = A(c.name, _href=URL('venues', 'view_venue', args=[c.id]))
    return dict(grid=grid, title=title, link_list=link_list)
コード例 #9
0
ファイル: venues.py プロジェクト: lawrancej/crowdranker
def view_venue():
    c = db.venue(request.args(0)) or redirect(URL('default', 'index'))
    props = db(db.user_properties.user == get_user_email()).select().first()
    # if c.raters_equal_submitters:
    db.venue.rate_constraint.readable = False
    if props == None and not is_user_admin(): 
        session.flash = T('Not Authorized.')
        redirect(URL('default', 'index'))
    # Checks view permission
    can_submit = access.can_submit(c, props)
    can_rate = access.can_rate(c, props)
    has_submitted = access.has_submitted(c, props)
    has_rated = access.has_rated(c, props)
    can_manage = access.can_manage(c, props)
    can_observe = access.can_observe(c, props)
    can_view_ratings = access.can_view_ratings(c, props)
    # Builds some links that are useful to give out to people.
    submission_link = URL('submission', 'submit', args=[c.id])
    review_link = URL('venues', 'reviewing_duties')
    if not (can_submit or can_rate or has_submitted or has_rated or can_manage or can_observe or can_view_ratings):
        session.flash = T('Not authorized.')
        redirect(URL('default', 'index'))
    if can_observe:
        db.venue.grading_instructions.readable = True
    venue_form = SQLFORM(db.venue, record=c, readonly=True)
    link_list = []
    if can_manage:
        link_list.append(A(T('Edit'), _href=URL('venues', 'edit', args=[c.id], user_signature=True)))
    if can_submit:
        link_list.append(A(T('Submit to this venue'), _href=URL('submission', 'submit', args=[c.id])))
    if can_manage:
        link_list.append(A(T('Add submission'), _href=URL('submission', 'manager_submit', args=[c.id])))
    if has_submitted:
        link_list.append(A(T('My submissions'), _href=URL('feedback', 'index', args=[c.id])))
    if can_view_ratings or access.can_view_submissions(c, props):
        link_list.append(A(T('Submissions'), _href=URL('ranking', 'view_submissions', args=[c.id])))
    if can_rate and not can_manage:
        link_list.append(A(T('Review'), _href=URL('rating', 'accept_review', args=[c.id], user_signature=True)))
    if can_observe or can_manage:
        link_list.append(A(T('Comparisons'), _href=URL('ranking', 'view_comparisons_index', args=[c.id])))
    if can_view_ratings:
        link_list.append(A(T('Crowd-grades'), _href=URL('ranking', 'view_grades', args=[c.id])))
    if is_user_admin():
        link_list.append(A(T('Experimental grades'), _href=URL('ranking', 'view_exp_grades', args=[c.id])))
    return dict(form=venue_form, link_list=link_list, venue=c, has_rated=has_rated,
                submission_link=submission_link, review_link=review_link)
コード例 #10
0
def interpolate_grades():
    """This function interpolates the specified final grades."""
    c = db.venue(request.args(0)) or redirect(URL('default', 'index'))
    props = db(db.user_properties.user == get_user_email()).select().first()
    if not access.can_manage(c, props):
        session.flash = T('Not authorized')
        redirect(URL('ranking', 'view_grades', args=[c.id]))
    grades = db(db.grades.venue_id == c.id).select(
        db.grades.id,
        db.grades.grade,
        db.grades.assigned_grade,
        orderby=db.grades.percentile).as_list()
    if len(grades) == 0:
        return
    # Fixes the lower end.
    last_assigned_idx = 0
    last_assigned_crowd_grade = util.get_or_0(grades[0], 'grade')
    if grades[0]['assigned_grade'] is None:
        last_assigned_grade = 0.0
        db(db.grades.id == grades[0]['id']).update(assigned_grade=0.0)
    else:
        last_assigned_grade = grades[0]['assigned_grade']
    # Interpolates the rest.
    for i, g in enumerate(grades):
        assigned_grade = g['assigned_grade']
        if assigned_grade is not None:
            # Interpolates from previous to this one.
            end_crowd_grade = util.get_or_0(g, 'grade')
            for k in range(last_assigned_idx + 1, i):
                crowd_grade = util.get_or_0(grades[k], 'grade')
                if end_crowd_grade == last_assigned_crowd_grade:
                    new_grade = end_crowd_grade
                else:
                    new_grade = (last_assigned_grade +
                                 (assigned_grade - last_assigned_grade) *
                                 (crowd_grade - last_assigned_crowd_grade) /
                                 (end_crowd_grade - last_assigned_crowd_grade))
                db(db.grades.id == grades[k]['id']).update(
                    assigned_grade=new_grade)
            last_assigned_idx = i
            last_assigned_grade = assigned_grade
            last_assigned_crowd_grade = end_crowd_grade
    db.commit()
    session.flash = T('The grades have been interpolated.')
    redirect(URL('ranking', 'view_grades', args=[c.id]))
コード例 #11
0
ファイル: rating.py プロジェクト: lawrancej/crowdranker
def crowd_grade():
    # Gets the information on the venue.
    c = db.venue(request.args(0)) or redirect(URL('default', 'index'))
    props = db(db.user_properties.user == get_user_email()).select().first()
    if not access.can_manage(c, props):
        session.flash = T('You cannot evaluate contributors for this venue')
        redirect(URL('default', 'index'))
    if is_user_admin():
        form = SQLFORM.factory(
            Field('algo', default=ALGO_DEFAULT, requires=IS_IN_SET(ALGO_LIST)),
            Field('run_id', default='exp'),
            Field('cost_type', default=ALGO_DEFAULT_COST_TYPE, requires=IS_IN_SET(['linear', 'quadratic'])),
            Field('pos_slope', 'double', default=ALGO_DEFAULT_POS_SLOPE, requires=IS_FLOAT_IN_RANGE(0.0, 1000.0)),
            Field('neg_slope', 'double', default=ALGO_DEFAULT_NEG_SLOPE, requires=IS_FLOAT_IN_RANGE(0.0, 1000.0)),
            Field('normalize_grades', 'boolean', default=ALGO_DEFAULT_NORMALIZE),
            Field('normalization_scale', 'double', default=ALGO_DEFAULT_NORMALIZATION_SCALE, requires=IS_FLOAT_IN_RANGE(0.01, 1000.0)),
            Field('reputation_method', default=ALGO_DEFAULT_REPUTATION_METHOD, requires=IS_IN_SET([ALGO_DEFAULT_REPUTATION_METHOD, 'stdev'])),
            Field('precision_coefficient', 'double', default=ALGO_DEFAULT_PREC_COEFF, requires=IS_FLOAT_IN_RANGE(0.0, 1000.0)),
            Field('use_submission_rank_in_reputation', 'boolean', default=True),
            Field('submission_rank_exponent_for_reputation', 'double', default=ALGO_DEFAULT_RANK_REP_EXP, requires=IS_FLOAT_IN_RANGE(0.1, 10.0)),
            Field('precision_method', default=ALGO_DEFAULT_PREC_METHOD, requires=IS_IN_SET([ALGO_PREC_METHOD_DIST, ALGO_PREC_METHOD_CORR])),
            Field('matrix_D_type', default=MATRIX_D_TYPE_GRADES_DIST, requires=IS_IN_SET([MATRIX_D_TYPE_GRADES_DIST, MATRIX_D_TYPE_GRADES_SINGLE])),
            Field('do_debias', 'boolean', default=ALGO_DEFAULT_DO_DEBIAS),
            Field('use_median', 'boolean', default=ALGO_DEFAULT_USE_MEDIAN),
            Field('use_reputation', 'boolean', default=ALGO_DEFAULT_USE_REPUTATION),
            Field('num_iterations', 'integer', default=ALGO_DEFAULT_NUM_ITERATIONS, requires=IS_INT_IN_RANGE(1, 20)),
            Field('publish', 'boolean', default=False, writable=access.is_real_manager(c, props)),
            )
    else:
        form = FORM.confirm(T('Run'),
            {T('Cancel'): URL('venues', 'view_venue', args=[c.id])})
    # Notice that the confirm form does not need and must not call
    # .accepts or .process because this is done internally (from web2py book).
    if ((is_user_admin() and form.process().accepted) or 
       (not is_user_admin() and form.accepted)):
        if is_user_admin():
            algo = form.vars.algo
            run_id = form.vars.run_id
            cost_type = form.vars.cost_type
            pos_slope = form.vars.pos_slope
            neg_slope = form.vars.neg_slope
            normalize_grades = form.vars.normalize_grades
            normalization_scale = form.vars.normalization_scale
            reputation_method = form.vars.reputation_method
            precision_coefficient = form.vars.precision_coefficient
            use_submission_rank_in_reputation = form.vars.use_submission_rank_in_reputation
            submission_rank_exp = form.vars.submission_rank_exponent_for_reputation
            precision_method = form.vars.precision_method
            num_iterations = form.vars.num_iterations
            do_debias = form.vars.do_debias
            use_median = form.vars.use_median
            use_reputation = form.vars.use_reputation
            publish = form.vars.publish
            matrix_D_type = form.vars.matrix_D_type
        else:            
            algo = ALGO_OPT
            run_id = 'default'
            cost_type = ALGO_DEFAULT_COST_TYPE
            pos_slope = ALGO_DEFAULT_POS_SLOPE
            neg_slope = ALGO_DEFAULT_NEG_SLOPE
            normalize_grades = ALGO_DEFAULT_NORMALIZE
            normalization_scale = ALGO_DEFAULT_NORMALIZATION_SCALE
            reputation_method = ALGO_DEFAULT_REPUTATION_METHOD
            precision_coefficient = ALGO_DEFAULT_PREC_COEFF
            use_submission_rank_in_reputation = True
            submission_rank_exp = ALGO_DEFAULT_RANK_REP_EXP
            precision_method = ALGO_DEFAULT_PREC_METHOD
            num_iterations = ALGO_DEFAULT_NUM_ITERATIONS
            do_debias = ALGO_DEFAULT_DO_DEBIAS
            use_median = ALGO_DEFAULT_USE_MEDIAN
            use_reputation = ALGO_DEFAULT_USE_REPUTATION
            matrix_D_type = MATRIX_D_TYPE_GRADES_DIST
            publish = True
        # Performs the computation.
        return redirect(URL('queues', 'run_rep_sys', vars={
                    REPUTATION_SYSTEM_PARAM_VENUE_ID: c.id,
                    REPUTATION_SYSTEM_ALGO: algo,
                    REPUTATION_SYSTEM_RUN_ID: run_id,
                    REPUTATION_SYSTEM_COST_TYPE: cost_type,
                    REPUTATION_SYSTEM_POS_SLOPE: pos_slope,
                    REPUTATION_SYSTEM_NEG_SLOPE: neg_slope,
                    REPUTATION_SYSTEM_NORMALIZE_GRADES: normalize_grades,
                    REPUTATION_SYSTEM_NORMALIZATION_SCALE: normalization_scale,
                    REPUTATION_SYSTEM_REPUTATION_METHOD: reputation_method,
                    REPUTATION_SYSTEM_PREC_COEFF: precision_coefficient,
                    REPUTATION_SYSTEM_PARAM_REVIEW_PERCENTAGE: c.reviews_as_percentage_of_grade,
                    REPUTATION_SYSTEM_USE_SUBMISSION_RANK_IN_REP: use_submission_rank_in_reputation,
                    REPUTATION_SYSTEM_SUBMISSION_RANK_REP_EXP: submission_rank_exp,
                    REPUTATION_SYSTEM_PREC_METHOD: precision_method,
                    REPUTATION_SYSTEM_PARAM_NUM_ITERATIONS: num_iterations,
                    REPUTATION_SYSTEM_STARTOVER: 'True',
                    REPUTATION_SYSTEM_DO_DEBIAS: do_debias,
                    REPUTATION_SYSTEM_USE_MEDIAN: use_median,
                    REPUTATION_SYSTEM_USE_REPUTATION: use_reputation,
                    REPUTATION_SYSTEM_PUBLISH: publish,
                    REPUTATION_SYSTEM_MATRIX_D_TYPE: matrix_D_type,
                    },
                    user_signature=True))
    venue_link = A(c.name, _href=URL('venues', 'view_venue', args=[c.id]))
    return dict(venue_link=venue_link, confirmation_form=form)
コード例 #12
0
ファイル: submission.py プロジェクト: lawrancej/crowdranker
def manager_submit():
    """This function is used by venue managers to do submissions on behalf of others.  It can be used
    even when the submission deadline is past."""
    # Gets the information on the venue.
    c = db.venue(request.args(0)) or redirect(URL('default', 'index'))
    # Checks that the user is a manager for the venue.
    manager_props = db(db.user_properties.user == get_user_email()).select().first()
    if not access.can_manage(c, manager_props):
        session.flash = T('Not authorized!')
        redirect(URL('default', 'index'))
    # Prepares the submission.
    db.submission.user.readable = db.submission.user.writable = True
    db.submission.user.default = ''
    db.submission.feedback.readable = db.submission.feedback.writable = False
    # Assigns default quality to the submission.
    avg, stdev = ranker.get_init_average_stdev()
    db.submission.quality.default = avg
    db.submission.error.default = stdev
    db.submission.percentile.readable = False
    db.submission.n_assigned_reviews.readable = False
    db.submission.n_completed_reviews.readable = False
    db.submission.n_rejected_reviews.readable = False
    # Check whether link submission is allowed.
    db.submission.link.readable = db.submission.link.writable = c.allow_link_submission
    # Check whether attachment submission is allowed.
    db.submission.content.readable = db.submission.content.writable = c.allow_file_upload

    # Prepares the submission form.
    form = SQLFORM(db.submission, upload=URL('download_manager', args=[None]))
    form.vars.venue_id = c.id
    form.vars.date_updated = datetime.utcnow()
    if request.vars.content != None and request.vars.content != '':
        form.vars.original_filename = request.vars.content.filename
    if form.process(onvalidation=write_comment_to_keystore).accepted:
        # Adds the venue to the list of venues where the user submitted.
        props = db(db.user_properties.user == form.vars.email).select().first()
        if props == None: 
            venues_has_submitted = []
        else:
            venues_has_submitted = util.get_list(props.venues_has_submitted)
        submitted_ids = util.id_list(venues_has_submitted)
        submitted_ids = util.list_append_unique(submitted_ids, c.id)
        if props == None:
            db(db.user_properties.user == form.vars.user).update(venues_has_submitted=submitted_ids)
        else:
            props.update_record(venues_has_submitted=submitted_ids)

        # If there is a prior submission of the same author to this venue, replaces the content.
        is_there_another = False
        other_subms = db((db.submission.user == form.vars.user) & 
                         (db.submission.venue_id == c.id)).select()
        for other_subm in other_subms:
            if other_subm.id != form.vars.id:
                is_there_another = True
                keystore_delete(other_subm.comment)
                other_subm.update_record(
                    date_updated = datetime.utcnow(),
                    original_filename = form.vars.original_filename,
                    content = form.vars.content,
                    link = form.vars.link,
                    comment = form.vars.comment,
                    n_assigned_reviews = 0,
                    n_completed_reviews = 0,
                    n_rejected_reviews = 0,
                    )
        # And deletes this submission.
        if is_there_another:
            db(db.submission.id == form.vars.id).delete()
            session.flash = T('The previous submission by the same author has been updated.')
        else:
            session.flash = T('The submission has been added.')
        db.commit()
        redirect(URL('ranking', 'view_submissions', args=[c.id]))
    instructions = keystore_read(c.submission_instructions, default='')
    if instructions == '':
        instructions = None
    else:
        instructions = MARKMIN(instructions)
    return dict(form=form, venue=c, instructions=instructions)
コード例 #13
0
def view_grades():
    """This function shows the final grade of each user.
    It takes as single argument the venue id.
    """

    # This function is used to get experimental grades from the db.
    def get_grade_fn(venue_id, run_id):
        def f(row):
            row = db((db.grades_exp.venue_id == venue_id)
                     & (db.grades_exp.user == row.user)
                     & (db.grades_exp.run_id == run_id)).select().first()
            if row is None:
                return 'None'
            # Generates a string summary.
            s = "subm_grade: %r subm_confidence: %r rev: %r rep: %r tot: %r" % (
                short_float_or_None(
                    row.subm_grade), short_float_or_None(row.subm_confidence),
                short_float_or_None(
                    row.review_grade), short_float_or_None(
                        row.reputation), short_float_or_None(row.grade))
            return s

        return f

    # Main function.
    c = db.venue(request.args(0)) or redirect(URL('default', 'index'))
    props = db(db.user_properties.user == get_user_email()).select().first()
    if not access.can_view_ratings(c, props):
        session.flash = T(
            'You do not have access to the final grades for this venue.')
        redirect(URL('venues', 'view_venue', args=[c.id]))
    # Checking that final grades are recent and don't need recomputation.
    venue_row = db(db.venue.id == c.id).select().first()
    grades_date = venue_row.latest_grades_date
    if grades_date is None:
        session.flash = T('The crowd-grades have not been computed yet.')
        redirect(URL('rating', 'crowd_grade', args=[c.id]))
    # The crowd-grades have been computed already.
    if is_user_admin():
        db.grades.reputation.readable = True
    db.grades.user.represent = represent_user_by_submission_feedback
    db.grades.venue_id.readable = False
    # Prepares the buttons at the top.
    link_list = []
    if access.can_manage(c, props):
        db.grades.assigned_grade.writable = True
        db.grades.assigned_grade.comment = T(
            'Assign the desired grade to a few users, '
            'then automatically fill-in the remaining '
            'grades via interpolation. ')
        is_editable = True
        link_list.append(
            A(T('Recompute crowd-grades'),
              _href=URL('rating', 'crowd_grade', args=[c.id])))
        link_list.append(
            A(T('Interpolate final grades'),
              _href=URL('ranking',
                        'interpolate_grades',
                        args=[c.id],
                        user_signature=True)))
        link_list.append(
            A(T('Clear final grades'),
              _href=URL('ranking',
                        'reset_grades',
                        args=[c.id],
                        user_signature=True)))

        # Creates button to release / withdraw grades.
        if c.grades_released:
            link_list.append(
                A(T('Hide grades from students'),
                  _href=URL('ranking',
                            'release_grades',
                            args=[c.id, 'False'],
                            user_signature=True)))
        else:
            link_list.append(
                A(T('Show grades to students'),
                  _href=URL('ranking',
                            'release_grades',
                            args=[c.id, 'True'],
                            user_signature=True)))
    else:
        db.grades.assigned_grade.writable = False
        is_editable = False
    # If one is the manager, and we are viewing experimental grades, offers the option
    # to download a spreadsheet including the experimental grades.
    if is_user_admin():
        link_list.append(
            A(T('View experimental runs'),
              _href=URL('ranking', 'view_exp_grades', args=[c.id])))
    if is_user_admin() and request.vars.run_ids is not None:
        link_list.append(
            A(T('Download research data'),
              _href=URL('research',
                        'download_research_data.csv',
                        args=[c.id],
                        vars=dict(run_ids=request.vars.run_ids),
                        user_signature=True)))
    if is_user_admin():
        link_list.append(
            A(T('Evaluate grades'),
              _href=URL('research',
                        'evaluate_grades',
                        args=[c.id],
                        user_signature=True)))
        link_list.append(
            A(T('Rerun evaluations'),
              _href=URL('research',
                        'rerun_evaluations',
                        args=[c.id],
                        user_signature=True)))
    # Chooses the display fields.
    display_fields = [
        db.grades.user, db.grades.venue_id, db.grades.submission_grade,
        db.grades.submission_percentile, db.grades.accuracy,
        db.grades.n_ratings, db.grades.grade, db.grades.percentile,
        db.grades.assigned_grade
    ]
    if is_user_admin():
        display_fields.append(db.grades.reputation)
        display_fields.append(db.grades.submission_control_grade)
        db.grades.submission_control_grade.readable = True
    # Adds columns for any extra grade we wish to see.
    grid_links = []
    if is_user_admin() and request.vars.run_ids is not None:
        run_ids = request.vars.run_ids.split(',')
        for r in run_ids:
            grid_links.append(dict(header=r, body=get_grade_fn(c.id, r)))
    if is_user_admin():
        # Adds a column for the true grade.
        grid_links.append(
            dict(header='',
                 body=lambda row: A(T('Enter control grade'),
                                    _class='btn',
                                    _href=URL('ranking',
                                              'edit_control_grade',
                                              args=[c.id, row.user],
                                              user_signature=True))))
    # Prepares the grid.
    q = (db.grades.venue_id == c.id)
    grid = SQLFORM.grid(
        q,
        fields=display_fields,
        args=request.args[:1],
        user_signature=False,
        details=False,
        create=False,
        editable=is_editable,
        deletable=False,
        links=grid_links,
        maxtextlength=24,
    )
    title = A(c.name, _href=URL('venues', 'view_venue', args=[c.id]))
    grades_date_info = represent_date(c.latest_grades_date, c)
    if c.grades_released:
        grades_visibility = T('Grades are visible to students')
    else:
        grades_visibility = T('Grades are not visible to students')
    return dict(grid=grid,
                title=title,
                link_list=link_list,
                grades_date_info=grades_date_info,
                grades_visibility=grades_visibility)
コード例 #14
0
def manager_submit():
    """This function is used by venue managers to do submissions on behalf of others.  It can be used
    even when the submission deadline is past."""
    # Gets the information on the venue.
    c = db.venue(request.args(0)) or redirect(URL('default', 'index'))
    # Checks that the user is a manager for the venue.
    manager_props = db(
        db.user_properties.user == get_user_email()).select().first()
    if not access.can_manage(c, manager_props):
        session.flash = T('Not authorized!')
        redirect(URL('default', 'index'))
    # Prepares the submission.
    db.submission.user.readable = db.submission.user.writable = True
    db.submission.user.default = ''
    db.submission.feedback.readable = db.submission.feedback.writable = False
    # Assigns default quality to the submission.
    avg, stdev = ranker.get_init_average_stdev()
    db.submission.quality.default = avg
    db.submission.error.default = stdev
    db.submission.percentile.readable = False
    db.submission.n_assigned_reviews.readable = False
    db.submission.n_completed_reviews.readable = False
    db.submission.n_rejected_reviews.readable = False
    # Check whether link submission is allowed.
    db.submission.link.readable = db.submission.link.writable = c.allow_link_submission
    # Check whether attachment submission is allowed.
    db.submission.content.readable = db.submission.content.writable = c.allow_file_upload

    # Prepares the submission form.
    form = SQLFORM(db.submission, upload=URL('download_manager', args=[None]))
    form.vars.venue_id = c.id
    form.vars.date_updated = datetime.utcnow()
    if request.vars.content != None and request.vars.content != '':
        form.vars.original_filename = request.vars.content.filename
    if form.process(onvalidation=write_comment_to_keystore).accepted:
        # Adds the venue to the list of venues where the user submitted.
        props = db(db.user_properties.user == form.vars.email).select().first()
        if props == None:
            venues_has_submitted = []
        else:
            venues_has_submitted = util.get_list(props.venues_has_submitted)
        submitted_ids = util.id_list(venues_has_submitted)
        submitted_ids = util.list_append_unique(submitted_ids, c.id)
        if props == None:
            db(db.user_properties.user == form.vars.user).update(
                venues_has_submitted=submitted_ids)
        else:
            props.update_record(venues_has_submitted=submitted_ids)

        # If there is a prior submission of the same author to this venue, replaces the content.
        is_there_another = False
        other_subms = db((db.submission.user == form.vars.user)
                         & (db.submission.venue_id == c.id)).select()
        for other_subm in other_subms:
            if other_subm.id != form.vars.id:
                is_there_another = True
                keystore_delete(other_subm.comment)
                other_subm.update_record(
                    date_updated=datetime.utcnow(),
                    original_filename=form.vars.original_filename,
                    content=form.vars.content,
                    link=form.vars.link,
                    comment=form.vars.comment,
                    n_assigned_reviews=0,
                    n_completed_reviews=0,
                    n_rejected_reviews=0,
                )
        # And deletes this submission.
        if is_there_another:
            db(db.submission.id == form.vars.id).delete()
            session.flash = T(
                'The previous submission by the same author has been updated.')
        else:
            session.flash = T('The submission has been added.')
        db.commit()
        redirect(URL('ranking', 'view_submissions', args=[c.id]))
    instructions = keystore_read(c.submission_instructions, default='')
    if instructions == '':
        instructions = None
    else:
        instructions = MARKMIN(instructions)
    return dict(form=form, venue=c, instructions=instructions)
コード例 #15
0
ファイル: venues.py プロジェクト: lawrancej/crowdranker
def edit():
    """Edits or creates a venue."""
    is_edit = (request.args(0) is not None)
    if is_edit:
        c = db.venue(request.args(0))
        if c is None:
            session.flash = T('No such assignment.')
            redirect(URL('venues', 'managed_index'))
    props = db(db.user_properties.user == get_user_email()).select().first()
    if props == None:
        managed_venue_list = []
        managed_user_lists = []
    else:
        managed_venue_list = util.get_list(props.venues_can_manage)
        managed_user_lists = util.get_list(props.managed_user_lists)
    if is_edit and not access.can_manage(c, props):
        session.flash = T('Not authorized.')
        redirect(URL('venues', 'managed_index'))
    is_real_manager = (not is_edit) or (c.id in managed_venue_list)
    logger.info("Is this real manager: %r" % is_real_manager)

    # Sets defaults
    set_homework_defaults(None)
    add_help_for_venue(None)
    if not is_edit:
        # Let's not bother someone who creates a venue with this.
        db.venue.grades_released.readable = db.venue.grades_released.writable = False
    if not is_real_manager:
        set_homework_defaults_for_admin(None)
    # Defaults for approved field.
    default_is_approved = is_user_admin(
    ) or IS_VENUE_CREATION_OPEN or get_user_email() in PREAPPROVED_EMAILS
    if not default_is_approved:
        email_parts = get_user_email().split('@')
        default_is_approved = len(
            email_parts) > 1 and email_parts[1] in PREAPPROVED_DOMAINS
    db.venue.is_approved.default = default_is_approved
    logger.info("User is admin: %r" % is_user_admin())
    if is_user_admin():
        db.venue.is_approved.writable = db.venue.is_approved.readable = True
    else:
        db.venue.is_approved.writable = False
        db.venue.is_approved.readable = False

    # Stores old defaults.
    if is_edit:
        old_managers = c.managers
        old_observers = c.observers
        old_submit_constraint = c.submit_constraint
        old_rate_constraint = c.rate_constraint

    # Define list_q as the query defining which user lists the user manages, OR, the previously existing list.
    allowed_user_lists = managed_user_lists
    if is_edit:
        allowed_user_lists.append(c.submit_constraint)
        allowed_user_lists.append(c.rate_constraint)
    if is_real_manager:
        list_q = (db.user_list.id.belongs(allowed_user_lists))
    elif is_user_admin:
        list_q = (db.user_list)
    else:
        list_q = (db.user_list.id < 0)
    db.venue.submit_constraint.requires = IS_EMPTY_OR(
        IS_IN_DB(db(list_q),
                 'user_list.id',
                 '%(name)s',
                 zero=T('-- Nobody --')))
    db.venue.rate_constraint.requires = IS_EMPTY_OR(
        IS_IN_DB(db(list_q),
                 'user_list.id',
                 '%(name)s',
                 zero=T('-- Nobody --')))

    # Generates the editing form.
    if is_edit:
        form = SQLFORM(db.venue, record=c)
        title = T('Edit Assignment')
    else:
        form = SQLFORM(db.venue)
        title = T('Create Assignment')

    # Creates a message to warn if approval will be required.
    if is_edit or default_is_approved:
        message = None
    else:
        message = SPAN(
            T('You can create an assignment.  However, before the assignment can be used, '
              'it will need to be approved by the crowdgrader.org admins (they will be '
              'notified when the assignment is created).  If you wish to be whitelisted, '
              'please contact the admins at '),
            A(EMAIL_TO, _href=("mailto:" + EMAIL_TO)))

    # If this is an edit, pre-fills in some fields.
    if is_edit and is_real_manager:
        read_info = keystore_multi_read(
            [c.description, c.submission_instructions, c.grading_instructions],
            default='')
        form.vars.description = read_info.get(c.description)
        form.vars.submission_instructions = read_info.get(
            c.submission_instructions)
        form.vars.grading_instructions = read_info.get(c.grading_instructions)
        this_venue = c
    else:
        this_venue = None

    if form.process(onvalidation=validate_venue(this_venue, True,
                                                is_real_manager)).accepted:
        id = form.vars.id
        if is_edit:
            # Do NOT combine these two ifs.
            if is_real_manager:
                # Note that this has to be called ONLY if the edit is done by the real manager.
                # Otherwise, things like form.vars.managers, form.vars.submit_constraint, etc, are None,
                # and the update_venue code will actually REMOVE access from those people -- even though
                # the people will still be listed as part of the venue.
                update_venue(c.id, form, old_managers, old_observers,
                             old_submit_constraint, old_rate_constraint)
        else:
            create_venue(id, form)
            logger.info("User " + get_user_email() +
                        " created venue: http://www.crowdgrader.org" +
                        URL('venues', 'view_venue', args=[id]))

            session.flash = T('The assignment has been created. ')
            if default_is_approved:
                redirect(URL('venues', 'view_venue', args=[id]))
            else:
                redirect(
                    URL('venues',
                        'venue_was_created',
                        args=[id],
                        user_signature=True))

        # Sends the user to look at the newly created or updated venue.
        redirect(URL('venues', 'view_venue', args=[id]))

    return dict(form=form, title=title, message=message)
コード例 #16
0
ファイル: venues.py プロジェクト: lawrancej/crowdranker
def view_venue():
    c = db.venue(request.args(0)) or redirect(URL('default', 'index'))
    props = db(db.user_properties.user == get_user_email()).select().first()
    # if c.raters_equal_submitters:
    db.venue.rate_constraint.readable = False
    if props == None and not is_user_admin():
        session.flash = T('Not Authorized.')
        redirect(URL('default', 'index'))
    # Checks view permission
    can_submit = access.can_submit(c, props)
    can_rate = access.can_rate(c, props)
    has_submitted = access.has_submitted(c, props)
    has_rated = access.has_rated(c, props)
    can_manage = access.can_manage(c, props)
    can_observe = access.can_observe(c, props)
    can_view_ratings = access.can_view_ratings(c, props)
    # Builds some links that are useful to give out to people.
    submission_link = URL('submission', 'submit', args=[c.id])
    review_link = URL('venues', 'reviewing_duties')
    if not (can_submit or can_rate or has_submitted or has_rated or can_manage
            or can_observe or can_view_ratings):
        session.flash = T('Not authorized.')
        redirect(URL('default', 'index'))
    if can_observe:
        db.venue.grading_instructions.readable = True
    venue_form = SQLFORM(db.venue, record=c, readonly=True)
    link_list = []
    if can_manage:
        link_list.append(
            A(T('Edit'),
              _href=URL('venues', 'edit', args=[c.id], user_signature=True)))
    if can_submit:
        link_list.append(
            A(T('Submit to this venue'),
              _href=URL('submission', 'submit', args=[c.id])))
    if can_manage:
        link_list.append(
            A(T('Add submission'),
              _href=URL('submission', 'manager_submit', args=[c.id])))
    if has_submitted:
        link_list.append(
            A(T('My submissions'), _href=URL('feedback', 'index',
                                             args=[c.id])))
    if can_view_ratings or access.can_view_submissions(c, props):
        link_list.append(
            A(T('Submissions'),
              _href=URL('ranking', 'view_submissions', args=[c.id])))
    if can_rate and not can_manage:
        link_list.append(
            A(T('Review'),
              _href=URL('rating',
                        'accept_review',
                        args=[c.id],
                        user_signature=True)))
    if can_observe or can_manage:
        link_list.append(
            A(T('Comparisons'),
              _href=URL('ranking', 'view_comparisons_index', args=[c.id])))
    if can_view_ratings:
        link_list.append(
            A(T('Crowd-grades'),
              _href=URL('ranking', 'view_grades', args=[c.id])))
    if is_user_admin():
        link_list.append(
            A(T('Experimental grades'),
              _href=URL('ranking', 'view_exp_grades', args=[c.id])))
    return dict(form=venue_form,
                link_list=link_list,
                venue=c,
                has_rated=has_rated,
                submission_link=submission_link,
                review_link=review_link)
コード例 #17
0
ファイル: venues.py プロジェクト: lawrancej/crowdranker
def edit():
    """Edits or creates a venue."""
    is_edit = (request.args(0) is not None)
    if is_edit:
        c = db.venue(request.args(0))
        if c is None:
            session.flash = T('No such assignment.')
            redirect(URL('venues', 'managed_index'))
    props = db(db.user_properties.user == get_user_email()).select().first()
    if props == None:
        managed_venue_list = []
        managed_user_lists = []
    else:
        managed_venue_list = util.get_list(props.venues_can_manage)
        managed_user_lists = util.get_list(props.managed_user_lists)
    if is_edit and not access.can_manage(c, props):
        session.flash = T('Not authorized.')
        redirect(URL('venues', 'managed_index'))
    is_real_manager = (not is_edit) or (c.id in managed_venue_list)
    logger.info("Is this real manager: %r" % is_real_manager)

    # Sets defaults
    set_homework_defaults(None)
    add_help_for_venue(None)
    if not is_edit:
        # Let's not bother someone who creates a venue with this.
        db.venue.grades_released.readable = db.venue.grades_released.writable = False
    if not is_real_manager:
        set_homework_defaults_for_admin(None)
    # Defaults for approved field.
    default_is_approved = is_user_admin() or IS_VENUE_CREATION_OPEN or get_user_email() in PREAPPROVED_EMAILS
    if not default_is_approved:
        email_parts = get_user_email().split('@')
        default_is_approved = len(email_parts) > 1 and email_parts[1] in PREAPPROVED_DOMAINS
    db.venue.is_approved.default = default_is_approved
    logger.info("User is admin: %r" % is_user_admin())
    if is_user_admin():
        db.venue.is_approved.writable = db.venue.is_approved.readable = True
    else:
        db.venue.is_approved.writable = False
        db.venue.is_approved.readable = False

    # Stores old defaults.
    if is_edit:
        old_managers = c.managers
        old_observers = c.observers
        old_submit_constraint = c.submit_constraint
        old_rate_constraint = c.rate_constraint

    # Define list_q as the query defining which user lists the user manages, OR, the previously existing list.
    allowed_user_lists = managed_user_lists
    if is_edit:
        allowed_user_lists.append(c.submit_constraint)
        allowed_user_lists.append(c.rate_constraint)
    if is_real_manager:
        list_q = (db.user_list.id.belongs(allowed_user_lists))
    elif is_user_admin:
        list_q = (db.user_list)
    else:
        list_q = (db.user_list.id < 0)
    db.venue.submit_constraint.requires = IS_EMPTY_OR(IS_IN_DB(
        db(list_q), 'user_list.id', '%(name)s', zero=T('-- Nobody --')))
    db.venue.rate_constraint.requires = IS_EMPTY_OR(IS_IN_DB(
        db(list_q), 'user_list.id', '%(name)s', zero=T('-- Nobody --')))

    # Generates the editing form.
    if is_edit:
        form = SQLFORM(db.venue, record=c)
        title = T('Edit Assignment')
    else:
        form = SQLFORM(db.venue)
        title = T('Create Assignment')

    # Creates a message to warn if approval will be required.
    if is_edit or default_is_approved:
        message = None
    else:
        message = SPAN(T(
            'You can create an assignment.  However, before the assignment can be used, '
            'it will need to be approved by the crowdgrader.org admins (they will be '
            'notified when the assignment is created).  If you wish to be whitelisted, '
            'please contact the admins at '), A(EMAIL_TO, _href=("mailto:" + EMAIL_TO)))
    
    # If this is an edit, pre-fills in some fields.
    if is_edit and is_real_manager:
        read_info = keystore_multi_read([c.description, c.submission_instructions, c.grading_instructions], default='')
        form.vars.description = read_info.get(c.description)
        form.vars.submission_instructions = read_info.get(c.submission_instructions)
        form.vars.grading_instructions = read_info.get(c.grading_instructions)
        this_venue = c
    else:
        this_venue = None
        
    if form.process(onvalidation=validate_venue(this_venue, True, is_real_manager)).accepted:
        id = form.vars.id
        if is_edit:
            # Do NOT combine these two ifs.
            if is_real_manager:
                # Note that this has to be called ONLY if the edit is done by the real manager.
                # Otherwise, things like form.vars.managers, form.vars.submit_constraint, etc, are None, 
                # and the update_venue code will actually REMOVE access from those people -- even though
                # the people will still be listed as part of the venue.
                update_venue(c.id, form, old_managers, old_observers, old_submit_constraint, old_rate_constraint)
        else:
            create_venue(id, form)
            logger.info("User " + get_user_email() + " created venue: http://www.crowdgrader.org" + URL('venues', 'view_venue', args=[id]))
                        
            session.flash = T('The assignment has been created. ')
            if default_is_approved:
                redirect(URL('venues', 'view_venue', args=[id]))
            else:
                redirect(URL('venues', 'venue_was_created', args=[id], user_signature=True))
            
        # Sends the user to look at the newly created or updated venue.
        redirect(URL('venues', 'view_venue', args=[id]))
        
    return dict(form=form, title=title, message=message)
コード例 #18
0
ファイル: ranking.py プロジェクト: lawrancej/crowdranker
def view_grades():
    """This function shows the final grade of each user.
    It takes as single argument the venue id.
    """
    # This function is used to get experimental grades from the db.
    def get_grade_fn(venue_id, run_id):
        def f(row):
            row = db((db.grades_exp.venue_id == venue_id) &
                     (db.grades_exp.user == row.user) &
                     (db.grades_exp.run_id == run_id)).select().first()
            if row is None:
                return 'None'
            # Generates a string summary.
            s = "subm_grade: %r subm_confidence: %r rev: %r rep: %r tot: %r" % (
                short_float_or_None(row.subm_grade),
                short_float_or_None(row.subm_confidence),
                short_float_or_None(row.review_grade),
                short_float_or_None(row.reputation),
                short_float_or_None(row.grade))
            return s
        return f
    
    # Main function.
    c = db.venue(request.args(0)) or redirect(URL('default', 'index'))
    props = db(db.user_properties.user == get_user_email()).select().first()
    if not access.can_view_ratings(c, props):
        session.flash = T('You do not have access to the final grades for this venue.')
        redirect(URL('venues', 'view_venue', args=[c.id]))
    # Checking that final grades are recent and don't need recomputation.
    venue_row = db(db.venue.id == c.id).select().first()
    grades_date = venue_row.latest_grades_date
    if grades_date is None:
        session.flash = T('The crowd-grades have not been computed yet.')
        redirect(URL('rating', 'crowd_grade', args=[c.id]))
    # The crowd-grades have been computed already.
    if is_user_admin():
        db.grades.reputation.readable = True
    db.grades.user.represent = represent_user_by_submission_feedback
    db.grades.venue_id.readable = False
    # Prepares the buttons at the top.
    link_list = []
    if access.can_manage(c, props):
        db.grades.assigned_grade.writable = True
        db.grades.assigned_grade.comment = T('Assign the desired grade to a few users, '
                                             'then automatically fill-in the remaining '
                                             'grades via interpolation. ')
        is_editable = True
        link_list.append(A(T('Recompute crowd-grades'), _href=URL('rating', 'crowd_grade', args=[c.id])))
        link_list.append(A(T('Interpolate final grades'), 
                           _href=URL('ranking', 'interpolate_grades', args=[c.id], user_signature=True)))
        link_list.append(A(T('Clear final grades'), 
                           _href=URL('ranking', 'reset_grades', args=[c.id], user_signature=True)))

        # Creates button to release / withdraw grades. 
        if c.grades_released:
            link_list.append(A(T('Hide grades from students'),
                               _href=URL('ranking', 'release_grades', args=[c.id, 'False'], user_signature=True)))
        else:
            link_list.append(A(T('Show grades to students'),
                               _href=URL('ranking', 'release_grades', args=[c.id, 'True'], user_signature=True)))
    else:
        db.grades.assigned_grade.writable = False
        is_editable = False
    # If one is the manager, and we are viewing experimental grades, offers the option
    # to download a spreadsheet including the experimental grades.
    if is_user_admin():
        link_list.append(A(T('View experimental runs'),
                           _href=URL('ranking', 'view_exp_grades', args=[c.id])))
    if is_user_admin() and request.vars.run_ids is not None:
        link_list.append(A(T('Download research data'), 
                           _href=URL('research', 'download_research_data.csv', args=[c.id], 
                                     vars=dict(run_ids=request.vars.run_ids),
                                     user_signature=True)))
    if is_user_admin():
        link_list.append(A(T('Evaluate grades'),
                           _href=URL('research', 'evaluate_grades', args=[c.id],
                                     user_signature=True)))
        link_list.append(A(T('Rerun evaluations'),
                           _href=URL('research', 'rerun_evaluations', args=[c.id],
                                     user_signature=True)))
    # Chooses the display fields.
    display_fields = [
        db.grades.user, db.grades.venue_id,
        db.grades.submission_grade, db.grades.submission_percentile,
        db.grades.accuracy, db.grades.n_ratings, 
        db.grades.grade, db.grades.percentile,
        db.grades.assigned_grade]
    if is_user_admin():
        display_fields.append(db.grades.reputation)
        display_fields.append(db.grades.submission_control_grade)
        db.grades.submission_control_grade.readable = True
    # Adds columns for any extra grade we wish to see.
    grid_links = []
    if is_user_admin() and request.vars.run_ids is not None:
        run_ids = request.vars.run_ids.split(',')
        for r in run_ids:
            grid_links.append(dict(
                header = r, 
                body = get_grade_fn(c.id, r)))
    if is_user_admin():
        # Adds a column for the true grade.
        grid_links.append(dict(
            header = '',
            body = lambda row: A(T('Enter control grade'), _class='btn',
                _href=URL('ranking', 'edit_control_grade', args=[c.id, row.user], user_signature=True))))
    # Prepares the grid.
    q = (db.grades.venue_id == c.id)
    grid = SQLFORM.grid(q,
        fields=display_fields,
        args=request.args[:1],
        user_signature=False, details=False,
        create=False, editable=is_editable, deletable=False,
        links=grid_links,
        maxtextlength=24,
        )
    title = A(c.name, _href=URL('venues', 'view_venue', args=[c.id]))
    grades_date_info = represent_date(c.latest_grades_date, c)
    if c.grades_released:
        grades_visibility = T('Grades are visible to students')
    else:
        grades_visibility = T('Grades are not visible to students')
    return dict(grid=grid, title=title, link_list=link_list, 
                grades_date_info=grades_date_info, grades_visibility=grades_visibility)