mlbrecaps 0.0.1__tar.gz → 0.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/PKG-INFO +4 -1
  2. mlbrecaps-0.0.2/mlbrecaps/__main__.py +25 -0
  3. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps/clip.py +32 -27
  4. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps/clips.py +26 -7
  5. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps/data/team-info.csv +1 -1
  6. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps/date_generator.py +3 -1
  7. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps/game.py +32 -20
  8. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps/player.py +20 -13
  9. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps/team.py +8 -4
  10. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps/utils.py +10 -4
  11. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps.egg-info/PKG-INFO +4 -1
  12. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps.egg-info/SOURCES.txt +0 -5
  13. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/setup.py +2 -2
  14. mlbrecaps-0.0.1/mlbrecaps/__main__.py +0 -21
  15. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/LICENSE.txt +0 -0
  16. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/README.md +0 -0
  17. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps/__init__.py +0 -0
  18. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps/date.py +0 -0
  19. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps/date_range.py +0 -0
  20. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps/game_generator.py +0 -0
  21. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps/play.py +0 -0
  22. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps/scripts.py +0 -0
  23. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps.egg-info/dependency_links.txt +0 -0
  24. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps.egg-info/requires.txt +2 -2
  25. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/mlbrecaps.egg-info/top_level.txt +0 -0
  26. {mlbrecaps-0.0.1 → mlbrecaps-0.0.2}/setup.cfg +0 -0
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mlbrecaps
3
- Version: 0.0.1
3
+ Version: 0.0.2
4
4
  Summary: Package that gathers information on given MLB games
5
5
  Home-page: https://github.com/MrRedwing/MLB-Recaps
6
6
  Author: Karsten Larson
7
7
  Author-email: karsten.larson.1@gmail.com
8
8
  License: MIT
9
+ Platform: UNKNOWN
9
10
  Classifier: License :: OSI Approved :: MIT License
10
11
  Classifier: Programming Language :: Python :: 3.11
11
12
  Classifier: Operating System :: OS Independent
@@ -65,3 +66,5 @@ All contributions are welcomed to improve the mlbrecaps package. To contribute s
65
66
  ### License
66
67
 
67
68
  This project is licensed under the MIT License - see the LICENSE file for details.
69
+
70
+
@@ -0,0 +1,25 @@
1
+ from typing import List
2
+ from pathlib import Path
3
+
4
+ from .scripts import get_highlights
5
+
6
+ from .date import Date
7
+ from .team import Team
8
+ from .clips import Clips
9
+
10
+ if __name__ == "__main__":
11
+ # Get teams for the search
12
+ team: Team = Team(input("Input team name: "))
13
+ date: Date = Date(input("Date MM/DD/YYYY: "))
14
+ file_path: Path = Path(input("Downloads filepath: "))
15
+
16
+ game_clips: List[Clips] = get_highlights(team, date)
17
+
18
+ for index, clips in enumerate(game_clips):
19
+ # Download all highlight clips
20
+ print(f"Game {index + 1}: {clips.plays[0].game}")
21
+
22
+ dir_path: Path = file_path / f"{index:03d}"
23
+ dir_path.mkdir(exist_ok=True)
24
+
25
+ clips.download(dir_path, verbose=True)
@@ -1,24 +1,26 @@
1
1
  from bs4 import BeautifulSoup
2
2
 
3
3
  import requests
4
- import json
5
- import subprocess
4
+ from pathlib import Path
6
5
 
7
6
  from .play import Play
8
7
 
8
+
9
9
  class Clip():
10
+ """A wrapper class for Play that allows for plays to be downloaded"""
10
11
 
11
- def __init__(self, play: Play, broadcast_type: str | None=None):
12
+ def __init__(self, play: Play, broadcast_type: str | None = None):
12
13
  if not isinstance(play, Play):
13
14
  raise ValueError("Play must be a Play object")
14
15
 
15
16
  self._play: Play = play
16
17
 
17
- match broadcast_type: # Enforce broad_type types
18
+ match broadcast_type: # Enforce broad_type types
18
19
  case "HOME" | "AWAY" | None:
