Пример #1
0
    def test_run_with_extended_students_syntax(self, platform_url, tmp_path):
        students_file = tmp_path / "students.yml"
        students_file.write_text(
            """
some-team:
    members: [simon]
other-team:
    members: [eve, alice]
        """.strip(),
            encoding=sys.getdefaultencoding(),
        )
        expected_teams = [
            plug.StudentTeam(name="some-team", members=["simon"]),
            plug.StudentTeam(name="other-team", members=["eve", "alice"]),
        ]

        funcs.run_repobee(
            f"{plug.cli.CoreCommand.teams.create} --base-url {platform_url} "
            f"--students-file {students_file}",
            plugins=[_repobee.ext.studentsyml],
        )

        assert sorted(funcs.get_student_teams(platform_url)) == sorted(
            expected_teams
        )
Пример #2
0
def _extract_groups(args: argparse.Namespace) -> List[plug.StudentTeam]:
    """Extract groups from args namespace.`

    Args:
        args: A namespace object.

    Returns:
        a list of student usernames, or None of neither `students` or
        `students_file` is in the namespace.
    """
    if "students" in args and args.students:
        students = [plug.StudentTeam(members=[s]) for s in args.students]
    elif "students_file" in args and args.students_file:
        students_file = pathlib.Path(args.students_file).resolve()
        if not students_file.is_file():
            raise exception.FileError(
                "'{!s}' is not a file".format(students_file))
        if not students_file.stat().st_size:
            raise exception.FileError("'{!s}' is empty".format(students_file))
        students = [
            plug.StudentTeam(members=[s for s in group.strip().split()])
            for group in students_file.read_text(
                encoding=sys.getdefaultencoding()).split(os.linesep)
            if group  # skip blank lines
        ]
    else:
        students = []

    return students
Пример #3
0
    def test_student_groups_parsed_correcly(
        self, empty_students_file, read_issue_mock, action, extra_args
    ):
        """Test that putting multiple students on the same line in the students
        file results in them being in the same group.
        """
        # arrange
        groupings = (
            ["study"],
            ["buddy", "shuddy"],
            ["grape"],
            ["cat", "dog", "mouse"],
        )
        expected_groups = sorted(
            plug.StudentTeam(members=group) for group in groupings
        )
        empty_students_file.write(
            os.linesep.join([" ".join(group) for group in groupings])
        )
        sys_args = [
            *action.as_name_tuple(),
            *BASE_ARGS,
            "--sf",
            str(empty_students_file),
            *extra_args,
        ]

        # act
        parsed_args, _ = _repobee.cli.parsing.handle_args(sys_args)

        # assert
        assert sorted(parsed_args.students) == expected_groups
Пример #4
0
def with_reviews(with_student_repos, tmpdir):
    assignment_name = assignment_names[1]
    expected_review_teams = [
        plug.StudentTeam(
            members=[],
            name=plug.generate_review_team_name(student_team_name,
                                                assignment_name),
        ) for student_team_name in STUDENT_TEAM_NAMES
    ]
    command = " ".join([
        *str(repobee_plug.cli.CoreCommand.reviews.assign).split(),
        *BASE_ARGS,
        "-a",
        assignment_name,
        *STUDENTS_ARG,
        "-n",
        "1",
    ])

    repobee_testhelpers.funcs.run_repobee(command,
                                          workdir=tmpdir,
                                          plugins=[_repobee.ext.gitlab])

    assert_on_groups(
        expected_review_teams,
        single_group_assertion=expected_num_members_group_assertion(1),
    )
    return (assignment_name, expected_review_teams)
Пример #5
0
    def test_setup_with_wrong_case_on_student(self, tmpdir):
        """User names are case insensitive on GitLab, and so setup should work
        fine even if the case of some character in a student's username is
        "incorrect".

        See https://github.com/repobee/repobee/issues/900
        """
        student = STUDENT_TEAMS[0].members[0]
        student_wrong_case = student.upper()
        assert (student !=
                student_wrong_case), "cases match, test is pointless :("

        command = " ".join([
            *repobee_plug.cli.CoreCommand.repos.setup.as_name_tuple(),
            *BASE_ARGS,
            *TEMPLATE_ORG_ARG,
            *MASTER_REPOS_ARG,
            "--students",
            student_wrong_case,
        ])

        run_repobee(command, workdir=tmpdir, plugins=[_repobee.ext.gitlab])

        assert_repos_exist([plug.StudentTeam(members=[student])],
                           assignment_names)
