def is_enrolled(course, *, roles=None): """Check whether the current user is enrolled as any of the ``roles`` for ``course``. :param course: the course code to check :type course: str :param roles: the roles to check for the user :type roles: list-like :return: ``True`` if the user is any of ``roles``, ``False`` otherwise """ try: endpoint = get_endpoint(course=course) for participation in get_user()["participations"]: if roles and participation["role"] not in roles: continue if participation["course"]["offering"] != endpoint: continue return True return False except Exception as e: # fail safe! print(e) return False
def get_assignments(course): endpoint = get_endpoint(course=course) assignments: List[Assignment] = Assignment.query.filter( Assignment.endpoint == endpoint).all() return render_template( "assignments.html", course=course, assignments=sorted(assignments, key=lambda a: -a.last_modified), )
def update(): if not os.path.exists("data"): try: os.makedirs("data") except FileExistsError as e: print("Data folder exists, false alarm!") sections = "sp21" in get_endpoint(course="cs61a") with connect_db() as db: gscope: List[Tuple[str, str]] = db( "SELECT name, gs_code FROM gscope", [], ).fetchall() adjustments: List[Tuple[str, str]] = db( "SELECT url, sheet FROM adjustments", [], ).fetchall() print("=================================================") roster_export.export() print("=================================================") okpy_export.export() gs_assignments = {} if not gscope: print("No Gradescope assignments found!", file=sys.stderr) for name, gs_code in gscope: print("=================================================") full_name = gs_export.export(name, gs_code) if full_name: gs_assignments[name] = full_name else: print(f"Gradescope export for '{name} ({gs_code})' failed.", file=sys.stderr) if sections: print("=================================================") sections_export.export() print("=================================================") assemble.assemble(gscope=gs_assignments, recovery=True, sections=sections, adjustments=adjustments) print("=================================================")
def register_course(course): if get_endpoint(course=course) not in get_staff_endpoints(): abort(403) with connect_db() as db: ret = db("SELECT * FROM bot_data WHERE course = (%s)", [course]).fetchone() if ret: # course already setup return redirect( get_add_to_slack_link(slack_workspace_name(course=course))) else: return redirect(url_for("course_config", course=course))
def create_assignment_rpc(course, name, file, command, batch_size, grading_base): assignment: Assignment = Assignment.query.filter_by( name=name, course=course, endpoint=get_endpoint(course=course)).one_or_none() if not assignment: assignment = Assignment( name=name, assignment_secret=new_secret(), course=course, endpoint=get_endpoint(course=course), ) db.session.add(assignment) assignment.file = file assignment.command = command assignment.last_modified = int(time.time()) assignment.batch_size = batch_size assignment.grading_base = grading_base db.session.commit() return assignment.assignment_secret
def is_staff(course): try: endpoint = get_endpoint(course=course) for participation in get_user()["participations"]: if participation["role"] not in AUTHORIZED_ROLES: continue if participation["course"][ "offering"] != endpoint and endpoint is not None: continue return True return False except Exception as e: # fail safe! print(e) return False
def get_endpoint(course=None): """Gets a course's most recent Okpy endpoint, typically the current semester's, using :meth:`~common.rpc.auth.get_endpoint`. :param course: the course code, such as "cs61a", inferred using :meth:`~common.course_config.get_course` if omitted :type course: str :return: the Okpy endpoint, such as "cal/cs61a/sp21" """ if getenv("ENV") != "prod": return "cal/cs61a/sp21" if not course: course = get_course() if course not in COURSE_ENDPOINTS: COURSE_ENDPOINTS[course] = auth.get_endpoint(course=course) return COURSE_ENDPOINTS[course]
def set_course_config(course): if get_endpoint(course=course) not in get_staff_endpoints(): abort(403) with connect_db() as db: for service in CONFIG["services"]: db( "DELETE FROM activated_services WHERE course=(%s) AND service=(%s)", [course, service], ) if service in request.form: db( "INSERT INTO activated_services VALUES (%s, %s)", [course, service], ) return redirect(url_for("course_config", course=course))
def fail_unfinished_jobs(course, assignment): endpoint = get_endpoint(course=course) jobs = (Job.query.join(Assignment).filter( Assignment.endpoint == endpoint).filter( Assignment.name == assignment).filter( Job.status.in_(("queued", "running"))).all()) count = Job.query.filter( Job.job_secret.in_([job.job_secret for job in jobs])).update( { Job.status: "failed", Job.finished_at: int(time.time()), Job.result: f"Marked as failed by {get_user()['email']}.", }, synchronize_session="fetch", ) db.session.commit() return dict(modified=count)
def job_details(course, id): endpoint = get_endpoint(course=course) job: Job = (Job.query.join(Assignment).filter( Assignment.endpoint == endpoint).filter( Job.external_job_id == id).one_or_none()) if not job: abort(404, "Job not found.") return render_template( "job.html", course=course, backup=job.backup, status=job.status, result=job.result, start=job.started_at, finish=job.finished_at, )
def okpy_batch_grade_impl(): data = request.json subm_ids = data["subm_ids"] assignment = data["assignment"] access_token = data["access_token"] if assignment == "test": return "OK" assignment: Optional[Assignment] = Assignment.query.get(assignment) if not assignment or assignment.endpoint != get_endpoint( course=assignment.course ): abort(404, "Unknown Assignment") if len(subm_ids) / assignment.batch_size > 50: abort( 405, "Too many batches! Please set the batch_size so that there are <= 50 batches.", ) job_secrets = [new_secret() for _ in subm_ids] queue_time = int(time.time()) jobs = [ Job( assignment_secret=assignment.assignment_secret, backup=backup_id, status="queued", job_secret=job_secret, external_job_id=new_secret(), access_token=access_token, queued_at=queue_time, ) for backup_id, job_secret in zip(subm_ids, job_secrets) ] db.session.bulk_save_objects(jobs) db.session.commit() trigger_jobs( assignment_id=assignment.assignment_secret, jobs=job_secrets, noreply=True ) return dict(jobs=[job.external_job_id for job in jobs])
def retrigger_unsuccessful_jobs(course, assignment): endpoint = get_endpoint(course=course) assignment = Assignment.query.filter_by(name=assignment, endpoint=endpoint).one() jobs = (Job.query.join(Assignment).filter( Assignment.endpoint == endpoint).filter( Assignment.name == assignment.name).filter( Job.status != "finished").all()) for job in jobs: job.status = "queued" db.session.commit() trigger_jobs( assignment_id=assignment.assignment_secret, jobs=[job.job_secret for job in jobs if job.status == "queued"], ) return dict(modified=len(jobs))
def course_config(course): if get_endpoint(course=course) not in get_staff_endpoints(): abort(403) with connect_db() as db: ret = db( "SELECT service FROM activated_services WHERE course = (%s)", [course]) active_services = set(x[0] for x in ret) service_list = "<br />".join(f""" <label> <input type="checkbox" name="{service}" {"checked" if service in active_services else ""} > {service.title()}: {description} </label> """ for service, description in CONFIG["services"].items()) return f"""
import os, roster_export, okpy_export, sys import gs_export, sections_export, assemble from typing import List, Tuple from common.rpc.auth import get_endpoint from common.db import connect_db if not os.path.exists("data"): try: os.makedirs("data") except FileExistsError as e: print("Data folder exists, false alarm!") sections = "fa20" in get_endpoint(course="cs61a") with connect_db() as db: gscope: List[Tuple[str, str]] = db( "SELECT name, gs_code FROM gscope", [], ).fetchall() acadh: List[Tuple[str, str]] = db( "SELECT url, sheet FROM acadh", [], ).fetchall() def update(): print("=================================================") roster_export.export()
def get_jobs(course, assign): endpoint = get_endpoint(course=course) jobs: List[Job] = (Job.query.join(Assignment).filter( Assignment.endpoint == endpoint).filter(Assignment.name == assign)) assign = (Assignment.query.filter(Assignment.name == assign).filter( Assignment.endpoint == endpoint).one_or_none()) if not assign: abort(404, "Assignment not found.") queued_at = request.args.get("queued_at", 0) if queued_at: jobs = jobs.filter(Job.queued_at == queued_at) status = request.args.get("status", "all") if status != "all": jobs = jobs.filter(Job.status == status) jobs = jobs.all() batches = {} for job in jobs: if str(job.queued_at) not in batches: batches[str(job.queued_at)] = { "jobs": [], "finished": 0, "failed": 0, "running": 0, "queued": 0, "completed": 0, "total": 0, } batch = batches[str(job.queued_at)] details = { "started_at": job.started_at, "finished_at": job.finished_at, "backup": job.backup, "status": job.status, "result": job.result, "id": job.external_job_id, } if details["finished_at"]: if details["status"] == "finished": batch["finished"] += 1 details["duration"] = details["finished_at"] - details[ "started_at"] else: batch["failed"] += 1 batch["completed"] += 1 elif details["started_at"]: batch["running"] += 1 else: batch["queued"] += 1 batch["total"] += 1 batch["progress"] = batch["completed"] / batch["total"] batch["jobs"].append(details) return render_template( "assignment.html", course=course, assign=assign, batches={k: batches[k] for k in sorted(batches, reverse=True)}, queued_at=str(queued_at), status=status, )