nba 0.1.1 → 0.2.0

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 (321) hide show
  1. checksums.yaml +5 -5
  2. data/AGENTS.md +362 -0
  3. data/CHANGELOG.md +169 -0
  4. data/CLAUDE.md +1 -0
  5. data/LICENSE +21 -0
  6. data/README.md +501 -101
  7. data/bin/console +10 -0
  8. data/bin/setup +6 -0
  9. data/exe/nba +8 -0
  10. data/lib/nba/all_time_leader.rb +77 -0
  11. data/lib/nba/all_time_leaders.rb +185 -0
  12. data/lib/nba/assist_leader.rb +92 -0
  13. data/lib/nba/assist_leaders.rb +64 -0
  14. data/lib/nba/assist_tracker.rb +108 -0
  15. data/lib/nba/assist_tracker_entry.rb +206 -0
  16. data/lib/nba/award.rb +128 -0
  17. data/lib/nba/box_score.rb +2 -0
  18. data/lib/nba/box_score_advanced.rb +114 -0
  19. data/lib/nba/box_score_advanced_player_stat.rb +297 -0
  20. data/lib/nba/box_score_advanced_team_stat.rb +237 -0
  21. data/lib/nba/box_score_advanced_v3.rb +124 -0
  22. data/lib/nba/box_score_defensive_player_stat.rb +281 -0
  23. data/lib/nba/box_score_defensive_team_stat.rb +85 -0
  24. data/lib/nba/box_score_defensive_v2.rb +190 -0
  25. data/lib/nba/box_score_four_factors.rb +91 -0
  26. data/lib/nba/box_score_four_factors_player_stat.rb +185 -0
  27. data/lib/nba/box_score_four_factors_team_stat.rb +141 -0
  28. data/lib/nba/box_score_four_factors_v3.rb +133 -0
  29. data/lib/nba/box_score_hustle.rb +226 -0
  30. data/lib/nba/box_score_hustle_player_stat.rb +233 -0
  31. data/lib/nba/box_score_hustle_team_stat.rb +189 -0
  32. data/lib/nba/box_score_matchup_stat.rb +417 -0
  33. data/lib/nba/box_score_matchups_v3.rb +184 -0
  34. data/lib/nba/box_score_misc.rb +100 -0
  35. data/lib/nba/box_score_misc_player_stat.rb +217 -0
  36. data/lib/nba/box_score_misc_team_stat.rb +173 -0
  37. data/lib/nba/box_score_misc_v3.rb +163 -0
  38. data/lib/nba/box_score_player_stat.rb +273 -0
  39. data/lib/nba/box_score_player_track.rb +223 -0
  40. data/lib/nba/box_score_player_track_stat.rb +273 -0
  41. data/lib/nba/box_score_player_track_team_stat.rb +229 -0
  42. data/lib/nba/box_score_scoring.rb +103 -0
  43. data/lib/nba/box_score_scoring_player_stat.rb +241 -0
  44. data/lib/nba/box_score_scoring_team_stat.rb +197 -0
  45. data/lib/nba/box_score_scoring_v3.rb +170 -0
  46. data/lib/nba/box_score_similarity_score.rb +119 -0
  47. data/lib/nba/box_score_similarity_stat.rb +76 -0
  48. data/lib/nba/box_score_starter_bench_stat.rb +257 -0
  49. data/lib/nba/box_score_summary.rb +285 -0
  50. data/lib/nba/box_score_summary_v2.rb +202 -0
  51. data/lib/nba/box_score_summary_v3.rb +120 -0
  52. data/lib/nba/box_score_summary_v3_data.rb +419 -0
  53. data/lib/nba/box_score_team_stat.rb +229 -0
  54. data/lib/nba/box_score_traditional.rb +101 -0
  55. data/lib/nba/box_score_traditional_v3.rb +195 -0
  56. data/lib/nba/box_score_usage.rb +102 -0
  57. data/lib/nba/box_score_usage_player_stat.rb +265 -0
  58. data/lib/nba/box_score_usage_team_stat.rb +221 -0
  59. data/lib/nba/box_score_usage_v3.rb +169 -0
  60. data/lib/nba/box_score_v3_helpers.rb +144 -0
  61. data/lib/nba/career_stats.rb +217 -0
  62. data/lib/nba/cli/display/player_display.rb +98 -0
  63. data/lib/nba/cli/display.rb +178 -0
  64. data/lib/nba/cli/formatters/game_formatters.rb +86 -0
  65. data/lib/nba/cli/formatters/leaders_formatters.rb +26 -0
  66. data/lib/nba/cli/formatters/player_formatters.rb +52 -0
  67. data/lib/nba/cli/formatters/standings_formatters.rb +26 -0
  68. data/lib/nba/cli/formatters/team_formatters.rb +67 -0
  69. data/lib/nba/cli/formatters/time_formatters.rb +82 -0
  70. data/lib/nba/cli/formatters.rb +56 -0
  71. data/lib/nba/cli/helpers.rb +135 -0
  72. data/lib/nba/cli.rb +171 -20
  73. data/lib/nba/client.rb +35 -0
  74. data/lib/nba/collection.rb +89 -0
  75. data/lib/nba/college_player_stat.rb +200 -0
  76. data/lib/nba/common_player_info.rb +142 -0
  77. data/lib/nba/common_playoff_series.rb +90 -0
  78. data/lib/nba/common_team_years.rb +113 -0
  79. data/lib/nba/conference.rb +39 -0
  80. data/lib/nba/connection.rb +84 -0
  81. data/lib/nba/cume_stats_player.rb +358 -0
  82. data/lib/nba/cume_stats_player_game.rb +217 -0
  83. data/lib/nba/cume_stats_player_games.rb +99 -0
  84. data/lib/nba/cume_stats_player_games_entry.rb +25 -0
  85. data/lib/nba/cume_stats_player_total.rb +481 -0
  86. data/lib/nba/cume_stats_team.rb +349 -0
  87. data/lib/nba/cume_stats_team_games.rb +145 -0
  88. data/lib/nba/cume_stats_team_games_entry.rb +25 -0
  89. data/lib/nba/cume_stats_team_player.rb +485 -0
  90. data/lib/nba/cume_stats_team_total.rb +267 -0
  91. data/lib/nba/data.rb +73 -0
  92. data/lib/nba/defense_hub.rb +109 -0
  93. data/lib/nba/defense_hub_stat.rb +57 -0
  94. data/lib/nba/defensive_shot_stat.rb +102 -0
  95. data/lib/nba/division.rb +49 -0
  96. data/lib/nba/draft_board.rb +126 -0
  97. data/lib/nba/draft_board_pick.rb +173 -0
  98. data/lib/nba/draft_combine_anthro_measurement.rb +163 -0
  99. data/lib/nba/draft_combine_drill_result.rb +115 -0
  100. data/lib/nba/draft_combine_drill_results.rb +112 -0
  101. data/lib/nba/draft_combine_non_stationary_shooting.rb +268 -0
  102. data/lib/nba/draft_combine_non_stationary_shooting_result.rb +355 -0
  103. data/lib/nba/draft_combine_player_anthro.rb +133 -0
  104. data/lib/nba/draft_combine_spot_shooting.rb +243 -0
  105. data/lib/nba/draft_combine_spot_shooting_result.rb +419 -0
  106. data/lib/nba/draft_combine_stat.rb +211 -0
  107. data/lib/nba/draft_combine_stats.rb +160 -0
  108. data/lib/nba/draft_history.rb +142 -0
  109. data/lib/nba/draft_pick.rb +154 -0
  110. data/lib/nba/dunk_score_leader.rb +93 -0
  111. data/lib/nba/dunk_score_leaders.rb +77 -0
  112. data/lib/nba/estimated_metrics_stat.rb +152 -0
  113. data/lib/nba/fantasy_profile_stat.rb +142 -0
  114. data/lib/nba/fantasy_widget.rb +72 -0
  115. data/lib/nba/fantasy_widget_player.rb +98 -0
  116. data/lib/nba/found_game.rb +260 -0
  117. data/lib/nba/franchise.rb +136 -0
  118. data/lib/nba/franchise_history.rb +142 -0
  119. data/lib/nba/franchise_leader.rb +147 -0
  120. data/lib/nba/franchise_leaders.rb +162 -0
  121. data/lib/nba/franchise_player.rb +224 -0
  122. data/lib/nba/franchise_players.rb +147 -0
  123. data/lib/nba/game.rb +80 -64
  124. data/lib/nba/game_log.rb +349 -0
  125. data/lib/nba/game_rotation.rb +152 -0
  126. data/lib/nba/game_streak.rb +102 -0
  127. data/lib/nba/games.rb +46 -0
  128. data/lib/nba/home_page_leader.rb +99 -0
  129. data/lib/nba/home_page_leaders.rb +75 -0
  130. data/lib/nba/home_page_stat.rb +57 -0
  131. data/lib/nba/home_page_v2.rb +110 -0
  132. data/lib/nba/hustle_stats_box_score.rb +182 -0
  133. data/lib/nba/infographic_fan_duel_player.rb +139 -0
  134. data/lib/nba/infographic_fan_duel_player_stat.rb +311 -0
  135. data/lib/nba/ist_standing.rb +167 -0
  136. data/lib/nba/ist_standings.rb +81 -0
  137. data/lib/nba/leader.rb +103 -0
  138. data/lib/nba/leaders.rb +110 -0
  139. data/lib/nba/leaders_tile.rb +57 -0
  140. data/lib/nba/leaders_tiles.rb +90 -0
  141. data/lib/nba/league.rb +37 -0
  142. data/lib/nba/league_dash_lineup_stat.rb +270 -0
  143. data/lib/nba/league_dash_lineups.rb +177 -0
  144. data/lib/nba/league_dash_opp_pt_shot.rb +150 -0
  145. data/lib/nba/league_dash_player_bio_stat.rb +217 -0
  146. data/lib/nba/league_dash_player_bio_stats.rb +164 -0
  147. data/lib/nba/league_dash_player_clutch.rb +212 -0
  148. data/lib/nba/league_dash_player_clutch_stat.rb +271 -0
  149. data/lib/nba/league_dash_player_pt_shot.rb +152 -0
  150. data/lib/nba/league_dash_player_pt_shot_stat.rb +193 -0
  151. data/lib/nba/league_dash_player_shot_location_stat.rb +265 -0
  152. data/lib/nba/league_dash_player_shot_locations.rb +210 -0
  153. data/lib/nba/league_dash_player_stat.rb +306 -0
  154. data/lib/nba/league_dash_player_stats.rb +176 -0
  155. data/lib/nba/league_dash_pt_defend.rb +160 -0
  156. data/lib/nba/league_dash_pt_defend_stat.rb +145 -0
  157. data/lib/nba/league_dash_pt_stats.rb +152 -0
  158. data/lib/nba/league_dash_pt_stats_stat.rb +169 -0
  159. data/lib/nba/league_dash_pt_team_defend.rb +158 -0
  160. data/lib/nba/league_dash_pt_team_defend_stat.rb +110 -0
  161. data/lib/nba/league_dash_team_clutch.rb +211 -0
  162. data/lib/nba/league_dash_team_clutch_stat.rb +237 -0
  163. data/lib/nba/league_dash_team_pt_shot.rb +150 -0
  164. data/lib/nba/league_dash_team_pt_shot_stat.rb +166 -0
  165. data/lib/nba/league_dash_team_shot_location_stat.rb +230 -0
  166. data/lib/nba/league_dash_team_shot_locations.rb +208 -0
  167. data/lib/nba/league_dash_team_stat.rb +275 -0
  168. data/lib/nba/league_dash_team_stats.rb +172 -0
  169. data/lib/nba/league_game_finder.rb +170 -0
  170. data/lib/nba/league_game_log.rb +224 -0
  171. data/lib/nba/league_hustle_stats_player.rb +161 -0
  172. data/lib/nba/league_hustle_stats_player_stat.rb +253 -0
  173. data/lib/nba/league_hustle_stats_team.rb +157 -0
  174. data/lib/nba/league_hustle_stats_team_stat.rb +179 -0
  175. data/lib/nba/league_lineup_viz.rb +184 -0
  176. data/lib/nba/league_lineup_viz_stat.rb +214 -0
  177. data/lib/nba/league_player_on_details.rb +175 -0
  178. data/lib/nba/league_player_on_details_stat.rb +313 -0
  179. data/lib/nba/league_season_matchup_stat.rb +241 -0
  180. data/lib/nba/league_season_matchups.rb +181 -0
  181. data/lib/nba/league_standing.rb +284 -0
  182. data/lib/nba/league_standings.rb +159 -0
  183. data/lib/nba/league_wide_shot_stat.rb +62 -0
  184. data/lib/nba/live_action.rb +240 -0
  185. data/lib/nba/live_box_score.rb +143 -0
  186. data/lib/nba/live_connection.rb +84 -0
  187. data/lib/nba/live_game.rb +230 -0
  188. data/lib/nba/live_play_by_play.rb +120 -0
  189. data/lib/nba/live_player_stat.rb +276 -0
  190. data/lib/nba/live_scoreboard.rb +102 -0
  191. data/lib/nba/matchup_rollup.rb +98 -0
  192. data/lib/nba/matchups_rollup.rb +81 -0
  193. data/lib/nba/pass_stat.rb +209 -0
  194. data/lib/nba/play.rb +258 -0
  195. data/lib/nba/play_by_play.rb +85 -0
  196. data/lib/nba/play_by_play_v3.rb +91 -0
  197. data/lib/nba/play_type_stat.rb +206 -0
  198. data/lib/nba/player.rb +242 -24
  199. data/lib/nba/player_awards.rb +110 -0
  200. data/lib/nba/player_career_by_college.rb +86 -0
  201. data/lib/nba/player_career_by_college_rollup.rb +143 -0
  202. data/lib/nba/player_career_stats.rb +77 -0
  203. data/lib/nba/player_compare.rb +156 -0
  204. data/lib/nba/player_comparison_stat.rb +242 -0
  205. data/lib/nba/player_dash_pt_pass.rb +164 -0
  206. data/lib/nba/player_dash_pt_reb.rb +235 -0
  207. data/lib/nba/player_dash_pt_shot_defend.rb +119 -0
  208. data/lib/nba/player_dash_pt_shots.rb +279 -0
  209. data/lib/nba/player_dashboard.rb +259 -0
  210. data/lib/nba/player_dashboard_stat.rb +248 -0
  211. data/lib/nba/player_estimated_metrics.rb +84 -0
  212. data/lib/nba/player_fantasy_profile_bar_graph.rb +147 -0
  213. data/lib/nba/player_game_log.rb +72 -0
  214. data/lib/nba/player_game_logs.rb +117 -0
  215. data/lib/nba/player_game_streak_finder.rb +108 -0
  216. data/lib/nba/player_index.rb +135 -0
  217. data/lib/nba/player_index_entry.rb +266 -0
  218. data/lib/nba/player_info.rb +225 -0
  219. data/lib/nba/player_next_n_games.rb +64 -0
  220. data/lib/nba/player_profile_v2.rb +169 -0
  221. data/lib/nba/player_vs_player.rb +153 -0
  222. data/lib/nba/players.rb +107 -0
  223. data/lib/nba/playoff_matchup.rb +84 -0
  224. data/lib/nba/playoff_picture.rb +98 -0
  225. data/lib/nba/playoff_series.rb +76 -0
  226. data/lib/nba/position.rb +48 -0
  227. data/lib/nba/rebound_stat.rb +189 -0
  228. data/lib/nba/response_parser.rb +116 -0
  229. data/lib/nba/roster.rb +74 -0
  230. data/lib/nba/rotation_entry.rb +154 -0
  231. data/lib/nba/schedule.rb +183 -0
  232. data/lib/nba/schedule_international.rb +182 -0
  233. data/lib/nba/scheduled_game.rb +240 -0
  234. data/lib/nba/scoreboard.rb +183 -0
  235. data/lib/nba/scoreboard_v3.rb +104 -0
  236. data/lib/nba/shot.rb +208 -0
  237. data/lib/nba/shot_chart.rb +75 -0
  238. data/lib/nba/shot_chart_league_wide.rb +102 -0
  239. data/lib/nba/shot_chart_lineup_detail.rb +109 -0
  240. data/lib/nba/shot_stat.rb +174 -0
  241. data/lib/nba/standing.rb +129 -0
  242. data/lib/nba/standings.rb +75 -0
  243. data/lib/nba/static.rb +107 -0
  244. data/lib/nba/synergy_play_types.rb +211 -0
  245. data/lib/nba/team.rb +203 -127
  246. data/lib/nba/team_and_players_vs_players.rb +227 -0
  247. data/lib/nba/team_and_players_vs_players_stat.rb +155 -0
  248. data/lib/nba/team_dash_pt_pass.rb +157 -0
  249. data/lib/nba/team_dash_pt_reb.rb +216 -0
  250. data/lib/nba/team_dash_pt_shots.rb +244 -0
  251. data/lib/nba/team_dashboard.rb +275 -0
  252. data/lib/nba/team_dashboard_stat.rb +248 -0
  253. data/lib/nba/team_detail.rb +117 -0
  254. data/lib/nba/team_details.rb +173 -0
  255. data/lib/nba/team_estimated_metrics.rb +91 -0
  256. data/lib/nba/team_estimated_metrics_stat.rb +146 -0
  257. data/lib/nba/team_game_log.rb +143 -0
  258. data/lib/nba/team_game_log_entry.rb +246 -0
  259. data/lib/nba/team_game_log_stat.rb +275 -0
  260. data/lib/nba/team_game_logs.rb +163 -0
  261. data/lib/nba/team_game_streak.rb +111 -0
  262. data/lib/nba/team_game_streak_finder.rb +109 -0
  263. data/lib/nba/team_historical_leader.rb +207 -0
  264. data/lib/nba/team_historical_leaders.rb +98 -0
  265. data/lib/nba/team_historical_record.rb +139 -0
  266. data/lib/nba/team_info.rb +150 -0
  267. data/lib/nba/team_info_common.rb +177 -0
  268. data/lib/nba/team_on_off_overall_stat.rb +477 -0
  269. data/lib/nba/team_on_off_player_stat.rb +523 -0
  270. data/lib/nba/team_on_off_player_summary.rb +135 -0
  271. data/lib/nba/team_pass_stat.rb +183 -0
  272. data/lib/nba/team_player_dashboard.rb +212 -0
  273. data/lib/nba/team_player_on_off_details.rb +218 -0
  274. data/lib/nba/team_player_on_off_summary.rb +214 -0
  275. data/lib/nba/team_player_stat.rb +275 -0
  276. data/lib/nba/team_rebound_stat.rb +189 -0
  277. data/lib/nba/team_season_rank.rb +110 -0
  278. data/lib/nba/team_shot_stat.rb +173 -0
  279. data/lib/nba/team_vs_player.rb +151 -0
  280. data/lib/nba/team_vs_player_stat.rb +157 -0
  281. data/lib/nba/team_year.rb +55 -0
  282. data/lib/nba/team_year_by_year_stats.rb +152 -0
  283. data/lib/nba/team_year_stat.rb +282 -0
  284. data/lib/nba/teams.rb +33 -0
  285. data/lib/nba/upcoming_game.rb +115 -0
  286. data/lib/nba/utils.rb +94 -0
  287. data/lib/nba/version.rb +5 -2
  288. data/lib/nba/video_detail.rb +103 -0
  289. data/lib/nba/video_details.rb +118 -0
  290. data/lib/nba/video_details_asset.rb +115 -0
  291. data/lib/nba/video_details_asset_entry.rb +91 -0
  292. data/lib/nba/video_event.rb +83 -0
  293. data/lib/nba/video_event_asset.rb +91 -0
  294. data/lib/nba/video_events.rb +106 -0
  295. data/lib/nba/video_events_asset.rb +107 -0
  296. data/lib/nba/video_status.rb +129 -0
  297. data/lib/nba/video_status_entry.rb +161 -0
  298. data/lib/nba/vs_player_stat.rb +156 -0
  299. data/lib/nba/win_probability.rb +117 -0
  300. data/lib/nba/win_probability_point.rb +140 -0
  301. data/lib/nba.rb +249 -5
  302. data/sig/equalizer.rbs +3 -0
  303. data/sig/nba.rbs +7297 -0
  304. data/sig/shale.rbs +24 -0
  305. data/sig/thor.rbs +19 -0
  306. metadata +324 -95
  307. data/.gitignore +0 -18
  308. data/.travis.yml +0 -22
  309. data/Gemfile +0 -23
  310. data/LICENSE.md +0 -22
  311. data/Rakefile +0 -18
  312. data/bin/nba +0 -7
  313. data/cache/teams.json +0 -16529
  314. data/lib/faraday_middleware/scrape_game.rb +0 -41
  315. data/lib/nba/request.rb +0 -37
  316. data/nba.gemspec +0 -28
  317. data/spec/fixtures/games.html +0 -785
  318. data/spec/fixtures/teams.json +0 -16529
  319. data/spec/game_spec.rb +0 -40
  320. data/spec/spec_helper.rb +0 -25
  321. data/spec/team_spec.rb +0 -93