19
20
  self.broadcast_type: str | None = broadcast_type
20
21
  case _:
21
- raise ValueError("BroadcastType must be None, \"HOME\", or \"AWAY\"")
22
+ raise ValueError(
23
+ "BroadcastType must be None, \"HOME\", or \"AWAY\"")
22
24
 
23
25
  self._clip_url: str = self.__generate()
24
26
 
@@ -33,13 +35,15 @@ class Clip():
33
35
  def play(self) -> Play:
34
36
  return self._play
35
37
 
36
- # gets the url of the clip to be downloaded from the savant clip
37
38
  def __get_url(self, site_url: str) -> str:
39
+ """
40
+ Gets the url of the clip to be downloaded from the savant clip
41
+ """
38
42
  # Get the savant site
39
43
  site: requests.Response = requests.get(site_url)
40
44
 
41
45
  # Find the video element of the savant clip, find the source url of the clip
42
- soup= BeautifulSoup(site.text, features="lxml")
46
+ soup = BeautifulSoup(site.text, features="lxml")
43
47
  video_obj = soup.find("video", id="sporty")
44
48
 
45
49
  if not video_obj:
@@ -51,12 +55,12 @@ class Clip():
51
55
  # Return the source url of the clip so it can be downloaded later
52
56
  return clip_url
53
57
 
54
-
55
- # finds the savant clip based on the given at-bat information
56
- # row must be a pandas dataframe row
57
58
  def __generate(self) -> str:
58
- # load the given game's json file
59
- game_json = self._play.game.game_json
59
+ """
60
+ Generates a savant clip based on the given at-bat information
61
+
62
+ Row must be a pandas dataframe row.
63
+ """
60
64
 
61
65
  # find the broadcast type so it's always corresponding
62
66
  # to the given batter's home team's broadcast
@@ -74,7 +78,7 @@ class Clip():
74
78
  # if the clip is alright return it
75
79
  if clip_url != "":
76
80
  return clip_url
77
-
81
+
78
82
  # if the clip is screwed up then it was a national tv game
79
83
  # return the correct national tv clip url
80
84
  site_url = f"https://baseballsavant.mlb.com/sporty-videos?playId={self._play.play_id}&videoType=NETWORK"
@@ -82,22 +86,23 @@ class Clip():
82
86
 
83
87
  return clip_url
84
88
 
85
- def download(self, path: str, verbose: bool =False) -> None:
86
- # create response object
87
- # if a time out happens, try five more times before crashing the entire program
88
- for z in range(5):
89
- try:
90
- r = requests.get(self._clip_url, stream=True, timeout=60)
91
- break
92
- except requests.exceptions.Timeout:
93
- print(f'Timeout has been raised. Link: {self._clip_url}')
89
+ def download(self, path: str | Path, verbose: bool = False) -> Path:
90
+ path = path if isinstance(path, Path) else Path(path)
91
+
92
+ # create response object
93
+ try:
94
+ r = requests.get(self._clip_url, stream=True, timeout=60)
95
+ except requests.exceptions.Timeout:
96
+ print(f'Timeout has been raised. Link: {self._clip_url}')
94
97
 
95
98
  # download the file to the specific location
96
99
  # honestly copied and pasted code, can't say much else
97
- with open(path, 'wb') as f:
98
- for chunk in r.iter_content(chunk_size = 1024*1024):
99
- if chunk:
100
- f.write(chunk)
100
+ with open(path, 'wb') as f:
101
+ for chunk in r.iter_content(chunk_size=1024*1024):
102
+ if chunk:
103
+ f.write(chunk)
101
104
 
102
105
  if verbose:
103
- print(f"Successfully downloaded: {path}")
106
+ print(f"Successfully downloaded: {path.absolute()}")
107
+
108
+ return path
@@ -1,20 +1,25 @@
1
1
  from typing import List, Literal
2
2
  from functools import singledispatchmethod
3
+ from pathlib import Path
3
4
 
4
5
  from .play import Play
5
6
  from .game import Game
6
7
  from .clip import Clip
