示例#1
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)
示例#2
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,
        )
示例#3
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,
        )
示例#4
0
def check_peer_review_progress(
    assignment_names: Iterable[str],
    teams: Iterable[plug.Team],
    title_regex: str,
    num_reviews: int,
    api: plug.PlatformAPI,
) -> None:
    """Check which teams have opened peer review issues in their allotted
    review repos

    Args:
        assignment_names: Names of assignments.
        teams: An iterable of student teams.
        title_regex: A regex to match against issue titles.
        num_reviews: Amount of reviews each student is expected to have made.
        api: An implementation of :py:class:`repobee_plug.PlatformAPI` used to
            interface with the platform (e.g. GitHub or GitLab) instance.

    """
    teams = list(teams)
    reviews = collections.defaultdict(list)

    review_team_names = [
        plug.generate_review_team_name(team, assignment_name) for team in teams
        for assignment_name in assignment_names
    ]

    review_teams = progresswrappers.get_teams(review_team_names,
                                              api,
                                              desc="Processing review teams")
    for review_team in review_teams:
        repos = list(api.get_team_repos(review_team))
        if len(repos) != 1:
            plug.log.warning(
                f"Expected {review_team.name} to have 1 associated "
                f"repo, found {len(repos)}. "
                f"Skipping...")
            continue

        reviewed_repo = repos[0]
        expected_reviewers = set(review_team.members)
        reviewing_teams = _extract_reviewing_teams(teams, expected_reviewers)

        review_issue_authors = {
            issue.author
            for issue in api.get_repo_issues(reviewed_repo)
            if re.match(title_regex, issue.title)
        }

        for team in reviewing_teams:
            reviews[str(team)].append(
                plug.Review(
                    repo=reviewed_repo.name,
                    done=any(
                        map(review_issue_authors.__contains__, team.members)),
                ))

    plug.echo(
        formatters.format_peer_review_progress_output(
            reviews, [team.name for team in teams], num_reviews))
示例#5
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)
示例#6
0
def _review_team_name(
    team: Union[str, plug.Team, plug.StudentTeam],
    assignment: str,
    key: Optional[str],
) -> str:
    if key:
        return _hash_if_key(str(team), key)
    else:
        return plug.generate_review_team_name(team, assignment)
示例#7
0
def review_teams(review_student_teams):
    master_repo = "week-1"
    review_teams = {}
    for i, student_team in enumerate(review_student_teams):
        review_teams[plug.generate_review_team_name(
            student_team, master_repo)] = itertools.chain.from_iterable(
                team.members for team in review_student_teams[:i] +
                review_student_teams[i + 1:])
    return review_teams
示例#8
0
    def test_end(self, platform_dir, platform_url, with_student_repos):
        review_team_names = {
            plug.generate_review_team_name(team, template_repo_name)
            for team in const.STUDENT_TEAMS
            for template_repo_name in const.TEMPLATE_REPO_NAMES
        }
        funcs.run_repobee(f"reviews assign -n 1 --base-url {platform_url} "
                          f"--assignments {const.TEMPLATE_REPOS_ARG}")

        funcs.run_repobee(f"reviews end --base-url {platform_url} "
                          f"--assignments {const.TEMPLATE_REPOS_ARG}")

        platform_teams = funcs.get_teams(platform_url, const.TARGET_ORG_NAME)
        review_teams = [
            team for team in platform_teams if team.name in review_team_names
        ]

        assert not review_teams
        assert len(platform_teams) == len(const.STUDENT_TEAMS)
示例#9
0
    def test_assign_single_review_creates_review_teams(self, platform_dir,
                                                       platform_url,
                                                       with_student_repos):
        expected_review_team_names = {
            plug.generate_review_team_name(team, template_repo_name)
            for team in const.STUDENT_TEAMS
            for template_repo_name in const.TEMPLATE_REPO_NAMES
        }

        funcs.run_repobee(f"reviews assign -n 1 --base-url {platform_url} "
                          f"--assignments {const.TEMPLATE_REPOS_ARG}")

        review_teams = [
            team
            for team in funcs.get_teams(platform_url, const.TARGET_ORG_NAME)
            if team.name in expected_review_team_names
        ]

        assert {t.name for t in review_teams} == expected_review_team_names
        assert all(map(lambda t: len(t.members) == 1, review_teams))
示例#10
0
    def test_end_with_allocations_file(
        self,
        platform_url,
        with_student_repos,
        tmp_path,
        activate_review_command_preview,
    ):
        """Test the RepoBee 4 version of `reviews end`, that just takes an
        allocations file.
        """
        # arrange
        workdir = tmp_path / "workdir"
        workdir.mkdir(exist_ok=False)
        alloc_file = workdir / "review_allocations.json"

        funcs.run_repobee(
            f"{plug.cli.CoreCommand.reviews.assign} "
            f"--base-url {platform_url} "
            f"-n 1 "
            f"--assignments {const.TEMPLATE_REPOS_ARG}",
            workdir=workdir,
        )

        # act
        funcs.run_repobee(
            f"{plug.cli.CoreCommand.reviews.end} --base-url {platform_url} "
            f"--allocations-file {alloc_file}",
            workdir=workdir,
        )

        # assert
        api = funcs.get_api(platform_url)
        review_team_names = {
            plug.generate_review_team_name(team, template_repo_name)
            for team in const.STUDENT_TEAMS
            for template_repo_name in const.TEMPLATE_REPO_NAMES
        }

        existing_team_names = {team.name for team in api.get_teams()}
        assert not existing_team_names.intersection(review_team_names)
示例#11
0
def purge_review_teams(
    assignment_names: Iterable[str],
    students: Iterable[plug.StudentTeam],
    api: plug.PlatformAPI,
) -> None:
    """Delete all review teams associated with the given assignment names and
    student teams.

    Args:
        assignment_names: Names of assignments.
        students: An iterble of student teams.
        api: An implementation of :py:class:`repobee_plug.PlatformAPI` used to
            interface with the platform (e.g. GitHub or GitLab) instance.
    """
    review_team_names = [
        plug.generate_review_team_name(student, assignment_name)
        for student in students for assignment_name in assignment_names
    ]
    teams = progresswrappers.get_teams(review_team_names,
                                       api,
                                       desc="Deleting review teams")
    for team in teams:
        api.delete_team(team)
        plug.log.info(f"Deleted {team.name}")
示例#12
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,
            )