@@ -0,0 +1,157 @@
1
+ require "json"
2
+ require_relative "client"
3
+ require_relative "collection"
4
+ require_relative "utils"
5
+
6
+ module NBA
7
+ # Provides methods to retrieve league-wide team hustle statistics
8
+ module LeagueHustleStatsTeam
9
+ # Result set name for hustle stats
10
+ # @return [String] the result set name
11
+ HUSTLE_STATS_TEAM = "HustleStatsTeam".freeze
12
+
13
+ # Season type constant for regular season
14
+ # @return [String] the season type
15
+ REGULAR_SEASON = "Regular Season".freeze
16
+
17
+ # Season type constant for playoffs
18
+ # @return [String] the season type
19
+ PLAYOFFS = "Playoffs".freeze
20
+
21
+ # Per mode constant for per game stats
22
+ # @return [String] the per mode
23
+ PER_GAME = "PerGame".freeze
24
+
25
+ # Per mode constant for totals
26
+ # @return [String] the per mode
27
+ TOTALS = "Totals".freeze
28
+
29
+ # Retrieves league-wide team hustle statistics
30
+ #
31
+ # @api public
32
+ # @example
33
+ # stats = NBA::LeagueHustleStatsTeam.all(season: 2024)
34
+ # stats.each { |s| puts "#{s.team_name}: #{s.deflections} deflections" }
35
+ # @param season [Integer] the season year
36
+ # @param season_type [String] the season type
37
+ # @param per_mode [String] the per mode
38
+ # @param client [Client] the API client to use
39
+ # @return [Collection] a collection of team hustle stats
40
+ def self.all(season: Utils.current_season, season_type: REGULAR_SEASON, per_mode: PER_GAME, client: CLIENT)
41
+ path = build_path(season, season_type, per_mode)
42
+ response = client.get(path)
43
+ parse_response(response)
44
+ end
45
+
46
+ # Builds the API path
47
+ #
48
+ # @api private
49
+ # @param season [Integer] the season year
50
+ # @param season_type [String] the season type
51
+ # @param per_mode [String] the per mode
52
+ # @return [String] the API path
53
+ def self.build_path(season, season_type, per_mode)
54
+ season_str = Utils.format_season(season)
55
+ "leaguehustlestatsteam?LeagueID=00&Season=#{season_str}&SeasonType=#{season_type}&PerMode=#{per_mode}"
56
+ end
57
+ private_class_method :build_path
58
+
59
+ # Parses the API response
60
+ #
61
+ # @api private
62
+ # @param response [String] the JSON response body
63
+ # @return [Collection] a collection of team hustle stats
64
+ def self.parse_response(response)
65
+ return Collection.new unless response
66
+
67
+ data = JSON.parse(response)
68
+ result_set = find_result_set(data)
69
+ return Collection.new unless result_set
70
+
71
+ headers = result_set["headers"]
72
+ rows = result_set["rowSet"]
73
+ return Collection.new unless headers && rows
74
+
75
+ stats = rows.map { |row| build_team_stat(headers, row) }
76
+ Collection.new(stats)
77
+ end
78
+ private_class_method :parse_response
79
+
80
+ # Finds the result set
81
+ #
82
+ # @api private
83
+ # @param data [Hash] the parsed JSON data
84
+ # @return [Hash, nil] the result set
85
+ def self.find_result_set(data)
86
+ result_sets = data["resultSets"]
87
+ return unless result_sets
88
+
89
+ result_sets.find { |rs| rs["name"].eql?(HUSTLE_STATS_TEAM) }
90
+ end
91
+ private_class_method :find_result_set
92
+
93
+ # Builds a team stat from a row
94
+ #
95
+ # @api private
96
+ # @param headers [Array<String>] the column headers
97
+ # @param row [Array] the row data
98
+ # @return [LeagueHustleStatsTeamStat] the team stat object
99
+ def self.build_team_stat(headers, row)
100
+ data = headers.zip(row).to_h
101
+ LeagueHustleStatsTeamStat.new(**team_stat_attributes(data))
102
+ end
103
+ private_class_method :build_team_stat
104
+
105
+ # Extracts team stat attributes from row data
106
+ #
107
+ # @api private
108
+ # @param data [Hash] the team stat row data
109
+ # @return [Hash] the team stat attributes
110
+ def self.team_stat_attributes(data)
111
+ identity_attributes(data).merge(hustle_attributes(data), box_out_attributes(data))
112
+ end
113
+ private_class_method :team_stat_attributes
114
+
115
+ # Extracts identity attributes
116
+ #
117
+ # @api private
118
+ # @param data [Hash] the team stat data
119
+ # @return [Hash] the identity attributes
120
+ def self.identity_attributes(data)
121
+ {team_id: data["TEAM_ID"], team_name: data["TEAM_NAME"],
122
+ min: data["MIN"]}
123
+ end
124
+ private_class_method :identity_attributes
125
+
126
+ # Extracts hustle attributes
127
+ #
128
+ # @api private
129
+ # @param data [Hash] the team stat data
130
+ # @return [Hash] the hustle attributes
131
+ def self.hustle_attributes(data)
132
+ {contested_shots: data["CONTESTED_SHOTS"],
133
+ contested_shots_2pt: data["CONTESTED_SHOTS_2PT"],
134
+ contested_shots_3pt: data["CONTESTED_SHOTS_3PT"],
135
+ deflections: data["DEFLECTIONS"], charges_drawn: data["CHARGES_DRAWN"],
136
+ screen_assists: data["SCREEN_ASSISTS"], screen_ast_pts: data["SCREEN_AST_PTS"],
137
+ off_loose_balls_recovered: data["OFF_LOOSE_BALLS_RECOVERED"],
138
+ def_loose_balls_recovered: data["DEF_LOOSE_BALLS_RECOVERED"],
139
+ loose_balls_recovered: data["LOOSE_BALLS_RECOVERED"],
140
+ pct_loose_balls_recovered_off: data["PCT_LOOSE_BALLS_RECOVERED_OFF"],
141
+ pct_loose_balls_recovered_def: data["PCT_LOOSE_BALLS_RECOVERED_DEF"]}
142
+ end
143
+ private_class_method :hustle_attributes
144
+
145
+ # Extracts box out attributes
146
+ #
147
+ # @api private
148
+ # @param data [Hash] the team stat data
149
+ # @return [Hash] the box out attributes
150
+ def self.box_out_attributes(data)
151
+ {off_boxouts: data["OFF_BOXOUTS"], def_boxouts: data["DEF_BOXOUTS"],
152
+ box_outs: data["BOX_OUTS"],
153
+ pct_box_outs_off: data["PCT_BOX_OUTS_OFF"], pct_box_outs_def: data["PCT_BOX_OUTS_DEF"]}
154
+ end
155
+ private_class_method :box_out_attributes
156
+ end
157
+ end
@@ -0,0 +1,179 @@
1
+ require "equalizer"
2
+ require "shale"
3
+
4
+ module NBA
5
+ # Represents league-wide hustle statistics for a team
6
+ class LeagueHustleStatsTeamStat < Shale::Mapper
7
+ include Equalizer.new(:team_id)
8
+
9
+ # @!attribute [rw] team_id
10
+ # Returns the team ID
11
+ # @api public
12
+ # @example
13
+ # stat.team_id #=> 1610612744
14
+ # @return [Integer] the team ID
15
+ attribute :team_id, Shale::Type::Integer
16
+
17
+ # @!attribute [rw] team_name
18
+ # Returns the team name
19
+ # @api public
20
+ # @example
21
+ # stat.team_name #=> "Golden State Warriors"
22
+ # @return [String] the team name
23
+ attribute :team_name, Shale::Type::String
24
+
25
+ # @!attribute [rw] min
26
+ # Returns total minutes played
27
+ # @api public
28
+ # @example
29
+ # stat.min #=> 19680.0
30
+ # @return [Float] total minutes
31
+ attribute :min, Shale::Type::Float
32
+
33
+ # @!attribute [rw] contested_shots
34
+ # Returns contested shots
35
+ # @api public
36
+ # @example
37
+ # stat.contested_shots #=> 2856
38
+ # @return [Integer] contested shots
39
+ attribute :contested_shots, Shale::Type::Integer
40
+
41
+ # @!attribute [rw] contested_shots_2pt
42
+ # Returns contested 2-point shots
43
+ # @api public
44
+ # @example
45
+ # stat.contested_shots_2pt #=> 1842
46
+ # @return [Integer] contested 2-point shots
47
+ attribute :contested_shots_2pt, Shale::Type::Integer
48
+
49
+ # @!attribute [rw] contested_shots_3pt
50
+ # Returns contested 3-point shots
51
+ # @api public
52
+ # @example
53
+ # stat.contested_shots_3pt #=> 1014
54
+ # @return [Integer] contested 3-point shots
55
+ attribute :contested_shots_3pt, Shale::Type::Integer
56
+
57
+ # @!attribute [rw] deflections
58
+ # Returns deflections
59
+ # @api public
60
+ # @example
61
+ # stat.deflections #=> 1024
62
+ # @return [Integer] deflections
63
+ attribute :deflections, Shale::Type::Integer
64
+
65
+ # @!attribute [rw] charges_drawn
66
+ # Returns charges drawn
67
+ # @api public
68
+ # @example
69
+ # stat.charges_drawn #=> 42
70
+ # @return [Integer] charges drawn
71
+ attribute :charges_drawn, Shale::Type::Integer
72
+
73
+ # @!attribute [rw] screen_assists
74
+ # Returns screen assists
75
+ # @api public
76
+ # @example
77
+ # stat.screen_assists #=> 1856
78
+ # @return [Integer] screen assists
79
+ attribute :screen_assists, Shale::Type::Integer
80
+
81
+ # @!attribute [rw] screen_ast_pts
82
+ # Returns points from screen assists
83
+ # @api public
84
+ # @example
85
+ # stat.screen_ast_pts #=> 3712
86
+ # @return [Integer] screen assist points
87
+ attribute :screen_ast_pts, Shale::Type::Integer
88
+
89
+ # @!attribute [rw] off_loose_balls_recovered
90
+ # Returns offensive loose balls recovered
91
+ # @api public
92
+ # @example
93
+ # stat.off_loose_balls_recovered #=> 312
94
+ # @return [Integer] offensive loose balls recovered
95
+ attribute :off_loose_balls_recovered, Shale::Type::Integer
96
+
97
+ # @!attribute [rw] def_loose_balls_recovered
98
+ # Returns defensive loose balls recovered
99
+ # @api public
100
+ # @example
101
+ # stat.def_loose_balls_recovered #=> 428
102
+ # @return [Integer] defensive loose balls recovered
103
+ attribute :def_loose_balls_recovered, Shale::Type::Integer
104
+
105
+ # @!attribute [rw] loose_balls_recovered
106
+ # Returns total loose balls recovered
107
+ # @api public
108
+ # @example
109
+ # stat.loose_balls_recovered #=> 740
110
+ # @return [Integer] loose balls recovered
111
+ attribute :loose_balls_recovered, Shale::Type::Integer
112
+
113
+ # @!attribute [rw] pct_loose_balls_recovered_off
114
+ # Returns percentage of offensive loose balls recovered
115
+ # @api public
116
+ # @example
117
+ # stat.pct_loose_balls_recovered_off #=> 0.422
118
+ # @return [Float] percentage of offensive loose balls recovered
119
+ attribute :pct_loose_balls_recovered_off, Shale::Type::Float
120
+
121
+ # @!attribute [rw] pct_loose_balls_recovered_def
122
+ # Returns percentage of defensive loose balls recovered
123
+ # @api public
124
+ # @example
125
+ # stat.pct_loose_balls_recovered_def #=> 0.578
126
+ # @return [Float] percentage of defensive loose balls recovered
127
+ attribute :pct_loose_balls_recovered_def, Shale::Type::Float
128
+
129
+ # @!attribute [rw] off_boxouts
130
+ # Returns offensive box outs
131
+ # @api public
132
+ # @example
133
+ # stat.off_boxouts #=> 245
134
+ # @return [Integer] offensive box outs
135
+ attribute :off_boxouts, Shale::Type::Integer
136
+
137
+ # @!attribute [rw] def_boxouts
138
+ # Returns defensive box outs
139
+ # @api public
140
+ # @example
141
+ # stat.def_boxouts #=> 1256
142
+ # @return [Integer] defensive box outs
143
+ attribute :def_boxouts, Shale::Type::Integer
144
+
145
+ # @!attribute [rw] box_outs
146
+ # Returns total box outs
147
+ # @api public
148
+ # @example
149
+ # stat.box_outs #=> 1501
150
+ # @return [Integer] total box outs
151
+ attribute :box_outs, Shale::Type::Integer
152
+
153
+ # @!attribute [rw] pct_box_outs_off
154
+ # Returns percentage of offensive box outs
155
+ # @api public
156
+ # @example
157
+ # stat.pct_box_outs_off #=> 0.163
158
+ # @return [Float] percentage of offensive box outs
159
+ attribute :pct_box_outs_off, Shale::Type::Float
160
+
161
+ # @!attribute [rw] pct_box_outs_def
162
+ # Returns percentage of defensive box outs
163
+ # @api public
164
+ # @example
165
+ # stat.pct_box_outs_def #=> 0.837
166
+ # @return [Float] percentage of defensive box outs
167
+ attribute :pct_box_outs_def, Shale::Type::Float
168
+
169
+ # Returns the team object
170
+ #
171
+ # @api public
172
+ # @example
173
+ # stat.team #=> #<NBA::Team>
174
+ # @return [Team, nil] the team object
175
+ def team
176
+ Teams.find(team_id)
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,184 @@
1
+ require "json"
2
+ require_relative "client"
3
+ require_relative "collection"
4
+ require_relative "league_lineup_viz_stat"
5
+ require_relative "utils"
6
+
7
+ module NBA
8
+ # Provides methods to retrieve league lineup visualization statistics
9
+ #
10
+ # @api public
11
+ module LeagueLineupViz
12
+ # Result set name for lineup visualization
13
+ # @return [String] the result set name
14
+ LEAGUE_LINEUP_VIZ = "LeagueLineupViz".freeze
15
+
16
+ # Regular season type constant
17
+ # @return [String] the season type
18
+ REGULAR_SEASON = "Regular Season".freeze
19
+
20
+ # Playoffs season type constant
21
+ # @return [String] the season type
22
+ PLAYOFFS = "Playoffs".freeze
23
+
24
+ # Per game mode constant
25
+ # @return [String] the per mode
26
+ PER_GAME = "PerGame".freeze
27
+
28
+ # Totals mode constant
29
+ # @return [String] the per mode
30
+ TOTALS = "Totals".freeze
31
+
32
+ # Base measure type constant
33
+ # @return [String] the measure type
34
+ BASE = "Base".freeze
35
+
36
+ # Advanced measure type constant
37
+ # @return [String] the measure type
38
+ ADVANCED = "Advanced".freeze
39
+
40
+ # Group quantity for 5-man lineups
41
+ # @return [Integer] the group quantity
42
+ FIVE_MAN = 5
43
+
44
+ # Group quantity for 4-man lineups
45
+ # @return [Integer] the group quantity
46
+ FOUR_MAN = 4
47
+
48
+ # Group quantity for 3-man lineups
49
+ # @return [Integer] the group quantity
50
+ THREE_MAN = 3
51
+
52
+ # Group quantity for 2-man lineups
53
+ # @return [Integer] the group quantity
54
+ TWO_MAN = 2
55
+
56
+ # Retrieves all league lineup visualization statistics
57
+ #
58
+ # @api public
59
+ # @example
60
+ # stats = NBA::LeagueLineupViz.all(season: 2024)
61
+ # stats.first.group_name #=> "S. Curry - K. Thompson - ..."
62
+ # @param season [Integer] the season year
63
+ # @param season_type [String] the season type
64
+ # @param per_mode [String] the per mode
65
+ # @param measure_type [String] the measure type
66
+ # @param group_quantity [Integer] the lineup size
67
+ # @param minutes_min [Integer] minimum minutes filter
68
+ # @param client [Client] the API client to use
69
+ # @return [Collection] a collection of lineup visualization statistics
70
+ def self.all(season: Utils.current_season, season_type: REGULAR_SEASON, per_mode: PER_GAME,
71
+ measure_type: BASE, group_quantity: FIVE_MAN, minutes_min: 0, client: CLIENT)
72
+ path = build_path(season, season_type: season_type, per_mode: per_mode, measure_type: measure_type,
73
+ group_quantity: group_quantity, minutes_min: minutes_min)
74
+ response = client.get(path)
75
+ parse_response(response)
76
+ end
77
+
78
+ # Builds the API request path
79
+ #
80
+ # @api private
81
+ # @return [String] the request path
82
+ def self.build_path(season, opts)
83
+ season_str = Utils.format_season(season)
84
+ "leaguelineupviz?LeagueID=00&Season=#{season_str}&SeasonType=#{opts.fetch(:season_type)}" \
85
+ "&PerMode=#{opts.fetch(:per_mode)}&MeasureType=#{opts.fetch(:measure_type)}" \
86
+ "&GroupQuantity=#{opts.fetch(:group_quantity)}&MinutesMin=#{opts.fetch(:minutes_min)}"
87
+ end
88
+ private_class_method :build_path
89
+
90
+ # Parses the API response into stat objects
91
+ #
92
+ # @api private
93
+ # @return [Collection] collection of lineup visualization stats
94
+ def self.parse_response(response)
95
+ return Collection.new if response.nil? || response.empty?
96
+
97
+ data = JSON.parse(response)
98
+ result_set = find_result_set(data)
99
+ build_stats(result_set)
100
+ end
101
+ private_class_method :parse_response
102
+
103
+ # Finds the result set by name
104
+ #
105
+ # @api private
106
+ # @return [Hash, nil] the result set hash or nil if not found
107
+ def self.find_result_set(data)
108
+ result_sets = data["resultSets"]
109
+ return unless result_sets
110
+
111
+ result_sets.find { |rs| rs["name"].eql?(LEAGUE_LINEUP_VIZ) }
112
+ end
113
+ private_class_method :find_result_set
114
+
115
+ # Builds stats collection from result set
116
+ #
117
+ # @api private
118
+ # @return [Collection] the stats collection
119
+ def self.build_stats(result_set)
120
+ return Collection.new unless result_set
121
+
122
+ headers = result_set["headers"]
123
+ rows = result_set["rowSet"]
124
+ return Collection.new unless headers && rows
125
+
126
+ Collection.new(rows.map { |row| build_stat(headers.zip(row).to_h) })
127
+ end
128
+ private_class_method :build_stats
129
+
130
+ # Builds a single stat object from API data
131
+ #
132
+ # @api private
133
+ # @return [LeagueLineupVizStat] the stat object
134
+ def self.build_stat(data)
135
+ LeagueLineupVizStat.new(**identity_info(data), **rating_info(data), **shooting_info(data),
136
+ **opponent_info(data))
137
+ end
138
+ private_class_method :build_stat
139
+
140
+ # Extracts identity information from data
141
+ #
142
+ # @api private
143
+ # @return [Hash] the identity information hash
144
+ def self.identity_info(data)
145
+ {group_id: data["GROUP_ID"], group_name: data["GROUP_NAME"],
146
+ team_id: data["TEAM_ID"], team_abbreviation: data["TEAM_ABBREVIATION"]}
147
+ end
148
+ private_class_method :identity_info
149
+
150
+ # Extracts rating information from data
151
+ #
152
+ # @api private
153
+ # @return [Hash] the rating information hash
154
+ def self.rating_info(data)
155
+ {min: data["MIN"], off_rating: data["OFF_RATING"],
156
+ def_rating: data["DEF_RATING"], net_rating: data["NET_RATING"],
157
+ pace: data["PACE"], ts_pct: data["TS_PCT"],
158
+ fta_rate: data["FTA_RATE"], tm_ast_pct: data["TM_AST_PCT"]}
159
+ end
160
+ private_class_method :rating_info
161
+
162
+ # Extracts shooting information from data
163
+ #
164
+ # @api private
165
+ # @return [Hash] the shooting information hash
166
+ def self.shooting_info(data)
167
+ {pct_fga_2pt: data["PCT_FGA_2PT"], pct_fga_3pt: data["PCT_FGA_3PT"],
168
+ pct_pts_2pt_mr: data["PCT_PTS_2PT_MR"], pct_pts_fb: data["PCT_PTS_FB"],
169
+ pct_pts_ft: data["PCT_PTS_FT"], pct_pts_paint: data["PCT_PTS_PAINT"],
170
+ pct_ast_fgm: data["PCT_AST_FGM"], pct_uast_fgm: data["PCT_UAST_FGM"]}
171
+ end
172
+ private_class_method :shooting_info
173
+
174
+ # Extracts opponent stats information from data
175
+ #
176
+ # @api private
177
+ # @return [Hash] the opponent stats information hash
178
+ def self.opponent_info(data)
179
+ {opp_fg3_pct: data["OPP_FG3_PCT"], opp_efg_pct: data["OPP_EFG_PCT"],
180
+ opp_fta_rate: data["OPP_FTA_RATE"], opp_tov_pct: data["OPP_TOV_PCT"]}
181
+ end
182
+ private_class_method :opponent_info
183
+ end
184
+ end