├── docs ├── code ├── source │ ├── srstatic │ │ ├── img │ │ │ └── Logo.png │ │ └── css │ │ │ └── custom-styles.css │ ├── reference │ │ ├── root.rst │ │ ├── resource.rst │ │ ├── personnel.rst │ │ ├── validation.rst │ │ ├── analytics.rst │ │ ├── client.rst │ │ ├── match.rst │ │ └── main.rst │ ├── srtemplates │ │ └── layout.html │ ├── guide │ │ ├── main.rst │ │ ├── analytics.rst │ │ ├── personnel.rst │ │ ├── validation.rst │ │ ├── resources.rst │ │ └── match.rst │ ├── installation.rst │ ├── index.rst │ ├── started.rst │ └── conf.py └── Makefile ├── tests ├── requirements.txt ├── README.md ├── test_resource.py ├── test_exceptions.py ├── test_clients.py └── test_endpoints.py ├── requirements.txt ├── requirements-docs.txt ├── .gitignore ├── MANIFEST.in ├── .travis.yml ├── soccermetrics ├── rest │ ├── resources │ │ ├── __init__.py │ │ ├── service_root.py │ │ ├── personnel.py │ │ ├── events.py │ │ ├── analytics.py │ │ ├── validation.py │ │ ├── match.py │ │ ├── base.py │ │ └── statistics.py │ └── __init__.py └── __init__.py ├── AUTHORS.rst ├── Makefile ├── examples ├── nationalteams │ ├── example_goalscorers.py │ ├── example_pbp.py │ ├── example_matchups.py │ ├── example_conditions.py │ ├── example_results.py │ ├── example_referee.py │ ├── example_rotations.py │ └── example_manager.py ├── clubs │ ├── example_goalscorers.py │ ├── example_pbp.py │ ├── example_matchups.py │ ├── example_conditions.py │ ├── example_results.py │ ├── example_referee.py │ ├── example_rotations.py │ ├── example_manager.py │ └── example_fantasy.py └── README.md ├── LICENSE ├── setup.py ├── CONTRIBUTING.rst ├── README.rst └── README.md /docs/code: -------------------------------------------------------------------------------- 1 | ../soccermetrics/ -------------------------------------------------------------------------------- /tests/requirements.txt: -------------------------------------------------------------------------------- 1 | nose 2 | coverage 3 | mock 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | easydict>=1.4 2 | requests>=1.2.3 3 | -------------------------------------------------------------------------------- /requirements-docs.txt: -------------------------------------------------------------------------------- 1 | Jinja2 2 | MarkupSafe 3 | Pygments 4 | Sphinx 5 | sphinx-bootstrap-theme 6 | docutils 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .coverage 3 | build/ 4 | dist/ 5 | *env/ 6 | *.egg-info 7 | *.py[co] 8 | *.*~ 9 | sphinx-setup.txt 10 | -------------------------------------------------------------------------------- /docs/source/srstatic/img/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soccermetrics/soccermetrics-client-py/HEAD/docs/source/srstatic/img/Logo.png -------------------------------------------------------------------------------- /docs/source/reference/root.rst: -------------------------------------------------------------------------------- 1 | .. _ref-root-obj: 2 | 3 | API Root 4 | ^^^^^^^^ 5 | 6 | .. automodule:: soccermetrics.rest.resources.service_root 7 | :members: 8 | -------------------------------------------------------------------------------- /docs/source/srstatic/css/custom-styles.css: -------------------------------------------------------------------------------- 1 | .navbar-inner { 2 | min-height: 95px; 3 | } 4 | 5 | section { 6 | padding-top: 15px; 7 | margin-top: -15px; 8 | } -------------------------------------------------------------------------------- /docs/source/reference/resource.rst: -------------------------------------------------------------------------------- 1 | .. _ref-resource-obj: 2 | 3 | Base Resources 4 | ^^^^^^^^^^^^^^ 5 | 6 | .. automodule:: soccermetrics.rest.resources.base 7 | :members: 8 | -------------------------------------------------------------------------------- /docs/source/reference/personnel.rst: -------------------------------------------------------------------------------- 1 | .. _ref-personnel-obj: 2 | 3 | Personnel Resources 4 | ------------------- 5 | 6 | .. automodule:: soccermetrics.rest.resources.personnel 7 | :members: 8 | -------------------------------------------------------------------------------- /docs/source/reference/validation.rst: -------------------------------------------------------------------------------- 1 | .. _ref-validation-obj: 2 | 3 | Validation Resources 4 | -------------------- 5 | 6 | .. automodule:: soccermetrics.rest.resources.validation 7 | :members: 8 | -------------------------------------------------------------------------------- /docs/source/reference/analytics.rst: -------------------------------------------------------------------------------- 1 | .. _ref-analytics-obj: 2 | 3 | Match Analytics Resources 4 | ------------------------- 5 | 6 | .. automodule:: soccermetrics.rest.resources.analytics 7 | :members: 8 | -------------------------------------------------------------------------------- /docs/source/srtemplates/layout.html: -------------------------------------------------------------------------------- 1 | {# Import the theme's layout. #} 2 | {% extends "!layout.html" %} 3 | 4 | {# Include our new CSS file into existing ones. #} 5 | {% set css_files = css_files + ['srstatic/css/custom-styles.css'] %} -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.md 3 | include README.rst 4 | include tests/README.md 5 | include examples/README.md 6 | include tests/requirements.txt 7 | include requirements.txt 8 | include Makefile 9 | recursive-include docs * -------------------------------------------------------------------------------- /docs/source/reference/client.rst: -------------------------------------------------------------------------------- 1 | .. _ref-client-obj: 2 | 3 | Client Object 4 | ------------- 5 | 6 | .. automodule:: soccermetrics.rest 7 | :members: 8 | 9 | .. _ref-exceptions: 10 | 11 | Exceptions 12 | ---------- 13 | 14 | .. automodule:: soccermetrics 15 | :members: -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.3" 4 | - "2.7" 5 | - "2.6" 6 | # command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors 7 | install: 8 | - pip install -r requirements.txt 9 | # command to run tests, e.g. python setup.py test 10 | script: nosetests -------------------------------------------------------------------------------- /docs/source/reference/match.rst: -------------------------------------------------------------------------------- 1 | .. _ref-match-obj: 2 | 3 | Match Resources 4 | --------------- 5 | 6 | .. automodule:: soccermetrics.rest.resources.match 7 | :members: 8 | 9 | .. automodule:: soccermetrics.rest.resources.events 10 | :members: 11 | 12 | .. automodule:: soccermetrics.rest.resources.statistics 13 | :members: -------------------------------------------------------------------------------- /docs/source/reference/main.rst: -------------------------------------------------------------------------------- 1 | .. _reference: 2 | 3 | API Reference 4 | ============= 5 | 6 | A complete guide to the classes and methods in the Soccermetrics Connect API. 7 | 8 | .. toctree:: 9 | :maxdepth: 1 10 | 11 | client 12 | resource 13 | root 14 | validation 15 | personnel 16 | match 17 | analytics 18 | -------------------------------------------------------------------------------- /docs/source/guide/main.rst: -------------------------------------------------------------------------------- 1 | .. _guide: 2 | 3 | User Guide 4 | ========== 5 | 6 | You can query the Soccermetrics REST API to retrieve historical match data, in- 7 | match statistics, and advanced analytics. Each link contains in-depth guides 8 | to specific resources. 9 | 10 | .. toctree:: 11 | :maxdepth: 1 12 | 13 | resources 14 | validation 15 | personnel 16 | match 17 | analytics 18 | 19 | -------------------------------------------------------------------------------- /soccermetrics/rest/resources/__init__.py: -------------------------------------------------------------------------------- 1 | from soccermetrics.rest.resources.base import (Resource, Link) 2 | from soccermetrics.rest.resources.service_root import Root 3 | from soccermetrics.rest.resources.validation import Validation 4 | from soccermetrics.rest.resources.personnel import Personnel 5 | from soccermetrics.rest.resources.match import MatchPlay 6 | from soccermetrics.rest.resources.analytics import MatchAnalytics -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | Credits 2 | ======= 3 | 4 | ``soccermetrics-client-py`` is written and maintained by Howard Hamilton of Soccermetrics Research, LLC. 5 | 6 | Contributors 7 | ------------ 8 | 9 | We thank the following people for their contributions to the project: 10 | 11 | - `Giacomo Lacava `_ 12 | 13 | Please add your name and GitHub URL alphabetically when you submit your first pull request. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean venv install test test-install coverage 2 | 3 | venv: 4 | virtualenv venv 5 | 6 | install: venv 7 | . venv/bin/activate; pip install -r requirements.txt; python setup.py install 8 | 9 | test-install: 10 | . venv/bin/activate; pip install -r test/requirements.txt 11 | 12 | test: 13 | . venv/bin/activate; nosetests 14 | 15 | coverage: 16 | . venv/bin/activate; nosetests -v --with-coverage --cover-package=soccermetrics 17 | 18 | clean: 19 | rm -rf venv/ 20 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | API Client Unit Tests 2 | ===================== 3 | 4 | This directory contains unit tests for the Soccermetrics API client. 5 | 6 | Setup 7 | ----- 8 | 9 | From root directory of the repository: 10 | 11 | $ python setup.py develop 12 | $ pip install -r tests/requirements.txt 13 | 14 | Run Tests 15 | --------- 16 | 17 | From root directory of the repository: 18 | 19 | $ nosetests -v tests/ 20 | 21 | Test Coverage Reports 22 | --------------------- 23 | 24 | We use `coverage` to create coverage reports for the package. 25 | 26 | From root directory of the repository: 27 | 28 | $ nosetests -v --with-coverage --cover-package=soccermetrics tests/ 29 | -------------------------------------------------------------------------------- /tests/test_resource.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import soccermetrics 4 | from soccermetrics import __api_version__ 5 | from soccermetrics.rest import SoccermetricsRestClient 6 | from soccermetrics.rest.resources import Resource 7 | 8 | class ResourceTest(unittest.TestCase): 9 | 10 | def setUp(self): 11 | base_url = "https://api-connect.soccermetrics.net" 12 | auth = dict(account="APP_ID",api_key="APP_KEY") 13 | self.resource = Resource(base_url, auth) 14 | 15 | def test_initialization(self): 16 | """Verify auth dictionary and versioning.""" 17 | self.assertEqual(self.resource.auth['account'],"APP_ID") 18 | self.assertEqual(self.resource.auth['api_key'],"APP_KEY") 19 | self.assertEqual(self.resource.endpoint,'/%s' % __api_version__) -------------------------------------------------------------------------------- /soccermetrics/rest/resources/service_root.py: -------------------------------------------------------------------------------- 1 | from soccermetrics.rest.resources import Resource 2 | 3 | class Root(Resource): 4 | """ 5 | Represents the service root of the REST API (/ endpoint). 6 | 7 | The Service Root is the central resource of the Soccermetrics 8 | API. From this point the user can access all of the publicly- 9 | available resources. 10 | 11 | Derived from :class:`base.Resource`. 12 | """ 13 | 14 | def __init__(self, base_uri, auth): 15 | """ 16 | Constructor of Root class. 17 | 18 | :param base_uri: Base URI of API. 19 | :type base_uri: string 20 | :param auth: Authentication credential. 21 | :type auth: tuple 22 | """ 23 | super(Root, self).__init__(base_uri, auth) 24 | 25 | self.endpoint += '/' -------------------------------------------------------------------------------- /examples/nationalteams/example_goalscorers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env/python 2 | # -*- encoding: utf-8 -*- 3 | # 4 | # List of goalscorers in football match. 5 | # (Japan vs Colombia, 2014 FIFA World Cup) 6 | # 7 | from soccermetrics.rest import SoccermetricsRestClient 8 | 9 | if __name__ == "__main__": 10 | client = SoccermetricsRestClient() 11 | 12 | match = client.natl.information.get(home_team_name="Japan", 13 | away_team_name="Colombia") 14 | 15 | for datum in match.data: 16 | print "Match id for Japan vs Colombia is %d" % datum.id 17 | 18 | for goal in client.link.get(datum.link.goals, sort='time_mins').data: 19 | print "Goal scored by %s in minute %d by %s" % ( 20 | goal.scoringTeamName, 21 | goal.timeMins, 22 | client.link.get(goal.link.player).data[0].fullName) 23 | -------------------------------------------------------------------------------- /examples/clubs/example_goalscorers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env/python 2 | # -*- encoding: utf-8 -*- 3 | # 4 | # List of goalscorers in football match. 5 | # (Arsenal vs Tottenham Hotspur) 6 | # 7 | from soccermetrics.rest import SoccermetricsRestClient 8 | 9 | if __name__ == "__main__": 10 | client = SoccermetricsRestClient() 11 | 12 | match = client.club.information.get(home_team_name="Arsenal", 13 | away_team_name="Tottenham Hotspur") 14 | 15 | for datum in match.data: 16 | print "Match id for Arsenal at home to Tottenham is %d" % datum.id 17 | 18 | for goal in client.link.get(datum.link.goals, sort='time_mins').data: 19 | print "Goal scored by %s in minute %d by %s" % ( 20 | goal.scoringTeamName, 21 | goal.timeMins, 22 | client.link.get(goal.link.player).data[0].fullName) 23 | -------------------------------------------------------------------------------- /soccermetrics/rest/resources/personnel.py: -------------------------------------------------------------------------------- 1 | from soccermetrics.rest.resources import Resource 2 | 3 | class Personnel(Resource): 4 | """ 5 | Represents a Personnel REST resource (/personnel/ endpoint). 6 | 7 | The Personnel resources let you access biographic and demographic 8 | data on the following personnel involved in a football match: 9 | 10 | * Players, 11 | * Managers, 12 | * Match referees. 13 | 14 | Derived from :class:`Resource`. 15 | """ 16 | def __init__(self, resource, base_uri, auth): 17 | """ 18 | Constructor of Personnel class. 19 | 20 | :param resource: Name of resource. 21 | :type resource: string 22 | :param base_uri: Base URI of API. 23 | :type base_uri: string 24 | :param auth: Authentication credential. 25 | :type auth: tuple 26 | """ 27 | super(Personnel, self).__init__(base_uri,auth) 28 | 29 | self.endpoint += "/personnel/%s" % resource -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2014 Soccermetrics Research, LLC. 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /soccermetrics/__init__.py: -------------------------------------------------------------------------------- 1 | __version_info__ = ('0','8','0') 2 | __version__ = '.'.join(__version_info__) 3 | 4 | __api_version__ = 'v1' 5 | 6 | class SoccermetricsException(Exception): 7 | """Base class for exceptions in Soccermetrics applications. 8 | 9 | Derived from :class:`Exception`. 10 | """ 11 | pass 12 | 13 | class SoccermetricsRestException(SoccermetricsException): 14 | """Custom exception for Soccermetrics REST API errors. 15 | 16 | The exception is raised with the following message:: 17 | 18 | **HTTP ERROR** : 19 | 20 | 21 | """ 22 | 23 | def __init__(self,status,uri,msg=""): 24 | """Constructor of SoccermetricsRestException. 25 | 26 | :param status: HTTP status code 27 | :type status: int 28 | :param uri: URI sent when exception was raised 29 | :type uri: string 30 | :param msg: Detailed error message 31 | :type msg: string or "" 32 | """ 33 | self.uri = uri 34 | self.status = status 35 | self.msg = msg 36 | 37 | def __str__(self): 38 | """String representation of exception.""" 39 | return "HTTP ERROR %s: %s \n %s" % (self.status, self.msg, self.uri) -------------------------------------------------------------------------------- /tests/test_exceptions.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from soccermetrics import SoccermetricsRestException 4 | 5 | class RestExceptionFormatTest(unittest.TestCase): 6 | """ 7 | Test format of client exception messages. 8 | """ 9 | 10 | def setUp(self): 11 | self.exc = SoccermetricsRestException(404,"/path/to/resource") 12 | 13 | def test_exception_status(self): 14 | """Verify status code of exception is an integer.""" 15 | self.assertEqual(self.exc.status, 404) 16 | 17 | def test_exception_uri(self): 18 | """Verify that URI in exception is URI sent through client.""" 19 | self.assertEqual(self.exc.uri, "/path/to/resource") 20 | 21 | def test_exception_msg(self): 22 | """Verify that exception message is empty.""" 23 | self.assertEqual(self.exc.msg, "") 24 | 25 | def test_exception_string(self): 26 | """Verify construction of string expression of exception.""" 27 | self.assertEqual(str(self.exc), "HTTP ERROR 404: \n /path/to/resource") 28 | 29 | def test_exception_with_msg(self): 30 | """Verify construction of string expression of exception with message.""" 31 | local = SoccermetricsRestException(404,"/path/to/resource",msg="Invalid resource request.") 32 | 33 | self.assertEqual(str(local), "HTTP ERROR 404: Invalid resource request. \n /path/to/resource") 34 | -------------------------------------------------------------------------------- /docs/source/guide/analytics.rst: -------------------------------------------------------------------------------- 1 | .. _analytics-resources: 2 | 3 | Match Analytics Resources 4 | ========================= 5 | 6 | The Match Analytics resources are a collection of advanced player and team analytics 7 | related to events in a soccer match and derived from basic match data. 8 | 9 | For more information consult the `Match Analytics Resource`_ documentation for the 10 | Connect API. 11 | 12 | Retrieving Match Analytics 13 | -------------------------- 14 | 15 | At present there are three sub-objects attached to the ``analytics`` object: 16 | 17 | * ``state``: Match state 18 | * ``segment``: Match segment 19 | * ``tsr``: Match Total Shot Ratio 20 | 21 | To retrieve analytics, you must pass the match ID to the ``get()`` call of the sub-object. 22 | :: 23 | 24 | from soccermetrics.rest import SoccermetricsRestClient 25 | client = SoccermetricsRestClient() 26 | 27 | state = client.analytics.state.get('6b063a7370ee438da8a36a79ab10c9b7') 28 | 29 | For most of the analytics sub-objects, no query parameters are permitted except for a 30 | few exceptions: 31 | :: 32 | 33 | from soccermetrics.rest import SoccermetricsRestClient 34 | client = SoccermetricsRestClient() 35 | 36 | state = client.analytics.state.get('6b063a7370ee438da8a36a79ab10c9b7', time='60') 37 | 38 | .. _`Match Analytics Resource`: http://soccermetrics.github.io/connect-api/resources/analytics.html -------------------------------------------------------------------------------- /examples/clubs/example_pbp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Compute changes in on-field composition during 4 | # a 2012-13 Premier League match. 5 | # 6 | 7 | from soccermetrics.rest import SoccermetricsRestClient 8 | 9 | client = SoccermetricsRestClient() 10 | 11 | home_club = "Liverpool" 12 | away_club = "Everton" 13 | 14 | # get match information 15 | match = client.club.information.get(home_team_name=home_club, 16 | away_team_name=away_club).all() 17 | 18 | # collect name and ID of all players in lineups 19 | lineup = client.link.get(match[0].link.lineups).all() 20 | players = {x.player:x.playerName for x in lineup} 21 | 22 | # get all segments of the match 23 | segments = client.link.get(match[0].link.analytics.match.segments).all() 24 | 25 | # loop over all segments 26 | # return players in each segment 27 | platoon = lambda rec: ', '.join([players[_id] for _id in rec]) 28 | 29 | for segment in segments: 30 | if segment.startStoppageMins > 0: 31 | match_time = "%d+%d" % (segment.startTimeMins, 32 | segment.startStoppageMins) 33 | else: 34 | match_time = "%d" % segment.startTimeMins 35 | print "Start segment: %s" % match_time 36 | print "Home Players: %s" % platoon(segment.homePlayersOn) 37 | print "Away Players: %s" % platoon(segment.awayPlayersOn) 38 | print "Duration: %s mins" % segment.duration -------------------------------------------------------------------------------- /examples/nationalteams/example_pbp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Compute changes in on-field composition during 4 | # a 2012-13 Premier League match. 5 | # 6 | 7 | from soccermetrics.rest import SoccermetricsRestClient 8 | 9 | client = SoccermetricsRestClient() 10 | 11 | home_club = "Brazil" 12 | away_club = "Croatia" 13 | 14 | # get match information 15 | match = client.natl.information.get(home_team_name=home_club, 16 | away_team_name=away_club).all() 17 | 18 | # collect name and ID of all players in lineups 19 | lineup = client.link.get(match[0].link.lineups).all() 20 | players = {x.player:x.playerName for x in lineup} 21 | 22 | # get all segments of the match 23 | segments = client.link.get(match[0].link.analytics.segments).all() 24 | 25 | # loop over all segments 26 | # return players in each segment 27 | platoon = lambda rec: ', '.join([players[_id] for _id in rec]) 28 | 29 | for segment in segments: 30 | if segment.startStoppageMins > 0: 31 | match_time = "%d+%d" % (segment.startTimeMins, 32 | segment.startStoppageMins) 33 | else: 34 | match_time = "%d" % segment.startTimeMins 35 | print "Start segment: %s" % match_time 36 | print "Home Players: %s" % platoon(segment.homePlayersOn) 37 | print "Away Players: %s" % platoon(segment.awayPlayersOn) 38 | print "Duration: %s mins" % segment.duration -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | Soccermetrics Connect API Client: Examples 2 | ========================================== 3 | 4 | This directory contains examples of tasks and analyses conducted with the API client library. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 |
ScriptDescription
matchupsCreate a fixture list.
conditionsRetrieve time/date/temperature data for a team's matches.
resultsCreate the classified football results.
refereeCreate a statistical record of a referee's performance, from time added 27 | on to cards shown to fouls called.
managerCreate a statistical record of a team's performance under a manager.
fantasyCreate time-series data of a player's fantasy statistics based 36 | on his on-field performance.
rotationsCalculate the number of squad rotations for a team's league matches.
fantasy Compute fantasy points by player and plot weekly fantasy performance.
pbpCompute changes in on-field composition during a league match.
52 | -------------------------------------------------------------------------------- /examples/clubs/example_matchups.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Create a fixture list by retrieving match dates, kickoff times, 4 | # and home/away teams, and other info for matches in a matchday. 5 | # 6 | 7 | import sys 8 | from soccermetrics.rest import SoccermetricsRestClient 9 | 10 | if __name__ == "__main__": 11 | 12 | # Create a SoccermetricsRestClient object. This call assumes that 13 | # SOCCERMETRICS_APP_ID and SOCCERMETRICS_APP_KEY are in your environment 14 | # variables, which we recommend. 15 | client = SoccermetricsRestClient() 16 | 17 | # Get starting and ending matchdays from command-line arguments. 18 | # Both numbers must be entered. 19 | if len(sys.argv) != 3: 20 | sys.stderr.write("Usage: python %s \n" % sys.argv[0]) 21 | raise SystemExit(1) 22 | matchday_start = int(sys.argv[1]) 23 | matchday_end = int(sys.argv[2]) 24 | 25 | # We will publish the match fixtures for the matchday range. 26 | for day in range(matchday_start,matchday_end+1): 27 | 28 | # Get match info data from all matches associated with a matchday. We 29 | # will make use of the sorting functionality in the Soccermetrics API. 30 | matches = client.club.information.get(matchday=day, 31 | sort='match_date,kickoff_time').all() 32 | 33 | # Now we can iterate over the sorted match list and print information 34 | # associated with the match, like the date, time, the two teams, the 35 | # venue and the referee. We'll format the string so that it looks nice. 36 | for match in matches: 37 | print "Matchday %02s %s %s %30s v %-30s \t%s (%s)" % (match.matchday, 38 | match.matchDate, match.kickoffTime, match.homeTeamName, 39 | match.awayTeamName, match.venueName, match.refereeName) 40 | # A newline to separate the matchdays. 41 | print 42 | 43 | -------------------------------------------------------------------------------- /examples/nationalteams/example_matchups.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Create a fixture list by retrieving match dates, kickoff times, 4 | # and home/away teams, and other info for matches in a matchday or round. 5 | # 6 | 7 | import sys 8 | from soccermetrics.rest import SoccermetricsRestClient 9 | 10 | if __name__ == "__main__": 11 | 12 | # Create a SoccermetricsRestClient object. This call assumes that 13 | # SOCCERMETRICS_APP_ID and SOCCERMETRICS_APP_KEY are in your environment 14 | # variables, which we recommend. 15 | client = SoccermetricsRestClient() 16 | 17 | # Get starting and ending matchdays from command-line arguments. 18 | # Both numbers must be entered. 19 | if len(sys.argv) != 3: 20 | sys.stderr.write("Usage: python %s \n" % sys.argv[0]) 21 | raise SystemExit(1) 22 | matchday_start = int(sys.argv[1]) 23 | matchday_end = int(sys.argv[2]) 24 | 25 | # We will publish the match fixtures for the matchday range. 26 | for day in range(matchday_start,matchday_end+1): 27 | 28 | # Get match info data from all matches associated with a matchday. We 29 | # will make use of the sorting functionality in the Soccermetrics API. 30 | matches = client.natl.information.get(matchday=day, 31 | sort='match_date,kickoff_time').all() 32 | 33 | # Now we can iterate over the sorted match list and print information 34 | # associated with the match, like the date, time, the two teams, the 35 | # venue and the referee. We'll format the string so that it looks nice. 36 | for match in matches: 37 | print "Matchday %02s %s %s %30s v %-30s \t%s (%s)" % (match.matchday, 38 | match.matchDate, match.kickoffTime, match.homeTeamName, 39 | match.awayTeamName, match.venueName, match.refereeName) 40 | # A newline to separate the matchdays. 41 | print 42 | 43 | -------------------------------------------------------------------------------- /examples/clubs/example_conditions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Retrieve match dates, kickoff times, and kickoff temps of all 4 | # of Everton's matches. 5 | # 6 | 7 | from soccermetrics.rest import SoccermetricsRestClient 8 | 9 | if __name__ == "__main__": 10 | 11 | # Create a SoccermetricsRestClient object. This call assumes that 12 | # SOCCERMETRICS_APP_ID and SOCCERMETRICS_APP_KEY are in your environment 13 | # variables, which we recommend. 14 | client = SoccermetricsRestClient() 15 | 16 | # Set club variable to Everton. Enables reuse if we want to 17 | # repeat this analysis with other clubs. 18 | club_name = "Everton" 19 | 20 | # Get match info data from all matches. We do this by first querying 21 | # for all of Everton's home matches, then by querying their away matches. 22 | # 23 | # The result is a list, so we can join them together by concatenation. 24 | # (Order doesn't matter, we're going to sort the result in a sec.) 25 | matches = [] 26 | for key in ['home_team_name','away_team_name']: 27 | param = {key: club_name} 28 | matches.extend(client.club.information.get(**param).all()) 29 | 30 | # Results from the API are unsorted, so sort the results by match date. 31 | # You can also sort by matchday but not all matches are played in order. 32 | sorted_matches = sorted(matches, key=lambda k: k.matchDate) 33 | 34 | # Now we can iterate over the sorted match list (and make sure you are 35 | # sorting over the sorted match list!) For every match record, retrieve 36 | # its match condition record, which contains weather conditions. 37 | # 38 | # Match date and kickoff time are in the match information record, but 39 | # temperatures are in the match condition record. 40 | print "Match Date,Kickoff Time,Kickoff Temp" 41 | for match in sorted_matches: 42 | condition = client.link.get(match.link.conditions).data[0] 43 | print "%s,%s,%2.1f" % (match.matchDate, match.kickoffTime, condition.kickoffTemp) 44 | -------------------------------------------------------------------------------- /examples/nationalteams/example_conditions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Retrieve match dates, kickoff times, and kickoff temps of all 4 | # of USA's matches. 5 | # 6 | 7 | from soccermetrics.rest import SoccermetricsRestClient 8 | 9 | if __name__ == "__main__": 10 | 11 | # Create a SoccermetricsRestClient object. This call assumes that 12 | # SOCCERMETRICS_APP_ID and SOCCERMETRICS_APP_KEY are in your environment 13 | # variables, which we recommend. 14 | client = SoccermetricsRestClient() 15 | 16 | # Set national team variable to USA. Enables reuse if we want to 17 | # repeat this analysis with other national teams. 18 | selection_name = "USA" 19 | 20 | # Get match info data from all matches. We do this by first querying 21 | # for all of USA's home matches, then by querying their away matches. 22 | # 23 | # The result is a list, so we can join them together by concatenation. 24 | # (Order doesn't matter, we're going to sort the result in a sec.) 25 | matches = [] 26 | for key in ['home_team_name','away_team_name']: 27 | param = {key: selection_name} 28 | matches.extend(client.natl.information.get(**param).all()) 29 | 30 | # Results from the API are unsorted, so sort the results by match date. 31 | # You can also sort by matchday but not all matches are played in order. 32 | sorted_matches = sorted(matches, key=lambda k: k.matchDate) 33 | 34 | # Now we can iterate over the sorted match list (and make sure you are 35 | # sorting over the sorted match list!) For every match record, retrieve 36 | # its match condition record, which contains weather conditions. 37 | # 38 | # Match date and kickoff time are in the match information record, but 39 | # temperatures are in the match condition record. 40 | print "Match Date,Kickoff Time,Kickoff Temp" 41 | for match in sorted_matches: 42 | condition = client.link.get(match.link.conditions).data[0] 43 | print "%s,%s,%2.1f" % (match.matchDate, match.kickoffTime, condition.kickoffTemp) 44 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from soccermetrics import __version__ 3 | from setuptools import setup, find_packages 4 | 5 | # To install the soccermetrics-client-py library, open a Terminal shell, 6 | # then run this file by typing: 7 | # 8 | # python setup.py install 9 | # 10 | # You need to have the setuptools module installed. Try reading the setuptools 11 | # documentation: http://pypi.python.org/pypi/setuptools 12 | REQUIRES = ["requests >= 1.2.3", "easydict >= 1.4"] 13 | 14 | if sys.version_info >= (3,0): 15 | REQUIRES.append('unittest2py3k') 16 | else: 17 | REQUIRES.append('unittest2') 18 | 19 | setup( 20 | name = "soccermetrics", 21 | version = __version__, 22 | description = "Soccermetrics API Client", 23 | author = "Soccermetrics Research", 24 | author_email = "api-support@soccermetrics.net", 25 | url = "http://github.com/soccermetrics/soccermetrics-client-py", 26 | keywords = ["soccer", "football", "soccermetrics", "soccer analytics", "API client"], 27 | install_requires = REQUIRES, 28 | packages = find_packages(), 29 | classifiers = [ 30 | "Development Status :: 4 - Beta", 31 | "Intended Audience :: Developers", 32 | "License :: OSI Approved :: MIT License", 33 | "Operating System :: OS Independent", 34 | "Programming Language :: Python", 35 | "Programming Language :: Python :: 2.5", 36 | "Programming Language :: Python :: 2.6", 37 | "Programming Language :: Python :: 2.7", 38 | "Topic :: Software Development :: Libraries :: Python Modules", 39 | ], 40 | long_description = """\ 41 | Soccermerics API Python Client Library 42 | -------------------------------------- 43 | 44 | DESCRIPTION 45 | The Soccermetrics API Client simplifies the process of makes calls to the 46 | Soccermetrics REST APIs. 47 | 48 | The Soccermetrics REST API are sports modeling and analytics layers on top 49 | of in-match soccer (football) data sources at varying levels of complexity. 50 | See http://www.github.com/soccermetrics/soccermetrics-client-py for more 51 | information. 52 | 53 | LICENSE The Soccermetrics API Python Client Library is distributed under 54 | the MIT License.""" ) 55 | -------------------------------------------------------------------------------- /examples/clubs/example_results.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Create a full-time result list for matches in a matchday. 4 | # 5 | 6 | import sys 7 | from soccermetrics.rest import SoccermetricsRestClient 8 | 9 | if __name__ == "__main__": 10 | 11 | # Create a SoccermetricsRestClient object. This call assumes that 12 | # SOCCERMETRICS_APP_ID and SOCCERMETRICS_APP_KEY are in your environment 13 | # variables, which we recommend. 14 | client = SoccermetricsRestClient() 15 | 16 | # Get starting and ending matchdays from command-line arguments. 17 | # Both numbers must be entered. 18 | if len(sys.argv) != 3: 19 | sys.stderr.write("Usage: python %s \n" % sys.argv[0]) 20 | raise SystemExit(1) 21 | matchday_start = int(sys.argv[1]) 22 | matchday_end = int(sys.argv[2]) 23 | 24 | # We will publish the match fixtures for the matchday range. 25 | for day in range(matchday_start,matchday_end+1): 26 | 27 | # Get match info data from all matches associated with a matchday. We 28 | # will make use of the sorting functionality in the Soccermetrics API. 29 | matches = client.club.information.get(matchday=day, 30 | sort='match_date,kickoff_time').all() 31 | 32 | # Now we can iterate over the sorted match list and we grab goal and 33 | # penalty kick events for each team. 34 | for match in matches: 35 | match_goals = [] 36 | # We use the hyperlinks in the match representation to retrieve goal and 37 | # penalty kick events under certain conditions. 38 | for team in [match.homeTeamName,match.awayTeamName]: 39 | goals = client.link.get(match.link.goals,scoring_team_name=team).all() 40 | pens = client.link.get(match.link.penalties,player_team_name=team, 41 | outcome_type="Goal").all() 42 | 43 | match_goals.append(len(goals)+len(pens)) 44 | 45 | print "Matchday %02s %s %s %30s %d-%d %-30s" % (match.matchday, 46 | match.matchDate, match.kickoffTime, match.homeTeamName, 47 | match_goals[0],match_goals[1],match.awayTeamName) 48 | # A newline to separate the matchdays. 49 | print 50 | -------------------------------------------------------------------------------- /examples/nationalteams/example_results.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Create a full-time result list for matches in a matchday. 4 | # 5 | 6 | import sys 7 | from soccermetrics.rest import SoccermetricsRestClient 8 | 9 | if __name__ == "__main__": 10 | 11 | # Create a SoccermetricsRestClient object. This call assumes that 12 | # SOCCERMETRICS_APP_ID and SOCCERMETRICS_APP_KEY are in your environment 13 | # variables, which we recommend. 14 | client = SoccermetricsRestClient() 15 | 16 | # Get starting and ending matchdays from command-line arguments. 17 | # Both numbers must be entered. 18 | if len(sys.argv) != 3: 19 | sys.stderr.write("Usage: python %s \n" % sys.argv[0]) 20 | raise SystemExit(1) 21 | matchday_start = int(sys.argv[1]) 22 | matchday_end = int(sys.argv[2]) 23 | 24 | # We will publish the match fixtures for the matchday range. 25 | for day in range(matchday_start,matchday_end+1): 26 | 27 | # Get match info data from all matches associated with a matchday. We 28 | # will make use of the sorting functionality in the Soccermetrics API. 29 | matches = client.natl.information.get(matchday=day, 30 | sort='match_date,kickoff_time').all() 31 | 32 | # Now we can iterate over the sorted match list and we grab goal and 33 | # penalty kick events for each team. 34 | for match in matches: 35 | match_goals = [] 36 | # We use the hyperlinks in the match representation to retrieve goal and 37 | # penalty kick events under certain conditions. 38 | for team in [match.homeTeamName,match.awayTeamName]: 39 | goals = client.link.get(match.link.goals,scoring_team_name=team).all() 40 | pens = client.link.get(match.link.penalties,player_team_name=team, 41 | outcome_type="Goal").all() 42 | 43 | match_goals.append(len(goals)+len(pens)) 44 | 45 | print "Matchday %02s %s %s %30s %d-%d %-30s" % (match.matchday, 46 | match.matchDate, match.kickoffTime, match.homeTeamName, 47 | match_goals[0],match_goals[1],match.awayTeamName) 48 | # A newline to separate the matchdays. 49 | print 50 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | How To Contribute 2 | ================= 3 | 4 | The Soccermetrics API clients are open source projects. Like all open source projects, they live from the generous help 5 | by multiple contributors who sacrifice their time. 6 | 7 | To make participation as pleasant as possible, this project adheres to the `Code of Conduct`_ by the Python Software Foundation. 8 | 9 | Here are a few hints and rules to get you started: 10 | 11 | - Add yourself to the AUTHORS.rst_ file in an alphabetical fashion. 12 | Every contribution is valuable and shall be credited. 13 | - No contribution is too small; please submit as many fixes for typos and grammar bloopers as you can! 14 | - Don’t *ever* break backward compatibility. 15 | If it ever *has* to happen for higher reasons, this project will follow the proven procedures_ of the Twisted project. 16 | - **Always** add tests and docs for your code. This is **NOT NEGOTIABLE**. Patches with missing tests or documentation won’t be merged. 17 | If a feature is not tested or documented, it doesn’t exist. 18 | - Obey `PEP 8`_ and `PEP 257`_. 19 | - Write `good commit messages`_. 20 | - Ideally, squash_ your commits, i.e. make your `pull requests`_ just one commit. 21 | 22 | .. note:: 23 | If you have something great but aren’t sure whether it adheres -- or even can adhere -- to the rules above: **please submit a pull request anyway**! 24 | 25 | In the best case, we can mold it into something, in the worst case the pull request gets politely closed. 26 | There’s absolutely nothing to fear. 27 | 28 | Thank you for considering to contribute to the Soccermetrics API clients! 29 | If you have any question or concerns, feel free to send an email to api-support@soccermetrics.net. 30 | 31 | 32 | .. _squash: http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html 33 | .. _`PEP 8`: http://www.python.org/dev/peps/pep-0008/ 34 | .. _`PEP 257`: http://www.python.org/dev/peps/pep-0257/ 35 | .. _`good commit messages`: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html 36 | .. _`Code of Conduct`: http://www.python.org/psf/codeofconduct/ 37 | .. _AUTHORS.rst: https://github.com/soccermetrics/soccermetrics-client-py/blob/master/AUTHORS.rst 38 | .. _procedures: http://twistedmatrix.com/trac/wiki/CompatibilityPolicy 39 | .. _`pull requests`: https://github.com/soccermetrics/soccermetrics-client-py/pulls 40 | .. _`freenode`: http://freenode.net -------------------------------------------------------------------------------- /soccermetrics/rest/resources/events.py: -------------------------------------------------------------------------------- 1 | from soccermetrics.rest.resources import Resource 2 | 3 | class MatchEventResource(Resource): 4 | """ 5 | Represents a Match Event REST resource. 6 | 7 | The Match Event resource controls access to data about the micro-events in a 8 | football match, such as ball touches, non-touch events, positional data, 9 | and explanatory data related to touch events. 10 | 11 | Micro-events are modelled as a combination of three components: events, 12 | actions, and modifiers. 13 | 14 | Derived from :class:`base.Resource`. 15 | """ 16 | def __init__(self, play, resource, base_uri, auth): 17 | """ 18 | Constructor of MatchEventResource class. 19 | 20 | :param play: Type of teams playing in matches. 21 | :type play: string 22 | :param resource: Name of resource. 23 | :type resource: string 24 | :param base_uri: Base URI of API. 25 | :type base_uri: string 26 | :param auth: Authentication credential. 27 | :type auth: tuple 28 | """ 29 | super(MatchEventResource, self).__init__(base_uri,auth) 30 | 31 | self.endpoint += "/%s/events/%s" % (play, resource) 32 | 33 | 34 | class MatchEvents(object): 35 | """ 36 | Establish access to Match Event objects (//events/ endpoint). 37 | 38 | +--------------+-----------------------+ 39 | | Attribute | Description | 40 | +==============+=======================+ 41 | | all | All micro events | 42 | +--------------+-----------------------+ 43 | | touches | All touch events | 44 | +--------------+-----------------------+ 45 | | actions | All event actions | 46 | +--------------+-----------------------+ 47 | """ 48 | def __init__(self, play, base_uri, auth): 49 | """ 50 | Constructor of MatchEvents class. 51 | 52 | :param play: Type of teams playing in matches. 53 | :type play: string 54 | :param base_uri: Base URI of API. 55 | :type base_uri: string 56 | :param auth: Authentication credential. 57 | :type auth: tuple 58 | """ 59 | self.all = MatchEventResource(play, "all", base_uri, auth) 60 | self.touches = MatchEventResource(play, "touches", base_uri, auth) 61 | self.actions = MatchEventResource(play, "actions", base_uri, auth) 62 | -------------------------------------------------------------------------------- /docs/source/guide/personnel.rst: -------------------------------------------------------------------------------- 1 | .. _personnel-resources: 2 | 3 | Personnel Resources 4 | =================== 5 | 6 | For more information, consult the `Personnel Resource`_ documentation. 7 | 8 | 9 | Retrieving a Player Record 10 | -------------------------- 11 | 12 | With the client you can ``get()`` a player record and use the ``link.get()`` method 13 | to access linked data, such as match appearances and actions. 14 | :: 15 | 16 | from soccermetrics.rest import SoccermetricsRestClient 17 | client = SoccermetricsRestClient() 18 | 19 | rvp = client.players.get(full_name='Robin Van Persie').all() 20 | rvp_matches = client.link.get(rvp.link.matches) 21 | rvp_goals = client.link.get(rvp.link.goals) 22 | 23 | Some players have nicknames: 24 | :: 25 | 26 | from soccermetrics.rest import SoccermetricsRestClient 27 | client = SoccermetricsRestClient() 28 | 29 | nani = client.players.get(full_name='Nani') 30 | 31 | .. note:: 32 | 33 | We recommend that you pass a Unicode string when searching on names 34 | or countries. For example, ``full_name=u"Roberto Martínez"`` . 35 | 36 | Retrieving a Manager Record 37 | --------------------------- 38 | 39 | As with players you can retrieve a manager record and linked data such as 40 | nationality, biographical data, and home and away matches managed. Some 41 | managers have nicknames (especially those who were former players), but 42 | that's very rare. 43 | :: 44 | 45 | from soccermetrics.rest import SoccermetricsRestClient 46 | client = SoccermetricsRestClient() 47 | 48 | saf = client.managers.get(full_name='Alex Ferguson') 49 | saf_scotland = client.link.get(saf.link.country) 50 | saf_home_matches = client.link.get(saf.link.homeMatches) 51 | 52 | 53 | Retrieving a Referee Record 54 | --------------------------- 55 | 56 | You can retrieve a referee record and data linked to it, which include 57 | biographical data and all matches officiated by the referee. Referees 58 | aren't considered special enough to merit nicknames (ones that you can print, 59 | anyway). 60 | :: 61 | 62 | from soccermetrics.rest import SoccermetricsRestClient 63 | client = SoccermetricsRestClient() 64 | 65 | webb = client.referees.get(full_name='Howard Webb') 66 | webb_england = client.link.get(webb.link.country) 67 | webb_matches = client.link.get(webb.link.matches) 68 | 69 | .. _`Personnel Resource`: http://soccermetrics.github.io/connect-api/resources/personnel.html -------------------------------------------------------------------------------- /docs/source/installation.rst: -------------------------------------------------------------------------------- 1 | .. _installation: 2 | 3 | Installation 4 | ============ 5 | 6 | The Soccermetrics API Python client library depends on the 7 | `Requests `_ and 8 | `easydict `_ libraries. You can install 9 | the client library within a virtual environment on your computer, or install 10 | it system-wide. 11 | 12 | Python 2.6+ is required. 13 | 14 | It's not required, but `autoenv `_ is 15 | very nice to have. 16 | 17 | Virtual Environment Install 18 | --------------------------- 19 | 20 | We recommend installing ``soccermetrics-client-py`` within a virtual environment 21 | using ``virtualenv``. That way you can run different versions of Python 22 | installations and libraries without dealing with conflicting dependencies. 23 | 24 | We also recommend installing `virtualenvwrapper `_. 25 | As it says on the label, it is a wrapper around `virtualenv` that manages the virtual 26 | environments on your machine and allows you to customize pre- and post-activation 27 | (and deactivation) behavior, such as setting environment variables or opening your text editor. 28 | 29 | Here is a link to `a nice tutorial on Virtualenv `_. 30 | And `a tutorial on virtualenvwrapper `_ as well. 31 | 32 | To install ``virtualenv`` on MacOS or Linux, create a folder and run one of these 33 | two commands as ``sudo``: 34 | 35 | .. sourcecode:: bash 36 | 37 | $ sudo easy_install virtualenv 38 | 39 | or 40 | 41 | .. sourcecode:: bash 42 | 43 | $ sudo pip install virtualenv 44 | 45 | If you are on Windows, `this link `_ 46 | will show you how to install ``pip`` and ``distribute``, which you will use to 47 | install ``virtualenv``. 48 | 49 | Download `the current zipped version of the source code`_ from GitHub, unzip the 50 | folder and run: 51 | 52 | .. sourcecode:: bash 53 | 54 | $ make install 55 | 56 | System-Wide Install 57 | ------------------- 58 | 59 | If you want to install ``soccermetrics-client-py`` system-wide -- not recommended 60 | because of possible library conflicts -- download 61 | `the current zipped version of the source code`_ from GitHub, unzip the 62 | folder and run: 63 | 64 | .. sourcecode:: bash 65 | 66 | $ make install 67 | 68 | .. _`the current zipped version of the source code`: https://github.com/soccermetrics/soccermetrics-client-py/archive/master.zip 69 | -------------------------------------------------------------------------------- /examples/clubs/example_referee.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Create a statistical record of a referee's performance, 4 | # from time added on to cards shown to fouls called. 5 | # 6 | 7 | from soccermetrics.rest import SoccermetricsRestClient 8 | 9 | if __name__ == "__main__": 10 | 11 | # Create a SoccermetricsRestClient object. This call assumes that 12 | # SOCCERMETRICS_APP_ID and SOCCERMETRICS_APP_KEY are in your environment 13 | # variables, which we recommend. 14 | client = SoccermetricsRestClient() 15 | 16 | # Get data for one referee. 17 | referee = client.referees.get(full_name="Howard Webb").data[0] 18 | 19 | # Get list of matches that referee directed 20 | matches = client.link.get(referee.link.club.matches,sort="match_date").all() 21 | 22 | # Use lengths of match halves to create list of time added on by referee 23 | timeon = [dict(first=45-match.firsthalfLength,second=45-match.secondhalfLength) 24 | for match in matches] 25 | 26 | # Iterate over list of matches and access penalty and disciplinary events 27 | # associated with each match. Report the number of penalties, yellow cards, 28 | # yellow/red and red cards to the screen, as well as time added on. 29 | penalties = [] 30 | yellows = [] 31 | reds = [] 32 | for match in matches: 33 | match_pens = client.link.get(match.link.penalties).all() 34 | match_yellows = client.link.get(match.link.offenses,card_type="Yellow").all() 35 | match_2ndyellows = client.link.get(match.link.offenses,card_type="Yellow/Red").all() 36 | match_reds = client.link.get(match.link.offenses,card_type="Red").all() 37 | 38 | penalties.extend(match_pens) 39 | yellows.extend(match_yellows) 40 | reds.extend(match_2ndyellows + match_reds) 41 | 42 | print """%s,%s v %s,%d,%d,%d,%d,%d,%d""" % (match.matchday, 43 | match.homeTeamName, match.awayTeamName, len(match_pens), 44 | len(match_yellows), len(match_2ndyellows), len(match_reds), 45 | match.firsthalfLength, match.secondhalfLength) 46 | 47 | # Create a temporary function to convert list of dictionaries to a list 48 | # of values associated with a key. 49 | dict2list = lambda vec,k: [x[k] for x in vec] 50 | 51 | # Create a unique list of fouls called by the referee. 52 | foul_list = set(dict2list(yellows,'foulType')+dict2list(reds,'foulType')) 53 | 54 | # Print list of fouls and number of yellow and red cards given for them. 55 | print "Foul Type,Yellows,Reds" 56 | for foul in foul_list: 57 | print "%30s,%2d,%2d" % (foul, 58 | sum([1 for x in yellows if x['foulType'] == foul]), 59 | sum([1 for x in reds if x['foulType'] == foul])) 60 | 61 | 62 | -------------------------------------------------------------------------------- /examples/nationalteams/example_referee.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Create a statistical record of a referee's performance, 4 | # from time added on to cards shown to fouls called. 5 | # 6 | 7 | from soccermetrics.rest import SoccermetricsRestClient 8 | 9 | if __name__ == "__main__": 10 | 11 | # Create a SoccermetricsRestClient object. This call assumes that 12 | # SOCCERMETRICS_APP_ID and SOCCERMETRICS_APP_KEY are in your environment 13 | # variables, which we recommend. 14 | client = SoccermetricsRestClient() 15 | 16 | # Get data for one referee. 17 | referee = client.referees.get(full_name="Howard Webb").data[0] 18 | 19 | # Get list of matches that referee directed 20 | matches = client.link.get(referee.link.natl.matches,sort="match_date").all() 21 | 22 | # Use lengths of match halves to create list of time added on by referee 23 | timeon = [dict(first=45-match.firsthalfLength,second=45-match.secondhalfLength) 24 | for match in matches] 25 | 26 | # Iterate over list of matches and access penalty and disciplinary events 27 | # associated with each match. Report the number of penalties, yellow cards, 28 | # yellow/red and red cards to the screen, as well as time added on. 29 | penalties = [] 30 | yellows = [] 31 | reds = [] 32 | for match in matches: 33 | match_pens = client.link.get(match.link.penalties).all() 34 | match_yellows = client.link.get(match.link.offenses,card_type="Yellow").all() 35 | match_2ndyellows = client.link.get(match.link.offenses,card_type="Yellow/Red").all() 36 | match_reds = client.link.get(match.link.offenses,card_type="Red").all() 37 | 38 | penalties.extend(match_pens) 39 | yellows.extend(match_yellows) 40 | reds.extend(match_2ndyellows + match_reds) 41 | 42 | print """%s,%s v %s,%d,%d,%d,%d,%d,%d""" % (match.matchday, 43 | match.homeTeamName, match.awayTeamName, len(match_pens), 44 | len(match_yellows), len(match_2ndyellows), len(match_reds), 45 | match.firsthalfLength, match.secondhalfLength) 46 | 47 | # Create a temporary function to convert list of dictionaries to a list 48 | # of values associated with a key. 49 | dict2list = lambda vec,k: [x[k] for x in vec] 50 | 51 | # Create a unique list of fouls called by the referee. 52 | foul_list = set(dict2list(yellows,'foulType')+dict2list(reds,'foulType')) 53 | 54 | # Print list of fouls and number of yellow and red cards given for them. 55 | print "Foul Type,Yellows,Reds" 56 | for foul in foul_list: 57 | print "%30s,%2d,%2d" % (foul, 58 | sum([1 for x in yellows if x['foulType'] == foul]), 59 | sum([1 for x in reds if x['foulType'] == foul])) 60 | 61 | 62 | -------------------------------------------------------------------------------- /examples/clubs/example_rotations.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Calculate squad rotation in Liverpool's league matches 4 | # in 2011-12 Premier League season. 5 | # 6 | 7 | from soccermetrics.rest import SoccermetricsRestClient 8 | 9 | if __name__ == "__main__": 10 | 11 | # Create a SoccermetricsRestClient object. This call assumes that 12 | # SOCCERMETRICS_APP_ID and SOCCERMETRICS_APP_KEY are in your environment 13 | # variables, which we recommend. 14 | client = SoccermetricsRestClient() 15 | 16 | # Create STARTING_XI constant and set it to 11. 17 | # It's not necessary, but we don't like to hard-code numbers. 18 | STARTING_XI = 11 19 | 20 | # Set club variable to Liverpool. Enables reuse if we want to 21 | # repeat this analysis with other clubs. 22 | club_name = "Liverpool" 23 | 24 | # Get unique IDs of all of Liverpool's matches. We want the matches in order, 25 | # so we do a few things: 26 | # (1) We query all of Liverpool's home matches, then all of their away matches 27 | # and join the results together. 28 | # (2) We sort the results by match date. 29 | matches = [] 30 | for key in ['home_team_name','away_team_name']: 31 | param = {key: club_name} 32 | matches.extend(client.match.information.get(**param).all()) 33 | sorted_matches = sorted(matches, key=lambda k: k.matchDate) 34 | 35 | starters = [] 36 | rotation_list = [] 37 | for match in sorted_matches: 38 | # Get all starting lineup information for Liverpool's matches. We are interested 39 | # in just the starting players for Liverpool, so we make player_team = 'Liverpool' 40 | # and set is_starting to True. 41 | lineup_data = client.link.get(match.link.lineups,player_team_name=club_name, 42 | is_starting=True).all() 43 | player_list = [x.player for x in lineup_data] 44 | starters.append(player_list) 45 | # After the first match, compute the number of players who are in the current 46 | # lineup and the previous one. Subtract that number from STARTING_XI to 47 | # get the number of squad rotations. 48 | if len(starters) > 1: 49 | num_no_changes = len(set(starters[-2]).intersection(starters[-1])) 50 | rotation_list.append(dict(date=match.matchDate, 51 | matchday=match.matchday, 52 | rotations=STARTING_XI-num_no_changes)) 53 | 54 | # Display the rotation history and a running total of squad rotations. 55 | print "Match\tMatch Date\tRotations\tTotal Rotations" 56 | cumul = [] 57 | for datum in rotation_list: 58 | cumul.append(datum['rotations']) 59 | print "%d\t%s\t%d\t%d" % (datum['matchday'],datum['date'],datum['rotations'], 60 | sum(cumul)) -------------------------------------------------------------------------------- /examples/nationalteams/example_rotations.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Calculate squad rotation in Liverpool's league matches 4 | # in 2011-12 Premier League season. 5 | # 6 | 7 | from soccermetrics.rest import SoccermetricsRestClient 8 | 9 | if __name__ == "__main__": 10 | 11 | # Create a SoccermetricsRestClient object. This call assumes that 12 | # SOCCERMETRICS_APP_ID and SOCCERMETRICS_APP_KEY are in your environment 13 | # variables, which we recommend. 14 | client = SoccermetricsRestClient() 15 | 16 | # Create STARTING_XI constant and set it to 11. 17 | # It's not necessary, but we don't like to hard-code numbers. 18 | STARTING_XI = 11 19 | 20 | # Set club variable to Liverpool. Enables reuse if we want to 21 | # repeat this analysis with other clubs. 22 | club_name = "Liverpool" 23 | 24 | # Get unique IDs of all of Liverpool's matches. We want the matches in order, 25 | # so we do a few things: 26 | # (1) We query all of Liverpool's home matches, then all of their away matches 27 | # and join the results together. 28 | # (2) We sort the results by match date. 29 | matches = [] 30 | for key in ['home_team_name','away_team_name']: 31 | param = {key: club_name} 32 | matches.extend(client.match.information.get(**param).all()) 33 | sorted_matches = sorted(matches, key=lambda k: k.matchDate) 34 | 35 | starters = [] 36 | rotation_list = [] 37 | for match in sorted_matches: 38 | # Get all starting lineup information for Liverpool's matches. We are interested 39 | # in just the starting players for Liverpool, so we make player_team = 'Liverpool' 40 | # and set is_starting to True. 41 | lineup_data = client.link.get(match.link.lineups,player_team_name=club_name, 42 | is_starting=True).all() 43 | player_list = [x.player for x in lineup_data] 44 | starters.append(player_list) 45 | # After the first match, compute the number of players who are in the current 46 | # lineup and the previous one. Subtract that number from STARTING_XI to 47 | # get the number of squad rotations. 48 | if len(starters) > 1: 49 | num_no_changes = len(set(starters[-2]).intersection(starters[-1])) 50 | rotation_list.append(dict(date=match.matchDate, 51 | matchday=match.matchday, 52 | rotations=STARTING_XI-num_no_changes)) 53 | 54 | # Display the rotation history and a running total of squad rotations. 55 | print "Match\tMatch Date\tRotations\tTotal Rotations" 56 | cumul = [] 57 | for datum in rotation_list: 58 | cumul.append(datum['rotations']) 59 | print "%d\t%s\t%d\t%d" % (datum['matchday'],datum['date'],datum['rotations'], 60 | sum(cumul)) -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | 2 | Soccermetrics API Python Client 3 | =============================== 4 | 5 | This is the documentation for the official Python client for the Soccermetrics 6 | REST API. 7 | 8 | **Contents:** 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | 13 | installation 14 | started 15 | guide/main 16 | reference/main 17 | 18 | .. _support: 19 | 20 | Support and Development 21 | ======================= 22 | 23 | All development occurs at `GitHub `_. 24 | To checkout the source: 25 | 26 | .. sourcecode:: bash 27 | 28 | $ git clone git@github.com:soccermetrics/soccermetrics-client-py.git dir_name 29 | 30 | Report bugs with the 31 | `GitHub issue tracker `_. 32 | 33 | We welcome all contributions to this project. Before you do contribute, please read our 34 | `contribution guidelines `_ first. 35 | 36 | If you have any questions about the client and its use in your applications, please seek help in the following order: 37 | 38 | - The documentation for the `API client`_ and the `API`_ itself. 39 | - The `Developer Forum`_ at the Soccermetrics API Developer Portal. 40 | - Message to our `Twitter feed `_ 41 | - Message to our support email address (api-support@soccermetrics.net). 42 | 43 | License 44 | ======= 45 | 46 | Copyright (c) 2011-2014 Soccermetrics Research, LLC. 47 | 48 | Permission is hereby granted, free of charge, to any person 49 | obtaining a copy of this software and associated documentation 50 | files (the "Software"), to deal in the Software without 51 | restriction, including without limitation the rights to use, 52 | copy, modify, merge, publish, distribute, sublicense, and/or sell 53 | copies of the Software, and to permit persons to whom the 54 | Software is furnished to do so, subject to the following 55 | conditions: 56 | 57 | The above copyright notice and this permission notice shall be 58 | included in all copies or substantial portions of the Software. 59 | 60 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 61 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 62 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 63 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 64 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 65 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 66 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 67 | OTHER DEALINGS IN THE SOFTWARE. 68 | 69 | Indices and tables 70 | ================== 71 | 72 | * :ref:`genindex` 73 | * :ref:`modindex` 74 | * :ref:`search` 75 | 76 | .. _`API`: http://soccermetrics.github.io/connect-api` 77 | .. _`API client`: http://soccermetrics.github.io/soccermetrics-client-py>` 78 | .. _`Developer Forum`: https://developer.soccermetrics.net/forum 79 | -------------------------------------------------------------------------------- /examples/clubs/example_manager.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- encoding: utf-8 -*- 3 | # 4 | # Create a statistical record of a team' performance 5 | # under a manager. 6 | # 7 | 8 | from soccermetrics.rest import SoccermetricsRestClient 9 | 10 | if __name__ == "__main__": 11 | 12 | # Create a SoccermetricsRestClient object. This call assumes that 13 | # SOCCERMETRICS_APP_ID and SOCCERMETRICS_APP_KEY are in your environment 14 | # variables, which we recommend. 15 | client = SoccermetricsRestClient() 16 | 17 | # Get data for one manager. 18 | manager = client.managers.get(full_name=u'Roberto Mancini').data[0] 19 | 20 | # Get lists of home and away matches in which manager was involved, using 21 | # the hyperlinks in the manager representation. Combine both lists and 22 | # sort by match date. 23 | hmatches = client.link.get(manager.link.club.homeMatches,sort="match_date").all() 24 | amatches = client.link.get(manager.link.club.awayMatches,sort="match_date").all() 25 | matches = sorted(hmatches + amatches,key=lambda k: k.matchDate) 26 | 27 | # Create a list of match substitution timings 28 | sub_list = [] 29 | for match in matches: 30 | # The team key is either home_team_name or away_team_name depending on 31 | # whether the manager is the home or away manager. This ternary statement 32 | # assigns the team key. 33 | team_key = 'home_team_name' if match.homeManagerName == manager.fullName \ 34 | else 'away_team_name' 35 | 36 | # We use the hyperlinks in the match representation to retrieve all 37 | # substitution data for one of the two teams involved, sorted by match time 38 | # and then stoppage time. 39 | subs = client.link.get(match.link.substitutions, 40 | team_name=getattr(match,team_key),sort="time_mins,stoppage_mins").data 41 | 42 | # Create a dictionary of substitution times in order. Fortunately 43 | # 'first', 'second', and 'third' are in alphabetical order! Initialize 44 | # all values in the dictionary to None, which allows us to account for 45 | # unused subs. 46 | sub_dict = dict(first=None,second=None,third=None) 47 | subkeys = sorted(sub_dict.keys()) 48 | # Loop over the ordered match substitution data and assign the match 49 | # times to the ordered keys. 50 | for k, sub in enumerate(subs): 51 | sub_dict[subkeys[k]] = int(sub.timeMins) 52 | 53 | # This is a diagnostic to the user. Display the matchday, the teams, 54 | # and the substitutions made by the manager. 55 | print "Matchday %2s: %s v %s: " % (match.matchday, match.homeTeamName, 56 | match.awayTeamName), 57 | print sub_dict['first'],sub_dict['second'],sub_dict['third'] 58 | 59 | # Append the substitution dictionary to the list. 60 | sub_list.append(sub_dict) 61 | 62 | # Create a temporary function to convert a list of dictionary key/values 63 | # to a list of values, given a specific key. If a value is None, 64 | # ignore it. 65 | dict2list = lambda vec,k: [x[k] for x in vec if x[k]] 66 | 67 | # Calculate the average time of the first, second, and third substitution. 68 | avg1st = sum(dict2list(sub_list,'first'))/len(dict2list(sub_list,'first')) 69 | avg2nd = sum(dict2list(sub_list,'second'))/len(dict2list(sub_list,'second')) 70 | avg3rd = sum(dict2list(sub_list,'third'))/len(dict2list(sub_list,'third')) 71 | 72 | print avg1st, avg2nd, avg3rd -------------------------------------------------------------------------------- /examples/nationalteams/example_manager.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- encoding: utf-8 -*- 3 | # 4 | # Create a statistical record of a team' performance 5 | # under a manager. 6 | # 7 | 8 | from soccermetrics.rest import SoccermetricsRestClient 9 | 10 | if __name__ == "__main__": 11 | 12 | # Create a SoccermetricsRestClient object. This call assumes that 13 | # SOCCERMETRICS_APP_ID and SOCCERMETRICS_APP_KEY are in your environment 14 | # variables, which we recommend. 15 | client = SoccermetricsRestClient() 16 | 17 | # Get data for one manager. 18 | manager = client.managers.get(full_name=u'Luiz Scolari').data[0] 19 | 20 | # Get lists of home and away matches in which manager was involved, using 21 | # the hyperlinks in the manager representation. Combine both lists and 22 | # sort by match date. 23 | hmatches = client.link.get(manager.link.natl.homeMatches,sort="match_date").all() 24 | amatches = client.link.get(manager.link.natl.awayMatches,sort="match_date").all() 25 | matches = sorted(hmatches + amatches,key=lambda k: k.matchDate) 26 | 27 | # Create a list of match substitution timings 28 | sub_list = [] 29 | for match in matches: 30 | # The team key is either home_team_name or away_team_name depending on 31 | # whether the manager is the home or away manager. This ternary statement 32 | # assigns the team key. 33 | team_key = 'home_team_name' if match.homeManagerName == manager.fullName \ 34 | else 'away_team_name' 35 | 36 | # We use the hyperlinks in the match representation to retrieve all 37 | # substitution data for one of the two teams involved, sorted by match time 38 | # and then stoppage time. 39 | subs = client.link.get(match.link.substitutions, 40 | team_name=getattr(match,team_key),sort="time_mins,stoppage_mins").data 41 | 42 | # Create a dictionary of substitution times in order. Fortunately 43 | # 'first', 'second', and 'third' are in alphabetical order! Initialize 44 | # all values in the dictionary to None, which allows us to account for 45 | # unused subs. 46 | sub_dict = dict(first=None,second=None,third=None) 47 | subkeys = sorted(sub_dict.keys()) 48 | # Loop over the ordered match substitution data and assign the match 49 | # times to the ordered keys. 50 | for k, sub in enumerate(subs): 51 | sub_dict[subkeys[k]] = int(sub.timeMins) 52 | 53 | # This is a diagnostic to the user. Display the matchday, the teams, 54 | # and the substitutions made by the manager. 55 | print "Matchday %2s: %s v %s: " % (match.matchday, match.homeTeamName, 56 | match.awayTeamName), 57 | print sub_dict['first'],sub_dict['second'],sub_dict['third'] 58 | 59 | # Append the substitution dictionary to the list. 60 | sub_list.append(sub_dict) 61 | 62 | # Create a temporary function to convert a list of dictionary key/values 63 | # to a list of values, given a specific key. If a value is None, 64 | # ignore it. 65 | dict2list = lambda vec,k: [x[k] for x in vec if x[k]] 66 | 67 | # Calculate the average time of the first, second, and third substitution. 68 | avg1st = sum(dict2list(sub_list,'first'))/len(dict2list(sub_list,'first')) 69 | avg2nd = sum(dict2list(sub_list,'second'))/len(dict2list(sub_list,'second')) 70 | avg3rd = sum(dict2list(sub_list,'third'))/len(dict2list(sub_list,'third')) 71 | 72 | print avg1st, avg2nd, avg3rd -------------------------------------------------------------------------------- /soccermetrics/rest/resources/analytics.py: -------------------------------------------------------------------------------- 1 | from soccermetrics.rest.resources import Resource 2 | 3 | class AnalyticsResource(Resource): 4 | """Represents an Analytics REST resource. 5 | 6 | The Match Analytics resources controls access to advanced player 7 | and team analytics related to events in a football match and 8 | derived from basic match data. 9 | 10 | Derived from :class:`resources.Resource`. 11 | """ 12 | def __init__(self, resource, base_uri, auth): 13 | """ 14 | Constructor of AnalyticsResource class. 15 | 16 | :param resource: Name of Analytics resource. 17 | :type resource: string 18 | :param base_uri: Base URI of API. 19 | :type base_uri: string 20 | :param auth: Authentication credential. 21 | :type auth: tuple 22 | """ 23 | super(AnalyticsResource, self).__init__(base_uri,auth) 24 | 25 | self.base_endpoint = self.endpoint + '/analytics/match' 26 | self.match = None 27 | self.resource = resource 28 | 29 | def EndpointURI(self): 30 | """ 31 | Construct URI of Analytics REST resource. 32 | 33 | URI is of format ``/analytics/match///``. 34 | 35 | :returns: URI of REST resource. 36 | :rtype: string 37 | """ 38 | return '/'.join(str(x) for x in [self.base_endpoint,self.match,self.resource] if x) 39 | 40 | def get(self, match, **kwargs): 41 | """ 42 | Retrieves a representation of Analytics REST resource. 43 | 44 | :param match: Unique ID associated with football match. 45 | :type match: integer 46 | :param kwargs: Collection of query parameters. 47 | :type kwargs: dict 48 | :returns: Resource representation. 49 | :rtype: Return value of :func:`Resource.get`. 50 | """ 51 | self.match = match 52 | self.endpoint = self.EndpointURI() 53 | return super(AnalyticsResource, self).get(**kwargs) 54 | 55 | def head(self): 56 | """ 57 | Retrieves header data of Analytics REST resource. 58 | 59 | :returns: Header data. 60 | :rtype: Return value of :func:`Resource.head`. 61 | """ 62 | self.match = None 63 | self.endpoint = self.EndpointURI() 64 | return super(AnalyticsResource, self).head() 65 | 66 | def options(self): 67 | """ 68 | Retrieves documentation of Analytics REST resource. 69 | 70 | If the status code is 200 (OK), returns the documentation. Otherwise, 71 | returns an error. 72 | 73 | Link resources are not included in the documentation. 74 | 75 | :returns: Resource documentation data. 76 | :rtype: Return value of :func:`Resource.options`. 77 | """ 78 | self.match = None 79 | self.endpoint = self.EndpointURI() 80 | return super(AnalyticsResource, self).options() 81 | 82 | class MatchAnalytics(object): 83 | """Represents a Match Analytics REST resource (/analytics/match endpoints). 84 | 85 | +----------------+--------------------------+ 86 | | Attribute | Description | 87 | +================+==========================+ 88 | | state | Match state | 89 | +----------------+--------------------------+ 90 | | segment | Match segments | 91 | +----------------+--------------------------+ 92 | | tsr | Match Total Shots Ratio | 93 | +----------------+--------------------------+ 94 | """ 95 | def __init__(self, base_uri, auth): 96 | self.state = AnalyticsResource("state", base_uri, auth) 97 | self.segment = AnalyticsResource("segment", base_uri, auth) 98 | self.tsr = AnalyticsResource("tsr", base_uri, auth) -------------------------------------------------------------------------------- /docs/source/guide/validation.rst: -------------------------------------------------------------------------------- 1 | .. _validation-resources: 2 | 3 | Validation Resources 4 | ==================== 5 | 6 | For more information, consult the `Validation Resource`_ documentation. 7 | 8 | The Validation resources are used to validate data inputted into the database, 9 | therefore ensuring data consistency and integrity. You might use these resources 10 | to populate dropdown boxes in an application or augment an API request. 11 | 12 | Validation resources are grouped into two categories: Match Overview resources, 13 | and Match Event resources. However, Match Overview and Match Event resources 14 | are accessed in the same way. There is also a Persons resource that supports 15 | all of the Personnel resources in the API. 16 | 17 | 18 | Retrieving Resources 19 | -------------------- 20 | 21 | To access the full representation of the Validation resource, make a ``get()`` call. 22 | :: 23 | 24 | from soccermetrics.rest import SoccermetricsRestClient 25 | client = SoccermetricsRestClient() 26 | 27 | confeds = client.confederations.get() 28 | for confed in confeds: 29 | print confed.name 30 | 31 | Results are paged, which is irrelevant for most of the Validation resources 32 | except the Countries resource. If you wish to receive all of the results 33 | at once, use the ``all()`` method on the response. 34 | :: 35 | 36 | from soccermetrics.rest import SoccermetricsRestClient 37 | client = SoccermetricsRestClient() 38 | 39 | resp = client.countries.get() 40 | countries = resp.all() 41 | 42 | 43 | Retrieving a Specific Record in a Resource 44 | ------------------------------------------ 45 | 46 | To access a specific resource, call ``get()`` with the unique ID of the record. 47 | You can also use the keyword ``uid`` in the function call. 48 | :: 49 | 50 | from soccermetrics.rest import SoccermetricsRestClient 51 | client = SoccermetricsRestClient() 52 | 53 | england = client.validation.countries.get("70570246789640e2a629d9b668df4c17") 54 | brazil = client.validation.countries.get(uid="70ce7b6218a24e1d94c21ed8b5cd070c") 55 | 56 | You can also use the query parameters associated with each resource to search 57 | for a specific record. 58 | :: 59 | 60 | from soccermetrics.rest import SoccermetricsRestClient 61 | client = SoccermetricsRestClient() 62 | 63 | cloudy_wx = client.validation.weather.get(desc='Cloudy') 64 | grass = client.validation.surfaces.get(name='Natural Grass') 65 | left_foot = client.validation.bodyparts.get(name='Left Foot') 66 | red_card = client.validation.cards.get(desc='Red') 67 | saved_penalty = client.validation.penalty_outcomes.get(desc='Saved') 68 | rooney = client.validation.persons.get(first_name='Wayne', last_name='Rooney') 69 | 70 | .. warning:: 71 | 72 | When retrieving records with the query parameter, there 73 | must be an exact match. 74 | 75 | Retrieving Records from the Persons Resource 76 | -------------------------------------------- 77 | 78 | The ``Persons`` resource provides access to biographical and demographical data 79 | of all people involved with a football match, such as managers, referees, and 80 | players. 81 | 82 | You can retrieve all of the records using the ``get()`` method, but it is not 83 | recommended because of the large size of the data returned. 84 | 85 | In practice you will use the query parameters to filter the records returned by 86 | the resource. Keep in mind that all of the fields are populated except for 87 | the ``nickname`` field, which is sometimes ``null``. 88 | :: 89 | 90 | from soccermetrics.rest import SoccermetricsRestClient 91 | client = SoccermetricsRestClient() 92 | 93 | argentina = client.validation.countries.get(name='Argentina').data 94 | argentines = client.validations.persons.get(country=argentina[0].id) 95 | 96 | If you search by string, it must match **exactly**, including any diacritical 97 | markings. So a search for ``Arsène Wenger`` 98 | :: 99 | 100 | from soccermetrics.rest import SoccermetricsRestClient 101 | client = SoccermetricsRestClient() 102 | 103 | wenger_ok = client.validation.persons.get(first_name=u'Arsène',last_name=u'Wenger') 104 | 105 | will create a match, but a search for ``Arsene Wenger`` 106 | :: 107 | 108 | from soccermetrics.rest import SoccermetricsRestClient 109 | client = SoccermetricsRestClient() 110 | 111 | wenger_err = client.validation.persons.get(first_name=u'Arsene',last_name=u'Wenger') 112 | 113 | will result in a ``SoccermetricsRestException``. 114 | 115 | .. _`Validation Resource`: http://soccermetrics.github.io/connect-api/resources/validation/main.html -------------------------------------------------------------------------------- /soccermetrics/rest/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from soccermetrics import SoccermetricsException 4 | from soccermetrics.rest.resources import Root 5 | from soccermetrics.rest.resources import Link 6 | from soccermetrics.rest.resources import Validation 7 | from soccermetrics.rest.resources import Personnel 8 | from soccermetrics.rest.resources import MatchPlay 9 | from soccermetrics.rest.resources import MatchAnalytics 10 | 11 | def find_credentials(): 12 | """ 13 | Search for API credentials in current environment. 14 | 15 | Looks for ``SOCCERMETRICS_APP_ID`` and ``SOCCERMETRICS_APP_KEY`` 16 | among the environment variables. Returns a ``(None, None)`` 17 | tuple if neither variable is not present in the environment. 18 | 19 | :returns: (account, api_key) or (None, None) 20 | """ 21 | try: 22 | account = os.environ["SOCCERMETRICS_APP_ID"] 23 | api_key = os.environ["SOCCERMETRICS_APP_KEY"] 24 | return account, api_key 25 | except KeyError: 26 | return None, None 27 | 28 | class SoccermetricsRestClient(object): 29 | """A client object for accessing the Soccermetrics REST API. 30 | 31 | +------------------+----------------------------+ 32 | | Attribute | Description | 33 | +==================+============================+ 34 | | root | Service root | 35 | +------------------+----------------------------+ 36 | | link | Link to resources | 37 | +------------------+----------------------------+ 38 | | validation | Validation resources | 39 | +------------------+----------------------------+ 40 | | players | Players resource | 41 | +------------------+----------------------------+ 42 | | managers | Managers resource | 43 | +------------------+----------------------------+ 44 | | referees | Referees resource | 45 | +------------------+----------------------------+ 46 | | club | Club match resources | 47 | +------------------+----------------------------+ 48 | | natl | Nat'l team match resources | 49 | +------------------+----------------------------+ 50 | | analytics | Match analytics resources | 51 | +------------------+----------------------------+ 52 | 53 | :param account: Soccermetrics API Application ID from `your account 54 | dashboard `_. 55 | :type account: string or None 56 | :param api_key: Soccermetrics API Application key from `your account 57 | dashboard `_. 58 | :type api_key: string or None 59 | """ 60 | def __init__(self, account=None, api_key=None, 61 | base_uri="https://api-connect.soccermetrics.net"): 62 | super(SoccermetricsRestClient, self).__init__() 63 | 64 | if not (account or api_key): 65 | account, api_key = find_credentials() 66 | if not (account and api_key): 67 | raise SoccermetricsException(""" 68 | Soccermetrics API could not find your credentials. Pass them into 69 | the SoccermetricsRestClient like this: 70 | 71 | client = SoccermetricsRestClient(account='xxxxxxxxxxx', 72 | api_key='yyyyyyyyyyyyyyyyy') 73 | 74 | Or, add your credentials to your shell environment. From the terminal, run 75 | 76 | echo "export SOCCERMETRICS_APP_ID=xxxxxxxxxxx" >> ~/.bashrc 77 | echo "export SOCCERMETRICS_APP_KEY=yyyyyyyyyyyyyyyyy" >> ~/.bashrc 78 | 79 | and be sure to replace the values for the application ID and auth key with the 80 | values from your Soccermetrics API Account at https://developer.soccermetrics.net/admin/access_details. 81 | """) 82 | 83 | auth = {'app_id': account, 'app_key': api_key} 84 | 85 | # Service root 86 | self.root = Root(base_uri, auth) 87 | 88 | # Access to links in API responses 89 | self.link = Link(base_uri, auth) 90 | 91 | # Validation objects 92 | self.validation = Validation(base_uri, auth) 93 | 94 | # Personnel objects 95 | self.players = Personnel("players", base_uri, auth) 96 | self.managers = Personnel("managers", base_uri, auth) 97 | self.referees = Personnel("referees", base_uri, auth) 98 | 99 | # Match objects for club/national team play 100 | self.club = MatchPlay("clubs", base_uri, auth) 101 | self.natl = MatchPlay("national", base_uri, auth) 102 | 103 | # Match Analytics objects 104 | self.analytics = MatchAnalytics(base_uri, auth) -------------------------------------------------------------------------------- /docs/source/started.rst: -------------------------------------------------------------------------------- 1 | .. _gettingstarted: 2 | 3 | Getting Started 4 | =============== 5 | 6 | To start using the Soccermetrics API, create a ``SoccermetricsRestClient``. 7 | 8 | Get Your API Credentials 9 | ------------------------ 10 | 11 | In order to use the Soccermetrics API Client, you must have a Soccermetrics API account. 12 | 13 | To get an account, go to the 14 | `Soccermetrics Connect API website `_ 15 | where you will fill in your contact information and select a usage plan. 16 | 17 | You will receive your API ID and secret key in a few minutes, unless you signed up 18 | for one of our student plans that require approval. 19 | 20 | Using Your API Credentials 21 | -------------------------- 22 | 23 | You'll need your Soccermetrics API credentials to use the ``SoccermetricsRestClient``. 24 | These get passed to the constructor or via environment variables. 25 | :: 26 | 27 | from soccermetrics.rest import SoccermetricsRestClient 28 | 29 | appID = "f53baabb" 30 | appKey = "demo1234567890demo1234567890" 31 | 32 | client = SoccermetricsRestClient(account=appID,api_key=appKey) 33 | 34 | .. warning:: This is a fictitious API ID and key. DO NOT USE IT!! 35 | 36 | If you call ``SoccermetricsRestClient`` without any parameters, the constructor 37 | will look for ``SOCCERMETRICS_APP_ID`` and ``SOCCERMETRICS_APP_KEY`` variables 38 | inside the current environment. 39 | 40 | We recommend that you keep your credentials in environment variables. 41 | That way you won't have to worry about accidentally posting your credentials 42 | in a public place. 43 | 44 | Match Information 45 | ----------------- 46 | 47 | Let's get information from all matches for a specific matchday in a league competition. 48 | We'll use it to access lineups and eventually match statistics. 49 | :: 50 | 51 | matches = client.club.match.information.get(matchday=10) 52 | for match in matches: 53 | print "%s: %s vs %s" % (match.match_date, match.home_team_name, 54 | match.away_team_name) 55 | 56 | .. warning:: 57 | 58 | In reality, you would pass the name of the competition in the call. Otherwise, 59 | you would receive all of the league matches for that matchday, over *all* of the 60 | competitions in the Soccermetrics database. 61 | 62 | To access matches from the group stage of a competition, we need to include *at minimum* the 63 | group round and group name: 64 | :: 65 | 66 | matches = client.natl.match.information.get(round_name='Group Stage', group=A, matchday=2) 67 | for match in matches: 68 | print "%s: %s vs %s" % (match.match_date, match.home_team_name, 69 | match.away_team_name) 70 | 71 | If we want to retrieve match from the knockout phase of a competition, we must pass 72 | the round name: 73 | :: 74 | 75 | matches = client.club.match.information.get(round_name="Third Round") 76 | for match in matches: 77 | print "%s: %s vs %s" % (match.match_date, match.home_team_name, 78 | match.away_team_name) 79 | 80 | Almost all of the resources have hyperlinks that refer to related resources. In this 81 | case we use the hyperlinks to access lineup data related to each match. We revise the 82 | above code so that we can get starting lineups: 83 | :: 84 | 85 | matches = client.club.match.information.get(matchday=10) 86 | for match in matches: 87 | for team_name in [match.home_team_name, match.away_team_name]: 88 | lineup_data = client.link.get( 89 | match.link.lineups, player_team_name=team_name, 90 | is_starting=True).all() 91 | starters = [x.player_name for x in lineup_data] 92 | print "%s: %s" % (team_name, starters) 93 | 94 | Match Statistics 95 | ---------------- 96 | 97 | We can go further and extract match statistics for each game, and create new 98 | types of statistics from those. Let's collect total shot data to develop 99 | Total Shot Ratios for each team. Again we use the hyperlinks associated 100 | with each match to access the statistical data. Revising the above code once more: 101 | :: 102 | 103 | ratio = [] 104 | matches = client.club.match.information.get(matchday=10) 105 | for match in matches: 106 | total_shots = [] 107 | for team_name in [match.home_team_name, match.away_team_name]: 108 | shot_data = client.link.get( 109 | match.link.stats.shots.totals, player_team_name=team_name).all() 110 | total_shots.append(sum([x.ontarget+x.offtarget for x in shots_data])) 111 | tsr = lambda k: total_shots[k]/float(sum(total_shots)) 112 | ratio.append(dict(team=match.home_team_name,tsr=tsr(0))) 113 | ratio.append(dict(team=match.away_team_name,tsr=tsr(1))) 114 | 115 | for data in ratio: 116 | print "%s: %0.3f" % (data['team'],data['tsr']) 117 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Soccermetrics API Python Client 2 | =============================== 3 | 4 | This is a Python module for using the Soccermetrics REST API. 5 | 6 | Installation 7 | ============ 8 | 9 | The Soccermetrics API Python client library depends on the 10 | `Requests `_ and 11 | `easydict `_ libraries. You can 12 | install the client library within a virtual environment on your 13 | computer, or install it system-wide. 14 | 15 | Python 2.6+ is required. 16 | 17 | It's not required, but 18 | `autoenv `_ is very nice to 19 | have. 20 | 21 | Virtual Environment Install 22 | --------------------------- 23 | 24 | We recommend installing ``soccermetrics-client-py`` within a virtual 25 | environment using ``virtualenv``. That way you can run different 26 | versions of Python installations and libraries without dealing with 27 | conflicting dependencies. 28 | 29 | We also recommend installing `virtualenvwrapper `_. 30 | As it says on the label, it is a wrapper around `virtualenv` that manages the virtual 31 | environments on your machine and allows you to customize pre- and post-activation 32 | (and deactivation) behavior, such as setting environment variables or opening your text editor. 33 | 34 | Here is a link to `a nice tutorial on 35 | Virtualenv `_. 36 | And `a tutorial on virtualenvwrapper `_ as well. 37 | 38 | To install ``virtualenv`` on MacOS or Linux, create a folder and run one 39 | of these two commands as ``sudo``: 40 | 41 | :: 42 | 43 | $ sudo easy_install virtualenv 44 | 45 | or 46 | 47 | :: 48 | 49 | $ sudo pip install virtualenv 50 | 51 | If you are on Windows, `this 52 | link `_ 53 | will show you how to install ``pip`` and ``distribute``, which you will 54 | use to install ``virtualenv``. 55 | 56 | Download [the current zipped version of the source code] 57 | (https://github.com/soccermetrics/soccermetrics-client-py/archive/master.zip) 58 | from GitHub, unzip the folder and run: 59 | 60 | :: 61 | 62 | $ make install 63 | 64 | System-Wide Install 65 | ------------------- 66 | 67 | If you want to install ``soccermetrics-client-py`` system-wide -- not 68 | recommended because of possible library conflicts -- download [the 69 | current zipped version of the source code] 70 | (https://github.com/soccermetrics/soccermetrics-client-py/archive/master.zip) 71 | from GitHub, unzip the folder and run: 72 | 73 | :: 74 | 75 | $ make install 76 | 77 | Getting Started 78 | =============== 79 | 80 | To start using the Soccermetrics API, create a 81 | ``SoccermetricsRestClient``. 82 | 83 | API Credentials 84 | --------------- 85 | 86 | You'll need your Soccermetrics API credentials to use the 87 | ``SoccermetricsRestClient``. These get passed to the constructor 88 | directly or via environment variables. 89 | 90 | :: 91 | 92 | from soccermetrics.rest import SoccermetricsRestClient 93 | 94 | appID = "f53baabb" 95 | appKey = "demo1234567890demo1234567890" 96 | 97 | client = SoccermetricsRestClient(account=appID,api_key=appKey) 98 | 99 | If you call ``SoccermetricsRestClient`` without any parameters, the 100 | constructor will look for ``SOCCERMETRICS_APP_ID`` and 101 | ``SOCCERMETRICS_APP_KEY`` variables inside the current environment. 102 | 103 | We recommend that you keep your credentials in environment variables. 104 | That way you won't have to worry about accidentally posting your 105 | credentials in a public place. 106 | 107 | :: 108 | 109 | from soccermetrics.rest import SoccermetricsRestClient 110 | client = SoccermetricsRestClient() 111 | 112 | Get Match Information 113 | --------------------- 114 | 115 | :: 116 | 117 | from soccermetrics.rest import SoccermetricsRestClient 118 | 119 | appID = "f53baabb" 120 | appKey = "demo1234567890demo1234567890" 121 | client = SoccermetricsRestClient() 122 | 123 | match = client.club.information.get(home_team_name="Everton", 124 | away_team_name="Liverpool").data[0] 125 | print match.matchday, match.matchDate, match.kickoffTime 126 | 127 | lineup_data = client.link.get(match.link.lineups, is_starting=True).all() 128 | for datum in lineup_data: 129 | print datum.playerName, datum.playerTeamName 130 | 131 | Get Player Statistical Data 132 | --------------------------- 133 | 134 | :: 135 | 136 | from soccermetrics.rest import SoccermetricsRestClient 137 | 138 | appID = "f53baabb" 139 | appKey = "demo1234567890demo1234567890" 140 | client = SoccermetricsRestClient() 141 | 142 | player = client.players.get(full_name=u'Robin van Persie').data[0] 143 | # goals at club level 144 | goals = client.link.get(player.link.club.goals) 145 | penalties = client.link.get(player.link.club.penalties,outcome_type="Goal") 146 | # goals at national team level 147 | natl_team_goals = client.link.get(player.link.natl.goals) 148 | natl_team_pens = client.link.get(player.link.natl.penalties,outcome_type="Goal") 149 | 150 | Get Advanced Analytics 151 | ---------------------- 152 | 153 | :: 154 | 155 | from soccermetrics.rest import SoccermetricsRestClient 156 | client = SoccermetricsRestClient() 157 | 158 | match = client.club.information.get(home_team_name='Manchester United', away_team_name='Stoke City').data[0] 159 | 160 | match_state_46 = client.link.get(match.link.analytics.club.state,time_mins=46) 161 | match_state_75 = client.link.get(match.link.analytics.club.state,time_mins=75) 162 | match_state_final = client.link.get(match.link.analytics.club.state) 163 | match_segments = client.link.get(match.link.analytics.club.segment) 164 | 165 | Learn More 166 | ========== 167 | 168 | - `Link to API 169 | documentation `_. 170 | - `Link to full client documentation 171 | here `_. 172 | 173 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Soccermetrics API Python Client 2 | =============================== 3 | 4 | This is a Python module for using the Soccermetrics REST API. 5 | 6 | # Build Status 7 | 8 | [![Build Status](https://travis-ci.org/soccermetrics/soccermetrics-client-py.svg?branch=master)] 9 | (https://travis-ci.org/soccermetrics/soccermetrics-client-py) 10 | 11 | # Installation 12 | 13 | The Soccermetrics API Python client library depends on the 14 | [Requests](http://docs.python-requests.org/en/latest/) and 15 | [easydict](http://pypi.python.org/pypi/easydict/) libraries. You can install 16 | the client library within a virtual environment on your computer, or install 17 | it system-wide. 18 | 19 | Python 2.6+ is required. 20 | 21 | It's not required, but [autoenv](https://github.com/kennethreitz/autoenv) is 22 | very nice to have. 23 | 24 | ## Virtual Environment Install 25 | 26 | We recommend installing `soccermetrics-client-py` within a virtual environment 27 | using `virtualenv`. That way you can run different versions of Python 28 | installations and libraries without dealing with conflicting dependencies. 29 | 30 | We also recommend installing [virtualenvwrapper](http://virtualenvwrapper.readthedocs.org/en/latest/). 31 | As it says on the label, it is a wrapper around `virtualenv` that manages the virtual 32 | environments on your machine and allows you to customize pre- and post-activation 33 | (and deactivation) behavior, such as setting environment variables or opening your text editor. 34 | 35 | Here is a link to [a nice tutorial on Virtualenv](http://sccr.mx/TXAL2F). 36 | And [a tutorial on virtualenvwrapper](http://sccr.mx/1pZ5Xtx) as well. 37 | 38 | To install `virtualenv` on MacOS or Linux, create a folder and run one of these 39 | two commands as `sudo`: 40 | 41 | $ sudo easy_install virtualenv 42 | 43 | or 44 | 45 | $ sudo pip install virtualenv 46 | 47 | If you are on Windows, [this link](http://flask.pocoo.org/docs/installation/#windows-easy-install) 48 | will show you how to install `pip` and `distribute`, which you will use to 49 | install `virtualenv`. 50 | 51 | Download [the current zipped version of the source code] 52 | (https://github.com/soccermetrics/soccermetrics-client-py/archive/master.zip) 53 | from GitHub, unzip the folder and run: 54 | 55 | $ make install 56 | 57 | ## System-Wide Install 58 | 59 | If you want to install `soccermetrics-client-py` system-wide -- not recommended 60 | because of possible library conflicts -- download 61 | [the current zipped version of the source code] 62 | (https://github.com/soccermetrics/soccermetrics-client-py/archive/master.zip) 63 | from GitHub, unzip the folder and run: 64 | 65 | $ make install 66 | 67 | # Getting Started 68 | 69 | To start using the Soccermetrics API, create a `SoccermetricsRestClient`. 70 | 71 | ## API Credentials 72 | 73 | You'll need your Soccermetrics API credentials to use the `SoccermetricsRestClient`. 74 | These get passed to the constructor directly or via environment variables. 75 | 76 | ```python 77 | from soccermetrics.rest import SoccermetricsRestClient 78 | 79 | appID = "f53baabb" 80 | appKey = "demo1234567890demo1234567890" 81 | 82 | client = SoccermetricsRestClient(account=appID,api_key=appKey) 83 | ``` 84 | 85 | If you call `SoccermetricsRestClient` without any parameters, the constructor 86 | will look for `SOCCERMETRICS_APP_ID` and `SOCCERMETRICS_APP_KEY` variables 87 | inside the current environment. 88 | 89 | We recommend that you keep your credentials in environment variables. 90 | That way you won't have to worry about accidentally posting your credentials 91 | in a public place. 92 | 93 | ```python 94 | from soccermetrics.rest import SoccermetricsRestClient 95 | client = SoccermetricsRestClient() 96 | ``` 97 | 98 | ## Get Match Information 99 | 100 | ```python 101 | from soccermetrics.rest import SoccermetricsRestClient 102 | 103 | appID = "f53baabb" 104 | appKey = "demo1234567890demo1234567890" 105 | client = SoccermetricsRestClient() 106 | 107 | # club teams 108 | match = client.club.information.get(home_team_name="Everton", 109 | away_team_name="Liverpool").data[0] 110 | print match.matchDate, match.kickoffTime 111 | 112 | lineup_data = client.link.get(match.link.lineups, is_starting=True).all() 113 | for datum in lineup_data: 114 | print datum.playerName, datum.playerTeamName 115 | 116 | # national teams 117 | natl_match = client.natl.information.get( 118 | home_team_name="Brazil", away_team_name="Croatia",phase="Group").data[0] 119 | lineup_data = client.link.get(natl_match.link.lineups, is_starting=True).all() 120 | for datum in lineup_data: 121 | print datum.playerName, datum.playerTeamName 122 | ``` 123 | 124 | ## Get Player Statistical Data 125 | 126 | ```python 127 | from soccermetrics.rest import SoccermetricsRestClient 128 | 129 | appID = "f53baabb" 130 | appKey = "demo1234567890demo1234567890" 131 | client = SoccermetricsRestClient() 132 | 133 | player = client.players.get(full_name=u'Robin van Persie').data[0] 134 | # goals at club level 135 | goals = client.link.get(player.link.club.goals) 136 | penalties = client.link.get(player.link.club.penalties,outcome_type="Goal") 137 | # goals at national team level 138 | natl_team_goals = client.link.get(player.link.natl.goals) 139 | natl_team_pens = client.link.get(player.link.natl.penalties,outcome_type="Goal") 140 | ``` 141 | 142 | ## Get Advanced Analytics 143 | 144 | ```python 145 | from soccermetrics.rest import SoccermetricsRestClient 146 | client = SoccermetricsRestClient() 147 | 148 | match = client.club.information.get(home_team_name='Manchester United', away_team_name='Stoke City').data[0] 149 | 150 | match_state_46 = client.link.get(match.link.analytics.match.state,time_mins=46) 151 | match_state_75 = client.link.get(match.link.analytics.match.state,time_mins=75) 152 | match_state_final = client.link.get(match.link.analytics.match.state) 153 | match_segments = client.link.get(match.link.analytics.match.segment) 154 | ``` 155 | 156 | # Learn More 157 | 158 | * [Link to API documentation](http://soccermetrics.github.io/connect-api). 159 | * [Link to full client documentation here](http://soccermetrics.github.io/soccermetrics-client-py). 160 | -------------------------------------------------------------------------------- /soccermetrics/rest/resources/validation.py: -------------------------------------------------------------------------------- 1 | from soccermetrics.rest.resources import Resource 2 | 3 | class ValidationResource(Resource): 4 | """ 5 | Establish access to Validation resources (/ endpoint). 6 | 7 | The Validation resources provide access to data that are used 8 | to ensure consistency and integrity in personnel and match 9 | records in the database. 10 | 11 | Derived from :class:`base.Resource`. 12 | """ 13 | 14 | def __init__(self, resource, base_uri, auth): 15 | """ 16 | Constructor of ValidationResource class. 17 | 18 | :param resource: Name of resource. 19 | :type resource: string 20 | :param base_uri: Base URI of API. 21 | :type base_uri: string 22 | :param auth: Authentication credential. 23 | :type auth: tuple 24 | """ 25 | super(ValidationResource, self).__init__(base_uri,auth) 26 | 27 | self.endpoint += "/%s" % resource 28 | 29 | class Validation(object): 30 | """Access to Validation objects. 31 | 32 | +----------------------+----------------------------+ 33 | | Attribute | Description | 34 | +======================+============================+ 35 | | phases | Competition Phases | 36 | +----------------------+----------------------------+ 37 | | groupRounds | Group Rounds | 38 | +----------------------+----------------------------+ 39 | | knockoutRounds | Knockout Rounds | 40 | +----------------------+----------------------------+ 41 | | confederations | Confederations | 42 | +----------------------+----------------------------+ 43 | | countries | Countries | 44 | +----------------------+----------------------------+ 45 | | competitions | Competitions | 46 | +----------------------+----------------------------+ 47 | | domesticCompetitions | Domestic Competitions | 48 | +----------------------+----------------------------+ 49 | | intlCompetitions | International Competitions | 50 | +----------------------+----------------------------+ 51 | | seasons | Seasons | 52 | +----------------------+----------------------------+ 53 | | teams | Teams | 54 | +----------------------+----------------------------+ 55 | | venues | Match Venues | 56 | +----------------------+----------------------------+ 57 | | timezones | Time Zones | 58 | +----------------------+----------------------------+ 59 | | nameOrder | Name Order | 60 | +----------------------+----------------------------+ 61 | | persons | Persons | 62 | +----------------------+----------------------------+ 63 | | positions | Positions | 64 | +----------------------+----------------------------+ 65 | | fouls | Fouls | 66 | +----------------------+----------------------------+ 67 | | cards | Cards | 68 | +----------------------+----------------------------+ 69 | | bodyparts | Body parts | 70 | +----------------------+----------------------------+ 71 | | shotevents | Shot events | 72 | +----------------------+----------------------------+ 73 | | penaltyOutcomes | Penalty Outcomes | 74 | +----------------------+----------------------------+ 75 | | actions | Event Actions | 76 | +----------------------+----------------------------+ 77 | | modifiers | Action Modifiers | 78 | +----------------------+----------------------------+ 79 | | modifierCategories | Action Modifier Categories | 80 | +----------------------+----------------------------+ 81 | | weather | Weather Conditions | 82 | +----------------------+----------------------------+ 83 | | surfaces | Surfaces | 84 | +----------------------+----------------------------+ 85 | """ 86 | 87 | def __init__(self, base_uri, auth): 88 | self.phases = ValidationResource("phases", base_uri, auth) 89 | self.groupRounds = ValidationResource("grouprounds", base_uri, auth) 90 | self.knockoutRounds = ValidationResource("knockoutrounds", base_uri, auth) 91 | self.confederations = ValidationResource("confederations", base_uri, auth) 92 | self.countries = ValidationResource("countries", base_uri, auth) 93 | self.competitions = ValidationResource("competitions", base_uri, auth) 94 | self.domesticCompetitions = ValidationResource("domestic_competitions", base_uri, auth) 95 | self.intlCompetitions = ValidationResource("intl_competitions", base_uri, auth) 96 | self.seasons = ValidationResource("seasons", base_uri, auth) 97 | self.teams = ValidationResource("teams", base_uri, auth) 98 | self.venues = ValidationResource("venues", base_uri, auth) 99 | self.timezones = ValidationResource("timezones", base_uri, auth) 100 | self.nameOrder = ValidationResource("name_order", base_uri, auth) 101 | self.persons = ValidationResource("persons", base_uri, auth) 102 | self.positions = ValidationResource("positions", base_uri, auth) 103 | self.fouls = ValidationResource("fouls", base_uri, auth) 104 | self.cards = ValidationResource("cards", base_uri, auth) 105 | self.bodyparts = ValidationResource("bodyparts", base_uri, auth) 106 | self.shotevents = ValidationResource("shotevents", base_uri, auth) 107 | self.penaltyOutcomes = ValidationResource("penalty_outcomes", base_uri, auth) 108 | self.actions = ValidationResource("actions", base_uri, auth) 109 | self.modifiers = ValidationResource("modifiers", base_uri, auth) 110 | self.modifierCategories = ValidationResource("modifier_categories", base_uri, auth) 111 | self.weather = ValidationResource("weather", base_uri, auth) 112 | self.surfaces = ValidationResource("surfaces", base_uri, auth) 113 | -------------------------------------------------------------------------------- /docs/source/guide/resources.rst: -------------------------------------------------------------------------------- 1 | .. _access-resources: 2 | 3 | Accessing Resources 4 | =================== 5 | 6 | To access Soccermetrics REST resources, you must first create a ``SoccermetricsRestClient`` object. 7 | 8 | Authentication 9 | -------------- 10 | 11 | The ``SoccermetricsRestClient`` requires the Soccermetrics API credentials. These credentials are 12 | passed directly to the constructor or through environment variables. We recommend that you keep your 13 | credentials in environment variables so that there is less of a chance of posting them in a public place. 14 | 15 | The ``SoccermetricsRestClient`` searches for the credentials in the ``SOCCERMETRICS_APP_ID`` and 16 | ``SOCCERMETRICS_APP_KEY`` variables inside the current environment. If they are present, a new 17 | object is created. 18 | :: 19 | 20 | from soccermetrics.rest import SoccermetricsRestClient 21 | client = SoccermetricsRestClient() 22 | 23 | Alternatively, you can pass the credentials directly to the constructor. 24 | :: 25 | 26 | from soccermetrics.rest import SoccermetricsRestClient 27 | 28 | APP_ID = "f53baabb" 29 | APP_KEY = "demo1234567890demo1234567890" 30 | client = SoccermetricsRestClient(account=APP_ID,api_key=APP_KEY) 31 | 32 | Making Requests 33 | --------------- 34 | 35 | The ``SoccermetricsRestClient`` gives you access to over 20 resources that contain 36 | historical match data and advanced analytics. Several resources have sub-resources 37 | that reach into the entire API. You can make GET, HEAD, or OPTIONS 38 | requests to these resources. 39 | 40 | .. _get-request: 41 | 42 | GET Requests 43 | ^^^^^^^^^^^^ 44 | 45 | To make a GET request, use the ``get()`` method associated with each resource. 46 | :: 47 | 48 | from soccermetrics.rest import SoccermetricsRestClient 49 | client = SoccermetricsRestClient() 50 | 51 | response = client.teams.get() 52 | 53 | For many requests you might want to filter results by one or more parameters. 54 | To do so, send keyword arguments to the ``get()`` method. 55 | :: 56 | 57 | from soccermetrics.rest import SoccermetricsRestClient 58 | client = SoccermetricsRestClient() 59 | 60 | response = client.club.match.information.get(home_team_name="Liverpool",away_team_name="Everton") 61 | 62 | GET requests also accept paging arguments. The following example will return 63 | page 4 of the match information resource with 20 matches per page. 64 | :: 65 | 66 | from soccermetrics.rest import SoccermetricsRestClient 67 | client = SoccermetricsRestClient() 68 | 69 | response = client.club.match.information.get(page=4,records=20) 70 | 71 | .. _head-request: 72 | 73 | HEAD Requests 74 | ^^^^^^^^^^^^^ 75 | 76 | To make a HEAD request, use the ``head()`` method associated with each resource. 77 | :: 78 | 79 | from soccermetrics.rest import SoccermetricsRestClient 80 | client = SoccermetricsRestClient() 81 | 82 | response = client.validation.teams.head() 83 | 84 | .. _options-request: 85 | 86 | OPTIONS Requests 87 | ^^^^^^^^^^^^^^^^ 88 | 89 | To make an OPTIONS request, use the ``options()`` method associated with each resource. 90 | :: 91 | 92 | from soccermetrics.rest import SoccermetricsRestClient 93 | client = SoccermetricsRestClient() 94 | 95 | response = client.natl.match.information.options() 96 | 97 | .. response-obj: 98 | 99 | Processing Responses 100 | -------------------- 101 | 102 | If your request is successful, your response will return a ``Response`` object. 103 | This object returns the following attributes: 104 | 105 | +------------+------------------------+ 106 | | Attribute | Description | 107 | +============+========================+ 108 | | status | Response status | 109 | +------------+------------------------+ 110 | | headers | Response headers | 111 | +------------+------------------------+ 112 | | data | Resource data | 113 | +------------+------------------------+ 114 | 115 | The following example handles the response from a resource request: 116 | :: 117 | 118 | from soccermetrics.rest import SoccermetricsRestClient 119 | client = SoccermetricsRestClient() 120 | 121 | response = client.natl.match.information.get(home_team_name="Brazil",away_team_name="Germany") 122 | 123 | status_code = response.status 124 | headers = response.headers 125 | data = response.data 126 | 127 | All resource responses are paged. Each response object has the following properties: 128 | 129 | +--------------+-------------------------+ 130 | | Attribute | Description | 131 | +==============+=========================+ 132 | | page | Current page number | 133 | +--------------+-------------------------+ 134 | | pages | Total number of pages | 135 | +--------------+-------------------------+ 136 | | records_page | Records per page | 137 | +--------------+-------------------------+ 138 | | records | Total number of records | 139 | +--------------+-------------------------+ 140 | 141 | The following methods permit paging through the response: 142 | 143 | +---------+---------------------------+ 144 | | Method | Description | 145 | +=========+===========================+ 146 | | first | First page of response | 147 | +---------+---------------------------+ 148 | | prev | Previous page of response | 149 | +---------+---------------------------+ 150 | | next | Next page of response | 151 | +---------+---------------------------+ 152 | | last | Last page of response | 153 | +---------+---------------------------+ 154 | 155 | If you wish to retrieve all of the records at once, use the ``all()`` method. 156 | :: 157 | 158 | from soccermetrics.rest import SoccermetricsRestClient 159 | client = SoccermetricsRestClient() 160 | 161 | goals = client.club.goals.get(matchday=5).all() 162 | 163 | .. _hyperlink-resources: 164 | 165 | Accessing Hyperlinked Resources 166 | ------------------------------- 167 | 168 | Many of the resources contain references to other subresources through hyperlinks. 169 | The client provides a ``link`` object to access these hyperlinks without 170 | having to reconstruct the data manually. You can then make ``get()``, ``head()``, 171 | and ``options()`` calls just as you would for any other resource. 172 | :: 173 | 174 | from soccermetrics.rest import SoccermetricsRestClient 175 | client = SoccermetricsRestClient() 176 | 177 | match = client.club.information.get(home_team_name="Liverpool",away_team_name="Everton") 178 | 179 | goals = client.link.get(match.link.goals) 180 | pens = client.link.get(match.link.penalties,outcome_type="Goal") 181 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | GH_PAGES_SOURCES = docs/source soccermetrics docs/Makefile 10 | 11 | # User-friendly check for sphinx-build 12 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 13 | $(error The `$(SPHINXBUILD)` command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the `$(SPHINXBUILD)` executable. Alternatively you can add the directory with the executable to your PATH. If you don`t have Sphinx installed, grab it from http://sphinx-doc.org/) 14 | endif 15 | 16 | # Internal variables. 17 | PAPEROPT_a4 = -D latex_paper_size=a4 18 | PAPEROPT_letter = -D latex_paper_size=letter 19 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) docs/source 20 | # the i18n builder cannot share the environment and doctrees with the others 21 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) docs/source 22 | 23 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 24 | 25 | help: 26 | @echo "Please use \`make ` where is one of" 27 | @echo " html to make standalone HTML files" 28 | @echo " dirhtml to make HTML files named index.html in directories" 29 | @echo " singlehtml to make a single large HTML file" 30 | @echo " pickle to make pickle files" 31 | @echo " json to make JSON files" 32 | @echo " htmlhelp to make HTML files and a HTML help project" 33 | @echo " qthelp to make HTML files and a qthelp project" 34 | @echo " devhelp to make HTML files and a Devhelp project" 35 | @echo " epub to make an epub" 36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 37 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 39 | @echo " text to make text files" 40 | @echo " man to make manual pages" 41 | @echo " texinfo to make Texinfo files" 42 | @echo " info to make Texinfo files and run them through makeinfo" 43 | @echo " gettext to make PO message catalogs" 44 | @echo " changes to make an overview of all changed/added/deprecated items" 45 | @echo " xml to make Docutils-native XML files" 46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 47 | @echo " linkcheck to check all external links for integrity" 48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 49 | 50 | clean: 51 | rm -rf $(BUILDDIR)/* 52 | 53 | html: 54 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 55 | @echo 56 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 57 | 58 | dirhtml: 59 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 60 | @echo 61 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 62 | 63 | singlehtml: 64 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 65 | @echo 66 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 67 | 68 | pickle: 69 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 70 | @echo 71 | @echo "Build finished; now you can process the pickle files." 72 | 73 | json: 74 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 75 | @echo 76 | @echo "Build finished; now you can process the JSON files." 77 | 78 | htmlhelp: 79 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 80 | @echo 81 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 82 | ".hhp project file in $(BUILDDIR)/htmlhelp." 83 | 84 | qthelp: 85 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 86 | @echo 87 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 88 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 89 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/SoccermetricsAPIPythonClient.qhcp" 90 | @echo "To view the help file:" 91 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/SoccermetricsAPIPythonClient.qhc" 92 | 93 | devhelp: 94 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 95 | @echo 96 | @echo "Build finished." 97 | @echo "To view the help file:" 98 | @echo "# mkdir -p $$HOME/.local/share/devhelp/SoccermetricsAPIPythonClient" 99 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/SoccermetricsAPIPythonClient" 100 | @echo "# devhelp" 101 | 102 | epub: 103 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 104 | @echo 105 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 106 | 107 | latex: 108 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 109 | @echo 110 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 111 | @echo "Run \`make` in that directory to run these through (pdf)latex" \ 112 | "(use \`make latexpdf` here to do that automatically)." 113 | 114 | latexpdf: 115 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 116 | @echo "Running LaTeX files through pdflatex..." 117 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 118 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 119 | 120 | latexpdfja: 121 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 122 | @echo "Running LaTeX files through platex and dvipdfmx..." 123 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 124 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 125 | 126 | text: 127 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 128 | @echo 129 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 130 | 131 | man: 132 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 133 | @echo 134 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 135 | 136 | texinfo: 137 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 138 | @echo 139 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 140 | @echo "Run \`make` in that directory to run these through makeinfo" \ 141 | "(use \`make info` here to do that automatically)." 142 | 143 | info: 144 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 145 | @echo "Running Texinfo files through makeinfo..." 146 | make -C $(BUILDDIR)/texinfo info 147 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 148 | 149 | gettext: 150 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 151 | @echo 152 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 153 | 154 | changes: 155 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 156 | @echo 157 | @echo "The overview file is in $(BUILDDIR)/changes." 158 | 159 | linkcheck: 160 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 161 | @echo 162 | @echo "Link check complete; look for any errors in the above output " \ 163 | "or in $(BUILDDIR)/linkcheck/output.txt." 164 | 165 | doctest: 166 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 167 | @echo "Testing of doctests in the sources finished, look at the " \ 168 | "results in $(BUILDDIR)/doctest/output.txt." 169 | 170 | xml: 171 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 172 | @echo 173 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 174 | 175 | pseudoxml: 176 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 177 | @echo 178 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 179 | 180 | gh-pages: 181 | git checkout gh-pages 182 | rm -rf build _sources _static _modules guide reference 183 | git checkout master $(GH_PAGES_SOURCES) 184 | git reset HEAD 185 | make -f docs/Makefile html 186 | mv -fv build/html/* ./ 187 | rm -rf $(GH_PAGES_SOURCES) build 188 | git add -A 189 | git commit -m "Generated gh-pages for `git log master -1 --pretty=short \ 190 | --abbrev-commit`" && git push origin gh-pages ; git checkout master 191 | -------------------------------------------------------------------------------- /soccermetrics/rest/resources/match.py: -------------------------------------------------------------------------------- 1 | from soccermetrics.rest.resources import Resource 2 | from soccermetrics.rest.resources.events import MatchEvents 3 | from soccermetrics.rest.resources.statistics import MatchStatistics 4 | 5 | class MatchResource(Resource): 6 | """ 7 | Represents a Match REST resource (/matches/ endpoints). 8 | 9 | The Match Resources are a collection of macro-events, micro-events, and 10 | summary statistics resources in the Soccermetrics Connect API. 11 | 12 | Derived from :class:`resources.Resource`. 13 | """ 14 | def __init__(self, play, base_uri, auth): 15 | """ 16 | Constructor of MatchResource class. 17 | 18 | :param play: Type of teams playing in matches. 19 | :type play: string 20 | :param base_uri: Base URI of API. 21 | :type base_uri: string 22 | :param auth: Authentication credential. 23 | :type auth: tuple 24 | """ 25 | super(MatchResource, self).__init__(base_uri,auth) 26 | 27 | self.base_endpoint = "%s/%s/matches" % (self.endpoint, play) 28 | self.match = None 29 | self.resource = None 30 | 31 | def EndpointURI(self): 32 | """ 33 | Construct URI of Match REST resource. 34 | 35 | URI is of format ``/matches///``. 36 | 37 | :returns: URI of REST resource. 38 | :rtype: string 39 | """ 40 | return '/'.join(str(x) for x in [self.base_endpoint,self.match,self.resource] if x) 41 | 42 | def get(self, match=None, uid=None, **kwargs): 43 | """ 44 | Retrieves a representation of Match REST resource. 45 | 46 | If the status code is 200 (OK), returns the representation. Otherwise, 47 | returns an error. 48 | 49 | :param match: Unique ID associated with football match. 50 | :type match: integer 51 | :param uid: Unique ID of API resource representation. 52 | :type uid: integer 53 | :param kwargs: Collection of query parameters. 54 | :type kwargs: dict 55 | :returns: Resource representation. 56 | :rtype: Return value of :func:`MatchResource.get`. 57 | """ 58 | self.match = match 59 | self.endpoint = self.EndpointURI() 60 | return super(MatchResource, self).get(uid, **kwargs) 61 | 62 | def head(self): 63 | """ 64 | Retrieves header data of Match REST resource. 65 | 66 | :returns: Header data. 67 | :rtype: Return value of :func:`MatchResource.head`. 68 | """ 69 | self.match = None 70 | self.endpoint = self.EndpointURI() 71 | return super(MatchResource, self).head() 72 | 73 | def options(self): 74 | """ 75 | Retrieves documentation of Match REST resource. 76 | 77 | If the status code is 200 (OK), returns the documentation. Otherwise, 78 | returns an error. 79 | 80 | Link resources are not included in the documentation. 81 | 82 | :returns: Resource documentation data. 83 | :rtype: Return value of :func:`MatchResource.options`. 84 | """ 85 | self.match = None 86 | self.endpoint = self.EndpointURI() 87 | return super(MatchResource, self).options() 88 | 89 | 90 | class MatchInformation(MatchResource): 91 | """ 92 | Access to Match Information resources (//matches/info resource). 93 | 94 | Derived from :class:`MatchResource`. 95 | """ 96 | def __init__(self, play, base_uri, auth): 97 | super(MatchInformation, self).__init__(play, base_uri, auth) 98 | self.resource = "info" 99 | 100 | 101 | class MatchConditions(MatchResource): 102 | """ 103 | Access to Match Conditions resources (//matches/conditions resource). 104 | 105 | Derived from :class:`MatchResource`. 106 | """ 107 | def __init__(self, play, base_uri, auth): 108 | super(MatchConditions, self).__init__(play, base_uri, auth) 109 | self.resource = "conditions" 110 | 111 | 112 | class MatchLineups(MatchResource): 113 | """ 114 | Access to Match Lineups resources (//matches/lineups resource). 115 | 116 | Derived from :class:`MatchResource`. 117 | """ 118 | def __init__(self, play, base_uri, auth): 119 | super(MatchLineups, self).__init__(play, base_uri, auth) 120 | self.resource = "lineups" 121 | 122 | 123 | class MatchGoals(MatchResource): 124 | """ 125 | Access to Match Goals resources (//matches/goals resource). 126 | 127 | Derived from :class:`MatchResource`. 128 | """ 129 | def __init__(self, play, base_uri, auth): 130 | super(MatchGoals, self).__init__(play, base_uri, auth) 131 | self.resource = "goals" 132 | 133 | class MatchPenalties(MatchResource): 134 | """ 135 | Access to Match Penalties resources (//matches/penalties resource). 136 | 137 | Derived from :class:`MatchResource`. 138 | """ 139 | def __init__(self, play, base_uri, auth): 140 | super(MatchPenalties, self).__init__(play, base_uri, auth) 141 | self.resource = "penalties" 142 | 143 | 144 | class MatchOffenses(MatchResource): 145 | """ 146 | Access to Match Offenses resources (//matches/offenses resource). 147 | 148 | Derived from :class:`MatchResource`. 149 | """ 150 | def __init__(self, play, base_uri, auth): 151 | super(MatchOffenses, self).__init__(play, base_uri, auth) 152 | self.resource = "offenses" 153 | 154 | 155 | class MatchSubstitutions(MatchResource): 156 | """ 157 | Access to Match Substitutions resources (//matches/substitutions resource). 158 | 159 | Derived from :class:`MatchResource`. 160 | """ 161 | def __init__(self, play, base_uri, auth): 162 | super(MatchSubstitutions, self).__init__(play, base_uri, auth) 163 | self.resource = "substitutions" 164 | 165 | 166 | class MatchShootouts(MatchResource): 167 | """ 168 | Access to Match Shootouts resources (//matches/shootouts resource). 169 | 170 | Derived from :class:`MatchResource`. 171 | """ 172 | def __init__(self, play, base_uri, auth): 173 | super(MatchShootouts, self).__init__(play, base_uri, auth) 174 | self.resource = "shootouts" 175 | 176 | 177 | class MatchPlay(object): 178 | """ 179 | Access to Match objects for a specific type of match (club, 180 | national team). 181 | 182 | +----------------+---------------------------+ 183 | | Attribute | Description | 184 | +================+===========================+ 185 | | information | Match information | 186 | +----------------+---------------------------+ 187 | | lineups | Match lineups | 188 | +----------------+---------------------------+ 189 | | conditions | Match conditions | 190 | +----------------+---------------------------+ 191 | | goals | Goal events | 192 | +----------------+---------------------------+ 193 | | penalties | Penalty kick events | 194 | +----------------+---------------------------+ 195 | | offenses | Disciplinary events | 196 | +----------------+---------------------------+ 197 | | substitutions | Substitution events | 198 | +----------------+---------------------------+ 199 | | shootouts | Penalty shootout events | 200 | +----------------+---------------------------+ 201 | | stats | Match statistics | 202 | +----------------+---------------------------+ 203 | | events | Match micro-events | 204 | +----------------+---------------------------+ 205 | """ 206 | def __init__(self, play, base_uri, auth): 207 | self.information = MatchInformation(play, base_uri, auth) 208 | self.lineups = MatchLineups(play, base_uri, auth) 209 | self.conditions = MatchConditions(play, base_uri, auth) 210 | self.goals = MatchGoals(play, base_uri, auth) 211 | self.penalties = MatchPenalties(play, base_uri, auth) 212 | self.offenses = MatchOffenses(play, base_uri, auth) 213 | self.substitutions = MatchSubstitutions(play, base_uri, auth) 214 | self.shootouts = MatchShootouts(play, base_uri, auth) 215 | 216 | self.stats = MatchStatistics(play, base_uri, auth) 217 | self.events = MatchEvents(play, base_uri, auth) -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Soccermetrics API Python Client documentation build configuration file, created by 4 | # sphinx-quickstart on Tue Sep 10 12:52:09 2013. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | import sphinx_bootstrap_theme 16 | 17 | # If extensions (or modules to document with autodoc) are in another directory, 18 | # add these directories to sys.path here. If the directory is relative to the 19 | # documentation root, use os.path.abspath to make it absolute, like shown here. 20 | sys.path.insert(0, os.path.abspath('../..')) 21 | 22 | # -- General configuration ----------------------------------------------------- 23 | 24 | # If your documentation needs a minimal Sphinx version, state it here. 25 | #needs_sphinx = '1.0' 26 | 27 | # Add any Sphinx extension module names here, as strings. They can be extensions 28 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 29 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage', 'sphinx.ext.viewcode'] 30 | 31 | # Add any paths that contain templates here, relative to this directory. 32 | templates_path = ['srtemplates'] 33 | 34 | # The suffix of source filenames. 35 | source_suffix = '.rst' 36 | 37 | # The encoding of source files. 38 | source_encoding = 'utf-8-sig' 39 | 40 | # The master toctree document. 41 | master_doc = 'index' 42 | 43 | # General information about the project. 44 | project = u'Soccermetrics API Python Client' 45 | copyright = u'2013-2014, Soccermetrics Research, LLC' 46 | 47 | # The version info for the project you're documenting, acts as replacement for 48 | # |version| and |release|, also used in various other places throughout the 49 | # built documents. 50 | # 51 | # The short X.Y version. 52 | version = '0.8' 53 | # The full version, including alpha/beta/rc tags. 54 | release = '0.8' 55 | 56 | # The language for content autogenerated by Sphinx. Refer to documentation 57 | # for a list of supported languages. 58 | #language = None 59 | 60 | # There are two options for replacing |today|: either, you set today to some 61 | # non-false value, then it is used: 62 | #today = '' 63 | # Else, today_fmt is used as the format for a strftime call. 64 | #today_fmt = '%B %d, %Y' 65 | 66 | # List of patterns, relative to source directory, that match files and 67 | # directories to ignore when looking for source files. 68 | exclude_patterns = [] 69 | 70 | # The reST default role (used for this markup: `text`) to use for all documents. 71 | #default_role = None 72 | 73 | # If true, '()' will be appended to :func: etc. cross-reference text. 74 | #add_function_parentheses = True 75 | 76 | # If true, the current module name will be prepended to all description 77 | # unit titles (such as .. function::). 78 | #add_module_names = True 79 | 80 | # If true, sectionauthor and moduleauthor directives will be shown in the 81 | # output. They are ignored by default. 82 | #show_authors = False 83 | 84 | # The name of the Pygments (syntax highlighting) style to use. 85 | pygments_style = 'sphinx' 86 | 87 | # A list of ignored prefixes for module index sorting. 88 | #modindex_common_prefix = [] 89 | 90 | # If true, keep warnings as "system message" paragraphs in the built documents. 91 | #keep_warnings = False 92 | 93 | 94 | # -- Options for HTML output --------------------------------------------------- 95 | 96 | # The theme to use for HTML and HTML Help pages. See the documentation for 97 | # a list of builtin themes. 98 | html_theme = 'bootstrap' 99 | 100 | # Theme options are theme-specific and customize the look and feel of a theme 101 | # further. For a list of options available for each theme, see the 102 | # documentation. 103 | #html_theme_options = {} 104 | 105 | # Add any paths that contain custom themes here, relative to this directory. 106 | html_theme_path = sphinx_bootstrap_theme.get_html_theme_path() 107 | 108 | # The name for this set of Sphinx documents. If None, it defaults to 109 | # " v documentation". 110 | html_title = "Soccermetrics API Python Client Documentation" 111 | 112 | # A shorter title for the navigation bar. Default is the same as html_title. 113 | html_short_title = "API Python Client" 114 | 115 | # The name of an image file (relative to this directory) to place at the top 116 | # of the sidebar. 117 | html_logo = 'srstatic/img/Logo.png' 118 | 119 | # The name of an image file (within the static path) to use as favicon of the 120 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 121 | # pixels large. 122 | #html_favicon = None 123 | 124 | # Add any paths that contain custom static files (such as style sheets) here, 125 | # relative to this directory. They are copied after the builtin static files, 126 | # so a file named "default.css" will overwrite the builtin "default.css". 127 | html_static_path = ['srstatic'] 128 | 129 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 130 | # using the given strftime format. 131 | html_last_updated_fmt = '%b %d, %Y' 132 | 133 | # If true, SmartyPants will be used to convert quotes and dashes to 134 | # typographically correct entities. 135 | #html_use_smartypants = True 136 | 137 | # Custom sidebar templates, maps document names to template names. 138 | #html_sidebars = {} 139 | 140 | # Additional templates that should be rendered to pages, maps page names to 141 | # template names. 142 | #html_additional_pages = {} 143 | 144 | # If false, no module index is generated. 145 | #html_domain_indices = True 146 | 147 | # If false, no index is generated. 148 | #html_use_index = True 149 | 150 | # If true, the index is split into individual pages for each letter. 151 | #html_split_index = False 152 | 153 | # If true, links to the reST sources are added to the pages. 154 | html_show_sourcelink = False 155 | 156 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 157 | #html_show_sphinx = True 158 | 159 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 160 | #html_show_copyright = True 161 | 162 | # Theme options 163 | html_theme_options = { 164 | # Navigation bar title. (Default: ``project`` value) 165 | 'navbar_title': "API Python Client", 166 | 167 | # Tab name for entire site. (Default: "Site") 168 | 'navbar_site_name': "Docs", 169 | 170 | # A list of tuples containing pages to link to. The value should 171 | # be in the form [(name, page), ..] 172 | 'navbar_links': [], 173 | 174 | # Global TOC depth for "site" navbar tab. (Default: 1) 175 | # Switching to -1 shows all levels. 176 | 'globaltoc_depth': 2, 177 | 178 | # Include hidden TOCs in Site navbar? 179 | # 180 | # Note: If this is "false", you cannot have mixed ``:hidden:`` and 181 | # non-hidden ``toctree`` directives in the same page, or else the build 182 | # will break. 183 | # 184 | # Values: "true" (default) or "false" 185 | 'globaltoc_includehidden': "true", 186 | 187 | # HTML navbar class (Default: "navbar") to attach to
element. 188 | # For black navbar, do "navbar navbar-inverse" 189 | 'navbar_class': "navbar navbar-inverse", 190 | 191 | # Fix navigation bar to top of page? 192 | # Values: "true" (default) or "false" 193 | 'navbar_fixed_top': "true", 194 | 195 | # Location of link to source. 196 | # Options are "nav" (default), "footer" or anything else to exclude. 197 | 'source_link_position': "nav", 198 | 199 | # Bootswatch (http://bootswatch.com/) theme. 200 | # Note that this is served off CDN, so won't be available offline. 201 | 'bootswatch_theme': "cosmo", 202 | } 203 | 204 | # If true, an OpenSearch description file will be output, and all pages will 205 | # contain a tag referring to it. The value of this option must be the 206 | # base URL from which the finished HTML is served. 207 | #html_use_opensearch = '' 208 | 209 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 210 | #html_file_suffix = None 211 | 212 | # Output file base name for HTML help builder. 213 | htmlhelp_basename = 'SoccermetricsAPIPythonClientdoc' 214 | 215 | 216 | # -- Options for LaTeX output -------------------------------------------------- 217 | 218 | latex_elements = { 219 | # The paper size ('letterpaper' or 'a4paper'). 220 | #'papersize': 'letterpaper', 221 | 222 | # The font size ('10pt', '11pt' or '12pt'). 223 | #'pointsize': '10pt', 224 | 225 | # Additional stuff for the LaTeX preamble. 226 | #'preamble': '', 227 | } 228 | 229 | # Grouping the document tree into LaTeX files. List of tuples 230 | # (source start file, target name, title, author, documentclass [howto/manual]). 231 | latex_documents = [ 232 | ('index', 'SoccermetricsAPIPythonClient.tex', u'Soccermetrics API Python Client Documentation', 233 | u'Soccermetrics Research, LLC', 'manual'), 234 | ] 235 | 236 | # The name of an image file (relative to this directory) to place at the top of 237 | # the title page. 238 | #latex_logo = None 239 | 240 | # For "manual" documents, if this is true, then toplevel headings are parts, 241 | # not chapters. 242 | #latex_use_parts = False 243 | 244 | # If true, show page references after internal links. 245 | #latex_show_pagerefs = False 246 | 247 | # If true, show URL addresses after external links. 248 | #latex_show_urls = False 249 | 250 | # Documents to append as an appendix to all manuals. 251 | #latex_appendices = [] 252 | 253 | # If false, no module index is generated. 254 | #latex_domain_indices = True 255 | 256 | 257 | # -- Options for manual page output -------------------------------------------- 258 | 259 | # One entry per manual page. List of tuples 260 | # (source start file, name, description, authors, manual section). 261 | man_pages = [ 262 | ('index', 'soccermetricsapipythonclient', u'Soccermetrics API Python Client Documentation', 263 | [u'Soccermetrics Research, LLC'], 1) 264 | ] 265 | 266 | # If true, show URL addresses after external links. 267 | #man_show_urls = False 268 | 269 | 270 | # -- Options for Texinfo output ------------------------------------------------ 271 | 272 | # Grouping the document tree into Texinfo files. List of tuples 273 | # (source start file, target name, title, author, 274 | # dir menu entry, description, category) 275 | texinfo_documents = [ 276 | ('index', 'SoccermetricsAPIPythonClient', u'Soccermetrics API Python Client Documentation', 277 | u'Soccermetrics Research, LLC', 'SoccermetricsAPIPythonClient', 'One line description of project.', 278 | 'Miscellaneous'), 279 | ] 280 | 281 | # Documents to append as an appendix to all manuals. 282 | #texinfo_appendices = [] 283 | 284 | # If false, no module index is generated. 285 | #texinfo_domain_indices = True 286 | 287 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 288 | #texinfo_show_urls = 'footnote' 289 | 290 | # If true, do not generate a @detailmenu in the "Top" node's menu. 291 | #texinfo_no_detailmenu = False 292 | -------------------------------------------------------------------------------- /tests/test_clients.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import unittest 3 | from os import environ 4 | 5 | import soccermetrics 6 | from soccermetrics import SoccermetricsException 7 | from soccermetrics.rest import SoccermetricsRestClient, find_credentials 8 | 9 | if sys.version_info[:2] == (2, 6): 10 | def assertIsInstance(self, obj, cls, msg=None): 11 | if not isinstance(obj, cls): 12 | standardMsg = '%s is not an instance of %r' % (safe_repr(obj), cls) 13 | self.fail(self._formatMessage(msg, standardMsg)) 14 | 15 | unittest.TestCase.assertIsInstance = assertIsInstance 16 | 17 | class NoCredentialsTest(unittest.TestCase): 18 | """ 19 | Test outcome of find_credentials() given absence of environment variables. 20 | """ 21 | 22 | def setUp(self): 23 | if 'SOCCERMETRICS_APP_KEY' in environ.keys(): 24 | del environ['SOCCERMETRICS_APP_KEY'] 25 | if 'SOCCERMETRICS_APP_ID' in environ.keys(): 26 | del environ['SOCCERMETRICS_APP_ID'] 27 | 28 | def test_no_credentials(self): 29 | """Verify find_credentials() returns pair of Nones if no env variables.""" 30 | account, api_key = find_credentials() 31 | self.assertEqual(account, None) 32 | self.assertEqual(api_key,None) 33 | 34 | class PartialCredentialsTest(unittest.TestCase): 35 | """ 36 | Test outcome of find_credentials() given partial presence of environment variables. 37 | """ 38 | 39 | def setUp(self): 40 | environ['SOCCERMETRICS_APP_KEY'] = 'AUTH_TOKEN' 41 | if 'SOCCERMETRICS_APP_ID' in environ.keys(): 42 | del environ['SOCCERMETRICS_APP_ID'] 43 | 44 | def test_partial_credentials(self): 45 | """Verify outcome of partial credentials sent to client initialization.""" 46 | account, api_key = find_credentials() 47 | self.assertEqual(account, None) 48 | self.assertEqual(api_key,None) 49 | 50 | class FullCredentialsTest(unittest.TestCase): 51 | """ 52 | Test outcome of find_credentials() given presence of environment variables. 53 | """ 54 | 55 | def setUp(self): 56 | environ['SOCCERMETRICS_APP_KEY'] = 'AUTH_TOKEN' 57 | environ['SOCCERMETRICS_APP_ID'] = 'APP_ID' 58 | 59 | def test_full_credentials(self): 60 | """Verify outcome of full credentials sent to client initialization.""" 61 | account, api_key = find_credentials() 62 | self.assertEqual(account, 'APP_ID') 63 | self.assertEqual(api_key,'AUTH_TOKEN') 64 | 65 | class RestClientTest(unittest.TestCase): 66 | """ 67 | Test for successful connection to SoccermetricsRestClient. 68 | """ 69 | 70 | def setUp(self): 71 | if 'SOCCERMETRICS_APP_KEY' in environ.keys(): 72 | del environ['SOCCERMETRICS_APP_KEY'] 73 | if 'SOCCERMETRICS_APP_ID' in environ.keys(): 74 | del environ['SOCCERMETRICS_APP_ID'] 75 | 76 | def test_fail_connect(self): 77 | """Verify exception raised if partial credentials sent.""" 78 | self.assertRaises(SoccermetricsException, SoccermetricsRestClient) 79 | 80 | def test_connect(self): 81 | """Verify client object created upon full credentials sent.""" 82 | self.client = SoccermetricsRestClient(account="APP_ID",api_key="APP_KEY") 83 | self.assertIsInstance(self.client, SoccermetricsRestClient) 84 | 85 | class RestClientAttributeTest(unittest.TestCase): 86 | """ 87 | Test for presence of attributes in SoccermetricsRestClient object. 88 | """ 89 | 90 | def setUp(self): 91 | self.client = SoccermetricsRestClient(account="APP_ID",api_key="APP_KEY") 92 | 93 | def test_https_endpoint(self): 94 | """Verify HTTPS endpoint.""" 95 | self.assertTrue(self.client.root.base_uri.startswith("https")) 96 | 97 | def test_service_root(self): 98 | """Verify existence of service root object.""" 99 | self.assertIsInstance(self.client.root, soccermetrics.rest.resources.Root) 100 | 101 | def test_link(self): 102 | """Verify existence of link object.""" 103 | self.assertIsInstance(self.client.link, soccermetrics.rest.resources.Link) 104 | 105 | def test_validation(self): 106 | """Verify existence of validation objects.""" 107 | self.assertIsInstance(self.client.validation.confederations, 108 | soccermetrics.rest.resources.validation.ValidationResource) 109 | self.assertIsInstance(self.client.validation.countries, 110 | soccermetrics.rest.resources.validation.ValidationResource) 111 | self.assertIsInstance(self.client.validation.competitions, 112 | soccermetrics.rest.resources.validation.ValidationResource) 113 | self.assertIsInstance(self.client.validation.domesticCompetitions, 114 | soccermetrics.rest.resources.validation.ValidationResource) 115 | self.assertIsInstance(self.client.validation.intlCompetitions, 116 | soccermetrics.rest.resources.validation.ValidationResource) 117 | self.assertIsInstance(self.client.validation.seasons, 118 | soccermetrics.rest.resources.validation.ValidationResource) 119 | self.assertIsInstance(self.client.validation.teams, 120 | soccermetrics.rest.resources.validation.ValidationResource) 121 | self.assertIsInstance(self.client.validation.venues, 122 | soccermetrics.rest.resources.validation.ValidationResource) 123 | self.assertIsInstance(self.client.validation.nameOrder, 124 | soccermetrics.rest.resources.validation.ValidationResource) 125 | self.assertIsInstance(self.client.validation.persons, 126 | soccermetrics.rest.resources.validation.ValidationResource) 127 | self.assertIsInstance(self.client.validation.positions, 128 | soccermetrics.rest.resources.validation.ValidationResource) 129 | self.assertIsInstance(self.client.validation.fouls, 130 | soccermetrics.rest.resources.validation.ValidationResource) 131 | self.assertIsInstance(self.client.validation.cards, 132 | soccermetrics.rest.resources.validation.ValidationResource) 133 | self.assertIsInstance(self.client.validation.bodyparts, 134 | soccermetrics.rest.resources.validation.ValidationResource) 135 | self.assertIsInstance(self.client.validation.shotevents, 136 | soccermetrics.rest.resources.validation.ValidationResource) 137 | self.assertIsInstance(self.client.validation.penaltyOutcomes, 138 | soccermetrics.rest.resources.validation.ValidationResource) 139 | self.assertIsInstance(self.client.validation.actions, 140 | soccermetrics.rest.resources.validation.ValidationResource) 141 | self.assertIsInstance(self.client.validation.modifiers, 142 | soccermetrics.rest.resources.validation.ValidationResource) 143 | self.assertIsInstance(self.client.validation.modifierCategories, 144 | soccermetrics.rest.resources.validation.ValidationResource) 145 | self.assertIsInstance(self.client.validation.weather, 146 | soccermetrics.rest.resources.validation.ValidationResource) 147 | self.assertIsInstance(self.client.validation.surfaces, 148 | soccermetrics.rest.resources.validation.ValidationResource) 149 | 150 | 151 | def test_personnel(self): 152 | """Verify existence of personnel resource objects.""" 153 | self.assertIsInstance(self.client.players, 154 | soccermetrics.rest.resources.Personnel) 155 | self.assertIsInstance(self.client.managers, 156 | soccermetrics.rest.resources.Personnel) 157 | self.assertIsInstance(self.client.referees, 158 | soccermetrics.rest.resources.Personnel) 159 | 160 | def test_club(self): 161 | """Verify existence of club match resource objects.""" 162 | self.assertIsInstance(self.client.club, 163 | soccermetrics.rest.resources.MatchPlay) 164 | 165 | self.assertIsInstance(self.client.club.information, 166 | soccermetrics.rest.resources.match.MatchInformation) 167 | 168 | self.assertIsInstance(self.client.club.lineups, 169 | soccermetrics.rest.resources.match.MatchLineups) 170 | 171 | self.assertIsInstance(self.client.club.conditions, 172 | soccermetrics.rest.resources.match.MatchConditions) 173 | 174 | self.assertIsInstance(self.client.club.goals, 175 | soccermetrics.rest.resources.match.MatchGoals) 176 | 177 | self.assertIsInstance(self.client.club.penalties, 178 | soccermetrics.rest.resources.match.MatchPenalties) 179 | 180 | self.assertIsInstance(self.client.club.offenses, 181 | soccermetrics.rest.resources.match.MatchOffenses) 182 | 183 | self.assertIsInstance(self.client.club.substitutions, 184 | soccermetrics.rest.resources.match.MatchSubstitutions) 185 | 186 | self.assertIsInstance(self.client.club.shootouts, 187 | soccermetrics.rest.resources.match.MatchShootouts) 188 | 189 | self.assertIsInstance(self.client.club.stats, 190 | soccermetrics.rest.resources.statistics.MatchStatistics) 191 | 192 | self.assertIsInstance(self.client.club.events, 193 | soccermetrics.rest.resources.events.MatchEvents) 194 | 195 | def test_natl(self): 196 | """Verify existence of national team match resource objects.""" 197 | 198 | self.assertIsInstance(self.client.natl, 199 | soccermetrics.rest.resources.MatchPlay) 200 | 201 | self.assertIsInstance(self.client.natl.information, 202 | soccermetrics.rest.resources.match.MatchInformation) 203 | 204 | self.assertIsInstance(self.client.natl.lineups, 205 | soccermetrics.rest.resources.match.MatchLineups) 206 | 207 | self.assertIsInstance(self.client.natl.conditions, 208 | soccermetrics.rest.resources.match.MatchConditions) 209 | 210 | self.assertIsInstance(self.client.natl.goals, 211 | soccermetrics.rest.resources.match.MatchGoals) 212 | 213 | self.assertIsInstance(self.client.natl.penalties, 214 | soccermetrics.rest.resources.match.MatchPenalties) 215 | 216 | self.assertIsInstance(self.client.natl.offenses, 217 | soccermetrics.rest.resources.match.MatchOffenses) 218 | 219 | self.assertIsInstance(self.client.natl.substitutions, 220 | soccermetrics.rest.resources.match.MatchSubstitutions) 221 | 222 | self.assertIsInstance(self.client.natl.shootouts, 223 | soccermetrics.rest.resources.match.MatchShootouts) 224 | 225 | self.assertIsInstance(self.client.natl.stats, 226 | soccermetrics.rest.resources.statistics.MatchStatistics) 227 | 228 | self.assertIsInstance(self.client.natl.events, 229 | soccermetrics.rest.resources.events.MatchEvents) 230 | 231 | def test_analytics(self): 232 | """Verify existence of analytics resource objects.""" 233 | self.assertIsInstance(self.client.analytics, 234 | soccermetrics.rest.resources.MatchAnalytics) 235 | 236 | 237 | -------------------------------------------------------------------------------- /docs/source/guide/match.rst: -------------------------------------------------------------------------------- 1 | .. _match-resources: 2 | 3 | Match Resources 4 | =============== 5 | 6 | The Match resources are a collection of macro-events, micro-events, and summary 7 | statistics resources in the Connect API. 8 | 9 | For more information, consult the `Match Resource`_ documentation for the API. 10 | 11 | Clubs or National Teams? 12 | ------------------------ 13 | 14 | The Connect API client divides Match resources into club and national team matches. The 15 | fact that countries are the "teams" in national team play introduces subtle but ultimately 16 | significant differences to match modeling, in particular match lineups. 17 | 18 | You can access club match data through the ``club`` object of the client: 19 | :: 20 | 21 | from soccermetrics.rest import SoccermetricsRestClient 22 | client = SoccermetricsRestClient() 23 | 24 | club_matches = client.club 25 | 26 | National team match data are accessed through the ``natl`` object of the client: 27 | :: 28 | 29 | from soccermetrics.rest import SoccermetricsRestClient 30 | client = SoccermetricsRestClient() 31 | 32 | natl_matches = client.natl 33 | 34 | Within the ``club`` and ``natl`` objects are sub-objects that permit access to macro-event, 35 | micro-event, and statistical resources of the API. 36 | 37 | Retrieving Match Resources 38 | -------------------------- 39 | 40 | As with other resources you can ``get()`` match records, but in the case of match 41 | records you can also use the unique ID of the football match to get data. 42 | :: 43 | 44 | from soccermetrics.rest import SoccermetricsRestClient 45 | client = SoccermetricsRestClient() 46 | 47 | match_info = client.natl.information.get('420aa27ce815499c85ec0301aff61ec4') 48 | 49 | If only one variable is passed to ``get()``, it is assumed to be the unique match ID. If 50 | two variables are passed without keywords, the first variable is assumed to be the 51 | unique match ID and the second the unique record ID. 52 | :: 53 | 54 | from soccermetrics.rest import SoccermetricsRestClient 55 | client = SoccermetricsRestClient() 56 | 57 | match_goal = client.natl.information.get('420aa27ce815499c85ec0301aff61ec4', 58 | '807f2a61bcea4a1bb98d66fface88b44') 59 | 60 | Using Phase Detail Information to Retrieve Match Data 61 | ----------------------------------------------------- 62 | 63 | In addition to using the match and record IDs to retrieve match records, you can use 64 | information on a match's `phase details`_ to perform similar retrievals. To do so, 65 | perform the search as keyword arguments in the ``get()`` call. 66 | :: 67 | 68 | from soccermetrics.rest import SoccermetricsRestClient 69 | client = SoccermetricsRestClient() 70 | 71 | # league matches -- use matchday 72 | matches = client.club.information.get(competition_name="Premier League", 73 | season_name="2011-12", matchday=5) 74 | 75 | # group matches -- use round_name and group 76 | matches = client.natl.goals.get(competition_name="FIFA World Cup", season_name="2014", 77 | round_name="Group Stage", group='B', matchday=1) 78 | 79 | # knockout matches -- use round_name 80 | matches = client.club.offenses.get(competition_name="UEFA Champions League", season_name="2010", 81 | round_name="Second Qualifying Round", matchday=1) 82 | 83 | Unique to other query parameters, you can insert an "*" to retrieve partial matches of 84 | ``round_name``. This is beneficial for knockout round names such as ``Round of 32 (1/16)``, 85 | ``Quarterfinal (1/4)``, or ``Semi-Final (1/2)``, where alternate descriptions of the 86 | round appear between parentheses. 87 | :: 88 | 89 | from soccermetrics.rest import SoccermetricsRestClient 90 | client = SoccermetricsRestClient() 91 | 92 | # knockout matches -- quarterfinal round 93 | matches = client.natl.information.get(competition_name="FIFA World Cup", season_name="2014", 94 | round_name="*1/4*") 95 | 96 | .. warning:: You cannot use phase details to filter Match Micro-Events or Statistics resources. 97 | 98 | Retrieving Match Information and Conditions 99 | ------------------------------------------- 100 | 101 | To access high-level data on a specific match or its environmental conditions, pass the 102 | unique ID through the ``match`` parameter in the ``get()`` call. 103 | :: 104 | 105 | from soccermetrics.rest import SoccermetricsRestClient 106 | client = SoccermetricsRestClient() 107 | 108 | match = client.natl.information.get('6b063a7370ee438da8a36a79ab10c9b7') 109 | conditions = client.natl.conditions.get('6b063a7370ee438da8a36a79ab10c9b7') 110 | 111 | There is only one ``information`` and ``conditions`` record per match ID, so a unique 112 | record ID is redundant. In fact, passing a record ID will result in a ``SoccermetricsRestException``. 113 | 114 | .. _match-macro: 115 | 116 | Retrieving Match Macro-Events 117 | ----------------------------- 118 | 119 | Match macro-events are the following: 120 | 121 | * ``lineups``: Match lineups 122 | * ``goals``: Goals 123 | * ``penalties``: Penalties 124 | * ``offenses``: Disciplinary offenses 125 | * ``substitutions``: Substitutions 126 | * ``shootouts``: Penalty shootouts 127 | 128 | If you want to access all of a specific type of macro-event that occurs in a match, 129 | pass the match ID to the ``get()`` call: 130 | :: 131 | 132 | from soccermetrics.rest import SoccermetricsRestClient 133 | client = SoccermetricsRestClient() 134 | 135 | match_goals = client.natl.goals.get('6b063a7370ee438da8a36a79ab10c9b7') 136 | match_subs = client.natl.substitutions.get('6b063a7370ee438da8a36a79ab10c9b7') 137 | 138 | If you want to access a *specific* macro-event from a match, pass the match ID **and** 139 | the record ID to the ``get()`` call: 140 | :: 141 | 142 | from soccermetrics.rest import SoccermetricsRestClient 143 | client = SoccermetricsRestClient() 144 | 145 | match_lineup_record = client.natl.lineups.get('6b063a7370ee438da8a36a79ab10c9b7', 146 | 'fcd53312a88c4e33b2a932746df0d3a8') 147 | match_goal = client.natl.goals.get('6b063a7370ee438da8a36a79ab10c9b7', 148 | '23d29e0d107f47068d8b85231b7f21c9') 149 | match_subs = client.natl.substitutions.get('6b063a7370ee438da8a36a79ab10c9b7', 150 | '05c62a4ceafb4dcd83d5a95a6b77baee') 151 | 152 | Of course, you don't have to use the match IDs to retrieve data -- you can also use 153 | query parameters such as ``home_team_name``, ``away_team_name``, or ``match_date``. 154 | Check the API documentation to find out which query parameters apply for the resource 155 | you're interested in. 156 | :: 157 | 158 | from soccermetrics.rest import SoccermetricsRestClient 159 | client = SoccermetricsRestClient() 160 | 161 | usa_goals = client.natl.goals.get(home_team_name="USA").all() + \ 162 | client.natl.goals.get(away_team_name="USA").all() 163 | 164 | june_25_matches = client.natl.information.get(match_date="2014-06-25") 165 | 166 | successful_pens = client.natl.penalties.get(outcome_type="Goal").all() 167 | 168 | .. _match-micro: 169 | 170 | Retrieving Match Micro-Events 171 | ----------------------------- 172 | 173 | Match micro-events are data on every event that occurs on the pitch during a match, whether 174 | non-touch events in which the ball is not touched (e.g. start/end period, injury stoppage, 175 | offside) or a ball touch event (e.g. pass, tackle, shot). 176 | 177 | There are three sub-objects attached to the ``events`` object: 178 | 179 | * ``all``: all micro-events 180 | * ``touches``: all touch-events 181 | * ``actions``: contextual data for a micro-event 182 | 183 | If you want to retrieve all of the micro-events for a match, pass the match ID to the 184 | ``get()`` call as a keyword parameter. 185 | :: 186 | 187 | from soccermetrics.rest import SoccermetricsRestClient 188 | client = SoccermetricsRestClient() 189 | 190 | match_events = client.natl.events.all.get(match='6b063a7370ee438da8a36a79ab10c9b7') 191 | 192 | To retrieve a specific micro-event, pass the unique record ID. 193 | :: 194 | 195 | from soccermetrics.rest import SoccermetricsRestClient 196 | client = SoccermetricsRestClient() 197 | 198 | pass_event = client.natl.events.all.get('1ab8728733454bd7942a5711518e7366') 199 | 200 | You can also use the query parameters to filter by specific types of events and match period. 201 | :: 202 | 203 | from soccermetrics.rest import SoccermetricsRestClient 204 | client = SoccermetricsRestClient() 205 | 206 | pass_events_2nd_half = client.natl.events.all.get(period=2, action_desc="Pass") 207 | 208 | .. warning:: At this time you cannot filter micro-events by time or space intervals. 209 | 210 | Almost every micro-event is associated with an event action, which will be included as a 211 | hyperlink in the response. We recommend that you use the ``link.get()`` call to access 212 | the action (for more information see :ref:`hyperlink-resources`). 213 | :: 214 | 215 | from soccermetrics.rest import SoccermetricsRestClient 216 | client = SoccermetricsRestClient() 217 | 218 | pass_event = client.natl.events.all.get('1ab8728733454bd7942a5711518e7366') 219 | pass_detail = client.link.get(pass_event.link.actions) 220 | 221 | Buf if you must, you can use the ``actions`` sub-object and the unique ID of the event action 222 | to retrieve **all** of the contextual data associated with an event action. 223 | :: 224 | 225 | from soccermetrics.rest import SoccermetricsRestClient 226 | client = SoccermetricsRestClient() 227 | 228 | pass_event = client.natl.events.all.get('1ab8728733454bd7942a5711518e7366') 229 | pass_detail = client.natl.events.actions.get('453baf79a6a742328e790bf29b01de57') 230 | 231 | .. _match-stats: 232 | 233 | Retrieving Match Statistics 234 | --------------------------- 235 | 236 | `Match Statistics`_ resources provide access to in-match statistical data of participating 237 | players in soccer matches. There are nine sub-objects of the ``statistics`` object 238 | in the client: 239 | 240 | * ``crosses``: Crossing statistics 241 | * ``defense``: Defensive statistics 242 | * ``fouls``: Foul statistics 243 | * ``goals``: Goal statistics 244 | * ``goalkeeper``: Goalkeeping statistics 245 | * ``passes``: Passing statistics 246 | * ``setpieces``: Set-piece statistics 247 | * ``shots``: Shot statistics 248 | * ``touches``: Ball touch statistics 249 | 250 | A specific statistical record is one tied to a player who appears in the match lineup. 251 | Use the lineup ID to access this record: 252 | :: 253 | 254 | from soccermetrics.rest import SoccermetricsRestClient 255 | client = SoccermetricsRestClient() 256 | 257 | total_passes = client.natl.stats.passes.totals.get('5815554e70e24a7cad674cc410f9da82') 258 | total_crosses = client.natl.stats.crosses.totals.get('5815554e70e24a7cad674cc410f9da82') 259 | 260 | You can also use the list of allowed query parameters to filter the statistics resources: 261 | :: 262 | 263 | from soccermetrics.rest import SoccermetricsRestClient 264 | client = SoccermetricsRestClient() 265 | 266 | total_passes = client.natl.stats.passes.totals.get(player_name=u"James Rodríguez").all() 267 | 268 | 269 | .. _`phase details`: http://soccermetrics.github.io/connect-api/resources/match/macros.html 270 | .. _`Match Resource`: http://soccermetrics.github.io/connect-api/resources/match/main.html 271 | .. _`Match Statistics`: http://soccermetrics.github.io/connect-api/resources/match/stats/main.html -------------------------------------------------------------------------------- /soccermetrics/rest/resources/base.py: -------------------------------------------------------------------------------- 1 | from easydict import EasyDict 2 | import requests 3 | 4 | from soccermetrics import SoccermetricsRestException 5 | from soccermetrics import __api_version__ as API_VERSION 6 | 7 | class Resource(object): 8 | """ 9 | Represents interactions with a REST API resource. 10 | 11 | Sets the high-level (versioning) endpoint for the API resource. 12 | 13 | :param base_uri: Base URI of API. 14 | :type base_uri: string 15 | :param auth: Authentication credential. 16 | :type auth: tuple 17 | """ 18 | def __init__(self, base_uri, auth): 19 | self.base_uri = base_uri 20 | self.auth = auth 21 | 22 | self.endpoint = "/%s" % API_VERSION 23 | 24 | def get(self, uid=None, **kwargs): 25 | """ 26 | Retrieves a representation of the REST resource. 27 | 28 | If the request is successful, returns an ``EasyDict`` object that 29 | uses dot notation for the response contents. 30 | 31 | A request may be unsuccessful for two reasons: 32 | 33 | * The API returns an error (HTTP code not 200). In this situation, 34 | the client raises a :exc:`~soccermetrics.SoccermetricsRestException` with 35 | the HTTP code, the request URI, and a detailed error message. 36 | * The ``requests`` module raises an exception. In this case, the client 37 | raises a :exc:`~soccermetrics.SoccermetricsRestException` with HTTP code 38 | 500, the request URI and a detailed error message from the module. 39 | 40 | :param uid: Unique ID of API resource representation. 41 | :type uid: integer 42 | :param kwargs: Collection of query parameters. 43 | :type kwargs: dict 44 | :returns: Resource representation. 45 | :rtype: ``EasyDict`` object. 46 | """ 47 | uri = "%s%s/%s" % (self.base_uri, self.endpoint, str(uid)) if uid else \ 48 | "%s%s" % (self.base_uri, self.endpoint) 49 | 50 | full_param_dict = dict(kwargs, **self.auth) 51 | 52 | try: 53 | resp = requests.get(uri,params=full_param_dict) 54 | if resp.status_code == 200: 55 | return Response(self.base_uri, self.auth, resp) 56 | else: 57 | data = resp.json() 58 | raise SoccermetricsRestException(resp.status_code,data['uri'],data['message']) 59 | except requests.exceptions.RequestException as e: 60 | raise SoccermetricsRestException(500, uri, msg=e) 61 | 62 | def head(self): 63 | """ 64 | Retrieves header data of the REST resource. 65 | 66 | The response is an object with the following attribute: 67 | 68 | +------------+-----------------------+ 69 | | Attribute | Description | 70 | +============+=======================+ 71 | | headers | Response headers | 72 | +------------+-----------------------+ 73 | 74 | If the request is successful, the client returns an ``EasyDict`` object that 75 | uses dot notation for the response contents. 76 | 77 | If the request is unsuccessful (HTTP code 4xx or 5xx), the client raises a 78 | :exc:`~soccermetrics.SoccermetricsRestException` that includes the HTTP status 79 | code and the request URI. 80 | 81 | :returns: Header data. 82 | :rtype: ``EasyDict`` object. 83 | """ 84 | uri = "%s%s" % (self.base_uri, self.endpoint) 85 | 86 | resp = requests.head(uri,params=self.auth) 87 | 88 | if resp.status_code < 400: 89 | return EasyDict(dict(headers=resp.headers)) 90 | else: 91 | raise SoccermetricsRestException(resp.status_code,resp.url) 92 | 93 | def options(self): 94 | """ 95 | Retrieves documentation of the REST resource representation. 96 | 97 | If the status code is 200 (OK), returns the documentation. Otherwise, 98 | returns an error. 99 | 100 | The response is an object with the following attributes: 101 | 102 | +------------+------------------------+ 103 | | Attribute | Description | 104 | +============+========================+ 105 | | headers | Response headers | 106 | +------------+------------------------+ 107 | | data | Resource documentation | 108 | +------------+------------------------+ 109 | 110 | Link resources are not included in the documentation. 111 | 112 | :returns: Resource documentation data. 113 | :rtype: ``EasyDict`` object. 114 | """ 115 | uri = "%s%s" % (self.base_uri, self.endpoint) 116 | 117 | resp = requests.options(uri,params=self.auth) 118 | 119 | if resp.status_code == 200: 120 | return Response(self.base_uri, self.auth, resp) 121 | else: 122 | raise SoccermetricsRestException(resp.status_code,resp.url) 123 | 124 | 125 | class Link(Resource): 126 | """ 127 | Represents interactions with hypertexted resources in the API. 128 | 129 | This class is effectively a client within a client, so it can access any API endpoint 130 | if the base URI and authentication pair are defined appropriately. 131 | 132 | Derived from :class:`Resource`. 133 | """ 134 | def __init__(self, base_uri, auth): 135 | """ 136 | Constructor of Link class. 137 | 138 | :param base_uri: Base URI of API. 139 | :type base_uri: string 140 | :param auth: Authentication credential. 141 | :type auth: tuple 142 | """ 143 | super(Link, self).__init__(base_uri, auth) 144 | 145 | def get(self, uri, **kwargs): 146 | """ 147 | Returns a representation of the REST resource. 148 | 149 | Its functionality is derived from :func:`Resource.get`. 150 | 151 | :param uri: URI of REST resource, relative to base URI. 152 | :type uri: string 153 | :param kwargs: Collection of query parameters. 154 | :type kwargs: dict 155 | :returns: Resource representation. 156 | :rtype: ``EasyDict`` object. 157 | """ 158 | self.endpoint = uri 159 | return super(Link, self).get(**kwargs) 160 | 161 | def head(self, uri): 162 | """ 163 | Retrieves header data of the REST resource. 164 | 165 | Its functionality is derived from :func:`Resource.head`. 166 | 167 | :param uri: URI of REST resource, relative to base URI. 168 | :type uri: string 169 | :returns: Header data. 170 | :rtype: ``EasyDict`` object. 171 | """ 172 | self.endpoint = uri 173 | return super(Link, self).head() 174 | 175 | def options(self, uri): 176 | """ 177 | Retrieves documentation of the REST resource representation. 178 | 179 | Its functionality is derived from :func:`Resource.options`. 180 | 181 | :param uri: URI of REST resource, relative to base URI. 182 | :type uri: string 183 | :returns: Resource documentation data. 184 | :rtype: ``EasyDict`` object. 185 | """ 186 | self.endpoint = uri 187 | return super(Link, self).options() 188 | 189 | 190 | class Response(Resource): 191 | """ 192 | Represents a REST API response object and its pagination properties and methods. 193 | 194 | The response is an object with the following attributes: 195 | 196 | +------------+-----------------------+ 197 | | Attribute | Description | 198 | +============+=======================+ 199 | | status | Response status code | 200 | +------------+-----------------------+ 201 | | headers | Response headers | 202 | +------------+-----------------------+ 203 | | _meta | Response meta-data, | 204 | | | internal use only | 205 | +------------+-----------------------+ 206 | | data | Response data | 207 | +------------+-----------------------+ 208 | 209 | The response also contains the following properties: 210 | 211 | +--------------+----------------------------+ 212 | | Property | Description | 213 | +==============+============================+ 214 | | page | Current page of response | 215 | +--------------+----------------------------+ 216 | | pages | Total pages of response | 217 | +--------------+----------------------------+ 218 | | records_page | Number of records per page | 219 | +--------------+----------------------------+ 220 | | records | Total records in response | 221 | +--------------+----------------------------+ 222 | 223 | You can use the following methods to page through the response: 224 | 225 | +--------------+-------------------------------+ 226 | | Method | Description | 227 | +==============+===============================+ 228 | | first() | First page of API response | 229 | +--------------+-------------------------------+ 230 | | prev() | Previous page of API response | 231 | +--------------+-------------------------------+ 232 | | next() | Next page of API response | 233 | +--------------+-------------------------------+ 234 | | last() | Last page of API response | 235 | +--------------+-------------------------------+ 236 | 237 | If you wish to access all of the data at once, there is the :func:`all` method. 238 | 239 | Derived from :class:`Resource`. 240 | """ 241 | def __init__(self, base_uri, auth, resp): 242 | """ 243 | Constructor of Response class. 244 | 245 | :param base_uri: Base URI of API. 246 | :type base_uri: string 247 | :param auth: Authentication credential pair. 248 | :type auth: tuple 249 | :param resp: response object from API request 250 | :type resp: JSON 251 | """ 252 | super(Response, self).__init__(base_uri, auth) 253 | jresp = resp.json() 254 | self._meta = EasyDict(jresp['meta']) 255 | self.status = resp.status_code 256 | self.headers = EasyDict(resp.headers) 257 | self.data = [EasyDict(rec) for rec in jresp['result']] 258 | 259 | def _iter(self): 260 | """ 261 | Custom iterator to retrieve all data from API response. 262 | 263 | Not intended for external use; if you wish to page 264 | through the response use the pagination methods. 265 | """ 266 | resp = self 267 | while True: 268 | yield (resp.data) 269 | if not resp._meta or not resp._meta.next: 270 | raise StopIteration 271 | else: 272 | resp = resp.next() 273 | 274 | @property 275 | def page(self): 276 | """ 277 | Current page of API response given pagination settings. 278 | 279 | If meta-data does not exist (i.e. no GET data), 280 | the current page is zero. 281 | """ 282 | return self._meta.page if self._meta else 0 283 | 284 | @property 285 | def pages(self): 286 | """ 287 | Total pages in API response given pagination settings. 288 | 289 | If meta-data does not exist (i.e. no GET data), the total 290 | number of pages is zero. 291 | """ 292 | return self._meta.total_pages if self._meta else 0 293 | 294 | @property 295 | def records_page(self): 296 | """ 297 | Records per page of the API response, either default or set by user's API request. 298 | 299 | If meta-data does not exist (i.e. no GET data), records per page is zero. 300 | """ 301 | return self._meta.records if self._meta else 0 302 | 303 | @property 304 | def records(self): 305 | """ 306 | Total records in the API response. 307 | 308 | If meta-data does not exist (i.e. no GET data), total number of records is zero. 309 | """ 310 | return self._meta.total_records if self._meta else 0 311 | 312 | def first(self): 313 | """ 314 | Go to first page of the API response. 315 | 316 | If meta-data does not exist (i.e. no GET data), return None. 317 | """ 318 | if self._meta: 319 | self.endpoint = self._meta.first 320 | return super(Response, self).get() 321 | else: 322 | return None 323 | 324 | def next(self): 325 | """ 326 | Go to next page of the API response. 327 | 328 | If meta-data does not exist (i.e. no GET data), return None. 329 | """ 330 | if self._meta and self._meta.next: 331 | self.endpoint = self._meta.next 332 | return super(Response, self).get() 333 | return None 334 | 335 | def prev(self): 336 | """ 337 | Go to previous page of the API response. 338 | 339 | If meta-data does not exist (i.e. no GET data), return None. 340 | """ 341 | if self._meta and self._meta.prev: 342 | self.endpoint = self._meta.prev 343 | return super(Response, self).get() 344 | return None 345 | 346 | def last(self): 347 | """ 348 | Go to last page of the API response. 349 | 350 | If meta-data does not exist (i.e. no GET data), return None. 351 | """ 352 | if self._meta: 353 | self.endpoint = self._meta.last 354 | return super(Response, self).get() 355 | return None 356 | 357 | def all(self): 358 | """ 359 | Retrieve all data of the API response at once. 360 | """ 361 | rec = [] 362 | for page in self._iter(): 363 | rec.extend(page) 364 | return rec -------------------------------------------------------------------------------- /tests/test_endpoints.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from soccermetrics.rest import SoccermetricsRestClient 4 | 5 | class ClientEndpointTest(unittest.TestCase): 6 | """ 7 | Test endpoints of API resources in client. 8 | """ 9 | 10 | def setUp(self): 11 | self.client = SoccermetricsRestClient(account="APP_ID",api_key="APP_KEY") 12 | 13 | def test_service_root(self): 14 | """Verify service root endpoint.""" 15 | self.assertEqual(self.client.root.endpoint, "/v1/") 16 | 17 | def test_validation_endpoints(self): 18 | """Verify validation resource endpoints without ID.""" 19 | self.assertEqual(self.client.validation.phases.endpoint, "/v1/phases") 20 | self.assertEqual(self.client.validation.groupRounds.endpoint, '/v1/grouprounds') 21 | self.assertEqual(self.client.validation.knockoutRounds.endpoint, '/v1/knockoutrounds') 22 | self.assertEqual(self.client.validation.confederations.endpoint, '/v1/confederations') 23 | self.assertEqual(self.client.validation.countries.endpoint, '/v1/countries') 24 | self.assertEqual(self.client.validation.competitions.endpoint, '/v1/competitions') 25 | self.assertEqual(self.client.validation.domesticCompetitions.endpoint, '/v1/domestic_competitions') 26 | self.assertEqual(self.client.validation.intlCompetitions.endpoint, '/v1/intl_competitions') 27 | self.assertEqual(self.client.validation.seasons.endpoint, '/v1/seasons') 28 | self.assertEqual(self.client.validation.teams.endpoint, '/v1/teams') 29 | self.assertEqual(self.client.validation.venues.endpoint, '/v1/venues') 30 | self.assertEqual(self.client.validation.timezones.endpoint, '/v1/timezones') 31 | self.assertEqual(self.client.validation.nameOrder.endpoint, '/v1/name_order') 32 | self.assertEqual(self.client.validation.persons.endpoint, '/v1/persons') 33 | self.assertEqual(self.client.validation.positions.endpoint, '/v1/positions') 34 | self.assertEqual(self.client.validation.fouls.endpoint, '/v1/fouls') 35 | self.assertEqual(self.client.validation.cards.endpoint, '/v1/cards') 36 | self.assertEqual(self.client.validation.bodyparts.endpoint, '/v1/bodyparts') 37 | self.assertEqual(self.client.validation.shotevents.endpoint, '/v1/shotevents') 38 | self.assertEqual(self.client.validation.penaltyOutcomes.endpoint, '/v1/penalty_outcomes') 39 | self.assertEqual(self.client.validation.actions.endpoint, '/v1/actions') 40 | self.assertEqual(self.client.validation.modifiers.endpoint, '/v1/modifiers') 41 | self.assertEqual(self.client.validation.modifierCategories.endpoint, '/v1/modifier_categories') 42 | self.assertEqual(self.client.validation.weather.endpoint, '/v1/weather') 43 | self.assertEqual(self.client.validation.surfaces.endpoint, '/v1/surfaces') 44 | 45 | def test_personnel_endpoints(self): 46 | """Verify personnel resource endpoints without ID.""" 47 | self.assertEqual(self.client.players.endpoint, '/v1/personnel/players') 48 | self.assertEqual(self.client.managers.endpoint, '/v1/personnel/managers') 49 | self.assertEqual(self.client.referees.endpoint, '/v1/personnel/referees') 50 | 51 | def test_club_match_endpoints(self): 52 | """Verify club match resource endpoints without match ID.""" 53 | self.assertEqual(self.client.club.information.EndpointURI(), '/v1/clubs/matches/info') 54 | self.assertEqual(self.client.club.lineups.EndpointURI(), '/v1/clubs/matches/lineups') 55 | self.assertEqual(self.client.club.conditions.EndpointURI(), '/v1/clubs/matches/conditions') 56 | self.assertEqual(self.client.club.goals.EndpointURI(), '/v1/clubs/matches/goals') 57 | self.assertEqual(self.client.club.penalties.EndpointURI(), '/v1/clubs/matches/penalties') 58 | self.assertEqual(self.client.club.offenses.EndpointURI(), '/v1/clubs/matches/offenses') 59 | self.assertEqual(self.client.club.substitutions.EndpointURI(), '/v1/clubs/matches/substitutions') 60 | self.assertEqual(self.client.club.shootouts.EndpointURI(), '/v1/clubs/matches/shootouts') 61 | 62 | def test_national_team_match_endpoints(self): 63 | """Verify national team match resource endpoints without match ID.""" 64 | self.assertEqual(self.client.natl.information.EndpointURI(), '/v1/national/matches/info') 65 | self.assertEqual(self.client.natl.lineups.EndpointURI(), '/v1/national/matches/lineups') 66 | self.assertEqual(self.client.natl.conditions.EndpointURI(), '/v1/national/matches/conditions') 67 | self.assertEqual(self.client.natl.goals.EndpointURI(), '/v1/national/matches/goals') 68 | self.assertEqual(self.client.natl.penalties.EndpointURI(), '/v1/national/matches/penalties') 69 | self.assertEqual(self.client.natl.offenses.EndpointURI(), '/v1/national/matches/offenses') 70 | self.assertEqual(self.client.natl.substitutions.EndpointURI(), '/v1/national/matches/substitutions') 71 | self.assertEqual(self.client.natl.shootouts.EndpointURI(), '/v1/national/matches/shootouts') 72 | 73 | def test_club_events_endpoints(self): 74 | """Verify club match micro-event endpoints without match or record IDs.""" 75 | self.assertEqual(self.client.club.events.all.endpoint, '/v1/clubs/events/all') 76 | self.assertEqual(self.client.club.events.touches.endpoint, '/v1/clubs/events/touches') 77 | self.assertEqual(self.client.club.events.actions.endpoint, '/v1/clubs/events/actions') 78 | 79 | def test_natl_events_endpoints(self): 80 | """Verify national team match micro-event endpoints without match or record IDs.""" 81 | self.assertEqual(self.client.natl.events.all.endpoint, '/v1/national/events/all') 82 | self.assertEqual(self.client.natl.events.touches.endpoint, '/v1/national/events/touches') 83 | self.assertEqual(self.client.natl.events.actions.endpoint, '/v1/national/events/actions') 84 | 85 | def test_club_statistics_endpoints(self): 86 | """Verify club match statistics endpoints without match or record IDs.""" 87 | self.assertEqual(self.client.club.stats.crosses.corners.endpoint, '/v1/clubs/stats/crosses/corners') 88 | self.assertEqual(self.client.club.stats.crosses.totals.endpoint, '/v1/clubs/stats/crosses/totals') 89 | 90 | self.assertEqual(self.client.club.stats.defense.actions.endpoint, '/v1/clubs/stats/defense/actions') 91 | self.assertEqual(self.client.club.stats.defense.blocks.endpoint, '/v1/clubs/stats/defense/blocks') 92 | self.assertEqual(self.client.club.stats.defense.clearances.endpoint, '/v1/clubs/stats/defense/clearances') 93 | self.assertEqual(self.client.club.stats.defense.goalline.endpoint, '/v1/clubs/stats/defense/goalline') 94 | self.assertEqual(self.client.club.stats.defense.tackles.endpoint, '/v1/clubs/stats/defense/tackles') 95 | 96 | self.assertEqual(self.client.club.stats.fouls.cards.endpoint, '/v1/clubs/stats/fouls/cards') 97 | self.assertEqual(self.client.club.stats.fouls.wins.endpoint, '/v1/clubs/stats/fouls/wins') 98 | 99 | self.assertEqual(self.client.club.stats.goals.assists.endpoint, '/v1/clubs/stats/goals/assists') 100 | self.assertEqual(self.client.club.stats.goals.bodyparts.endpoint, '/v1/clubs/stats/goals/bodyparts') 101 | self.assertEqual(self.client.club.stats.goals.locations.endpoint, '/v1/clubs/stats/goals/locations') 102 | self.assertEqual(self.client.club.stats.goals.penalties.endpoint, '/v1/clubs/stats/goals/penalties') 103 | self.assertEqual(self.client.club.stats.goals.totals.endpoint, '/v1/clubs/stats/goals/totals') 104 | 105 | self.assertEqual(self.client.club.stats.goalkeeper.actions.endpoint, '/v1/clubs/stats/goalkeeper/actions') 106 | self.assertEqual(self.client.club.stats.goalkeeper.goals.endpoint, '/v1/clubs/stats/goalkeeper/goals') 107 | self.assertEqual(self.client.club.stats.goalkeeper.shots.endpoint, '/v1/clubs/stats/goalkeeper/shots') 108 | self.assertEqual(self.client.club.stats.goalkeeper.saves.endpoint, '/v1/clubs/stats/goalkeeper/saves') 109 | 110 | self.assertEqual(self.client.club.stats.passes.directions.endpoint, '/v1/clubs/stats/passes/directions') 111 | self.assertEqual(self.client.club.stats.passes.lengths.endpoint, '/v1/clubs/stats/passes/lengths') 112 | self.assertEqual(self.client.club.stats.passes.locations.endpoint, '/v1/clubs/stats/passes/locations') 113 | self.assertEqual(self.client.club.stats.passes.totals.endpoint, '/v1/clubs/stats/passes/totals') 114 | 115 | self.assertEqual(self.client.club.stats.setpieces.corners.endpoint, '/v1/clubs/stats/setpieces/corners') 116 | self.assertEqual(self.client.club.stats.setpieces.freekicks.endpoint, '/v1/clubs/stats/setpieces/freekicks') 117 | self.assertEqual(self.client.club.stats.setpieces.throwins.endpoint, '/v1/clubs/stats/setpieces/throwins') 118 | 119 | self.assertEqual(self.client.club.stats.shots.bodyparts.endpoint, '/v1/clubs/stats/shots/bodyparts') 120 | self.assertEqual(self.client.club.stats.shots.locations.endpoint, '/v1/clubs/stats/shots/locations') 121 | self.assertEqual(self.client.club.stats.shots.plays.endpoint, '/v1/clubs/stats/shots/plays') 122 | self.assertEqual(self.client.club.stats.shots.totals.endpoint, '/v1/clubs/stats/shots/totals') 123 | 124 | self.assertEqual(self.client.club.stats.touches.duels.endpoint, '/v1/clubs/stats/touches/duels') 125 | self.assertEqual(self.client.club.stats.touches.locations.endpoint, '/v1/clubs/stats/touches/locations') 126 | self.assertEqual(self.client.club.stats.touches.totals.endpoint, '/v1/clubs/stats/touches/totals') 127 | 128 | def test_natl_statistics_endpoints(self): 129 | """Verify national team match statistics endpoints without match or record IDs.""" 130 | self.assertEqual(self.client.natl.stats.crosses.corners.endpoint, '/v1/national/stats/crosses/corners') 131 | self.assertEqual(self.client.natl.stats.crosses.totals.endpoint, '/v1/national/stats/crosses/totals') 132 | 133 | self.assertEqual(self.client.natl.stats.defense.actions.endpoint, '/v1/national/stats/defense/actions') 134 | self.assertEqual(self.client.natl.stats.defense.blocks.endpoint, '/v1/national/stats/defense/blocks') 135 | self.assertEqual(self.client.natl.stats.defense.clearances.endpoint, '/v1/national/stats/defense/clearances') 136 | self.assertEqual(self.client.natl.stats.defense.goalline.endpoint, '/v1/national/stats/defense/goalline') 137 | self.assertEqual(self.client.natl.stats.defense.tackles.endpoint, '/v1/national/stats/defense/tackles') 138 | 139 | self.assertEqual(self.client.natl.stats.fouls.cards.endpoint, '/v1/national/stats/fouls/cards') 140 | self.assertEqual(self.client.natl.stats.fouls.wins.endpoint, '/v1/national/stats/fouls/wins') 141 | 142 | self.assertEqual(self.client.natl.stats.goals.assists.endpoint, '/v1/national/stats/goals/assists') 143 | self.assertEqual(self.client.natl.stats.goals.bodyparts.endpoint, '/v1/national/stats/goals/bodyparts') 144 | self.assertEqual(self.client.natl.stats.goals.locations.endpoint, '/v1/national/stats/goals/locations') 145 | self.assertEqual(self.client.natl.stats.goals.penalties.endpoint, '/v1/national/stats/goals/penalties') 146 | self.assertEqual(self.client.natl.stats.goals.totals.endpoint, '/v1/national/stats/goals/totals') 147 | 148 | self.assertEqual(self.client.natl.stats.goalkeeper.actions.endpoint, '/v1/national/stats/goalkeeper/actions') 149 | self.assertEqual(self.client.natl.stats.goalkeeper.goals.endpoint, '/v1/national/stats/goalkeeper/goals') 150 | self.assertEqual(self.client.natl.stats.goalkeeper.shots.endpoint, '/v1/national/stats/goalkeeper/shots') 151 | self.assertEqual(self.client.natl.stats.goalkeeper.saves.endpoint, '/v1/national/stats/goalkeeper/saves') 152 | 153 | self.assertEqual(self.client.natl.stats.passes.directions.endpoint, '/v1/national/stats/passes/directions') 154 | self.assertEqual(self.client.natl.stats.passes.lengths.endpoint, '/v1/national/stats/passes/lengths') 155 | self.assertEqual(self.client.natl.stats.passes.locations.endpoint, '/v1/national/stats/passes/locations') 156 | self.assertEqual(self.client.natl.stats.passes.totals.endpoint, '/v1/national/stats/passes/totals') 157 | 158 | self.assertEqual(self.client.natl.stats.setpieces.corners.endpoint, '/v1/national/stats/setpieces/corners') 159 | self.assertEqual(self.client.natl.stats.setpieces.freekicks.endpoint, '/v1/national/stats/setpieces/freekicks') 160 | self.assertEqual(self.client.natl.stats.setpieces.throwins.endpoint, '/v1/national/stats/setpieces/throwins') 161 | 162 | self.assertEqual(self.client.natl.stats.shots.bodyparts.endpoint, '/v1/national/stats/shots/bodyparts') 163 | self.assertEqual(self.client.natl.stats.shots.locations.endpoint, '/v1/national/stats/shots/locations') 164 | self.assertEqual(self.client.natl.stats.shots.plays.endpoint, '/v1/national/stats/shots/plays') 165 | self.assertEqual(self.client.natl.stats.shots.totals.endpoint, '/v1/national/stats/shots/totals') 166 | 167 | self.assertEqual(self.client.natl.stats.touches.duels.endpoint, '/v1/national/stats/touches/duels') 168 | self.assertEqual(self.client.natl.stats.touches.locations.endpoint, '/v1/national/stats/touches/locations') 169 | self.assertEqual(self.client.natl.stats.touches.totals.endpoint, '/v1/national/stats/touches/totals') 170 | 171 | def test_analytics_endpoints(self): 172 | """Verify analytics endpoints without match ID.""" 173 | self.assertEqual(self.client.analytics.state.EndpointURI(), '/v1/analytics/match/state') 174 | self.assertEqual(self.client.analytics.segment.EndpointURI(), '/v1/analytics/match/segment') 175 | self.assertEqual(self.client.analytics.tsr.EndpointURI(), '/v1/analytics/match/tsr') 176 | -------------------------------------------------------------------------------- /soccermetrics/rest/resources/statistics.py: -------------------------------------------------------------------------------- 1 | from soccermetrics.rest.resources import Resource 2 | 3 | class MatchStatisticsResource(Resource): 4 | """ 5 | Represents a Match Statistics REST resource. 6 | 7 | The Match Statistics resources controls access to summary in-match 8 | statistical data of players who are in the match lineups of a 9 | football match. 10 | 11 | Derived from :class:`base.Resource`. 12 | """ 13 | def __init__(self, play, statistic, resource, base_uri, auth): 14 | """ 15 | Constructor of MatchStatisticsResource class. 16 | 17 | :param play: Type of teams playing in matches. 18 | :type play: string 19 | :param statistic: Statistic type. 20 | :type statistic: string 21 | :param resource: Name of resource. 22 | :type resource: string 23 | :param base_uri: Base URI of API. 24 | :type base_uri: string 25 | :param auth: Authentication credential. 26 | :type auth: tuple 27 | """ 28 | super(MatchStatisticsResource, self).__init__(base_uri,auth) 29 | 30 | self.endpoint += "/%s/stats/%s/%s" % (play, statistic, resource) 31 | 32 | 33 | class CrossingStatistics(object): 34 | """ 35 | Establish access to Cross statistical resources (//stats/crosses endpoint). 36 | 37 | +----------------+-----------------------+ 38 | | Attribute | Description | 39 | +================+=======================+ 40 | | corners | Crosses from corners | 41 | +----------------+-----------------------+ 42 | | totals | Total crossing stats | 43 | +----------------+-----------------------+ 44 | 45 | """ 46 | def __init__(self, play, base_uri, auth): 47 | """ 48 | Constructor of CrossingStatistics class. 49 | 50 | :param play: Type of teams playing in matches. 51 | :type play: string 52 | :param base_uri: Base URI of API. 53 | :type base_uri: string 54 | :param auth: Authentication credential. 55 | :type auth: tuple 56 | """ 57 | super(CrossingStatistics, self).__init__() 58 | 59 | statistic = "crosses" 60 | 61 | self.corners = MatchStatisticsResource(play, statistic, "corners", base_uri, auth) 62 | self.totals = MatchStatisticsResource(play, statistic, "totals", base_uri, auth) 63 | 64 | 65 | class DefensiveStatistics(object): 66 | """ 67 | Establish access to Defensive statistical resources (//stats/defense endpoint). 68 | 69 | +----------------+----------------------------+ 70 | | Attribute | Description | 71 | +================+============================+ 72 | | actions | Defensive actions | 73 | +----------------+----------------------------+ 74 | | blocks | Shot block stats | 75 | +----------------+----------------------------+ 76 | | clearances | Ball clearance stats | 77 | +----------------+----------------------------+ 78 | | goalline | Goal-line clearance stats | 79 | +----------------+----------------------------+ 80 | | tackles | Tackling stats | 81 | +----------------+----------------------------+ 82 | """ 83 | def __init__(self, play, base_uri, auth): 84 | """ 85 | Constructor of DefensiveStatistics class. 86 | 87 | :param play: Type of teams playing in matches. 88 | :type play: string 89 | :param base_uri: Base URI of API. 90 | :type base_uri: string 91 | :param auth: Authentication credential. 92 | :type auth: tuple 93 | """ 94 | super(DefensiveStatistics, self).__init__() 95 | 96 | statistic = "defense" 97 | 98 | self.actions = MatchStatisticsResource(play, statistic, "actions", base_uri, auth) 99 | self.blocks = MatchStatisticsResource(play, statistic, "blocks", base_uri, auth) 100 | self.clearances = MatchStatisticsResource(play, statistic, "clearances", base_uri, auth) 101 | self.goalline = MatchStatisticsResource(play, statistic, "goalline", base_uri, auth) 102 | self.tackles = MatchStatisticsResource(play, statistic, "tackles", base_uri, auth) 103 | 104 | 105 | class FoulingStatistics(object): 106 | """ 107 | Access to Foul statistical resources (//stats/fouls endpoint). 108 | 109 | +--------------+-----------------------+ 110 | | Attribute | Description | 111 | +==============+=======================+ 112 | | cards | Card stats | 113 | +--------------+-----------------------+ 114 | | wins | Foul suffered stats | 115 | +--------------+-----------------------+ 116 | """ 117 | def __init__(self, play, base_uri, auth): 118 | """ 119 | Constructor of FoulingStatistics class. 120 | 121 | :param play: Type of teams playing in matches. 122 | :type play: string 123 | :param base_uri: Base URI of API. 124 | :type base_uri: string 125 | :param auth: Authentication credential. 126 | :type auth: tuple 127 | """ 128 | super(FoulingStatistics, self).__init__() 129 | 130 | statistic = "fouls" 131 | 132 | self.cards = MatchStatisticsResource(play, statistic, "cards", base_uri, auth) 133 | self.wins = MatchStatisticsResource(play, statistic, "wins", base_uri, auth) 134 | 135 | 136 | class GoalStatistics(object): 137 | """ 138 | Access to Goal statistical resources (//stats/goals endpoint). 139 | 140 | +----------------+----------------------------+ 141 | | Attribute | Description | 142 | +================+============================+ 143 | | assists | Goal assist stats | 144 | +----------------+----------------------------+ 145 | | bodyparts | Goalscoring bodypart stats | 146 | +----------------+----------------------------+ 147 | | locations | Goalscoring location stats | 148 | +----------------+----------------------------+ 149 | | penalties | Match penalty stats | 150 | +----------------+----------------------------+ 151 | | totals | Total goalscoring stats | 152 | +----------------+----------------------------+ 153 | """ 154 | def __init__(self, play, base_uri, auth): 155 | """ 156 | Constructor of GoalStatistics class. 157 | 158 | :param play: Type of teams playing in matches. 159 | :type play: string 160 | :param base_uri: Base URI of API. 161 | :type base_uri: string 162 | :param auth: Authentication credential. 163 | :type auth: tuple 164 | """ 165 | super(GoalStatistics, self).__init__() 166 | 167 | statistic = "goals" 168 | 169 | self.assists = MatchStatisticsResource(play, statistic, "assists", base_uri, auth) 170 | self.bodyparts = MatchStatisticsResource(play, statistic, "bodyparts", base_uri, auth) 171 | self.locations = MatchStatisticsResource(play, statistic, "locations", base_uri, auth) 172 | self.penalties = MatchStatisticsResource(play, statistic, "penalties", base_uri, auth) 173 | self.totals = MatchStatisticsResource(play, statistic, "totals", base_uri, auth) 174 | 175 | 176 | class GoalkeepingStatistics(object): 177 | """ 178 | Access to Goalkeeper statistical resources (//stats/goalkeeper endpoint). 179 | 180 | +----------------+----------------------------+ 181 | | Attribute | Description | 182 | +================+============================+ 183 | | actions | Goalkeeping action stats | 184 | +----------------+----------------------------+ 185 | | goals | Goals allowed stats | 186 | +----------------+----------------------------+ 187 | | shots | Shots allowed stats | 188 | +----------------+----------------------------+ 189 | | saves | Goalkeeper saves stats | 190 | +----------------+----------------------------+ 191 | """ 192 | def __init__(self, play, base_uri, auth): 193 | """ 194 | Constructor of GoalkeepingStatistics class. 195 | 196 | :param play: Type of teams playing in matches. 197 | :type play: string 198 | :param base_uri: Base URI of API. 199 | :type base_uri: string 200 | :param auth: Authentication credential. 201 | :type auth: tuple 202 | """ 203 | super(GoalkeepingStatistics, self).__init__() 204 | 205 | statistic = "goalkeeper" 206 | 207 | self.actions = MatchStatisticsResource(play, statistic, "actions", base_uri, auth) 208 | self.goals = MatchStatisticsResource(play, statistic, "goals", base_uri, auth) 209 | self.shots = MatchStatisticsResource(play, statistic, "shots", base_uri, auth) 210 | self.saves = MatchStatisticsResource(play, statistic, "saves", base_uri, auth) 211 | 212 | 213 | class PassingStatistics(object): 214 | """ 215 | Access to Passing statistical resources (//stats/passes endpoint). 216 | 217 | +----------------+----------------------------+ 218 | | Attribute | Description | 219 | +================+============================+ 220 | | directions | Pass direction stats | 221 | +----------------+----------------------------+ 222 | | lengths | Pass length stats | 223 | +----------------+----------------------------+ 224 | | locations | Pass location stats | 225 | +----------------+----------------------------+ 226 | | totals | Total passing stats | 227 | +----------------+----------------------------+ 228 | """ 229 | def __init__(self, play, base_uri, auth): 230 | """ 231 | Constructor of PassingStatistics class. 232 | 233 | :param play: Type of teams playing in matches. 234 | :type play: string 235 | :param base_uri: Base URI of API. 236 | :type base_uri: string 237 | :param auth: Authentication credential. 238 | :type auth: tuple 239 | """ 240 | super(PassingStatistics, self).__init__() 241 | 242 | statistic = "passes" 243 | 244 | self.directions = MatchStatisticsResource(play, statistic, "directions", base_uri, auth) 245 | self.lengths = MatchStatisticsResource(play, statistic, "lengths", base_uri, auth) 246 | self.locations = MatchStatisticsResource(play, statistic, "locations", base_uri, auth) 247 | self.totals = MatchStatisticsResource(play, statistic, "totals", base_uri, auth) 248 | 249 | 250 | class SetPieceStatistics(object): 251 | """ 252 | Access to Set-Piece statistical resources (//stats/setpieces endpoint). 253 | 254 | +----------------+----------------------------+ 255 | | Attribute | Description | 256 | +================+============================+ 257 | | corners | Corner kick stats | 258 | +----------------+----------------------------+ 259 | | freekicks | Direct freekick stats | 260 | +----------------+----------------------------+ 261 | | throwins | Throw-in stats | 262 | +----------------+----------------------------+ 263 | """ 264 | def __init__(self, play, base_uri, auth): 265 | """ 266 | Constructor of SetPieceStatistics class. 267 | 268 | :param play: Type of teams playing in matches. 269 | :type play: string 270 | :param base_uri: Base URI of API. 271 | :type base_uri: string 272 | :param auth: Authentication credential. 273 | :type auth: tuple 274 | """ 275 | super(SetPieceStatistics, self).__init__() 276 | 277 | statistic = "setpieces" 278 | 279 | self.corners = MatchStatisticsResource(play, statistic, "corners", base_uri, auth) 280 | self.freekicks = MatchStatisticsResource(play, statistic, "freekicks", base_uri, auth) 281 | self.throwins = MatchStatisticsResource(play, statistic, "throwins", base_uri, auth) 282 | 283 | 284 | class ShotStatistics(object): 285 | """ 286 | Access to Shot statistical resources (//stats/shots endpoint). 287 | 288 | +----------------+----------------------------+ 289 | | Attribute | Description | 290 | +================+============================+ 291 | | bodyparts | Shot bodypart stats | 292 | +----------------+----------------------------+ 293 | | locations | Shot location stats | 294 | +----------------+----------------------------+ 295 | | plays | Shot play stats | 296 | +----------------+----------------------------+ 297 | | totals | Total shot stats | 298 | +----------------+----------------------------+ 299 | """ 300 | def __init__(self, play, base_uri, auth): 301 | """ 302 | Constructor of ShotStatistics class. 303 | 304 | :param play: Type of teams playing in matches. 305 | :type play: string 306 | :param base_uri: Base URI of API. 307 | :type base_uri: string 308 | :param auth: Authentication credential. 309 | :type auth: tuple 310 | """ 311 | super(ShotStatistics, self).__init__() 312 | 313 | statistic = "shots" 314 | 315 | self.bodyparts = MatchStatisticsResource(play, statistic, "bodyparts", base_uri, auth) 316 | self.locations = MatchStatisticsResource(play, statistic, "locations", base_uri, auth) 317 | self.plays = MatchStatisticsResource(play, statistic, "plays", base_uri, auth) 318 | self.totals = MatchStatisticsResource(play, statistic, "totals", base_uri, auth) 319 | 320 | 321 | class TouchStatistics(object): 322 | """ 323 | Access to Touch statistical resources (//stats/touches endpoint). 324 | 325 | +----------------+----------------------------+ 326 | | Attribute | Description | 327 | +================+============================+ 328 | | duels | 50/50 dueling stats | 329 | +----------------+----------------------------+ 330 | | locations | Ball touch location stats | 331 | +----------------+----------------------------+ 332 | | totals | Total ball touch stats | 333 | +----------------+----------------------------+ 334 | """ 335 | def __init__(self, play, base_uri, auth): 336 | """ 337 | Constructor of TouchStatistics class. 338 | 339 | :param play: Type of teams playing in matches. 340 | :type play: string 341 | :param base_uri: Base URI of API. 342 | :type base_uri: string 343 | :param auth: Authentication credential. 344 | :type auth: tuple 345 | """ 346 | super(TouchStatistics, self).__init__() 347 | 348 | statistic = "touches" 349 | 350 | self.duels = MatchStatisticsResource(play, statistic, "duels", base_uri, auth) 351 | self.locations = MatchStatisticsResource(play, statistic, "locations", base_uri, auth) 352 | self.totals = MatchStatisticsResource(play, statistic, "totals", base_uri, auth) 353 | 354 | 355 | class MatchStatistics(object): 356 | """ 357 | Establish access to Match Statistics objects (//stats endpoints). 358 | 359 | +----------------+----------------------------+ 360 | | Attribute | Description | 361 | +================+============================+ 362 | | crosses | Crossing statistics | 363 | +----------------+----------------------------+ 364 | | defense | Defensive statistics | 365 | +----------------+----------------------------+ 366 | | fouls | Foul statistics | 367 | +----------------+----------------------------+ 368 | | goals | Goal statistics | 369 | +----------------+----------------------------+ 370 | | goalkeeper | Goalkeeping statistics | 371 | +----------------+----------------------------+ 372 | | passes | Passing statistics | 373 | +----------------+----------------------------+ 374 | | setpieces | Set-piece statistics | 375 | +----------------+----------------------------+ 376 | | shots | Shot statistics | 377 | +----------------+----------------------------+ 378 | | touches | Ball touch statistics | 379 | +----------------+----------------------------+ 380 | """ 381 | def __init__(self, play, base_uri, auth): 382 | """ 383 | Constructor of MatchStatistics class. 384 | 385 | :param play: Type of teams playing in matches. 386 | :type play: string 387 | :param base_uri: Base URI of API. 388 | :type base_uri: string 389 | :param auth: Authentication credential. 390 | :type auth: tuple 391 | """ 392 | 393 | self.crosses = CrossingStatistics(play, base_uri, auth) 394 | self.defense = DefensiveStatistics(play, base_uri, auth) 395 | self.fouls = FoulingStatistics(play, base_uri, auth) 396 | self.goals = GoalStatistics(play, base_uri, auth) 397 | self.goalkeeper = GoalkeepingStatistics(play, base_uri, auth) 398 | self.passes = PassingStatistics(play, base_uri, auth) 399 | self.setpieces = SetPieceStatistics(play, base_uri, auth) 400 | self.shots = ShotStatistics(play, base_uri, auth) 401 | self.touches = TouchStatistics(play, base_uri, auth) -------------------------------------------------------------------------------- /examples/clubs/example_fantasy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env/python 2 | # -*- encoding: utf-8 -*- 3 | # 4 | # Compute fantasy points by player and plot weekly fantasy performance. 5 | # Also plot distribution of fantasy points. 6 | 7 | # All players 8 | # ----------- 9 | # [x] Played up to 60 minutes 1 10 | # [x] Played 60 minutes or more 2 11 | # [x] Goal assist 3 12 | # [x] Yellow card -1 13 | # [x] Own goal -2 14 | # [x] Penalty miss -2 15 | # [x] Red card -3 16 | 17 | # Goalkeeper-specific 18 | # ------------------- 19 | # [x] Every three shots made by goalkeeper 1 20 | # [x] Clean sheet by goalkeeper/defender 4 21 | # [x] Penalty save 5 22 | # [x] Goal scored by goalkeeper/defender 6 23 | # [x] Every two goals conceded by goalkeeper/defender -1 24 | 25 | # Defender-specific 26 | # ----------------- 27 | # [x] Clean sheet by goalkeeper/defender 4 28 | # [x] Goal scored by goalkeeper/defender 6 29 | # [x] Every two goals conceded by goalkeeper/defender -1 30 | 31 | # Midfielder-specific 32 | # ------------------- 33 | # [x] Clean sheet by midfielder 1 34 | # [x] Goal scored by midfielder 5 35 | 36 | # Forward-specific 37 | # ---------------- 38 | # [x] Goal scored by forward 4 39 | 40 | import re 41 | 42 | from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas 43 | from matplotlib.figure import Figure 44 | 45 | from soccermetrics.rest import SoccermetricsRestClient 46 | 47 | forwardList = ['Left Forward', 'Central Forward', 'Right Forward', 'Striker'] 48 | midfieldList = ['Left Winger', 'Right Winger', 'Central Midfielder', 'Midfielder'] 49 | defensiveList = ['Wing-back','Left Wing-back','Right Wing-back', 'Full-back', 50 | 'Left Full-back', 'Right Full-back', 'Libero', 'Defender', 51 | 'Central Defender', 'Goalkeeper'] 52 | 53 | class matchTime: 54 | """Match time conversion class. 55 | 56 | Takes into account lengths of match periods. 57 | 58 | :Example: 59 | 60 | >>> mt = matchTime() 61 | >>> mt = matchTime([47,49]) 62 | 63 | """ 64 | 65 | PERIODS = [(1, 46), (46, 91)] 66 | 67 | def __init__(self, matchLength=[45, 45]): 68 | self.matchLength = matchLength 69 | 70 | def total(self): 71 | return sum(self.matchLength) 72 | 73 | def absoluteTime(self,time): 74 | """Expresses match time on an absolute time scale. 75 | 76 | :param time: Match time. 77 | :type time: int or list of ints (time,stoppage) 78 | :rtype: int 79 | 80 | :Example: 81 | >>> mt = matchTime() 82 | >>> mt.absoluteTime(70) 83 | 70 84 | 85 | """ 86 | if type(time) is int: 87 | time = (time,0) 88 | 89 | # check value of time[0] 90 | # if not end of period set stoppage time to zero 91 | if (time[0] and (time[0] % 45)): 92 | newtime = (time[0],0) 93 | time = newtime 94 | 95 | # find period in which match time belongs 96 | for period,k in zip(matchTime.PERIODS,range(len(matchTime.PERIODS))): 97 | minSet = set(range(period[0],period[1])) 98 | if minSet.intersection((time[0],)): 99 | v = k 100 | 101 | return sum(self.matchLength[0:v]) + sum(time)-matchTime.PERIODS[v][0] + 1 102 | 103 | def calcSampVariance(vector): 104 | """Calculate (sample) variance of vector""" 105 | if len(vector) <= 1: 106 | return 0 107 | else: 108 | mean = calcMean(vector) 109 | sos = [(x-mean)*(x-mean) for x in vector] 110 | return sum(sos)/(len(vector)-1) 111 | 112 | def calcMean(vector): 113 | """Calculate mean of vector""" 114 | if len(vector) == 0: 115 | return 0.0 116 | else: 117 | return sum(vector)/float(len(vector)) 118 | 119 | def calcPosMultiplier(positionName): 120 | """Calculate point multiplier given position""" 121 | multiplier = 1 122 | 123 | if positionName in forwardList: 124 | multiplier = 4 125 | elif positionName in midfieldList: 126 | multiplier = 5 127 | elif positionName in defensiveList: 128 | multiplier = 6 129 | 130 | return multiplier 131 | 132 | def calcMinutesPlayed(client, lineup, match): 133 | """Calculate total minutes played by a player in a match. 134 | 135 | This routine accounts for the following outcomes: 136 | 137 | (1) Started and played full match. 138 | (2) Started match and was sent off. 139 | (3) Started match and was substituted out. 140 | (4) Entered match and played remainder of match. 141 | (5) Entered match and was sent off. 142 | (6) Entered match and was substituted out. 143 | """ 144 | # get total playing time of match 145 | match_length = matchTime([match.firsthalfLength, match.secondhalfLength]) 146 | # did player start match? 147 | if lineup.isStarting: 148 | # was player sent off? 149 | ejected = client.link.get(match.link.events.offenses, 150 | player_name=lineup.playerName, card_type="Red").all() + \ 151 | client.link.get(match.link.events.offenses, 152 | player_name=lineup.playerName, card_type="Yellow/Red").all() 153 | if ejected: 154 | eject_time = (ejected[0].timeMins, ejected[0].stoppageMins) 155 | player_time = match_length.absoluteTime(eject_time) 156 | else: 157 | # was player substituted out? 158 | subs = client.link.get(match.link.events.substitutions, 159 | out_player_name=lineup.playerName).all() 160 | if subs: 161 | subs_time = (subs[0].timeMins, subs[0].stoppageMins) 162 | player_time = match_length.absoluteTime(subs_time) 163 | else: 164 | player_time = match_length.total() 165 | # did player enter as substitute? 166 | else: 167 | subs = client.link.get(match.link.events.substitutions, 168 | in_player_name=lineup.playerName).all() 169 | if subs: 170 | subs_time = (subs[0].timeMins, subs[0].stoppageMins) 171 | # was player sent off? 172 | ejected = client.link.get(match.link.events.offenses, 173 | player_name=lineup.playerName, card_type="Red").all() + \ 174 | client.link.get(match.link.events.offenses, 175 | player_name=lineup.playerName, card_type="Yellow/Red").all() 176 | if ejected: 177 | eject_time = (ejected[0].timeMins, ejected[0].stoppageMins) 178 | player_time = match_length.absoluteTime(eject_time) - \ 179 | match_length.absoluteTime(subs_time) 180 | else: 181 | # was sub substituted? 182 | subsubs = client.link.get(match.link.events.substitutions, 183 | out_player_name=lineup.playerName).all() 184 | if subsubs: 185 | subsubs_time = (subsubs[0].timeMins, subsubs[0].stoppageMins) 186 | player_time = match_length.absoluteTime(subsubs_time) - \ 187 | match_length.absoluteTime(subs_time) 188 | else: 189 | player_time = match_length.total() - match_length.absoluteTime(subs_time) 190 | else: 191 | player_time = 0 192 | return player_time 193 | 194 | def calcYellowCardPoints(client, resp): 195 | if resp.data: 196 | lineup = resp.data[0] 197 | yellows = client.link.get(lineup.link.stats.fouls.cards).data[0].yellows 198 | return -yellows 199 | else: 200 | return 0 201 | 202 | def calcRedCardPoints(client, resp): 203 | if resp.data: 204 | lineup = resp.data[0] 205 | reds = client.link.get(lineup.link.stats.fouls.cards).data[0].reds 206 | return -3 * reds 207 | else: 208 | return 0 209 | 210 | def calcGoalPoints(client, resp): 211 | if resp.data: 212 | lineup = resp.data[0] 213 | match = client.link.get(lineup.link.match).data[0] 214 | goals = client.link.get(match.link.events.goals).all() 215 | if goals: 216 | goals_not_own = len([x for x in goals 217 | if x.player == lineup.player 218 | and x.playerTeam == x.scoringTeam]) 219 | goals_own = len([x for x in goals 220 | if x.player == lineup.player 221 | and x.playerTeam != x.scoringTeam]) 222 | return calcPosMultiplier(lineup.positionName)*goals_not_own - 2*goals_own 223 | return 0 224 | 225 | def calcGoalsAllowedPoints(client, resp): 226 | if resp.data: 227 | lineup = resp.data[0] 228 | isDefPlayer = lineup.positionName in defensiveList 229 | match = client.link.get(lineup.link.match).data[0] 230 | # get goals from opposing team 231 | if match.homeTeamName == lineup.playerTeam: 232 | opp_team_name = match.awayTeamName 233 | else: 234 | opp_team_name = match.homeTeamName 235 | goals = client.link.get(match.link.events.goals, scoring_team_name=opp_team_name).all() 236 | # if player started match... 237 | if lineup.isStarting: 238 | # was player sent off? 239 | ejected = client.link.get(match.link.events.offenses, 240 | player_name=lineup.playerName, card_type="Red").all() + \ 241 | client.link.get(match.link.events.offenses, 242 | player_name=lineup.playerName, card_type="Yellow/Red").all() 243 | if ejected: 244 | allowed_preeject = len([x for x in goals 245 | if x.timeMins < ejected[0].timeMins]) 246 | allowed_posteject = len([x for x in goals 247 | if x.timeMins >= ejected[0].timeMins]) 248 | goals_allowed = allowed_preeject * isDefPlayer + allowed_posteject 249 | else: 250 | # player not ejected -- was player substituted out? 251 | subs = client.link.get(match.link.events.substitutions, 252 | out_player_name=lineup.playerName).all() 253 | if subs: 254 | goals_allowed = len([x for x in goals 255 | if x.timeMins < subs[0].timeMins]) * isDefPlayer 256 | else: 257 | # player in full duration of match 258 | goals_allowed = len(goals) * isDefPlayer 259 | # if player did not start... 260 | else: 261 | # did player join as substitute? 262 | subs = client.link.get(match.link.events.substitutions, 263 | in_player_name=lineup.playerName).all() 264 | if subs: 265 | # was player ejected? 266 | ejected = client.link.get(match.link.events.offenses, 267 | player_name=lineup.playerName, card_type="Red").all() + \ 268 | client.link.get(match.link.events.offenses, 269 | player_name=lineup.playerName, card_type="Yellow/Red").all() 270 | if ejected: 271 | allowed_preeject = len([x for x in goals 272 | if x.timeMins >= subs[0].timeMins 273 | and x.timeMins < ejected[0].timeMins]) 274 | allowed_posteject = len([x for x in goals 275 | if x.timeMins >= ejected[0].timeMins]) 276 | goals_allowed = allowed_preeject * isDefPlayer + allowed_posteject 277 | else: 278 | # sub not ejected -- was sub substituted out? 279 | subsubs = client.link.get(match.link.events.substitutions, 280 | out_player_name=lineup.playerName).all() 281 | if subsubs: 282 | goals_allowed = len([x for x in goals 283 | if x.timeMins >= subs[0].timeMins 284 | and x.timeMins < subsubs[0].timeMins]) * isDefPlayer 285 | else: 286 | goals_allowed = len([x for x in goals 287 | if x.timeMins >= subs[0].timeMins]) * isDefPlayer 288 | return int(goals_allowed*0.5) 289 | return 0 290 | 291 | def calcGoalAssistPoints(client, resp): 292 | if resp.data: 293 | lineup = resp.data[0] 294 | assists = client.link.get(lineup.link.stats.goals.assists).data[0].total 295 | return 3*assists 296 | else: 297 | return 0 298 | 299 | def calcCleanSheetPoints(client, resp): 300 | if resp.data: 301 | lineup = resp.data[0] 302 | match = client.link.get(lineup.link.match).data[0] 303 | match_length = matchTime([match.firsthalfLength, match.secondhalfLength]) 304 | if match.homeTeamName == lineup.playerTeam: 305 | opp_team_name = match.awayTeamName 306 | else: 307 | opp_team_name = match.homeTeamName 308 | goals = client.link.get(match.link.events.goals, scoring_team_name=opp_team_name).all() 309 | # did player start match? 310 | if lineup.isStarting: 311 | # was player sent off? 312 | ejected = client.link.get(match.link.events.offenses, 313 | player_name=lineup.playerName, card_type="Red").all() + \ 314 | client.link.get(match.link.events.offenses, 315 | player_name=lineup.playerName, card_type="Yellow/Red").all() 316 | if ejected: 317 | eject_time = (ejected[0].timeMins, ejected[0].stoppageMins) 318 | player_time = match_length.absoluteTime(eject_time) 319 | goals_allowed = len([x for x in goals 320 | if x.timeMins <= eject_time[0]]) 321 | else: 322 | # was player substituted out? 323 | subs = client.link.get(match.link.events.substitutions, 324 | out_player_name=lineup.playerName).all() 325 | if subs: 326 | subs_time = (subs[0].timeMins, subs[0].stoppageMins) 327 | player_time = match_length.absoluteTime(subs_time) 328 | goals_allowed = len([x for x in goals 329 | if x.timeMins <= subs_time[0]]) 330 | else: 331 | # player in for full match 332 | player_time = match_length.total() 333 | goals_allowed = len(goals) 334 | else: 335 | # did player join as substitute? 336 | subs = client.link.get(match.link.events.substitutions, 337 | in_player_name=lineup.playerName).all() 338 | if subs: 339 | subs_time = (subs[0].timeMins, subs[0].stoppageMins) 340 | # was player sent off? 341 | ejected = client.link.get(match.link.events.offenses, 342 | player_name=lineup.playerName, card_type="Red").all() + \ 343 | client.link.get(match.link.events.offenses, 344 | player_name=lineup.playerName, card_type="Yellow/Red").all() 345 | if ejected: 346 | eject_time = (ejected[0].timeMins, ejected[0].stoppageMins) 347 | player_time = match_length.absoluteTime(eject_time) - \ 348 | match_length.absoluteTime(subs_time) 349 | goals_allowed = len([x for x in goals if x.timeMins <= eject_time[0]]) - \ 350 | len([x for x in goals if x.timeMins <= subs_time[0]]) 351 | else: 352 | # was sub substituted out? 353 | subsubs = client.link.get(match.link.events.substitutions, 354 | out_player_name=lineup.playerName).all() 355 | if subsubs: 356 | subsubs_time = (subsubs[0].timeMins, subsubs[0].stoppageMins) 357 | player_time = match_length.absoluteTime(subsubs_time) - \ 358 | match_length.absoluteTime(subs_time) 359 | goals_allowed = len([x for x in goals 360 | if x.timeMins >= subs[0].timeMins 361 | and x.timeMins < subsubs[0].timeMins]) 362 | else: 363 | player_time = match_length.total() - match_length.absoluteTime(subs_time) 364 | goals_allowed = len([x for x in goals if x.timeMins >= subs_time[0]]) 365 | else: 366 | player_time = 0 367 | goals_allowed = None 368 | if goals_allowed is not None: 369 | if goals_allowed == 0 and player_time >= 60: 370 | if lineup.positionName in defensiveList: 371 | return 4 372 | elif lineup.positionName in midfieldList: 373 | return 1 374 | return 0 375 | 376 | def calcSavePoints(client, resp): 377 | if resp.data: 378 | lineup = resp.data[0] 379 | saves = client.link.get(lineup.link.stats.goalkeeper.saves).data[0] 380 | return (saves.insidebox + saves.outsidebox)/3 381 | else: 382 | return 0 383 | 384 | def calcPenaltyPoints(client, resp): 385 | if resp.data: 386 | lineup = resp.data[0] 387 | match = client.link.get(lineup.link.match).data[0] 388 | # penalty kick misses 389 | pen_goals = client.link.get(match.link.events.penalties, 390 | player_name=lineup.playerName).all() 391 | if pen_goals: 392 | pen_misses = len([x for x in pen_goals if x.outcomeType != "Goal"]) 393 | else: 394 | pen_misses = 0 395 | # penalty kick saves 396 | pen_saves = client.link.get(match.link.stats.goalkeeper.saves).data[0].penalty 397 | # return points (combo pen saves and misses) 398 | return 5*pen_saves - 3*pen_misses 399 | else: 400 | return 0 401 | 402 | def calcParticipationPoints(client, resp): 403 | if resp.data: 404 | lineup = resp.data[0] 405 | match = client.link.get(lineup.link.match).data[0] 406 | player_time = calcMinutesPlayed(client, lineup, match) 407 | return 1 if player_time < 60 else 2 408 | return 0 409 | 410 | def plotFantasyCharts(name, matchdays, yvec): 411 | """Create chart of fantasy point time history and distribution.""" 412 | 413 | # Create a regex pattern for name 414 | p = re.compile('\s') 415 | 416 | # Create a figure with size 6 x 6 inches. 417 | # Create a canvas and add the figure to it. 418 | fig = Figure(figsize=(6,6)) 419 | canvas = FigureCanvas(fig) 420 | 421 | # Create a subplot and define the title and axis labels. 422 | ax = fig.add_subplot(211) 423 | ax.set_title(u"%s: 2011-12 Fantasy Performance" % (name,),fontsize=14) 424 | ax.set_xlabel("Matchday",fontsize=12) 425 | ax.set_ylabel("Official FPL Points",fontsize=12) 426 | 427 | # Generate the line plot. 428 | ax.plot(matchdays, yvec, 'b-') 429 | 430 | # Create the second subplot and define the title and axis labels. 431 | ax = fig.add_subplot(212) 432 | ax.set_xlabel("Official FPL Points",fontsize=12) 433 | ax.set_ylabel("Occurrences",fontsize=12) 434 | 435 | # Generate the histogram. 436 | ax.hist(yvec, bins=range(min(yvec),max(yvec)+2), 437 | align='left', alpha=0.5, facecolor='blue') 438 | 439 | # Annotate the second subplot with total/average points. 440 | ax.text(0.6, 0.9, "Total Points: %d" % (sum(yvec),), transform=ax.transAxes) 441 | ax.text(0.6, 0.8, "Expected Points: %2.1f" % (calcMean(yvec),), 442 | transform=ax.transAxes) 443 | ax.text(0.6, 0.7, "Variance: %2.1f" % (calcSampVariance(yvec),), 444 | transform=ax.transAxes) 445 | 446 | # Save figure to a PNG file. 447 | canvas.print_figure('%s_2011-12.png' % (p.sub('_',name),), dpi=500) 448 | 449 | if __name__ == "__main__": 450 | 451 | client = SoccermetricsRestClient() 452 | 453 | pname = u'Robin van Persie' 454 | 455 | matchdays = range(1,39) 456 | 457 | Total = [] 458 | for day in matchdays: 459 | resp = client.match.lineups.get(matchday=day,player_name=pname) 460 | if resp.status == 200: 461 | if resp.data: 462 | print "Played on matchday %d" % day 463 | else: 464 | print "Did not play on matchday %d" % day 465 | 466 | weekly = [ 467 | calcParticipationPoints(client,resp), 468 | calcGoalPoints(client,resp), 469 | calcGoalsAllowedPoints(client,resp), 470 | calcCleanSheetPoints(client,resp), 471 | calcGoalAssistPoints(client,resp), 472 | calcSavePoints(client,resp), 473 | calcPenaltyPoints(client,resp), 474 | calcYellowCardPoints(client,resp), 475 | calcRedCardPoints(client,resp) 476 | ] 477 | 478 | Total.append(sum(weekly)) 479 | 480 | fantasypts = sum(weekly) 481 | print "Weekly points: ", weekly 482 | print "Total points: %d" % fantasypts 483 | 484 | # Generate fantasy plot 485 | plotFantasyCharts(pname, matchdays, Total) --------------------------------------------------------------------------------