Drop-in Replacement of pychallonge

Overview

pychal

Pychal is a drop-in replacement of pychallonge with some extra features and support for new Python versions. Pychal provides python bindings for the CHALLONGE! API.

Differences

The only diffence with the pychallonge is the dictionary keys with dashes are now with undescores for example the key 'created-at' is now 'created_at'.

Requirements

  • iso8601
  • tzlocal
  • pytz
  • requests

Python version support

  • 2.7
  • 3.4+

Installation

For the stable version

pip install pychal

For latest development

pip install -e git+http://github.com/ZEDGR/pychal#egg=pychal

Usage

import challonge

# Tell pychal about your [CHALLONGE! API credentials](http://api.challonge.com/v1).
challonge.set_credentials("your_challonge_username", "your_api_key")

# Retrieve a tournament by its id (or its url).
tournament = challonge.tournaments.show(3272)

# Tournaments, matches, and participants are all represented as normal Python dicts.
print(tournament["id"]) # 3272
print(tournament["name"]) # My Awesome Tournament
print(tournament["started_at"]) # None

# Retrieve the participants for a given tournament.
participants = challonge.participants.index(tournament["id"])
print(len(participants)) # 13

# Start the tournament and retrieve the updated information to see the effects
# of the change.
challonge.tournaments.start(tournament["id"])
tournament = challonge.tournaments.show(tournament["id"])
print(tournament["started_at"]) # 2011-07-31 16:16:02-04:00

See challonge.com for full API documentation.

API Issues

The Challonge API has some issues with the attachments endpoints. The create and update endpoints are not working correctly. When you try to upload an attachment with asset(file) the API returns 500 internal server error. The same happens with asset + description. This problem has been reported to Challonge.

Other problems that I have noticed is that the check in process through the API seems weird. When you undo check in a participant the field 'checked_in' remains True but in the website the participant is correctly not checked in. That's why I haven't write any tests on this.

Fixed by pychal: In the datetime fields the api returns random timezone offsets, pychal convert those to your machine's local time. Also you can set any timezone you want with set_timezone function.

Running the unit tests

Pychal comes with a set of unit tests. The tests are not comprehensive, but do utilize each method and verify basic functionality.

In order to test behavior of the python bindings, API calls must be made to CHALLONGE!, which requires a username and api key. To run the tests with your credentials, set CHALLONGE_USER and CHALLONGE_KEY appropriately in your environment.

$ git clone http://github.com/ZEDGR/pychal pychal
$ CHALLONGE_USER=my_user CHALLONGE_KEY=my_api_key python pychal/tests.py
...............................
----------------------------------------------------------------------
Ran 31 tests in 98.176s

OK

Note that several tournaments are created, published, started, and completed over the course of the unit tests. These should be cleaned up by the end, but if any of the tests fail they may not be cleaned up. As long as the basic functions work, you can clean up errant tournaments as follows.

import challonge
challonge.set_credentials("my_user", "my_api_key")
for t in challonge.tournaments.index():
    if t["name"].startswith("pychal"):
        challonge.tournaments.destroy(t["id"])
