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,273 @@
1
+ require "equalizer"
2
+ require "shale"
3
+
4
+ module NBA
5
+ # Represents a player's box score statistics for a game
6
+ class BoxScorePlayerStat < Shale::Mapper
7
+ include Equalizer.new(:game_id, :player_id)
8
+
9
+ # @!attribute [rw] game_id
10
+ # Returns the game ID
11
+ # @api public
12
+ # @example
13
+ # stat.game_id #=> "0022400350"
14
+ # @return [String] the game ID
15
+ attribute :game_id, Shale::Type::String
16
+
17
+ # @!attribute [rw] team_id
18
+ # Returns the team ID
19
+ # @api public
20
+ # @example
21
+ # stat.team_id #=> 1610612744
22
+ # @return [Integer] the team ID
23
+ attribute :team_id, Shale::Type::Integer
24
+
25
+ # @!attribute [rw] team_abbreviation
26
+ # Returns the team abbreviation
27
+ # @api public
28
+ # @example
29
+ # stat.team_abbreviation #=> "GSW"
30
+ # @return [String] the team abbreviation
31
+ attribute :team_abbreviation, Shale::Type::String
32
+
33
+ # @!attribute [rw] team_city
34
+ # Returns the team city
35
+ # @api public
36
+ # @example
37
+ # stat.team_city #=> "Golden State"
38
+ # @return [String] the team city
39
+ attribute :team_city, Shale::Type::String
40
+
41
+ # @!attribute [rw] player_id
42
+ # Returns the player ID
43
+ # @api public
44
+ # @example
45
+ # stat.player_id #=> 201939
46
+ # @return [Integer] the player ID
47
+ attribute :player_id, Shale::Type::Integer
48
+
49
+ # @!attribute [rw] player_name
50
+ # Returns the player name
51
+ # @api public
52
+ # @example
53
+ # stat.player_name #=> "Stephen Curry"
54
+ # @return [String] the player name
55
+ attribute :player_name, Shale::Type::String
56
+
57
+ # @!attribute [rw] start_position
58
+ # Returns the starting position
59
+ # @api public
60
+ # @example
61
+ # stat.start_position #=> "G"
62
+ # @return [String] the starting position
63
+ attribute :start_position, Shale::Type::String
64
+
65
+ # @!attribute [rw] comment
66
+ # Returns any comment (e.g., "DNP - Rest")
67
+ # @api public
68
+ # @example
69
+ # stat.comment #=> "DNP - Rest"
70
+ # @return [String] the comment
71
+ attribute :comment, Shale::Type::String
72
+
73
+ # @!attribute [rw] min
74
+ # Returns minutes played
75
+ # @api public
76
+ # @example
77
+ # stat.min #=> "32:45"
78
+ # @return [String] the minutes played
79
+ attribute :min, Shale::Type::String
80
+
81
+ # @!attribute [rw] fgm
82
+ # Returns field goals made
83
+ # @api public
84
+ # @example
85
+ # stat.fgm #=> 10
86
+ # @return [Integer] field goals made
87
+ attribute :fgm, Shale::Type::Integer
88
+
89
+ # @!attribute [rw] fga
90
+ # Returns field goals attempted
91
+ # @api public
92
+ # @example
93
+ # stat.fga #=> 20
94
+ # @return [Integer] field goals attempted
95
+ attribute :fga, Shale::Type::Integer
96
+
97
+ # @!attribute [rw] fg_pct
98
+ # Returns field goal percentage
99
+ # @api public
100
+ # @example
101
+ # stat.fg_pct #=> 0.500
102
+ # @return [Float] field goal percentage
103
+ attribute :fg_pct, Shale::Type::Float
104
+
105
+ # @!attribute [rw] fg3m
106
+ # Returns three-pointers made
107
+ # @api public
108
+ # @example
109
+ # stat.fg3m #=> 5
110
+ # @return [Integer] three-pointers made
111
+ attribute :fg3m, Shale::Type::Integer
112
+
113
+ # @!attribute [rw] fg3a
114
+ # Returns three-pointers attempted
115
+ # @api public
116
+ # @example
117
+ # stat.fg3a #=> 12
118
+ # @return [Integer] three-pointers attempted
119
+ attribute :fg3a, Shale::Type::Integer
120
+
121
+ # @!attribute [rw] fg3_pct
122
+ # Returns three-point percentage
123
+ # @api public
124
+ # @example
125
+ # stat.fg3_pct #=> 0.417
126
+ # @return [Float] three-point percentage
127
+ attribute :fg3_pct, Shale::Type::Float
128
+
129
+ # @!attribute [rw] ftm
130
+ # Returns free throws made
131
+ # @api public
132
+ # @example
133
+ # stat.ftm #=> 8
134
+ # @return [Integer] free throws made
135
+ attribute :ftm, Shale::Type::Integer
136
+
137
+ # @!attribute [rw] fta
138
+ # Returns free throws attempted
139
+ # @api public
140
+ # @example
141
+ # stat.fta #=> 9
142
+ # @return [Integer] free throws attempted
143
+ attribute :fta, Shale::Type::Integer
144
+
145
+ # @!attribute [rw] ft_pct
146
+ # Returns free throw percentage
147
+ # @api public
148
+ # @example
149
+ # stat.ft_pct #=> 0.889
150
+ # @return [Float] free throw percentage
151
+ attribute :ft_pct, Shale::Type::Float
152
+
153
+ # @!attribute [rw] oreb
154
+ # Returns offensive rebounds
155
+ # @api public
156
+ # @example
157
+ # stat.oreb #=> 2
158
+ # @return [Integer] offensive rebounds
159
+ attribute :oreb, Shale::Type::Integer
160
+
161
+ # @!attribute [rw] dreb
162
+ # Returns defensive rebounds
163
+ # @api public
164
+ # @example
165
+ # stat.dreb #=> 6
166
+ # @return [Integer] defensive rebounds
167
+ attribute :dreb, Shale::Type::Integer
168
+
169
+ # @!attribute [rw] reb
170
+ # Returns total rebounds
171
+ # @api public
172
+ # @example
173
+ # stat.reb #=> 8
174
+ # @return [Integer] total rebounds
175
+ attribute :reb, Shale::Type::Integer
176
+
177
+ # @!attribute [rw] ast
178
+ # Returns assists
179
+ # @api public
180
+ # @example
181
+ # stat.ast #=> 11
182
+ # @return [Integer] assists
183
+ attribute :ast, Shale::Type::Integer
184
+
185
+ # @!attribute [rw] stl
186
+ # Returns steals
187
+ # @api public
188
+ # @example
189
+ # stat.stl #=> 3
190
+ # @return [Integer] steals
191
+ attribute :stl, Shale::Type::Integer
192
+
193
+ # @!attribute [rw] blk
194
+ # Returns blocks
195
+ # @api public
196
+ # @example
197
+ # stat.blk #=> 1
198
+ # @return [Integer] blocks
199
+ attribute :blk, Shale::Type::Integer
200
+
201
+ # @!attribute [rw] tov
202
+ # Returns turnovers
203
+ # @api public
204
+ # @example
205
+ # stat.tov #=> 4
206
+ # @return [Integer] turnovers
207
+ attribute :tov, Shale::Type::Integer
208
+
209
+ # @!attribute [rw] pf
210
+ # Returns personal fouls
211
+ # @api public
212
+ # @example
213
+ # stat.pf #=> 2
214
+ # @return [Integer] personal fouls
215
+ attribute :pf, Shale::Type::Integer
216
+
217
+ # @!attribute [rw] pts
218
+ # Returns points
219
+ # @api public
220
+ # @example
221
+ # stat.pts #=> 33
222
+ # @return [Integer] points
223
+ attribute :pts, Shale::Type::Integer
224
+
225
+ # @!attribute [rw] plus_minus
226
+ # Returns plus/minus
227
+ # @api public
228
+ # @example
229
+ # stat.plus_minus #=> 12
230
+ # @return [Integer] plus/minus
231
+ attribute :plus_minus, Shale::Type::Integer
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
+
253
+ # Returns whether the player started the game
254
+ #
255
+ # @api public
256
+ # @example
257
+ # stat.starter? #=> true
258
+ # @return [Boolean] true if the player started
259
+ def starter?
260
+ !start_position.nil? && !start_position.empty?
261
+ end
262
+
263
+ # Returns the game object for this box score
264
+ #
265
+ # @api public
266
+ # @example
267
+ # stat.game #=> #<NBA::Game>
268
+ # @return [Game, nil] the game object
269
+ def game
270
+ Games.find(game_id)
271
+ end
272
+ end
273
+ end
@@ -0,0 +1,223 @@
1
+ require "json"
2
+ require_relative "client"
3
+ require_relative "collection"
4
+ require_relative "box_score_player_track_stat"
5
+ require_relative "box_score_player_track_team_stat"
6
+ require_relative "utils"
7
+
8
+ module NBA
9
+ # Provides methods to retrieve player tracking statistics for a game
10
+ module BoxScorePlayerTrack
11
+ # Result set name for player stats
12
+ # @return [String] the result set name
13
+ PLAYER_STATS = "PlayerStats".freeze
14
+
15
+ # Result set name for team stats
16
+ # @return [String] the result set name
17
+ TEAM_STATS = "TeamStats".freeze
18
+
19
+ # Retrieves player tracking statistics for players in a game
20
+ #
21
+ # @api public
22
+ # @example
23
+ # stats = NBA::BoxScorePlayerTrack.player_stats(game: "0022400001")
24
+ # stats.each { |s| puts "#{s.player_name}: #{s.distance} miles, #{s.touches} touches" }
25
+ # @param game [String, Game] the game ID or Game object
26
+ # @param client [Client] the API client to use
27
+ # @return [Collection] a collection of player tracking stats
28
+ def self.player_stats(game:, client: CLIENT)
29
+ game_id = Utils.extract_id(game)
30
+ path = "boxscoreplayertrackv3?GameID=#{game_id}"
31
+ response = client.get(path)
32
+ parse_player_response(response, game_id)
33
+ end
34
+
35
+ # Retrieves player tracking statistics for teams in a game
36
+ #
37
+ # @api public
38
+ # @example
39
+ # stats = NBA::BoxScorePlayerTrack.team_stats(game: "0022400001")
40
+ # stats.each { |s| puts "#{s.team_name}: #{s.distance} miles, #{s.touches} touches" }
41
+ # @param game [String, Game] the game ID or Game object
42
+ # @param client [Client] the API client to use
43
+ # @return [Collection] a collection of team tracking stats
44
+ def self.team_stats(game:, client: CLIENT)
45
+ game_id = Utils.extract_id(game)
46
+ path = "boxscoreplayertrackv3?GameID=#{game_id}"
47
+ response = client.get(path)
48
+ parse_team_response(response, game_id)
49
+ end
50
+
51
+ # Parses player stats response
52
+ # @api private
53
+ # @param response [String, nil] the JSON response
54
+ # @param game_id [String] the game ID
55
+ # @return [Collection] collection of player stats
56
+ def self.parse_player_response(response, game_id)
57
+ return Collection.new unless response
58
+
59
+ data = JSON.parse(response)
60
+ result_set = find_result_set(data, PLAYER_STATS)
61
+ return Collection.new unless result_set
62
+
63
+ build_player_stats(result_set, game_id)
64
+ end
65
+ private_class_method :parse_player_response
66
+
67
+ # Parses team stats response
68
+ # @api private
69
+ # @param response [String, nil] the JSON response
70
+ # @param game_id [String] the game ID
71
+ # @return [Collection] collection of team stats
72
+ def self.parse_team_response(response, game_id)
73
+ return Collection.new unless response
74
+
75
+ data = JSON.parse(response)
76
+ result_set = find_result_set(data, TEAM_STATS)
77
+ return Collection.new unless result_set
78
+
79
+ build_team_stats(result_set, game_id)
80
+ end
81
+ private_class_method :parse_team_response
82
+
83
+ # Finds a result set by name
84
+ # @api private
85
+ # @param data [Hash] the parsed JSON
86
+ # @param name [String] the result set name
87
+ # @return [Hash, nil] the result set
88
+ def self.find_result_set(data, name)
89
+ result_sets = data["resultSets"]
90
+ return unless result_sets
91
+
92
+ result_sets.find { |rs| rs.fetch("name").eql?(name) }
93
+ end
94
+ private_class_method :find_result_set
95
+
96
+ # Builds player stats collection
97
+ # @api private
98
+ # @param result_set [Hash] the result set
99
+ # @param game_id [String] the game ID
100
+ # @return [Collection] collection of player stats
101
+ def self.build_player_stats(result_set, game_id)
102
+ headers = result_set["headers"]
103
+ rows = result_set["rowSet"]
104
+ return Collection.new unless headers && rows
105
+
106
+ stats = rows.map { |row| build_player_stat(headers, row, game_id) }
107
+ Collection.new(stats)
108
+ end
109
+ private_class_method :build_player_stats
110
+
111
+ # Builds team stats collection
112
+ # @api private
113
+ # @param result_set [Hash] the result set
114
+ # @param game_id [String] the game ID
115
+ # @return [Collection] collection of team stats
116
+ def self.build_team_stats(result_set, game_id)
117
+ headers = result_set["headers"]
118
+ rows = result_set["rowSet"]
119
+ return Collection.new unless headers && rows
120
+
121
+ stats = rows.map { |row| build_team_stat(headers, row, game_id) }
122
+ Collection.new(stats)
123
+ end
124
+ private_class_method :build_team_stats
125
+
126
+ # Builds a player stat
127
+ # @api private
128
+ # @param headers [Array<String>] the headers
129
+ # @param row [Array] the row data
130
+ # @param game_id [String] the game ID
131
+ # @return [BoxScorePlayerTrackStat] the player stat
132
+ def self.build_player_stat(headers, row, game_id)
133
+ data = headers.zip(row).to_h
134
+ BoxScorePlayerTrackStat.new(**player_stat_attributes(data, game_id))
135
+ end
136
+ private_class_method :build_player_stat
137
+
138
+ # Builds a team stat
139
+ # @api private
140
+ # @param headers [Array<String>] the headers
141
+ # @param row [Array] the row data
142
+ # @param game_id [String] the game ID
143
+ # @return [BoxScorePlayerTrackTeamStat] the team stat
144
+ def self.build_team_stat(headers, row, game_id)
145
+ data = headers.zip(row).to_h
146
+ BoxScorePlayerTrackTeamStat.new(**team_stat_attributes(data, game_id))
147
+ end
148
+ private_class_method :build_team_stat
149
+
150
+ # Combines player stat attributes
151
+ # @api private
152
+ # @param data [Hash] the raw data
153
+ # @param game_id [String] the game ID
154
+ # @return [Hash] the combined attributes
155
+ def self.player_stat_attributes(data, game_id)
156
+ player_identity(data, game_id).merge(tracking_stats(data), passing_stats(data), shooting_stats(data))
157
+ end
158
+ private_class_method :player_stat_attributes
159
+
160
+ # Combines team stat attributes
161
+ # @api private
162
+ # @param data [Hash] the raw data
163
+ # @param game_id [String] the game ID
164
+ # @return [Hash] the combined attributes
165
+ def self.team_stat_attributes(data, game_id)
166
+ team_identity(data, game_id).merge(tracking_stats(data), passing_stats(data), shooting_stats(data))
167
+ end
168
+ private_class_method :team_stat_attributes
169
+
170
+ # Extracts player identity attributes
171
+ # @api private
172
+ # @param data [Hash] the raw data
173
+ # @param game_id [String] the game ID
174
+ # @return [Hash] identity attributes
175
+ def self.player_identity(data, game_id)
176
+ {game_id: game_id, team_id: data.fetch("TEAM_ID"), team_abbreviation: data.fetch("TEAM_ABBREVIATION"),
177
+ team_city: data.fetch("TEAM_CITY"), player_id: data.fetch("PLAYER_ID"), player_name: data.fetch("PLAYER_NAME"),
178
+ start_position: data.fetch("START_POSITION"), comment: data.fetch("COMMENT"), min: data.fetch("MIN")}
179
+ end
180
+ private_class_method :player_identity
181
+
182
+ # Extracts team identity attributes
183
+ # @api private
184
+ # @param data [Hash] the raw data
185
+ # @param game_id [String] the game ID
186
+ # @return [Hash] identity attributes
187
+ def self.team_identity(data, game_id)
188
+ {game_id: game_id, team_id: data.fetch("TEAM_ID"), team_name: data.fetch("TEAM_NAME"),
189
+ team_abbreviation: data.fetch("TEAM_ABBREVIATION"), team_city: data.fetch("TEAM_CITY"), min: data.fetch("MIN")}
190
+ end
191
+ private_class_method :team_identity
192
+
193
+ # Extracts tracking stats
194
+ # @api private
195
+ # @param data [Hash] the raw data
196
+ # @return [Hash] tracking stats
197
+ def self.tracking_stats(data)
198
+ {speed: data.fetch("SPD"), distance: data.fetch("DIST"), oreb_chances: data.fetch("ORBC"), dreb_chances: data.fetch("DRBC"),
199
+ reb_chances: data.fetch("RBC"), touches: data.fetch("TCHS")}
200
+ end
201
+ private_class_method :tracking_stats
202
+
203
+ # Extracts passing stats
204
+ # @api private
205
+ # @param data [Hash] the raw data
206
+ # @return [Hash] passing stats
207
+ def self.passing_stats(data)
208
+ {secondary_ast: data.fetch("SAST"), ft_ast: data.fetch("FTAST"), passes: data.fetch("PASS"), ast: data.fetch("AST")}
209
+ end
210
+ private_class_method :passing_stats
211
+
212
+ # Extracts shooting stats
213
+ # @api private
214
+ # @param data [Hash] the raw data
215
+ # @return [Hash] shooting stats
216
+ def self.shooting_stats(data)
217
+ {cfgm: data.fetch("CFGM"), cfga: data.fetch("CFGA"), cfg_pct: data.fetch("CFG_PCT"),
218
+ ufgm: data.fetch("UFGM"), ufga: data.fetch("UFGA"), ufg_pct: data.fetch("UFG_PCT"),
219
+ dfgm: data.fetch("DFGM"), dfga: data.fetch("DFGA"), dfg_pct: data.fetch("DFG_PCT")}
220
+ end
221
+ private_class_method :shooting_stats
222
+ end
223
+ end