Skip to content

Commit

Permalink
Fix Cloud-CV#10: Add user authentication along with displaying challe…
Browse files Browse the repository at this point in the history
…nge details. (Cloud-CV#9)

* Add auth to the CLI app

* Add token through token file

* Define CLI optiions for challenges

* Add fetching challenge from the backend

* Add error handling

* Add tests for challenge commands

* Split display view into multiple functions

* Update README.md

* Update README.md

* Update README.md

* Update README.md
  • Loading branch information
guyandtheworld authored and RishabhJain2018 committed May 26, 2018
1 parent a96fc3f commit aa44574
Show file tree
Hide file tree
Showing 13 changed files with 332 additions and 36 deletions.
1 change: 1 addition & 0 deletions evalai/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
name = "evalai"
9 changes: 0 additions & 9 deletions evalai/auth.py

This file was deleted.

67 changes: 62 additions & 5 deletions evalai/challenges.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,66 @@
import click

from click import echo

from evalai.utils.challenges import (
get_challenge_list,
get_ongoing_challenge_list,
get_past_challenge_list,
get_future_challenge_list,)


@click.group(invoke_without_command=True)
@click.pass_context
def challenges(ctx):
"""
Challenges and related options.
"""
if ctx.invoked_subcommand is None:
welcome_text = """Welcome to the EvalAI CLI. Use evalai challenges --help for viewing all the options."""
echo(welcome_text)


@click.group(invoke_without_command=True, name='list')
@click.pass_context
def list_challenges(ctx):
"""
Used to list all the challenges.
Invoked by running `evalai challenges list`
"""
if ctx.invoked_subcommand is None:
get_challenge_list()


@click.command(name='ongoing')
def list_ongoing_challenges():
"""
Used to list all the challenges which are active.
Invoked by running `evalai challenges list ongoing`
"""
get_ongoing_challenge_list()


@click.command(name='past')
def list_past_challenges():
"""
Used to list all the past challenges.
Invoked by running `evalai challenges list past`
"""
get_past_challenge_list()


@click.command(name='future')
def list_future_challenges():
"""
Used to list all the challenges which are coming up.
Invoked by running `evalai challenges list future`
"""
get_future_challenge_list()


# Command -> evalai challenges list
challenges.add_command(list_challenges)

@click.command()
def challenges():
"""Example script."""
echo('Hello Challenges!')
# Command -> evalai challenges list ongoing/past/future
list_challenges.add_command(list_ongoing_challenges)
list_challenges.add_command(list_past_challenges)
list_challenges.add_command(list_future_challenges)
12 changes: 7 additions & 5 deletions evalai/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from click import echo

from .auth import auth
from .challenges import challenges
from .submissions import submissions
from .teams import teams
Expand All @@ -11,12 +10,15 @@
@click.group(invoke_without_command=True)
@click.pass_context
def main(ctx):
"""
Welcome to the EvalAI CLI.
"""
if ctx.invoked_subcommand is None:
echo('I was invoked without subcommand')
else:
echo('I am about to invoke %s' % ctx.invoked_subcommand)
welcome_text = """Welcome to the EvalAI CLI. Use evalai --help for viewing all the options"""
echo(welcome_text)

main.add_command(auth)

# Command -> evalai auth/challenges/submissions/teams
main.add_command(challenges)
main.add_command(submissions)
main.add_command(teams)
Empty file added evalai/utils/__init__.py
Empty file.
39 changes: 39 additions & 0 deletions evalai/utils/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import os
import json
from os.path import expanduser

from click import echo


AUTH_TOKEN = 'token.json'
AUTH_TOKEN_PATH = "{}/.evalai/{}".format(expanduser('~'), AUTH_TOKEN)


def get_token():
"""
Loads token to be used for sending requests.
"""
if os.path.exists(AUTH_TOKEN_PATH):
with open(str(AUTH_TOKEN_PATH), 'r') as TokenObj:
try:
data = TokenObj.read()
except (OSError, IOError) as e:
echo(e)
data = json.loads(data)
token = data["token"]
return token
else:
echo("\nYour token file doesn't exists.")
echo("\nIt should be present at ~/.evalai/token.json\n")
return None


def get_headers():
"""
Returns token formatted in header for sending requests.
"""
headers = {
"Authorization": "Token {}".format(get_token()),
}

return headers
86 changes: 86 additions & 0 deletions evalai/utils/challenges.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import os
import requests
import sys

from click import echo, style

from pylsy import pylsytable

from evalai.utils.auth import get_headers
from evalai.utils.urls import Urls
from evalai.utils.common import valid_token


API_HOST_URL = os.environ.get("EVALAI_API_URL", 'http://localhost:8000')


def print_challenge_table(challenge):
br = style("------------------------------------------------------------------", bold=True)

challenge_title = "\n{}".format(style(challenge["title"], bold=True, fg="green"))
challenge_id = "ID: {}\n\n".format(style(str(challenge["id"]), bold=True, fg="blue"))

title = "{} {}".format(challenge_title, challenge_id)

description = "{}\n".format(challenge["short_description"])
date = "End Date : " + style(challenge["end_date"].split("T")[0], fg="red")
date = "\n{}\n\n".format(style(date, bold=True))
challenge = "{}{}{}{}".format(title, description, date, br)
return challenge


def get_challenges(url):

headers = get_headers()
try:
response = requests.get(url, headers=headers)
response.raise_for_status()
except requests.exceptions.HTTPError as err:
echo(err)
sys.exit(1)
except requests.exceptions.RequestException as err:
echo(err)
sys.exit(1)

response_json = response.json()
if valid_token(response_json):

challenges = response_json["results"]
if len(challenges) is not 0:
for challenge in challenges:
challenge = print_challenge_table(challenge)
echo(challenge)
else:
echo("Sorry, no challenges found.")


def get_challenge_list():
"""
Fetches the list of challenges from the backend.
"""
url = "{}{}".format(API_HOST_URL, Urls.challenge_list.value)
get_challenges(url)


def get_past_challenge_list():
"""
Fetches the list of past challenges from the backend.
"""
url = "{}{}".format(API_HOST_URL, Urls.past_challenge_list.value)
get_challenges(url)


def get_ongoing_challenge_list():
"""
Fetches the list of ongoing challenges from the backend.
"""
url = "{}{}".format(API_HOST_URL, Urls.challenge_list.value)
get_challenges(url)


def get_future_challenge_list():
"""
Fetches the list of future challenges from the backend.
"""
url = "{}{}".format(API_HOST_URL, Urls.future_challenge_list.value)
get_challenges(url)
16 changes: 16 additions & 0 deletions evalai/utils/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from click import echo


def valid_token(response):
"""
Checks if token is valid.
"""

if ('detail' in response):
if (response['detail'] == 'Invalid token'):
echo("The authentication token you are using isn't valid. Please try again.")
return False
if (response['detail'] == 'Token has expired'):
echo("Sorry, the token has expired.")
return False
return True
6 changes: 6 additions & 0 deletions evalai/utils/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from enum import Enum

class Urls(Enum):
challenge_list = "/api/challenges/challenge/all"
past_challenge_list = "/api/challenges/challenge/past"
future_challenge_list = "/api/challenges/challenge/future"
31 changes: 14 additions & 17 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,34 @@

setup(
name=PROJECT,
version='1.0',
version='1.6a1',

description='Use EvalAI through the CLI!',
long_description=long_description,

author='Cloud-CV',
author_email='[email protected]',

url='https://github.com/Cloud-CV/evalai_cli',
download_url='https://github.com/Cloud-CV/evalai_cli/tarball/master',
url='https://github.com/Cloud-CV/evalai_cli ',

classifiers=['Development Status :: 1 - Alpha',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Intended Audience :: Developers',
'Environment :: Console',
],
classifiers=(
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
),

platforms=['Any'],

scripts=[],

provides=[],
install_requires=[
'click==6.7',
'pandas==0.22.0',
'pylsy==3.6',
'requests==2.18.4',
'responses==0.9.0',
'click',
'colorama',
'pandas',
'pylsy',
'requests',
'responses',
],

namespace_packages=[],
Expand Down
Empty file added tests/__init__.py
Empty file.
17 changes: 17 additions & 0 deletions tests/data/challenge_response.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
challenges = """
{'results': [{'is_active': True, 'anonymous_leaderboard': False, 'blocked_email_domains': [], 'submission_guidelines': 'Perspiciatis id sunt ab magni rerum laboriosam. Alias temporibus ratione est animi. Quisqua\
m reiciendis impedit fugiat corporis nesciunt totam. Odit molestiae praesentium et fuga architecto suscipit. At deleniti fugiat necessitatibus vel provident qui perspiciatis.', 'title': 'Olivia Challenge',\
'enable_forum': True, 'short_description': 'Ratione laboriosam quae tempora. Temporibus porro repellat rem facere. In impedit cupiditate voluptatum aut omnis animi illo. Perferendis ratione dolores eaque nulla iusto\
mollitia facere voluptatum. Earum dolor corporis quo enim quia optio.', 'image': None, 'evaluation_details': 'Amet officia saepe quis tempora magnam eum. Quidem ab consectetur exercitationem omnis. Nostrum\
consequuntur architecto eaque mollitia ab minima expedita quam. Velit itaque voluptates suscipit aliquam perspiciatis itaque cupiditate.', 'description': 'Excepturi eligendi minus modi delectus dolore\
asperiores voluptatem. Aspernatur itaque vitae repellendus. Natus ut tenetur labore dolores ex repudiandae.', 'id': 2, 'start_date': '2018-02-02T18:56:42.747134Z', 'terms_and_conditions': 'Est vero fugiat\
temporibus necessitatibus. Ea nihil possimus consequuntur doloribus odio. Vero voluptates non et repellat perferendis ipsam. Ex dicta nemo numquam cupiditate recusandae impedit.', 'allowed_email_domains': [], 'end_date': '2019-09\
25T18:56:42.747145Z', 'creator': {'team_name': 'South Lisafurt Host Team', 'created_by': 'host', 'id': 2}, 'approved_by_admin': True, 'published': True}, {'is_active': False, 'anonymous_leaderboard': False,\
'blocked_email_domains': [], 'submission_guidelines': 'Ullam vitae explicabo consequuntur odit fugiat pariatur doloribus ab. Qui ullam adipisci est corporis facilis. Quas excepturi deleniti\
dolorum tempora necessitatibus.', 'title': 'Jason Challenge', 'enable_forum': True, 'short_description': 'Dicta tempore quia itaque ex quam. Quas sequi in voluptates esse aspernatur deleniti. In magnam ipsam totam ratione quidem\
praesentium eius distinctio.', 'image': None, 'evaluation_details': 'Adipisci possimus tenetur illum maiores. Laboriosam error nostrum illum nesciunt cumque officiis suscipit. Occaecati velit fugiat alias magnam\
voluptas voluptatem ad. Repudiandae velit impedit veniam numquam.', 'description': 'Voluptates consequatur commodi odit repellendus quam. Id nemo provident ipsa cupiditate enim blanditiis autem. Recusandae vero\
necessitatibus debitis esse eveniet consequatur. Provident saepe officiis incidunt cum.', 'id': 3, 'start_date': '2016-12-29T18:56:42.752783Z', 'terms_and_conditions': 'Recusandae saepe ipsum saepe ullam aut. Cum eius\
nihil blanditiis itaque. Fugiat sed quod nostrum.', 'allowed_email_domains': [], 'end_date': '2018-02-02T18:56:42.752795Z', 'creator': {'team_name': 'South Lisafurt Host Team', 'created_by': 'host', 'id': 2},\
'approved_by_admin': True, 'published': True}], 'next': None, 'count': 10, 'previous': None}
"""
Loading

0 comments on commit aa44574

Please sign in to comment.