Пример #1
0
        def staff_add_extension(netid, cid, aid):
            assignment = db.get_assignment(cid, aid)
            if not assignment:
                return util.error(
                    "Invalid course or assignment. Please try again.")

            if util.check_missing_fields(request.form, "netids", "max_runs",
                                         "start", "end"):
                return util.error("Missing fields. Please try again.")

            student_netids = request.form["netids"].replace(
                " ", "").lower().split(",")
            for student_netid in student_netids:
                if not util.valid_id(student_netid) or not verify_student(
                        student_netid, cid):
                    return util.error(
                        f"Invalid or non-existent student NetID: {student_netid}"
                    )

            try:
                max_runs = int(request.form["max_runs"])
                if max_runs < 1:
                    return util.error("Max Runs must be a positive integer.")
            except ValueError:
                return util.error("Max Runs must be a positive integer.")

            start = util.parse_form_datetime(request.form["start"]).timestamp()
            end = util.parse_form_datetime(request.form["end"]).timestamp()
            if start >= end:
                return util.error("Start must be before End.")

            for student_netid in student_netids:
                db.add_extension(cid, aid, student_netid, max_runs, start, end)
            return util.success("")
Пример #2
0
 def demote_course_admin(netid, cid):
     staff_id = request.form.get('netid')
     if not verify_staff(staff_id, cid) or not verify_admin(
             staff_id, cid):
         return util.error(f"'{staff_id}' is not a admin")
     db.remove_admin_from_course(cid, staff_id)
     return util.success(
         f"Successfully removed '{staff_id}' from admin")
Пример #3
0
        def staff_get_workers(netid, cid):
            if not verify_staff(netid, cid):
                return abort(HTTPStatus.FORBIDDEN)

            workers = bw_api.get_workers(cid)
            if workers is not None:
                return util.success(jsonify(workers), HTTPStatus.OK)
            return util.error("")
Пример #4
0
 def promote_course_staff(netid, cid):
     staff_id = request.form.get('netid')
     if not verify_staff(staff_id, cid):
         return util.error(f"'{staff_id}' is not a staff")
     result = db.add_admin_to_course(cid, staff_id)
     if none_modified(result):
         return util.error(f"'{staff_id}' is already an admin")
     return util.success(f"Successfully made '{staff_id}' admin")
Пример #5
0
        def staff_get_run_log(netid, cid, aid, run_id):
            if not verify_staff(netid, cid):
                return abort(HTTPStatus.FORBIDDEN)

            log = bw_api.get_grading_run_log(cid, run_id)
            if log:
                return util.success(jsonify(log), HTTPStatus.OK)
            return util.error("")
Пример #6
0
 def staff_delete_scheduled_run(netid, cid, aid, run_id):
     sched_run = db.get_scheduled_run(cid, aid, run_id)
     if sched_run is None:
         return util.error("Cannot find scheduled run")
     sched_api.delete_scheduled_run(sched_run["scheduled_run_id"])
     if not db.delete_scheduled_run(cid, aid, run_id):
         return util.error(
             "Failed to delete scheduled run. Please try again")
     return util.success("")
Пример #7
0
 def add_course_staff(netid, cid):
     new_staff_id = request.form.get('netid').lower()
     if new_staff_id is None:
         return util.error("Cannot find netid field")
     if not util.is_valid_netid(new_staff_id):
         return util.error(f"Poorly formatted NetID: '{new_staff_id}'")
     result = db.add_staff_to_course(cid, str(new_staff_id))
     if none_modified(result):
         return util.error(
             f"'{new_staff_id}' is already a course staff")
     return util.success(f"Successfully added {new_staff_id}")
Пример #8
0
        def staff_delete_extension(netid, cid, aid):
            extension_id = request.form["_id"]
            delete_result = db.delete_extension(extension_id)

            if delete_result is None:
                return util.error(
                    "Invalid extension, please refresh the page.")
            if delete_result.deleted_count != 1:
                return util.error("Failed to delete extension.")

            return util.success("")
Пример #9
0
        def admin_update_roster(netid, cid):
            netids = request.form["roster"].strip().lower().split("\n")

            for i, student_id in enumerate(netids):
                if not util.is_valid_netid(student_id):
                    return util.error(
                        f"Poorly formatted NetID on line {i + 1}: '{student_id}'"
                    )

            db.overwrite_student_roster(cid, netids)
            return util.success("Successfully updated roster.", HTTPStatus.OK)