7
8
  from .utils import async_run
8
9
 
10
+
9
11
  class Clips():
10
- def __init__(self, plays: List[Play] | Play, broadcast_type: Literal["HOME", "AWAY"] | None=None):
12
+ """Container class for working with and generating multiple clips from a list of plays"""
13
+
14
+ def __init__(self, plays: List[Play] | Play, broadcast_type: Literal["HOME", "AWAY"] | None = None):
11
15
  self.__set_plays(plays)
12
16
 
13
- match broadcast_type: # Enforce broad_type types
17
+ match broadcast_type: # Enforce broad_type types
14
18
  case "HOME" | "AWAY" | None:
15
19
  self._broadcast_type: str | None = broadcast_type
16
20
  case _:
17
- raise ValueError("BroadcastType must be None, \"HOME\", or \"AWAY\"")
21
+ raise ValueError(
22
+ "BroadcastType must be None, \"HOME\", or \"AWAY\"")
18
23
 
19
24
  # Generate clip objects for each play passed
20
25
  self._clips = async_run(Clip, self._plays, self._broadcast_type)
@@ -22,7 +27,7 @@ class Clips():
22
27
  @singledispatchmethod
23
28
  def __set_plays(self, plays) -> None:
24
29
  raise ValueError("A Play or list of Play objects must be passed")
25
-
30
+
26
31
  @__set_plays.register(list)
27
32
  def _(self, plays: List[Play]) -> None:
28
33
  if len(plays) == 0 or not isinstance(plays[0], Play):
@@ -46,6 +51,20 @@ class Clips():
46
51
  def broadcast_type(self) -> str:
47
52
  return self._broadcast_type
48
53
 
49
- def download(self, path: str, verbose: bool=False) -> None:
50
- paths = [f"{path}{index:03d}.mp4" for index in range(len(self._clips))]
51
- async_run(Clip.download, self._clips, paths, verbose)
54
+ def download(self, dir_path: str | Path, verbose: bool = False) -> List[Path]:
55
+ """Download all clips into a single directory"""
56
+ # Convert path string to Path object
57
+ dir_path: Path = dir_path if isinstance(
58
+ dir_path, Path) else Path(dir_path)
59
+ dir_path.mkdir(exist_ok=True, parents=True)
60
+
61
+ # Get all paths for the mp4s
62
+ paths: List[Path] = [
63
+ dir_path / f"{index:04d}.mp4" for index in range(len(self._clips))]
64
+
65
+ # Create all paths in memory if they don't exist
66
+ for p in paths:
67
+ p.touch()
68
+
69
+ # Download all clips and return the download paths
70
+ return async_run(Clip.download, self._clips, paths, verbose)
@@ -1,6 +1,6 @@
1
1
  Team ID,Code,File Code,Abbreviation,Name,Full Name,Brief Name
2
2
  108,ana,ana,LAA,LA Angels,Los Angeles Angels,Angels
3
- 109,ari,ari,ARI,Arizona,Arizona Diamondbacks,D-backs
3
+ 109,ari,ari,AZ,Arizona,Arizona Diamondbacks,D-backs
4
4
  110,bal,bal,BAL,Baltimore,Baltimore Orioles,Orioles
5
5
  111,bos,bos,BOS,Boston,Boston Red Sox,Red Sox
6
6
  112,chn,chc,CHC,Chi Cubs,Chicago Cubs,Cubs
@@ -4,7 +4,9 @@ from copy import copy
4
4
  from .date import Date
5
5
  from .date_range import DateRange
6
6
 
7
+
7
8
  class DateGenerator():
9
+ """Static library for generating date objects"""
8
10
 
9
11
  @classmethod
10
12
  def today(cls) -> Date:
@@ -50,4 +52,4 @@ class DateGenerator():
50
52
 
51
53
  end_dt.prev()
52
54
 
53
- return DateRange(start_dt, end_dt)
55
+ return DateRange(start_dt, end_dt)
@@ -1,10 +1,9 @@
1
1
  import pandas as pd
2
- import io
3
2
  import requests
4
3
  import json
5
4
 
