Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #119: Add command & feature for Creating challenge. #149

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions evalai/challenges.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from evalai.utils.common import Date
from evalai.utils.challenges import (
create_challenge,
display_all_challenge_list,
display_future_challenge_list,
display_ongoing_challenge_list,
Expand Down Expand Up @@ -76,6 +77,19 @@ def challenge(ctx, challenge):
display_challenge_details(challenge)


@challenges.command(context_settings={"ignore_unknown_options": True})
@click.option("--file", type=click.File("rb"), required=True, help="Challenge Zip file.")
@click.argument("team", type=int)
def create(file, team):
"""
Create challenge with Zip file & HostTeam ID.
"""
"""
Invoked by running 'evalai challenges create --file FILE TEAM'
"""
create_challenge(file, team)


@challenges.command()
def ongoing():
"""
Expand Down
51 changes: 51 additions & 0 deletions evalai/utils/challenges.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,57 @@
requests.packages.urllib3.disable_warnings()


def create_challenge(file, team):
"""
Function to create a challenge.
"""
url = "{}{}".format(get_host_url(), URLS.create_challenge.value)
url = url.format(team)

headers = get_request_header()
file = {"zip_configuration": file}

try:
response = requests.post(url, headers=headers, files=file)
response.raise_for_status()
except requests.exceptions.HTTPError as err:
if response.status_code in EVALAI_ERROR_CODES:
validate_token(response.json())
echo(
style(
"\nError: {}\n"
"\nUse `evalai challenges` to fetch the active challenges.\n"
"\nUse `evalai challenge CHALLENGE phases` to fetch the "
"active phases.\n".format(response.json()["error"]),
fg="red",
bold=True,
)
)
else:
echo(err)
sys.exit(1)
except requests.exceptions.RequestException as err:
echo(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@KhalidRmb can we change this to a message like invalid host team id if the issue is with team id otherwise the request error message

Copy link
Member Author

@KhalidRmb KhalidRmb May 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will require modification in the API, since there's no code to send an apt response for invalid host id here: https://github.com/Cloud-CV/EvalAI/blob/d5f28ae941dae1e50e623a7b880fefbacea3ef4b/apps/challenges/views.py#L606
Based on it, we can modify the cli code.

style(
"\nCould not establish a connection to EvalAI."
" Please check the Host URL.\n",
bold=True,
fg="red",
)
)
sys.exit(1)
response = response.json()
echo(
style(
"\n{}\n".format(
response["success"]
),
fg="green",
bold=True,
)
)


def pretty_print_challenge_data(challenges):
"""
Function to print the challenge data
Expand Down
1 change: 1 addition & 0 deletions evalai/utils/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@


class URLS(Enum):
create_challenge = "/api/challenges/challenge/challenge_host_team/{}/zip_upload/"
login = "/api/auth/login"
challenge_list = "/api/challenges/challenge/all"
past_challenge_list = "/api/challenges/challenge/past"
Expand Down
6 changes: 6 additions & 0 deletions tests/data/challenge_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,3 +392,9 @@
]
}
"""

create_challenge_result = """
{
"success": "Challenge Challenge Title has been created successfully and sent for review to EvalAI Admin."
}
"""
Binary file added tests/data/test_zip_file.zip
Binary file not shown.
56 changes: 56 additions & 0 deletions tests/test_challenges.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
import os
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@KhalidRmb can you arrange the imports in lexicographic order

import responses

from beautifultable import BeautifulTable
Expand Down Expand Up @@ -971,3 +972,58 @@ def test_display_challenge_phase_split_list_with_string_argument(self):
result = runner.invoke(challenge, ["two", "participate", "3"])
response = result.output
assert response == output


class TestCreateChallenge(BaseTestClass):
def setup(self):
self.message = json.loads(challenge_response.create_challenge_result)

url = "{}{}"
responses.add(
responses.POST,
url.format(API_HOST_URL, URLS.create_challenge.value).format("4"),
json=self.message,
status=201,
)

@responses.activate
def test_create_challenge_when_file_is_not_valid(self):
expected = (
"Usage: challenges create [OPTIONS] TEAM\n"
'\nError: Invalid value for "--file": Could not open file: file: No such file or directory\n'
)
runner = CliRunner()
result = runner.invoke(
challenges, ["create", "--file", "file", "4"]
)
response = result.output
assert response == expected

@responses.activate
def test_create_challenge_when_file_and_id_are_valid(self):
expected = 'Challenge Challenge Title has been created successfully and sent for review to EvalAI Admin.'

runner = CliRunner()

my_path = os.path.abspath(os.path.dirname(__file__))
file = os.path.join(my_path, "data")

result = runner.invoke(
challenges, ["create", "--file", "{}/test_zip_file.zip".format(file), "4"]
)
assert result.output.strip() == expected

@responses.activate
def test_create_challenge_when_id_is_not_valid(self):
expected = ("Could not establish a connection to EvalAI."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@KhalidRmb not sure if this is the correct message for invalid challenge id, user should know the mistake is in challenge id. @RishabhJain2018 your input?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think he is referring to host team id. Let him confirm first.

Copy link
Member Author

@KhalidRmb KhalidRmb May 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am referring to the host team ID here. For a host id invalid message to be displayed, the API must send a message back saying why creation failed. Based on that we must modify the cli code. @RishabhJain2018 @Ram81

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I was referring to host team id

" Please check the Host URL.")

runner = CliRunner()

my_path = os.path.abspath(os.path.dirname(__file__))
file = os.path.join(my_path, "data")

result = runner.invoke(
challenges, ["create", "--file", "{}/test_zip_file.zip".format(file), "111"]
)
assert result.output.strip() == expected
53 changes: 53 additions & 0 deletions tests/test_requests.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
import os
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

import responses

from click.testing import CliRunner
Expand Down Expand Up @@ -320,6 +321,44 @@ def test_display_leaderboard_for_http_error_404(self):
assert response == expected


class TestCreateChallengeWhenZipFileDoesNotExist(BaseTestClass):
def setup(self):

response_data = """
{
"error": "The zip file contents cannot be extracted. Please check the format!"
}
"""
error_data = json.loads(response_data)
url = "{}{}"
responses.add(
responses.POST,
url.format(API_HOST_URL, URLS.create_challenge.value).format("4"),
json=error_data,
status=406,
)

@responses.activate
def test_create_challenge_when_zip_file_does_not_exist(self):

expected = ("\nError: The zip file contents cannot be extracted. Please check the format!\n"
"\nUse `evalai challenges` to fetch the active challenges.\n"
"\nUse `evalai challenge CHALLENGE phases` to fetch the "
"active phases.\n\n"
)
runner = CliRunner()

with runner.isolated_filesystem():
with open("test_file.txt", "w") as f:
f.write("1 2 3 4 5 6")

result = runner.invoke(
challenges, ["create", "--file", "test_file.txt", "4"],
)

assert result.output == expected


class TestSubmissionDetailsWhenObjectDoesNotExist(BaseTestClass):
def setup(self):

Expand Down Expand Up @@ -594,6 +633,12 @@ def setup(self):

# Challenge URLS

responses.add(
responses.POST,
url.format(API_HOST_URL, URLS.create_challenge.value),
body=RequestException("..."),
)

responses.add(
responses.GET,
url.format(API_HOST_URL, URLS.challenge_list.value),
Expand Down Expand Up @@ -721,6 +766,14 @@ def setup(self):
body=RequestException("..."),
)

@responses.activate
def test_create_challenge_for_request_exception(self):
runner = CliRunner()
my_path = os.path.abspath(os.path.dirname(__file__))
file = os.path.join(my_path, "data")
result = runner.invoke(challenges, ["create", "--file", "{}/test_zip_file.zip".format(file), "4"])
assert result.exit_code == 1

@responses.activate
def test_display_challenge_list_for_request_exception(self):
runner = CliRunner()
Expand Down