From 824d98d718fb161a347632a68517f6bdea3c4ad6 Mon Sep 17 00:00:00 2001 From: johnmadden86 Date: Sat, 19 Oct 2019 01:22:29 +0100 Subject: [PATCH 1/9] activate skipped tests --- tests/test_fpl.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_fpl.py b/tests/test_fpl.py index 045998a..84a6cbb 100644 --- a/tests/test_fpl.py +++ b/tests/test_fpl.py @@ -187,20 +187,20 @@ async def test_gameweek(self, loop, fpl): gameweek = await fpl.get_gameweek(20, return_json=True) assert isinstance(gameweek, dict) - @pytest.mark.skip(reason="Cannot currently test it.") async def test_classic_league(self, loop, fpl): - classic_league = await fpl.get_classic_league(34438) + await fpl.login() + classic_league = await fpl.get_classic_league(173226) assert isinstance(classic_league, ClassicLeague) - classic_league = await fpl.get_classic_league(34438, return_json=True) + classic_league = await fpl.get_classic_league(173226, return_json=True) assert isinstance(classic_league, dict) - @pytest.mark.skip(reason="Cannot currently test it.") async def test_h2h_league(self, loop, fpl): - h2h_league = await fpl.get_h2h_league(63368) + await fpl.login() + h2h_league = await fpl.get_h2h_league(902521) assert isinstance(h2h_league, H2HLeague) - h2h_league = await fpl.get_h2h_league(63368, True) + h2h_league = await fpl.get_h2h_league(902521, True) assert isinstance(h2h_league, dict) async def test_login_with_no_email_password(self, loop, mocker, monkeypatch, fpl): From 15c95280fdb6273a15d10a7be878dfca16180126 Mon Sep 17 00:00:00 2001 From: johnmadden86 Date: Sat, 19 Oct 2019 01:46:09 +0100 Subject: [PATCH 2/9] get bootstrap static data on init --- fpl/fpl.py | 40 +++++++++++++++++++++++----------------- setup.py | 2 +- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/fpl/fpl.py b/fpl/fpl.py index 8c94dc0..8db6682 100644 --- a/fpl/fpl.py +++ b/fpl/fpl.py @@ -27,6 +27,8 @@ import itertools import os +import requests + from .constants import API_URLS from .models.classic_league import ClassicLeague from .models.fixture import Fixture @@ -39,11 +41,19 @@ position_converter, scale, team_converter) -class FPL(): +class FPL: """The FPL class.""" def __init__(self, session): self.session = session + static = requests.get(API_URLS["static"]).json() # use synchronous request + for k, v in static.items(): + try: + v = {w["id"]: w for w in v} + except (KeyError, TypeError): + pass + setattr(self, k, v) + setattr(self, "current_gameweek", next(event for event in static["events"] if event["is_current"])['id']) async def get_user(self, user_id=None, return_json=False): """Returns the user with the given ``user_id``. @@ -91,13 +101,13 @@ async def get_teams(self, team_ids=None, return_json=False): :type return_json: bool :rtype: list """ - url = API_URLS["static"] - teams = await fetch(self.session, url) - teams = teams["teams"] + teams = getattr(self, "teams") if team_ids: team_ids = set(team_ids) - teams = [team for team in teams if team["id"] in team_ids] + teams = [team for team in teams.values() if team["id"] in team_ids] + else: + teams = [team for team in teams.values()] if return_json: return teams @@ -145,9 +155,8 @@ async def get_team(self, team_id, return_json=False): """ assert 0 < int( team_id) < 21, "Team ID must be a number between 1 and 20." - url = API_URLS["static"] - teams = await fetch(self.session, url) - team = next(team for team in teams["teams"] + teams = getattr(self, "teams") + team = next(team for team in teams.values() if team["id"] == int(team_id)) if return_json: @@ -226,11 +235,10 @@ async def get_player(self, player_id, players=None, include_summary=False, :raises ValueError: Player with ``player_id`` not found """ if not players: - players = await fetch(self.session, API_URLS["static"]) - players = players["elements"] + players = getattr(self, "elements") try: - player = next(player for player in players + player = next(player for player in players.values() if player["id"] == player_id) except StopIteration: raise ValueError(f"Player with ID {player_id} not found") @@ -263,11 +271,10 @@ async def get_players(self, player_ids=None, include_summary=False, :type return_json: bool :rtype: list """ - players = await fetch(self.session, API_URLS["static"]) - players = players["elements"] + players = getattr(self, "elements") if not player_ids: - player_ids = [player["id"] for player in players] + player_ids = [player["id"] for player in players.values()] tasks = [asyncio.ensure_future( self.get_player( @@ -422,11 +429,10 @@ async def get_gameweek(self, gameweek_id, include_live=False, :rtype: :class:`Gameweek` or ``dict`` """ - static_gameweeks = await fetch(self.session, API_URLS["static"]) - static_gameweeks = static_gameweeks["events"] + static_gameweeks = getattr(self, "events") try: - static_gameweek = next(gameweek for gameweek in static_gameweeks if + static_gameweek = next(gameweek for gameweek in static_gameweeks.values() if gameweek["id"] == gameweek_id) except StopIteration: raise ValueError(f"Gameweek with ID {gameweek_id} not found") diff --git a/setup.py b/setup.py index 8aa7157..b1ff261 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,7 @@ "pytest-aiohttp", "pytest-cov", "pytest-mock", - "pytest", + "pytest", 'requests' ], entry_points=""" [console_scripts] From 2f12a0f8364f5bdb318ce203b7f1f30f04e9c6bf Mon Sep 17 00:00:00 2001 From: johnmadden86 Date: Sat, 19 Oct 2019 01:53:16 +0100 Subject: [PATCH 3/9] assign fixture stats on init --- fpl/models/fixture.py | 87 +++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 40 deletions(-) diff --git a/fpl/models/fixture.py b/fpl/models/fixture.py index fa659e1..5ca012c 100644 --- a/fpl/models/fixture.py +++ b/fpl/models/fixture.py @@ -9,7 +9,7 @@ def add_player(location, information): location.append({"player": player, "goals": goals}) -class Fixture(): +class Fixture: """A class representing fixtures in the Fantasy Premier League. Basic usage:: @@ -27,19 +27,24 @@ class Fixture(): >>> asyncio.run(main()) Arsenal vs. Man City - 10 Aug 19:00 """ + def __init__(self, fixture_information): for k, v in fixture_information.items(): + if k == 'stats': + v = {w['identifier']: {'a': w['a'], 'h': w['h']} for w in v} setattr(self, k, v) - def _get_players(self, metric): + def _get_players(self, metric): # no longer used """Helper function that returns a dictionary containing players for the given metric (away and home). """ stats = getattr(self, "stats", []) for statistic in stats: - if metric in statistic.keys(): - return statistic[metric] - + if metric == statistic['identifier']: + # # merge home and away player lists and sort in descending order + # return sorted(statistic['a'] + statistic['h'], key=lambda x: x['value'], reverse=True) + print(metric, statistic) + return statistic return {} def get_goalscorers(self): @@ -47,78 +52,80 @@ def get_goalscorers(self): :rtype: dict """ - if not getattr(self, "finished", False): - return {} - - return self._get_players("goals_scored") + try: + return self.stats["goals_scored"] + except KeyError: + return {'a': [], 'h': []} def get_assisters(self): """Returns all players who made an assist in the fixture. :rtype: dict """ - if not getattr(self, "finished", False): - return {} - - return self._get_players("assists") + try: + return self.stats["assists"] + except KeyError: + return {'a': [], 'h': []} def get_own_goalscorers(self): """Returns all players who scored an own goal in the fixture. :rtype: dict """ - if not getattr(self, "finished", False): - return {} - - return self._get_players("own_goals") + try: + return self.stats["own_goals"] + except KeyError: + return {'a': [], 'h': []} def get_yellow_cards(self): """Returns all players who received a yellow card in the fixture. :rtype: dict """ - if not getattr(self, "finished", False): - return {} - - return self._get_players("yellow_cards") + try: + return self.stats["yellow_cards"] + except KeyError: + return {'a': [], 'h': []} def get_red_cards(self): """Returns all players who received a red card in the fixture. :rtype: dict """ - if not getattr(self, "finished", False): - return {} - - return self._get_players("red_cards") + try: + return self.stats["red_cards"] + except KeyError: + return {'a': [], 'h': []} def get_penalty_saves(self): """Returns all players who saved a penalty in the fixture. :rtype: dict """ - if not getattr(self, "finished", False): - return {} - - return self._get_players("penalties_saved") + try: + return self.stats["penalties_saved"] + except KeyError: + return {'a': [], 'h': []} def get_penalty_misses(self): """Returns all players who missed a penalty in the fixture. :rtype: dict """ - if not getattr(self, "finished", False): - return {} - - return self._get_players("penalties_missed") + try: + return self.stats["penalties_missed"] + except KeyError: + return {'a': [], 'h': []} def get_saves(self): """Returns all players who made a save in the fixture. :rtype: dict """ - if not getattr(self, "finished", False): - return {} + try: + return self.stats["saves"] + except KeyError: + return {'a': [], 'h': []} return self._get_players("saves") @@ -137,12 +144,12 @@ def get_bps(self): :rtype: dict """ - if not getattr(self, "finished", False): - return {} - - return self._get_players("bps") + try: + return self.stats["bps"] + except KeyError: + return {'a': [], 'h': []} def __str__(self): return (f"{team_converter(self.team_h)} vs. " f"{team_converter(self.team_a)} - " - f"{self.deadline_time_formatted}") + f"{self.kickoff_time}") From d84216093aee9efdd3f86890da78ec58d630b7a4 Mon Sep 17 00:00:00 2001 From: johnmadden86 Date: Sat, 19 Oct 2019 01:58:26 +0100 Subject: [PATCH 4/9] live bonus for fixture --- fpl/models/fixture.py | 52 +++- tests/test_fixture.py | 622 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 578 insertions(+), 96 deletions(-) diff --git a/fpl/models/fixture.py b/fpl/models/fixture.py index 5ca012c..92d7fc9 100644 --- a/fpl/models/fixture.py +++ b/fpl/models/fixture.py @@ -9,6 +9,7 @@ def add_player(location, information): location.append({"player": player, "goals": goals}) +# noinspection PyUnresolvedReferences class Fixture: """A class representing fixtures in the Fantasy Premier League. @@ -127,17 +128,54 @@ def get_saves(self): except KeyError: return {'a': [], 'h': []} - return self._get_players("saves") - - def get_bonus(self): + def get_bonus(self, provisional=False): """Returns all players who received bonus points in the fixture. :rtype: dict """ - if not getattr(self, "finished", False): - return {} - - return self._get_players("bonus") + if self.finished: + return self.stats["bonus"] + elif self.started and provisional: + bps = self.get_bps() + home = [b["element"] for b in bps['h']] + away = [b["element"] for b in bps['a']] + bps = bps['a'] + bps['h'] + bps = {b["element"]: b["value"] for b in bps} # map to dict + bps_values = set(bps.values()) + + try: + bps1 = max(bps_values) # highest bps + bps_values.remove(bps1) + bps2 = max(bps_values) # 2nd highest bps + bps_values.remove(bps2) + bps3 = max(bps_values) # 3rd highest bps + except ValueError: # empty set + return {'a': [], 'h': []} + + else: + bonus3 = list(filter(lambda x: bps[x] == bps1, bps.keys())) + bonus2 = bonus1 = [] + if len(bonus3) == 1: + bonus2 = list(filter(lambda x: bps[x] == bps2, bps.keys())) + if len(bonus3) + len(bonus2) == 2: + if len(bonus3) == 2: # 2 way tie for 3 bonus + bonus1 = list(filter(lambda x: bps[x] == bps2, bps.keys())) + else: + bonus1 = list(filter(lambda x: bps[x] == bps3, bps.keys())) + bonus3 = [{"value": 3, "element": b} for b in bonus3] + bonus2 = [{"value": 2, "element": b} for b in bonus2] + bonus1 = [{"value": 1, "element": b} for b in bonus1] + bonus = bonus3 + bonus2 + bonus1 + h = [] + a = [] + for b in bonus: + if b["element"] in home: + h.append(b) + elif b["element"] in away: + a.append(b) + return {'a': a, 'h': h} + else: + return {'a': [], 'h': []} def get_bps(self): """Returns the bonus points of each player. diff --git a/tests/test_fixture.py b/tests/test_fixture.py index 6a01d0e..b72f058 100644 --- a/tests/test_fixture.py +++ b/tests/test_fixture.py @@ -1,97 +1,530 @@ from fpl.models.fixture import Fixture fixture_data = { - "id": 6, - "kickoff_time_formatted": "10 Aug 20:00", + "code": 1059760, + "event": 6, + "finished": True, + "finished_provisional": True, + "id": 59, + "kickoff_time": "2019-09-20T19:00:00Z", + "minutes": 90, + "provisional_start_time": False, "started": True, - "event_day": 1, - "deadline_time": "2018-08-10T18:00:00Z", - "deadline_time_formatted": "10 Aug 19:00", + "team_a": 3, + "team_a_score": 3, + "team_h": 16, + "team_h_score": 1, "stats": [ { - "goals_scored": { - "a": [{"value": 1, "element": 234}], - "h": [{"value": 1, "element": 286}, {"value": 1, "element": 302}], - } - }, - { - "assists": { - "a": [{"value": 1, "element": 221}], - "h": [{"value": 1, "element": 295}, {"value": 1, "element": 297}], - } - }, - {"own_goals": {"a": [], "h": []}}, - {"penalties_saved": {"a": [], "h": []}}, - {"penalties_missed": {"a": [], "h": []}}, - { - "yellow_cards": { - "a": [{"value": 1, "element": 226}], - "h": [{"value": 1, "element": 304}, {"value": 1, "element": 481}], - } - }, - {"red_cards": {"a": [], "h": []}}, - { - "saves": { - "a": [{"value": 4, "element": 213}], - "h": [{"value": 3, "element": 282}], - } - }, - { - "bonus": { - "a": [{"value": 1, "element": 234}], - "h": [{"value": 3, "element": 286}, {"value": 2, "element": 302}], - } - }, - { - "bps": { - "a": [ - {"value": 25, "element": 234}, - {"value": 23, "element": 221}, - {"value": 16, "element": 213}, - {"value": 16, "element": 215}, - {"value": 15, "element": 225}, - {"value": 14, "element": 220}, - {"value": 13, "element": 227}, - {"value": 13, "element": 231}, - {"value": 12, "element": 219}, - {"value": 10, "element": 233}, - {"value": 6, "element": 226}, - {"value": 5, "element": 228}, - {"value": 3, "element": 492}, - {"value": 2, "element": 236}, - ], - "h": [ - {"value": 30, "element": 286}, - {"value": 29, "element": 302}, - {"value": 24, "element": 297}, - {"value": 22, "element": 295}, - {"value": 16, "element": 289}, - {"value": 15, "element": 282}, - {"value": 15, "element": 292}, - {"value": 13, "element": 291}, - {"value": 13, "element": 305}, - {"value": 13, "element": 481}, - {"value": 8, "element": 304}, - {"value": 4, "element": 298}, - {"value": 3, "element": 303}, - {"value": -2, "element": 306}, - ], - } + "identifier": "goals_scored", + "a": [ + { + "value": 1, + "element": 59 + }, + { + "value": 1, + "element": 67 + }, + { + "value": 1, + "element": 505 + } + ], + "h": [ + { + "value": 1, + "element": 321 + } + ] }, + { + "identifier": "assists", + "a": [ + { + "value": 1, + "element": 65 + }, + { + "value": 1, + "element": 476 + }, + { + "value": 1, + "element": 494 + } + ], + "h": [ + { + "value": 1, + "element": 437 + } + ] + }, + { + "identifier": "own_goals", + "a": [ + + ], + "h": [ + + ] + }, + { + "identifier": "penalties_saved", + "a": [ + + ], + "h": [ + + ] + }, + { + "identifier": "penalties_missed", + "a": [ + + ], + "h": [ + + ] + }, + { + "identifier": "yellow_cards", + "a": [ + { + "value": 1, + "element": 82 + }, + { + "value": 1, + "element": 446 + }, + { + "value": 1, + "element": 505 + } + ], + "h": [ + { + "value": 1, + "element": 328 + } + ] + }, + { + "identifier": "red_cards", + "a": [ + + ], + "h": [ + + ] + }, + { + "identifier": "saves", + "a": [ + { + "value": 6, + "element": 494 + } + ], + "h": [ + + ] + }, + { + "identifier": "bonus", + "a": [ + { + "value": 3, + "element": 494 + }, + { + "value": 1, + "element": 67 + } + ], + "h": [ + { + "value": 2, + "element": 321 + } + ] + }, + { + "identifier": "bps", + "a": [ + { + "value": 34, + "element": 494 + }, + { + "value": 30, + "element": 67 + }, + { + "value": 26, + "element": 59 + }, + { + "value": 25, + "element": 65 + }, + { + "value": 23, + "element": 476 + }, + { + "value": 23, + "element": 505 + }, + { + "value": 14, + "element": 58 + }, + { + "value": 7, + "element": 82 + }, + { + "value": 6, + "element": 446 + }, + { + "value": 4, + "element": 79 + }, + { + "value": 3, + "element": 75 + }, + { + "value": 3, + "element": 80 + }, + { + "value": 2, + "element": 68 + } + ], + "h": [ + { + "value": 31, + "element": 321 + }, + { + "value": 23, + "element": 328 + }, + { + "value": 17, + "element": 437 + }, + { + "value": 16, + "element": 486 + }, + { + "value": 14, + "element": 304 + }, + { + "value": 14, + "element": 508 + }, + { + "value": 13, + "element": 307 + }, + { + "value": 9, + "element": 320 + }, + { + "value": 9, + "element": 325 + }, + { + "value": 7, + "element": 319 + }, + { + "value": 6, + "element": 305 + }, + { + "value": 4, + "element": 313 + }, + { + "value": 3, + "element": 532 + }, + { + "value": 2, + "element": 323 + } + ] + } ], - "team_h_difficulty": 3, - "team_a_difficulty": 4, - "code": 987597, - "kickoff_time": "2018-08-10T19:00:00Z", - "team_h_score": 2, - "team_a_score": 1, - "finished": True, + "team_h_difficulty": 2, + "team_a_difficulty": 2 +} +fixture_data_not_finished = { + "code": 1059760, + "event": 6, + "finished": False, + "finished_provisional": False, + "id": 59, + "kickoff_time": "2019-09-20T19:00:00Z", "minutes": 90, "provisional_start_time": False, - "finished_provisional": True, - "event": 1, - "team_a": 11, - "team_h": 14, + "started": True, + "team_a": 3, + "team_a_score": 3, + "team_h": 16, + "team_h_score": 1, + "stats": [ + { + "identifier": "goals_scored", + "a": [ + { + "value": 1, + "element": 59 + }, + { + "value": 1, + "element": 67 + }, + { + "value": 1, + "element": 505 + } + ], + "h": [ + { + "value": 1, + "element": 321 + } + ] + }, + { + "identifier": "assists", + "a": [ + { + "value": 1, + "element": 65 + }, + { + "value": 1, + "element": 476 + }, + { + "value": 1, + "element": 494 + } + ], + "h": [ + { + "value": 1, + "element": 437 + } + ] + }, + { + "identifier": "own_goals", + "a": [ + + ], + "h": [ + + ] + }, + { + "identifier": "penalties_saved", + "a": [ + + ], + "h": [ + + ] + }, + { + "identifier": "penalties_missed", + "a": [ + + ], + "h": [ + + ] + }, + { + "identifier": "yellow_cards", + "a": [ + { + "value": 1, + "element": 82 + }, + { + "value": 1, + "element": 446 + }, + { + "value": 1, + "element": 505 + } + ], + "h": [ + { + "value": 1, + "element": 328 + } + ] + }, + { + "identifier": "red_cards", + "a": [ + + ], + "h": [ + + ] + }, + { + "identifier": "saves", + "a": [ + { + "value": 6, + "element": 494 + } + ], + "h": [ + + ] + }, + { + "identifier": "bonus", + "a": [], + "h": [] + }, + { + "identifier": "bps", + "a": [ + { + "value": 34, + "element": 494 + }, + { + "value": 30, + "element": 67 + }, + { + "value": 26, + "element": 59 + }, + { + "value": 25, + "element": 65 + }, + { + "value": 23, + "element": 476 + }, + { + "value": 23, + "element": 505 + }, + { + "value": 14, + "element": 58 + }, + { + "value": 7, + "element": 82 + }, + { + "value": 6, + "element": 446 + }, + { + "value": 4, + "element": 79 + }, + { + "value": 3, + "element": 75 + }, + { + "value": 3, + "element": 80 + }, + { + "value": 2, + "element": 68 + } + ], + "h": [ + { + "value": 31, + "element": 321 + }, + { + "value": 23, + "element": 328 + }, + { + "value": 17, + "element": 437 + }, + { + "value": 16, + "element": 486 + }, + { + "value": 14, + "element": 304 + }, + { + "value": 14, + "element": 508 + }, + { + "value": 13, + "element": 307 + }, + { + "value": 9, + "element": 320 + }, + { + "value": 9, + "element": 325 + }, + { + "value": 7, + "element": 319 + }, + { + "value": 6, + "element": 305 + }, + { + "value": 4, + "element": 313 + }, + { + "value": 3, + "element": 532 + }, + { + "value": 2, + "element": 323 + } + ] + } + ], + "team_h_difficulty": 2, + "team_a_difficulty": 2 } @@ -100,20 +533,31 @@ class TestFixture(object): def test_init(): fixture = Fixture(fixture_data) for k, v in fixture_data.items(): - assert getattr(fixture, k) == v + if k != 'stats': + assert getattr(fixture, k) == v @staticmethod def _do_test_not_finished(fixture, method): - delattr(fixture, "finished") + fixture.finished = False + fixture.stats['bonus']['a'] = [] + fixture.stats['bonus']['h'] = [] data_dict = getattr(fixture, method)() assert isinstance(data_dict, dict) - assert len(data_dict) == 0 + assert len(data_dict) == 2 @staticmethod def _do_test_finished(fixture, method): data_dict = getattr(fixture, method)() assert isinstance(data_dict, dict) - assert len(data_dict) == 2 + assert tuple(data_dict.keys()) == ('a', 'h') + assert isinstance(data_dict['a'], list) + assert isinstance(data_dict['h'], list) + assert all([isinstance(d, dict) for d in data_dict['a']]) + assert all([isinstance(d, dict) for d in data_dict['h']]) + assert all([tuple(d.keys()) == ('value', 'element') for d in data_dict['a']]) + assert all([tuple(d.keys()) == ('value', 'element') for d in data_dict['h']]) + assert all([all([isinstance(v, int) for v in d.values()]) for d in data_dict['a']]) + assert all([all([isinstance(v, int) for v in d.values()]) for d in data_dict['h']]) def test_get_goalscorers_not_finished(self, fixture): self._do_test_not_finished(fixture, "get_goalscorers") @@ -177,4 +621,4 @@ def test_get_bps_finished(self, fixture): @staticmethod def test_str(fixture): - assert str(fixture) == "Norwich vs. Man City - 10 Aug 19:00" + assert str(fixture) == "Southampton vs. Bournemouth - 2019-09-20T19:00:00Z" From 7c9ca190931d4a5269ac772b7718e01f39385ed4 Mon Sep 17 00:00:00 2001 From: johnmadden86 Date: Sat, 19 Oct 2019 02:21:47 +0100 Subject: [PATCH 5/9] return id indexed dicts for fixtures, teams, players etc --- fpl/fpl.py | 32 +++++++++++++++++------------ tests/test_fpl.py | 52 +++++++++++++++++++++++++++-------------------- 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/fpl/fpl.py b/fpl/fpl.py index 8c94dc0..9d3c5aa 100644 --- a/fpl/fpl.py +++ b/fpl/fpl.py @@ -39,7 +39,7 @@ position_converter, scale, team_converter) -class FPL(): +class FPL: """The FPL class.""" def __init__(self, session): @@ -102,8 +102,8 @@ async def get_teams(self, team_ids=None, return_json=False): if return_json: return teams - return [Team(team_information, self.session) - for team_information in teams] + return {team_information["id"]: Team(team_information, self.session) + for team_information in teams} async def get_team(self, team_id, return_json=False): """Returns the team with the given ``team_id``. @@ -177,6 +177,7 @@ async def get_player_summary(self, player_id, return_json=False): return PlayerSummary(player_summary) + # not used async def get_player_summaries(self, player_ids, return_json=False): """Returns a list of summaries of players whose ID are in the ``player_ids`` list. @@ -275,7 +276,10 @@ async def get_players(self, player_ids=None, include_summary=False, for player_id in player_ids] players = await asyncio.gather(*tasks) - return players + if return_json: + return list(filter(lambda p: p["id"] in player_ids, players)) + + return {player.id: player for player in players} async def get_fixture(self, fixture_id, return_json=False): """Returns the fixture with the given ``fixture_id``. @@ -352,7 +356,7 @@ async def get_fixtures_by_id(self, fixture_ids, return_json=False): if return_json: return fixtures - return [Fixture(fixture) for fixture in fixtures] + return {fixture["id"]: Fixture(fixture) for fixture in fixtures} async def get_fixtures_by_gameweek(self, gameweek, return_json=False): """Returns a list of all fixtures of the given ``gameweek``. @@ -375,7 +379,7 @@ async def get_fixtures_by_gameweek(self, gameweek, return_json=False): if return_json: return fixtures - return [Fixture(fixture) for fixture in fixtures] + return {fixture["id"]: Fixture(fixture) for fixture in fixtures} async def get_fixtures(self, return_json=False): """Returns a list of *all* fixtures. @@ -402,10 +406,9 @@ async def get_fixtures(self, return_json=False): if return_json: return fixtures - return [Fixture(fixture) for fixture in fixtures] + return {fixture["id"]: Fixture(fixture) for fixture in fixtures} - async def get_gameweek(self, gameweek_id, include_live=False, - return_json=False): + async def get_gameweek(self, gameweek_id, return_json=False): """Returns the gameweek with the ID ``gameweek_id``. Information is taken from e.g.: @@ -441,8 +444,7 @@ async def get_gameweek(self, gameweek_id, include_live=False, return Gameweek(static_gameweek) - async def get_gameweeks(self, gameweek_ids=None, include_live=False, - return_json=False): + async def get_gameweeks(self, gameweek_ids=None, return_json=False): """Returns either a list of *all* gamweeks, or a list of gameweeks whose IDs are in the ``gameweek_ids`` list. @@ -462,11 +464,15 @@ async def get_gameweeks(self, gameweek_ids=None, include_live=False, gameweek_ids = range(1, 39) tasks = [asyncio.ensure_future( - self.get_gameweek(gameweek_id, include_live, return_json)) + self.get_gameweek(gameweek_id, return_json)) for gameweek_id in gameweek_ids] gameweeks = await asyncio.gather(*tasks) - return gameweeks + + if return_json: + return gameweeks + + return {gameweek.id: gameweek for gameweek in gameweeks} async def get_classic_league(self, league_id, return_json=False): """Returns the classic league with the given ``league_id``. Requires diff --git a/tests/test_fpl.py b/tests/test_fpl.py index 84a6cbb..cc24dc8 100644 --- a/tests/test_fpl.py +++ b/tests/test_fpl.py @@ -50,20 +50,20 @@ async def test_team(self, loop, fpl): async def test_teams(self, loop, fpl): teams = await fpl.get_teams() - assert isinstance(teams, list) - assert len(teams) == 20 - assert isinstance(teams[0], Team) + assert isinstance(teams, dict) + assert len(teams.values()) == 20 + assert isinstance(teams[10], Team) teams = await fpl.get_teams(return_json=True) assert isinstance(teams, list) assert len(teams) == 20 - assert isinstance(teams[0], dict) + assert isinstance(teams[1], dict) teams = await fpl.get_teams(team_ids=[1, 2, 3]) - assert isinstance(teams, list) - assert len(teams) == 3 - assert isinstance(teams[0], Team) - assert [team.id for team in teams] == [1, 2, 3] + assert isinstance(teams, dict) + assert len(teams.values()) == 3 + assert isinstance(teams[1], Team) + assert [team.id for team in teams.values()] == [1, 2, 3] async def test_player_summary(self, loop, fpl): # test non positive id @@ -107,19 +107,24 @@ async def test_player(self, loop, fpl): async def test_players(self, loop, fpl): players = await fpl.get_players() - assert isinstance(players, list) - assert isinstance(players[0], Player) + assert isinstance(players, dict) + assert isinstance(players[1], Player) players = await fpl.get_players(return_json=True) assert isinstance(players, list) assert isinstance(players[0], dict) players = await fpl.get_players([1, 2, 3]) - assert len(players) == 3 + assert len(players.values()) == 3 - players = await fpl.get_players([1, 2, 3], True) + players = await fpl.get_players([1, 2, 3], include_summary=True) + assert len(players.values()) == 3 + summary_keys = ("history_past", "history", "fixtures") + assert all([isinstance(getattr(players[2], key), list) for key in summary_keys]) + + players = await fpl.get_players([1, 2, 3], include_summary=True, return_json=True) assert len(players) == 3 - assert isinstance(players[0].fixtures, list) + assert all([isinstance(players[2][key], list) for key in summary_keys]) async def test_fixture(self, loop, fpl): # test fixture with unknown id @@ -139,8 +144,8 @@ async def test_fixtures_by_id(self, loop, fpl): assert len(fixtures) == 0 fixtures = await fpl.get_fixtures_by_id([100, 200, 300]) - assert isinstance(fixtures, list) - assert isinstance(fixtures[0], Fixture) + assert isinstance(fixtures, dict) + assert isinstance(fixtures[100], Fixture) fixtures = await fpl.get_fixtures_by_id( [100, 200, 300], return_json=True) @@ -153,8 +158,8 @@ async def test_fixtures_by_id(self, loop, fpl): async def test_fixtures_by_gameweek(self, loop, fpl): for gameweek in range(1, 39): fixtures = await fpl.get_fixtures_by_gameweek(gameweek) - assert isinstance(fixtures, list) - assert isinstance(fixtures[0], Fixture) + assert isinstance(fixtures, dict) + assert all([isinstance(fixtures[fixture_id], Fixture) for fixture_id in fixtures.keys()]) fixtures = await fpl.get_fixtures_by_gameweek( gameweek, return_json=True) @@ -162,17 +167,17 @@ async def test_fixtures_by_gameweek(self, loop, fpl): async def test_fixtures(self, loop, fpl): fixtures = await fpl.get_fixtures() - assert isinstance(fixtures, list) - assert isinstance(fixtures[0], Fixture) + assert isinstance(fixtures, dict) + assert isinstance(fixtures[10], Fixture) fixtures = await fpl.get_fixtures(return_json=True) assert isinstance(fixtures[0], dict) async def test_gameweeks(self, loop, fpl): gameweeks = await fpl.get_gameweeks() - assert isinstance(gameweeks, list) - assert len(gameweeks) == 38 - assert isinstance(gameweeks[0], Gameweek) + assert isinstance(gameweeks, dict) + assert len(gameweeks.values()) == 38 + assert isinstance(gameweeks[10], Gameweek) gameweeks = await fpl.get_gameweeks([1, 2, 3], return_json=True) assert isinstance(gameweeks, list) @@ -183,9 +188,12 @@ async def test_gameweek(self, loop, fpl): gameweek = await fpl.get_gameweek(20) assert isinstance(gameweek, Gameweek) assert gameweek.id == 20 + assert not hasattr(gameweek, "elements") gameweek = await fpl.get_gameweek(20, return_json=True) assert isinstance(gameweek, dict) + assert gameweek["id"] == 20 + assert "elements" not in gameweek.keys() async def test_classic_league(self, loop, fpl): await fpl.login() From 2b87fbb9ae19400094a869e143bada7cfd9a6acc Mon Sep 17 00:00:00 2001 From: johnmadden86 Date: Sat, 19 Oct 2019 02:23:35 +0100 Subject: [PATCH 6/9] unit test --- tests/test_fpl.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/test_fpl.py b/tests/test_fpl.py index 84a6cbb..888cb30 100644 --- a/tests/test_fpl.py +++ b/tests/test_fpl.py @@ -17,6 +17,21 @@ async def test_init(self, loop): session = aiohttp.ClientSession() fpl = FPL(session) assert fpl.session is session + keys = [ + "events", + "game_settings", + "phases", + "teams", + "elements", + "element_types", + "element_stats", + "total_players", + "current_gameweek", + ] + assert all([hasattr(fpl, key) for key in keys]) + assert all([isinstance(getattr(fpl, key), dict) for key in keys[:-3]]) + assert isinstance(getattr(fpl, keys[-3]), list) + assert all([isinstance(getattr(fpl, key), int) for key in keys[-2:]]) await session.close() async def test_user(self, loop, fpl): From 7393e83886d0ca88624d8efe7a75c53ee7f0e996 Mon Sep 17 00:00:00 2001 From: johnmadden86 Date: Sat, 19 Oct 2019 02:28:18 +0100 Subject: [PATCH 7/9] include live data in gameweek class --- fpl/fpl.py | 28 +++++++++++++++++++++++----- tests/test_fpl.py | 15 +++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/fpl/fpl.py b/fpl/fpl.py index 8c94dc0..c0e7f21 100644 --- a/fpl/fpl.py +++ b/fpl/fpl.py @@ -39,7 +39,7 @@ position_converter, scale, team_converter) -class FPL(): +class FPL: """The FPL class.""" def __init__(self, session): @@ -431,10 +431,28 @@ async def get_gameweek(self, gameweek_id, include_live=False, except StopIteration: raise ValueError(f"Gameweek with ID {gameweek_id} not found") - live_gameweek = await fetch( - self.session, API_URLS["gameweek_live"].format(gameweek_id)) - - live_gameweek.update(static_gameweek) + if include_live: + live_gameweek = await fetch( + self.session, API_URLS["gameweek_live"].format(gameweek_id)) + + # convert element list to dict + live_gameweek["elements"] = {element['id']: element for element in live_gameweek['elements']} + + # include live bonus points + if not static_gameweek['finished']: + fixtures = await self.get_fixtures_by_gameweek(gameweek_id) + fixtures = filter(lambda f: not f.finished, fixtures) + bonus_for_gameweek = [] + for fixture in fixtures: + bonus = fixture.get_bonus(provisional=True) + bonus_for_gameweek.extend(bonus['a'] + bonus['h']) + bonus_for_gameweek = {b['element']: b['value'] for b in bonus_for_gameweek} + for player_id, bonus_points in bonus_for_gameweek: + if live_gameweek["elements"][player_id]["bonus"] == 0: + live_gameweek["elements"][player_id]["bonus"] += bonus_points + live_gameweek["elements"][player_id]["total_points"] += bonus_points + + static_gameweek.update(live_gameweek) if return_json: return static_gameweek diff --git a/tests/test_fpl.py b/tests/test_fpl.py index 84a6cbb..4390bfb 100644 --- a/tests/test_fpl.py +++ b/tests/test_fpl.py @@ -183,10 +183,25 @@ async def test_gameweek(self, loop, fpl): gameweek = await fpl.get_gameweek(20) assert isinstance(gameweek, Gameweek) assert gameweek.id == 20 + assert not hasattr(gameweek, "elements") gameweek = await fpl.get_gameweek(20, return_json=True) assert isinstance(gameweek, dict) + assert gameweek["id"] == 20 + assert "elements" not in gameweek.keys() + gameweek = await fpl.get_gameweek(1, include_live=True) + assert isinstance(gameweek, Gameweek) + assert hasattr(gameweek, "elements") + assert isinstance(gameweek.elements, dict) + + gameweek = await fpl.get_gameweek(1, include_live=True, return_json=True) + assert isinstance(gameweek, dict) + assert "elements" in gameweek.keys() + assert isinstance(gameweek["elements"], dict) + + + @pytest.mark.skip(reason="Cannot currently test it.") async def test_classic_league(self, loop, fpl): await fpl.login() classic_league = await fpl.get_classic_league(173226) From 45c52a59357b173f6c03459e04ffc07482a1c0b0 Mon Sep 17 00:00:00 2001 From: johnmadden86 Date: Sat, 19 Oct 2019 02:30:21 +0100 Subject: [PATCH 8/9] . --- tests/test_fpl.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_fpl.py b/tests/test_fpl.py index 4390bfb..4f3927f 100644 --- a/tests/test_fpl.py +++ b/tests/test_fpl.py @@ -182,6 +182,7 @@ async def test_gameweeks(self, loop, fpl): async def test_gameweek(self, loop, fpl): gameweek = await fpl.get_gameweek(20) assert isinstance(gameweek, Gameweek) + # noinspection PyUnresolvedReferences assert gameweek.id == 20 assert not hasattr(gameweek, "elements") @@ -193,6 +194,7 @@ async def test_gameweek(self, loop, fpl): gameweek = await fpl.get_gameweek(1, include_live=True) assert isinstance(gameweek, Gameweek) assert hasattr(gameweek, "elements") + # noinspection PyUnresolvedReferences assert isinstance(gameweek.elements, dict) gameweek = await fpl.get_gameweek(1, include_live=True, return_json=True) @@ -200,7 +202,6 @@ async def test_gameweek(self, loop, fpl): assert "elements" in gameweek.keys() assert isinstance(gameweek["elements"], dict) - @pytest.mark.skip(reason="Cannot currently test it.") async def test_classic_league(self, loop, fpl): await fpl.login() @@ -253,6 +254,7 @@ async def test_points_against(self, loop, fpl): points_against = await fpl.get_points_against() assert isinstance(points_against, dict) + # noinspection PyPep8Naming async def test_FDR(self, loop, fpl): def test_main(fdr): assert isinstance(fdr, dict) From b60c788c55480ba92ed4731ffc11c617758e8bc7 Mon Sep 17 00:00:00 2001 From: johnmadden86 Date: Sat, 19 Oct 2019 14:36:07 +0100 Subject: [PATCH 9/9] apply live scores to players --- fpl/fpl.py | 31 ++++++++++++++++++++----------- tests/test_fpl.py | 11 +++++++---- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/fpl/fpl.py b/fpl/fpl.py index b0fb03c..12087a5 100644 --- a/fpl/fpl.py +++ b/fpl/fpl.py @@ -216,7 +216,7 @@ async def get_player_summaries(self, player_ids, return_json=False): return [PlayerSummary(player_summary) for player_summary in player_summaries] - async def get_player(self, player_id, players=None, include_summary=False, + async def get_player(self, player_id, players=None, gameweek=None, include_summary=False, return_json=False): """Returns the player with the given ``player_id``. @@ -224,6 +224,7 @@ async def get_player(self, player_id, players=None, include_summary=False, https://fantasy.premierleague.com/api/bootstrap-static/ https://fantasy.premierleague.com/api/element-summary/1/ (optional) + :param gameweek: the current gameweek data (for applying live scores) :param player_id: A player's ID. :type player_id: string or int :param list players: (optional) A list of players. @@ -249,12 +250,15 @@ async def get_player(self, player_id, players=None, include_summary=False, player["id"], return_json=True) player.update(player_summary) + if gameweek: + player["live_score"] = gameweek.elements[player_id]["stats"]["total_points"] + if return_json: return player return Player(player, self.session) - async def get_players(self, player_ids=None, include_summary=False, + async def get_players(self, player_ids=None, include_summary=False, include_live=None, return_json=False): """Returns either a list of *all* players, or a list of players whose IDs are in the given ``player_ids`` list. @@ -263,6 +267,7 @@ async def get_players(self, player_ids=None, include_summary=False, https://fantasy.premierleague.com/api/bootstrap-static/ https://fantasy.premierleague.com/api/element-summary/1/ (optional) + :param include_live: (optional) include a player's live score :param list player_ids: (optional) A list of player IDs :param boolean include_summary: (optional) Includes a player's summary if ``True``. @@ -273,13 +278,17 @@ async def get_players(self, player_ids=None, include_summary=False, :rtype: list """ players = getattr(self, "elements") + gameweek = None if not player_ids: player_ids = [player["id"] for player in players.values()] + if include_live: + gameweek = await self.get_gameweek(getattr(self, "current_gameweek"), include_live=True) + tasks = [asyncio.ensure_future( self.get_player( - player_id, players, include_summary, return_json)) + player_id, players, gameweek, include_summary, return_json)) for player_id in player_ids] players = await asyncio.gather(*tasks) @@ -415,7 +424,7 @@ async def get_fixtures(self, return_json=False): return {fixture["id"]: Fixture(fixture) for fixture in fixtures} - async def get_gameweek(self, gameweek_id, return_json=False): + async def get_gameweek(self, gameweek_id, include_live=False, return_json=False): """Returns the gameweek with the ID ``gameweek_id``. Information is taken from e.g.: @@ -423,7 +432,7 @@ async def get_gameweek(self, gameweek_id, return_json=False): https://fantasy.premierleague.com/api/event/1/live/ :param int gameweek_id: A gameweek's ID. - :param bool include_summary: (optional) Includes a gameweek's live data + :param bool include_live: (optional) Includes a gameweek's live data if ``True``. :param return_json: (optional) Boolean. If ``True`` returns a ``dict``, if ``False`` returns a :class:`Gameweek` object. Defaults to @@ -450,16 +459,16 @@ async def get_gameweek(self, gameweek_id, return_json=False): # include live bonus points if not static_gameweek['finished']: fixtures = await self.get_fixtures_by_gameweek(gameweek_id) - fixtures = filter(lambda f: not f.finished, fixtures) + fixtures = filter(lambda f: not f.finished, fixtures.values()) bonus_for_gameweek = [] for fixture in fixtures: bonus = fixture.get_bonus(provisional=True) bonus_for_gameweek.extend(bonus['a'] + bonus['h']) bonus_for_gameweek = {b['element']: b['value'] for b in bonus_for_gameweek} - for player_id, bonus_points in bonus_for_gameweek: - if live_gameweek["elements"][player_id]["bonus"] == 0: - live_gameweek["elements"][player_id]["bonus"] += bonus_points - live_gameweek["elements"][player_id]["total_points"] += bonus_points + for player_id, bonus_points in bonus_for_gameweek.items(): + if live_gameweek["elements"][player_id]["stats"]["bonus"] == 0: + live_gameweek["elements"][player_id]["stats"]["bonus"] += bonus_points + live_gameweek["elements"][player_id]["stats"]["total_points"] += bonus_points static_gameweek.update(live_gameweek) @@ -488,7 +497,7 @@ async def get_gameweeks(self, gameweek_ids=None, return_json=False): gameweek_ids = range(1, 39) tasks = [asyncio.ensure_future( - self.get_gameweek(gameweek_id, return_json)) + self.get_gameweek(gameweek_id, False, return_json)) for gameweek_id in gameweek_ids] gameweeks = await asyncio.gather(*tasks) diff --git a/tests/test_fpl.py b/tests/test_fpl.py index 69e6831..036fdb4 100644 --- a/tests/test_fpl.py +++ b/tests/test_fpl.py @@ -137,6 +137,10 @@ async def test_players(self, loop, fpl): summary_keys = ("history_past", "history", "fixtures") assert all([isinstance(getattr(players[2], key), list) for key in summary_keys]) + players = await fpl.get_players([1, 2, 3], include_live=True) + assert len(players.values()) == 3 + assert isinstance(getattr(players[2], "live_score"), int) + players = await fpl.get_players([1, 2, 3], include_summary=True, return_json=True) assert len(players) == 3 assert all([isinstance(players[2][key], list) for key in summary_keys]) @@ -222,7 +226,6 @@ async def test_gameweek(self, loop, fpl): assert "elements" in gameweek.keys() assert isinstance(gameweek["elements"], dict) - @pytest.mark.skip(reason="Cannot currently test it.") async def test_classic_league(self, loop, fpl): await fpl.login() classic_league = await fpl.get_classic_league(173226) @@ -276,11 +279,11 @@ async def test_points_against(self, loop, fpl): # noinspection PyPep8Naming async def test_FDR(self, loop, fpl): - def test_main(fdr): - assert isinstance(fdr, dict) + def test_main(fdr_): + assert isinstance(fdr_, dict) location_extrema = {"H": [], "A": []} - for _, positions in fdr.items(): + for _, positions in fdr_.items(): for location in positions.values(): location_extrema["H"].append(location["H"]) location_extrema["A"].append(location["A"])