6
5
  from functools import lru_cache, singledispatchmethod
7
- from typing import List, Optional
6
+ from typing import List, Optional, Dict
8
7
 
9
8
  from .team import Team
10
9
  from .date import Date
@@ -12,6 +11,7 @@ from .player import Player
12
11
  from .play import Play
13
12
  from .utils import async_run, dataframe_from_url
14
13
 
14
+
15
15
  class Game():
16
16
 
17
17
  @lru_cache(maxsize=3)
@@ -19,8 +19,7 @@ class Game():
19
19
  return super().__new__(cls)
20
20
 
21
21
  def __init__(self, game_pk: int):
22
- self._game_pk: int = game_pk
23
- self._game_data: pd.DataFrame | None = None
22
+ self._game_pk: int = game_pk
24
23
 
25
24
  # finds the url of the game based on the game_pk information stored in the at-bat data
26
25
  game_url = f"https://baseballsavant.mlb.com/gf?game_pk={self._game_pk}"
@@ -32,18 +31,20 @@ class Game():
32
31
  self._home_json = self._game_json["team_home"]
33
32
 
34
33
  # Get home/away and date
35
- self._home: Team = Team(self._game_json["home_team_data"]["abbreviation"])
36
- self._away: Team = Team(self._game_json["away_team_data"]["abbreviation"])
34
+ self._home: Team = Team(
35
+ self._game_json["home_team_data"]["abbreviation"])
36
+ self._away: Team = Team(
37
+ self._game_json["away_team_data"]["abbreviation"])
37
38
  self._date: Date = Date(self._game_json["gameDate"])
38
39
 
39
40
  # Get both lineups
40
- self._home_lineup: List[int] = self._game_json["home_lineup"]
41
- self._away_lineup: List[int] = self._game_json["away_lineup"]
41
+ self._home_lineup_ids: List[int] = self._game_json["home_lineup"]
42
+ self._away_lineup_ids: List[int] = self._game_json["away_lineup"]
42
43
 
43
44
  # Get the final scores
44
45
  self._home_score: int = self._game_json["scoreboard"]["linescore"]["teams"]["home"]["runs"]
45
46
  self._away_score: int = self._game_json["scoreboard"]["linescore"]["teams"]["away"]["runs"]
46
-
47
+
47
48
  @singledispatchmethod
48
49
  def road_status(self, team: Team) -> str:
49
50
  raise ValueError("team must be of type Team")
@@ -73,16 +74,16 @@ class Game():
73
74
 
74
75
  @property
75
76
  def home_lineup(self) -> List[int]:
76
- return self._home_lineup.copy()
77
+ return self._home_lineup_ids.copy()
77
78
 
78
79
  @property
79
80
  def away_lineup(self) -> List[int]:
80
- return self._away_lineup.copy()
81
+ return self._away_lineup_ids.copy()
81
82
 
82
83
  def get_lineup(self, team) -> List[int]:
83
84
  if team == self._away:
84
- return self.away_lineup()
85
-
85
+ return self.away_lineup
86
+
86
87
  return self.home_lineup
87
88
 
88
89
  @property
@@ -113,8 +114,9 @@ class Game():
113
114
  def home_json(self):
114
115
  return self._home_json.copy()
115
116
 
116
- def get_highlights(self, plays:int =10, team: Optional[str]=None):
117
- df = self.__get_data()
117
+ def get_highlights(self, plays: int = 10, team: Optional[str] = None) -> List[Play]:
118
+ """Gets the highlights of both, away, or home teams"""
119
+ df: pd.DataFrame = self.__get_data()
118
120
 
119
121
  if plays <= 0:
120
122
  raise ValueError("Plays must be greater than 0")
@@ -124,14 +126,16 @@ class Game():
124
126
 
125
127
  # extra logic for when a home or away team is specified
126
128
  key = None if team else abs
127
- ascending = False if not team else (True, False)[team.lower() == "home"]
129
+ ascending = False if not team else (
130
+ True, False)[team.lower() == "home"]
128
131
 
129
132
  # removes all non-events (balls, strikes, etc.)