Пример #10
0
        def add_assignment(netid, cid):
            missing = util.check_missing_fields(
                request.form, *[
                    "aid", "max_runs", "quota", "start", "end", "config",
                    "visibility"
                ])
            if missing:
                return util.error(f"Missing fields ({', '.join(missing)}).")

            aid = request.form["aid"]
            if not util.valid_id(aid):
                return util.error(
                    "Invalid Assignment ID. Allowed characters: a-z A-Z _ - .")

            new_assignment = db.get_assignment(cid, aid)
            if new_assignment:
                return util.error("Assignment ID already exists.")

            try:
                max_runs = int(request.form["max_runs"])
                if max_runs < MIN_PREDEADLINE_RUNS:
                    return util.error(
                        f"Max Runs must be at least {MIN_PREDEADLINE_RUNS}.")
            except ValueError:
                return util.error("Max Runs must be a positive integer.")

            quota = request.form["quota"]
            if not db.Quota.is_valid(quota):
                return util.error("Quota Type is invalid.")

            start = util.parse_form_datetime(request.form["start"]).timestamp()
            end = util.parse_form_datetime(request.form["end"]).timestamp()
            if start is None or end is None:
                return util.error("Missing or invalid Start or End.")
            if start >= end:
                return util.error("Start must be before End.")

            try:
                config = json.loads(request.form["config"])
                msg = bw_api.set_assignment_config(cid, aid, config)

                if msg:
                    return util.error(
                        f"Failed to add assignment to Broadway: {msg}")
            except json.decoder.JSONDecodeError:
                return util.error("Failed to decode config JSON")

            visibility = request.form["visibility"]

            db.add_assignment(cid, aid, max_runs, quota, start, end,
                              visibility)
            return util.success("")
Пример #11
0
        def edit_assignment(netid, cid, aid):
            course = db.get_course(cid)
            assignment = db.get_assignment(cid, aid)
            if course is None or assignment is None:
                return abort(HTTPStatus.NOT_FOUND)

            missing = util.check_missing_fields(
                request.form,
                *["max_runs", "quota", "start", "end", "visibility"])
            if missing:
                return util.error(f"Missing fields ({', '.join(missing)}).")

            try:
                max_runs = int(request.form["max_runs"])
                if max_runs < MIN_PREDEADLINE_RUNS:
                    return util.error(
                        f"Max Runs must be at least {MIN_PREDEADLINE_RUNS}.")
            except ValueError:
                return util.error("Max Runs must be a positive integer.")

            quota = request.form["quota"]
            if not db.Quota.is_valid(quota):
                return util.error("Quota Type is invalid.")

            start = util.parse_form_datetime(request.form["start"]).timestamp()
            end = util.parse_form_datetime(request.form["end"]).timestamp()
            if start is None or end is None:
                return util.error("Missing or invalid Start or End.")
            if start >= end:
                return util.error("Start must be before End.")

            try:
                config_str = request.form.get("config")

                if config_str is not None:  # skip update otherwise
                    config = json.loads(request.form["config"])
                    msg = bw_api.set_assignment_config(cid, aid, config)

                    if msg:
                        return util.error(
                            f"Failed to update assignment config to Broadway: {msg}"
                        )
            except json.decoder.JSONDecodeError:
                return util.error("Failed to decode config JSON")

            visibility = request.form["visibility"]

            if not db.update_assignment(cid, aid, max_runs, quota, start, end,
                                        visibility):
                return util.error("Save failed or no changes were made.")
            return util.success("")
Пример #12
0
        def upload_roster_file(netid, cid):
            file_content = request.form.get('content')
            netids = file_content.strip().lower().split('\n')
            for i, student_id in enumerate(netids):
                if not util.is_valid_netid(student_id):
                    return util.error(
                        f"Poorly formatted NetID on line {i + 1}: '{student_id}'"
                    )

            result = db.overwrite_student_roster(cid, netids)
            if none_modified(result):
                return util.error(
                    "The new roster is the same as the current one.")
            return util.success("Successfully updated roster.")
Пример #13
0
        def student_grade_assignment(netid, cid, aid):
            if not verify_student_or_staff(netid, cid):
                return abort(HTTPStatus.FORBIDDEN)
            if not verify_csrf_token(request.form.get("csrf_token")):
                return abort(HTTPStatus.BAD_REQUEST)

            now = util.now_timestamp()
            ext_to_use = None
            current_csrf_token = request.form.get("csrf_token")

            if not verify_staff(netid, cid):
                # not a staff member; perform quota checks
                num_available_runs = get_available_runs(cid, aid, netid, now)
                active_extensions, num_extension_runs = get_active_extensions(
                    cid, aid, netid, now)

                if num_available_runs + num_extension_runs <= 0:
                    restore_csrf_token(current_csrf_token)
                    return util.error("No grading runs available.")
                if num_available_runs <= 0:
                    # find the extension that is closest to expiration
                    ext_to_use = min(active_extensions,
                                     key=lambda ext: ext["end"])

            now_rounded = util.timestamp_round_up_minute(now)

            run_id = bw_api.start_grading_run(cid, aid, [netid], now_rounded)
            if run_id is None:
                restore_csrf_token(current_csrf_token)
                return util.error(
                    "Failed to start grading run. Please try again.")

            db.add_grading_run(cid,
                               aid,
                               netid,
                               now,
                               run_id,
                               extension_used=ext_to_use)
            return util.success("")
