From c7270dcc8125a42dda1e1a07547f68cd01ffc64d Mon Sep 17 00:00:00 2001 From: Adarsh S <4darshofficial@gmail.com> Date: Thu, 19 Jul 2018 17:38:02 +0530 Subject: [PATCH] Add commands to view & create challenge host & participant teams(#74) * Tests for host teams * Fix conflicts * Fix conflicts * Add team url validators * update validators --- evalai/teams.py | 56 +++++++++++++++---- evalai/utils/teams.py | 44 +++++++++++---- evalai/utils/urls.py | 2 + tests/data/__init__.py | 0 tests/data/teams_response.py | 61 +++++++++++++++++++- tests/test_requests.py | 44 ++++++++------- tests/test_teams.py | 105 ++++++++++++++++++++++++++++++----- 7 files changed, 253 insertions(+), 59 deletions(-) create mode 100644 tests/data/__init__.py diff --git a/evalai/teams.py b/evalai/teams.py index a15678e8f..4ef1dfd00 100644 --- a/evalai/teams.py +++ b/evalai/teams.py @@ -1,30 +1,62 @@ import click +import sys +import validators + +from click import echo, style from evalai.utils.teams import ( - create_participant_team, - display_participant_teams, + create_team, + display_teams, ) @click.group(invoke_without_command=True) @click.pass_context -def teams(ctx): +@click.option('--host', '-h', is_flag=True, + help="View your host teams.") +@click.option('--participant', '-p', is_flag=True, + help="View your host teams.") +def teams(ctx, host, participant): + """ + List all the participant/host teams of a user. + """ """ - List all the participant teams of a user. - Invoked by running `evalai teams` """ if ctx.invoked_subcommand is None: - display_participant_teams() + if host == participant: + echo("Sorry, wrong flag. Please pass either one of the flags " + "{} or {}.".format(style("--participant", bold=True, fg="yellow"), + style("--host", bold=True, fg="yellow"))) + sys.exit(1) + + display_teams(host) @teams.command() -def create(): +@click.argument('TEAM', type=str) +def create(team): + """ + Create a participant or host team. + """ """ - Create a participant team. - Invoked by running `evalai teams create` """ - team_name = click.prompt("Enter team name: ", type=str) - if click.confirm("Please confirm the team name - %s" % (team_name), abort=True): - create_participant_team(team_name) + is_host = False + if team not in ("host", "participant"): + echo("Sorry, wrong argument. Please choose either " + "{} or {}.".format(style("participant", bold=True, fg="yellow"), + style("host", bold=True, fg="yellow"))) + sys.exit(1) + + team_name = click.prompt("Enter team name", type=str) + if click.confirm("Please confirm the team name - {}".format(team_name), abort=True): + team_url = "" + if click.confirm("Do you want to enter the Team URL".format(team_name)): + team_url = click.prompt('Team URL', type=str) + while not (validators.url(team_url) or validators.domain(team_url)): + echo("Sorry, please enter a valid link.") + team_url = click.prompt('Team URL', type=str) + + is_host = (team == "host") + create_team(team_name, team_url, is_host) diff --git a/evalai/utils/teams.py b/evalai/utils/teams.py index d689e0403..7f89ab9f4 100644 --- a/evalai/utils/teams.py +++ b/evalai/utils/teams.py @@ -11,29 +11,40 @@ from evalai.utils.config import EVALAI_ERROR_CODES -def pretty_print_team_data(teams): +def pretty_print_team_data(teams, is_host): """ Function to print the team data """ table = BeautifulTable(max_width=200) attributes = ["id", "team_name", "created_by"] - columns_attributes = ["ID", "Team Name", "Created By", "Members"] + columns_attributes = ["ID", "Team Name", "Created By", "Members", "Team URL"] table.column_headers = columns_attributes for team in teams: values = list(map(lambda item: team[item], attributes)) - members = ", ".join(map(lambda member: member["member_name"], team["members"])) + if is_host: + members = ", ".join(map(lambda member: member["user"], team["members"])) + else: + members = ", ".join(map(lambda member: member["member_name"], team["members"])) values.append(members) + if team["team_url"]: + values.append(team["team_url"]) + else: + values.append("None") table.append_row(values) echo(table) -def display_participant_teams(): +def display_teams(is_host): """ - Function to display all the participant teams of a user + Function to display all the participant or host teams of a user """ + url = "{}{}" headers = get_request_header() + if is_host: + url = url.format(get_host_url(), URLS.host_team_list.value) + else: + url = url.format(get_host_url(), URLS.participant_team_list.value) - url = "{}{}".format(get_host_url(), URLS.participant_team_list.value) try: response = requests.get(url, headers=headers) response.raise_for_status() @@ -51,22 +62,29 @@ def display_participant_teams(): teams = response["results"] if len(teams) != 0: - pretty_print_team_data(teams) + pretty_print_team_data(teams, is_host) else: echo("Sorry, no teams found!") -def create_participant_team(team_name): +def create_team(team_name, team_url, is_host): """ Function to create a new team by taking in the team name as input. """ - url = "{}{}".format(get_host_url(), URLS.participant_team_list.value) + url = "{}{}" + + if is_host: + url = url.format(get_host_url(), URLS.create_host_team.value) + else: + url = url.format(get_host_url(), URLS.participant_team_list.value) headers = get_request_header() headers['Content-Type'] = 'application/json' data = {} data["team_name"] = team_name + if team_url: + data["team_url"] = team_url data = json.dumps(data) try: response = requests.post( @@ -92,8 +110,12 @@ def create_participant_team(team_name): if response.status_code == 201: response = response.json() - echo(style("\nThe team {} was successfully created.\n".format(response["team_name"]), - fg="green", bold=True)) + if is_host: + echo(style("\nYour host team {} was successfully created.\n".format(response["team_name"]), + fg="green", bold=True)) + else: + echo(style("\nYour participant team {} was successfully created.\n".format(response["team_name"]), + fg="green", bold=True)) def participate_in_a_challenge(challenge_id, participant_team_id): diff --git a/evalai/utils/urls.py b/evalai/utils/urls.py index 8df057e21..a887bcbc8 100644 --- a/evalai/utils/urls.py +++ b/evalai/utils/urls.py @@ -10,6 +10,8 @@ class URLS(Enum): host_teams = "/api/hosts/challenge_host_team/" host_challenges = "/api/challenges/challenge_host_team/{}/challenge" challenge_phase_split_detail = "/api/challenges/{}/challenge_phase_split" + create_host_team = "/api/hosts/create_challenge_host_team" + host_team_list = "/api/hosts/challenge_host_team/" participant_challenges = "/api/participants/participant_team/{}/challenge" participant_team_list = "/api/participants/participant_team" participate_in_a_challenge = "/api/challenges/challenge/{}/participant_team/{}" diff --git a/tests/data/__init__.py b/tests/data/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/data/teams_response.py b/tests/data/teams_response.py index a01009e4b..ee436b526 100644 --- a/tests/data/teams_response.py +++ b/tests/data/teams_response.py @@ -14,7 +14,8 @@ "status": "Self" } ], - "team_name": "OASIS" + "team_name": "OASIS", + "team_url": "" }, { "created_by": "admin", @@ -26,13 +27,69 @@ "status": "Self" } ], - "team_name": "ROSES" + "team_name": "ROSES", + "team_url": "" } ] } """ +host_teams = """ +{ + "count": 2, + "next": null, + "previous": null, + "results": [ + { + "created_by": "participant", + "id": 4, + "members": [ + { + "id": 4, + "permissions": "Admin", + "status": "Self", + "team_name": 4, + "user": "participant" + } + ], + "team_name": "Team1", + "team_url": "" + }, + { + "created_by": "participant", + "id": 5, + "members": [ + { + "id": 5, + "permissions": "Admin", + "status": "Self", + "team_name": 5, + "user": "participant" + } + ], + "team_name": "Team2", + "team_url": "" + }, + { + "created_by": "participant", + "id": 6, + "members": [ + { + "id": 6, + "permissions": "Admin", + "status": "Self", + "team_name": 6, + "user": "participant" + } + ], + "team_name": "Team3", + "team_url": "" + } + ] +} +""" + create_team = """ { "created_by": "admin", diff --git a/tests/test_requests.py b/tests/test_requests.py index 0343aa550..1925d54ba 100644 --- a/tests/test_requests.py +++ b/tests/test_requests.py @@ -132,18 +132,19 @@ def test_display_participant_and_host_challenge_lists_for_http_error_404(self): @responses.activate def test_display_participant_team_for_http_error_404(self): runner = CliRunner() - result = runner.invoke(teams) + result = runner.invoke(teams, ["--participant"]) response = result.output url = "{}{}".format(API_HOST_URL, URLS.participant_team_list.value) expected = "{}{}".format(self.expected.format(url), "\n") assert response == expected @responses.activate - def test_create_participant_team_for_http_error_404(self): - user_prompt_text = ("Enter team name: : TeamTest\n" - "Please confirm the team name - TeamTest [y/N]: y\n") + def test_create_team_for_http_error_404(self): + user_prompt_text = ("Enter team name: TeamTest\n" + "Please confirm the team name - TeamTest [y/N]: y\n" + "Do you want to enter the Team URL [y/N]: N\n") runner = CliRunner() - result = runner.invoke(teams, ['create'], input="TeamTest\ny\n") + result = runner.invoke(teams, ['create', 'participant'], input="TeamTest\ny\nN") response = result.output url = "{}{}".format(API_HOST_URL, URLS.participant_team_list.value) expected = "{}{}".format(self.expected.format(url), "\n") @@ -271,16 +272,17 @@ def setup(self): @responses.activate def test_display_participant_team_for_object_does_not_exist(self): runner = CliRunner() - result = runner.invoke(teams) + result = runner.invoke(teams, ["--participant"]) response = result.output.rstrip() assert response == self.expected @responses.activate - def test_create_participant_team_for_object_does_not_exist(self): - user_prompt_text = ("Enter team name: : TeamTest\n" - "Please confirm the team name - TeamTest [y/N]: y\n") + def test_create_team_for_object_does_not_exist(self): + user_prompt_text = ("Enter team name: TeamTest\n" + "Please confirm the team name - TeamTest [y/N]: y\n" + "Do you want to enter the Team URL [y/N]: N\n") runner = CliRunner() - result = runner.invoke(teams, ['create'], input="TeamTest\ny\n") + result = runner.invoke(teams, ['create', 'participant'], input="TeamTest\ny\nN") response = result.output.rstrip() expected = "{}{}".format(user_prompt_text, self.expected) assert response == expected @@ -305,10 +307,11 @@ def setup(self): @responses.activate def test_participate_in_a_challenge_for_team_name_exists(self): - user_prompt_text = ("Enter team name: : TeamTest\n" - "Please confirm the team name - TeamTest [y/N]: y\n") + user_prompt_text = ("Enter team name: TeamTest\n" + "Please confirm the team name - TeamTest [y/N]: y\n" + "Do you want to enter the Team URL [y/N]: N\n") runner = CliRunner() - result = runner.invoke(teams, ['create'], input="TeamTest\ny\n") + result = runner.invoke(teams, ['create', 'participant'], input="TeamTest\ny\nN") response = result.output.rstrip() expected = "Error: participant team with this team name already exists." expected = "{}{}".format(user_prompt_text, expected) @@ -424,10 +427,11 @@ def setup(self): # Teams URLS - responses.add(responses.GET, url.format(API_HOST_URL, URLS.participant_team_list.value), body=Exception('...')) + responses.add(responses.GET, url.format(API_HOST_URL, URLS.participant_team_list.value), + body=RequestException('...')) responses.add(responses.POST, url.format(API_HOST_URL, URLS.participant_team_list.value), - body=Exception('...')) + body=RequestException('...')) responses.add(responses.POST, url.format(API_HOST_URL, URLS.participate_in_a_challenge.value).format("2", "3"), body=RequestException('...')) @@ -503,16 +507,14 @@ def test_display_participant_and_host_challenge_lists_for_request_exception(self @responses.activate def test_display_participant_team_for_request_exception(self): runner = CliRunner() - result = runner.invoke(teams) + result = runner.invoke(teams, ["--participant"]) assert result.exit_code == -1 @responses.activate - def test_create_participant_team_for_request_exception(self): + def test_create_team_for_request_exception(self): runner = CliRunner() - result = runner.invoke(teams, ['create'], input="TeamTest\ny\n") - output = ("Enter team name: : TeamTest\n" - "Please confirm the team name - TeamTest [y/N]: y") - assert result.output.strip() == output + result = runner.invoke(teams, ['create', 'participant'], input="TeamTest\ny\nN") + assert result.exit_code == 1 @responses.activate def test_participate_in_a_challenge_for_request_exception(self): diff --git a/tests/test_teams.py b/tests/test_teams.py index 406abad6a..4ba237715 100644 --- a/tests/test_teams.py +++ b/tests/test_teams.py @@ -17,6 +17,7 @@ class TestTeams: def setup(self): team_list_data = json.loads(teams_response.teams_list) team_created_data = json.loads(teams_response.create_team) + host_team = json.loads(teams_response.host_teams) url = "{}{}" responses.add(responses.GET, url.format(API_HOST_URL, URLS.participant_team_list.value), @@ -25,48 +26,126 @@ def setup(self): responses.add(responses.POST, url.format(API_HOST_URL, URLS.participant_team_list.value), json=team_created_data, status=201) + responses.add(responses.POST, url.format(API_HOST_URL, URLS.create_host_team.value), + json=team_created_data, status=201) + responses.add(responses.POST, url.format(API_HOST_URL, URLS.participate_in_a_challenge.value).format("2", "3"), json=team_list_data, status=201) - self.teams = team_list_data["results"] + responses.add(responses.GET, url.format(API_HOST_URL, URLS.host_team_list.value), + json=host_team, status=201) + + self.participant_teams = team_list_data["results"] + self.host_teams = host_team["results"] @responses.activate - def test_teams_list(self): + def test_display_participant_teams_list(self): table = BeautifulTable(max_width=200) attributes = ["id", "team_name", "created_by"] - columns_attributes = ["ID", "Team Name", "Created By", "Members"] + columns_attributes = ["ID", "Team Name", "Created By", "Members", "Team URL"] table.column_headers = columns_attributes - for team in self.teams: + for team in self.participant_teams: values = list(map(lambda item: team[item], attributes)) members = ", ".join(map(lambda member: member["member_name"], team["members"])) values.append(members) + if team["team_url"]: + values.append(team["team_url"]) + else: + values.append("None") + table.append_row(values) + output = str(table) + runner = CliRunner() + result = runner.invoke(teams, ["--participant"]) + response = result.output.rstrip() + assert response == output + + @responses.activate + def test_display_host_teams_list(self): + table = BeautifulTable(max_width=200) + attributes = ["id", "team_name", "created_by"] + columns_attributes = ["ID", "Team Name", "Created By", "Members", "Team URL"] + table.column_headers = columns_attributes + for team in self.host_teams: + values = list(map(lambda item: team[item], attributes)) + members = ", ".join(map(lambda member: member["user"], team["members"])) + values.append(members) + if team["team_url"]: + values.append(team["team_url"]) + else: + values.append("None") table.append_row(values) output = str(table) runner = CliRunner() - result = runner.invoke(teams) + result = runner.invoke(teams, ["--host"]) response = result.output.rstrip() assert response == output @responses.activate def test_create_participant_team_for_option_yes(self): - output = ("Enter team name: : TeamTest\n" + output = ("Enter team name: TeamTest\n" "Please confirm the team name - TeamTest [y/N]: y\n" - "\nThe team TestTeam was successfully created.\n\n") + "Do you want to enter the Team URL [y/N]: N\n" + "\nYour participant team TestTeam was successfully created.") runner = CliRunner() - result = runner.invoke(teams, ['create'], input="TeamTest\ny\n") - response = result.output + result = runner.invoke(teams, ['create', 'participant'], input="TeamTest\ny\nN") + response = result.output.strip() assert response == output @responses.activate - def test_create_participant_team_for_option_no(self): - output = ("Enter team name: : TeamTest\n" + def test_create_host_team_for_option_yes(self): + output = ("Enter team name: TeamTest\n" + "Please confirm the team name - TeamTest [y/N]: y\n" + "Do you want to enter the Team URL [y/N]: N\n" + "\nYour host team TestTeam was successfully created.") + runner = CliRunner() + result = runner.invoke(teams, ['create', 'host'], input="TeamTest\ny\nN") + response = result.output.strip() + assert response == output + + @responses.activate + def test_create_team_for_option_no(self): + output = ("Enter team name: TeamTest\n" "Please confirm the team name - TeamTest [y/N]: n\n" "Aborted!\n") runner = CliRunner() - result = runner.invoke(teams, ['create'], input="TeamTest\nn\n") + result = runner.invoke(teams, ['create', 'host'], input="TeamTest\nn\n") response = result.output assert response == output + @responses.activate + def test_create_host_team_for_option_yes_with_valid_team_url(self): + output = ("Enter team name: TeamTest\n" + "Please confirm the team name - TeamTest [y/N]: y\n" + "Do you want to enter the Team URL [y/N]: Y\n" + "Team URL: http://testteam.com\n" + "\nYour host team TestTeam was successfully created.") + runner = CliRunner() + result = runner.invoke(teams, ['create', 'host'], input="TeamTest\ny\nY\nhttp://testteam.com\n") + response = result.output.strip() + assert response == output + + @responses.activate + def test_create_host_team_for_option_yes_with_invalid_team_url(self): + output = ("Enter team name: TeamTest\n" + "Please confirm the team name - TeamTest [y/N]: y\n" + "Do you want to enter the Team URL [y/N]: Y\n" + "Team URL: TestURL\n" + "Sorry, please enter a valid link.\n" + "Team URL: http://testteam.com\n" + "\nYour host team TestTeam was successfully created.") + runner = CliRunner() + result = runner.invoke(teams, ['create', 'host'], input="TeamTest\ny\nY\nTestURL\nhttp://testteam.com") + response = result.output.strip() + assert response == output + + @responses.activate + def test_create_team_for_wrong_argument(self): + output = ("Sorry, wrong argument. Please choose either participant or host.") + runner = CliRunner() + result = runner.invoke(teams, ['create', 'test']) + response = result.output.strip() + assert response == output + @responses.activate def test_participate_in_a_challenge(self): output = "Your team id {} is now participating in this challenge!\n".format("3") @@ -106,6 +185,6 @@ def setup(self): def test_teams_list_with_no_teams_data(self): expected = "Sorry, no teams found!\n" runner = CliRunner() - result = runner.invoke(teams) + result = runner.invoke(teams, ["--participant"]) response = result.output assert response == expected