Example #1
0
def add_reference_solution(api: ApiClient, exercise_id, note, runtime_environment, files):
    solution = api.create_reference_solution(exercise_id, {
        "note": note,
        "runtimeEnvironmentId": runtime_environment,
        "files": [api.upload_file(file, open(file, "r")) for file in files]
    })
    click.echo(solution["id"])
Example #2
0
def check_rs_evaluations(api: ApiClient, threshold):
    """
    Find exercises that had no successful reference solution evaluation in
    a given number of days
    """
    for exercise in api.get_exercises():
        solutions = api.get_reference_solutions(exercise["id"])
        if not solutions:
            logging.error("Exercise %s has no reference solutions", exercise["id"])
            continue

        found = False
        found_recent = False

        for solution in solutions:
            for evaluation in api.get_reference_solution_evaluations(solution["id"]):
                status_ok = evaluation["evaluationStatus"] == "done"
                submission_timestamp = int(evaluation["submittedAt"])
                submission_timestamp = max(0, submission_timestamp)
                submission_date = datetime.fromtimestamp(submission_timestamp)
                threshold_date = datetime.utcnow() - timedelta(days=int(threshold))
                recent = submission_date >= threshold_date
                if status_ok:
                    found = True
                    if recent:
                        found_recent = True
                        break

        if not found_recent:
            if found:
                logging.error("Exercise %s has no recent successful evaluations", exercise["id"])
            else:
                logging.error("Exercise %s has never had any successful evaluations", exercise["id"])
Example #3
0
def caslogin(data_dir: Path, api_url):
    """
    Log in using CAS UK
    """

    cas_url = CAS_URL.substitute(service_url=quote_plus(api_url))

    username = click.prompt("User name")
    password = click.prompt("Password", hide_input=True)

    session = requests.session()
    login_page = session.get(cas_url)

    soup = BeautifulSoup(login_page.text, "lxml")
    form = soup.select("form#fm1")[0]

    form_data = {}
    for input in form.select("input"):
        form_data[input["name"]] = input["value"] if input.has_attr(
            "value") else ""

    form_data.update({"username": username, "password": password})

    response = session.post(cas_url, data=form_data, allow_redirects=False)
    ticket = parse_qs(urlparse(
        response.raw.get_redirect_location()).query)["ticket"][0]

    api = ApiClient(api_url)
    api_login_response = api.login_external("cas-uk", "cas", {
        "ticket": ticket,
        "clientUrl": api_url
    })

    api_token = api_login_response["accessToken"]
    UserContext(api_url, api_token).store(data_dir / "context.yaml")
Example #4
0
File: cli.py Project: ReCodEx/cli
def update_points(api: ApiClient, points_id, points, note):
    """
    Update shadow assignment points (one points record of one user)
    """

    awarded_at = int(time.time())
    api.update_shadow_assignment_points(points_id, points, note, awarded_at)
Example #5
0
File: cli.py Project: ReCodEx/cli
def download_best_solutions(api: ApiClient, download_dir, assignment_id):
    """
    Download the best solution of this assignment from each user.
    """
    if download_dir is None:
        download_dir = "."
    if not os.path.exists(download_dir) or not os.path.isdir(download_dir):
        click.echo("Download path '{}' must exist and must be a directory.".format(download_dir))
        return

    # Get assignment metadata and best solution for each student ...
    assignment = api.get_assignment(assignment_id)
    if assignment is None:
        click.echo("Assignment not found.")
        return
    click.echo("Downloading best solutions of '{}' to '{}' ...".format(
        getAssignmentName(assignment), download_dir))
    best_solutions = api.get_assignment_best_solutions(assignment_id)

    # Iterate over students
    for student in api.get_group_students(assignment["groupId"]):
        # Try to find best solution for the student
        best = best_solutions[student["id"]]
        if best:
            # File name comprise user name in plain ASCII and its ID for uniqueness
            file_name = "{}-{}-{}.zip".format(
                asciiize_string(student["name"]["lastName"]),
                asciiize_string(student["name"]["firstName"]), student["id"])
            points = safe_get_solution_points(best)
            created = datetime.datetime.fromtimestamp(best["createdAt"]).strftime('%Y-%m-%d %H:%M:%S')
            click.echo("Saving {} ... {} points, {}".format(file_name, points, created))
            api.download_solution(best['id'], "{}/{}".format(download_dir, file_name))