Пример #6
0
    def test_assign_one_review(self, with_student_repos, tmpdir):
        assignment_name = assignment_names[1]
        expected_review_teams = [
            plug.StudentTeam(
                members=[],
                name=plug.generate_review_team_name(student_team_name,
                                                    assignment_name),
            ) for student_team_name in STUDENT_TEAM_NAMES
        ]
        command = " ".join([
            *repobee_plug.cli.CoreCommand.reviews.assign.as_name_tuple(),
            *BASE_ARGS,
            "-a",
            assignment_name,
            *STUDENTS_ARG,
            "-n",
            "1",
        ])
        group_assertion = expected_num_members_group_assertion(
            expected_num_members=1)

        run_repobee(command, workdir=tmpdir, plugins=[_repobee.ext.gitlab])

        assert_on_groups(expected_review_teams,
                         single_group_assertion=group_assertion)
        assert_num_issues(STUDENT_TEAMS, [assignment_name], 1)
        assert_issues_exist(
            STUDENT_TEAMS,
            [assignment_name],
            _repobee.command.peer.DEFAULT_REVIEW_ISSUE,
            expected_num_asignees=1,
        )
Пример #7
0
def with_reviews(with_student_repos):
    assignment_name = assignment_names[1]
    expected_review_teams = [
        plug.StudentTeam(
            members=[],
            name=plug.generate_review_team_name(
                student_team_name, assignment_name
            ),
        )
        for student_team_name in STUDENT_TEAM_NAMES
    ]
    command = " ".join(
        [
            REPOBEE_GITLAB,
            *str(repobee_plug.cli.CoreCommand.reviews.assign).split(),
            *BASE_ARGS,
            "-a",
            assignment_name,
            *STUDENTS_ARG,
            "-n",
            "1",
        ]
    )

    result = run_in_docker(command)

    assert result.returncode == 0
    assert_on_groups(
        expected_review_teams,
        single_group_assertion=expected_num_members_group_assertion(1),
    )
    return (assignment_name, expected_review_teams)
Пример #8
0
def _post_setup(
    pts: List[PushSpec], newly_created: bool, api: plug.PlatformAPI
) -> Mapping[Any, Any]:
    teams_and_repos = [
        (pt.metadata["team"], pt.metadata["repo"]) for pt in pts
    ]
    student_repos = [
        plug.StudentRepo(
            name=repo.name,
            url=repo.url,
            team=plug.StudentTeam(name=team.name, members=team.members),
        )
        for team, repo in teams_and_repos
    ]
    student_repos_iter = plug.cli.io.progress_bar(
        student_repos, desc="Executing post_setup hooks"
    )

    return plugin.execute_tasks(
        student_repos_iter,
        plug.manager.hook.post_setup,
        api,
        cwd=None,
        copy_repos=False,
        extra_kwargs=dict(newly_created=newly_created),
    )
Пример #9
0
def _execute_post_setup_hook(push_tuples: List[git.Push],
                             api: plug.PlatformAPI) -> Mapping[Any, Any]:
    """Execute the post_setup hook on the given push tuples. Note that the push
    tuples are expected to have the "team" and "repo" keys set in the metadata.
    """
    post_setup_exists = any(
        ["post_setup" in dir(p) for p in plug.manager.get_plugins()])
    if not post_setup_exists or not push_tuples:
        return {}

    teams_and_repos = [(pt.metadata["team"], pt.metadata["repo"])
                       for pt in push_tuples]
    student_repos = [
        plug.StudentRepo(
            name=repo.name,
            url=repo.url,
            team=plug.StudentTeam(members=team.members),
        ) for team, repo in teams_and_repos
    ]
    student_repos_iter = plug.cli.io.progress_bar(
        student_repos, desc="Executing post_setup hooks")

    return plugin.execute_tasks(
        student_repos_iter,
        plug.manager.hook.post_setup,
        api,
        cwd=None,
        copy_repos=False,
    )
