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,101 @@
1
+ require_relative "client"
2
+ require_relative "response_parser"
3
+ require_relative "box_score"
4
+ require_relative "utils"
5
+
6
+ module NBA
7
+ # Provides methods to retrieve traditional box score statistics
8
+ module BoxScoreTraditional
9
+ # Result set name for player stats
10
+ # @return [String] the result set name
11
+ PLAYER_STATS = "PlayerStats".freeze
12
+
13
+ # Result set name for team stats
14
+ # @return [String] the result set name
15
+ TEAM_STATS = "TeamStats".freeze
16
+
17
+ # Retrieves player box score stats for a game
18
+ #
19
+ # @api public
20
+ # @example
21
+ # stats = NBA::BoxScoreTraditional.player_stats(game: "0022400001")
22
+ # stats.each { |s| puts "#{s.player_name}: #{s.pts} pts" }
23
+ # @param game [String, Game] the game ID or Game object
24
+ # @param client [Client] the API client to use
25
+ # @return [Collection] a collection of player box score stats
26
+ def self.player_stats(game:, client: CLIENT)
27
+ path = "boxscoretraditionalv2?GameID=#{Utils.extract_id(game)}"
28
+ ResponseParser.parse(client.get(path), result_set: PLAYER_STATS) { |data| build_player_stat(data) }
29
+ end
30
+
31
+ # Retrieves team box score stats for a game
32
+ #
33
+ # @api public
34
+ # @example
35
+ # stats = NBA::BoxScoreTraditional.team_stats(game: "0022400001")
36
+ # stats.each { |s| puts "#{s.team_name}: #{s.pts} pts" }
37
+ # @param game [String, Game] the game ID or Game object
38
+ # @param client [Client] the API client to use
39
+ # @return [Collection] a collection of team box score stats
40
+ def self.team_stats(game:, client: CLIENT)
41
+ path = "boxscoretraditionalv2?GameID=#{Utils.extract_id(game)}"
42
+ ResponseParser.parse(client.get(path), result_set: TEAM_STATS) { |data| build_team_stat(data) }
43
+ end
44
+
45
+ # Builds a player stat object from API data
46
+ # @api private
47
+ # @return [BoxScorePlayerStat]
48
+ def self.build_player_stat(data)
49
+ BoxScorePlayerStat.new(**player_identity(data), **shooting_stats(data), **counting_stats(data))
50
+ end
51
+ private_class_method :build_player_stat
52
+
53
+ # Builds a team stat object from API data
54
+ # @api private
55
+ # @return [BoxScoreTeamStat]
56
+ def self.build_team_stat(data)
57
+ BoxScoreTeamStat.new(**team_identity(data), **shooting_stats(data), **counting_stats(data))
58
+ end
59
+ private_class_method :build_team_stat
60
+
61
+ # Extracts player identity attributes from data
62
+ # @api private
63
+ # @return [Hash]
64
+ def self.player_identity(data)
65
+ {game_id: data["GAME_ID"], team_id: data["TEAM_ID"], team_abbreviation: data["TEAM_ABBREVIATION"],
66
+ team_city: data["TEAM_CITY"], player_id: data["PLAYER_ID"], player_name: data["PLAYER_NAME"],
67
+ start_position: data["START_POSITION"], comment: data["COMMENT"], min: data["MIN"]}
68
+ end
69
+ private_class_method :player_identity
70
+
71
+ # Extracts team identity attributes from data
72
+ # @api private
73
+ # @return [Hash]
74
+ def self.team_identity(data)
75
+ {game_id: data["GAME_ID"], team_id: data["TEAM_ID"], team_name: data["TEAM_NAME"],
76
+ team_abbreviation: data["TEAM_ABBREVIATION"], team_city: data["TEAM_CITY"], min: data["MIN"]}
77
+ end
78
+ private_class_method :team_identity
79
+
80
+ # Extracts shooting statistics from data
81
+ # @api private
82
+ # @return [Hash]
83
+ def self.shooting_stats(data)
84
+ {fgm: data["FGM"], fga: data["FGA"], fg_pct: data["FG_PCT"],
85
+ fg3m: data["FG3M"], fg3a: data["FG3A"], fg3_pct: data["FG3_PCT"],
86
+ ftm: data["FTM"], fta: data["FTA"], ft_pct: data["FT_PCT"]}
87
+ end
88
+ private_class_method :shooting_stats
89
+
90
+ # Extracts counting statistics from data
91
+ # @api private
92
+ # @return [Hash]
93
+ def self.counting_stats(data)
94
+ {oreb: data["OREB"], dreb: data["DREB"], reb: data["REB"],
95
+ ast: data["AST"], stl: data["STL"], blk: data["BLK"],
96
+ tov: data["TO"], pf: data["PF"], pts: data["PTS"],
97
+ plus_minus: data["PLUS_MINUS"]}
98
+ end
99
+ private_class_method :counting_stats
100
+ end
101
+ end
@@ -0,0 +1,195 @@
1
+ require "json"
2
+ require_relative "client"
3
+ require_relative "collection"
4
+ require_relative "box_score_player_stat"
5
+ require_relative "box_score_team_stat"
6
+ require_relative "box_score_starter_bench_stat"
7
+ require_relative "box_score_v3_helpers"
8
+ require_relative "utils"
9
+
10
+ module NBA
11
+ # Provides methods to retrieve traditional box score statistics using V3 API
12
+ module BoxScoreTraditionalV3
13
+ # @return [String] JSON key for traditional box score data
14
+ BOX_SCORE_KEY = "boxScoreTraditional".freeze
15
+ # @return [String] JSON key for player statistics
16
+ PLAYER_STATS = "PlayerStats".freeze
17
+ # @return [String] JSON key for team statistics
18
+ TEAM_STATS = "TeamStats".freeze
19
+ # @return [String] JSON key for starter/bench statistics
20
+ STARTER_BENCH_STATS = "TeamStarterBenchStats".freeze
21
+
22
+ # Retrieves player box score stats for a game
23
+ #
24
+ # @api public
25
+ # @example
26
+ # stats = NBA::BoxScoreTraditionalV3.player_stats(game: "0022400001")
27
+ # stats.each { |stat| puts "#{stat.player_name}: #{stat.pts} pts" }
28
+ # @param game [String, Integer] the game ID
29
+ # @param start_period [Integer] the starting period
30
+ # @param end_period [Integer] the ending period
31
+ # @param client [Client] the API client to use
32
+ # @return [Collection] a collection of player stats
33
+ def self.player_stats(game:, start_period: 0, end_period: 0, client: CLIENT)
34
+ game_id = Utils.extract_id(game)
35
+ response = client.get(build_path(game_id, start_period, end_period))
36
+ parse_player_response(response, game_id)
37
+ end
38
+
39
+ # Retrieves team box score stats for a game
40
+ #
41
+ # @api public
42
+ # @example
43
+ # stats = NBA::BoxScoreTraditionalV3.team_stats(game: "0022400001")
44
+ # stats.each { |stat| puts "#{stat.team_abbreviation}: #{stat.pts} pts" }
45
+ # @param game [String, Integer] the game ID
46
+ # @param start_period [Integer] the starting period
47
+ # @param end_period [Integer] the ending period
48
+ # @param client [Client] the API client to use
49
+ # @return [Collection] a collection of team stats
50
+ def self.team_stats(game:, start_period: 0, end_period: 0, client: CLIENT)
51
+ game_id = Utils.extract_id(game)
52
+ response = client.get(build_path(game_id, start_period, end_period))
53
+ parse_team_response(response, game_id)
54
+ end
55
+
56
+ # Retrieves starter vs bench breakdown stats for a game
57
+ #
58
+ # @api public
59
+ # @example
60
+ # stats = NBA::BoxScoreTraditionalV3.starter_bench_stats(game: "0022400001")
61
+ # stats.each { |stat| puts "#{stat.team_abbreviation} #{stat.starters_bench}: #{stat.pts} pts" }
62
+ # @param game [String, Integer] the game ID
63
+ # @param start_period [Integer] the starting period
64
+ # @param end_period [Integer] the ending period
65
+ # @param client [Client] the API client to use
66
+ # @return [Collection] a collection of starter/bench stats
67
+ def self.starter_bench_stats(game:, start_period: 0, end_period: 0, client: CLIENT)
68
+ game_id = Utils.extract_id(game)
69
+ response = client.get(build_path(game_id, start_period, end_period))
70
+ parse_starter_bench_response(response, game_id)
71
+ end
72
+
73
+ # Builds the API request path
74
+ # @api private
75
+ # @return [String] the request path
76
+ def self.build_path(game_id, start_period, end_period)
77
+ "boxscoretraditionalv3?GameID=#{game_id}&StartPeriod=#{start_period}" \
78
+ "&EndPeriod=#{end_period}&StartRange=0&EndRange=0&RangeType=0"
79
+ end
80
+ private_class_method :build_path
81
+
82
+ # Parses the API response into player stat objects
83
+ # @api private
84
+ # @return [Collection] collection of player stats
85
+ def self.parse_player_response(response, game_id)
86
+ return Collection.new unless response
87
+
88
+ data = JSON.parse(response)
89
+ players = BoxScoreV3Helpers.extract_players(data, BOX_SCORE_KEY)
90
+ return Collection.new unless players
91
+
92
+ Collection.new(players.map { |p| build_player_stat(p, game_id) })
93
+ end
94
+ private_class_method :parse_player_response
95
+
96
+ # Parses the API response into team stat objects
97
+ # @api private
98
+ # @return [Collection] collection of team stats
99
+ def self.parse_team_response(response, game_id)
100
+ return Collection.new unless response
101
+
102
+ data = JSON.parse(response)
103
+ teams = BoxScoreV3Helpers.extract_teams(data, BOX_SCORE_KEY)
104
+ return Collection.new unless teams
105
+
106
+ Collection.new(teams.map { |t| build_team_stat(t, game_id) })
107
+ end
108
+ private_class_method :parse_team_response
109
+
110
+ # Parses the API response into starter/bench stat objects
111
+ # @api private
112
+ # @return [Collection] collection of starter/bench stats
113
+ def self.parse_starter_bench_response(response, game_id)
114
+ return Collection.new unless response
115
+
116
+ data = JSON.parse(response)
117
+ stat_data = extract_starter_bench(data)
118
+ return Collection.new unless stat_data
119
+
120
+ Collection.new(stat_data.map { |d| build_starter_bench_stat(d, game_id) })
121
+ end
122
+ private_class_method :parse_starter_bench_response
123
+
124
+ # Extracts starter/bench data from parsed response
125
+ # @api private
126
+ # @return [Array, nil] array of starter/bench data or nil
127
+ def self.extract_starter_bench(data)
128
+ box_score = data[BOX_SCORE_KEY]
129
+ return unless box_score
130
+
131
+ stats = []
132
+ add_team_starter_bench(stats, box_score, "homeTeam")
133
+ add_team_starter_bench(stats, box_score, "awayTeam")
134
+ stats
135
+ end
136
+ private_class_method :extract_starter_bench
137
+
138
+ # Adds starter/bench data for a team
139
+ # @api private
140
+ # @return [void]
141
+ def self.add_team_starter_bench(stats, box_score, team_key)
142
+ team = box_score[team_key]
143
+ return unless team
144
+
145
+ %w[starters bench].each do |group|
146
+ group_stats = team.dig("statistics", group)
147
+ stats << {team: team, group: group, stats: group_stats} if group_stats
148
+ end
149
+ end
150
+ private_class_method :add_team_starter_bench
151
+
152
+ # Builds a player stat object from raw data
153
+ # @api private
154
+ # @return [BoxScorePlayerStat] the player stat object
155
+ def self.build_player_stat(player, game_id)
156
+ stats = player.fetch("statistics", {})
157
+ BoxScorePlayerStat.new(
158
+ **BoxScoreV3Helpers.player_identity(player, game_id),
159
+ min: stats["minutes"],
160
+ **BoxScoreV3Helpers.shooting_stats(stats),
161
+ **BoxScoreV3Helpers.counting_stats(stats)
162
+ )
163
+ end
164
+ private_class_method :build_player_stat
165
+
166
+ # Builds a team stat object from raw data
167
+ # @api private
168
+ # @return [BoxScoreTeamStat] the team stat object
169
+ def self.build_team_stat(team, game_id)
170
+ stats = team.fetch("statistics", {})
171
+ BoxScoreTeamStat.new(
172
+ **BoxScoreV3Helpers.team_identity(team, game_id),
173
+ min: stats["minutes"],
174
+ **BoxScoreV3Helpers.shooting_stats(stats),
175
+ **BoxScoreV3Helpers.counting_stats(stats)
176
+ )
177
+ end
178
+ private_class_method :build_team_stat
179
+
180
+ # Builds a starter/bench stat object from raw data
181
+ # @api private
182
+ # @return [BoxScoreStarterBenchStat] the starter/bench stat object
183
+ def self.build_starter_bench_stat(data, game_id)
184
+ team = data.fetch(:team)
185
+ stats = data.fetch(:stats)
186
+ BoxScoreStarterBenchStat.new(
187
+ **BoxScoreV3Helpers.team_identity(team, game_id),
188
+ starters_bench: data.fetch(:group), min: stats["minutes"],
189
+ **BoxScoreV3Helpers.shooting_stats(stats),
190
+ **BoxScoreV3Helpers.counting_stats(stats)
191
+ )
192
+ end
193
+ private_class_method :build_starter_bench_stat
194
+ end
195
+ end
@@ -0,0 +1,102 @@
1
+ require_relative "client"
2
+ require_relative "response_parser"
3
+ require_relative "box_score_usage_player_stat"
4
+ require_relative "box_score_usage_team_stat"
5
+ require_relative "utils"
6
+
7
+ module NBA
8
+ # Provides methods to retrieve usage box score statistics
9
+ module BoxScoreUsage
10
+ # Result set name for player stats
11
+ # @return [String] the result set name
12
+ PLAYER_STATS = "PlayerStats".freeze
13
+
14
+ # Result set name for team stats
15
+ # @return [String] the result set name
16
+ TEAM_STATS = "TeamStats".freeze
17
+
18
+ # Retrieves player usage box score stats for a game
19
+ #
20
+ # @api public
21
+ # @example
22
+ # stats = NBA::BoxScoreUsage.player_stats(game: "0022400001")
23
+ # stats.each { |s| puts "#{s.player_name}: #{s.usg_pct}% usage" }
24
+ # @param game [String, Game] the game ID or Game object
25
+ # @param client [Client] the API client to use
26
+ # @return [Collection] a collection of player usage box score stats
27
+ def self.player_stats(game:, client: CLIENT)
28
+ path = "boxscoreusagev2?GameID=#{Utils.extract_id(game)}"
29
+ ResponseParser.parse(client.get(path), result_set: PLAYER_STATS) { |data| build_player_stat(data) }
30
+ end
31
+
32
+ # Retrieves team usage box score stats for a game
33
+ #
34
+ # @api public
35
+ # @example
36
+ # stats = NBA::BoxScoreUsage.team_stats(game: "0022400001")
37
+ # stats.each { |s| puts "#{s.team_name}: #{s.pct_pts}% of pts" }
38
+ # @param game [String, Game] the game ID or Game object
39
+ # @param client [Client] the API client to use
40
+ # @return [Collection] a collection of team usage box score stats
41
+ def self.team_stats(game:, client: CLIENT)
42
+ path = "boxscoreusagev2?GameID=#{Utils.extract_id(game)}"
43
+ ResponseParser.parse(client.get(path), result_set: TEAM_STATS) { |data| build_team_stat(data) }
44
+ end
45
+
46
+ # Builds a player usage stat from API data
47
+ # @api private
48
+ # @return [BoxScoreUsagePlayerStat]
49
+ def self.build_player_stat(data)
50
+ BoxScoreUsagePlayerStat.new(**player_identity(data), **shooting_usage(data), **other_usage(data))
51
+ end
52
+ private_class_method :build_player_stat
53
+
54
+ # Builds a team usage stat from API data
55
+ # @api private
56
+ # @return [BoxScoreUsageTeamStat]
57
+ def self.build_team_stat(data)
58
+ BoxScoreUsageTeamStat.new(**team_identity(data), **shooting_usage(data), **other_usage(data))
59
+ end
60
+ private_class_method :build_team_stat
61
+
62
+ # Extracts player identity attributes from data
63
+ # @api private
64
+ # @return [Hash]
65
+ def self.player_identity(data)
66
+ {game_id: data["GAME_ID"], team_id: data["TEAM_ID"], team_abbreviation: data["TEAM_ABBREVIATION"],
67
+ team_city: data["TEAM_CITY"], player_id: data["PLAYER_ID"], player_name: data["PLAYER_NAME"],
68
+ start_position: data["START_POSITION"], comment: data["COMMENT"], min: data["MIN"]}
69
+ end
70
+ private_class_method :player_identity
71
+
72
+ # Extracts team identity attributes from data
73
+ # @api private
74
+ # @return [Hash]
75
+ def self.team_identity(data)
76
+ {game_id: data["GAME_ID"], team_id: data["TEAM_ID"], team_name: data["TEAM_NAME"],
77
+ team_abbreviation: data["TEAM_ABBREVIATION"], team_city: data["TEAM_CITY"], min: data["MIN"]}
78
+ end
79
+ private_class_method :team_identity
80
+
81
+ # Extracts shooting usage statistics from data
82
+ # @api private
83
+ # @return [Hash]
84
+ def self.shooting_usage(data)
85
+ {usg_pct: data["USG_PCT"], pct_fgm: data["PCT_FGM"], pct_fga: data["PCT_FGA"],
86
+ pct_fg3m: data["PCT_FG3M"], pct_fg3a: data["PCT_FG3A"],
87
+ pct_ftm: data["PCT_FTM"], pct_fta: data["PCT_FTA"]}
88
+ end
89
+ private_class_method :shooting_usage
90
+
91
+ # Extracts other usage statistics from data
92
+ # @api private
93
+ # @return [Hash]
94
+ def self.other_usage(data)
95
+ {pct_oreb: data["PCT_OREB"], pct_dreb: data["PCT_DREB"], pct_reb: data["PCT_REB"],
96
+ pct_ast: data["PCT_AST"], pct_tov: data["PCT_TOV"], pct_stl: data["PCT_STL"],
97
+ pct_blk: data["PCT_BLK"], pct_blka: data["PCT_BLKA"],
98
+ pct_pf: data["PCT_PF"], pct_pfd: data["PCT_PFD"], pct_pts: data["PCT_PTS"]}
99
+ end
100
+ private_class_method :other_usage
101
+ end
102
+ end
@@ -0,0 +1,265 @@
1
+ require "equalizer"
2
+ require "shale"
3
+
4
+ module NBA
5
+ # Represents a player's usage box score statistics for a game
6
+ class BoxScoreUsagePlayerStat < Shale::Mapper
7
+ include Equalizer.new(:game_id, :player_id)
8
+
9
+ # @!attribute [rw] game_id
10
+ # Returns the game ID
11
+ # @api public
12
+ # @example
13
+ # stat.game_id #=> "0022400565"
14
+ # @return [String] the game ID
15
+ attribute :game_id, Shale::Type::String
16
+
17
+ # @!attribute [rw] team_id
18
+ # Returns the team ID
19
+ # @api public
20
+ # @example
21
+ # stat.team_id #=> 1610612744
22
+ # @return [Integer] the team ID
23
+ attribute :team_id, Shale::Type::Integer
24
+
25
+ # @!attribute [rw] team_abbreviation
26
+ # Returns the team abbreviation
27
+ # @api public
28
+ # @example
29
+ # stat.team_abbreviation #=> "GSW"
30
+ # @return [String] the team abbreviation
31
+ attribute :team_abbreviation, Shale::Type::String
32
+
33
+ # @!attribute [rw] team_city
34
+ # Returns the team city
35
+ # @api public
36
+ # @example
37
+ # stat.team_city #=> "Golden State"
38
+ # @return [String] the team city
39
+ attribute :team_city, Shale::Type::String
40
+
41
+ # @!attribute [rw] player_id
42
+ # Returns the player ID
43
+ # @api public
44
+ # @example
45
+ # stat.player_id #=> 201939
46
+ # @return [Integer] the player ID
47
+ attribute :player_id, Shale::Type::Integer
48
+
49
+ # @!attribute [rw] player_name
50
+ # Returns the player name
51
+ # @api public
52
+ # @example
53
+ # stat.player_name #=> "Stephen Curry"
54
+ # @return [String] the player name
55
+ attribute :player_name, Shale::Type::String
56
+
57
+ # @!attribute [rw] start_position
58
+ # Returns the starting position
59
+ # @api public
60
+ # @example
61
+ # stat.start_position #=> "G"
62
+ # @return [String] the starting position
63
+ attribute :start_position, Shale::Type::String
64
+
65
+ # @!attribute [rw] comment
66
+ # Returns any comment (e.g., "DNP - Rest")
67
+ # @api public
68
+ # @example
69
+ # stat.comment #=> "DNP - Rest"
70
+ # @return [String] the comment
71
+ attribute :comment, Shale::Type::String
72
+
73
+ # @!attribute [rw] min
74
+ # Returns minutes played
75
+ # @api public
76
+ # @example
77
+ # stat.min #=> "35:24"
78
+ # @return [String] the minutes played
79
+ attribute :min, Shale::Type::String
80
+
81
+ # @!attribute [rw] usg_pct
82
+ # Returns usage percentage
83
+ # @api public
84
+ # @example
85
+ # stat.usg_pct #=> 0.285
86
+ # @return [Float] usage percentage
87
+ attribute :usg_pct, Shale::Type::Float
88
+
89
+ # @!attribute [rw] pct_fgm
90
+ # Returns percentage of team field goals made
91
+ # @api public
92
+ # @example
93
+ # stat.pct_fgm #=> 0.24
94
+ # @return [Float] the percentage
95
+ attribute :pct_fgm, Shale::Type::Float
96
+
97
+ # @!attribute [rw] pct_fga
98
+ # Returns percentage of team field goals attempted
99
+ # @api public
100
+ # @example
101
+ # stat.pct_fga #=> 0.27
102
+ # @return [Float] the percentage
103
+ attribute :pct_fga, Shale::Type::Float
104
+
105
+ # @!attribute [rw] pct_fg3m
106
+ # Returns percentage of team 3-pointers made
107
+ # @api public
108
+ # @example
109
+ # stat.pct_fg3m #=> 0.35
110
+ # @return [Float] the percentage
111
+ attribute :pct_fg3m, Shale::Type::Float
112
+
113
+ # @!attribute [rw] pct_fg3a
114
+ # Returns percentage of team 3-pointers attempted
115
+ # @api public
116
+ # @example
117
+ # stat.pct_fg3a #=> 0.38
118
+ # @return [Float] the percentage
119
+ attribute :pct_fg3a, Shale::Type::Float
120
+
121
+ # @!attribute [rw] pct_ftm
122
+ # Returns percentage of team free throws made
123
+ # @api public
124
+ # @example
125
+ # stat.pct_ftm #=> 0.28
126
+ # @return [Float] the percentage
127
+ attribute :pct_ftm, Shale::Type::Float
128
+
129
+ # @!attribute [rw] pct_fta
130
+ # Returns percentage of team free throws attempted
131
+ # @api public
132
+ # @example
133
+ # stat.pct_fta #=> 0.30
134
+ # @return [Float] the percentage
135
+ attribute :pct_fta, Shale::Type::Float
136
+
137
+ # @!attribute [rw] pct_oreb
138
+ # Returns percentage of team offensive rebounds
139
+ # @api public
140
+ # @example
141
+ # stat.pct_oreb #=> 0.15
142
+ # @return [Float] the percentage
143
+ attribute :pct_oreb, Shale::Type::Float
144
+
145
+ # @!attribute [rw] pct_dreb
146
+ # Returns percentage of team defensive rebounds
147
+ # @api public
148
+ # @example
149
+ # stat.pct_dreb #=> 0.22
150
+ # @return [Float] the percentage
151
+ attribute :pct_dreb, Shale::Type::Float
152
+
153
+ # @!attribute [rw] pct_reb
154
+ # Returns percentage of team total rebounds
155
+ # @api public
156
+ # @example
157
+ # stat.pct_reb #=> 0.19
158
+ # @return [Float] the percentage
159
+ attribute :pct_reb, Shale::Type::Float
160
+
161
+ # @!attribute [rw] pct_ast
162
+ # Returns percentage of team assists
163
+ # @api public
164
+ # @example
165
+ # stat.pct_ast #=> 0.32
166
+ # @return [Float] the percentage
167
+ attribute :pct_ast, Shale::Type::Float
168
+
169
+ # @!attribute [rw] pct_tov
170
+ # Returns percentage of team turnovers
171
+ # @api public
172
+ # @example
173
+ # stat.pct_tov #=> 0.18
174
+ # @return [Float] the percentage
175
+ attribute :pct_tov, Shale::Type::Float
176
+
177
+ # @!attribute [rw] pct_stl
178
+ # Returns percentage of team steals
179
+ # @api public
180
+ # @example
181
+ # stat.pct_stl #=> 0.25
182
+ # @return [Float] the percentage
183
+ attribute :pct_stl, Shale::Type::Float
184
+
185
+ # @!attribute [rw] pct_blk
186
+ # Returns percentage of team blocks
187
+ # @api public
188
+ # @example
189
+ # stat.pct_blk #=> 0.20
190
+ # @return [Float] the percentage
191
+ attribute :pct_blk, Shale::Type::Float
192
+
193
+ # @!attribute [rw] pct_blka
194
+ # Returns percentage of team blocked attempts
195
+ # @api public
196
+ # @example
197
+ # stat.pct_blka #=> 0.12
198
+ # @return [Float] the percentage
199
+ attribute :pct_blka, Shale::Type::Float
200
+
201
+ # @!attribute [rw] pct_pf
202
+ # Returns percentage of team personal fouls
203
+ # @api public
204
+ # @example
205
+ # stat.pct_pf #=> 0.16
206
+ # @return [Float] the percentage
207
+ attribute :pct_pf, Shale::Type::Float
208
+
209
+ # @!attribute [rw] pct_pfd
210
+ # Returns percentage of team personal fouls drawn
211
+ # @api public
212
+ # @example
213
+ # stat.pct_pfd #=> 0.21
214
+ # @return [Float] the percentage
215
+ attribute :pct_pfd, Shale::Type::Float
216
+
217
+ # @!attribute [rw] pct_pts
218
+ # Returns percentage of team points
219
+ # @api public
220
+ # @example
221
+ # stat.pct_pts #=> 0.26
222
+ # @return [Float] the percentage
223
+ attribute :pct_pts, Shale::Type::Float
224
+
225
+ # Returns the player object
226
+ #
227
+ # @api public
228
+ # @example
229
+ # stat.player #=> #<NBA::Player>
230
+ # @return [Player, nil] the player object
231
+ def player
232
+ Players.find(player_id)
233
+ end
234
+
235
+ # Returns the team object
236
+ #
237
+ # @api public
238
+ # @example
239
+ # stat.team #=> #<NBA::Team>
240
+ # @return [Team, nil] the team object
241
+ def team
242
+ Teams.find(team_id)
243
+ end
244
+
245
+ # Returns whether the player started the game
246
+ #
247
+ # @api public
248
+ # @example
249
+ # stat.starter? #=> true
250
+ # @return [Boolean] true if the player started
251
+ def starter?
252
+ !start_position.nil? && !start_position.empty?
253
+ end
254
+
255
+ # Returns the game object for this box score
256
+ #
257
+ # @api public
258
+ # @example
259
+ # stat.game #=> #<NBA::Game>
260
+ # @return [Game, nil] the game object
261
+ def game
262
+ Games.find(game_id)
263
+ end
264
+ end
265
+ end