Example #6
0
def add_localization(api: ApiClient, locale, exercise_id, include_name):
    """
    Add (or update if exists) a localized text of an exercise. The text is read from the standard input.
    If includeName flag is set, the first line of the input is used as the name.
    """
    full_exercise = api.get_exercise(exercise_id)
    copy_props = ["version", "difficulty", "localizedTexts", "isPublic", "isLocked", "configurationType",
                  "solutionFilesLimit", "solutionFilesLimit", "solutionSizeLimit", "mergeJudgeLogs"]
    exercise = {}
    for prop in copy_props:
        exercise[prop] = full_exercise[prop]

    localizedText = next((lt for lt in exercise["localizedTexts"] if lt["locale"] == locale), None)
    if localizedText is None:
        localizedText = {
            "locale": locale,
            "name": "",
            "text": "",
            "link": "",
            "description": ""
        }
        exercise["localizedTexts"].append(localizedText)

    input_stream = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8')
    if include_name is True:
        localizedText["name"] = input_stream.readline().strip()

    localizedText["text"] = input_stream.read()
    api.update_exercise(exercise_id, exercise)
Example #7
0
File: cli.py Project: ReCodEx/cli
def create_points(api: ApiClient, assignment_id, user_id, points, note):
    """
    Create shadow assignment points record (for one user)
    """

    awarded_at = int(time.time())
    api.create_shadow_assignment_points(assignment_id, user_id, points, note,
                                        awarded_at)
Example #8
0
def set_flag(api: ApiClient, flag, solution_id, unset):
    """
    Set solution flag (accepted or reviewed).
    """

    if flag not in ["accepted", "reviewed"]:
        click.echo("Invalid flag '{}'.".format(flag))
    else:
        api.solution_set_flag(solution_id, flag, not unset)
Example #9
0
def evaluate_all_rs(api: ApiClient):
    """
    Request evaluation for all reference solutions
    """
    with click.progressbar(api.get_exercises()) as bar:
        for exercise in bar:
            try:
                api.evaluate_reference_solutions(exercise["id"])
            except Exception as e:
                logging.error("Error in exercise {}: {}".format(exercise["id"], str(e)))
Example #10
0
def set_config(api: ApiClient, exercise_id, file_name, useJson):
    """
    Load a JSON or YAML from a file and set it as configuration.
    """
    with open(file_name, 'r') as stream:
        if useJson:
            config = json.load(stream)
        else:
            config = yaml.safe_load(stream)
    api.update_exercise_config(exercise_id, config)
Example #11
0
def call(api: ApiClient, method, path):
    """
    Perform an API call directly
    """

    method = method.lower()
    data = {}

    if method in ("post", "put"):
        data = json.loads(sys.stdin.read())

    pprint(api.extract_payload(api.call(method, path, data=data)))
Example #12
0
def tags_get_all(api: ApiClient, stats):
    """
    Get all tag names available. Optionally with statistics (how many exercises use each tag).
    """
    if stats:
        tags = api.get_exercise_tags_stats()
        for tag, count in tags.items():
            click.echo("{} {}".format(tag, count))
    else:
        tags = api.get_exercise_tags()
        for tag in tags:
            click.echo(tag)
Example #13
0
File: cli.py Project: ReCodEx/cli
def login(data_dir: Path, api_url):
    """
    Log in using a ReCodEx account
    """

    username = click.prompt("User name")
    password = click.prompt("Password", hide_input=True)

    api = ApiClient(api_url)
    api_login_response = api.login(username, password)

    api_token = api_login_response["accessToken"]
    UserContext(api_url, api_token).store(data_dir / "context.yaml")
Example #14
0
File: cli.py Project: ReCodEx/cli
def takeover(data_dir:Path, api: ApiClient, user_id):
    """
    Log in as a different user
    """

    token = api.takeover(user_id)["accessToken"]
    UserContext(api.api_url, token).store(data_dir / "context.yaml")