Пример #10
0
    def test_assign_one_review(self, with_student_repos, extra_args):
        assignment_name = assignment_names[1]
        expected_review_teams = [
            plug.StudentTeam(
                members=[],
                name=plug.generate_review_team_name(student_team_name,
                                                    assignment_name),
            ) for student_team_name in STUDENT_TEAM_NAMES
        ]
        command = " ".join([
            REPOBEE_GITLAB,
            *repobee_plug.cli.CoreCommand.reviews.assign.as_name_tuple(),
            *BASE_ARGS,
            "-a",
            assignment_name,
            *STUDENTS_ARG,
            "-n",
            "1",
        ])
        group_assertion = expected_num_members_group_assertion(
            expected_num_members=1)

        result = run_in_docker_with_coverage(command, extra_args=extra_args)

        assert result.returncode == 0
        assert_on_groups(expected_review_teams,
                         single_group_assertion=group_assertion)
        assert_num_issues(STUDENT_TEAMS, [assignment_name], 1)
        assert_issues_exist(
            STUDENT_TEAMS,
            [assignment_name],
            _repobee.command.peer.DEFAULT_REVIEW_ISSUE,
            expected_num_asignees=1,
        )
Пример #11
0
def _extract_groups(args: argparse.Namespace) -> List[plug.StudentTeam]:
    """Extract groups from args namespace.`

    Args:
        args: A namespace object.

    Returns:
        a list of student usernames, or None of neither `students` or
        `students_file` is in the namespace.
    """
    if "students" in args and args.students:
        students = [plug.StudentTeam(members=[s]) for s in args.students]
    elif "students_file" in args and args.students_file:
        students_file = pathlib.Path(args.students_file).resolve()
        if not students_file.is_file():
            raise exception.FileError(f"'{students_file}' is not a file")
        if not students_file.stat().st_size:
            raise exception.FileError(f"'{students_file}' is empty")

        students = list(
            plug.manager.hook.parse_students_file(students_file=students_file))
    else:
        students = []

    return students
Пример #12
0
def parse_students_file(
    students_file: pathlib.Path, ) -> Iterable[plug.StudentTeam]:
    return [
        plug.StudentTeam(members=[s for s in group.strip().split()])
        for group in students_file.read_text(
            encoding=sys.getdefaultencoding()).split(os.linesep)
        if group  # skip blank lines
    ]
Пример #13
0
    def test_setup_with_token_owner_as_student(self, tmpdir):
        """Setting up with the token owner as a student should not cause
        a crash (see #812)
        """
        command = " ".join([
            *repobee_plug.cli.CoreCommand.repos.setup.as_name_tuple(),
            *BASE_ARGS,
            *TEMPLATE_ORG_ARG,
            *MASTER_REPOS_ARG,
            "--students",
            TEACHER,
        ])

        run_repobee(command, workdir=tmpdir, plugins=[_repobee.ext.gitlab])
        assert_repos_exist([plug.StudentTeam(members=[TEACHER])],
                           assignment_names)
Пример #14
0
    def test_setup_with_token_owner_as_student(self, extra_args):
        """Setting up with the token owner as a student should not cause
        a crash (see #812)
        """
        command = " ".join([
            REPOBEE_GITLAB,
            *repobee_plug.cli.CoreCommand.repos.setup.as_name_tuple(),
            *BASE_ARGS,
            *TEMPLATE_ORG_ARG,
            *MASTER_REPOS_ARG,
            "--students",
            TEACHER,
        ])

        result = run_in_docker_with_coverage(command, extra_args=extra_args)
        assert result.returncode == 0
        assert_repos_exist([plug.StudentTeam(members=[TEACHER])],
                           assignment_names)
Пример #15
0
def get_student_teams(
        platform_url: str,
        org_name: str = const.TARGET_ORG_NAME) -> List[plug.StudentTeam]:
    """Like :py:func:`get_platform_teams`, but converts each team to a
    :py:class:`~repobee_plug.StudentTeam`. for easier comparison.

    Args:
        platform_url: URL to the directory used by the
            :py:class:`fakeapi.FakeAPI`.
        org_name: The organization to get repos from.
    Returns:
        A list of student teams.
    """
    api = get_api(platform_url, org_name=org_name)
    return [
        plug.StudentTeam(members=[usr.username for usr in team.members])
        for team in api._teams[org_name].values()
    ]
