[
  {
    "path": ".gitignore",
    "content": "*.pyc\nchallenges/*.pyc\n\nconfig.py\nREADME\nREADME.txt\n\n# Distribution / packaging\n.Python\nenv/\nbin/\nbuild/\ndevelop-eggs/\ndist/\neggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\n*.egg-info/\n.installed.cfg\n*.egg\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 Archit Verma\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "README.md",
    "content": "Challenge CLI\n=====\n\n[![PyPI version](https://badge.fury.io/py/challenge-cli.svg)](https://badge.fury.io/py/challenge-cli)\n\nProgramming Challenges for Hackers - a CLI for all the programming challenges. \n\n![](http://i.imgur.com/F7BbEH2.png)\n\nInstall\n=====\n\n### Using `pip`\n\n```bash\n$ pip install challenge-cli\n````\n\n### Build from source\n\n\n```bash\n$ git clone git@github.com:architv/chcli.git\n$ cd chcli\n$ python setup.py install\n```\n\nUsage\n====\n\n### Get active challenges\n\n```bash\n$ challenges --active\n$ challenges --active -p HR -p TC # get active challenges from HackerRank(HR) and topcoder(TC).\n```\n\n### Get upcoming challenges\n\n```bash\n$ challenges --upcoming\n```\n\n### Open a challenge in browser\n\n```bash\n$ challenges --active 1 # opens the first active challenge in your browser\n```\n\n### Get upcoming challenges from a particular platform\n\n```bash\n$ challenges --upcoming -p HR -p TC # HR and TC are platform code for HackerRank and TopCoder Respectively\n```\n\n### Get short challenges\n\n```bash\n$ challenges --short -p CF # get all the short challenges from codeforces\n```\n\n### Get hiring challenges\n\n```bash\n$ challenges --hiring # get all the hiring challenges\n```\n\n### Get challenges from all platforms with a set time period\n\n```bash\n$ challenges -t 2 # get all the active challenges and upcoming challenges which start in the next 2 days\n```\n\n### Help\n```bash\n$ challenges --help\n```\n\n### Platform and Platform codes\n\n- TC: topcoder.com\n- HR: hackerrank.com\n- CF: codeforces.com\n- HE: hackerearth.com\n- CC: codechef.com\n- GCJ: Google Code Jam\n- KA: kaggle.com\n\nFor a full list of supported platform and platform codes [see this](challenges/platformids.py).\n\nDemo\n====\n\n### Active Challenges\n![](http://i.imgur.com/Siedm4R.gif)\n\n### Open a challenge in browser\n![](http://i.imgur.com/mxsrc8C.gif)\n\n### Hiring Challenges\n![](http://i.imgur.com/c30BEqG.gif)\n\n### Short Challenges from a particular list of platform\n![](http://i.imgur.com/SKQgona.png?1)\n\n### Upcoming Challenges within 1 day\n![](http://i.imgur.com/3mX7YGh.png)\n\nTodo\n====\n- [ ] Fix alignment issues\n\n\nLicence\n====\nOpen sourced under [MIT License](LICENSE)\n\nSupport\n====\nIf you like my work, please support the project by donating.\n\n- https://gratipay.com/~architv\n"
  },
  {
    "path": "challenges/__init__.py",
    "content": ""
  },
  {
    "path": "challenges/cli.py",
    "content": "import sys\nimport webbrowser\n\nimport requests\nimport click\n\nfrom local_exceptions import IncorrectParametersException\n\nimport writers\n\nfrom platformids import platforms as contest_platforms\nfrom utilities import time_difference\n\nBASE_URL = \"http://challengehuntapp.appspot.com/v2\"\nPLATFORM_IDS = contest_platforms\n\ndef check_platforms(platforms):\n  \"\"\"Checks if the platforms have a valid platform code\"\"\"\n  if len(platforms) > 0:\n    return all(platform in PLATFORM_IDS for platform in platforms)\n  return True\n\n\ndef get_contests_data():\n  \"\"\"Gets all the data for all the contests\"\"\"\n  req = requests.get(BASE_URL)\n\n  if req.status_code == requests.codes.ok:\n    return req.json()\n  else:\n    click.secho(\"Couldn't get the data\", fg=\"red\", bold=True)\n    click.secho(\"Exiting...\", fg=\"red\", bold=True)\n    sys.exit()\n\ndef get_platform_filter(platforms):\n    if platforms:\n        platform_filter = [PLATFORM_IDS[platform] for platform in platforms]\n    else:\n        platform_filter = PLATFORM_IDS.values()\n    return platform_filter\n\ndef active_contests(platforms):\n  \"\"\"Gets all the active contests based on time and platforms\"\"\"\n  contests_data = get_contests_data()\n  platform_filter = get_platform_filter(platforms)\n\n  active_challenges = [contest for contest in contests_data[\"active\"] if contest[\"host_name\"] in platform_filter]\n\n  return active_challenges\n  \n\ndef upcoming_contests(platforms, time):\n  \"\"\"Gets all the upcoming contests based on time and platforms\"\"\"\n  contests_data = get_contests_data()\n  platform_filter = get_platform_filter(platforms)\n\n  upcoming_challenges = [contest for contest in contests_data[\"pending\"] if contest[\"host_name\"] in platform_filter \n    and time_difference(contest[\"start\"]).days <= time]\n\n  return upcoming_challenges\n\n\ndef hiring_contests():\n  \"\"\"Gets all the hiring challenges from all the availbale platforms\"\"\"\n  contests_data = get_contests_data()\n  active_contests = contests_data[\"active\"]\n  upcoming_contests = contests_data[\"pending\"]\n  get_challenge_name = lambda x : x.lower().split()\n  hiring_challenges = [contest for contest in active_contests\n    if \"hiring\" in get_challenge_name(contest[\"contest_name\"])]\n  hiring_challenges += [contest for contest in upcoming_contests\n    if \"hiring\" in get_challenge_name(contest[\"contest_name\"])]\n  return hiring_challenges\n\n\ndef short_contests(platforms):\n  \"\"\"Gets all the short contests(less than or equal to 4 hours of duration)\"\"\"\n  contests_data = get_contests_data()\n  active_contests = contests_data[\"active\"]\n  upcoming_contests = contests_data[\"pending\"]\n\n  platform_filter = get_platform_filter(platforms)\n  get_challenge_duration = lambda x : int(x.split(\":\")[0]) if \"days\" not in x else float(\"inf\")\n  short_contests = [contest for contest in active_contests\n    if get_challenge_duration(contest[\"duration\"]) <= 4 and contest[\"host_name\"] in platform_filter]\n  short_contests += [contest for contest in upcoming_contests\n    if get_challenge_duration(contest[\"duration\"]) <= 4 and contest[\"host_name\"] in platform_filter]\n  return short_contests\n\n\ndef get_all_contests(platforms, time):\n  \"\"\"Gets all the contests and writes it to standard output\"\"\"\n  contests_data = get_contests_data()\n  active_contests = contests_data[\"active\"]\n  upcoming_contests = contests_data[\"pending\"]\n\n  platform_filter = get_platform_filter(platforms)\n\n  contests_data = [contest for contest in active_contests \n  if contest[\"host_name\"] in platform_filter]\n  contests_data += [contest for contest in upcoming_contests \n  if contest[\"host_name\"] in platform_filter and time_difference(contest[\"start\"]).days <= time]\n  return contests_data\n\n\n@click.command()\n@click.option('--active', is_flag=True, help=\"Shows all the active contests\")\n@click.option('--upcoming', is_flag=True, help=\"Shows all the upcoming contests\")\n@click.option('--hiring', is_flag=True, help=\"Shows all the hiring contests\")\n@click.option('--short', is_flag=True, help=\"Shows all the short contests\")\n@click.option('--platforms', '-p', multiple=True,\n              help=(\"Choose the platform whose contests you want to see. \"\n                    \"See platform codes for more info\"))\n@click.argument('goto', nargs=1, required=False, type=click.INT)\n@click.option('--time', '-t', default=6,\n              help=\"The number of days in the past for which you want to see the contests\")\ndef main(active, upcoming, hiring, short, goto, platforms, time):\n  \"\"\"A CLI for active and upcoming programming challenges from various platforms\"\"\"\n\n  if not check_platforms(platforms):\n    raise IncorrectParametersException('Invlaid code for platform. Please check the platform ids')\n\n  try:\n    if active:\n      active_challenges = active_contests(platforms)\n      if goto:\n        webbrowser.open(active_challenges[goto - 1][\"contest_url\"], new=2)\n      else:\n        writers.write_contests(active_challenges, \"active\")\n      return\n\n    if upcoming:\n      upcoming_challenges = upcoming_contests(platforms, time)\n      if goto:\n        goto = int(goto)\n        webbrowser.open(upcoming_challenges[goto - 1][\"contest_url\"], new=2)\n      else:\n        writers.write_contests(upcoming_challenges, \"upcoming\")\n      return\n\n    if hiring:\n      hiring_challenges = hiring_contests()\n      if goto:\n        webbrowser.open(hiring_challenges[goto - 1][\"contest_url\"], new=2)\n      else:\n        writers.write_contests(hiring_challenges, \"hiring\")\n      return\n\n    if short:\n      short_challenges = short_contests(platforms)\n      if goto:\n        goto = int(goto)\n        webbrowser.open(short_challenges[goto - 1][\"contest_url\"], new=2)\n      else:\n        writers.write_contests(short_challenges, \"short\")\n      return\n\n    all_contests = get_all_contests(platforms, time)\n    if goto:\n      webbrowser.open(all_contests[goto - 1][\"contest_url\"], new=2)\n    else:\n      writers.write_contests(all_contests, \"all\")\n  except IncorrectParametersException as e:\n    click.secho(e.message, fg=\"red\", bold=True)\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "challenges/local_exceptions.py",
    "content": "class IncorrectParametersException(Exception):\n  pass"
  },
  {
    "path": "challenges/platformids.py",
    "content": "platforms = {\n\t\"UA\": \"usaco.org\",\n  \"CC\": \"codechef.com\", \n  \"EOL\":\"e-olimp.com\",\n  \"CH24\": \"ch24.org\", \n  \"HR\": \"hackerrank.com\", \n  \"HE\": \"hackerearth.com\",\n  \"ICPC\": \"icfpcontest.org\", \n  \"GCJ\": \"google.com/codejam\", \n  \"DE24\": \"deadline24.pl\", \n  \"IOI\": \"stats.ioinformatics.org\", \n  \"PE\": \"projecteuler.net\", \n  \"SN\": \"contests.snarknews.info\", \n  \"CG\": \"codingame.com\", \n  \"CF\": \"codeforces.com\", \n  \"ELY\": \"e-olymp.com\",\n  \"DMOJ\": \"dmoj.ca\",\n  \"MARA\": \"marathon24.com\",\n  \"IPSC\": \"ipsc.ksp.sk\", \n  \"UVA\": \"uva.onlinejudge.org\",\n  \"OPEN\": \"opener.itransition.com\", \n  \"HSIN\": \"hsin.hr/coci\",\n  \"CFT\": \"ctftime.org\", \n  \"KA\": \"kaggle.com\",\n  \"TC\": \"topcoder.com\",\n  \"FBHC\": \"facebook.com/hackercup\"\n}"
  },
  {
    "path": "challenges/utilities.py",
    "content": "from datetime import datetime, timedelta\nfrom time import strptime\n\nfrom collections import namedtuple\n\ndef format_date(date_object):\n  \"\"\"Formats the date and returns the datetime object\"\"\"\n  # date_time = date_object.split(\"+\")\n  return datetime.strptime(str(date_object), \"%Y-%m-%dT%H:%M:%S\")\n\n\ndef time_difference(target_time):\n  \"\"\"Calculate the difference between the current time and the given time\"\"\"\n  TimeDiff = namedtuple(\"TimeDiff\", [\"days\", \"hours\", \"minutes\", \"seconds\"])\n  time_diff = format_date(target_time) - datetime.utcnow()\n  hours, remainder = divmod(time_diff.seconds, 3600)\n  minutes, seconds = divmod(remainder, 60)\n  return TimeDiff(days=time_diff.days, hours=hours, minutes=minutes, seconds=seconds)\n"
  },
  {
    "path": "challenges/writers.py",
    "content": "import json\n\nimport click\n\nfrom utilities import time_difference\n\ndef colors():\n  \"\"\"Creates an enum for colors\"\"\"\n  enums = dict(\n    TIME_LEFT=\"red\",\n    CONTEST_NAME=\"yellow\",\n    HOST=\"green\",\n    MISC=\"blue\",\n    TIME_TO_START=\"green\",\n  )\n\n  return type('Enum', (), enums)\n\n\ndef challenge():\n  \"\"\"Creates an enum for contest type\"\"\"\n  enums = dict(\n    ACTIVE=\"active\",\n    UPCOMING=\"upcoming\",\n    HIRING=\"hiring\",\n    ALL=\"all\",\n    SHORT=\"short\",\n  )\n\n  return type('Enum', (), enums)\n\n\ndef write_contests(contests, contest_type):\n  \"\"\"Prints the contests based on the parameters passed\"\"\"\n  write_contest_header(contest_type)\n\n  for index, contest in enumerate(contests):\n\n    time_diff_string = get_time_string(contest, contest_type)\n    contest_name = contest[\"contest_name\"].encode('utf-8')\n\n    click.echo()\n    click.secho(\"%-3s\" % str(index+1), nl=False, bold=True)\n    click.secho(\"  %-50s\" %\n                contest_name, nl=False, fg=colors().CONTEST_NAME, bold=True)\n    click.secho(\"    %-20s\" % time_diff_string, nl=False, fg=colors().TIME_TO_START, bold=True)\n    click.secho(\"    %-11s\" %\n                 str(contest[\"duration\"]), nl=False, bold=True)\n    click.secho(\"    %-15s\" % contest[\"host_name\"], fg=colors().HOST, bold=True)\n\n\ndef get_time_string(contest, contest_type):\n  \"\"\"Return a string with time for the contest to begin/end\"\"\"\n\n  if contest_type == challenge().ACTIVE:\n    time_diff = time_difference(contest[\"end\"])\n  elif contest_type == challenge().UPCOMING:\n    time_diff = time_difference(contest[\"start\"])\n  elif contest_type in [challenge().HIRING, challenge().SHORT, challenge().ALL]:\n    try:\n      time_diff = time_difference(contest[\"start\"])\n    except:\n      time_diff = time_difference(contest[\"end\"])\n  time_diff_string = \"\"\n\n  if time_diff.days > 0: \n    time_diff_string = \"{0} days {1} hours\".format(time_diff.days, time_diff.hours)\n  elif time_diff.hours > 0:\n    time_diff_string = \"{0} hours {1} minutes\".format(time_diff.hours, time_diff.minutes)\n  else:\n    time_diff_string = \"{0} minutes\".format(time_diff.minutes)\n  return time_diff_string\n\n\ndef write_contest_header(contest_type):\n  \"\"\"Prints the header for the type of contest\"\"\"\n  if contest_type == challenge().ACTIVE:\n    click.secho(\"%-3s  %-50s    %-20s    %-11s    %-15s\" % \n    (\"NO.\", \"NAME\", \"ENDS IN\", \"DURATION\", \"PLATFORM\"))\n  elif contest_type == challenge().UPCOMING:\n    click.secho(\"%-3s  %-50s    %-20s    %-11s    %-15s\" % \n    (\"NO.\", \"NAME\", \"STARTS IN\", \"DURATION\", \"PLATFORM\"))\n  elif contest_type in [challenge().HIRING, challenge().SHORT, challenge().ALL]:\n    click.secho(\"%-3s  %-50s    %-20s    %-11s    %-15s\" % \n    (\"NO.\", \"NAME\", \"STARTS/ENDS IN\", \"DURATION\", \"PLATFORM\"))    \n"
  },
  {
    "path": "requirements.txt",
    "content": "argparse==1.2.1\nclick==5.1\nrequests==2.7.0\nwsgiref==0.1.2\n"
  },
  {
    "path": "setup.py",
    "content": "#!/usr/bin/env python\n\nfrom setuptools import setup\nimport sys\n\nsetup(\n    name='challenge-cli',\n    version='0.0.1.0',\n    description='Programming challenges for hackers',\n    author='Archit Verma',\n    license='MIT',\n    classifiers=[\n        # How mature is this project? Common values are\n        #   3 - Alpha\n        #   4 - Beta\n        #   5 - Production/Stablegit\n        'Development Status :: 3 - Alpha',\n        'Intended Audience :: Developers',\n        'Topic :: Software Development :: Build Tools',\n        'License :: OSI Approved :: MIT License',\n\n        # Specify the Python versions you support here. In particular, ensure\n        # that you indicate whether you support Python 2, Python 3 or both.\n        'Programming Language :: Python :: 2',\n        'Programming Language :: Python :: 2.6',\n        'Programming Language :: Python :: 2.7',\n    ],\n    keywords = \"programming challenges competitive topcoder codeforces tool cli\",\n    author_email='architv07@gmail.com',\n    url='https://github.com/architv/chcli',\n    packages=['challenges'],\n    install_requires=[\n        \"click>=5.0\",\n        \"requests==2.7.0\"\n    ] + ([\"colorama==0.3.3\"] if \"win\" in sys.platform else []),\n    entry_points = {\n        'console_scripts': [\n            'challenges = challenges.cli:main'\n      ],\n    }\n)\n"
  }
]