Example #15
0
def search(api: ApiClient, context: UserContext, search_string, as_csv):
    """
    Search for a user
    """

    if as_csv:
        fieldnames = ['id', 'title_before', 'first_name', 'last_name', 'title_after', 'avatar_url']
        csv_writer = csv.DictWriter(sys.stdout, fieldnames=fieldnames)
        csv_writer.writeheader()

    instances_ids = api.get_user(context.user_id)["privateData"]["instancesIds"]
    for instance_id in instances_ids:
        for user in api.search_users(instance_id, search_string):
            if as_csv:
                csv_writer.writerow(format_user_csv(user))
            else:
                click.echo("{} {}".format(user["fullName"], user["id"]))
Example #16
0
def get(api: ApiClient, exercise_id, useJson):
    """
    Get exercise data and print it in JSON or Yaml.
    """
    exercise = api.get_exercise(exercise_id)
    if useJson:
        json.dump(exercise, sys.stdout, sort_keys=True, indent=4)
    else:
        yaml.dump(exercise, sys.stdout)
Example #17
0
def get_config(api: ApiClient, exercise_id, useJson):
    """
    Get exercise configuration in JSON (or possibly yaml) format
    """
    config = api.get_exercise_config(exercise_id)
    if useJson:
        json.dump(config, sys.stdout, sort_keys=True, indent=4)
    else:
        yaml.dump(config, sys.stdout)
Example #18
0
def get(api: ApiClient, user_id, useJson):
    """
    Get user data and print it in JSON or Yaml.
    """
    user = api.get_user(user_id)
    if useJson:
        json.dump(user, sys.stdout, sort_keys=True, indent=4)
    else:
        yaml.dump(user, sys.stdout)
Example #19
0
def init(data_dir: Path, api_url):
    """
    Set up the CLI with a token that you already own
    """

    api_token = click.prompt("API token")
    api = ApiClient(api_url, api_token)

    try:
        api.get_status()
    except:
        click.echo("API connection test failed", err=True)
        raise

    context = UserContext(api_url, api_token)

    data_dir.mkdir(parents=True, exist_ok=True)
    context.store(data_dir / "context.yaml")
Example #20
0
def register(api: ApiClient, context: UserContext, email, first_name, last_name, password, instance_id, join_group):
    """
    Register new user with local account
    """
    if instance_id is None:
        instances_ids = api.get_user(context.user_id)["privateData"]["instancesIds"]
        if len(instances_ids) != 1:
            click.echo("Instance ID is ambiguous. Provide explicit ID via --instance_id option.")
            return
        instance_id = instances_ids[0]

    res = api.register_user(instance_id, email, first_name, last_name, password)
    user_id = res['user']['id']
    click.echo("User {id} ({first_name} {last_name}, {email}) registered in instance {instance_id}".format(
        id=user_id, first_name=first_name, last_name=last_name, email=email, instance_id=instance_id))

    for group_id in join_group:
        api.group_add_student(group_id, user_id)
        click.echo("User {} joined group {}".format(user_id, group_id))
Example #21
0
def cli(ctx: click.Context):
    """
    ReCodEx CLI
    """

    config_dir = Path(appdirs.user_config_dir("recodex"))
    data_dir = Path(appdirs.user_data_dir("recodex"))

    context_path = data_dir / "context.yaml"
    user_context = UserContext.load(
        context_path) if context_path.exists() else UserContext()
    api_client = ApiClient(user_context.api_url, user_context.api_token)

    if user_context.api_token is not None and user_context.is_token_almost_expired(
    ) and not user_context.is_token_expired:
        user_context = user_context.replace_token(api_client.refresh_token())
        user_context.store(context_path)

    ctx.obj = ReCodExContext(api_client, config_dir, data_dir, user_context)
Example #22
0
def detail(api: ApiClient, group_id, useJson):
    """
    Read detailed data about given group
    """

    group = api.get_group(group_id)
    if useJson is True:
        json.dump(group, sys.stdout, sort_keys=True, indent=4)
    elif useJson is False:
        yaml.dump(group, sys.stdout)
Example #23
0
def all(api: ApiClient, useJson):
    """
    Return all groups (visible to the user)
    """

    groups = api.get_all_groups()
    if useJson is True:
        json.dump(groups, sys.stdout, sort_keys=True, indent=4)
    elif useJson is False:
        yaml.dump(groups, sys.stdout)