Пример #16
0
def _clone_to_student_repos(
    team_repo_tuples: List[Tuple[plug.Team, List[plug.Repo]]],
    workdir: pathlib.Path,
    clone_dir: pathlib.Path,
    api: plug.PlatformAPI,
) -> List[plug.StudentRepo]:
    student_repos = [
        plug.StudentRepo(
            name=repo.name,
            team=plug.StudentTeam(name=team.name, members=list(team.members)),
            url=repo.url,
            _path=workdir / team.name / repo.name,
        ) for team, repos in team_repo_tuples for repo in repos
    ]
    list(
        _repobee.git.clone_student_repos(student_repos,
                                         clone_dir,
                                         update_local=False,
                                         api=api))
    return student_repos
Пример #17
0
    def test_setup_with_twice_with_wrong_case_on_second_setup(self, tmpdir):
        """User names on GitLab are case insensitive, and so setting up repos
        for the same student with two different cases of characters should work
        the same as setting up just once

        See
        https://github.com/repobee/repobee/issues/900#issuecomment-830075510
        for a bug where this was not the case.
        """
        student = STUDENT_TEAMS[0].members[0]
        student_lowercase = STUDENT_TEAMS[0].members[0].lower()
        # the original should be lowercase
        assert (student == student_lowercase
                ), "expected real student username to be lowercase"

        student_uppercase = student_lowercase.upper()
        base_command = [
            *repobee_plug.cli.CoreCommand.repos.setup.as_name_tuple(),
            *BASE_ARGS,
            *TEMPLATE_ORG_ARG,
            *MASTER_REPOS_ARG,
            "--students",
        ]

        def _cmd(student):
            return " ".join(base_command + [student])

        run_repobee(
            _cmd(student_lowercase),
            workdir=tmpdir,
            plugins=[_repobee.ext.gitlab],
        )
        run_repobee(
            _cmd(student_uppercase),
            workdir=tmpdir,
            plugins=[_repobee.ext.gitlab],
        )

        assert_repos_exist([plug.StudentTeam(members=[student])],
                           assignment_names)
Пример #18
0
    def test_students_missing_from_grades_file_causes_crash(
            self, tmp_grades_file, mocked_hook_results):
        """Test that if a specified student is missing from the grades
        file, there is a crash.
        """
        missing_team = plug.StudentTeam(members=["randomdude"])
        args = argparse.Namespace(
            students=list(TEAMS) + [missing_team],
            hook_results_file="",  # don't care, read_results_file is mocked
            grades_file=tmp_grades_file,
            assignments="week-1 week-2 week-4 week-6".split(),
            edit_msg_file=str(tmp_grades_file.parent / "editmsg.txt"),
            teachers=list(TEACHERS),
            grade_specs=[PASS_GRADESPEC_FORMAT],
            allow_other_states=False,
        )

        with pytest.raises(_exception.FileError) as exc_info:
            csvgrades.callback(args=args)

        assert "student(s) {} missing from the grades file".format(
            missing_team.members[0]) in str(exc_info.value)