130
133
  # then sorts for highest win expectancy for either team
131
134
  # then only keeps the top plays
132
135
  # also make sure the plays are in chronological order of the game
133
136
  df = df[df.events.notnull()]
134
- df = df.sort_values(by="delta_home_win_exp", key=key, ascending=ascending)
137
+ df = df.sort_values(by="delta_home_win_exp",
138
+ key=key, ascending=ascending)
135
139
  df = df.head(plays)
136
140
  df = df.sort_values(by="pitch_number", ascending=True)
137
141
  df = df.sort_values(by="at_bat_number", ascending=True)
@@ -150,8 +154,10 @@ class Game():
150
154
 
151
155
  is_home_player = player.player_id in self.home_lineup
152
156
 
153
- df = df[df.events.notnull() & ((df.batter == player.player_id) | (df.pitcher == player.player_id))]
154
- df = df.sort_values(by="delta_home_win_exp", key=None, ascending=is_home_player)
157
+ df = df[df.events.notnull() & ((df.batter == player.player_id)
158
+ | (df.pitcher == player.player_id))]
159
+ df = df.sort_values(by="delta_home_win_exp",
160
+ key=None, ascending=is_home_player)
155
161
  df = df.sort_values(by="at_bat_number", ascending=True)
156
162
 
157
163
  if is_home_player:
@@ -164,6 +170,12 @@ class Game():
164
170
  rows = [row for index, row in df.iterrows()]
165
171
  return async_run(Play, self, rows)
166
172
 
173
+ @property
174
+ def homers(self) -> Dict[int, int]:
175
+ df: pd.DataFrame = self.__get_data()
176
+ df = df[df.events == "home_run"]
177
+
178
+ return df["batter"].value_counts().to_dict()
179
+
167
180
  def __str__(self) -> str:
168
181
  return f"{self._away.abbreviation} - {self._home.abbreviation}, Final: {self._away_score}-{self._home_score}, Date: {self._date}, GamePK: {self._game_pk}"
169
-
@@ -1,19 +1,24 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import pandas as pd
4
- import io
5
4
  import requests
6
5
  import json
6
+ from functools import lru_cache
7
7
 
8
- from typing import List, Dict, Optional, TYPE_CHECKING
8
+ from typing import List, TYPE_CHECKING
9
9
  if TYPE_CHECKING:
10
10
  from .game import Play
11
11
 
12
- from .date_generator import DateGenerator
12
+ from .date import Date
13
13
  from .utils import async_run, dataframe_copy, dataframe_from_url
14
14
 
15
+
15
16
  class Player():
16
17
 
18
+ @lru_cache(maxsize=100)
19
+ def __new__(cls, player_id: int):
20
+ return super().__new__(cls)
21
+
17
22
  def __init__(self, player_id: int):
18
23
  self._player_id: int = player_id
19
24
 
@@ -32,7 +37,7 @@ class Player():
32
37
 
33
38
  def is_pitcher(self) -> bool:
34
39
  return self._primary_position_abbr == "P"
35
-
40
+
36
41
  def is_batter(self) -> bool:
37
42
  return not self.is_pitcher()
38
43
 
@@ -75,33 +80,35 @@ class Player():
75
80
  # Encapsulates mutable data by making a copy
76
81
  @dataframe_copy
77
82
  def get_homerun_data(self, season: int) -> pd.DataFrame:
78
- return self.__get_homerun_data(season)
83
+ df: pd.DataFrame = self.__get_homerun_data(season)
84
+ df = df.sort_values("game_date")
85
+ return df
79
86
 
80
87
  def get_homerun_count(self, season: int) -> int:
81
88
  return len(self.__get_homerun_data(season).index)
82
89
 
83
- def get_homeruns(self, season: int | Date) -> List[Play]:
90
+ def get_homeruns(self, season: int) -> List[Play]:
84
91
  from .game import Play, Game
85
92
 
86
93
  homerun_data = self.__get_homerun_data(season)
87
- # homerun_data.to_csv("./homeruns.csv")
88
-
89
- # if date:
90
- # homerun_data = homerun_data[homerun_data[]]
91
94
 
