def assignments(): with DbCursor() as c: student = _get_student(c) user_id, _, _, login, _, _ = student super_value = get_super(c, user_id) dropped = super_value is not None and super_value < 0 c.execute( "SELECT assignment, score, slipunits, updated FROM grades WHERE user = ?", [user_id]) grade_info = { assignment: (score, slipunits, updated) for assignment, score, slipunits, updated in c.fetchall() } template_common = _template_common(c) assignments_info = [] if not dropped: for assignment in config.assignments: a = assignment.student_view(login) if now_compare(a.not_visible_before) >= 0: assignments_info.append( (a.name, a.full_score, a.weight, a.due_date) + grade_info.get(a.name, (None, None, None))) return render_template("dashboard/assignments.html", assignments_info=assignments_info, **template_common)
def students_one(identifier, type_): with DbCursor() as c: student = None if type_ in ("id", "user_id"): student = get_user_by_id(c, identifier) elif type_ in ("github", "_github_explicit"): student = get_user_by_github(c, identifier) elif type_ == "login": student = get_user_by_login(c, identifier) elif type_ in ("sid", "student_id"): student = get_user_by_student_id(c, identifier) if student is None: abort(404) user_id, _, _, _, _, _ = student super_ = get_super(c, user_id) photo = None if student_photos_enabled: photo = get_photo(c, user_id) c.execute( '''SELECT users.id, users.name, users.github, groupsusers.`group` FROM groupsusers LEFT JOIN users ON groupsusers.user = users.id WHERE groupsusers.`group` IN (SELECT `group` FROM groupsusers WHERE user = ?)''', [user_id]) groups = OrderedDict() for g_user_id, g_name, g_github, g_group in c.fetchall(): groups.setdefault(g_group, []).append( (g_user_id, g_name, g_github)) grouplimit = get_grouplimit(c, user_id) c.execute( '''SELECT transaction_name, source, assignment, score, slipunits, updated, description FROM gradeslog WHERE user = ? ORDER BY updated DESC''', [user_id]) entries = c.fetchall() full_scores = { assignment.name: assignment.full_score for assignment in config.assignments } events = [entry + (full_scores.get(entry[2]), ) for entry in entries] c.execute( "SELECT assignment, score, slipunits, updated FROM grades WHERE user = ?", [user_id]) grade_info = { assignment: (score, slipunits, updated) for assignment, score, slipunits, updated in c.fetchall() } assignments_info = [(a.name, a.full_score, a.weight, a.due_date) + grade_info.get(a.name, (None, None, None)) for a in config.assignments] return render_template("ta/students_one.html", student=student, super_=super_, photo=photo, groups=groups.items(), grouplimit=grouplimit, events=events, assignments_info=assignments_info, **_template_common())
def students_one(identifier, type_): with DbCursor() as c: student = None if type_ in ("id", "user_id"): student = get_user_by_id(c, identifier) elif type_ in ("github", "_github_explicit"): student = get_user_by_github(c, identifier) elif type_ == "login": student = get_user_by_login(c, identifier) elif type_ in ("sid", "student_id"): student = get_user_by_student_id(c, identifier) if student is None: abort(404) user_id, _, _, _, _, _ = student super_ = get_super(c, user_id) photo = None if student_photos_enabled: photo = get_photo(c, user_id) c.execute( """SELECT users.id, users.name, users.github, groupsusers.`group` FROM groupsusers LEFT JOIN users ON groupsusers.user = users.id WHERE groupsusers.`group` IN (SELECT `group` FROM groupsusers WHERE user = ?)""", [user_id], ) groups = OrderedDict() for g_user_id, g_name, g_github, g_group in c.fetchall(): groups.setdefault(g_group, []).append((g_user_id, g_name, g_github)) grouplimit = get_grouplimit(c, user_id) c.execute( """SELECT transaction_name, source, assignment, score, slipunits, updated, description FROM gradeslog WHERE user = ? ORDER BY updated DESC""", [user_id], ) entries = c.fetchall() full_scores = {assignment.name: assignment.full_score for assignment in config.assignments} events = [entry + (full_scores.get(entry[2]),) for entry in entries] c.execute("SELECT assignment, score, slipunits, updated FROM grades WHERE user = ?", [user_id]) grade_info = {assignment: (score, slipunits, updated) for assignment, score, slipunits, updated in c.fetchall()} assignments_info = [ (a.name, a.full_score, a.weight, a.due_date) + grade_info.get(a.name, (None, None, None)) for a in config.assignments ] return render_template( "ta/students_one.html", student=student, super_=super_, photo=photo, groups=groups.items(), grouplimit=grouplimit, events=events, assignments_info=assignments_info, **_template_common() )
def build_now(): job_name = request.form.get("f_job_name") repo = request.form.get("f_repo") assignment = get_assignment_by_name(job_name) if not assignment: abort(400) assignment = assignment.student_view(repo) if assignment.manual_grading: abort(400) with DbCursor() as c: student = _get_student(c) user_id, _, _, login, _, _ = student super_value = get_super(c, user_id) dropped = super_value is not None and super_value < 0 if assignment.is_group: repos = get_groups(c, user_id) else: repos = [login] if repo not in repos: abort(403) if now_compare(assignment.not_visible_before, add_grace_period( assignment.cannot_build_after)) != 0 or dropped: abort(400) branch_hash = get_branch_hash(repo, "master") message = None if branch_hash: message = get_commit_message(repo, branch_hash) # This section doesn't absolutely NEED to be consistent with the previous transaction, # since we have not specified any actual data constraints. The only possible logical error # would be to fulfill a request when the current user's permissions have been revoked # between these two transactions. with DbCursor() as c: build_name = create_build(c, job_name, repo, branch_hash, message) if should_limit_source(repo, job_name): rate_limit_fail_build(build_name) else: job = Job(build_name, repo, "Web interface") dockergrader_queue.enqueue(job) return redirect(url_for("dashboard.builds_one", name=build_name))
def group_create(): if not config.groups_enabled: abort(404) try: githubs = request.form.getlist("f_github") github_job = None mailer_jobs = [] with DbCursor() as c: student = _get_student(c) inviter_user_id, inviter_name, _, _, inviter_github, _ = student super_value = get_super(c, inviter_user_id) dropped = super_value is not None and super_value < 0 if dropped: fail_validation( "You are not enrolled in the class; contact a TA if you believe this is a mistake" ) grouplimit = get_grouplimit(c, inviter_user_id) if grouplimit < 1: fail_validation("You are in too many groups already") invitees = [] invitation_user_ids = set() for github in githubs: if not github: continue invitee = get_user_by_github(c, github) if invitee is None: fail_validation("GitHub username not found: %s" % github) invitee_id, _, _, _, _, _ = invitee if invitee_id == inviter_user_id: continue if invitee_id in invitation_user_ids: continue invitation_user_ids.add(invitee_id) invitees.append(invitee) if not config.group_min_size <= len( invitation_user_ids) + 1 <= config.group_max_size: if config.group_min_size == config.group_max_size: fail_validation( "You need exactly %d people in your group" % config.group_min_size) else: fail_validation( "You need between %d and %d people in your group" % (config.group_min_size, config.group_max_size)) if config.mailer_enabled: for _, invitee_name, _, _, _, invitee_email in invitees: email_payload = create_email( "group_invite", invitee_email, "%s has invited you to a group" % inviter_name, inviter_name=inviter_name, inviter_github=inviter_github, invitee_name=invitee_name, invitees=invitees) mailer_job = mailer_queue.create(c, "send", email_payload) mailer_jobs.append(mailer_job) invitation_id = get_next_autoincrementing_value( c, "group_next_invitation_id") for invitation_user_id in invitation_user_ids: c.execute( "INSERT INTO invitations (invitation_id, user, status) VALUES (?, ?, ?)", [invitation_id, invitation_user_id, INVITED]) c.execute( "INSERT INTO invitations (invitation_id, user, status) VALUES (?, ?, ?)", [invitation_id, inviter_user_id, ACCEPTED]) modify_grouplimit(c, inviter_user_id, -1) group_name, group_members = finalize_group_if_ready( c, invitation_id) if group_name: if not config.github_read_only_mode: group_githubs = [] for _, _, _, github in group_members: assert github, "GitHub handle is empty" group_githubs.append(github) github_job = repomanager_queue.create( c, "assign_repo", (group_name, group_githubs)) if config.mailer_enabled: for _, name, email, github in group_members: email_payload = create_email( "group_confirm", email, "%s has been created" % group_name, group_name=group_name, name=name, group_members=group_members) mailer_job = mailer_queue.create( c, "send", email_payload) mailer_jobs.append(mailer_job) if config.mailer_enabled: for mailer_job in mailer_jobs: mailer_queue.enqueue(mailer_job) if github_job and not config.github_read_only_mode: repomanager_queue.enqueue(github_job) return redirect(url_for("dashboard.group")) except ValidationError as e: return redirect_with_error(url_for("dashboard.group"), e)
def group_respond(): if not config.groups_enabled: abort(404) try: github_job = None mailer_jobs = [] with DbCursor() as c: invitation_id = request.form.get("f_group") response = request.form.get("f_response") if not invitation_id: fail_validation( "Expected an invitation identifier (probably a programming error)" ) if response not in ("accept", "reject"): fail_validation( "Expected a response (probably a programming error)") student = _get_student(c) user_id, _, _, _, _, _ = student super_value = get_super(c, user_id) dropped = super_value is not None and super_value < 0 if dropped: fail_validation( "You are not enrolled in the class; contact a TA if you believe this is a mistake" ) c.execute( '''SELECT status FROM invitations WHERE invitation_id = ? AND user = ?''', [invitation_id, user_id]) statuses = c.fetchall() if len(statuses) != 1: fail_validation("Invitation has already been responded to") status = statuses[0][0] if response == "accept": if status != INVITED: fail_validation("Invitation has already been responded to") grouplimit = get_grouplimit(c, user_id) if grouplimit < 1: fail_validation("You have joined too many groups already") c.execute( '''UPDATE invitations SET status = ? WHERE invitation_id = ? AND user = ?''', [ACCEPTED, invitation_id, user_id]) modify_grouplimit(c, user_id, -1) group_name, group_members = finalize_group_if_ready( c, invitation_id) if group_name: if not config.github_read_only_mode: group_githubs = [] for _, _, _, github in group_members: assert github, "GitHub handle is empty" group_githubs.append(github) github_job = repomanager_queue.create( c, "assign_repo", (group_name, group_githubs)) if config.mailer_enabled: for _, name, email, github in group_members: email_payload = create_email( "group_confirm", email, "%s has been created" % group_name, group_name=group_name, name=name, group_members=group_members) mailer_job = mailer_queue.create( c, "send", email_payload) mailer_jobs.append(mailer_job) elif response == "reject": if status not in (ACCEPTED, INVITED): fail_validation("Invitation has already been rejected") c.execute( '''UPDATE invitations SET status = ? WHERE invitation_id = ? AND user = ?''', [REJECTED, invitation_id, user_id]) if status == ACCEPTED: # Give them back +1 to their group limit modify_grouplimit(c, user_id, +1) if config.mailer_enabled: for mailer_job in mailer_jobs: mailer_queue.enqueue(mailer_job) if github_job and not config.github_read_only_mode: repomanager_queue.enqueue(github_job) return redirect(url_for("dashboard.group")) except ValidationError as e: return redirect_with_error(url_for("dashboard.group"), e)
def assignments_one(name): with DbCursor() as c: student = _get_student(c) user_id, _, _, login, _, _ = student assignment = get_assignment_by_name(name) super_value = get_super(c, user_id) dropped = super_value is not None and super_value < 0 if not assignment: abort(404) assignment = assignment.student_view(login) slipunits_now = slip_units_now(assignment.due_date) is_visible = now_compare( assignment.not_visible_before) >= 0 and not dropped if assignment.manual_grading: can_build = False else: can_build = now_compare(assignment.cannot_build_after) <= 0 if not is_visible: abort(404) c.execute( "SELECT score, slipunits, updated FROM grades WHERE user = ? AND assignment = ?", [user_id, name]) grade = c.fetchone() if not grade: grade = (None, None, None) if assignment.is_group: repos = get_groups(c, user_id) else: repos = [login] c.execute( '''SELECT build_name, source, status, score, `commit`, message, started FROM builds WHERE job = ? AND source IN (%s) ORDER BY started DESC''' % (",".join(["?"] * len(repos))), [name] + repos) builds = c.fetchall() if builds: most_recent_repo = builds[0][1] else: most_recent_repo = None if grade[0] is not None: c.execute( '''SELECT COUNT(*) + 1 FROM grades WHERE assignment = ? AND score > ?''', [name, grade[0]]) rank, = c.fetchone() else: rank = None c.execute( '''SELECT COUNT(*), AVG(score) FROM grades WHERE assignment = ? AND score IS NOT NULL''', [name]) stats = c.fetchone() if stats[0] == 0: variance = None stddev = None else: c.execute( "SELECT AVG((score - ?) * (score - ?)) FROM grades WHERE assignment = ?", [stats[1], stats[1], name]) variance, = c.fetchone() stddev = sqrt(variance) assignment_info = ( (assignment.name, assignment.full_score, assignment.weight, assignment.due_date, assignment.category, assignment.is_group, assignment.manual_grading) + grade + (rank, ) + stats + (stddev, )) template_common = _template_common(c) return render_template("dashboard/assignments_one.html", assignment_info=assignment_info, builds=builds, repos=repos, most_recent_repo=most_recent_repo, slipunits_now=slipunits_now, can_build=can_build, **template_common)