Пример #19
0
def assign_peer_reviews(
    assignment_names: Iterable[str],
    teams: Iterable[plug.StudentTeam],
    num_reviews: int,
    issue: Optional[plug.Issue],
    double_blind_key: Optional[str],
    api: plug.PlatformAPI,
) -> None:
    """Assign peer reviewers among the students to each student repo. Each
    student is assigned to review num_reviews repos, and consequently, each
    repo gets reviewed by num_reviews reviewers.

    In practice, each student repo has a review team generated (called
    <student-repo-name>-review), to which num_reviews _other_ students are
    assigned. The team itself is given pull-access to the student repo, so
    that reviewers can view code and open issues, but cannot modify the
    contents of the repo.

    Args:
        assignment_names: Names of assginments.
        teams: Team objects specifying student groups.
        num_reviews: Amount of reviews each student should perform
            (consequently, the amount of reviews of each repo)
        issue: An issue with review instructions to be opened in the considered
            repos.
        double_blind_key: If provided, use key to make double-blind review
            allocation.
        api: An implementation of :py:class:`repobee_plug.PlatformAPI` used to
            interface with the platform (e.g. GitHub or GitLab) instance.
    """
    issue = issue or DEFAULT_REVIEW_ISSUE
    expected_repo_names = set(plug.generate_repo_names(teams,
                                                       assignment_names))
    fetched_teams = progresswrappers.get_teams(teams,
                                               api,
                                               desc="Fetching teams and repos")
    team_repo_tuples = [(team, list(api.get_team_repos(team)))
                        for team in fetched_teams]
    fetched_repos = list(
        itertools.chain.from_iterable(repos for _, repos in team_repo_tuples))
    fetched_repo_dict = {r.name: r for r in fetched_repos}

    missing = expected_repo_names - fetched_repo_dict.keys()
    if missing:
        raise plug.NotFoundError(f"Can't find repos: {', '.join(missing)}")

    if double_blind_key:
        plug.log.info(f"Creating anonymous repos with key: {double_blind_key}")
        fetched_repo_dict = _create_anonymized_repos(
            [(team, _only_expected_repos(repos, expected_repo_names))
             for team, repos in team_repo_tuples],
            double_blind_key,
            api,
        )

    allocations_for_output = []
    for assignment_name in assignment_names:
        plug.echo("Allocating reviews")
        allocations = plug.manager.hook.generate_review_allocations(
            teams=teams, num_reviews=num_reviews)
        # adjust names of review teams
        review_team_specs, reviewed_team_names = list(
            zip(*[(
                plug.StudentTeam(
                    members=alloc.review_team.members,
                    name=_review_team_name(
                        alloc.reviewed_team,
                        assignment_name,
                        key=double_blind_key,
                    ),
                ),
                alloc.reviewed_team,
            ) for alloc in allocations]))

        review_teams = _repobee.command.teams.create_teams(
            review_team_specs, plug.TeamPermission.PULL, api)
        review_teams_progress = plug.cli.io.progress_bar(
            review_teams,
            desc="Creating review teams",
            total=len(review_team_specs),
        )

        for review_team, reviewed_team_name in zip(review_teams_progress,
                                                   reviewed_team_names):
            reviewed_repo = fetched_repo_dict[plug.generate_repo_name(
                reviewed_team_name, assignment_name)]

            review_teams_progress.write(  # type: ignore
                f"Assigning {' and '.join(review_team.members)} "
                f"to review {reviewed_repo.name}")
            api.assign_repo(review_team, reviewed_repo,
                            plug.TeamPermission.PULL)
            api.create_issue(
                issue.title,
                issue.body,
                reviewed_repo,
                # It's not possible to assign users with read-access in Gitea
                # FIXME redesign so Gitea does not require special handling
                assignees=review_team.members
                if not isinstance(api, _repobee.ext.gitea.GiteaAPI) else None,
            )

            allocations_for_output.append({
                "reviewed_repo": {
                    "name": reviewed_repo.name,
                    "url": reviewed_repo.url,
                },
                "review_team": {
                    "name": review_team.name,
                    "members": review_team.members,
                },
            })

        if featflags.is_feature_enabled(
                featflags.FeatureFlag.REPOBEE_4_REVIEW_COMMANDS):
            output = dict(allocations=allocations_for_output,
                          num_reviews=num_reviews)
            pathlib.Path("review_allocations.json").write_text(
                json.dumps(output, indent=4),
                encoding=sys.getdefaultencoding(),
            )
Пример #20
0
    DIR_PATHS_WITH_SPACES.exists()), "test pre-reference error, dir must exit"
assert ABSTRACT_TEST_REPO.exists(), "test pre-reference error, dir must exit"

JUNIT_PATH = str(envvars.JUNIT_PATH)
HAMCREST_PATH = str(envvars.HAMCREST_PATH)

RTD = str(CUR_DIR / "reference-tests")
IGNORE_TESTS = ["FiboTest"]
CLASSPATH = "some-stuf:nice/path:path/to/unimportant/lib.jar"

CLASSPATH_WITH_JARS = CLASSPATH + ":{}:{}".format(JUNIT_PATH, HAMCREST_PATH)

NUM_PRIME_CHECKER_TESTS = 3
NUM_FIBO_TESTS = 2

DUMMY_TEAM = plug.StudentTeam(members=[], name="dummy")


def setup_hooks(
    reference_tests_dir=RTD,
    assignments=ASSIGNMENT_NAMES,
    ignore_tests=[],
    hamcrest_path=HAMCREST_PATH,
    junit_path=JUNIT_PATH,
    verbose=False,
    very_verbose=False,
    disable_security=False,
    run_student_tests=False,
    timeout=10,
):
    """Return an instance of JUnit4Hooks with pre-configured arguments."""
Пример #21
0
    "docker-compose down",
    "docker network rm development",
    f"rm -rf {str(DOCKER_VOLUME)}",
    f"git checkout {DOCKER_VOLUME}",
]