92
95
  games: List[Game] = async_run(Game, list(homerun_data["game_pk"]))
93
96
  rows = [row for index, row in homerun_data.iterrows()]
94
97
 
95
98
  return async_run(Play, games, rows)[::-1]
96
99
 
100
+ @classmethod
101
+ def generate_players(cls, player_ids: List[int]) -> List[Player]:
102
+ return async_run(cls, player_ids)
103
+
97
104
  def __eq__(self, o: object) -> bool:
98
105
  if not isinstance(o, Player):
99
106
  return False
100
107
 
101
- return self._player_id == other._player_id
108
+ return self._player_id == o._player_id
102
109
 
103
110
  def __hash__(self) -> int:
104
- return self._player_id
111
+ return hash(self._player_id)
105
112
 
106
113
  def __str__(self) -> str:
107
- return f"{self.__class__}@PlayerID={self._player_id}:FirstName={self._first_name}:LastName={self._last_name}"
114
+ return f"{self.__class__}@PlayerID={self._player_id}:FirstName={self._first_name}:LastName={self._last_name}"
@@ -1,15 +1,19 @@
1
1
  import pandas as pd
2
- import os
2
+ from pathlib import Path
3
3
 
4
4
  from functools import cache
5
5
  from typing import List, Dict
6
6
 
7
+
7
8
  class Team():
