Repository: zero-sum-seattle/python-mlb-statsapi

Issue:
Processing game_pk information: PlayDetails.__init__() got an unexpected keyword argument 'violation'
Note: I'm using the latest version of: 
python-mlb-statsapi 0.5.16
MLB-StatsAPI        1.7.1

This function broke after the topperformers class update 2 weeks ago:

For some game_pk's the information returns as expected, but for a few(errors below) I'm receiving a PlayDetails.__init__() error, and I can't find that class in the repository:
PlayDetails.__init__() got an unexpected keyword argument 'violation'


**Code:**
def get_game_info(game_pk):
    mlb = mlbstatsapi.Mlb()
    try:
        game = mlb.get_game(game_pk)
        venue_id = game.gamedata.venue.id
        venue_name = game.gamedata.venue.name
        venue_lat = game.gamedata.venue.location.defaultcoordinates.latitude
        venue_long = game.gamedata.venue.location.defaultcoordinates.longitude
        venue_elevation = game.gamedata.venue.location.elevation
        venue_surface = game.gamedata.venue.fieldinfo.turftype
        return venue_id, venue_name, venue_lat, venue_long, venue_elevation, venue_surface
    except Exception as e:
        print(f"Error processing game_pk {game_pk}: {e}")   
        return "None", "None", "None", "None", "None", "None"

