コード例 #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"])
コード例 #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"])
コード例 #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")
コード例 #4
0
ファイル: cli.py プロジェクト: 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)
コード例 #5
0
ファイル: cli.py プロジェクト: 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))
コード例 #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)
コード例 #7
0
ファイル: cli.py プロジェクト: 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)
コード例 #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)
コード例 #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)))
コード例 #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)
コード例 #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)))
コード例 #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)
コード例 #13
0
ファイル: cli.py プロジェクト: 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")
コード例 #14
0
ファイル: cli.py プロジェクト: 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")
コード例 #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"]))
コード例 #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)
コード例 #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)
コード例 #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)
コード例 #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")
コード例 #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))
コード例 #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)
コード例 #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)
コード例 #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)
コード例 #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)
コード例 #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"]))
コード例 #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"])))
コード例 #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"]))
コード例 #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))
コード例 #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"]))
コード例 #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-----")