8
- _team_lookup: pd.DataFrame = pd.read_csv(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/team-info.csv'))
9
+ """Team data class"""
10
+ _team_lookup: pd.DataFrame = pd.read_csv(
11
+ Path(__file__).parent / "data" / "team-info.csv"
12
+ )
9
13
 
10
- # Speeds up and saves memory by caching the same type of objects
11
14
  @cache
12
15
  def __new__(cls, abbr: str):
16
+ """Speeds up and saves memory by caching objects of the same team"""
13
17
  return super().__new__(cls)
14
18
 
15
19
  def __init__(self, abbr: str):
@@ -38,4 +42,4 @@ class Team():
38
42
  return self._abbr == other._abbr
39
43
 
40
44
  def __str__(self) -> str:
41
- return f"{self.__class__}@Name={self._team}:Abbreviation={self._abbr}:ID={self._team_id}"
45
+ return f"{self.__class__}@Name={self._team}:Abbreviation={self._abbr}:ID={self._team_id}"
@@ -8,6 +8,7 @@ import functools
8
8
 
9
9
  from typing import Any, List, Tuple
10
10
 
11
+
11
12
  def async_run(func: callable, *args: Any | List[Any]) -> List[Any]:
12
13
  # Makes all arguments into lists
13
14
  try:
@@ -15,7 +16,8 @@ def async_run(func: callable, *args: Any | List[Any]) -> List[Any]:
15
16
  except ValueError:
16
17
  raise ValueError("At least one argument must be a list")
17
18
 
18
- repeated_args = [arg if isinstance(arg, list) else list(itertools.repeat(arg, max_length)) for arg in args]
19
+ repeated_args = [arg if isinstance(arg, list) else list(
20
+ itertools.repeat(arg, max_length)) for arg in args]
19
21
 
20
22
  # Ensure all list arguments are of the same length
21
23
  if not all(len(i) == len(repeated_args[0]) for i in repeated_args):
@@ -25,13 +27,15 @@ def async_run(func: callable, *args: Any | List[Any]) -> List[Any]:
25
27
  return func(*args)
26
28
 
27
29
  async def generate() -> List[Any]:
28
- tasks = [asyncio.create_task(create(*args)) for args in zip(*repeated_args)]
30
+ tasks = [asyncio.create_task(create(*args))
31
+ for args in zip(*repeated_args)]
29
32
 
30
33
  await asyncio.gather(*tasks)
31
34
  return [task.result() for task in tasks]
32
35
 
33
36
  return asyncio.run(generate())
34
37
 
38
+
35
39
  def copy_cache(func):
36
40
  func = functools.cache(func)
37
41
 
@@ -40,13 +44,15 @@ def copy_cache(func):
40
44
 
41
45
  return wrapper
42
46
 
47
+
43
48
  def dataframe_copy(func):
44
49
  def wrapper(*args, **kwargs):
45
50
  return func(*args, **kwargs).copy()
46
51
 
47
52
  return wrapper
48
53
 
49
- def dataframe_from_url(func, use_cache: bool=True):
54
+
55
+ def dataframe_from_url(func, use_cache: bool = True):
50
56
  if use_cache:
51
57
  func = functools.cache(func)
52
58
 
@@ -56,4 +62,4 @@ def dataframe_from_url(func, use_cache: bool=True):
56
62
 
57
63
  return pd.read_csv(io.StringIO(csv.decode('utf-8')))
58
64
 
59
- return wrapper
65
+ return wrapper
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mlbrecaps
3
- Version: 0.0.1
3
+ Version: 0.0.2
4
4
  Summary: Package that gathers information on given MLB games
5
5
  Home-page: https://github.com/MrRedwing/MLB-Recaps
6
6
  Author: Karsten Larson
7
7
  Author-email: karsten.larson.1@gmail.com
8
8
  License: MIT
9
+ Platform: UNKNOWN
9
10
  Classifier: License :: OSI Approved :: MIT License
10
11
  Classifier: Programming Language :: Python :: 3.11
11
12
  Classifier: Operating System :: OS Independent
@@ -65,3 +66,5 @@ All contributions are welcomed to improve the mlbrecaps package. To contribute s
65
66
  ### License
66
67
 
67
68
  This project is licensed under the MIT License - see the LICENSE file for details.
69
+
70
+
@@ -1,11 +1,6 @@
1
1
  LICENSE.txt
2
2
  README.md
3
3
  setup.py
4
- /home/karsten/coding/python/recaps/mlbrecaps.egg-info/PKG-INFO
5
- /home/karsten/coding/python/recaps/mlbrecaps.egg-info/SOURCES.txt
6
- /home/karsten/coding/python/recaps/mlbrecaps.egg-info/dependency_links.txt
7
- /home/karsten/coding/python/recaps/mlbrecaps.egg-info/requires.txt
8
- /home/karsten/coding/python/recaps/mlbrecaps.egg-info/top_level.txt
9
4
  mlbrecaps/__init__.py
10
5
  mlbrecaps/__main__.py
11
6
  mlbrecaps/clip.py
@@ -1,11 +1,11 @@
1
1
  from setuptools import find_packages, setup
2
2
 
3
3
  with open("README.md", "r") as f:
4
- long_description = f.read()
4
+ long_description = f.read()
5
5
 
6
6
  setup(
7
7
  name="mlbrecaps",
8
- version="0.0.1",
8
+ version="0.0.2",
9
9
  description="Package that gathers information on given MLB games",
10
10
  packages=find_packages(include=["mlbrecaps"]),
11
11
  long_description=long_description,
@@ -1,21 +0,0 @@
1
- from .scripts import get_highlights
2
-
3
- from .date import Date
4
- from .team import Team
5
-
6
- if __name__ == "__main__":
7
- # Get teams for the search
8
- team = Team(input("Input team name: "))
9
- date = Date(input("Date MM/DD/YYYY: "))
10
- file_path = input("Downloads filepath: ")
11
-
12
- if not file_path.endswith("/"):
13
- file_path += "/"
14
-
15
- game_clips = get_highlights(team, date)
16
-
17
- for index, clips in enumerate(game_clips):
18
- # Download all highlight clips
19
- print(f"Game {index + 1}: {clips.plays[0].game}")
20
-
21
- clips.download(f"{file_path}{index}", verbose=True)
File without changes
File without changes
File without changes
File without changes
@@ -1,8 +1,8 @@
1
+ beautifulsoup4>=4.4.0
2
+ lxml>=4.2.1
1
3
  numpy>=1.13.0
2
4
  pandas>=1.0.3
3
- beautifulsoup4>=4.4.0
4
5
  requests>=2.18.1
5
- lxml>=4.2.1
6
6
 
7
7
  [dev]
8
8
  twine
File without changes