def enter_grades_confirm(): try: f_step = request.form.get("f_step") if f_step not in ("1", "2"): fail_validation("Enum out of range (probably a programming error)") step = int(f_step) assignment_name = request.form.get("f_assignment") if not assignment_name: fail_validation("Assignment name is required") assignment = get_assignment_by_name(assignment_name) if assignment is None: fail_validation("Assignment not found: %s" % assignment_name) min_score, max_score = assignment.min_score, assignment.max_score description = request.form.get("f_description") if not description: fail_validation("Transaction description is required") transaction_source = github_username() entries = [] user_id_set = set() with DbCursor() as c: valid_identifiers, ambiguous_identifiers = get_valid_ambiguous_identifiers( c) def try_add(f_student, f_score, f_slipunits): if not any((f_student, f_score, f_slipunits)): return elif not f_student and (f_score or f_slipunits): fail_validation( "Expected student SID, login, or name, but none provided" ) elif f_student in ambiguous_identifiers: fail_validation( "The identifier '%s' is ambiguous. Please use another." % f_student) else: if step == 1: students = get_users_by_identifier(c, f_student) elif step == 2: student = get_user_by_id(c, f_student) # Let the usual error handling take care of this case students = [student] if student else [] if not students: fail_validation("Student or group not found: %s" % f_student) for student in students: user_id, student_name, _, _, _, _ = student if user_id in user_id_set: fail_validation( "Student was listed more than once: %s" % student_name) try: score = float_or_none(f_score) except ValueError: fail_validation("Not a valid score: %s" % f_score) try: slipunits = int_or_none(f_slipunits) except ValueError: fail_validation( "Slip %s amount not valid: %s" % (slip_unit_name_plural, f_slipunits)) if slipunits is not None and slipunits < 0: fail_validation("Slip %s cannot be negative" % slip_unit_name_plural) if score is not None and not min_score <= score <= max_score: fail_validation( "Score is out of allowed range: %s (Range: %s to %s)" % (f_score, str(min_score), str(max_score))) entries.append([user_id, score, slipunits]) user_id_set.add(user_id) if step == 1: f_students = request.form.getlist("f_student") f_scores = request.form.getlist("f_score") f_slipunitss = request.form.getlist("f_slipunits") if not same_length(f_students, f_scores, f_slipunitss): fail_validation( "Different numbers of students, scores, and slip %s " + "reported. Browser bug?" % slip_unit_name_plural) for f_student, f_score, f_slipunits in zip( f_students, f_scores, f_slipunitss): try_add(f_student, f_score, f_slipunits) f_csv = request.form.get("f_csv", "") for row in csv.reader(StringIO.StringIO(f_csv), delimiter=",", quotechar='"'): if len(row) != 3: fail_validation("CSV rows must contain 3 entries") try_add(*row) if not entries: fail_validation("No grade or slip %s changes entered" % slip_unit_name_plural) if step == 1: c.execute( '''SELECT id, name, sid, login, github FROM users WHERE id IN (%s)''' % (",".join(["?"] * len(entries))), [user_id for user_id, _, _ in entries]) students = c.fetchall() details_user = {} for user_id, name, sid, login, github in students: details_user[user_id] = [name, sid, login, github] c.execute( '''SELECT user, score, slipunits, updated FROM grades WHERE assignment = ? AND user IN (%s)''' % (",".join(["?"] * len(entries))), [assignment.name] + [user_id for user_id, _, _ in entries]) grades = c.fetchall() details_grade = {} for user_id, score, slipunits, updated in grades: details_grade[user_id] = [score, slipunits, updated] entries_details = [] for entry in entries: user_id = entry[0] entry_details = (entry + details_user.get(user_id, [None] * 4) + details_grade.get(user_id, [None] * 3)) entries_details.append(entry_details) elif step == 2: transaction_number = get_next_autoincrementing_value( c, "enter_grades_last_transaction_number") transaction_name = "enter-grades-%s" % transaction_number for user_id, score, slipunits in entries: assign_grade_batch(c, [user_id], assignment.name, score, slipunits, transaction_name, description, transaction_source, manual=True, dont_lower=False) if step == 1: entries_csv = StringIO.StringIO() entries_csv_writer = csv.writer(entries_csv, delimiter=",", quotechar='"') for entry in entries: entries_csv_writer.writerow(entry) return render_template("ta/enter_grades_confirm.html", entries_details=entries_details, entries_csv=entries_csv.getvalue(), assignment_name=assignment.name, description=description, full_score=assignment.full_score, **_template_common()) elif step == 2: if len(entries) == 1: flash("1 grade committed", "success") else: flash("%d grades committed" % len(entries), "success") return redirect(url_for("ta.enter_grades")) except ValidationError as e: return redirect_with_error(url_for("ta.enter_grades"), e)
def get_transaction_id(self, c): option_key = "%s_next_transaction_id" % self.queue_name transaction_id = get_next_autoincrementing_value(c, option_key) return transaction_id
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 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: 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)