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,349 @@
1
+ require "json"
2
+ require_relative "client"
3
+ require_relative "collection"
4
+ require_relative "cume_stats_team_player"
5
+ require_relative "cume_stats_team_total"
6
+ require_relative "utils"
7
+
8
+ module NBA
9
+ # Provides methods to retrieve cumulative team statistics
10
+ module CumeStatsTeam
11
+ # Result set name for game by game stats
12
+ # @return [String] the result set name
13
+ GAME_BY_GAME_STATS = "GameByGameStats".freeze
14
+
15
+ # Result set name for total team stats
16
+ # @return [String] the result set name
17
+ TOTAL_TEAM_STATS = "TotalTeamStats".freeze
18
+
19
+ # Retrieves cumulative team statistics for specific games
20
+ #
21
+ # @api public
22
+ # @example
23
+ # result = NBA::CumeStatsTeam.find(team: 1610612744, game_ids: ["0022400001", "0022400002"], season: 2024)
24
+ # result[:game_by_game].each { |p| puts "#{p.player_name}: #{p.pts} pts" }
25
+ # result[:total].pts #=> 220
26
+ # @param team [Integer, Team] the team ID or Team object
27
+ # @param game_ids [Array<String>, String] game IDs (array or pipe-separated string)
28
+ # @param season [Integer] the season year
29
+ # @param season_type [String] the season type
30
+ # @param league [String, League] the league ID or League object
31
+ # @param client [Client] the API client to use
32
+ # @return [Hash, nil] hash with :game_by_game (Collection) and :total (CumeStatsTeamTotal) keys, or nil
33
+ def self.find(team:, game_ids:, season:, season_type: "Regular Season", league: League::NBA, client: CLIENT)
34
+ team_id = Utils.extract_id(team)
35
+ return unless team_id
36
+
37
+ game_ids_str = normalize_game_ids(game_ids)
38
+ return unless game_ids_str
39
+
40
+ path = build_path(team_id, game_ids_str, season, season_type, league)
41
+ response = client.get(path)
42
+ parse_response(response)
43
+ end
44
+
45
+ # Normalizes game IDs to pipe-separated string
46
+ #
47
+ # @api private
48
+ # @param game_ids [Array<String>, String] game IDs
49
+ # @return [String, nil] pipe-separated game IDs or nil
50
+ def self.normalize_game_ids(game_ids)
51
+ return if game_ids.nil?
52
+ return game_ids if game_ids.instance_of?(String)
53
+ return if game_ids.empty?
54
+
55
+ game_ids.join("|")
56
+ end
57
+ private_class_method :normalize_game_ids
58
+
59
+ # Builds the API request path
60
+ #
61
+ # @api private
62
+ # @param team_id [Integer] the team ID
63
+ # @param game_ids [String] pipe-separated game IDs
64
+ # @param season [Integer] the season year
65
+ # @param season_type [String] the season type
66
+ # @param league [String, League] the league ID or League object
67
+ # @return [String] the API request path
68
+ def self.build_path(team_id, game_ids, season, season_type, league)
69
+ season_str = Utils.format_season(season)
70
+ league_id = Utils.extract_id(league)
71
+ "cumestatsteam?TeamID=#{team_id}&GameIDs=#{game_ids}&LeagueID=#{league_id}&Season=#{season_str}&SeasonType=#{season_type}"
72
+ end
73
+ private_class_method :build_path
74
+
75
+ # Parses the API response
76
+ #
77
+ # @api private
78
+ # @param response [String, nil] the JSON response
79
+ # @return [Hash, nil] parsed result hash or nil
80
+ def self.parse_response(response)
81
+ return unless response
82
+
83
+ data = JSON.parse(response)
84
+ return unless data
85
+
86
+ game_by_game = parse_game_by_game(data)
87
+ total = parse_total(data)
88
+
89
+ return unless game_by_game || total
90
+
91
+ {game_by_game: game_by_game, total: total}
92
+ rescue JSON::ParserError
93
+ nil
94
+ end
95
+ private_class_method :parse_response
96
+
97
+ # Parses game by game stats
98
+ #
99
+ # @api private
100
+ # @param data [Hash] the parsed JSON data
101
+ # @return [Collection, nil] collection of player stats or nil
102
+ def self.parse_game_by_game(data)
103
+ result_set = find_result_set(data, GAME_BY_GAME_STATS)
104
+ return unless result_set
105
+
106
+ build_player_collection(result_set)
107
+ end
108
+ private_class_method :parse_game_by_game
109
+
110
+ # Parses total team stats
111
+ #
112
+ # @api private
113
+ # @param data [Hash] the parsed JSON data
114
+ # @return [CumeStatsTeamTotal, nil] total stats or nil
115
+ def self.parse_total(data)
116
+ result_set = find_result_set(data, TOTAL_TEAM_STATS)
117
+ return unless result_set
118
+
119
+ build_total(result_set)
120
+ end
121
+ private_class_method :parse_total
122
+
123
+ # Finds a result set by name
124
+ #
125
+ # @api private
126
+ # @param data [Hash] the parsed JSON data
127
+ # @param name [String] the result set name
128
+ # @return [Hash, nil] the result set or nil
129
+ def self.find_result_set(data, name)
130
+ result_sets = data["resultSets"]
131
+ return unless result_sets
132
+
133
+ result_sets.find { |rs| rs["name"].eql?(name) }
134
+ end
135
+ private_class_method :find_result_set
136
+
137
+ # Builds player collection from result set
138
+ #
139
+ # @api private
140
+ # @param result_set [Hash] the result set
141
+ # @return [Collection] collection of player stats
142
+ def self.build_player_collection(result_set)
143
+ headers = result_set["headers"]
144
+ rows = result_set["rowSet"]
145
+ return Collection.new unless headers && rows
146
+
147
+ players = rows.map { |row| build_player(headers, row) }
148
+ Collection.new(players)
149
+ end
150
+ private_class_method :build_player_collection
151
+
152
+ # Builds a player stat object
153
+ #
154
+ # @api private
155
+ # @param headers [Array<String>] the headers
156
+ # @param row [Array] the row data
157
+ # @return [CumeStatsTeamPlayer] the player stat
158
+ def self.build_player(headers, row)
159
+ data = headers.zip(row).to_h
160
+ CumeStatsTeamPlayer.new(**PlayerAttributes.extract(data))
161
+ end
162
+ private_class_method :build_player
163
+
164
+ # Builds total stat object
165
+ #
166
+ # @api private
167
+ # @param result_set [Hash] the result set
168
+ # @return [CumeStatsTeamTotal, nil] total stat or nil
169
+ def self.build_total(result_set)
170
+ headers = result_set["headers"]
171
+ row = result_set.dig("rowSet", 0)
172
+ return unless headers && row
173
+
174
+ data = headers.zip(row).to_h
175
+ CumeStatsTeamTotal.new(**TotalAttributes.extract(data))
176
+ end
177
+ private_class_method :build_total
178
+
179
+ # Extracts player attributes from data
180
+ # @api private
181
+ module PlayerAttributes
182
+ # Extracts all player attributes from data
183
+ # @api private
184
+ # @param data [Hash] the row data
185
+ # @return [Hash] extracted attributes
186
+ def self.extract(data)
187
+ identity(data).merge(stats(data)).merge(averages(data)).merge(per_min(data))
188
+ end
189
+
190
+ # Extracts identity attributes from data
191
+ # @api private
192
+ # @param data [Hash] the row data
193
+ # @return [Hash] identity attributes
194
+ def self.identity(data)
195
+ {person_id: data["PERSON_ID"], player_name: data["PLAYER_NAME"],
196
+ jersey_num: data["JERSEY_NUM"], team_id: data["TEAM_ID"]}
197
+ end
198
+
199
+ # Extracts stat attributes from data
200
+ # @api private
201
+ # @param data [Hash] the row data
202
+ # @return [Hash] stat attributes
203
+ def self.stats(data)
204
+ time_stats(data).merge(shooting_stats(data)).merge(other_stats(data))
205
+ end
206
+
207
+ # Extracts time stat attributes from data
208
+ # @api private
209
+ # @param data [Hash] the row data
210
+ # @return [Hash] time stat attributes
211
+ def self.time_stats(data)
212
+ {gp: data["GP"], gs: data["GS"],
213
+ actual_minutes: data["ACTUAL_MINUTES"], actual_seconds: data["ACTUAL_SECONDS"]}
214
+ end
215
+
216
+ # Extracts shooting stat attributes from data
217
+ # @api private
218
+ # @param data [Hash] the row data
219
+ # @return [Hash] shooting stat attributes
220
+ def self.shooting_stats(data)
221
+ {fgm: data["FGM"], fga: data["FGA"], fg_pct: data["FG_PCT"],
222
+ fg3m: data["FG3M"], fg3a: data["FG3A"], fg3_pct: data["FG3_PCT"],
223
+ ftm: data["FTM"], fta: data["FTA"], ft_pct: data["FT_PCT"]}
224
+ end
225
+
226
+ # Extracts other stat attributes from data
227
+ # @api private
228
+ # @param data [Hash] the row data
229
+ # @return [Hash] other stat attributes
230
+ def self.other_stats(data)
231
+ {oreb: data["OREB"], dreb: data["DREB"], tot_reb: data["TOT_REB"],
232
+ ast: data["AST"], pf: data["PF"], stl: data["STL"],
233
+ tov: data["TOV"], blk: data["BLK"], pts: data["PTS"]}
234
+ end
235
+
236
+ # Extracts average stat attributes from data
237
+ # @api private
238
+ # @param data [Hash] the row data
239
+ # @return [Hash] average stat attributes
240
+ def self.averages(data)
241
+ {avg_minutes: data["AVG_MINUTES"], fgm_pg: data["FGM_PG"],
242
+ fga_pg: data["FGA_PG"], fg3m_pg: data["FG3M_PG"],
243
+ fg3a_pg: data["FG3A_PG"], ftm_pg: data["FTM_PG"],
244
+ fta_pg: data["FTA_PG"], oreb_pg: data["OREB_PG"],
245
+ dreb_pg: data["DREB_PG"], reb_pg: data["REB_PG"],
246
+ ast_pg: data["AST_PG"], pf_pg: data["PF_PG"],
247
+ stl_pg: data["STL_PG"], tov_pg: data["TOV_PG"],
248
+ blk_pg: data["BLK_PG"], pts_pg: data["PTS_PG"]}
249
+ end
250
+
251
+ # Extracts per-minute stat attributes from data
252
+ # @api private
253
+ # @param data [Hash] the row data
254
+ # @return [Hash] per-minute stat attributes
255
+ def self.per_min(data)
256
+ {fgm_per_min: data["FGM_PER_MIN"], fga_per_min: data["FGA_PER_MIN"],
257
+ fg3m_per_min: data["FG3M_PER_MIN"], fg3a_per_min: data["FG3A_PER_MIN"],
258
+ ftm_per_min: data["FTM_PER_MIN"], fta_per_min: data["FTA_PER_MIN"],
259
+ oreb_per_min: data["OREB_PER_MIN"], dreb_per_min: data["DREB_PER_MIN"],
260
+ reb_per_min: data["REB_PER_MIN"], ast_per_min: data["AST_PER_MIN"],
261
+ pf_per_min: data["PF_PER_MIN"], stl_per_min: data["STL_PER_MIN"],
262
+ tov_per_min: data["TOV_PER_MIN"], blk_per_min: data["BLK_PER_MIN"],
263
+ pts_per_min: data["PTS_PER_MIN"]}
264
+ end
265
+ end
266
+
267
+ # Extracts total attributes from data
268
+ # @api private
269
+ module TotalAttributes
270
+ # Extracts all total attributes from data
271
+ # @api private
272
+ # @param data [Hash] the row data
273
+ # @return [Hash] extracted attributes
274
+ def self.extract(data)
275
+ identity(data).merge(record(data)).merge(stats(data))
276
+ end
277
+
278
+ # Extracts identity attributes from data
279
+ # @api private
280
+ # @param data [Hash] the row data
281
+ # @return [Hash] identity attributes
282
+ def self.identity(data)
283
+ {team_id: data["TEAM_ID"], city: data["CITY"], nickname: data["NICKNAME"]}
284
+ end
285
+
286
+ # Extracts record attributes from data
287
+ # @api private
288
+ # @param data [Hash] the row data
289
+ # @return [Hash] record attributes
290
+ def self.record(data)
291
+ games(data).merge(wins_losses(data)).merge(other_record(data))
292
+ end
293
+
294
+ # Extracts game attributes from data
295
+ # @api private
296
+ # @param data [Hash] the row data
297
+ # @return [Hash] game attributes
298
+ def self.games(data)
299
+ {gp: data["GP"], gs: data["GS"]}
300
+ end
301
+
302
+ # Extracts win and loss attributes from data
303
+ # @api private
304
+ # @param data [Hash] the row data
305
+ # @return [Hash] win and loss attributes
306
+ def self.wins_losses(data)
307
+ {w: data["W"], l: data["L"],
308
+ w_home: data["W_HOME"], l_home: data["L_HOME"],
309
+ w_road: data["W_ROAD"], l_road: data["L_ROAD"]}
310
+ end
311
+
312
+ # Extracts other record attributes from data
313
+ # @api private
314
+ # @param data [Hash] the row data
315
+ # @return [Hash] other record attributes
316
+ def self.other_record(data)
317
+ {team_turnovers: data["TEAM_TURNOVERS"], team_rebounds: data["TEAM_REBOUNDS"]}
318
+ end
319
+
320
+ # Extracts stat attributes from data
321
+ # @api private
322
+ # @param data [Hash] the row data
323
+ # @return [Hash] stat attributes
324
+ def self.stats(data)
325
+ shooting(data).merge(other(data))
326
+ end
327
+
328
+ # Extracts shooting attributes from data
329
+ # @api private
330
+ # @param data [Hash] the row data
331
+ # @return [Hash] shooting attributes
332
+ def self.shooting(data)
333
+ {fgm: data["FGM"], fga: data["FGA"], fg_pct: data["FG_PCT"],
334
+ fg3m: data["FG3M"], fg3a: data["FG3A"], fg3_pct: data["FG3_PCT"],
335
+ ftm: data["FTM"], fta: data["FTA"], ft_pct: data["FT_PCT"]}
336
+ end
337
+
338
+ # Extracts other stat attributes from data
339
+ # @api private
340
+ # @param data [Hash] the row data
341
+ # @return [Hash] other stat attributes
342
+ def self.other(data)
343
+ {oreb: data["OREB"], dreb: data["DREB"], tot_reb: data["TOT_REB"],
344
+ ast: data["AST"], pf: data["PF"], stl: data["STL"],
345
+ tov: data["TOV"], blk: data["BLK"], pts: data["PTS"]}
346
+ end
347
+ end
348
+ end
349
+ end
@@ -0,0 +1,145 @@
1
+ require "json"
2
+ require_relative "client"
3
+ require_relative "collection"
4
+ require_relative "cume_stats_team_games_entry"
5
+ require_relative "utils"
6
+
7
+ module NBA
8
+ # Provides methods to retrieve cumulative stats team games
9
+ module CumeStatsTeamGames
10
+ # Result set name
11
+ # @return [String] the result set name
12
+ RESULTS = "CumeStatsTeamGames".freeze
13
+
14
+ # Regular season type
15
+ # @return [String] the season type
16
+ REGULAR_SEASON = "Regular Season".freeze
17
+
18
+ # Playoffs season type
19
+ # @return [String] the season type
20
+ PLAYOFFS = "Playoffs".freeze
21
+
22
+ # Retrieves cumulative stats team games
23
+ #
24
+ # @api public
25
+ # @example
26
+ # games = NBA::CumeStatsTeamGames.all(team: 1610612747, season: 2023)
27
+ # games.each { |g| puts "#{g.matchup}: #{g.game_id}" }
28
+ # @param team [Integer, String, Team] the team ID or Team object
29
+ # @param season [Integer] the season year
30
+ # @param season_type [String] the season type (Regular Season, Playoffs)
31
+ # @param league [String, League] the league ID or League object (default NBA)
32
+ # @param location [String, nil] the location filter (Home, Road)
33
+ # @param outcome [String, nil] the outcome filter (W, L)
34
+ # @param season_id [String, nil] the season ID
35
+ # @param vs_conference [String, nil] the vs conference filter (East, West)
36
+ # @param vs_division [String, nil] the vs division filter
37
+ # @param vs_team [Integer, String, Team, nil] the vs team ID or Team object
38
+ # @param client [Client] the API client to use
39
+ # @return [Collection] a collection of cumulative stats team games entries
40
+ def self.all(team:, season:, season_type: REGULAR_SEASON, league: League::NBA, location: nil, outcome: nil,
41
+ season_id: nil, vs_conference: nil, vs_division: nil, vs_team: nil, client: CLIENT)
42
+ opts = {team: team, season: season, season_type: season_type, league: league, location: location,
43
+ outcome: outcome, season_id: season_id, vs_conference: vs_conference,
44
+ vs_division: vs_division, vs_team: vs_team}
45
+ path = PathBuilder.build(opts)
46
+ response = client.get(path)
47
+ parse_response(response)
48
+ end
49
+
50
+ # Builds API path from options
51
+ # @api private
52
+ module PathBuilder
53
+ # Builds the complete API path from options
54
+ # @api private
55
+ # @param opts [Hash] the options hash
56
+ # @return [String] the complete API path
57
+ def self.build(opts)
58
+ "cumestatsteamgames?#{Utils.build_query(**build_params(opts))}"
59
+ end
60
+
61
+ # Builds all parameters from options
62
+ # @api private
63
+ # @param opts [Hash] the options hash
64
+ # @return [Hash] the merged parameters
65
+ def self.build_params(opts)
66
+ required_params(opts).merge(optional_params(opts))
67
+ end
68
+
69
+ # Builds required parameters from options
70
+ # @api private
71
+ # @param opts [Hash] the options hash
72
+ # @return [Hash] the required parameters
73
+ def self.required_params(opts)
74
+ {
75
+ TeamID: Utils.extract_id(opts.fetch(:team)),
76
+ LeagueID: Utils.extract_league_id(opts.fetch(:league)),
77
+ Season: Utils.format_season(opts.fetch(:season)),
78
+ SeasonType: opts.fetch(:season_type)
79
+ }
80
+ end
81
+
82
+ # Builds optional parameters from options
83
+ # @api private
84
+ # @param opts [Hash] the options hash
85
+ # @return [Hash] the optional parameters
86
+ def self.optional_params(opts)
87
+ {
88
+ Location: opts.fetch(:location), Outcome: opts.fetch(:outcome), SeasonID: opts.fetch(:season_id),
89
+ VsConference: opts.fetch(:vs_conference), VsDivision: opts.fetch(:vs_division),
90
+ VsTeamID: Utils.extract_id(opts.fetch(:vs_team))
91
+ }
92
+ end
93
+ end
94
+
95
+ # Parses the API response into entry objects
96
+ # @api private
97
+ # @return [Collection] collection of entries
98
+ def self.parse_response(response)
99
+ return Collection.new unless response
100
+
101
+ data = JSON.parse(response)
102
+ result_set = find_result_set(data)
103
+ return Collection.new unless result_set
104
+
105
+ headers = result_set["headers"]
106
+ rows = result_set["rowSet"]
107
+ return Collection.new unless headers && rows
108
+
109
+ entries = rows.map { |row| build_entry(headers, row) }
110
+ Collection.new(entries)
111
+ end
112
+ private_class_method :parse_response
113
+
114
+ # Finds the result set in the response
115
+ # @api private
116
+ # @return [Hash, nil] the result set hash
117
+ def self.find_result_set(data)
118
+ result_sets = data["resultSets"]
119
+ return unless result_sets
120
+
121
+ result_sets.find { |rs| rs["name"].eql?(RESULTS) }
122
+ end
123
+ private_class_method :find_result_set
124
+
125
+ # Builds a CumeStatsTeamGamesEntry object from raw data
126
+ # @api private
127
+ # @return [CumeStatsTeamGamesEntry] the entry object
128
+ def self.build_entry(headers, row)
129
+ data = headers.zip(row).to_h
130
+ CumeStatsTeamGamesEntry.new(**entry_attributes(data))
131
+ end
132
+ private_class_method :build_entry
133
+
134
+ # Extracts entry attributes from data
135
+ # @api private
136
+ # @return [Hash] the entry attributes
137
+ def self.entry_attributes(data)
138
+ {
139
+ matchup: data["MATCHUP"],
140
+ game_id: data["GAME_ID"]
141
+ }
142
+ end
143
+ private_class_method :entry_attributes
144
+ end
145
+ end
@@ -0,0 +1,25 @@
1
+ require "equalizer"
2
+ require "shale"
3
+
4
+ module NBA
5
+ # Represents a cumulative stats team games entry
6
+ class CumeStatsTeamGamesEntry < 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 #=> 22300001
14
+ # @return [Integer] the game ID
15
+ attribute :game_id, Shale::Type::Integer
16
+
17
+ # @!attribute [rw] matchup
18
+ # Returns the game matchup
19
+ # @api public
20
+ # @example
21
+ # entry.matchup #=> "LAL @ DEN"
22
+ # @return [String] the matchup
23
+ attribute :matchup, Shale::Type::String
24
+ end
25
+ end