TEACHER_USER = "******"
TEACHER_TOKEN = ((CURRENT_DIR /
                  "teacher_token.txt").read_text(encoding="utf8").strip())

ADMIN_USER = "******"
ADMIN_TOKEN = ((CURRENT_DIR /
                "admin_token.txt").read_text(encoding="utf8").strip())

STUDENT_TEAMS = [
    plug.StudentTeam(members=line.strip().split())
    for line in (CURRENT_DIR /
                 "students.txt").read_text("utf8").strip().split("\n")
]


def main(args: List[str]) -> None:
    def _usage():
        print("usage: python giteamanager.py <prime|setup|teardown>")
        sys.exit()

    if len(args) != 2:
        _usage()

    cmd = args[1]
    if cmd == "prime":
Пример #22
0
import argparse
import sys
import pathlib
import random
from unittest import mock

import pytest
from _repobee import plugin
import repobee_plug as plug

from repobee_feedback import feedback

ASSIGNMENT_NAMES = ("task-1", "task-2")
STUDENT_TEAMS = tuple([
    plug.StudentTeam(members=members)
    for members in (["slarse"], ["glassey"], ["grundb", "glennol"])
])
STUDENT_TEAM_NAMES = tuple(map(str, STUDENT_TEAMS))

PASS_ISSUE = plug.Issue(title="Pass", body="Well done!\nAbsolutely flawless!")
KOMP_ISSUE = plug.Issue(title="Komplettering",
                        body="Not perfect, you need to fix this.")
FAIL_ISSUE = plug.Issue(title="Fail",
                        body="Unfortunately, there are severe errors.")
ISSUES = (PASS_ISSUE, KOMP_ISSUE, FAIL_ISSUE)

random.seed(512)


def _write_issue(issue: plug.Issue, path: pathlib.Path):
    text = "{}\n{}".format(issue.title, issue.body)
Пример #23
0
import pathlib

import repobee_plug as plug

from repobee_testhelpers._internal.templates import TEMPLATE_REPOS_DIR

DIR = pathlib.Path(__file__).resolve().parent
TOKEN = (DIR.parent / "token").read_text(encoding="utf-8").strip()
ADMIN_TOKEN = "".join(reversed(TOKEN))
OAUTH_USER = "******"
BASE_DOMAIN = "localhost:3000"
BASE_URL = "https://" + BASE_DOMAIN
ORG_NAME = "dd1337-fall2020"
TEMPLATE_ORG_NAME = "dd1337-master"
TEACHER = "ric"
assignment_names = [p.name for p in TEMPLATE_REPOS_DIR.iterdir() if p.is_dir()]
TEMPLATE_REPO_PATHS = list(dir_.absolute()
                           for dir_ in TEMPLATE_REPOS_DIR.iterdir()
                           if dir_.is_dir())
STUDENT_TEAMS = [
    plug.StudentTeam(members=[s.strip()])
    for s in (DIR.parent / "students.txt").read_text().strip().split("\n")
]
STUDENT_TEAM_NAMES = [str(t) for t in STUDENT_TEAMS]
STUDENT_REPO_NAMES = plug.generate_repo_names(STUDENT_TEAMS, assignment_names)
BASE_ARGS_NO_TB = ["--bu", BASE_URL, "-o", ORG_NAME, "-t", TOKEN]
BASE_ARGS = [*BASE_ARGS_NO_TB, "--tb"]
STUDENTS_ARG = ["-s", " ".join(STUDENT_TEAM_NAMES)]
MASTER_REPOS_ARG = ["-a", " ".join(assignment_names)]
TEMPLATE_ORG_ARG = ["--template-org-name", TEMPLATE_ORG_NAME]
Пример #24
0
 def merge_teams(teams):
     members = list(
         itertools.chain.from_iterable([team.members for team in teams])
     )
     return plug.StudentTeam(members=members)
Пример #25
0
def _to_student_team(name: str, data: dict) -> plug.StudentTeam:
    if _MEMBERS_KEY not in data:
        raise plug.PlugError(f"Missing members mapping for '{name}'")
    return plug.StudentTeam(name=name, members=data[_MEMBERS_KEY])