Comments
  • cannot import pychallonge

    cannot import pychallonge

    I get the following error in my script while trying to run on a Raspberry pi:

    Traceback (most recent call last): File "Challonge Ticker.py", line 11, in import pychallonge ImportError: No module named pychallonge

    I've installed pychallonge using pip install pychallonge

    I'm trying to modify the following for use on my raspberry pi, but it keeps failing saying p1 = challonge.participants.show(tournamentURL, currentMatch["player1-id"])["name"]; KeyError: 'player1-id'

     `# Carmen "Beanwolf" Condeluci
     # 7/10/14
    
     from __future__ import print_function
     from shutil import move
     import challonge
     import sys
     import time
     import os
     import dateutil
     import pychallonge'
    
     print("Barebones Challonge Match Ticker Utility, ver 1.00")
     print("Made by Carmen \"Beanwolf\" Condeluci, most recent build published on 7/10/2014.\n")'
    
     print("This program pulls recently completed and/or upcoming matches from your Challonge hosted tournament and outputs a summary of the match to a text file. This can then be used with either OBS or XSplit's internal text scrolling tools, or an HTML/Flash source that can process text from and external file.\n")
    
     print("This file is saved as \"output.txt\" in the same directory as this program.")
     print("It will not appear until after one iteration has completed.\n")
    
     print("The number of completed and upcoming matches that will fit on the standard XSplit or OBS text scrolls is not too much. OBS, for example, tops out after about 4 completed matchs and 2 upcoming matches with a normal size font and some leading text. Keep this in consideration when working with these utilities.\n")
    
      challongeUsername = "MYUSERNAME"
      challongeAPIKey = "MYAPIKEY"
    
     challonge.set_credentials(challongeUsername, challongeAPIKey)
    
     # Retrieve a tournament by its id (or its url).
     tournamentURL = raw_input("\nPlease enter the tournament's URL indentifier.\n(\"challonge.com/xzr2pwvh\" would have a URL indentifier of \"xzr2pwvh\"): \n")
    
     tournament = challonge.tournaments.show(tournamentURL)
    
     matchesToShow = int(raw_input("\nEnter the number of completed matches you would like to show: "))
     upcomingToShow = int(raw_input("\nEnter the number of upcoming matches you would like to show: "))
    
     leadingText = raw_input("\nEnter any leading text that you would like to have on your ticker: \n")
    
     # Tournaments, matches, and participants are all represented as normal Python dicts.
    
     # Retrieve the participants for a given tournament.
     participants = challonge.participants.index(tournament["id"])
    
     print("\nThe program will now start in 5 seconds. It will continue to update indefinitely every minute until manually stopped with Ctrl + C.")
     time.sleep(5);
    
     #Overall loop would begin here
     while True:
    
    print("Starting an interation...")
    
    output = open('output.txt.tmp', 'w')
    
    output.write(leadingText)
    
    #shows user controlled amount of completed matches (higher rounds first)
    
    output.write("  <|>  Most Recent Completed Matches:  ")
    
    completedMatches = challonge.matches.index(tournament["id"], state="complete")
    comLength = len(completedMatches)
    
    # print(completedMatches)
    
    if matchesToShow > comLength:
    	for x in range(comLength, 0, -1):
    		if x != comLength:
    			output.write(" | ")
    
    		currentMatch = completedMatches[x-1]
    
    		comRound = str(currentMatch["round"])
    		if "-" not in comRound:
    			comRoundString = "W" + comRound
    		else:
    			comRoundString = "L" + comRound[1:]
    
    		p1 = challonge.participants.show(tournamentURL, currentMatch["player1-id"])["name"];
    		p2 = challonge.participants.show(tournamentURL, currentMatch["player2-id"])["name"]
    		output.write(comRoundString + ": " + p1 + " vs. " + p2 + ", " + currentMatch["scores-csv"])
    else:
    	for x in range(comLength, (comLength - matchesToShow), -1):
    		if x != comLength:
    			output.write(" | ")
    
    		currentMatch = completedMatches[x-1]
    
    		comRound = str(currentMatch["round"])
    		if "-" not in comRound:
    			comRoundString = "W" + comRound
    		else:
    			comRoundString = "L" + comRound[1:]
    
    		p1 = challonge.participants.show(tournamentURL, currentMatch["player1-id"])["name"];
    		p2 = challonge.participants.show(tournamentURL, currentMatch["player2-id"])["name"]
    		output.write(comRoundString + ": " + p1 + " vs. " + p2 + ", " + currentMatch["scores-csv"])
    
    #Shows upcoming matches
    
    output.write("  <|>  Upcoming Matches:  ")
    
    pendingMatches = challonge.matches.index(tournament["id"], state="open")
    pendLength = len(pendingMatches)
    
    # print(pendingMatches)
    
    if upcomingToShow > pendLength:
    	for x in range(pendLength, 0, -1):
    		if x != pendLength:
    			output.write(" | ")
    
    		pendingMatch = pendingMatches[x-1]
    
    		penRound = str(pendingMatch["round"])
    		if "-" not in penRound:
    			penRoundString = "W" + penRound
    		else:
    			penRoundString = "L" + penRound[1:]
    
    		p1 = challonge.participants.show(tournamentURL, pendingMatch["player1-id"])["name"];
    		p2 = challonge.participants.show(tournamentURL, pendingMatch["player2-id"])["name"]
    		output.write(penRoundString + ": " + p1 + " vs. " + p2)
    else:
    	for x in range(pendLength, (pendLength - upcomingToShow), -1):
    		if x != pendLength:
    			output.write(" | ")
    
    		pendingMatch = pendingMatches[x-1]
    
    		penRound = str(pendingMatch["round"])
    		if "-" not in penRound:
    			penRoundString = "W" + penRound
    		else:
    			penRoundString = "L" + penRound[1:]
    
    		p1 = challonge.participants.show(tournamentURL, pendingMatch["player1-id"])["name"];
    		p2 = challonge.participants.show(tournamentURL, pendingMatch["player2-id"])["name"]
    		output.write(penRoundString + ": " + p1 + " vs. " + p2)
    
    output.write("  <|>  ")
    
    output.close();
    
    move('output.txt.tmp', 'output.txt')
    
    print("Finished an iteration!\n")
    
    time.sleep(60)`
    

    I modified the code from: https://github.com/CCondeluci/Simple-Challonge-Ticker

    Any ideas on why this would not be working?

    opened by jhamilt0n 3
  • Tournament participants with all-number names get cast to float type

    Tournament participants with all-number names get cast to float type

    https://github.com/ZEDGR/pychal/blob/ebefcc8fc05798ce71f1199eb2481a4b99c84608/challonge/api.py#L121

    The value of key 'display_name' in the dictionary returned from participants.index() is a float when the tournament participant's name passes float(v) without throwing ValueError. Thus in the returned dictionary every participant's name is of type str except those that can be interpreted as numbers, which are of type float.

    FWIW, pychallonge does not have this issue.

    opened by GradyMoran 3
  • ParticipantsTestCase test_update is failing

    ParticipantsTestCase test_update is failing

    Since the new update (1.11.1), I have been seeing a FAIL on main.ParticipantsTestCase, now.

    Traceback (most recent call last):
      File "tests.py", line 213, in test_update
        self.assertTrue(p1["updated_at"] >= self.ps[0]["updated_at"])
    AssertionError: False is not true
    

    I've not yet dug in to it, however.

    opened by russellvt 2
  • Fix issue #3

    Fix issue #3

    https://github.com/ZEDGR/pychal/issues/3

    _parse now never casts certain response fields which are always strings to non-string types.

    Version changed to 1.8.1.

    opened by GradyMoran 2
  • Change to allow for setting of tiebreaker rules through API

    Change to allow for setting of tiebreaker rules through API

    After finally updating my fork of pychallonge to the latest most-official version, I think this change should be implemented in the official version. In challonge/api.py, I believe that line 157 should read:

    params.append(("%s[%s][]" % (prefix, k), val))

    When I originally wrote this section of code in June of 2015 (see this commit), the goal of it was to allow for tiebreaker rules (and the order of tiebreaker rules) for round-robin tournaments to be set via the API. To my knowledge, the attribute "tie_breaks" is the only attribute in the API where the value is a list. I have tested out my version of the code, and it does allow for the setting of tiebreaker rules. I have not tested out ZEDGR's code specifically, but I did attempt a more direct method of updating the API using ZEDGR's version of line 157 and I got an error. I would suggest attempting to run the following lines of code and seeing what happens:

    import challonge t = challonge.tournaments.create("Test RR", "hut5wr9w", tournament_type="round robin", ranked_by="match wins", tie_breaks=["match wins vs tied","points difference","points scored"])

    My current commit also contains a bulk_add unit test, which ZEDGR's doesn't, but that's less important.

    opened by margotphoenix 2
  • inconsistent timezone

    inconsistent timezone

    Same program ran twice the timezone is different both times.

    [ryan@ryanvile fgc]$ ./get_tournament.py | grep start 'group_stages_were_started': False, 'public_predictions_before_start_time': False, 'start_at': datetime.datetime(2017, 2, 28, 11, 35**, tzinfo=<FixedOffset '-05:00' datetime.timedelta(-1, 68400)>),** 'started_at': datetime.datetime(2017, 2, 28, 11, 43, 51, 214000, **tzinfo=<FixedOffset '-05:00' datetime.timedelta(-1, 68400)>),** 'started_checking_in_at': None, 'group_stages_were_started': False, 'public_predictions_before_start_time': False, 'start_at': datetime.datetime(2017, 2, 28, 13, 30, tzinfo=**<FixedOffset '-05:00' datetime.timedelta(-1, 68400)>),** 'started_at': datetime.datetime(2017, 2, 28, 16, 58, 58, 792000, **tzinfo=<FixedOffset '-05:00' datetime.timedelta(-1, 68400)>),** 'started_checking_in_at': None, 'group_stages_were_started': False, 'public_predictions_before_start_time': False, 'start_at': datetime.datetime(2017, 2, 28, 17, 35, tzinfo=**<FixedOffset '-05:00' datetime.timedelta(-1, 68400)>),** 'started_at': datetime.datetime(2017, 2, 28, 18, 2, 39, 53000, **tzinfo=<FixedOffset '-05:00' datetime.timedelta(-1, 68400)>),** 'started_checking_in_at': None, [ryan@ryanvile fgc]$ ./get_tournament.py | grep start 'group_stages_were_started': False, 'public_predictions_before_start_time': False, 'start_at': datetime.datetime(2017, 2, 28, 17, 35, tzinfo=**<FixedOffset '+01:00' datetime.timedelta(0, 3600)>),** 'started_at': datetime.datetime(2017, 2, 28, 17, 43, 51, 214000, **tzinfo=<FixedOffset '+01:00' datetime.timedelta(0, 3600)>),** 'started_checking_in_at': None, 'group_stages_were_started': False, 'public_predictions_before_start_time': False, 'start_at': datetime.datetime(2017, 2, 28, 19, 30, **tzinfo=<FixedOffset '+01:00' datetime.timedelta(0, 3600)>),** 'started_at': datetime.datetime(2017, 2, 28, 22, 58, 58, 792000, **tzinfo=<FixedOffset '+01:00' datetime.timedelta(0, 3600)>),** 'started_checking_in_at': None, 'group_stages_were_started': False, 'public_predictions_before_start_time': False, 'start_at': datetime.datetime(2017, 2, 28, 23, 35, tzinfo=**<FixedOffset '+01:00' datetime.timedelta(0, 3600)>),** 'started_at': datetime.datetime(2017, 3, 1, 0, 2, 39, 53000, **tzinfo=<FixedOffset '+01:00' datetime.timedelta(0, 3600)>),** 'started_checking_in_at': None,

    opened by whod81 2
  • _parse() treats 'misc' as datetime

    _parse() treats 'misc' as datetime

    In api.py the list of string values to treat as text do not include 'misc', which is provided in response to the participant.bulk_add() and index() calls. The API documentation states that 'misc' is user generated string value. Attempts to parse this as a datetime field can alter the fields value.

    opened by ElHeffeVonPhennig 1
  • Ensure every API call has a response

    Ensure every API call has a response

    I might be missing something but in all but your GET requests, you don't send back any response. There is still useful information in the response for PUT and POST requests that at the very least would eliminate having to run a second API call to get the information and confirm what you sent.

    Also always returning a response at least let's you write error handling if the PUT/POST fails or returns something you don't expect.

    opened by CapnRedB 0
