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,224 @@
1
+ require "json"
2
+ require_relative "client"
3
+ require_relative "collection"
4
+ require_relative "game_log"
5
+ require_relative "team_game_log"
6
+ require_relative "utils"
7
+
8
+ module NBA
9
+ # Provides methods to retrieve league-wide game logs
10
+ module LeagueGameLog
11
+ # Season type constant for regular season
12
+ # @return [String] the season type
13
+ REGULAR_SEASON = "Regular Season".freeze
14
+
15
+ # Season type constant for playoffs
16
+ # @return [String] the season type
17
+ PLAYOFFS = "Playoffs".freeze
18
+
19
+ # Player or team constant for player logs
20
+ # @return [String] the player/team type
21
+ PLAYER = "P".freeze
22
+
23
+ # Player or team constant for team logs
24
+ # @return [String] the player/team type
25
+ TEAM = "T".freeze
26
+
27
+ # Result set name for league game log
28
+ # @return [String] the result set name
29
+ LEAGUE_GAME_LOG = "LeagueGameLog".freeze
30
+
31
+ # Retrieves league-wide player game logs
32
+ #
33
+ # @api public
34
+ # @example
35
+ # logs = NBA::LeagueGameLog.player_logs(season: 2024)
36
+ # logs.each { |l| puts "#{l.game_date}: #{l.matchup} - #{l.pts} pts" }
37
+ # @param season [Integer] the season year
38
+ # @param season_type [String] the season type (Regular Season or Playoffs)
39
+ # @param client [Client] the API client to use
40
+ # @return [Collection] a collection of game logs
41
+ def self.player_logs(season: Utils.current_season, season_type: REGULAR_SEASON, client: CLIENT)
42
+ path = build_path(season, season_type, PLAYER)
43
+ response = client.get(path)
44
+ parse_response(response)
45
+ end
46
+
47
+ # Retrieves league-wide team game logs
48
+ #
49
+ # @api public
50
+ # @example
51
+ # logs = NBA::LeagueGameLog.team_logs(season: 2024)
52
+ # logs.each { |l| puts "#{l.game_date}: #{l.matchup} - #{l.pts} pts" }
53
+ # @param season [Integer] the season year
54
+ # @param season_type [String] the season type (Regular Season or Playoffs)
55
+ # @param client [Client] the API client to use
56
+ # @return [Collection] a collection of game logs
57
+ def self.team_logs(season: Utils.current_season, season_type: REGULAR_SEASON, client: CLIENT)
58
+ path = build_path(season, season_type, TEAM)
59
+ response = client.get(path)
60
+ parse_team_response(response)
61
+ end
62
+
63
+ # Builds the API path
64
+ #
65
+ # @api private
66
+ # @param season [Integer] the season year
67
+ # @param season_type [String] the season type
68
+ # @param player_or_team [String] player or team type
69
+ # @return [String] the API path
70
+ def self.build_path(season, season_type, player_or_team)
71
+ season_str = Utils.format_season(season)
72
+ encoded_type = season_type
73
+ "leaguegamelog?LeagueID=00&Season=#{season_str}&SeasonType=#{encoded_type}&PlayerOrTeam=#{player_or_team}"
74
+ end
75
+ private_class_method :build_path
76
+
77
+ # Parses the API response for player logs
78
+ #
79
+ # @api private
80
+ # @param response [String] the JSON response body
81
+ # @return [Collection] a collection of game logs
82
+ def self.parse_response(response)
83
+ return Collection.new unless response
84
+
85
+ data = JSON.parse(response)
86
+ result_set = find_result_set(data)
87
+ return Collection.new unless result_set
88
+
89
+ headers = result_set["headers"]
90
+ rows = result_set["rowSet"]
91
+ return Collection.new unless headers && rows
92
+
93
+ logs = rows.map { |row| build_game_log(headers, row) }
94
+ Collection.new(logs)
95
+ end
96
+ private_class_method :parse_response
97
+
98
+ # Parses the API response for team logs
99
+ #
100
+ # @api private
101
+ # @param response [String] the JSON response body
102
+ # @return [Collection] a collection of team game log entries
103
+ def self.parse_team_response(response)
104
+ return Collection.new unless response
105
+
106
+ data = JSON.parse(response)
107
+ result_set = find_result_set(data)
108
+ return Collection.new unless result_set
109
+
110
+ headers = result_set["headers"]
111
+ rows = result_set["rowSet"]
112
+ return Collection.new unless headers && rows
113
+
114
+ logs = rows.map { |row| build_team_game_log(headers, row) }
115
+ Collection.new(logs)
116
+ end
117
+ private_class_method :parse_team_response
118
+
119
+ # Finds the result set
120
+ #
121
+ # @api private
122
+ # @param data [Hash] the parsed JSON data
123
+ # @return [Hash, nil] the result set
124
+ def self.find_result_set(data)
125
+ result_sets = data["resultSets"]
126
+ return unless result_sets
127
+
128
+ result_sets.find { |rs| rs["name"].eql?(LEAGUE_GAME_LOG) }
129
+ end
130
+ private_class_method :find_result_set
131
+
132
+ # Builds a game log from a row
133
+ #
134
+ # @api private
135
+ # @param headers [Array<String>] the column headers
136
+ # @param row [Array] the row data
137
+ # @return [GameLog] the game log object
138
+ def self.build_game_log(headers, row)
139
+ data = headers.zip(row).to_h
140
+ GameLog.new(**game_log_attributes(data))
141
+ end
142
+ private_class_method :build_game_log
143
+
144
+ # Builds a team game log from a row
145
+ #
146
+ # @api private
147
+ # @param headers [Array<String>] the column headers
148
+ # @param row [Array] the row data
149
+ # @return [TeamGameLogEntry] the team game log object
150
+ def self.build_team_game_log(headers, row)
151
+ data = headers.zip(row).to_h
152
+ TeamGameLogEntry.new(**team_game_log_attributes(data))
153
+ end
154
+ private_class_method :build_team_game_log
155
+
156
+ # Extracts game log attributes from row data
157
+ #
158
+ # @api private
159
+ # @param data [Hash] the game log row data
160
+ # @return [Hash] the game log attributes
161
+ def self.game_log_attributes(data)
162
+ game_info_attributes(data).merge(shooting_attributes(data), counting_attributes(data))
163
+ end
164
+ private_class_method :game_log_attributes
165
+
166
+ # Extracts team game log attributes from row data
167
+ #
168
+ # @api private
169
+ # @param data [Hash] the game log row data
170
+ # @return [Hash] the game log attributes
171
+ def self.team_game_log_attributes(data)
172
+ team_info_attributes(data).merge(shooting_attributes(data), counting_attributes(data))
173
+ end
174
+ private_class_method :team_game_log_attributes
175
+
176
+ # Extracts game info attributes
177
+ #
178
+ # @api private
179
+ # @param data [Hash] the game log data
180
+ # @return [Hash] the game info attributes
181
+ def self.game_info_attributes(data)
182
+ {season_id: data.fetch("SEASON_ID"), player_id: data.fetch("PLAYER_ID"),
183
+ game_id: data.fetch("GAME_ID"), game_date: data.fetch("GAME_DATE"),
184
+ matchup: data.fetch("MATCHUP"), wl: data.fetch("WL"), min: data.fetch("MIN")}
185
+ end
186
+ private_class_method :game_info_attributes
187
+
188
+ # Extracts team info attributes
189
+ #
190
+ # @api private
191
+ # @param data [Hash] the game log data
192
+ # @return [Hash] the team info attributes
193
+ def self.team_info_attributes(data)
194
+ {team_id: data.fetch("TEAM_ID"), game_id: data.fetch("GAME_ID"),
195
+ game_date: data.fetch("GAME_DATE"), matchup: data.fetch("MATCHUP"),
196
+ wl: data.fetch("WL"), min: data.fetch("MIN")}
197
+ end
198
+ private_class_method :team_info_attributes
199
+
200
+ # Extracts shooting attributes
201
+ #
202
+ # @api private
203
+ # @param data [Hash] the game log data
204
+ # @return [Hash] the shooting attributes
205
+ def self.shooting_attributes(data)
206
+ {fgm: data.fetch("FGM"), fga: data.fetch("FGA"), fg_pct: data.fetch("FG_PCT"),
207
+ fg3m: data.fetch("FG3M"), fg3a: data.fetch("FG3A"), fg3_pct: data.fetch("FG3_PCT"),
208
+ ftm: data.fetch("FTM"), fta: data.fetch("FTA"), ft_pct: data.fetch("FT_PCT")}
209
+ end
210
+ private_class_method :shooting_attributes
211
+
212
+ # Extracts counting stats attributes
213
+ #
214
+ # @api private
215
+ # @param data [Hash] the game log data
216
+ # @return [Hash] the counting stats attributes
217
+ def self.counting_attributes(data)
218
+ {oreb: data.fetch("OREB"), dreb: data.fetch("DREB"), reb: data.fetch("REB"),
219
+ ast: data.fetch("AST"), stl: data.fetch("STL"), blk: data.fetch("BLK"),
220
+ tov: data.fetch("TOV"), pf: data.fetch("PF"), pts: data.fetch("PTS"), plus_minus: data.fetch("PLUS_MINUS")}
221
+ end
222
+ private_class_method :counting_attributes
223
+ end
224
+ end
@@ -0,0 +1,161 @@
1
+ require "json"
2
+ require_relative "client"
3
+ require_relative "collection"
4
+ require_relative "utils"
5
+
6
+ module NBA
7
+ # Provides methods to retrieve league-wide player hustle statistics
8
+ module LeagueHustleStatsPlayer
9
+ # Result set name for hustle stats
10
+ # @return [String] the result set name
11
+ HUSTLE_STATS_PLAYER = "HustleStatsPlayer".freeze
12
+
13
+ # Season type constant for regular season
14
+ # @return [String] the season type
15
+ REGULAR_SEASON = "Regular Season".freeze
16
+
17
+ # Season type constant for playoffs
18
+ # @return [String] the season type
19
+ PLAYOFFS = "Playoffs".freeze
20
+
21
+ # Per mode constant for per game stats
22
+ # @return [String] the per mode
23
+ PER_GAME = "PerGame".freeze
24
+
25
+ # Per mode constant for totals
26
+ # @return [String] the per mode
27
+ TOTALS = "Totals".freeze
28
+
29
+ # Retrieves league-wide player hustle statistics
30
+ #
31
+ # @api public
32
+ # @example
33
+ # stats = NBA::LeagueHustleStatsPlayer.all(season: 2024)
34
+ # stats.each { |s| puts "#{s.player_name}: #{s.deflections} deflections" }
35
+ # @param season [Integer] the season year
36
+ # @param season_type [String] the season type
37
+ # @param per_mode [String] the per mode
38
+ # @param client [Client] the API client to use
39
+ # @return [Collection] a collection of player hustle stats
40
+ def self.all(season: Utils.current_season, season_type: REGULAR_SEASON, per_mode: PER_GAME, client: CLIENT)
41
+ path = build_path(season, season_type, per_mode)
42
+ response = client.get(path)
43
+ parse_response(response)
44
+ end
45
+
46
+ # Builds the API path
47
+ #
48
+ # @api private
49
+ # @param season [Integer] the season year
50
+ # @param season_type [String] the season type
51
+ # @param per_mode [String] the per mode
52
+ # @return [String] the API path
53
+ def self.build_path(season, season_type, per_mode)
54
+ season_str = Utils.format_season(season)
55
+ "leaguehustlestatsplayer?LeagueID=00&Season=#{season_str}&SeasonType=#{season_type}&PerMode=#{per_mode}"
56
+ end
57
+ private_class_method :build_path
58
+
59
+ # Parses the API response
60
+ #
61
+ # @api private
62
+ # @param response [String] the JSON response body
63
+ # @return [Collection] a collection of player hustle stats
64
+ def self.parse_response(response)
65
+ return Collection.new unless response
66
+
67
+ data = JSON.parse(response)
68
+ result_set = find_result_set(data)
69
+ return Collection.new unless result_set
70
+
71
+ headers = result_set["headers"]
72
+ rows = result_set["rowSet"]
73
+ return Collection.new unless headers && rows
74
+
75
+ stats = rows.map { |row| build_player_stat(headers, row) }
76
+ Collection.new(stats)
77
+ end
78
+ private_class_method :parse_response
79
+
80
+ # Finds the result set
81
+ #
82
+ # @api private
83
+ # @param data [Hash] the parsed JSON data
84
+ # @return [Hash, nil] the result set
85
+ def self.find_result_set(data)
86
+ result_sets = data["resultSets"]
87
+ return unless result_sets
88
+
89
+ result_sets.find { |rs| rs["name"].eql?(HUSTLE_STATS_PLAYER) }
90
+ end
91
+ private_class_method :find_result_set
92
+
93
+ # Builds a player stat from a row
94
+ #
95
+ # @api private
96
+ # @param headers [Array<String>] the column headers
97
+ # @param row [Array] the row data
98
+ # @return [LeagueHustleStatsPlayerStat] the player stat object
99
+ def self.build_player_stat(headers, row)
100
+ data = headers.zip(row).to_h
101
+ LeagueHustleStatsPlayerStat.new(**player_stat_attributes(data))
102
+ end
103
+ private_class_method :build_player_stat
104
+
105
+ # Extracts player stat attributes from row data
106
+ #
107
+ # @api private
108
+ # @param data [Hash] the player stat row data
109
+ # @return [Hash] the player stat attributes
110
+ def self.player_stat_attributes(data)
111
+ identity_attributes(data).merge(hustle_attributes(data), box_out_attributes(data))
112
+ end
113
+ private_class_method :player_stat_attributes
114
+
115
+ # Extracts identity attributes
116
+ #
117
+ # @api private
118
+ # @param data [Hash] the player stat data
119
+ # @return [Hash] the identity attributes
120
+ def self.identity_attributes(data)
121
+ {player_id: data["PLAYER_ID"], player_name: data["PLAYER_NAME"],
122
+ team_id: data["TEAM_ID"], team_abbreviation: data["TEAM_ABBREVIATION"],
123
+ age: data["AGE"], g: data["G"], min: data["MIN"]}
124
+ end
125
+ private_class_method :identity_attributes
126
+
127
+ # Extracts hustle attributes
128
+ #
129
+ # @api private
130
+ # @param data [Hash] the player stat data
131
+ # @return [Hash] the hustle attributes
132
+ def self.hustle_attributes(data)
133
+ {contested_shots: data["CONTESTED_SHOTS"],
134
+ contested_shots_2pt: data["CONTESTED_SHOTS_2PT"],
135
+ contested_shots_3pt: data["CONTESTED_SHOTS_3PT"],
136
+ deflections: data["DEFLECTIONS"], charges_drawn: data["CHARGES_DRAWN"],
137
+ screen_assists: data["SCREEN_ASSISTS"], screen_ast_pts: data["SCREEN_AST_PTS"],
138
+ off_loose_balls_recovered: data["OFF_LOOSE_BALLS_RECOVERED"],
139
+ def_loose_balls_recovered: data["DEF_LOOSE_BALLS_RECOVERED"],
140
+ loose_balls_recovered: data["LOOSE_BALLS_RECOVERED"],
141
+ pct_loose_balls_recovered_off: data["PCT_LOOSE_BALLS_RECOVERED_OFF"],
142
+ pct_loose_balls_recovered_def: data["PCT_LOOSE_BALLS_RECOVERED_DEF"]}
143
+ end
144
+ private_class_method :hustle_attributes
145
+
146
+ # Extracts box out attributes
147
+ #
148
+ # @api private
149
+ # @param data [Hash] the player stat data
150
+ # @return [Hash] the box out attributes
151
+ def self.box_out_attributes(data)
152
+ {off_boxouts: data["OFF_BOXOUTS"], def_boxouts: data["DEF_BOXOUTS"],
153
+ box_out_player_team_rebs: data["BOX_OUT_PLAYER_TEAM_REBS"],
154
+ box_out_player_rebs: data["BOX_OUT_PLAYER_REBS"], box_outs: data["BOX_OUTS"],
155
+ pct_box_outs_off: data["PCT_BOX_OUTS_OFF"], pct_box_outs_def: data["PCT_BOX_OUTS_DEF"],
156
+ pct_box_outs_team_reb: data["PCT_BOX_OUTS_TEAM_REB"],
157
+ pct_box_outs_reb: data["PCT_BOX_OUTS_REB"]}
158
+ end
159
+ private_class_method :box_out_attributes
160
+ end
161
+ end
@@ -0,0 +1,253 @@
1
+ require "equalizer"
2
+ require "shale"
3
+
4
+ module NBA
5
+ # Represents league-wide hustle statistics for a player
6
+ class LeagueHustleStatsPlayerStat < Shale::Mapper
7
+ include Equalizer.new(:player_id)
8
+
9
+ # @!attribute [rw] player_id
10
+ # Returns the player ID
11
+ # @api public
12
+ # @example
13
+ # stat.player_id #=> 201939
14
+ # @return [Integer] the player ID
15
+ attribute :player_id, Shale::Type::Integer
16
+
17
+ # @!attribute [rw] player_name
18
+ # Returns the player name
19
+ # @api public
20
+ # @example
21
+ # stat.player_name #=> "Stephen Curry"
22
+ # @return [String] the player name
23
+ attribute :player_name, Shale::Type::String
24
+
25
+ # @!attribute [rw] team_id
26
+ # Returns the team ID
27
+ # @api public
28
+ # @example
29
+ # stat.team_id #=> 1610612744
30
+ # @return [Integer] the team ID
31
+ attribute :team_id, Shale::Type::Integer
32
+
33
+ # @!attribute [rw] team_abbreviation
34
+ # Returns the team abbreviation
35
+ # @api public
36
+ # @example
37
+ # stat.team_abbreviation #=> "GSW"
38
+ # @return [String] the team abbreviation
39
+ attribute :team_abbreviation, Shale::Type::String
40
+
41
+ # @!attribute [rw] age
42
+ # Returns the player's age
43
+ # @api public
44
+ # @example
45
+ # stat.age #=> 36
46
+ # @return [Integer] the age
47
+ attribute :age, Shale::Type::Integer
48
+
49
+ # @!attribute [rw] g
50
+ # Returns games played
51
+ # @api public
52
+ # @example
53
+ # stat.g #=> 72
54
+ # @return [Integer] games played
55
+ attribute :g, Shale::Type::Integer
56
+
57
+ # @!attribute [rw] min
58
+ # Returns total minutes played
59
+ # @api public
60
+ # @example
61
+ # stat.min #=> 2484.5
62
+ # @return [Float] total minutes
63
+ attribute :min, Shale::Type::Float
64
+
65
+ # @!attribute [rw] contested_shots
66
+ # Returns contested shots
67
+ # @api public
68
+ # @example
69
+ # stat.contested_shots #=> 156
70
+ # @return [Integer] contested shots
71
+ attribute :contested_shots, Shale::Type::Integer
72
+
73
+ # @!attribute [rw] contested_shots_2pt
74
+ # Returns contested 2-point shots
75
+ # @api public
76
+ # @example
77
+ # stat.contested_shots_2pt #=> 98
78
+ # @return [Integer] contested 2-point shots
79
+ attribute :contested_shots_2pt, Shale::Type::Integer
80
+
81
+ # @!attribute [rw] contested_shots_3pt
82
+ # Returns contested 3-point shots
83
+ # @api public
84
+ # @example
85
+ # stat.contested_shots_3pt #=> 58
86
+ # @return [Integer] contested 3-point shots
87
+ attribute :contested_shots_3pt, Shale::Type::Integer
88
+
89
+ # @!attribute [rw] deflections
90
+ # Returns deflections
91
+ # @api public
92
+ # @example
93
+ # stat.deflections #=> 85
94
+ # @return [Integer] deflections
95
+ attribute :deflections, Shale::Type::Integer
96
+
97
+ # @!attribute [rw] charges_drawn
98
+ # Returns charges drawn
99
+ # @api public
100
+ # @example
101
+ # stat.charges_drawn #=> 5
102
+ # @return [Integer] charges drawn
103
+ attribute :charges_drawn, Shale::Type::Integer
104
+
105
+ # @!attribute [rw] screen_assists
106
+ # Returns screen assists
107
+ # @api public
108
+ # @example
109
+ # stat.screen_assists #=> 142
110
+ # @return [Integer] screen assists
111
+ attribute :screen_assists, Shale::Type::Integer
112
+
113
+ # @!attribute [rw] screen_ast_pts
114
+ # Returns points from screen assists
115
+ # @api public
116
+ # @example
117
+ # stat.screen_ast_pts #=> 284
118
+ # @return [Integer] screen assist points
119
+ attribute :screen_ast_pts, Shale::Type::Integer
120
+
121
+ # @!attribute [rw] off_loose_balls_recovered
122
+ # Returns offensive loose balls recovered
123
+ # @api public
124
+ # @example
125
+ # stat.off_loose_balls_recovered #=> 25
126
+ # @return [Integer] offensive loose balls recovered
127
+ attribute :off_loose_balls_recovered, Shale::Type::Integer
128
+
129
+ # @!attribute [rw] def_loose_balls_recovered
130
+ # Returns defensive loose balls recovered
131
+ # @api public
132
+ # @example
133
+ # stat.def_loose_balls_recovered #=> 35
134
+ # @return [Integer] defensive loose balls recovered
135
+ attribute :def_loose_balls_recovered, Shale::Type::Integer
136
+
137
+ # @!attribute [rw] loose_balls_recovered
138
+ # Returns total loose balls recovered
139
+ # @api public
140
+ # @example
141
+ # stat.loose_balls_recovered #=> 60
142
+ # @return [Integer] loose balls recovered
143
+ attribute :loose_balls_recovered, Shale::Type::Integer
144
+
145
+ # @!attribute [rw] pct_loose_balls_recovered_off
146
+ # Returns percentage of offensive loose balls recovered
147
+ # @api public
148
+ # @example
149
+ # stat.pct_loose_balls_recovered_off #=> 0.417
150
+ # @return [Float] percentage of offensive loose balls recovered
151
+ attribute :pct_loose_balls_recovered_off, Shale::Type::Float
152
+
153
+ # @!attribute [rw] pct_loose_balls_recovered_def
154
+ # Returns percentage of defensive loose balls recovered
155
+ # @api public
156
+ # @example
157
+ # stat.pct_loose_balls_recovered_def #=> 0.583
158
+ # @return [Float] percentage of defensive loose balls recovered
159
+ attribute :pct_loose_balls_recovered_def, Shale::Type::Float
160
+
161
+ # @!attribute [rw] off_boxouts
162
+ # Returns offensive box outs
163
+ # @api public
164
+ # @example
165
+ # stat.off_boxouts #=> 12
166
+ # @return [Integer] offensive box outs
167
+ attribute :off_boxouts, Shale::Type::Integer
168
+
169
+ # @!attribute [rw] def_boxouts
170
+ # Returns defensive box outs
171
+ # @api public
172
+ # @example
173
+ # stat.def_boxouts #=> 45
174
+ # @return [Integer] defensive box outs
175
+ attribute :def_boxouts, Shale::Type::Integer
176
+
177
+ # @!attribute [rw] box_out_player_team_rebs
178
+ # Returns team rebounds from player box outs
179
+ # @api public
180
+ # @example
181
+ # stat.box_out_player_team_rebs #=> 38
182
+ # @return [Integer] team rebounds from box outs
183
+ attribute :box_out_player_team_rebs, Shale::Type::Integer
184
+
185
+ # @!attribute [rw] box_out_player_rebs
186
+ # Returns player rebounds from box outs
187
+ # @api public
188
+ # @example
189
+ # stat.box_out_player_rebs #=> 22
190
+ # @return [Integer] player rebounds from box outs
191
+ attribute :box_out_player_rebs, Shale::Type::Integer
192
+
193
+ # @!attribute [rw] box_outs
194
+ # Returns total box outs
195
+ # @api public
196
+ # @example
197
+ # stat.box_outs #=> 57
198
+ # @return [Integer] total box outs
199
+ attribute :box_outs, Shale::Type::Integer
200
+
201
+ # @!attribute [rw] pct_box_outs_off
202
+ # Returns percentage of offensive box outs
203
+ # @api public
204
+ # @example
205
+ # stat.pct_box_outs_off #=> 0.211
206
+ # @return [Float] percentage of offensive box outs
207
+ attribute :pct_box_outs_off, Shale::Type::Float
208
+
209
+ # @!attribute [rw] pct_box_outs_def
210
+ # Returns percentage of defensive box outs
211
+ # @api public
212
+ # @example
213
+ # stat.pct_box_outs_def #=> 0.789
214
+ # @return [Float] percentage of defensive box outs
215
+ attribute :pct_box_outs_def, Shale::Type::Float
216
+
217
+ # @!attribute [rw] pct_box_outs_team_reb
218
+ # Returns percentage of box outs resulting in team rebounds
219
+ # @api public
220
+ # @example
221
+ # stat.pct_box_outs_team_reb #=> 0.667
222
+ # @return [Float] percentage of box outs resulting in team rebounds
223
+ attribute :pct_box_outs_team_reb, Shale::Type::Float
224
+
225
+ # @!attribute [rw] pct_box_outs_reb
226
+ # Returns percentage of box outs resulting in player rebounds
227
+ # @api public
228
+ # @example
229
+ # stat.pct_box_outs_reb #=> 0.386
230
+ # @return [Float] percentage of box outs resulting in player rebounds
231
+ attribute :pct_box_outs_reb, Shale::Type::Float
232
+
233
+ # Returns the player object
234
+ #
235
+ # @api public
236
+ # @example
237
+ # stat.player #=> #<NBA::Player>
238
+ # @return [Player, nil] the player object
239
+ def player
240
+ Players.find(player_id)
241
+ end
242
+
243
+ # Returns the team object
244
+ #
245
+ # @api public
246
+ # @example
247
+ # stat.team #=> #<NBA::Team>
248
+ # @return [Team, nil] the team object
249
+ def team
250
+ Teams.find(team_id)
251
+ end
252
+ end
253
+ end