Exemplo n.º 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
        )
Exemplo n.º 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
Exemplo n.º 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
Exemplo n.º 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)
Exemplo n.º 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)
Exemplo n.º 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,
        )
Exemplo n.º 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)
Exemplo n.º 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),
    )
Exemplo n.º 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,
    )
Exemplo n.º 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,
        )
Exemplo n.º 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
Exemplo n.º 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
    ]
Exemplo n.º 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)
Exemplo n.º 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)
Exemplo n.º 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()
    ]
Exemplo n.º 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
Exemplo n.º 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)
Exemplo n.º 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)
Exemplo n.º 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(),
            )
Exemplo n.º 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."""
Exemplo n.º 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":
Exemplo n.º 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)
Exemplo n.º 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]
Exemplo n.º 24
0
 def merge_teams(teams):
     members = list(
         itertools.chain.from_iterable([team.members for team in teams])
     )
     return plug.StudentTeam(members=members)
Exemplo n.º 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])
Exemplo n.º 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,
            )
Exemplo n.º 27
0
def review_student_teams():
    return [
        plug.StudentTeam(members=[student])
        for student in ("ham", "spam", "bacon", "eggs")
    ]
Exemplo n.º 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", ))
Exemplo n.º 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