Owner
ZED
TODO: Add a bio
ZED
Replacement for the default Dark Sky Home Assistant integration using Pirate Weather

Pirate Weather Integrations This integration is designed to replace the default Dark Sky integration in Home Assistant with a slightly modified, but f

Alexander Rey 129 Jan 6, 2023
A replacement for Reddit /r/copypasta CummyBot2000 with extra measures to avoid it being banned.

CummyBot1984 A replacement for Reddit /r/copypasta's CummyBot2000 with extra measures to respect Reddit's API rules. Features Copies and replies to ev

null 2 Feb 21, 2022
Drop-in replacement of Django admin comes with lots of goodies, fully extensible with plugin support, pretty UI based on Twitter Bootstrap.

Xadmin Drop-in replacement of Django admin comes with lots of goodies, fully extensible with plugin support, pretty UI based on Twitter Bootstrap. Liv

差沙 4.7k Dec 31, 2022
pdb++, a drop-in replacement for pdb (the Python debugger)

pdb++, a drop-in replacement for pdb What is it? This module is an extension of the pdb module of the standard library. It is meant to be fully compat

null 1k Dec 24, 2022
A drop-in replacement for Django's runserver.

About A drop in replacement for Django's built-in runserver command. Features include: An extendable interface for handling things such as real-time l

