Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates to PlaybyPlay Regex. Added Integration Tests to Validate Game PlaybyPlay Data #70

Merged
merged 1 commit into from
Apr 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ flake8-steps: &steps
- run: python -m flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
#- run: pytest
- run: pip install --user pytest
- run: python -m pytest tests/stats/library/playbyplay/test_playbyplayregex.py
- run: python -m pytest tests/stats/library/playbyplay/test_play_by_play_regex.py

jobs:
Python34:
Expand Down
32 changes: 25 additions & 7 deletions nba_api/stats/library/playbyplayregex.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,37 @@
import re
from collections import defaultdict
from nba_api.stats.library.eventmsgtype import EventMsgType

# regex patterns for all playbyplay and playbyplayv2 HOMEDESCRIPTION & VISITORDESCRIPTION fields
pattern_player_char = r'((?#char)(\. \w+)|(\-?\'?\w+))?'
pattern_player_suffix = r'((?#suffix)([\s](Jr\.|III|II|IV)))?'
pattern_referee = r'(?P<referee>.*)'
pattern_team = r'(?P<team>\w+( \w+)?)'
pattern_field_goal_type = r'(?P<field_goal_type>[\w+( |\-)]*)'
pattern_free_throw_type = r'(?P<free_throw_type>(\w+ )?(\d of \d)|\w+)'
pattern_free_throw_type = r'(?P<free_throw_type>(.* )?(\d of \d)|\w+)'
pattern_player = r"(?P<player>\w+{player_char}{player_suffix})".format(player_char=pattern_player_char,player_suffix=pattern_player_suffix)
pattern_points = r"\((?P<points>\d+) PTS\)"
pattern_rebound_team = r'^{team} Rebound$'.format(team=pattern_team)
pattern_turnover_team = r'^{team} Turnover: (?P<turnover_type>\w+\s?\w+) \(T#(?P<turnovers>\d.)\)$'.format(team=pattern_team)
pattern_turnover_team = r'^{team} Turnover: (?P<turnover_type>.*) \(T#(?P<turnovers>\d+)\)$'.format(team=pattern_team)
pattern_timeout = r'^{team} Timeout: ((?P<timeout_type>\w+)) \(\w+ (?P<full>\d+) \w+ (?P<short>\d+)\)$'.format(team=pattern_team)
pattern_block = r"^{player} BLOCK \((?P<blocks>\d+) BLK\)$".format(player=pattern_player)
pattern_ejection = r'^{player} Ejection:(?P<ejection_type>.*)$'.format(player=pattern_player)
pattern_field_goal_made = r"^{player}\s{{1,2}}?((?P<distance>\d+)\' )?{field_goal_type} {points}( \({player_ast} (?P<assists>\d+) AST\))?$".format(
player=pattern_player,player_ast=pattern_player.replace('player','player_ast'), field_goal_type=pattern_field_goal_type,points=pattern_points)
pattern_field_goal_missed = r"^MISS {player}\s{{1,2}}?((?P<distance>\d+)\' )?{field_goal_type}$".format(player=pattern_player,field_goal_type=pattern_field_goal_type)
pattern_foul = r"^{player} (?P<foul_type>.*)\s(?=(\(\w+(?P<personal>\d+)(\.\w+(?P<team>[\d|\w]+))?\) (\((?P<referee>\w.\w+)\)))$)".format(player=pattern_player)
pattern_foul_player = r"^{player} (?P<foul_type>.*)\s(?=(\(\w+(?P<personal>\d+)(\.\w+(?P<team>[\d|\w]+))?\) (\({referee}\)))$)".format(player=pattern_player, referee=pattern_referee)
pattern_foul_team = r"^{team} T.Foul \((?P<foul_type>.*) {player} \) (\({referee}\))".format(player=pattern_player, team=pattern_team, referee=pattern_referee)
pattern_free_throw_made = r"^{player} Free Throw {free_throw_type} {points}$".format(player=pattern_player,free_throw_type=pattern_free_throw_type,points=pattern_points)
pattern_free_throw_miss = r"^MISS {player} Free Throw {free_throw_type}$".format(player=pattern_player,free_throw_type=pattern_free_throw_type)
pattern_jump_ball = r"^Jump Ball {player_home} vs. {player_away}: Tip to {player_tip}$".format(player_home=pattern_player.replace('player','player_home'),
pattern_jump_ball = r"^Jump Ball {player_home} vs. {player_away}: Tip to( {player_tip})?$".format(player_home=pattern_player.replace('player','player_home'),
player_away=pattern_player.replace('player','player_away'),
player_tip=pattern_player.replace('player','player_tip'))
pattern_rebound_player = r"^{player} REBOUND \(Off:(?P<offensive>\d+) Def:(?P<defensive>\d+)\)$".format(player=pattern_player)
pattern_steal = r"^{player}\s{{1,2}}?STEAL \((?P<steals>\d+) STL\)$".format(player=pattern_player)
pattern_substitution = r"^SUB: {player_in} FOR {player_out}$".format(
player_in=pattern_player.replace('player','player_in'),player_out=pattern_player.replace('player','player_out'))
pattern_turnover_player = r"^{player}\s{{1,2}}?(?P<turnover_type>[\w+?\-? ]*) (Turnover[ ]?)+ \(P(?P<personal>\d+)\.T(?P<team>\d+)\)$".format(player=pattern_player)
pattern_violation = r"^{player} Violation:(?P<violation_type>\w+\s?\w+)\s\((?P<referee>\w.\w+)\)$".format(player=pattern_player)
pattern_violation = r"^{player} Violation:(?P<violation_type>\w+\s?\w+)\s\({referee}\)$".format(player=pattern_player, referee=pattern_referee)

# compiled regex for all playbyplay and playbyplayv2 HOMEDESCRIPTION & VISITORDESCRIPTION fields
re_block = re.compile(pattern_block)
Expand All @@ -36,7 +40,8 @@
re_field_goal_missed = re.compile(pattern_field_goal_missed)
re_free_throw_made = re.compile(pattern_free_throw_made)
re_free_throw_miss = re.compile(pattern_free_throw_miss)
re_foul = re.compile(pattern_foul)
re_foul_player = re.compile(pattern_foul_player)
re_foul_team = re.compile(pattern_foul_team)
re_rebound_player = re.compile(pattern_rebound_player)
re_rebound_team = re.compile(pattern_rebound_team)
re_turnover_player = re.compile(pattern_turnover_player)
Expand All @@ -45,4 +50,17 @@
re_substitution = re.compile(pattern_substitution)
re_timeout = re.compile(pattern_timeout)
re_violation = re.compile(pattern_violation)
re_jump_ball = re.compile(pattern_jump_ball)
re_jump_ball = re.compile(pattern_jump_ball)

eventmsgtype_to_re = defaultdict(list, {
EventMsgType.EJECTION : [re_ejection],
EventMsgType.FIELD_GOAL_MADE : [re_field_goal_made],
EventMsgType.FIELD_GOAL_MISSED : [re_field_goal_missed, re_block],
EventMsgType.FREE_THROW : [re_free_throw_made, re_free_throw_miss],
EventMsgType.REBOUND : [re_rebound_player, re_rebound_team],
EventMsgType.TURNOVER : [re_turnover_player, re_steal, re_turnover_team],
EventMsgType.FOUL : [re_foul_player, re_foul_team],
EventMsgType.VIOLATION : [re_violation],
EventMsgType.SUBSTITUTION : [re_substitution],
EventMsgType.TIMEOUT : [re_timeout],
EventMsgType.JUMP_BALL : [re_jump_ball]})
91 changes: 0 additions & 91 deletions tests/stats/library/playbyplay/conftest.py

This file was deleted.

105 changes: 105 additions & 0 deletions tests/stats/library/playbyplay/play_by_play_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import pytest
from collections import defaultdict
from nba_api.stats.library.eventmsgtype import EventMsgType

playbyplay = defaultdict(list)

#Block
playbyplay["Block"].append({"description" : "Collins BLOCK (1 BLK)", "player" : "Collins", "blocks" : "1"})

#Ejection
playbyplay["Ejection"].append({"description" : "Robinson Ejection:Second Technical", "player" : "Robinson", "ejection_type" : "Second Technical"})

#Field Goal Made (With Assist)
playbyplay["FieldGoalMade"].append({"description" : "S. Hill 24' 3PT Jump Shot (3 PTS) (Mahinmi 1 AST)", "player" : "S. Hill", "distance" : "24", "field_goal_type" : "3PT Jump Shot", "points" : "3", "player_ast" : "Mahinmi", "assists" : "1"})

#Field Goal Made (Without Assist)
playbyplay["FieldGoalMade"].append({"description" : "Aldridge 6' Turnaround Hook Shot (8 PTS)", "player" : "Aldridge", "distance" : "6", "field_goal_type" : "Turnaround Hook Shot", "points" : "8", "player_ast" : None, "assists" : None})

#Field Goal Made (Without Distance)
playbyplay["FieldGoalMade"].append({"description" : "Broekhoff 3PT Jump Shot (3 PTS) (Lee 2 AST)", "player" : "Broekhoff", "distance" : None, "field_goal_type" : "3PT Jump Shot", "points" : "3", "player_ast" : "Lee", "assists" : "2"})

#Field Goal Missed
playbyplay["FieldGoalMissed"].append({"description" : "MISS O'Quinn 17' Jump Shot", "player" : "O'Quinn", "distance" : "17", "field_goal_type" : "Jump Shot"})

#Foul Player
playbyplay["FoulPlayer"].append({"description" : "Collison P.FOUL (P1.TN) (M.Lindsay)", "player" : "Collison", "foul_type" : "P.FOUL", "personal" : "1", "team" : "N", "referee" : "M.Lindsay"})

#Foul Player (Long Referee Name)
playbyplay["FoulPlayer"].append({"description" : "Allen P.FOUL (P1.T3) (J.Van Duyne)", "player" : "Allen", "foul_type" : "P.FOUL", "personal" : "1", "team" : "3", "referee" : "J.Van Duyne"})

#Foul Team
playbyplay["FoulTeam"].append({"description" : "BUCKS T.Foul (Def. 3 Sec Lopez ) (M.Callahan)", "team" : "BUCKS", "foul_type" : "Def. 3 Sec", "player" : "Lopez", "referee" : "M.Callahan"})

#Free Throw Made
playbyplay["FreeThrowMade"].append({"description" : "Sumner Free Throw 2 of 2 (1 PTS)", "player" : "Sumner", "free_throw_type" : "2 of 2", "points" : "1"})

#Free Throw Made (Clear Path 1 of 2)
playbyplay["FreeThrowMade"].append({"description" : "Powell Free Throw Clear Path 1 of 2 (18 PTS)", "player" : "Powell", "free_throw_type" : "Clear Path 1 of 2", "points" : "18"})

#Free Throw Missed
playbyplay["FreeThrowMissed"].append({"description" : "MISS Prince Free Throw 1 of 2", "player" : "Prince", "free_throw_type" : "1 of 2"})

#Jump Ball
playbyplay["JumpBall"].append({"description" : "Jump Ball Collins vs. O'Quinn: Tip to Leaf", "player_home" : "Collins", "player_away" : "O'Quinn", "player_tip" : "Leaf"})

#Jump Ball (Without player_tip)
playbyplay["JumpBall"].append({"description" : "Jump Ball Green vs. McKinnie: Tip to", "player_home" : "Green", "player_away" : "McKinnie", "player_tip" : None})

#Player (Single Name)
playbyplay["Player"].append({"description" : "Millsap 25' 3PT Jump Shot (9 PTS) (Teague 1 AST)", "player" : "Millsap"})

#Player (Hyphenated Name)
playbyplay["Player"].append({"description" : "Bates-Diop P.FOUL (P1.T4) (M.Boland)", "player" : "Bates-Diop"})

#Player (Apostrophe)
playbyplay["Player"].append({"description" : "O'Quinn 20' Jump Shot (2 PTS) (Oladipo 1 AST)", "player" : "O'Quinn"})

#Player (First Initial dot Last Name)
playbyplay["Player"].append({"description" : "S. Hill 24' 3PT Jump Shot (3 PTS) (Mahinmi 1 AST)", "player" : "S. Hill"})

#Player (Junior)
playbyplay["Player"].append({"description" : "Porter Jr. 10' Driving Floating Jump Shot (2 PTS)", "player" : "Porter Jr."})

#Player (Second)
playbyplay["Player"].append({"description" : "Payton II 2' Driving Reverse Layup (2 PTS) (Middleton 6 AST)", "player" : "Payton II"})

#Player (Third)
playbyplay["Player"].append({"description" : "Robinson III Free Throw 1 of 1 (3 PTS)", "player" : "Robinson III"})

#Player (Fourth)
playbyplay["Player"].append({"description" : "Walker IV REBOUND (Off:0 Def:1)", "player" : "Walker IV"})

#Rebound Player
playbyplay["ReboundPlayer"].append({"description" : "Zubac REBOUND (Off:2 Def:4)", "player" : "Zubac", "offensive" : "2", "defensive" : "4"})

#Rebound Team (One Word)
playbyplay["ReboundTeam"].append({"description" : "Timberwolves Rebound", "team" : "Timberwolves"})

#Rebound Team (Two Words)
playbyplay["ReboundTeam"].append({"description" : "TRAIL BLAZERS Rebound", "team" : "TRAIL BLAZERS"})

#Steal
playbyplay["Steal"].append({"description" : "Bradley STEAL (2 STL)", "player" : "Bradley", "steals" : "2"})

#Substitution
playbyplay["Substitution"].append({"description" : "SUB: Sefolosha FOR Ingles", "player_in" : "Sefolosha", "player_out" : "Ingles"})

#Timeout
playbyplay["Timeout"].append({"description" : "TRAIL BLAZERS Timeout: Regular (Full 5 Short 0)", "team" : "TRAIL BLAZERS", "timeout_type" : "Regular", "full" : "5", "short" : "0"})

#Turnover Player
playbyplay["TurnoverPlayer"].append({"description" : "G. Harrison Double Dribble Turnover (P1.T10)", "player" : "G. Harrison", "turnover_type" : "Double Dribble", "personal" : "1", "team" : "10"})

#Turnover Team (less than 10)
playbyplay["TurnoverTeam"].append({"description" : "NUGGETS Turnover: Shot Clock (T#12)", "team" : "NUGGETS", "turnover_type" : "Shot Clock", "turnovers" : "12"})

#Turnover Team (greater than 9)
playbyplay["TurnoverTeam"].append({"description" : "NETS Turnover: Shot Clock (T#6)", "team" : "NETS", "turnover_type" : "Shot Clock", "turnovers" : "6"})

#Turnover Team (turnover_type starts with digit)
playbyplay["TurnoverTeam"].append({"description" : "WIZARDS Turnover: 5 Second Violation (T#12)", "team" : "WIZARDS", "turnover_type" : "5 Second Violation", "turnovers" : "12"})

#Violation
playbyplay["Violation"].append({"description" : "Dieng Violation:Kicked Ball (T.Brown)", "player" : "Dieng", "violation_type" : "Kicked Ball", "referee" : "T.Brown"})

Loading