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]))
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]))
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)
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]))
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]))
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]))
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]))
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)
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)
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]))
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)
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)
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)
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)
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)
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)
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)
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)