Пример #14
0
        def trigger_scheduled_run(cid, aid, scheduled_run_id):
            sched_run = db.get_scheduled_run_by_scheduler_id(
                cid, aid, scheduled_run_id)
            if sched_run is None:
                logging.warning(
                    "Received trigger scheduled run request for scheduled_run_id '%s' but cannot find corresponding run.",
                    scheduled_run_id)
                return util.error("")
            if sched_run["status"] != ScheduledRunStatus.SCHEDULED:
                logging.warning(
                    "Received trigger scheduled run for _id '%s' but this run has status '%s', which is not 'scheduled'.",
                    str(sched_run["_id"]), sched_run["status"])
                return util.error("")

            # If roster is not provided, use course roster
            if sched_run["roster"] is None:
                course = db.get_course(cid)
                if course is None:
                    return util.error("")
                netids = course["student_ids"]
            # If a roster is provided, use it
            else:
                netids = sched_run["roster"]

            # Start broadway grading run
            bw_run_id = bw_api.start_grading_run(cid,
                                                 f"{aid}_{sched_run['_id']}",
                                                 netids, sched_run["due_time"])
            if bw_run_id is None:
                logging.warning("Failed to trigger run with broadway")
                db.update_scheduled_run_status(sched_run["_id"],
                                               ScheduledRunStatus.FAILED)
                return util.error("")
            else:
                db.update_scheduled_run_status(sched_run["_id"],
                                               ScheduledRunStatus.RAN)
                db.update_scheduled_run_bw_run_id(sched_run["_id"], bw_run_id)
            return util.success("")
Пример #15
0
        def add_or_edit_scheduled_run(cid, aid, run_id, form,
                                      scheduled_run_id):
            # course and assignment name validation
            course = db.get_course(cid)
            assignment = db.get_assignment(cid, aid)
            if course is None or assignment is None:
                return abort(HTTPStatus.NOT_FOUND)

            # form validation
            missing = util.check_missing_fields(request.form, "run_time",
                                                "due_time", "name", "config")
            if missing:
                return util.error(f"Missing fields ({', '.join(missing)}).")
            run_time = util.parse_form_datetime(
                request.form["run_time"]).timestamp()
            if run_time is None:
                return util.error("Missing or invalid run time.")
            if run_time <= util.now_timestamp():
                return util.error("Run time must be in the future.")
            due_time = util.parse_form_datetime(
                request.form["due_time"]).timestamp()
            if due_time is None:
                return util.error("Missing or invalid due time.")
            if "roster" not in request.form or not request.form["roster"]:
                roster = None
            else:
                roster = request.form["roster"].replace(" ",
                                                        "").lower().split(",")
                for student_netid in roster:
                    if not util.valid_id(student_netid) or not verify_student(
                            student_netid, cid):
                        return util.error(
                            f"Invalid or non-existent student NetID: {student_netid}"
                        )
            try:
                config = json.loads(request.form["config"])
                msg = bw_api.set_assignment_config(cid, f"{aid}_{run_id}",
                                                   config)
                if msg:
                    return util.error(
                        f"Failed to upload config to Broadway: {msg}")
            except json.decoder.JSONDecodeError:
                return util.error("Failed to decode config JSON")

            # Schedule a new run with scheduler
            if scheduled_run_id is None:
                scheduled_run_id = sched_api.schedule_run(run_time, cid, aid)
                if scheduled_run_id is None:
                    return util.error("Failed to schedule run with scheduler")
            # Or if the run was already scheduled, update the time
            else:
                if not sched_api.update_scheduled_run(scheduled_run_id,
                                                      run_time):
                    return util.error(
                        "Failed to update scheduled run time with scheduler")

            assert scheduled_run_id is not None

            if not db.add_or_update_scheduled_run(
                    run_id, cid, aid, run_time, due_time, roster,
                    request.form["name"], scheduled_run_id):
                return util.error(
                    "Failed to save the changes, please try again.")
            return util.success("")
Пример #16
0
 def delete_assignment(netid, cid, aid):
     if not db.remove_assignment(cid, aid):
         return util.error("Assignment doesn't exist")
     return util.success("")
Пример #17
0
 def staff_get_scheduled_run(netid, cid, aid, run_id):
     sched_run = db.get_scheduled_run(cid, aid, run_id)
     if sched_run is None:
         return util.error("Cannot find scheduled run")
     del sched_run["_id"]
     return util.success(json.dumps(sched_run), 200)
Пример #18
0
 def staff_get_extensions(netid, cid, aid):
     extensions = list(db.get_extensions(cid, aid))
     for ext in extensions:
         ext["_id"] = str(ext["_id"])
     return util.success(jsonify(extensions), HTTPStatus.OK)
Пример #19
0
 def remove_course_staff(netid, cid):
     staff_id = request.form.get('netid')
     result = db.remove_staff_from_course(cid, staff_id)
     if none_modified(result):
         return util.error(f"'{staff_id}' is not a staff")
     return util.success(f"Successfully removed '{staff_id}'")