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)
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, )
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, )
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))
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)
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)
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
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)
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))
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)
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}")
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, )