**ERROR(For example processing 2023-06-03 game_pk's):**
717885
Error processing game_pk 717885: PlayDetails.__init__() got an unexpected keyword argument 'violation'

Hints from issue comments:
@KCNilssen: Thanks for the heads up! Looks like there is another update to the mlb api and we will need to add that field to game details@mustseetv314: > Thanks for the heads up! Looks like there is another update to the mlb api and we will need to add that field to game details

All good, appreciate it. Use this all the time, so kudos to ya'll. Great stuff!@mustseetv314: > Thanks for the heads up! Looks like there is another update to the mlb api and we will need to add that field to game details

Hey KC! Any ETA on when ya'll will make this code fix? Thanks!@mustseetv314: @KCNilssen any ETA for the fix here? Thanks!@Mattsface: I'm sorry I'll go ahead and fix this now@Mattsface: Can you share your code?

I have no issues with gamepk 717885 @mustseetv314 @mustseetv314: sure @Mattsface ill run it again and post code@mustseetv314: @Mattsface  @KCNilssen 

Code snipet plus 2 .csv attached:
Input = mlb_games.csv
output = mlb_games_ready.csv
--As you can see, it grabs the venue name, lat, long, elevation, surface for some...but errors with for some...PlayDetails.__init()__
![image](https://github.com/zero-sum-seattle/python-mlb-statsapi/assets/160516675/f44b728f-0771-4fde-9ea9-12e4dc48defe)

[mlb_games.csv](https://github.com/zero-sum-seattle/python-mlb-statsapi/files/14748785/mlb_games.csv)
[mlb_games_ready.csv](https://github.com/zero-sum-seattle/python-mlb-statsapi/files/14748786/mlb_games_ready.csv)

pip list
![image](https://github.com/zero-sum-seattle/python-mlb-statsapi/assets/160516675/ffdb7bf2-a432-4510-9ac2-69b7c61444fa)


#Add Game Venue and Location Detail
csv_file = 'mlb_games.csv'
df = pd.read_csv(csv_file)

# Initialize new columns
df['venue_id'] = None
df['venue_name'] = None
df['venue_latitude'] = None
df['venue_longitude'] = None
df['venue_elevation'] = None
df['venue_surface'] = None

for index, row in df.iterrows():
    game_pk = row['GamePk']
    print(game_pk)
    try:
        venue_id, venue_name, venue_lat, venue_long, venue_elevation, venue_surface = get_game_info(game_pk)
        df.at[index, 'venue_id'] = venue_id
        df.at[index, 'venue_name'] = venue_name
        df.at[index, 'venue_latitude'] = venue_lat
        df.at[index, 'venue_longitude'] = venue_long
        df.at[index, 'venue_elevation'] = venue_elevation
        df.at[index, 'venue_surface'] = venue_surface
    except Exception as e:
        print(f"Error processing game_pk {game_pk}: {e}")

new_column_order = [
    'Date', 'Time', 'GamePk', 'Away Team', 'Home Team', 'venue_id', 'venue_name', 'venue_latitude', 'venue_longitude', 'venue_elevation', 'venue_surface']

# Check if all columns are included in new_column_order
assert set(new_column_order) == set(df.columns), "New column order does not match DataFrame columns"

# Reindex the DataFrame with the new column order
df = df[new_column_order]

# Save the updated DataFrame back to a CSV
updated_csv_file = 'mlb_games_ready.csv'
df.to_csv(updated_csv_file, index=False)
@mustseetv314: Function:

#Function to get weather and venue for a given game_pk
def get_game_info(game_pk):
    mlb = mlbstatsapi.Mlb()
    try:
        game = mlb.get_game(game_pk)
        venue_id = game.gamedata.venue.id
        venue_name = game.gamedata.venue.name
        venue_lat = game.gamedata.venue.location.defaultcoordinates.latitude
        venue_long = game.gamedata.venue.location.defaultcoordinates.longitude
        venue_elevation = game.gamedata.venue.location.elevation
        venue_surface = game.gamedata.venue.fieldinfo.turftype
        return venue_id, venue_name, venue_lat, venue_long, venue_elevation, venue_surface
    except Exception as e:
        print(f"Error processing game_pk {game_pk}: {e}")   
        return "None", "None", "None", "None", "None", "None"@KCNilssen: @mustseetv314 Your issue should be resolved now, let us know If you have any more issues@mustseetv314: @KCNilssen @Mattsface looks good now gents, appreciate it!

Upgraded to: python-mlb-statsapi 0.5.17 and errors are now gone.
![image](https://github.com/zero-sum-seattle/python-mlb-statsapi/assets/160516675/2c940a64-9bb4-442d-bc80-f269b4f0d8f3)


Solution patch:
diff --git a/mlbstatsapi/models/data/data.py b/mlbstatsapi/models/data/data.py
index 10ee65a..dc95183 100644
--- a/mlbstatsapi/models/data/data.py
+++ b/mlbstatsapi/models/data/data.py
@@ -233,6 +233,22 @@ class CodeDesc:
     code: str
     description: Optional[str] = None
 
+@dataclass
+class Violation:
+    """
+    
+    Attributes
+    ----------
+    type : str
+        the type of violation during the play
+    description : str
+        the description of the violation that occured
+    player : player
+        the player that caused the violation
+    """
+    type: Optional[str] = None
+    description: Optional[str] = None
+    player: Optional[dict] = None
 
 @dataclass(repr=False)
 class Count:
@@ -332,12 +348,14 @@ class PlayDetails:
     trailcolor: Optional[str] = None
     fromcatcher: Optional[bool] = None
     disengagementnum: Optional[int] = None
+    violation: Optional[Union[Violation, dict]] = field(default_factory=dict)
     
     def __post_init__(self):
         self.call = CodeDesc(**self.call) if self.call else self.call
         self.batside = CodeDesc(**self.batside) if self.batside else self.batside
         self.pitchhand = CodeDesc(**self.pitchhand) if self.pitchhand else self.pitchhand
         self.type = CodeDesc(**self.type) if self.type else self.type
+        self.violation = Violation(**self.violation) if self.violation else self.violation
 
     def __repr__(self) -> str:
         kws = [f'{key}={value}' for key, value in self.__dict__.items() if value is not None and value]
diff --git a/pyproject.toml b/pyproject.toml
index 6f27aeb..6d211c4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
 
 [project]
 name = "python-mlb-statsapi"
-version = "0.5.16"
+version = "0.5.17"
 
 authors = [
   { name="Matthew Spah", email="spahmatthew@gmail.com" },


Test patch:
diff --git a/tests/external_tests/game/test_game.py b/tests/external_tests/game/test_game.py
index 8a75448..b6125a7 100644
--- a/tests/external_tests/game/test_game.py
+++ b/tests/external_tests/game/test_game.py
@@ -15,14 +15,14 @@ class TestGame(unittest.TestCase):
     @classmethod
     def setUpClass(cls) -> None:
         cls.mlb = Mlb()
-        cls.game = cls.mlb.get_game(662242)
+        cls.game = cls.mlb.get_game(717911)
 
     @classmethod
     def tearDownClass(cls) -> None:
         pass
 
     def test_game_creation(self):
-        self.assertEqual(self.game.id, 662242)
+        self.assertEqual(self.game.id, 717911)
         self.assertIsInstance(self.game, Game)
 
     def test_game_attrs(self):