From 3b8c0fb040f8ec3a7dcbdfc23d9006680dfc0570 Mon Sep 17 00:00:00 2001 From: amosbastian Date: Tue, 12 Feb 2019 23:25:56 +0000 Subject: [PATCH 01/19] Update requirements.txt --- requirements.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/requirements.txt b/requirements.txt index 80a427e..24e1054 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,8 @@ appdirs==1.4.3 async-timeout==3.0.1 atomicwrites==1.2.1 attrs==18.2.0 +beautifulsoup4==4.7.1 +bs4==0.0.1 certifi==2018.11.29 chardet==3.0.4 Click==7.0 @@ -22,6 +24,7 @@ pytest==4.2.0 pytest-aiohttp==0.3.0 requests==2.21.0 six==1.12.0 +soupsieve==1.7.3 update-checker==0.16 urllib3==1.24.1 websocket-client==0.54.0 From e8c71ca6a9975082234652eee20b1c213ccc71ba Mon Sep 17 00:00:00 2001 From: amosbastian Date: Tue, 12 Feb 2019 23:28:28 +0000 Subject: [PATCH 02/19] Add comment_template.md --- comment_template.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 comment_template.md diff --git a/comment_template.md b/comment_template.md new file mode 100644 index 0000000..353b6a8 --- /dev/null +++ b/comment_template.md @@ -0,0 +1,5 @@ +{comment_body} + +--- + +^Made ^by ^/u/esoemah. ^Source: ^https://github.com/amosbastian/FPLbot From 610103aeeb11092b2c8f324e76f353fbbcb5d1e1 Mon Sep 17 00:00:00 2001 From: amosbastian Date: Wed, 13 Feb 2019 14:39:39 +0000 Subject: [PATCH 03/19] Move player_vs_team_table() to utils.py --- FPLbot/bot.py | 42 +++--------------------------------------- FPLbot/utils.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/FPLbot/bot.py b/FPLbot/bot.py index 7143f0a..8327722 100644 --- a/FPLbot/bot.py +++ b/FPLbot/bot.py @@ -12,7 +12,8 @@ from pymongo import MongoClient from constants import fpl_team_names, versus_pattern -from utils import create_logger, get_player_table, to_fpl_team, update_players +from utils import (create_logger, get_player_table, player_vs_team_table, + to_fpl_team, update_players) dirname = os.path.dirname(os.path.realpath(__file__)) logger = create_logger() @@ -81,42 +82,6 @@ async def post_price_changes(self): self.subreddit.submit(post_title, selftext=post_body) await update_players() - def player_vs_team_table(self, fixtures): - """Returns a Markdown table showing the player's performance in the - given fixtures. - """ - table = ("|Fixture|Date|MP|G|xG|A|xA|NPG|NPxG|KP|\n" - "|:-|:-:|-:|-:|-:|-:|-:|-:|-:|-:|\n") - - for fixture in fixtures: - home_team = f"{fixture['h_team']} {fixture['h_goals']}" - away_team = f"{fixture['a_goals']} {fixture['a_team']}" - - # Highlight the winning team - if int(fixture["h_goals"]) > int(fixture["a_goals"]): - home_team = f"**{home_team}**" - elif int(fixture["h_goals"]) < int(fixture["a_goals"]): - away_team = f"**{away_team}**" - - # Highlight whether the player was a starter or not - if fixture["position"].lower() != "sub": - fixture["time"] = f"**{fixture['time']}**" - - table += ( - f"|{home_team}-{away_team}" - f"|{fixture['date']}" - f"|{fixture['time']}" - f"|{fixture['goals']}" - f"|{float(fixture['xG']):.2f}" - f"|{fixture['assists']}" - f"|{float(fixture['xA']):.2f}" - f"|{fixture['npg']}" - f"|{float(fixture['npxG']):.2f}" - f"|{fixture['key_passes']}|\n" - ) - - return table - def versus_team_handler(self, player_name, team_name, number_of_fixtures): """Function for handling player vs. team comment.""" # Find most relevant player using text search @@ -148,8 +113,7 @@ def versus_team_handler(self, player_name, team_name, number_of_fixtures): fixture_count += 1 relevant_fixtures.append(fixture) - player_vs_team_table = self.player_vs_team_table(relevant_fixtures) - return player_vs_team_table + return player_vs_team_table(relevant_fixtures) def add_comment_to_database(self, comment): logger.info(f"Adding comment with ID {comment.id} to the database.") diff --git a/FPLbot/utils.py b/FPLbot/utils.py index cf04072..b029bff 100644 --- a/FPLbot/utils.py +++ b/FPLbot/utils.py @@ -180,6 +180,43 @@ def get_player_table(players, risers=True): return table_header + table_body +def player_vs_team_table( fixtures): + """Returns a Markdown table showing the player's performance in the + given fixtures. + """ + table = ("|Fixture|Date|MP|G|xG|A|xA|NPG|NPxG|KP|\n" + "|:-|:-:|-:|-:|-:|-:|-:|-:|-:|-:|\n") + + for fixture in fixtures: + home_team = f"{fixture['h_team']} {fixture['h_goals']}" + away_team = f"{fixture['a_goals']} {fixture['a_team']}" + + # Highlight the winning team + if int(fixture["h_goals"]) > int(fixture["a_goals"]): + home_team = f"**{home_team}**" + elif int(fixture["h_goals"]) < int(fixture["a_goals"]): + away_team = f"**{away_team}**" + + # Highlight whether the player was a starter or not + if fixture["position"].lower() != "sub": + fixture["time"] = f"**{fixture['time']}**" + + table += ( + f"|{home_team}-{away_team}" + f"|{fixture['date']}" + f"|{fixture['time']}" + f"|{fixture['goals']}" + f"|{float(fixture['xG']):.2f}" + f"|{fixture['assists']}" + f"|{float(fixture['xA']):.2f}" + f"|{fixture['npg']}" + f"|{float(fixture['npxG']):.2f}" + f"|{fixture['key_passes']}|\n" + ) + + return table + + def to_fpl_team(team_name): try: return to_fpl_team_dict[team_name] From 683d6bdf817f8289ef166efbb98ae67eb09ba9e6 Mon Sep 17 00:00:00 2001 From: amosbastian Date: Wed, 13 Feb 2019 14:54:14 +0000 Subject: [PATCH 04/19] Update comment_template & add it to bot --- FPLbot/bot.py | 9 ++++++++- FPLbot/utils.py | 2 +- comment_template.md | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/FPLbot/bot.py b/FPLbot/bot.py index 8327722..32ac7e4 100644 --- a/FPLbot/bot.py +++ b/FPLbot/bot.py @@ -113,7 +113,14 @@ def versus_team_handler(self, player_name, team_name, number_of_fixtures): fixture_count += 1 relevant_fixtures.append(fixture) - return player_vs_team_table(relevant_fixtures) + post_template = open(f"{dirname}/../comment_template.md").read() + table_header = f"# {player_name.title()} vs. {team_name.title()}\n" + table_body = player_vs_team_table(relevant_fixtures) + + return post_template.format( + comment_header=table_header, + comment_body=table_body + ) def add_comment_to_database(self, comment): logger.info(f"Adding comment with ID {comment.id} to the database.") diff --git a/FPLbot/utils.py b/FPLbot/utils.py index b029bff..4a1021a 100644 --- a/FPLbot/utils.py +++ b/FPLbot/utils.py @@ -180,7 +180,7 @@ def get_player_table(players, risers=True): return table_header + table_body -def player_vs_team_table( fixtures): +def player_vs_team_table(fixtures): """Returns a Markdown table showing the player's performance in the given fixtures. """ diff --git a/comment_template.md b/comment_template.md index 353b6a8..124f1ae 100644 --- a/comment_template.md +++ b/comment_template.md @@ -1,3 +1,5 @@ +{comment_header} + {comment_body} --- From a39a7a0fbc48fd99452700a7957570a03fdceb35 Mon Sep 17 00:00:00 2001 From: amosbastian Date: Wed, 13 Feb 2019 14:55:54 +0000 Subject: [PATCH 05/19] Update highlighting of team that won --- FPLbot/utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/FPLbot/utils.py b/FPLbot/utils.py index 4a1021a..420efc8 100644 --- a/FPLbot/utils.py +++ b/FPLbot/utils.py @@ -188,14 +188,14 @@ def player_vs_team_table(fixtures): "|:-|:-:|-:|-:|-:|-:|-:|-:|-:|-:|\n") for fixture in fixtures: - home_team = f"{fixture['h_team']} {fixture['h_goals']}" - away_team = f"{fixture['a_goals']} {fixture['a_team']}" + home_team = f"{fixture['h_team']}" + away_team = f"{fixture['a_goals']}" # Highlight the winning team if int(fixture["h_goals"]) > int(fixture["a_goals"]): - home_team = f"**{home_team}**" + home_team = f"**{home_team}** {fixture['h_goals']}" elif int(fixture["h_goals"]) < int(fixture["a_goals"]): - away_team = f"**{away_team}**" + away_team = f"**{away_team}** {fixture['a_team']}" # Highlight whether the player was a starter or not if fixture["position"].lower() != "sub": From f04e43bd20cdde38adba4f8f213931d382fb0214 Mon Sep 17 00:00:00 2001 From: amosbastian Date: Wed, 13 Feb 2019 15:13:10 +0000 Subject: [PATCH 06/19] Add find_players() function --- FPLbot/utils.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/FPLbot/utils.py b/FPLbot/utils.py index 420efc8..982e4ff 100644 --- a/FPLbot/utils.py +++ b/FPLbot/utils.py @@ -217,6 +217,21 @@ def player_vs_team_table(fixtures): return table +def find_player(player_name): + # Find most relevant player using text search + players = database.players.find( + {"$text": {"$search": player_name}}, + {"score": {"$meta": "textScore"}} + ).sort([("score", {"$meta": "textScore"})]) + + try: + player = list(players.limit(1))[0] + except IndexError: + logger.error(f"Player {player_name} could not be found!") + return None + return player + + def to_fpl_team(team_name): try: return to_fpl_team_dict[team_name] From 6e4dcfcbf45ca50b9ce8be25547ada8fc9900c74 Mon Sep 17 00:00:00 2001 From: amosbastian Date: Wed, 13 Feb 2019 15:18:51 +0000 Subject: [PATCH 07/19] Add boilerplate for versus_player_handler() --- FPLbot/bot.py | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/FPLbot/bot.py b/FPLbot/bot.py index 32ac7e4..c398b16 100644 --- a/FPLbot/bot.py +++ b/FPLbot/bot.py @@ -12,8 +12,8 @@ from pymongo import MongoClient from constants import fpl_team_names, versus_pattern -from utils import (create_logger, get_player_table, player_vs_team_table, - to_fpl_team, update_players) +from utils import (create_logger, find_player, get_player_table, + player_vs_team_table, to_fpl_team, update_players) dirname = os.path.dirname(os.path.realpath(__file__)) logger = create_logger() @@ -82,18 +82,33 @@ async def post_price_changes(self): self.subreddit.submit(post_title, selftext=post_body) await update_players() + def versus_player_handler(self, player_A_name, player_B_name, + number_of_fixtures): + """Function for handling player vs. player comment.""" + player_A = find_player(player_A_name) + player_B = find_player(player_B_name) + + if not player_A or not player_B: + return + + player_A_fixtures = player_A["understat_history"] + player_B_fixtures = player_B["understat_history"] + + if not number_of_fixtures: + number_of_fixtures = max(len(player_A_fixtures), len(player_B_fixtures)) + + fixture_count = 0 + relevant_fixtures = [] + for fixtures in zip(player_A_fixtures, player_B_fixtures): + fixture_A = fixtures[0] + fixture_B = fixtures[1] + + print(fixture_A, fixture_B) + def versus_team_handler(self, player_name, team_name, number_of_fixtures): """Function for handling player vs. team comment.""" - # Find most relevant player using text search - players = self.database.players.find( - {"$text": {"$search": player_name}}, - {"score": {"$meta": "textScore"}} - ).sort([("score", {"$meta": "textScore"})]) - - try: - player = list(players.limit(1))[0] - except IndexError: - logger.error(f"Player {player_name} could not be found!") + player = find_player(player_name) + if not player: return if not number_of_fixtures: @@ -177,7 +192,7 @@ async def main(config): async with aiohttp.ClientSession() as session: fpl_bot = FPLBot(config, session) - fpl_bot.run() + fpl_bot.versus_player_handler("pogba", "son", None) if __name__ == "__main__": From 5aae5039f2f8f9a2c913a48acfbf68ab3e5a94c0 Mon Sep 17 00:00:00 2001 From: amosbastian Date: Wed, 13 Feb 2019 16:13:42 +0000 Subject: [PATCH 08/19] Add footer to player vs. team table --- FPLbot/utils.py | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/FPLbot/utils.py b/FPLbot/utils.py index 982e4ff..5d51a15 100644 --- a/FPLbot/utils.py +++ b/FPLbot/utils.py @@ -185,26 +185,28 @@ def player_vs_team_table(fixtures): given fixtures. """ table = ("|Fixture|Date|MP|G|xG|A|xA|NPG|NPxG|KP|\n" - "|:-|:-:|-:|-:|-:|-:|-:|-:|-:|-:|\n") + "|:-|:-|-:|-:|-:|-:|-:|-:|-:|-:|\n") + + total = {} for fixture in fixtures: - home_team = f"{fixture['h_team']}" - away_team = f"{fixture['a_goals']}" + home_team = f"{fixture['h_team']} {fixture['h_goals']}" + away_team = f"{fixture['a_goals']} {fixture['a_team']}" # Highlight the winning team if int(fixture["h_goals"]) > int(fixture["a_goals"]): - home_team = f"**{home_team}** {fixture['h_goals']}" + home_team = f"**{fixture['h_team']}** {fixture['h_goals']}" elif int(fixture["h_goals"]) < int(fixture["a_goals"]): - away_team = f"**{away_team}** {fixture['a_team']}" + away_team = f"**{fixture['a_goals']}** {fixture['a_team']}" # Highlight whether the player was a starter or not if fixture["position"].lower() != "sub": - fixture["time"] = f"**{fixture['time']}**" + minutes_played = f"**{fixture['time']}**" table += ( f"|{home_team}-{away_team}" f"|{fixture['date']}" - f"|{fixture['time']}" + f"|{minutes_played}" f"|{fixture['goals']}" f"|{float(fixture['xG']):.2f}" f"|{fixture['assists']}" @@ -214,7 +216,27 @@ def player_vs_team_table(fixtures): f"|{fixture['key_passes']}|\n" ) - return table + for key, value in fixture.items(): + total.setdefault(key, 0) + try: + total[key] += float(value) + except ValueError: + continue + + # Add footer with totals + table_footer = ( + f"|||**{int(total['time'])}**" + f"|**{int(total['goals'])}**" + f"|**{total['xG']:.2f}**" + f"|**{int(total['assists'])}**" + f"|**{total['xA']:.2f}**" + f"|**{total['npg']}**" + f"|**{total['npxG']:.2f}**" + f"|**{int(total['key_passes'])}**|\n" + ) + + print(table + table_footer) + return table + table_footer def find_player(player_name): From 9a7af1fbe0adde4dc24244d232171d29d3894000 Mon Sep 17 00:00:00 2001 From: amosbastian Date: Thu, 14 Feb 2019 13:23:32 +0000 Subject: [PATCH 09/19] Add 1st version player_vs_player_table() --- FPLbot/utils.py | 65 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/FPLbot/utils.py b/FPLbot/utils.py index 5d51a15..9f1d146 100644 --- a/FPLbot/utils.py +++ b/FPLbot/utils.py @@ -180,6 +180,68 @@ def get_player_table(players, risers=True): return table_header + table_body +def get_total(total, fixture): + for key, value in fixture.items(): + total.setdefault(key, 0) + try: + total[key] += float(value) + except ValueError: + continue + return total + + +def player_vs_player_table(fixtures): + table = ("|xA|A|xG|G|MP|Fixture|Fixture|MP|G|xG|A|xA|\n" + "|-:|-:|-:|-:|-:|:-|-:|-:|-:|-:|-:|-:|\n") + + total_A = {} + total_B = {} + for fixture in fixtures: + fixture_A = fixture[0] + fixture_B = fixture[1] + + # Highlight whether the player was a starter or not + if fixture_A["position"].lower() != "sub": + minutes_played_A = f"**{fixture_A['time']}**" + + if fixture_B["position"].lower() != "sub": + minutes_played_B = f"**{fixture_B['time']}**" + + table += ( + f"|{float(fixture_A['xA']):.2f}" + f"|{fixture_A['assists']}" + f"|{float(fixture_A['xG']):.2f}" + f"|{fixture_A['goals']}" + f"|{minutes_played_A}" + f"|{fixture_A['h_team']} {fixture_A['h_goals']}-" + f"{fixture_A['a_goals']} {fixture_A['a_team']}" + f"|{fixture_B['h_team']} {fixture_B['h_goals']}-" + f"{fixture_B['a_goals']} {fixture_B['a_team']}" + f"|{minutes_played_B}" + f"|{fixture_B['goals']}" + f"|{float(fixture_B['xG']):.2f}" + f"|{fixture_B['assists']}" + f"|{float(fixture_B['xA']):.2f}|\n" + ) + total_A = get_total(total_A, fixture_A) + total_B = get_total(total_B, fixture_B) + + table_footer = ( + f"|**{total_A['xA']:.2f}**" + f"|**{int(total_A['assists'])}**" + f"|**{total_A['xG']:.2f}**" + f"|**{int(total_A['goals'])}**" + f"|**{int(total_A['time'])}**||" + f"|**{int(total_B['time'])}**" + f"|**{int(total_B['goals'])}**" + f"|**{total_B['xG']:.2f}**" + f"|**{int(total_B['assists'])}**" + f"|**{total_B['xA']:.2f}**|\n" + ) + + print(table + table_footer) + + def player_vs_team_table(fixtures): """Returns a Markdown table showing the player's performance in the given fixtures. @@ -192,6 +254,7 @@ def player_vs_team_table(fixtures): for fixture in fixtures: home_team = f"{fixture['h_team']} {fixture['h_goals']}" away_team = f"{fixture['a_goals']} {fixture['a_team']}" + minutes_played = fixture["time"] # Highlight the winning team if int(fixture["h_goals"]) > int(fixture["a_goals"]): @@ -201,7 +264,7 @@ def player_vs_team_table(fixtures): # Highlight whether the player was a starter or not if fixture["position"].lower() != "sub": - minutes_played = f"**{fixture['time']}**" + minutes_played = f"**{minutes_played}**" table += ( f"|{home_team}-{away_team}" From d3b1cbd1246706b455ab09b1f5b0340012175e3e Mon Sep 17 00:00:00 2001 From: amosbastian Date: Thu, 14 Feb 2019 13:24:14 +0000 Subject: [PATCH 10/19] Update versus_player_handler() --- FPLbot/bot.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/FPLbot/bot.py b/FPLbot/bot.py index c398b16..3122520 100644 --- a/FPLbot/bot.py +++ b/FPLbot/bot.py @@ -13,7 +13,8 @@ from constants import fpl_team_names, versus_pattern from utils import (create_logger, find_player, get_player_table, - player_vs_team_table, to_fpl_team, update_players) + player_vs_player_table, player_vs_team_table, to_fpl_team, + update_players) dirname = os.path.dirname(os.path.realpath(__file__)) logger = create_logger() @@ -91,19 +92,22 @@ def versus_player_handler(self, player_A_name, player_B_name, if not player_A or not player_B: return - player_A_fixtures = player_A["understat_history"] - player_B_fixtures = player_B["understat_history"] + player_A_fixtures = [fixture for fixture in player_A["understat_history"] + if to_fpl_team(fixture["h_team"].lower()) in fpl_team_names or + to_fpl_team(fixture["a_team"].lower()) in fpl_team_names] + player_B_fixtures = [fixture for fixture in player_B["understat_history"] + if to_fpl_team(fixture["h_team"].lower()) in fpl_team_names or + to_fpl_team(fixture["a_team"].lower()) in fpl_team_names] if not number_of_fixtures: number_of_fixtures = max(len(player_A_fixtures), len(player_B_fixtures)) - fixture_count = 0 - relevant_fixtures = [] - for fixtures in zip(player_A_fixtures, player_B_fixtures): - fixture_A = fixtures[0] - fixture_B = fixtures[1] + print(number_of_fixtures) + + fixtures = zip(player_A_fixtures[:number_of_fixtures], + player_B_fixtures[:number_of_fixtures]) - print(fixture_A, fixture_B) + player_vs_player_table(fixtures) def versus_team_handler(self, player_name, team_name, number_of_fixtures): """Function for handling player vs. team comment.""" @@ -192,7 +196,7 @@ async def main(config): async with aiohttp.ClientSession() as session: fpl_bot = FPLBot(config, session) - fpl_bot.versus_player_handler("pogba", "son", None) + fpl_bot.versus_player_handler("pogba", "mohamed salah", 5) if __name__ == "__main__": From 47696adadc9e05ee4bda6188211b5359eac61f01 Mon Sep 17 00:00:00 2001 From: amosbastian Date: Tue, 19 Feb 2019 15:53:23 +0100 Subject: [PATCH 11/19] Add init.py & create_text_indexes() in utils.py * running init.py will create the database with text indexes * create_text_indexes() is the function that actually creates them Resolves: #3 --- FPLbot/init.py | 21 +++++++++++++++++++++ FPLbot/utils.py | 9 +++++++++ 2 files changed, 30 insertions(+) create mode 100644 FPLbot/init.py diff --git a/FPLbot/init.py b/FPLbot/init.py new file mode 100644 index 0000000..462417f --- /dev/null +++ b/FPLbot/init.py @@ -0,0 +1,21 @@ +import asyncio + +from pymongo import MongoClient + +from utils import update_players + +client = MongoClient() +database = client.fpl + + +async def main(): + await update_players() + + +if __name__ == "__main__": + try: + asyncio.run(main()) + except AttributeError: + loop = asyncio.get_event_loop() + loop.run_until_complete(main()) + loop.close() diff --git a/FPLbot/utils.py b/FPLbot/utils.py index 9f1d146..70ac489 100644 --- a/FPLbot/utils.py +++ b/FPLbot/utils.py @@ -125,6 +125,14 @@ async def get_understat_players(): return players +def create_text_indexes(): + database.players.create_index([ + ("web_name", "text"), + ("first_name", "text"), + ("second_name", "text") + ]) + + async def update_players(): """Updates all players in the database.""" logger.info("Updating FPL players in database.") @@ -137,6 +145,7 @@ async def update_players(): requests = [ReplaceOne({"id": player["id"]}, player, upsert=True) for player in players] database.players.bulk_write(requests) + create_text_indexes() logger.info("Adding Understat data to players in database.") understat_players = await get_understat_players() From 6533a7fbfefd20f07db0d0f7378009614115442d Mon Sep 17 00:00:00 2001 From: amosbastian Date: Tue, 19 Feb 2019 16:36:02 +0100 Subject: [PATCH 12/19] Update regex for diacritics --- FPLbot/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FPLbot/constants.py b/FPLbot/constants.py index cfa0e5b..84d3048 100644 --- a/FPLbot/constants.py +++ b/FPLbot/constants.py @@ -78,7 +78,7 @@ "understat_history" ] -versus_pattern = re.compile(r"!fplbot\s+([^\W\d]+(?:[\s-][^\W\d]+)*)\s+(?:vs.|vs)\s+([a-zA-Z ]+)(\d+)?") +versus_pattern = re.compile(r"!fplbot\s+([A-zÀ-ÿ]+(?:[\s-][A-zÀ-ÿ]+)*)\s+(?:vs.|vs)\s+([A-zÀ-ÿ]+(?:[\s-][A-zÀ-ÿ]+)*)\s*(\d+)?") to_fpl_team_dict = { "arsenal fc": "arsenal", From 48c133ec2d0639588b2a37f51f15c1a314034895 Mon Sep 17 00:00:00 2001 From: amosbastian Date: Tue, 19 Feb 2019 16:36:21 +0100 Subject: [PATCH 13/19] Add get_relevant_fixtures & update player_vs_player_table --- FPLbot/utils.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/FPLbot/utils.py b/FPLbot/utils.py index 70ac489..da7d7e6 100644 --- a/FPLbot/utils.py +++ b/FPLbot/utils.py @@ -11,8 +11,8 @@ from fpl.utils import position_converter, team_converter from pymongo import MongoClient, ReplaceOne -from constants import (desired_attributes, player_dict, team_dict, - to_fpl_team_dict) +from constants import (desired_attributes, fpl_team_names, player_dict, + team_dict, to_fpl_team_dict) client = MongoClient() database = client.fpl @@ -209,12 +209,15 @@ def player_vs_player_table(fixtures): fixture_A = fixture[0] fixture_B = fixture[1] + minutes_played_A = fixture_A["time"] + minutes_played_B = fixture_B["time"] + # Highlight whether the player was a starter or not if fixture_A["position"].lower() != "sub": - minutes_played_A = f"**{fixture_A['time']}**" + minutes_played_A = f"**{minutes_played_A}**" if fixture_B["position"].lower() != "sub": - minutes_played_B = f"**{fixture_B['time']}**" + minutes_played_B = f"**{minutes_played_B}**" table += ( f"|{float(fixture_A['xA']):.2f}" @@ -248,7 +251,7 @@ def player_vs_player_table(fixtures): f"|**{total_B['xA']:.2f}**|\n" ) - print(table + table_footer) + return table + table_footer def player_vs_team_table(fixtures): @@ -347,6 +350,13 @@ def understat_team_converter(team_name): return team_name +def get_relevant_fixtures(player): + return [fixture for fixture in player["understat_history"] + if (to_fpl_team(fixture["h_team"].lower()) in fpl_team_names or + to_fpl_team(fixture["a_team"].lower()) in fpl_team_names) and + int(fixture["time"]) > 0] + + if __name__ == "__main__": try: asyncio.run(update_players()) From 76fa0215a8c494c6916414b4d383642c0fa81be9 Mon Sep 17 00:00:00 2001 From: amosbastian Date: Tue, 19 Feb 2019 16:39:09 +0100 Subject: [PATCH 14/19] Update docstring get_relevant_fixtures --- FPLbot/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/FPLbot/utils.py b/FPLbot/utils.py index da7d7e6..75940b0 100644 --- a/FPLbot/utils.py +++ b/FPLbot/utils.py @@ -351,6 +351,7 @@ def understat_team_converter(team_name): def get_relevant_fixtures(player): + """Return all fixtures that the player has played for his current team.""" return [fixture for fixture in player["understat_history"] if (to_fpl_team(fixture["h_team"].lower()) in fpl_team_names or to_fpl_team(fixture["a_team"].lower()) in fpl_team_names) and From d790403423101d579eb96c1fd313c8c618e0b935 Mon Sep 17 00:00:00 2001 From: amosbastian Date: Tue, 19 Feb 2019 16:39:32 +0100 Subject: [PATCH 15/19] Update player_vs_player_handler --- FPLbot/bot.py | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/FPLbot/bot.py b/FPLbot/bot.py index 3122520..24e75d8 100644 --- a/FPLbot/bot.py +++ b/FPLbot/bot.py @@ -14,7 +14,7 @@ from constants import fpl_team_names, versus_pattern from utils import (create_logger, find_player, get_player_table, player_vs_player_table, player_vs_team_table, to_fpl_team, - update_players) + update_players, get_relevant_fixtures) dirname = os.path.dirname(os.path.realpath(__file__)) logger = create_logger() @@ -92,22 +92,27 @@ def versus_player_handler(self, player_A_name, player_B_name, if not player_A or not player_B: return - player_A_fixtures = [fixture for fixture in player_A["understat_history"] - if to_fpl_team(fixture["h_team"].lower()) in fpl_team_names or - to_fpl_team(fixture["a_team"].lower()) in fpl_team_names] - player_B_fixtures = [fixture for fixture in player_B["understat_history"] - if to_fpl_team(fixture["h_team"].lower()) in fpl_team_names or - to_fpl_team(fixture["a_team"].lower()) in fpl_team_names] + player_A_fixtures = get_relevant_fixtures(player_A) + player_B_fixtures = get_relevant_fixtures(player_B) if not number_of_fixtures: - number_of_fixtures = max(len(player_A_fixtures), len(player_B_fixtures)) - - print(number_of_fixtures) + number_of_fixtures = max(len(player_A_fixtures), + len(player_B_fixtures)) fixtures = zip(player_A_fixtures[:number_of_fixtures], player_B_fixtures[:number_of_fixtures]) - player_vs_player_table(fixtures) + post_template = open(f"{dirname}/../comment_template.md").read() + table_header = ( + f"# {player_A['web_name']} (£{player_A['now_cost'] / 10.0:.1f}) " + f"vs. {player_B['web_name']} (£{player_B['now_cost'] / 10.0:.1f}) " + f"in their last {number_of_fixtures} fixtures") + table_body = player_vs_player_table(fixtures) + + return post_template.format( + comment_header=table_header, + comment_body=table_body + ) def versus_team_handler(self, player_name, team_name, number_of_fixtures): """Function for handling player vs. team comment.""" @@ -162,11 +167,15 @@ def comment_handler(self, comment): opponent_name = match.group(2).lower().replace(".", "").strip() number = match.group(3) + if number: + number = int(number) + if to_fpl_team(opponent_name) in fpl_team_names: reply_text = self.versus_team_handler( player_name, opponent_name, number) else: - return + reply_text = self.versus_player_handler( + player_name, opponent_name, number) if reply_text: logger.info(f"Replying ({player_name} vs. {opponent_name}) to " @@ -196,7 +205,7 @@ async def main(config): async with aiohttp.ClientSession() as session: fpl_bot = FPLBot(config, session) - fpl_bot.versus_player_handler("pogba", "mohamed salah", 5) + fpl_bot.run() if __name__ == "__main__": From e01ef3f1190ef14fb599f254b282da9045d8cb25 Mon Sep 17 00:00:00 2001 From: amosbastian Date: Tue, 19 Feb 2019 16:56:38 +0100 Subject: [PATCH 16/19] Update get_relevant_fixtures() Now can be used to get the relevant fixtures vs. a specific team. --- FPLbot/utils.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/FPLbot/utils.py b/FPLbot/utils.py index 75940b0..fd3dc61 100644 --- a/FPLbot/utils.py +++ b/FPLbot/utils.py @@ -310,7 +310,6 @@ def player_vs_team_table(fixtures): f"|**{int(total['key_passes'])}**|\n" ) - print(table + table_footer) return table + table_footer @@ -350,12 +349,24 @@ def understat_team_converter(team_name): return team_name -def get_relevant_fixtures(player): +def get_relevant_fixtures(player, team_name=None): """Return all fixtures that the player has played for his current team.""" - return [fixture for fixture in player["understat_history"] - if (to_fpl_team(fixture["h_team"].lower()) in fpl_team_names or + fixtures = [ + fixture for fixture in player["understat_history"] + if (to_fpl_team(fixture["h_team"].lower()) in fpl_team_names or to_fpl_team(fixture["a_team"].lower()) in fpl_team_names) and - int(fixture["time"]) > 0] + int(fixture["time"]) > 0 + ] + print(fixtures) + if team_name: + fixtures = [ + fixture for fixture in fixtures + if team_name == fixture["h_team"].lower() or + team_name == fixture["a_team"].lower() + ] + print(fixtures) + + return fixtures if __name__ == "__main__": From 08f76972b1f7b9533a0bba06945a5cfcb838d96b Mon Sep 17 00:00:00 2001 From: amosbastian Date: Tue, 19 Feb 2019 16:57:52 +0100 Subject: [PATCH 17/19] Update versus_team_handler() to use get_relevant_fixtures() --- FPLbot/bot.py | 24 +++++++----------------- FPLbot/utils.py | 4 +++- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/FPLbot/bot.py b/FPLbot/bot.py index 24e75d8..7d07ec2 100644 --- a/FPLbot/bot.py +++ b/FPLbot/bot.py @@ -106,7 +106,7 @@ def versus_player_handler(self, player_A_name, player_B_name, table_header = ( f"# {player_A['web_name']} (£{player_A['now_cost'] / 10.0:.1f}) " f"vs. {player_B['web_name']} (£{player_B['now_cost'] / 10.0:.1f}) " - f"in their last {number_of_fixtures} fixtures") + f"(last {number_of_fixtures} fixtures)") table_body = player_vs_player_table(fixtures) return post_template.format( @@ -123,23 +123,13 @@ def versus_team_handler(self, player_name, team_name, number_of_fixtures): if not number_of_fixtures: number_of_fixtures = len(player["understat_history"]) - fixture_count = 0 - relevant_fixtures = [] - team_name = to_fpl_team(team_name) - for fixture in player["understat_history"]: - if fixture_count >= int(number_of_fixtures): - break - - if (team_name != fixture["h_team"].lower() and - team_name != fixture["a_team"].lower()): - continue - - fixture_count += 1 - relevant_fixtures.append(fixture) - + fixtures = get_relevant_fixtures( + player, team_name=to_fpl_team(team_name))[:number_of_fixtures] post_template = open(f"{dirname}/../comment_template.md").read() - table_header = f"# {player_name.title()} vs. {team_name.title()}\n" - table_body = player_vs_team_table(relevant_fixtures) + table_header = ( + f"# {player_name.title()} vs. {team_name.title()} (last " + f"{len(fixtures)} fixtures)") + table_body = player_vs_team_table(fixtures) return post_template.format( comment_header=table_header, diff --git a/FPLbot/utils.py b/FPLbot/utils.py index fd3dc61..79416df 100644 --- a/FPLbot/utils.py +++ b/FPLbot/utils.py @@ -350,7 +350,9 @@ def understat_team_converter(team_name): def get_relevant_fixtures(player, team_name=None): - """Return all fixtures that the player has played for his current team.""" + """Return all fixtures that the player has played for his current team + (optionally) against the given team. + """ fixtures = [ fixture for fixture in player["understat_history"] if (to_fpl_team(fixture["h_team"].lower()) in fpl_team_names or From d888b8e40060a7fd101b5a073f177a4d4fcc8c53 Mon Sep 17 00:00:00 2001 From: amosbastian Date: Tue, 19 Feb 2019 18:23:57 +0100 Subject: [PATCH 18/19] Add price_changes.py --- FPLbot/price_changes.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 FPLbot/price_changes.py diff --git a/FPLbot/price_changes.py b/FPLbot/price_changes.py new file mode 100644 index 0000000..c326a91 --- /dev/null +++ b/FPLbot/price_changes.py @@ -0,0 +1,25 @@ +import asyncio +import json +import os + +import aiohttp + +from bot import FPLBot + +dirname = os.path.dirname(os.path.realpath(__file__)) + +async def main(config): + async with aiohttp.ClientSession() as session: + fpl_bot = FPLBot(config, session) + + await fpl_bot.post_price_changes() + + +if __name__ == "__main__": + config = json.loads(open(f"{dirname}/../config.json").read()) + try: + asyncio.run(main(config)) + except AttributeError: + loop = asyncio.get_event_loop() + loop.run_until_complete(main(config)) + loop.close() From c0a18372cbad18debe3a4cdaa595051c62948de1 Mon Sep 17 00:00:00 2001 From: amosbastian Date: Tue, 19 Feb 2019 18:47:18 +0100 Subject: [PATCH 19/19] Update README.md --- FPLbot/utils.py | 3 +-- README.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/FPLbot/utils.py b/FPLbot/utils.py index 79416df..f13fd43 100644 --- a/FPLbot/utils.py +++ b/FPLbot/utils.py @@ -359,14 +359,13 @@ def get_relevant_fixtures(player, team_name=None): to_fpl_team(fixture["a_team"].lower()) in fpl_team_names) and int(fixture["time"]) > 0 ] - print(fixtures) + if team_name: fixtures = [ fixture for fixture in fixtures if team_name == fixture["h_team"].lower() or team_name == fixture["a_team"].lower() ] - print(fixtures) return fixtures diff --git a/README.md b/README.md index 73734f5..e2fbd30 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ configuration file. Its current features are: * Posting the price changes of Fantasy Premier League players +* Comparing the performance of a player vs. a team +* Comparing the performance of a palyer vs. another player ## Installation @@ -19,11 +21,58 @@ Python 3.6+. cd FPLbot pip install -r requirements.txt -To initialise the database you should do the following: +To initialise the database with text indexes you should do the following: - python FPLbot/utils.py + python FPLbot/init.py -Once this has been done, you can schedule a cron job to run the bot whenever you want! +Once this has been done, you should create your own `config.json` with the correct values (see [configuration](#configuration)). +With this filled in, you can run the bot using + + python FPLbot/bot.py + +As for the price changes, you should schedule a cron job, like this for example: + + 25 1 * * * /home/amos/FPLbot/venv/bin/python /home/amos/FPLbot/FPLbot/price_changes.py + +## Usage + +The bot can be called on [/r/FantasyPL](https://www.reddit.com/r/FantasyPL/) using the following two commands: + +1. `!fplbot vs. ` +2. `!fplbot vs. ` + +The bot uses text indexes to search for the player(s) and using a manually created mapping (so you don't have to use e.g. "man utd" exactly, but other variations are fine as well, like "man u" or "manchester united"). The number of fixtures is completely optional, and if not specified, it simply uses *all* fixtures that are considered relevant. Here are two examples: + +1. + + !fplbot heung-min son vs. mane 5 + +### Son (£8.9) vs. Mané (£9.6) (last 5 fixtures) + +|xA|A|xG|G|MP|Fixture|Fixture|MP|G|xG|A|xA| +|-:|-:|-:|-:|-:|:-|-:|-:|-:|-:|-:|-:| +|0.13|0|0.42|1|**90**|Spurs 3-1 Leicester|Liverpool 3-0 Bournemouth|**89**|1|0.58|0|0.12| +|0.70|0|0.19|1|**90**|Spurs 1-0 Newcastle United|West Ham 1-1 Liverpool|**90**|1|0.64|0|0.00| +|0.00|0|0.20|1|**90**|Spurs 2-1 Watford|Liverpool 1-1 Leicester|**90**|1|0.18|0|0.10| +|0.38|0|0.05|0|**90**|Spurs 0-1 Man Utd|Liverpool 4-3 Crystal Palace|**90**|1|0.47|0|0.01| +|0.08|1|0.50|1|**77**|Cardiff 0-3 Spurs|Brighton 0-1 Liverpool|**90**|0|0.10|0|0.10| +|**1.29**|**1**|**1.36**|**4**|**437**|||**449**|**4**|**1.97**|**0**|**0.34**| + +--- + +2. + + !fplbot rashford vs. liverpool + +### Rashford vs. Liverpool (last 4 fixtures) + +|Fixture|Date|MP|G|xG|A|xA|NPG|NPxG|KP| +|:-|:-|-:|-:|-:|-:|-:|-:|-:|-:| +|**Liverpool** 3-1 Man Utd|2018-12-16|**90**|0|0.02|0|0.00|0|0.02|0| +|**Man Utd** 2-1 Liverpool|2018-03-10|**72**|2|0.17|0|0.00|2|0.17|0| +|Liverpool 0-0 Man Utd|2017-10-14|24|0|0.00|0|0.00|0|0.00|0| +|Liverpool 0-0 Man Utd|2016-10-17|**78**|0|0.00|0|0.00|0|0.00|0| +|||**264**|**2**|**0.19**|**0**|**0.00**|**2.0**|**0.19**|**0**| ## Configuration @@ -35,5 +84,6 @@ Once this has been done, you can schedule a cron job to run the bot whenever you |CLIENT_SECRET|The bot's client secret| |USER_AGENT|A unique identifier that helps Reddit determine the source of network requests| |SUBREDDIT|The subreddit the bot will post to| +|BOT_PREFIX|The prefix used to call the bot, e.g.: "!fplbot"| For more information about how to set up a bot see [Reddit's guide](https://github.com/reddit-archive/reddit/wiki/OAuth2-Quick-Start-Example#first-steps).