Example #24
0
def edit(api: ApiClient, id, name, gravatar):
    """
    Edit profile of a user
    """

    user = api.get_user(id)
    data = {
        "degreesAfterName": user['name']['degreesBeforeName'],
        "degreesBeforeName": user['name']['degreesAfterName'],
        "email": user["privateData"]["email"],
        "gravatarUrlEnabled": user['avatarUrl'] is not None,
    }

    if name is not None:
        data["firstName"] = name[0]
        data["lastName"] = name[1]

    if gravatar is not None:
        data["gravatarUrlEnabled"] = gravatar
    api.update_user(id, data)
Example #25
0
def list_all(api: ApiClient, useJson, offset, limit, order, locale):
    """
    List all exercises (as ID and name) in JSON, Yaml, or as a plain list.
    """
    exercises = api.get_exercises(offset, limit, order, locale)
    if useJson is True:
        json.dump(exercises, sys.stdout, sort_keys=True, indent=4)
    elif useJson is False:
        yaml.dump(exercises, sys.stdout)
    else:
        for exercise in exercises:
            click.echo("{}\t{}".format(exercise["id"], exercise["name"]))
Example #26
0
def assignments(api: ApiClient, group_id, useJson):
    """
    List all (regular) assignments of a group.
    """

    assignments = api.get_group_assignments(group_id)
    if useJson is True:
        json.dump(assignments, sys.stdout, sort_keys=True, indent=4)
    elif useJson is False:
        yaml.dump(assignments, sys.stdout)
    else:
        for assignment in assignments:
            click.echo("{} {}".format(assignment["id"], get_localized_name(assignment["localizedTexts"])))
Example #27
0
def students(api: ApiClient, group_id, useJson):
    """
    List all students of a group.
    """

    students = api.get_group_students(group_id)
    if useJson is True:
        json.dump(students, sys.stdout, sort_keys=True, indent=4)
    elif useJson is False:
        yaml.dump(students, sys.stdout)
    else:
        for student in students:
            click.echo("{} {}".format(student["id"], student["fullName"]))
Example #28
0
def get_ref_solution_evaluations(api: ApiClient, ref_solution_id, useJson):
    """
    Get reference solution evaluations and print them all in JSON or Yaml, or as a plain list.
    """
    evaluations = api.get_reference_solution_evaluations(ref_solution_id)
    if useJson is True:
        json.dump(evaluations, sys.stdout, sort_keys=True, indent=4)
    elif useJson is False:
        yaml.dump(evaluations, sys.stdout)
    else:
        for evaluation in evaluations:
            ts = int(evaluation["submittedAt"])
            date = datetime.utcfromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
            click.echo("{} {} {} {}".format(evaluation["id"],
                       evaluation["evaluationStatus"], evaluation["isCorrect"], date))
Example #29
0
def get_ref_solutions(api: ApiClient, exercise_id, useJson):
    """
    List all reference solutions of given exercise in JSON, Yaml, or as a plain list.
    """
    solutions = api.get_reference_solutions(exercise_id)
    if useJson is True:
        json.dump(solutions, sys.stdout, sort_keys=True, indent=4)
    elif useJson is False:
        yaml.dump(solutions, sys.stdout)
    else:
        for solution in solutions:
            ts = int(solution["solution"]["createdAt"])
            date = datetime.utcfromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
            click.echo("{} {} {} {}".format(solution["id"], solution["runtimeEnvironmentId"],
                                            date, solution["description"]))
Example #30
0
def get_comments(api: ApiClient, solution_id, useJson):
    """
    Get solution comments.
    """

    comments = api.get_solution_comments(solution_id)

    if useJson is True:
        json.dump(comments["comments"], sys.stdout, sort_keys=True, indent=4)
    elif useJson is False:
        yaml.dump(comments["comments"], sys.stdout)
    else:
        for comment in comments["comments"]:
            posted = datetime.datetime.fromtimestamp(comment["postedAt"]).strftime('%Y-%m-%d %H:%M:%S')
            click.echo("\n>>> {} at {} ({}) [{}]:".format(comment["user"]["name"], posted, "private" if comment["isPrivate"] else "public", comment["id"]))
            click.echo(comment["text"])
            click.echo("\n-----")