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,169 @@
1
+ require "json"
2
+ require_relative "client"
3
+ require_relative "collection"
4
+ require_relative "career_stats"
5
+ require_relative "utils"
6
+
7
+ module NBA
8
+ # Provides methods to retrieve comprehensive player profile data
9
+ #
10
+ # @api public
11
+ module PlayerProfileV2
12
+ # Regular season result set name
13
+ # @return [String] the result set name
14
+ CAREER_REGULAR_SEASON = "CareerTotalsRegularSeason".freeze
15
+
16
+ # Post season result set name
17
+ # @return [String] the result set name
18
+ CAREER_POST_SEASON = "CareerTotalsPostSeason".freeze
19
+
20
+ # Season totals regular season result set name
21
+ # @return [String] the result set name
22
+ SEASON_REGULAR_SEASON = "SeasonTotalsRegularSeason".freeze
23
+
24
+ # Season totals post season result set name
25
+ # @return [String] the result set name
26
+ SEASON_POST_SEASON = "SeasonTotalsPostSeason".freeze
27
+
28
+ # Retrieves career regular season totals for a player
29
+ #
30
+ # @api public
31
+ # @example
32
+ # stats = NBA::PlayerProfileV2.career_regular_season(player: 201939)
33
+ # @param player [Integer, Player] the player ID or Player object
34
+ # @param client [Client] the API client to use
35
+ # @return [Collection] career regular season totals
36
+ def self.career_regular_season(player:, client: CLIENT)
37
+ fetch_stats(player, CAREER_REGULAR_SEASON, client: client)
38
+ end
39
+
40
+ # Retrieves career post season totals for a player
41
+ #
42
+ # @api public
43
+ # @example
44
+ # stats = NBA::PlayerProfileV2.career_post_season(player: 201939)
45
+ # @param player [Integer, Player] the player ID or Player object
46
+ # @param client [Client] the API client to use
47
+ # @return [Collection] career post season totals
48
+ def self.career_post_season(player:, client: CLIENT)
49
+ fetch_stats(player, CAREER_POST_SEASON, client: client)
50
+ end
51
+
52
+ # Retrieves season-by-season regular season totals for a player
53
+ #
54
+ # @api public
55
+ # @example
56
+ # stats = NBA::PlayerProfileV2.season_regular_season(player: 201939)
57
+ # @param player [Integer, Player] the player ID or Player object
58
+ # @param client [Client] the API client to use
59
+ # @return [Collection] season-by-season regular season totals
60
+ def self.season_regular_season(player:, client: CLIENT)
61
+ fetch_stats(player, SEASON_REGULAR_SEASON, client: client)
62
+ end
63
+
64
+ # Retrieves season-by-season post season totals for a player
65
+ #
66
+ # @api public
67
+ # @example
68
+ # stats = NBA::PlayerProfileV2.season_post_season(player: 201939)
69
+ # @param player [Integer, Player] the player ID or Player object
70
+ # @param client [Client] the API client to use
71
+ # @return [Collection] season-by-season post season totals
72
+ def self.season_post_season(player:, client: CLIENT)
73
+ fetch_stats(player, SEASON_POST_SEASON, client: client)
74
+ end
75
+
76
+ # Fetches stats from a specific result set
77
+ #
78
+ # @api private
79
+ # @return [Collection] the stats collection
80
+ def self.fetch_stats(player, result_set_name, client:)
81
+ player_id = Utils.extract_id(player)
82
+ path = "playerprofilev2?PlayerID=#{player_id}&PerMode=PerGame"
83
+ response = client.get(path)
84
+ parse_result_set(response, result_set_name, player_id)
85
+ end
86
+ private_class_method :fetch_stats
87
+
88
+ # Parses a specific result set from the response
89
+ #
90
+ # @api private
91
+ # @return [Collection] the parsed stats collection
92
+ def self.parse_result_set(response, result_set_name, player_id)
93
+ return Collection.new if response.nil? || response.empty?
94
+
95
+ result_set = find_result_set(JSON.parse(response), result_set_name)
96
+ build_collection(result_set, player_id)
97
+ end
98
+ private_class_method :parse_result_set
99
+
100
+ # Finds a result set by name
101
+ #
102
+ # @api private
103
+ # @return [Hash, nil] the result set hash or nil if not found
104
+ def self.find_result_set(data, result_set_name)
105
+ result_sets = data["resultSets"]
106
+ return unless result_sets
107
+
108
+ result_sets.find { |rs| rs["name"].eql?(result_set_name) }
109
+ end
110
+ private_class_method :find_result_set
111
+
112
+ # Builds a collection from a result set
113
+ #
114
+ # @api private
115
+ # @return [Collection] the stats collection
116
+ def self.build_collection(result_set, player_id)
117
+ return Collection.new unless result_set
118
+
119
+ headers = result_set["headers"]
120
+ rows = result_set["rowSet"]
121
+ return Collection.new unless headers && rows
122
+
123
+ Collection.new(rows.map { |row| build_stats(headers.zip(row).to_h, player_id) })
124
+ end
125
+ private_class_method :build_collection
126
+
127
+ # Builds career stats from API data
128
+ #
129
+ # @api private
130
+ # @return [CareerStats] the career stats object
131
+ def self.build_stats(data, player_id)
132
+ CareerStats.new(**season_info(data, player_id), **shooting_stats(data), **counting_stats(data))
133
+ end
134
+ private_class_method :build_stats
135
+
136
+ # Extracts season information from data
137
+ #
138
+ # @api private
139
+ # @return [Hash] the season information hash
140
+ def self.season_info(data, player_id)
141
+ {player_id: player_id, season_id: data["SEASON_ID"], team_id: data["TEAM_ID"],
142
+ team_abbreviation: data["TEAM_ABBREVIATION"], player_age: data["PLAYER_AGE"],
143
+ gp: data["GP"], gs: data["GS"], min: data["MIN"]}
144
+ end
145
+ private_class_method :season_info
146
+
147
+ # Extracts shooting statistics from data
148
+ #
149
+ # @api private
150
+ # @return [Hash] the shooting statistics hash
151
+ def self.shooting_stats(data)
152
+ {fgm: data["FGM"], fga: data["FGA"], fg_pct: data["FG_PCT"],
153
+ fg3m: data["FG3M"], fg3a: data["FG3A"], fg3_pct: data["FG3_PCT"],
154
+ ftm: data["FTM"], fta: data["FTA"], ft_pct: data["FT_PCT"]}
155
+ end
156
+ private_class_method :shooting_stats
157
+
158
+ # Extracts counting statistics from data
159
+ #
160
+ # @api private
161
+ # @return [Hash] the counting statistics hash
162
+ def self.counting_stats(data)
163
+ {oreb: data["OREB"], dreb: data["DREB"], reb: data["REB"],
164
+ ast: data["AST"], stl: data["STL"], blk: data["BLK"],
165
+ tov: data["TOV"], pf: data["PF"], pts: data["PTS"]}
166
+ end
167
+ private_class_method :counting_stats
168
+ end
169
+ end
@@ -0,0 +1,153 @@
1
+ require "json"
2
+ require_relative "client"
3
+ require_relative "collection"
4
+ require_relative "vs_player_stat"
5
+ require_relative "utils"
6
+
7
+ module NBA
8
+ # Provides methods to retrieve player vs player statistics
9
+ #
10
+ # @api public
11
+ module PlayerVsPlayer
12
+ # Result set name for overall comparison
13
+ # @return [String] the result set name
14
+ OVERALL = "Overall".freeze
15
+
16
+ # Result set name for on/off court comparison
17
+ # @return [String] the result set name
18
+ ON_OFF_COURT = "OnOffCourt".freeze
19
+
20
+ # Retrieves overall comparison statistics between two players
21
+ #
22
+ # @api public
23
+ # @example
24
+ # stats = NBA::PlayerVsPlayer.overall(player: 201939, vs_player: 201566)
25
+ # stats.first.pts #=> 26.4
26
+ # @param player [Integer, Player] the player ID or Player object
27
+ # @param vs_player [Integer, Player] the vs player ID or Player object
28
+ # @param season [Integer] the season year
29
+ # @param season_type [String] the season type
30
+ # @param per_mode [String] the per mode
31
+ # @param client [Client] the API client to use
32
+ # @return [Collection] a collection of vs player statistics
33
+ def self.overall(player:, vs_player:, season: Utils.current_season, season_type: "Regular Season",
34
+ per_mode: "PerGame", client: CLIENT)
35
+ options = {season_type: season_type, per_mode: per_mode, result_set: OVERALL}
36
+ fetch_stats(player, vs_player, season, options, client: client)
37
+ end
38
+
39
+ # Retrieves on/off court comparison statistics between two players
40
+ #
41
+ # @api public
42
+ # @example
43
+ # stats = NBA::PlayerVsPlayer.on_off_court(player: 201939, vs_player: 201566)
44
+ # stats.each { |s| puts "#{s.court_status}: #{s.pts}" }
45
+ # @param player [Integer, Player] the player ID or Player object
46
+ # @param vs_player [Integer, Player] the vs player ID or Player object
47
+ # @param season [Integer] the season year
48
+ # @param season_type [String] the season type
49
+ # @param per_mode [String] the per mode
50
+ # @param client [Client] the API client to use
51
+ # @return [Collection] a collection of vs player statistics
52
+ def self.on_off_court(player:, vs_player:, season: Utils.current_season, season_type: "Regular Season",
53
+ per_mode: "PerGame", client: CLIENT)
54
+ options = {season_type: season_type, per_mode: per_mode, result_set: ON_OFF_COURT}
55
+ fetch_stats(player, vs_player, season, options, client: client)
56
+ end
57
+
58
+ # Fetches vs player stats from the API
59
+ #
60
+ # @api private
61
+ # @return [Collection] collection of vs player stats
62
+ def self.fetch_stats(player, vs_player, season, options, client:)
63
+ path = build_path(player, vs_player, season, options.fetch(:season_type), options.fetch(:per_mode))
64
+ response = client.get(path)
65
+ parse_response(response, options.fetch(:result_set))
66
+ end
67
+ private_class_method :fetch_stats
68
+
69
+ # Builds the API path
70
+ #
71
+ # @api private
72
+ # @return [String] the request path
73
+ def self.build_path(player, vs_player, season, season_type, per_mode)
74
+ player_id = Utils.extract_id(player)
75
+ vs_player_id = Utils.extract_id(vs_player)
76
+ season_str = Utils.format_season(season)
77
+ "playervsplayer?PlayerID=#{player_id}&VsPlayerID=#{vs_player_id}&Season=#{season_str}" \
78
+ "&SeasonType=#{season_type}&PerMode=#{per_mode}&LeagueID=00"
79
+ end
80
+ private_class_method :build_path
81
+
82
+ # Parses the API response into vs player stat objects
83
+ #
84
+ # @api private
85
+ # @return [Collection] collection of vs player stats
86
+ def self.parse_response(response, result_set_name)
87
+ return Collection.new if response.nil? || response.empty?
88
+
89
+ data = JSON.parse(response)
90
+ result_set = find_result_set(data, result_set_name)
91
+ build_collection(result_set)
92
+ end
93
+ private_class_method :parse_response
94
+
95
+ # Finds a result set by name
96
+ #
97
+ # @api private
98
+ # @return [Hash, nil] the result set hash or nil if not found
99
+ def self.find_result_set(data, result_set_name)
100
+ result_sets = data["resultSets"]
101
+ return unless result_sets
102
+
103
+ result_sets.find { |rs| rs["name"].eql?(result_set_name) }
104
+ end
105
+ private_class_method :find_result_set
106
+
107
+ # Builds a collection from a result set
108
+ #
109
+ # @api private
110
+ # @return [Collection] the stats collection
111
+ def self.build_collection(result_set)
112
+ return Collection.new unless result_set
113
+
114
+ headers = result_set["headers"]
115
+ rows = result_set["rowSet"]
116
+ return Collection.new unless headers && rows
117
+
118
+ Collection.new(rows.map { |row| build_vs_player_stat(headers.zip(row).to_h) })
119
+ end
120
+ private_class_method :build_collection
121
+
122
+ # Builds a vs player stat from API data
123
+ #
124
+ # @api private
125
+ # @return [VsPlayerStat] the vs player stat object
126
+ def self.build_vs_player_stat(data)
127
+ VsPlayerStat.new(**identity_info(data), **stat_info(data))
128
+ end
129
+ private_class_method :build_vs_player_stat
130
+
131
+ # Extracts identity information from data
132
+ #
133
+ # @api private
134
+ # @return [Hash] the identity information hash
135
+ def self.identity_info(data)
136
+ {player_id: data["PLAYER_ID"], vs_player_id: data["VS_PLAYER_ID"],
137
+ court_status: data["COURT_STATUS"], gp: data["GP"],
138
+ min: data["MIN"]}
139
+ end
140
+ private_class_method :identity_info
141
+
142
+ # Extracts stat information from data
143
+ #
144
+ # @api private
145
+ # @return [Hash] the stat information hash
146
+ def self.stat_info(data)
147
+ {pts: data["PTS"], reb: data["REB"], ast: data["AST"],
148
+ stl: data["STL"], blk: data["BLK"], tov: data["TOV"],
149
+ fg_pct: data["FG_PCT"], plus_minus: data["PLUS_MINUS"]}
150
+ end
151
+ private_class_method :stat_info
152
+ end
153
+ end
@@ -0,0 +1,107 @@
1
+ require_relative "client"
2
+ require_relative "response_parser"
3
+ require_relative "player"
4
+ require_relative "utils"
5
+
6
+ module NBA
7
+ # Provides methods to retrieve NBA players
8
+ module Players
9
+ # Retrieves all NBA players
10
+ #
11
+ # @api public
12
+ # @example
13
+ # players = NBA::Players.all
14
+ # players.each { |player| puts player.full_name }
15
+ # @param season [Integer] the season year (defaults to current season)
16
+ # @param only_current [Boolean] whether to only return current players
17
+ # @param client [Client] the API client to use
18
+ # @return [Collection] a collection of all players
19
+ def self.all(season: Utils.current_season, only_current: true, client: CLIENT)
20
+ current_flag = only_current ? 1 : 0
21
+ path = "commonallplayers?LeagueID=00&Season=#{Utils.format_season(season)}&IsOnlyCurrentSeason=#{current_flag}"
22
+ ResponseParser.parse(client.get(path)) { |data| build_player_summary(data) }
23
+ end
24
+
25
+ # Finds a player by ID
26
+ #
27
+ # @api public
28
+ # @example
29
+ # roster = NBA::Roster.find(team: NBA::Team::GSW)
30
+ # curry = roster.find { |p| p.jersey_number == 30 }
31
+ # player = NBA::Players.find(curry.id)
32
+ # @param player_id [Integer, Player] the player ID or Player object
33
+ # @param client [Client] the API client to use
34
+ # @return [Player, nil] the player with the given ID, or nil if not found
35
+ def self.find(player_id, client: CLIENT)
36
+ id = Utils.extract_id(player_id)
37
+ return unless id
38
+
39
+ path = "commonplayerinfo?PlayerID=#{id}"
40
+ ResponseParser.parse_single(client.get(path)) { |data| build_player_detail(data) }
41
+ end
42
+
43
+ # Builds a player summary from list data
44
+ #
45
+ # @api private
46
+ # @param data [Hash] the player row data
47
+ # @return [Player] the player object
48
+ def self.build_player_summary(data)
49
+ full_name = data["DISPLAY_FIRST_LAST"]
50
+ Player.new(
51
+ id: data["PERSON_ID"],
52
+ full_name: full_name,
53
+ first_name: full_name&.split&.first,
54
+ last_name: full_name&.split&.last,
55
+ is_active: [1, "Active"].include?(data["ROSTERSTATUS"])
56
+ )
57
+ end
58
+ private_class_method :build_player_summary
59
+
60
+ # Builds a player detail from API data
61
+ # @api private
62
+ # @return [Player]
63
+ def self.build_player_detail(data)
64
+ Player.new(**identity_info(data), **physical_info(data), **draft_info(data))
65
+ end
66
+ private_class_method :build_player_detail
67
+
68
+ # Extracts identity information from data
69
+ # @api private
70
+ # @return [Hash]
71
+ def self.identity_info(data)
72
+ {id: data["PERSON_ID"], full_name: data["DISPLAY_FIRST_LAST"],
73
+ first_name: data["FIRST_NAME"], last_name: data["LAST_NAME"],
74
+ is_active: [1, "Active"].include?(data["ROSTERSTATUS"])}
75
+ end
76
+ private_class_method :identity_info
77
+
78
+ # Extracts physical information from data
79
+ # @api private
80
+ # @return [Hash]
81
+ def self.physical_info(data)
82
+ {jersey_number: Utils.parse_integer(data["JERSEY"]), height: data["HEIGHT"],
83
+ weight: Utils.parse_integer(data["WEIGHT"]), college: data["SCHOOL"],
84
+ country: data["COUNTRY"], position: build_position(data["POSITION"])}
85
+ end
86
+ private_class_method :physical_info
87
+
88
+ # Builds a position from name
89
+ # @api private
90
+ # @return [Position, nil]
91
+ def self.build_position(name)
92
+ return unless name
93
+
94
+ Position.new(name: name)
95
+ end
96
+ private_class_method :build_position
97
+
98
+ # Extracts draft information from data
99
+ # @api private
100
+ # @return [Hash]
101
+ def self.draft_info(data)
102
+ {draft_year: Utils.parse_integer(data["DRAFT_YEAR"]), draft_round: Utils.parse_integer(data["DRAFT_ROUND"]),
103
+ draft_number: Utils.parse_integer(data["DRAFT_NUMBER"])}
104
+ end
105
+ private_class_method :draft_info
106
+ end
107
+ end
@@ -0,0 +1,84 @@
1
+ require "shale"
2
+ require "equalizer"
3
+
4
+ module NBA
5
+ # Represents a playoff matchup in the playoff picture
6
+ #
7
+ # @api public
8
+ # @example
9
+ # matchup.conference #=> "East"
10
+ # matchup.high_seed_team #=> "Boston Celtics"
11
+ class PlayoffMatchup < Shale::Mapper
12
+ include Equalizer.new(:conference, :high_seed_team_id, :low_seed_team_id)
13
+
14
+ # Returns the conference name
15
+ # @api public
16
+ # @example
17
+ # matchup.conference #=> "East"
18
+ # @return [String, nil] the conference name
19
+ attribute :conference, Shale::Type::String
20
+
21
+ # Returns the high seed ranking
22
+ # @api public
23
+ # @example
24
+ # matchup.high_seed_rank #=> 1
25
+ # @return [Integer, nil] the high seed ranking
26
+ attribute :high_seed_rank, Shale::Type::Integer
27
+
28
+ # Returns the high seed team name
29
+ # @api public
30
+ # @example
31
+ # matchup.high_seed_team #=> "Boston Celtics"
32
+ # @return [String, nil] the high seed team name
33
+ attribute :high_seed_team, Shale::Type::String
34
+
35
+ # Returns the high seed team ID
36
+ # @api public
37
+ # @example
38
+ # matchup.high_seed_team_id #=> 1610612738
39
+ # @return [Integer, nil] the high seed team ID
40
+ attribute :high_seed_team_id, Shale::Type::Integer
41
+
42
+ # Returns the low seed ranking
43
+ # @api public
44
+ # @example
45
+ # matchup.low_seed_rank #=> 8
46
+ # @return [Integer, nil] the low seed ranking
47
+ attribute :low_seed_rank, Shale::Type::Integer
48
+
49
+ # Returns the low seed team name
50
+ # @api public
51
+ # @example
52
+ # matchup.low_seed_team #=> "Miami Heat"
53
+ # @return [String, nil] the low seed team name
54
+ attribute :low_seed_team, Shale::Type::String
55
+
56
+ # Returns the low seed team ID
57
+ # @api public
58
+ # @example
59
+ # matchup.low_seed_team_id #=> 1610612748
60
+ # @return [Integer, nil] the low seed team ID
61
+ attribute :low_seed_team_id, Shale::Type::Integer
62
+
63
+ # Returns the high seed series wins
64
+ # @api public
65
+ # @example
66
+ # matchup.high_seed_series_wins #=> 4
67
+ # @return [Integer, nil] the high seed series wins
68
+ attribute :high_seed_series_wins, Shale::Type::Integer
69
+
70
+ # Returns the low seed series wins
71
+ # @api public
72
+ # @example
73
+ # matchup.low_seed_series_wins #=> 1
74
+ # @return [Integer, nil] the low seed series wins
75
+ attribute :low_seed_series_wins, Shale::Type::Integer
76
+
77
+ # Returns the series status description
78
+ # @api public
79
+ # @example
80
+ # matchup.series_status #=> "BOS wins 4-1"
81
+ # @return [String, nil] the series status description
82
+ attribute :series_status, Shale::Type::String
83
+ end
84
+ end
@@ -0,0 +1,98 @@
1
+ require "json"
2
+ require_relative "client"
3
+ require_relative "collection"
4
+ require_relative "league"
5
+ require_relative "playoff_matchup"
6
+ require_relative "response_parser"
7
+ require_relative "utils"
8
+
9
+ module NBA
10
+ # Provides methods to retrieve NBA playoff picture data
11
+ module PlayoffPicture
12
+ # Retrieves playoff matchups for both conferences
13
+ #
14
+ # @api public
15
+ # @example
16
+ # matchups = NBA::PlayoffPicture.all(season: 2023)
17
+ # matchups.each { |m| puts "#{m.high_seed_team} vs #{m.low_seed_team}" }
18
+ # @param season [Integer] the season year (defaults to current season)
19
+ # @param league [String, League] the league ID or League object
20
+ # @param client [Client] the API client to use
21
+ # @return [Collection] a collection of playoff matchups
22
+ def self.all(season: Utils.current_season, league: League::NBA, client: CLIENT)
23
+ league_id = Utils.extract_league_id(league)
24
+ season_id = Utils.format_season_id(season)
25
+ path = "playoffpicture?LeagueID=#{league_id}&SeasonID=#{season_id}"
26
+ response = client.get(path)
27
+ parse_playoff_picture(response)
28
+ end
29
+
30
+ # Parses the playoff picture response
31
+ #
32
+ # @api private
33
+ # @param response [String] the JSON response body
34
+ # @return [Collection] a collection of playoff matchups
35
+ def self.parse_playoff_picture(response)
36
+ return Collection.new if response.nil? || response.empty?
37
+
38
+ data = JSON.parse(response)
39
+ result_sets = data["resultSets"]
40
+ return Collection.new unless result_sets
41
+
42
+ matchups = []
43
+ matchups.concat(parse_conference(result_sets, "EastConfPlayoffPicture", "East"))
44
+ matchups.concat(parse_conference(result_sets, "WestConfPlayoffPicture", "West"))
45
+ Collection.new(matchups)
46
+ end
47
+ private_class_method :parse_playoff_picture
48
+
49
+ # Parses a conference result set
50
+ #
51
+ # @api private
52
+ # @param result_sets [Array] all result sets
53
+ # @param name [String] the result set name to find
54
+ # @param conference [String] the conference name
55
+ # @return [Array<PlayoffMatchup>] array of matchups
56
+ def self.parse_conference(result_sets, name, conference)
57
+ result_set = result_sets.find { |rs| rs.fetch("name").eql?(name) }
58
+ return [] unless result_set
59
+
60
+ headers = result_set["headers"]
61
+ rows = result_set["rowSet"]
62
+ return [] unless headers && rows
63
+
64
+ rows.map { |row| build_matchup(headers.zip(row).to_h, conference) }
65
+ end
66
+ private_class_method :parse_conference
67
+
68
+ # Builds a playoff matchup from data
69
+ #
70
+ # @api private
71
+ # @param data [Hash] the row data
72
+ # @param conference [String] the conference name
73
+ # @return [PlayoffMatchup] the matchup object
74
+ def self.build_matchup(data, conference)
75
+ PlayoffMatchup.new(**matchup_attributes(data, conference))
76
+ end
77
+ private_class_method :build_matchup
78
+
79
+ # Extracts matchup attributes from data
80
+ #
81
+ # @api private
82
+ # @param data [Hash] the row data
83
+ # @param conference [String] the conference name
84
+ # @return [Hash] matchup attributes
85
+ def self.matchup_attributes(data, conference)
86
+ {conference: conference, high_seed_rank: data["HIGH_SEED_RANK"],
87
+ high_seed_team: data["HIGH_SEED_TEAM"],
88
+ high_seed_team_id: data["HIGH_SEED_TEAM_ID"],
89
+ low_seed_rank: data["LOW_SEED_RANK"],
90
+ low_seed_team: data["LOW_SEED_TEAM"],
91
+ low_seed_team_id: data["LOW_SEED_TEAM_ID"],
92
+ high_seed_series_wins: data["HIGH_SEED_SERIES_W"],
93
+ low_seed_series_wins: data["LOW_SEED_SERIES_W"],
94
+ series_status: data["SERIES_STATUS"]}
95
+ end
96
+ private_class_method :matchup_attributes
97
+ end
98
+ end
@@ -0,0 +1,76 @@
1
+ module NBA
2
+ # Represents a playoff series
3
+ class PlayoffSeries < Shale::Mapper
4
+ include Equalizer.new(:game_id, :series_id)
5
+
6
+ # @!attribute [rw] game_id
7
+ # Returns the game ID
8
+ # @api public
9
+ # @example
10
+ # series.game_id #=> "0042400101"
11
+ # @return [String] the game ID
12
+ attribute :game_id, Shale::Type::String
13
+
14
+ # @!attribute [rw] home_team_id
15
+ # Returns the home team ID
16
+ # @api public
17
+ # @example
18
+ # series.home_team_id #=> 1610612744
19
+ # @return [Integer] the home team ID
20
+ attribute :home_team_id, Shale::Type::Integer
21
+
22
+ # @!attribute [rw] visitor_team_id
23
+ # Returns the visitor team ID
24
+ # @api public
25
+ # @example
26
+ # series.visitor_team_id #=> 1610612747
27
+ # @return [Integer] the visitor team ID
28
+ attribute :visitor_team_id, Shale::Type::Integer
29
+
30
+ # @!attribute [rw] series_id
31
+ # Returns the series ID
32
+ # @api public
33
+ # @example
34
+ # series.series_id #=> "0042400101"
35
+ # @return [String] the series ID
36
+ attribute :series_id, Shale::Type::String
37
+
38
+ # @!attribute [rw] game_num
39
+ # Returns the game number in the series
40
+ # @api public
41
+ # @example
42
+ # series.game_num #=> 1
43
+ # @return [Integer] the game number
44
+ attribute :game_num, Shale::Type::Integer
45
+
46
+ # Returns the home team object
47
+ #
48
+ # @api public
49
+ # @example
50
+ # series.home_team #=> #<NBA::Team>
51
+ # @return [Team, nil] the home team object
52
+ def home_team
53
+ Teams.find(home_team_id)
54
+ end
55
+
56
+ # Returns the visitor team object
57
+ #
58
+ # @api public
59
+ # @example
60
+ # series.visitor_team #=> #<NBA::Team>
61
+ # @return [Team, nil] the visitor team object
62
+ def visitor_team
63
+ Teams.find(visitor_team_id)
64
+ end
65
+
66
+ # Returns the game object
67
+ #
68
+ # @api public
69
+ # @example
70
+ # series.game #=> #<NBA::Game>
71
+ # @return [Game, nil] the game object
72
+ def game
73
+ Games.find(game_id)
74
+ end
75
+ end
76
+ end