Пример #26
0
def assign_peer_reviews(
    assignment_names: Iterable[str],
    teams: Iterable[plug.StudentTeam],
    num_reviews: int,
    issue: Optional[plug.Issue],
    api: plug.PlatformAPI,
) -> None:
    """Assign peer reviewers among the students to each student repo. Each
    student is assigned to review num_reviews repos, and consequently, each
    repo gets reviewed by num_reviews reviewers.

    In practice, each student repo has a review team generated (called
    <student-repo-name>-review), to which num_reviews _other_ students are
    assigned. The team itself is given pull-access to the student repo, so
    that reviewers can view code and open issues, but cannot modify the
    contents of the repo.

    Args:
        assignment_names: Names of assginments.
        teams: Team objects specifying student groups.
        num_reviews: Amount of reviews each student should perform
            (consequently, the amount of reviews of each repo)
        issue: An issue with review instructions to be opened in the considered
            repos.
        api: An implementation of :py:class:`repobee_plug.PlatformAPI` used to
            interface with the platform (e.g. GitHub or GitLab) instance.
    """
    issue = issue or DEFAULT_REVIEW_ISSUE
    expected_repo_names = plug.generate_repo_names(teams, assignment_names)
    fetched_teams = progresswrappers.get_teams(teams,
                                               api,
                                               desc="Fetching teams and repos")
    fetched_repos = list(
        itertools.chain.from_iterable(map(api.get_team_repos, fetched_teams)))
    fetched_repo_dict = {r.name: r for r in fetched_repos}

    missing = set(expected_repo_names) - set(fetched_repo_dict.keys())
    if missing:
        raise plug.NotFoundError(f"Can't find repos: {', '.join(missing)}")

    for assignment_name in assignment_names:
        plug.echo("Allocating reviews")
        allocations = plug.manager.hook.generate_review_allocations(
            teams=teams, num_reviews=num_reviews)
        # adjust names of review teams
        review_team_specs, reviewed_team_names = list(
            zip(*[(
                plug.StudentTeam(
                    members=alloc.review_team.members,
                    name=plug.generate_review_team_name(
                        str(alloc.reviewed_team), assignment_name),
                ),
                alloc.reviewed_team,
            ) for alloc in allocations]))

        review_teams = _repobee.command.teams.create_teams(
            review_team_specs, plug.TeamPermission.PULL, api)
        review_teams_progress = plug.cli.io.progress_bar(
            review_teams,
            desc="Creating review teams",
            total=len(review_team_specs),
        )

        for review_team, reviewed_team_name in zip(review_teams_progress,
                                                   reviewed_team_names):
            reviewed_repo = fetched_repo_dict[plug.generate_repo_name(
                reviewed_team_name, assignment_name)]
            review_teams_progress.write(  # type: ignore
                f"Assigning {' and '.join(review_team.members)} "
                f"to review {reviewed_repo.name}")
            api.assign_repo(review_team, reviewed_repo,
                            plug.TeamPermission.PULL)
            api.create_issue(
                issue.title,
                issue.body,
                reviewed_repo,
                assignees=review_team.members,
            )
Пример #27
0
def review_student_teams():
    return [
        plug.StudentTeam(members=[student])
        for student in ("ham", "spam", "bacon", "eggs")
    ]
Пример #28
0
"""Module for constants used throughout the test suite."""
import string
import collections
from datetime import datetime
from itertools import permutations

import repobee_plug as plug

USER = "******"
ORG_NAME = "test-org"
TEMPLATE_ORG_NAME = "test-master-org"
HOST_URL = "https://some_enterprise_host"
BASE_URL = "{}/api/v3".format(HOST_URL)

# 5! = 120 different students
STUDENTS = tuple(
    plug.StudentTeam(members=["".join(perm)])
    for perm in permutations(string.ascii_lowercase[:5]))
ISSUE_PATH = "some/issue/path"
ISSUE = plug.Issue(title="Best title", body="This is the body of the issue.")
PLUGINS = ["javac", "pylint"]
TOKEN = "besttoken1337"
CONFIG_TOKEN = "bestconfigtoken"
FIXED_DATETIME = datetime(2009, 11, 22)

User = collections.namedtuple("User", ("login", ))
Пример #29
0
 def test_constructor_lowercases_member_names(self):
     members = ["siMON", "alIce", "EVE"]
     members_lowercase = ["simon", "alice", "eve"]
     team = plug.StudentTeam(members=members)
     assert team.members == members_lowercase