David Cramer 1.3k Dec 15, 2022
Python implementation of cover trees, near-drop-in replacement for scipy.spatial.kdtree

This is a Python implementation of cover trees, a data structure for finding nearest neighbors in a general metric space (e.g., a 3D box with periodic

Patrick Varilly 28 Nov 25, 2022
A drop-in replacement for Django's runserver.

About A drop in replacement for Django's built-in runserver command. Features include: An extendable interface for handling things such as real-time l

David Cramer 1.3k Dec 15, 2022
A drop-in replacement for argparse that allows options to also be set via config files and/or environment variables.

ConfigArgParse Overview Applications with more than a handful of user-settable options are best configured through a combination of command line args,

null 634 Dec 22, 2022
pdb++, a drop-in replacement for pdb (the Python debugger)

pdb++, a drop-in replacement for pdb What is it? This module is an extension of the pdb module of the standard library. It is meant to be fully compat

null 1k Jan 2, 2023
An Aspiring Drop-In Replacement for NumPy at Scale

Legate NumPy is a Legate library that aims to provide a distributed and accelerated drop-in replacement for the NumPy API on top of the Legion runtime. Using Legate NumPy you do things like run the final example of the Python CFD course completely unmodified on 2048 A100 GPUs in a DGX SuperPOD and achieve good weak scaling.

Legate 502 Jan 3, 2023
Drop-in replacement of Django admin comes with lots of goodies, fully extensible with plugin support, pretty UI based on Twitter Bootstrap.

Xadmin Drop-in replacement of Django admin comes with lots of goodies, fully extensible with plugin support, pretty UI based on Twitter Bootstrap. Liv

差沙 4.7k Dec 31, 2022
A drop-in replacement for Django's runserver.

About A drop in replacement for Django's built-in runserver command. Features include: An extendable interface for handling things such as real-time l

David Cramer 1.3k Dec 15, 2022
A drop-in replacement for django's ImageField that provides a flexible, intuitive and easily-extensible interface for quickly creating new images from the one assigned to the field.

django-versatileimagefield A drop-in replacement for django's ImageField that provides a flexible, intuitive and easily-extensible interface for creat

Jonathan Ellenberger 490 Dec 13, 2022
A drop-in replacement for django's ImageField that provides a flexible, intuitive and easily-extensible interface for quickly creating new images from the one assigned to the field.

django-versatileimagefield A drop-in replacement for django's ImageField that provides a flexible, intuitive and easily-extensible interface for creat

Jonathan Ellenberger 490 Dec 13, 2022
Drag’n’drop Pivot Tables and Charts for Jupyter/IPython Notebook, care of PivotTable.js

pivottablejs: the Python module Drag’n’drop Pivot Tables and Charts for Jupyter/IPython Notebook, care of PivotTable.js Installation pip install pivot

Nicolas Kruchten 512 Dec 26, 2022
Drop-in MessagePack support for ASGI applications and frameworks

msgpack-asgi msgpack-asgi allows you to add automatic MessagePack content negotiation to ASGI applications (Starlette, FastAPI, Quart, etc.), with a s

Florimond Manca 128 Jan 2, 2023
Drag’n’drop Pivot Tables and Charts for Jupyter/IPython Notebook, care of PivotTable.js

pivottablejs: the Python module Drag’n’drop Pivot Tables and Charts for Jupyter/IPython Notebook, care of PivotTable.js Installation pip install pivot

Nicolas Kruchten 419 Feb 11, 2021
Drop-down terminal for GNOME

Guake 3 README Introduction Guake is a python based dropdown terminal made for the GNOME desktop environment. Guake's style of window is based on an F

Guake 4.1k Dec 25, 2022
A simple wrapper to make a flat file drop in raplacement for mongodb out of TinyDB

Purpose A simple wrapper to make a drop in replacement for mongodb out of tinydb. This module is an attempt to add an interface familiar to those curr

null 180 Jan 1, 2023
Tkinter Designer - Create Beautiful Tkinter GUIs by Drag and Drop.

Tkinter Designer is created to speed up and beautify Python GUI Experience. It uses well know design software called Figma. Which makes creating Tkinter GUI in Python a piece of cake.

Parth Jadhav 5.2k Jan 9, 2023