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,358 @@
1
+ require "json"
2
+ require_relative "client"
3
+ require_relative "collection"
4
+ require_relative "cume_stats_player_game"
5
+ require_relative "cume_stats_player_total"
6
+ require_relative "league"
7
+ require_relative "utils"
8
+
9
+ module NBA
10
+ # Provides methods to retrieve cumulative player statistics
11
+ module CumeStatsPlayer
12
+ # Result set name for game-by-game stats
13
+ # @return [String] the result set name
14
+ GAME_BY_GAME_STATS = "GameByGameStats".freeze
15
+
16
+ # Result set name for total player stats
17
+ # @return [String] the result set name
18
+ TOTAL_PLAYER_STATS = "TotalPlayerStats".freeze
19
+
20
+ # Retrieves cumulative player statistics for specific games
21
+ #
22
+ # @api public
23
+ # @example
24
+ # stats = NBA::CumeStatsPlayer.find(
25
+ # player: 201939,
26
+ # game_ids: ["0022400001", "0022400002"],
27
+ # season: "2024-25",
28
+ # season_type: "Regular Season"
29
+ # )
30
+ # stats[:game_by_game].each { |g| puts "#{g.game_date}: #{g.pts} PTS" }
31
+ # puts "Total: #{stats[:total].pts} PTS"
32
+ # @param player [Integer, Player] the player ID or Player object
33
+ # @param game_ids [Array<String>, String] game IDs as array or pipe-separated string
34
+ # @param season [String] the season (e.g., "2024-25")
35
+ # @param season_type [String] the season type (default "Regular Season")
36
+ # @param league [String, League] the league ID or League object (default NBA)
37
+ # @param client [Client] the API client to use
38
+ # @return [Hash, nil] hash with :game_by_game and :total keys, or nil
39
+ def self.find(player:, game_ids:, season:, season_type: "Regular Season", league: League::NBA, client: CLIENT)
40
+ player_id = Utils.extract_id(player)
41
+ league_id = Utils.extract_league_id(league)
42
+ game_ids_param = format_game_ids(game_ids)
43
+
44
+ path = "cumestatsplayer?PlayerID=#{player_id}&GameIDs=#{game_ids_param}" \
45
+ "&LeagueID=#{league_id}&Season=#{season}&SeasonType=#{season_type}"
46
+ response = client.get(path)
47
+ parse_response(response)
48
+ end
49
+
50
+ # Parses the API response
51
+ #
52
+ # @api private
53
+ # @param response [String] the JSON response body
54
+ # @return [Hash, nil] hash with :game_by_game and :total keys, or nil
55
+ def self.parse_response(response)
56
+ return unless response
57
+
58
+ data = JSON.parse(response)
59
+ game_by_game = parse_game_by_game(data)
60
+ total = parse_total(data)
61
+
62
+ return unless game_by_game && total
63
+
64
+ {game_by_game: game_by_game, total: total}
65
+ end
66
+ private_class_method :parse_response
67
+
68
+ # Parses the game-by-game stats result set
69
+ #
70
+ # @api private
71
+ # @param data [Hash] the parsed JSON data
72
+ # @return [Collection, nil] collection of game stats, or nil
73
+ def self.parse_game_by_game(data)
74
+ result_set = find_result_set(data, GAME_BY_GAME_STATS)
75
+ return unless result_set
76
+
77
+ headers = result_set["headers"]
78
+ rows = result_set["rowSet"]
79
+ return unless headers && rows
80
+
81
+ games = rows.map { |row| build_game(headers, row) }
82
+ Collection.new(games)
83
+ end
84
+ private_class_method :parse_game_by_game
85
+
86
+ # Parses the total player stats result set
87
+ #
88
+ # @api private
89
+ # @param data [Hash] the parsed JSON data
90
+ # @return [CumeStatsPlayerTotal, nil] total stats, or nil
91
+ def self.parse_total(data)
92
+ result_set = find_result_set(data, TOTAL_PLAYER_STATS)
93
+ return unless result_set
94
+
95
+ headers = result_set["headers"]
96
+ row = result_set.dig("rowSet", 0)
97
+ return unless headers && row
98
+
99
+ build_total(headers, row)
100
+ end
101
+ private_class_method :parse_total
102
+
103
+ # Finds a result set by name
104
+ #
105
+ # @api private
106
+ # @param data [Hash] the parsed JSON data
107
+ # @param name [String] the result set name
108
+ # @return [Hash, nil] the result set
109
+ def self.find_result_set(data, name)
110
+ result_sets = data["resultSets"]
111
+ return unless result_sets
112
+
113
+ result_sets.find { |rs| rs["name"].eql?(name) }
114
+ end
115
+ private_class_method :find_result_set
116
+
117
+ # Builds a game stat object from a row
118
+ #
119
+ # @api private
120
+ # @param headers [Array<String>] the column headers
121
+ # @param row [Array] the row data
122
+ # @return [CumeStatsPlayerGame] the game stat object
123
+ def self.build_game(headers, row)
124
+ data = headers.zip(row).to_h
125
+ CumeStatsPlayerGame.new(**GameAttributes.extract(data))
126
+ end
127
+ private_class_method :build_game
128
+
129
+ # Builds a total stat object from a row
130
+ #
131
+ # @api private
132
+ # @param headers [Array<String>] the column headers
133
+ # @param row [Array] the row data
134
+ # @return [CumeStatsPlayerTotal] the total stat object
135
+ def self.build_total(headers, row)
136
+ data = headers.zip(row).to_h
137
+ CumeStatsPlayerTotal.new(**TotalAttributes.extract(data))
138
+ end
139
+ private_class_method :build_total
140
+
141
+ # Formats game IDs as a pipe-separated string
142
+ #
143
+ # @api private
144
+ # @param game_ids [Array<String>, String] game IDs as array or string
145
+ # @return [String] pipe-separated game IDs
146
+ def self.format_game_ids(game_ids)
147
+ game_ids.instance_of?(Array) ? game_ids.join("|") : game_ids
148
+ end
149
+ private_class_method :format_game_ids
150
+
151
+ # Extracts game attributes from row data
152
+ # @api private
153
+ module GameAttributes
154
+ # Extracts all game attributes from data
155
+ # @api private
156
+ # @param data [Hash] the row data
157
+ # @return [Hash] extracted attributes
158
+ def self.extract(data)
159
+ identity(data).merge(stats(data))
160
+ end
161
+
162
+ # Extracts identity attributes from data
163
+ # @api private
164
+ # @param data [Hash] the row data
165
+ # @return [Hash] identity attributes
166
+ def self.identity(data)
167
+ {
168
+ game_id: data["GAME_ID"],
169
+ matchup: data["MATCHUP"],
170
+ game_date: data["GAME_DATE"],
171
+ vs_team_id: data["VS_TEAM_ID"],
172
+ vs_team_city: data["VS_TEAM_CITY"],
173
+ vs_team_name: data["VS_TEAM_NAME"]
174
+ }
175
+ end
176
+
177
+ # Extracts stat attributes from data
178
+ # @api private
179
+ # @param data [Hash] the row data
180
+ # @return [Hash] stat attributes
181
+ def self.stats(data)
182
+ time(data).merge(shooting(data)).merge(rebounds(data)).merge(other(data))
183
+ end
184
+
185
+ # Extracts time attributes from data
186
+ # @api private
187
+ # @param data [Hash] the row data
188
+ # @return [Hash] time attributes
189
+ def self.time(data)
190
+ {min: data["MIN"], sec: data["SEC"]}
191
+ end
192
+
193
+ # Extracts shooting attributes from data
194
+ # @api private
195
+ # @param data [Hash] the row data
196
+ # @return [Hash] shooting attributes
197
+ def self.shooting(data)
198
+ {
199
+ fgm: data["FGM"], fga: data["FGA"], fg_pct: data["FG_PCT"],
200
+ fg3m: data["FG3M"], fg3a: data["FG3A"], fg3_pct: data["FG3_PCT"],
201
+ ftm: data["FTM"], fta: data["FTA"], ft_pct: data["FT_PCT"]
202
+ }
203
+ end
204
+
205
+ # Extracts rebound attributes from data
206
+ # @api private
207
+ # @param data [Hash] the row data
208
+ # @return [Hash] rebound attributes
209
+ def self.rebounds(data)
210
+ {oreb: data["OREB"], dreb: data["DREB"], reb: data["REB"]}
211
+ end
212
+
213
+ # Extracts other stat attributes from data
214
+ # @api private
215
+ # @param data [Hash] the row data
216
+ # @return [Hash] other stat attributes
217
+ def self.other(data)
218
+ {
219
+ ast: data["AST"], pf: data["PF"], stl: data["STL"],
220
+ tov: data["TOV"], blk: data["BLK"], pts: data["PTS"]
221
+ }
222
+ end
223
+ end
224
+
225
+ # Extracts total attributes from row data
226
+ # @api private
227
+ module TotalAttributes
228
+ # Extracts all total attributes from data
229
+ # @api private
230
+ # @param data [Hash] the row data
231
+ # @return [Hash] extracted attributes
232
+ def self.extract(data)
233
+ identity(data).merge(totals(data)).merge(averages(data)).merge(maximums(data))
234
+ end
235
+
236
+ # Extracts identity attributes from data
237
+ # @api private
238
+ # @param data [Hash] the row data
239
+ # @return [Hash] identity attributes
240
+ def self.identity(data)
241
+ {
242
+ player_id: data["PLAYER_ID"], player_name: data["PLAYER_NAME"],
243
+ jersey_num: data["JERSEY_NUM"], season: data["SEASON"],
244
+ gp: data["GP"], gs: data["GS"],
245
+ actual_minutes: data["ACTUAL_MINUTES"], actual_seconds: data["ACTUAL_SECONDS"]
246
+ }
247
+ end
248
+
249
+ # Extracts total stat attributes from data
250
+ # @api private
251
+ # @param data [Hash] the row data
252
+ # @return [Hash] total stat attributes
253
+ def self.totals(data)
254
+ shooting(data).merge(rebounds_and_other(data))
255
+ end
256
+
257
+ # Extracts shooting attributes from data
258
+ # @api private
259
+ # @param data [Hash] the row data
260
+ # @return [Hash] shooting attributes
261
+ def self.shooting(data)
262
+ {
263
+ fgm: data["FGM"], fga: data["FGA"], fg_pct: data["FG_PCT"],
264
+ fg3m: data["FG3M"], fg3a: data["FG3A"], fg3_pct: data["FG3_PCT"],
265
+ ftm: data["FTM"], fta: data["FTA"], ft_pct: data["FT_PCT"]
266
+ }
267
+ end
268
+
269
+ # Extracts rebound and other stat attributes from data
270
+ # @api private
271
+ # @param data [Hash] the row data
272
+ # @return [Hash] rebound and other stat attributes
273
+ def self.rebounds_and_other(data)
274
+ {
275
+ oreb: data["OREB"], dreb: data["DREB"], tot_reb: data["TOT_REB"],
276
+ ast: data["AST"], pf: data["PF"], stl: data["STL"],
277
+ tov: data["TOV"], blk: data["BLK"], pts: data["PTS"]
278
+ }
279
+ end
280
+
281
+ # Extracts average stat attributes from data
282
+ # @api private
283
+ # @param data [Hash] the row data
284
+ # @return [Hash] average stat attributes
285
+ def self.averages(data)
286
+ avg_time(data).merge(avg_shooting(data)).merge(avg_other(data))
287
+ end
288
+
289
+ # Extracts average time attributes from data
290
+ # @api private
291
+ # @param data [Hash] the row data
292
+ # @return [Hash] average time attributes
293
+ def self.avg_time(data)
294
+ {avg_min: data["AVG_MIN"], avg_sec: data["AVG_SEC"]}
295
+ end
296
+
297
+ # Extracts average shooting attributes from data
298
+ # @api private
299
+ # @param data [Hash] the row data
300
+ # @return [Hash] average shooting attributes
301
+ def self.avg_shooting(data)
302
+ {
303
+ avg_fgm: data["AVG_FGM"], avg_fga: data["AVG_FGA"],
304
+ avg_fg3m: data["AVG_FG3M"], avg_fg3a: data["AVG_FG3A"],
305
+ avg_ftm: data["AVG_FTM"], avg_fta: data["AVG_FTA"],
306
+ avg_oreb: data["AVG_OREB"], avg_dreb: data["AVG_DREB"]
307
+ }
308
+ end
309
+
310
+ # Extracts average other stat attributes from data
311
+ # @api private
312
+ # @param data [Hash] the row data
313
+ # @return [Hash] average other stat attributes
314
+ def self.avg_other(data)
315
+ {
316
+ avg_tot_reb: data["AVG_TOT_REB"], avg_ast: data["AVG_AST"],
317
+ avg_pf: data["AVG_PF"], avg_stl: data["AVG_STL"],
318
+ avg_tov: data["AVG_TOV"], avg_blk: data["AVG_BLK"],
319
+ avg_pts: data["AVG_PTS"]
320
+ }
321
+ end
322
+
323
+ # Extracts maximum stat attributes from data
324
+ # @api private
325
+ # @param data [Hash] the row data
326
+ # @return [Hash] maximum stat attributes
327
+ def self.maximums(data)
328
+ max_shooting(data).merge(max_other(data))
329
+ end
330
+
331
+ # Extracts maximum shooting attributes from data
332
+ # @api private
333
+ # @param data [Hash] the row data
334
+ # @return [Hash] maximum shooting attributes
335
+ def self.max_shooting(data)
336
+ {
337
+ max_min: data["MAX_MIN"], max_fgm: data["MAX_FGM"],
338
+ max_fga: data["MAX_FGA"], max_fg3m: data["MAX_FG3M"],
339
+ max_fg3a: data["MAX_FG3A"], max_ftm: data["MAX_FTM"],
340
+ max_fta: data["MAX_FTA"], max_oreb: data["MAX_OREB"],
341
+ max_dreb: data["MAX_DREB"], max_reb: data["MAX_REB"]
342
+ }
343
+ end
344
+
345
+ # Extracts maximum other stat attributes from data
346
+ # @api private
347
+ # @param data [Hash] the row data
348
+ # @return [Hash] maximum other stat attributes
349
+ def self.max_other(data)
350
+ {
351
+ max_ast: data["MAX_AST"], max_pf: data["MAX_PF"],
352
+ max_stl: data["MAX_STL"], max_tov: data["MAX_TOV"],
353
+ max_blk: data["MAX_BLK"], max_pts: data["MAX_PTS"]
354
+ }
355
+ end
356
+ end
357
+ end
358
+ end
@@ -0,0 +1,217 @@
1
+ require "equalizer"
2
+ require "shale"
3
+
4
+ module NBA
5
+ # Represents a single game in cumulative player statistics
6
+ class CumeStatsPlayerGame < Shale::Mapper
7
+ include Equalizer.new(:game_id)
8
+
9
+ # @!attribute [rw] game_id
10
+ # Returns the game ID
11
+ # @api public
12
+ # @example
13
+ # game.game_id #=> "0022400001"
14
+ # @return [String] the game ID
15
+ attribute :game_id, Shale::Type::String
16
+
17
+ # @!attribute [rw] matchup
18
+ # Returns the matchup description
19
+ # @api public
20
+ # @example
21
+ # game.matchup #=> "GSW vs. LAL"
22
+ # @return [String] the matchup description
23
+ attribute :matchup, Shale::Type::String
24
+
25
+ # @!attribute [rw] game_date
26
+ # Returns the game date
27
+ # @api public
28
+ # @example
29
+ # game.game_date #=> "2024-10-22"
30
+ # @return [String] the game date
31
+ attribute :game_date, Shale::Type::String
32
+
33
+ # @!attribute [rw] vs_team_id
34
+ # Returns the opposing team ID
35
+ # @api public
36
+ # @example
37
+ # game.vs_team_id #=> 1610612747
38
+ # @return [Integer] the opposing team ID
39
+ attribute :vs_team_id, Shale::Type::Integer
40
+
41
+ # @!attribute [rw] vs_team_city
42
+ # Returns the opposing team city
43
+ # @api public
44
+ # @example
45
+ # game.vs_team_city #=> "Los Angeles"
46
+ # @return [String] the opposing team city
47
+ attribute :vs_team_city, Shale::Type::String
48
+
49
+ # @!attribute [rw] vs_team_name
50
+ # Returns the opposing team name
51
+ # @api public
52
+ # @example
53
+ # game.vs_team_name #=> "Lakers"
54
+ # @return [String] the opposing team name
55
+ attribute :vs_team_name, Shale::Type::String
56
+
57
+ # @!attribute [rw] min
58
+ # Returns the minutes played
59
+ # @api public
60
+ # @example
61
+ # game.min #=> 35
62
+ # @return [Integer] the minutes played
63
+ attribute :min, Shale::Type::Integer
64
+
65
+ # @!attribute [rw] sec
66
+ # Returns the seconds played
67
+ # @api public
68
+ # @example
69
+ # game.sec #=> 42
70
+ # @return [Integer] the seconds played
71
+ attribute :sec, Shale::Type::Integer
72
+
73
+ # @!attribute [rw] fgm
74
+ # Returns the field goals made
75
+ # @api public
76
+ # @example
77
+ # game.fgm #=> 10
78
+ # @return [Integer] the field goals made
79
+ attribute :fgm, Shale::Type::Integer
80
+
81
+ # @!attribute [rw] fga
82
+ # Returns the field goals attempted
83
+ # @api public
84
+ # @example
85
+ # game.fga #=> 20
86
+ # @return [Integer] the field goals attempted
87
+ attribute :fga, Shale::Type::Integer
88
+
89
+ # @!attribute [rw] fg_pct
90
+ # Returns the field goal percentage
91
+ # @api public
92
+ # @example
93
+ # game.fg_pct #=> 0.500
94
+ # @return [Float] the field goal percentage
95
+ attribute :fg_pct, Shale::Type::Float
96
+
97
+ # @!attribute [rw] fg3m
98
+ # Returns the three-point field goals made
99
+ # @api public
100
+ # @example
101
+ # game.fg3m #=> 3
102
+ # @return [Integer] the three-point field goals made
103
+ attribute :fg3m, Shale::Type::Integer
104
+
105
+ # @!attribute [rw] fg3a
106
+ # Returns the three-point field goals attempted
107
+ # @api public
108
+ # @example
109
+ # game.fg3a #=> 8
110
+ # @return [Integer] the three-point field goals attempted
111
+ attribute :fg3a, Shale::Type::Integer
112
+
113
+ # @!attribute [rw] fg3_pct
114
+ # Returns the three-point field goal percentage
115
+ # @api public
116
+ # @example
117
+ # game.fg3_pct #=> 0.375
118
+ # @return [Float] the three-point field goal percentage
119
+ attribute :fg3_pct, Shale::Type::Float
120
+
121
+ # @!attribute [rw] ftm
122
+ # Returns the free throws made
123
+ # @api public
124
+ # @example
125
+ # game.ftm #=> 7
126
+ # @return [Integer] the free throws made
127
+ attribute :ftm, Shale::Type::Integer
128
+
129
+ # @!attribute [rw] fta
130
+ # Returns the free throws attempted
131
+ # @api public
132
+ # @example
133
+ # game.fta #=> 8
134
+ # @return [Integer] the free throws attempted
135
+ attribute :fta, Shale::Type::Integer
136
+
137
+ # @!attribute [rw] ft_pct
138
+ # Returns the free throw percentage
139
+ # @api public
140
+ # @example
141
+ # game.ft_pct #=> 0.875
142
+ # @return [Float] the free throw percentage
143
+ attribute :ft_pct, Shale::Type::Float
144
+
145
+ # @!attribute [rw] oreb
146
+ # Returns the offensive rebounds
147
+ # @api public
148
+ # @example
149
+ # game.oreb #=> 2
150
+ # @return [Integer] the offensive rebounds
151
+ attribute :oreb, Shale::Type::Integer
152
+
153
+ # @!attribute [rw] dreb
154
+ # Returns the defensive rebounds
155
+ # @api public
156
+ # @example
157
+ # game.dreb #=> 6
158
+ # @return [Integer] the defensive rebounds
159
+ attribute :dreb, Shale::Type::Integer
160
+
161
+ # @!attribute [rw] reb
162
+ # Returns the total rebounds
163
+ # @api public
164
+ # @example
165
+ # game.reb #=> 8
166
+ # @return [Integer] the total rebounds
167
+ attribute :reb, Shale::Type::Integer
168
+
169
+ # @!attribute [rw] ast
170
+ # Returns the assists
171
+ # @api public
172
+ # @example
173
+ # game.ast #=> 5
174
+ # @return [Integer] the assists
175
+ attribute :ast, Shale::Type::Integer
176
+
177
+ # @!attribute [rw] pf
178
+ # Returns the personal fouls
179
+ # @api public
180
+ # @example
181
+ # game.pf #=> 3
182
+ # @return [Integer] the personal fouls
183
+ attribute :pf, Shale::Type::Integer
184
+
185
+ # @!attribute [rw] stl
186
+ # Returns the steals
187
+ # @api public
188
+ # @example
189
+ # game.stl #=> 2
190
+ # @return [Integer] the steals
191
+ attribute :stl, Shale::Type::Integer
192
+
193
+ # @!attribute [rw] tov
194
+ # Returns the turnovers
195
+ # @api public
196
+ # @example
197
+ # game.tov #=> 3
198
+ # @return [Integer] the turnovers
199
+ attribute :tov, Shale::Type::Integer
200
+
201
+ # @!attribute [rw] blk
202
+ # Returns the blocks
203
+ # @api public
204
+ # @example
205
+ # game.blk #=> 1
206
+ # @return [Integer] the blocks
207
+ attribute :blk, Shale::Type::Integer
208
+
209
+ # @!attribute [rw] pts
210
+ # Returns the points
211
+ # @api public
212
+ # @example
213
+ # game.pts #=> 30
214
+ # @return [Integer] the points
215
+ attribute :pts, Shale::Type::Integer
216
+ end
217
+ end
@@ -0,0 +1,99 @@
1
+ require_relative "client"
2
+ require_relative "response_parser"
3
+ require_relative "cume_stats_player_games_entry"
4
+ require_relative "utils"
5
+
6
+ module NBA
7
+ # Provides methods to retrieve cumulative stats player games
8
+ #
9
+ # @api public
10
+ module CumeStatsPlayerGames
11
+ # Result set name for cumulative stats player games
12
+ # @return [String] the result set name
13
+ RESULTS = "CumeStatsPlayerGames".freeze
14
+
15
+ # Retrieves all cumulative stats player games for specified criteria
16
+ #
17
+ # @api public
18
+ # @example
19
+ # games = NBA::CumeStatsPlayerGames.all(player: 201939, season: 2024, season_type: "Regular Season")
20
+ # games.each { |game| puts "#{game.matchup} - #{game.game_id}" }
21
+ # @param player [Integer, Player] the player ID or Player object
22
+ # @param season [Integer] the season year
23
+ # @param season_type [String] the season type (Regular Season, Playoffs)
24
+ # @param league [String, League] the league ID or League object (default NBA)
25
+ # @param location [String, nil] filter by location (Home, Road)
26
+ # @param outcome [String, nil] filter by outcome (W, L)
27
+ # @param vs_conference [String, nil] filter by opponent conference
28
+ # @param vs_division [String, nil] filter by opponent division
29
+ # @param vs_team [Integer, Team, nil] filter by opponent team ID or Team object
30
+ # @param client [Client] the API client to use
31
+ # @return [Collection] a collection of cumulative stats player games entries
32
+ def self.all(player:, season:, season_type: "Regular Season", league: League::NBA, location: nil,
33
+ outcome: nil, vs_conference: nil, vs_division: nil, vs_team: nil, client: CLIENT)
34
+ opts = {player: player, season: season, season_type: season_type, league: league,
35
+ location: location, outcome: outcome, vs_conference: vs_conference,
36
+ vs_division: vs_division, vs_team: vs_team}
37
+ path = PathBuilder.build(opts)
38
+ ResponseParser.parse(client.get(path), result_set: RESULTS) { |data| build_entry(data) }
39
+ end
40
+
41
+ # Builds an entry from API data
42
+ #
43
+ # @api private
44
+ # @return [CumeStatsPlayerGamesEntry]
45
+ def self.build_entry(data)
46
+ CumeStatsPlayerGamesEntry.new(**entry_attributes(data))
47
+ end
48
+ private_class_method :build_entry
49
+
50
+ # Extracts entry attributes from data
51
+ #
52
+ # @api private
53
+ # @param data [Hash] the entry data
54
+ # @return [Hash] the entry attributes
55
+ def self.entry_attributes(data)
56
+ {game_id: data["GAME_ID"], matchup: data["MATCHUP"]}
57
+ end
58
+ private_class_method :entry_attributes
59
+
60
+ # Builds API path from options
61
+ # @api private
62
+ module PathBuilder
63
+ # Builds the complete API path from options
64
+ # @api private
65
+ # @param opts [Hash] the options hash
66
+ # @return [String] the complete API path
67
+ def self.build(opts)
68
+ base_path(opts) + optional_params(opts)
69
+ end
70
+
71
+ # Builds the base API path from options
72
+ # @api private
73
+ # @param opts [Hash] the options hash
74
+ # @return [String] the base API path
75
+ def self.base_path(opts)
76
+ player_id = Utils.extract_id(opts.fetch(:player))
77
+ league_id = Utils.extract_league_id(opts.fetch(:league))
78
+ season_str = Utils.format_season(opts.fetch(:season))
79
+
80
+ "cumestatsplayergames?PlayerID=#{player_id}&LeagueID=#{league_id}" \
81
+ "&Season=#{season_str}&SeasonType=#{opts.fetch(:season_type)}"
82
+ end
83
+
84
+ # Builds optional parameters string from options
85
+ # @api private
86
+ # @param opts [Hash] the options hash
87
+ # @return [String] the optional parameters string
88
+ def self.optional_params(opts)
89
+ path = ""
90
+ path += "&Location=#{opts.fetch(:location)}" if opts.fetch(:location)
91
+ path += "&Outcome=#{opts.fetch(:outcome)}" if opts.fetch(:outcome)
92
+ path += "&VsConference=#{opts.fetch(:vs_conference)}" if opts.fetch(:vs_conference)
93
+ path += "&VsDivision=#{opts.fetch(:vs_division)}" if opts.fetch(:vs_division)
94
+ path += "&VsTeamID=#{Utils.extract_id(opts.fetch(:vs_team))}" if opts.fetch(:vs_team)
95
+ path
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,25 @@
1
+ require "equalizer"
2
+ require "shale"
3
+
4
+ module NBA
5
+ # Represents a single game entry from cumulative stats player games
6
+ class CumeStatsPlayerGamesEntry < Shale::Mapper
7
+ include Equalizer.new(:game_id)
8
+
9
+ # @!attribute [rw] game_id
10
+ # Returns the game ID
11
+ # @api public
12
+ # @example
13
+ # entry.game_id #=> "0022400001"
14
+ # @return [String] the game ID
15
+ attribute :game_id, Shale::Type::String
16
+
17
+ # @!attribute [rw] matchup
18
+ # Returns the game matchup description
19
+ # @api public
20
+ # @example
21
+ # entry.matchup #=> "GSW vs. LAL"
22
+ # @return [String] the matchup description
23
+ attribute :matchup, Shale::Type::String
24
+ end
25
+ end