示例#1
0
def _convert_404_to_not_found_error(msg):
    try:
        yield
    except gitlab.exceptions.GitlabError as exc:
        if exc.response_code == 404:
            raise plug.NotFoundError(msg)
        raise plug.PlatformError(str(exc), status=exc.response_code) from exc
示例#2
0
def _try_api_request(ignore_statuses: Optional[Iterable[int]] = None):
    """Context manager for trying API requests.

    Args:
        ignore_statuses: One or more status codes to ignore (only
        applicable if the exception is a gitlab.exceptions.GitlabError).
    """
    try:
        yield
    except gitlab.exceptions.GitlabError as e:
        if ignore_statuses and e.response_code in ignore_statuses:
            return

        if e.response_code == 404:
            raise plug.NotFoundError(str(e), status=404) from e
        elif e.response_code == 401:
            raise plug.BadCredentials(
                "credentials rejected, verify that token has correct access.",
                status=401,
            ) from e
        else:
            raise plug.PlatformError(str(e), status=e.response_code) from e
    except (exception.RepoBeeException, plug.PlugError):
        raise
    except Exception as e:
        raise plug.UnexpectedException(
            f"a {type(e).__name__} occured unexpectedly: {str(e)}") from e
示例#3
0
def _convert_404_to_not_found_error(msg):
    try:
        yield
    except gitlab.exceptions.GitlabError as exc:
        if exc.response_code == 404:
            raise plug.NotFoundError(msg)
        raise plug.UnexpectedException(
            f"An unexpected exception occured. {type(exc).__name__}: {exc}")
示例#4
0
    def get_repo(self, repo_name: str, team_name: Optional[str]) -> plug.Repo:
        """See :py:meth:`repobee_plug.PlatformAPI.get_repo`."""
        repos = (self._get_team(team_name).repos
                 if team_name else self._repos[self._org_name].values())
        for repo in repos:
            if repo.name == repo_name:
                return repo.to_plug_repo()

        raise plug.NotFoundError(f"{team_name} has no repository {repo_name}")
示例#5
0
    def _get_organization(self, org_name):
        matches = [
            g for g in self._gitlab.groups.list(search=org_name)
            if g.path == org_name
        ]

        if not matches:
            raise plug.NotFoundError(org_name, status=404)

        return matches[0]
示例#6
0
文件: github.py 项目: repobee/repobee
def _convert_404_to_not_found_error(msg):
    """Catch a github.GithubException with status 404 and convert to
    plug.NotFoundError with the provided message. If the GithubException
    does not have status 404, instead raise plug.UnexpectedException.
    """
    try:
        yield
    except github.GithubException as exc:
        if exc.status == 404:
            raise plug.NotFoundError(msg)
        raise plug.UnexpectedException(
            f"An unexpected exception occured. {type(exc).__name__}: {exc}")
示例#7
0
    def delete_repo(self, repo: plug.Repo) -> None:
        """See :py:meth:`repobee_plug.PlatformAPI.delete_repo`."""
        repo_bucket = self._repos.get(self._org_name, {})
        if repo.name not in repo_bucket:
            raise plug.NotFoundError(
                f"no such repo '{self._org_name}/{repo.name}'")

        repo_path = self._repodir / self._org_name / repo.name
        shutil.rmtree(repo_path)
        del repo_bucket[repo.name]
        for team in self._teams[self._org_name].values():
            try:
                team.repos.remove(repo.implementation)
            except KeyError:
                pass
示例#8
0
    def _verify_group(group_name: str, gl: gitlab.Gitlab) -> None:
        """Check that the group exists and that the user is an owner."""
        user = gl.user.username

        plug.echo(f"Trying to fetch group {group_name}")
        slug_matched = [
            group for group in gl.groups.list(search=group_name)
            if group.path == group_name
        ]
        if not slug_matched:
            raise plug.NotFoundError(
                f"Could not find group with slug {group_name}. Verify that "
                f"you have access to the group, and that you've provided "
                f"the slug (the name in the address bar).")
        group = slug_matched[0]
        plug.echo(f"SUCCESS: Found group {group.name}")

        plug.echo(
            f"Verifying that user {user} is an owner of group {group_name}")
        GitLabAPI._verify_membership(user, group)
示例#9
0
def _connect_to_api(base_url: str, token: str, org_name: str,
                    user: str) -> plug.PlatformAPI:
    """Return an API instance connected to the specified API endpoint."""
    required_args = plug.manager.hook.api_init_requires()
    kwargs = {}
    if "base_url" in required_args:
        kwargs["base_url"] = base_url
    if "token" in required_args:
        kwargs["token"] = token
    if "org_name" in required_args:
        kwargs["org_name"] = org_name
    if "user" in required_args:
        kwargs["user"] = user
    api_class = plug.manager.hook.get_api_class()
    try:
        return api_class(**kwargs)
    except plug.NotFoundError:
        # more informative message
        raise plug.NotFoundError("either organization {} could not be found, "
                                 "or the base url '{}' is incorrect".format(
                                     org_name, base_url))
示例#10
0
def _try_api_request(ignore_statuses: Optional[Iterable[int]] = None):
    """Context manager for trying API requests.

    Args:
        ignore_statuses: One or more status codes to ignore (only
        applicable if the exception is a github.GithubException).

    Raises:
        plug.NotFoundError
        plug.BadCredentials
        plug.PlatformError
        plug.ServiceNotFoundError
        plug.UnexpectedException
    """
    try:
        yield
    except plug.PlugError:
        raise
    except github.GithubException as e:
        if ignore_statuses and e.status in ignore_statuses:
            return

        if e.status == 404:
            raise plug.NotFoundError(str(e), status=404)
        elif e.status == 401:
            raise plug.BadCredentials(
                "credentials rejected, verify that token has correct access.",
                status=401,
            )
        else:
            raise plug.PlatformError(str(e), status=e.status)
    except gaierror:
        raise plug.ServiceNotFoundError(
            "GitHub service could not be found, check the url"
        )
    except Exception as e:
        raise plug.UnexpectedException(
            "a {} occured unexpectedly: {}".format(type(e).__name__, str(e))
        )
示例#11
0
 def _get_team(self, team_id: str) -> Team:
     if team_id not in self._teams[self._org_name]:
         raise plug.NotFoundError(f"invalid team id: {team_id}")
     return self._teams[self._org_name][team_id]
示例#12
0
 def _get_user(self, username: str) -> User:
     if username not in self._users:
         raise plug.NotFoundError(f"no such user: {username}")
     return self._users[username]
示例#13
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(),
            )
示例#14
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,
            )