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,183 @@
1
+ require "json"
2
+ require_relative "client"
3
+ require_relative "collection"
4
+ require_relative "utils"
5
+
6
+ require_relative "scheduled_game"
7
+
8
+ module NBA
9
+ # Provides methods to retrieve league schedule
10
+ module Schedule
11
+ # Result set name for league schedule
12
+ # @return [String] the result set name
13
+ LEAGUE_SCHEDULE = "LeagueSchedule".freeze
14
+
15
+ # Retrieves the league schedule
16
+ #
17
+ # @api public
18
+ # @example
19
+ # games = NBA::Schedule.all(season: 2024)
20
+ # games.each { |g| puts "#{g.game_date}: #{g.away_team_name} @ #{g.home_team_name}" }
21
+ # @param season [Integer] the season year
22
+ # @param league [String, League] the league ID or League object (default NBA)
23
+ # @param client [Client] the API client to use
24
+ # @return [Collection] a collection of scheduled games
25
+ def self.all(season: Utils.current_season, league: League::NBA, client: CLIENT)
26
+ league_id = Utils.extract_league_id(league)
27
+ path = build_path(season, league_id)
28
+ response = client.get(path)
29
+ parse_response(response)
30
+ end
31
+
32
+ # Retrieves games for a specific date
33
+ #
34
+ # @api public
35
+ # @example
36
+ # games = NBA::Schedule.by_date(date: Date.today, season: 2024)
37
+ # games.each { |g| puts "#{g.away_team_name} @ #{g.home_team_name}" }
38
+ # @param date [Date] the date
39
+ # @param season [Integer] the season year
40
+ # @param league [String, League] the league ID or League object (default NBA)
41
+ # @param client [Client] the API client to use
42
+ # @return [Collection] a collection of scheduled games
43
+ def self.by_date(date:, season: Utils.current_season, league: League::NBA, client: CLIENT)
44
+ all_games = all(season: season, league: league, client: client)
45
+ date_str = date.strftime
46
+ filtered = all_games.select { |g| g.game_date&.start_with?(date_str) }
47
+ Collection.new(filtered)
48
+ end
49
+
50
+ # Retrieves games for a specific team
51
+ #
52
+ # @api public
53
+ # @example
54
+ # games = NBA::Schedule.by_team(team: NBA::Team::GSW, season: 2024)
55
+ # games.each { |g| puts "#{g.game_date}: #{g.away_team_tricode} @ #{g.home_team_tricode}" }
56
+ # @param team [Integer, Team] the team ID or Team object
57
+ # @param season [Integer] the season year
58
+ # @param league [String, League] the league ID or League object (default NBA)
59
+ # @param client [Client] the API client to use
60
+ # @return [Collection] a collection of scheduled games
61
+ def self.by_team(team:, season: Utils.current_season, league: League::NBA, client: CLIENT)
62
+ team_id = extract_team_id(team)
63
+ all_games = all(season: season, league: league, client: client)
64
+ filtered = all_games.select { |g| g.home_team_id.eql?(team_id) || g.away_team_id.eql?(team_id) }
65
+ Collection.new(filtered)
66
+ end
67
+
68
+ # Builds the API request path
69
+ # @api private
70
+ # @return [String] the request path
71
+ def self.build_path(season, league_id)
72
+ season_str = Utils.format_season(season)
73
+ "scheduleleaguev2?LeagueID=#{league_id}&Season=#{season_str}"
74
+ end
75
+ private_class_method :build_path
76
+
77
+ # Parses the API response into scheduled game objects
78
+ # @api private
79
+ # @return [Collection] collection of scheduled games
80
+ def self.parse_response(response)
81
+ return Collection.new unless response
82
+
83
+ data = JSON.parse(response)
84
+ game_dates = data.dig("leagueSchedule", "gameDates")
85
+ return Collection.new unless game_dates
86
+
87
+ games = game_dates.flat_map { |date_entry| parse_date_entry(date_entry) }
88
+ Collection.new(games)
89
+ end
90
+ private_class_method :parse_response
91
+
92
+ # Parses a date entry into game objects
93
+ # @api private
94
+ # @return [Array<ScheduledGame>] array of games for the date
95
+ def self.parse_date_entry(date_entry)
96
+ games = date_entry["games"]
97
+ return [] unless games
98
+
99
+ games.map { |game| build_scheduled_game(game) }
100
+ end
101
+ private_class_method :parse_date_entry
102
+
103
+ # Builds a ScheduledGame object from raw data
104
+ # @api private
105
+ # @return [ScheduledGame] the scheduled game object
106
+ def self.build_scheduled_game(data)
107
+ ScheduledGame.new(**scheduled_game_attributes(data))
108
+ end
109
+ private_class_method :build_scheduled_game
110
+
111
+ # Combines all scheduled game attributes
112
+ # @api private
113
+ # @return [Hash] the combined attributes
114
+ def self.scheduled_game_attributes(data)
115
+ game_info_attributes(data).merge(home_team_attributes(data), away_team_attributes(data), venue_attributes(data))
116
+ end
117
+ private_class_method :scheduled_game_attributes
118
+
119
+ # Extracts game info attributes from data
120
+ # @api private
121
+ # @return [Hash] game info attributes
122
+ def self.game_info_attributes(data)
123
+ {game_date: data["gameDateTimeUTC"], game_id: data["gameId"],
124
+ game_code: data["gameCode"], game_status: data["gameStatus"],
125
+ game_status_text: data["gameStatusText"]}
126
+ end
127
+ private_class_method :game_info_attributes
128
+
129
+ # Extracts home team attributes from data
130
+ # @api private
131
+ # @return [Hash] home team attributes
132
+ def self.home_team_attributes(data)
133
+ home = data["homeTeam"] || {}
134
+ {home_team_id: home["teamId"], home_team_name: home["teamName"],
135
+ home_team_city: home["teamCity"], home_team_tricode: home["teamTricode"],
136
+ home_team_wins: home["wins"], home_team_losses: home["losses"],
137
+ home_team_score: home["score"]}
138
+ end
139
+ private_class_method :home_team_attributes
140
+
141
+ # Extracts away team attributes from data
142
+ # @api private
143
+ # @return [Hash] away team attributes
144
+ def self.away_team_attributes(data)
145
+ away = data["awayTeam"] || {}
146
+ {away_team_id: away["teamId"], away_team_name: away["teamName"],
147
+ away_team_city: away["teamCity"], away_team_tricode: away["teamTricode"],
148
+ away_team_wins: away["wins"], away_team_losses: away["losses"],
149
+ away_team_score: away["score"]}
150
+ end
151
+ private_class_method :away_team_attributes
152
+
153
+ # Extracts venue attributes from data
154
+ # @api private
155
+ # @return [Hash] venue attributes
156
+ def self.venue_attributes(data)
157
+ {arena_name: data["arenaName"], arena_city: data["arenaCity"],
158
+ arena_state: data["arenaState"], broadcasters: format_broadcasters(data["broadcasters"])}
159
+ end
160
+ private_class_method :venue_attributes
161
+
162
+ # Formats broadcasters into a string
163
+ # @api private
164
+ # @return [String, nil] formatted broadcasters
165
+ def self.format_broadcasters(broadcasters)
166
+ return unless broadcasters
167
+
168
+ national = broadcasters["nationalTvBroadcasters"]
169
+ return unless national&.any?
170
+
171
+ national.map { |b| b["broadcasterDisplay"] }.join(", ")
172
+ end
173
+ private_class_method :format_broadcasters
174
+
175
+ # Extracts team ID from team object or integer
176
+ # @api private
177
+ # @return [Integer] the team ID
178
+ def self.extract_team_id(team)
179
+ team.instance_of?(Team) ? team.id : team
180
+ end
181
+ private_class_method :extract_team_id
182
+ end
183
+ end
@@ -0,0 +1,182 @@
1
+ require "json"
2
+ require_relative "client"
3
+ require_relative "collection"
4
+ require_relative "schedule"
5
+ require_relative "utils"
6
+
7
+ module NBA
8
+ # Provides methods to retrieve international league schedule
9
+ module ScheduleInternational
10
+ # Result set name for league schedule
11
+ # @return [String] the result set name
12
+ LEAGUE_SCHEDULE = "LeagueSchedule".freeze
13
+
14
+ # Retrieves the international league schedule
15
+ #
16
+ # @api public
17
+ # @example
18
+ # games = NBA::ScheduleInternational.all(season: 2024)
19
+ # games.each { |g| puts "#{g.game_date}: #{g.away_team_name} @ #{g.home_team_name}" }
20
+ # @param season [Integer] the season year
21
+ # @param league [String, League] the league ID or League object (default NBA)
22
+ # @param client [Client] the API client to use
23
+ # @return [Collection] a collection of scheduled games
24
+ def self.all(season: Utils.current_season, league: League::NBA, client: CLIENT)
25
+ league_id = Utils.extract_league_id(league)
26
+ path = build_path(season, league_id)
27
+ response = client.get(path)
28
+ parse_response(response)
29
+ end
30
+
31
+ # Retrieves international games for a specific date
32
+ #
33
+ # @api public
34
+ # @example
35
+ # games = NBA::ScheduleInternational.by_date(date: Date.today, season: 2024)
36
+ # games.each { |g| puts "#{g.away_team_name} @ #{g.home_team_name}" }
37
+ # @param date [Date] the date
38
+ # @param season [Integer] the season year
39
+ # @param league [String, League] the league ID or League object (default NBA)
40
+ # @param client [Client] the API client to use
41
+ # @return [Collection] a collection of scheduled games
42
+ def self.by_date(date:, season: Utils.current_season, league: League::NBA, client: CLIENT)
43
+ all_games = all(season: season, league: league, client: client)
44
+ date_str = date.strftime
45
+ filtered = all_games.select { |g| g.game_date&.start_with?(date_str) }
46
+ Collection.new(filtered)
47
+ end
48
+
49
+ # Retrieves international games for a specific team
50
+ #
51
+ # @api public
52
+ # @example
53
+ # games = NBA::ScheduleInternational.by_team(team: NBA::Team::GSW, season: 2024)
54
+ # games.each { |g| puts "#{g.game_date}: #{g.away_team_tricode} @ #{g.home_team_tricode}" }
55
+ # @param team [Integer, Team] the team ID or Team object
56
+ # @param season [Integer] the season year
57
+ # @param league [String, League] the league ID or League object (default NBA)
58
+ # @param client [Client] the API client to use
59
+ # @return [Collection] a collection of scheduled games
60
+ def self.by_team(team:, season: Utils.current_season, league: League::NBA, client: CLIENT)
61
+ team_id = extract_team_id(team)
62
+ all_games = all(season: season, league: league, client: client)
63
+ filtered = all_games.select { |g| g.home_team_id.eql?(team_id) || g.away_team_id.eql?(team_id) }
64
+ Collection.new(filtered)
65
+ end
66
+
67
+ # Builds the API request path
68
+ # @api private
69
+ # @return [String] the request path
70
+ def self.build_path(season, league_id)
71
+ season_str = Utils.format_season(season)
72
+ "scheduleleaguev2int?LeagueID=#{league_id}&Season=#{season_str}"
73
+ end
74
+ private_class_method :build_path
75
+
76
+ # Parses the API response into scheduled game objects
77
+ # @api private
78
+ # @return [Collection] collection of scheduled games
79
+ def self.parse_response(response)
80
+ return Collection.new if response.nil? || response.empty?
81
+
82
+ data = JSON.parse(response)
83
+ game_dates = data.dig("leagueSchedule", "gameDates")
84
+ return Collection.new unless game_dates
85
+
86
+ games = game_dates.flat_map { |date_entry| parse_date_entry(date_entry) }
87
+ Collection.new(games)
88
+ end
89
+ private_class_method :parse_response
90
+
91
+ # Parses a date entry into game objects
92
+ # @api private
93
+ # @return [Array<ScheduledGame>] array of games for the date
94
+ def self.parse_date_entry(date_entry)
95
+ games = date_entry["games"]
96
+ return [] unless games
97
+
98
+ games.map { |game| build_scheduled_game(game) }
99
+ end
100
+ private_class_method :parse_date_entry
101
+
102
+ # Builds a ScheduledGame object from raw data
103
+ # @api private
104
+ # @return [ScheduledGame] the scheduled game object
105
+ def self.build_scheduled_game(data)
106
+ ScheduledGame.new(**scheduled_game_attributes(data))
107
+ end
108
+ private_class_method :build_scheduled_game
109
+
110
+ # Combines all scheduled game attributes
111
+ # @api private
112
+ # @return [Hash] the combined attributes
113
+ def self.scheduled_game_attributes(data)
114
+ game_info_attributes(data).merge(home_team_attributes(data), away_team_attributes(data), venue_attributes(data))
115
+ end
116
+ private_class_method :scheduled_game_attributes
117
+
118
+ # Extracts game info attributes from data
119
+ # @api private
120
+ # @return [Hash] game info attributes
121
+ def self.game_info_attributes(data)
122
+ {game_date: data["gameDateTimeUTC"], game_id: data["gameId"],
123
+ game_code: data["gameCode"], game_status: data["gameStatus"],
124
+ game_status_text: data["gameStatusText"]}
125
+ end
126
+ private_class_method :game_info_attributes
127
+
128
+ # Extracts home team attributes from data
129
+ # @api private
130
+ # @return [Hash] home team attributes
131
+ def self.home_team_attributes(data)
132
+ home = data["homeTeam"] || {}
133
+ {home_team_id: home["teamId"], home_team_name: home["teamName"],
134
+ home_team_city: home["teamCity"], home_team_tricode: home["teamTricode"],
135
+ home_team_wins: home["wins"], home_team_losses: home["losses"],
136
+ home_team_score: home["score"]}
137
+ end
138
+ private_class_method :home_team_attributes
139
+
140
+ # Extracts away team attributes from data
141
+ # @api private
142
+ # @return [Hash] away team attributes
143
+ def self.away_team_attributes(data)
144
+ away = data["awayTeam"] || {}
145
+ {away_team_id: away["teamId"], away_team_name: away["teamName"],
146
+ away_team_city: away["teamCity"], away_team_tricode: away["teamTricode"],
147
+ away_team_wins: away["wins"], away_team_losses: away["losses"],
148
+ away_team_score: away["score"]}
149
+ end
150
+ private_class_method :away_team_attributes
151
+
152
+ # Extracts venue attributes from data
153
+ # @api private
154
+ # @return [Hash] venue attributes
155
+ def self.venue_attributes(data)
156
+ {arena_name: data["arenaName"], arena_city: data["arenaCity"],
157
+ arena_state: data["arenaState"], broadcasters: format_broadcasters(data["broadcasters"])}
158
+ end
159
+ private_class_method :venue_attributes
160
+
161
+ # Formats broadcasters into a string
162
+ # @api private
163
+ # @return [String, nil] formatted broadcasters
164
+ def self.format_broadcasters(broadcasters)
165
+ return unless broadcasters
166
+
167
+ national = broadcasters["nationalTvBroadcasters"]
168
+ return unless national&.any?
169
+
170
+ national.map { |b| b["broadcasterDisplay"] }.join(", ")
171
+ end
172
+ private_class_method :format_broadcasters
173
+
174
+ # Extracts team ID from team object or integer
175
+ # @api private
176
+ # @return [Integer] the team ID
177
+ def self.extract_team_id(team)
178
+ team.instance_of?(Team) ? team.id : team
179
+ end
180
+ private_class_method :extract_team_id
181
+ end
182
+ end
@@ -0,0 +1,240 @@
1
+ module NBA
2
+ # Represents a scheduled game
3
+ class ScheduledGame < Shale::Mapper
4
+ include Equalizer.new(:game_id)
5
+
6
+ # @!attribute [rw] game_date
7
+ # Returns the game date
8
+ # @api public
9
+ # @example
10
+ # game.game_date #=> "2024-10-22T19:00:00Z"
11
+ # @return [String] the game date
12
+ attribute :game_date, Shale::Type::String
13
+
14
+ # @!attribute [rw] game_id
15
+ # Returns the game ID
16
+ # @api public
17
+ # @example
18
+ # game.game_id #=> "0022400001"
19
+ # @return [String] the game ID
20
+ attribute :game_id, Shale::Type::String
21
+
22
+ # @!attribute [rw] game_code
23
+ # Returns the game code
24
+ # @api public
25
+ # @example
26
+ # game.game_code #=> "20241022/LALGSW"
27
+ # @return [String] the game code
28
+ attribute :game_code, Shale::Type::String
29
+
30
+ # @!attribute [rw] game_status
31
+ # Returns the game status (1=scheduled, 2=in progress, 3=final)
32
+ # @api public
33
+ # @example
34
+ # game.game_status #=> 1
35
+ # @return [Integer] the game status
36
+ attribute :game_status, Shale::Type::Integer
37
+
38
+ # @!attribute [rw] game_status_text
39
+ # Returns the game status text
40
+ # @api public
41
+ # @example
42
+ # game.game_status_text #=> "7:00 pm ET"
43
+ # @return [String] the game status text
44
+ attribute :game_status_text, Shale::Type::String
45
+
46
+ # @!attribute [rw] home_team_id
47
+ # Returns the home team ID
48
+ # @api public
49
+ # @example
50
+ # game.home_team_id #=> 1610612744
51
+ # @return [Integer] the home team ID
52
+ attribute :home_team_id, Shale::Type::Integer
53
+
54
+ # @!attribute [rw] home_team_name
55
+ # Returns the home team name
56
+ # @api public
57
+ # @example
58
+ # game.home_team_name #=> "Warriors"
59
+ # @return [String] the home team name
60
+ attribute :home_team_name, Shale::Type::String
61
+
62
+ # @!attribute [rw] home_team_city
63
+ # Returns the home team city
64
+ # @api public
65
+ # @example
66
+ # game.home_team_city #=> "Golden State"
67
+ # @return [String] the home team city
68
+ attribute :home_team_city, Shale::Type::String
69
+
70
+ # @!attribute [rw] home_team_tricode
71
+ # Returns the home team tricode/abbreviation
72
+ # @api public
73
+ # @example
74
+ # game.home_team_tricode #=> "GSW"
75
+ # @return [String] the home team tricode
76
+ attribute :home_team_tricode, Shale::Type::String
77
+
78
+ # @!attribute [rw] home_team_wins
79
+ # Returns the home team wins
80
+ # @api public
81
+ # @example
82
+ # game.home_team_wins #=> 46
83
+ # @return [Integer] the home team wins
84
+ attribute :home_team_wins, Shale::Type::Integer
85
+
86
+ # @!attribute [rw] home_team_losses
87
+ # Returns the home team losses
88
+ # @api public
89
+ # @example
90
+ # game.home_team_losses #=> 36
91
+ # @return [Integer] the home team losses
92
+ attribute :home_team_losses, Shale::Type::Integer
93
+
94
+ # @!attribute [rw] home_team_score
95
+ # Returns the home team score
96
+ # @api public
97
+ # @example
98
+ # game.home_team_score #=> 112
99
+ # @return [Integer] the home team score
100
+ attribute :home_team_score, Shale::Type::Integer
101
+
102
+ # @!attribute [rw] away_team_id
103
+ # Returns the away team ID
104
+ # @api public
105
+ # @example
106
+ # game.away_team_id #=> 1610612747
107
+ # @return [Integer] the away team ID
108
+ attribute :away_team_id, Shale::Type::Integer
109
+
110
+ # @!attribute [rw] away_team_name
111
+ # Returns the away team name
112
+ # @api public
113
+ # @example
114
+ # game.away_team_name #=> "Lakers"
115
+ # @return [String] the away team name
116
+ attribute :away_team_name, Shale::Type::String
117
+
118
+ # @!attribute [rw] away_team_city
119
+ # Returns the away team city
120
+ # @api public
121
+ # @example
122
+ # game.away_team_city #=> "Los Angeles"
123
+ # @return [String] the away team city
124
+ attribute :away_team_city, Shale::Type::String
125
+
126
+ # @!attribute [rw] away_team_tricode
127
+ # Returns the away team tricode/abbreviation
128
+ # @api public
129
+ # @example
130
+ # game.away_team_tricode #=> "LAL"
131
+ # @return [String] the away team tricode
132
+ attribute :away_team_tricode, Shale::Type::String
133
+
134
+ # @!attribute [rw] away_team_wins
135
+ # Returns the away team wins
136
+ # @api public
137
+ # @example
138
+ # game.away_team_wins #=> 43
139
+ # @return [Integer] the away team wins
140
+ attribute :away_team_wins, Shale::Type::Integer
141
+
142
+ # @!attribute [rw] away_team_losses
143
+ # Returns the away team losses
144
+ # @api public
145
+ # @example
146
+ # game.away_team_losses #=> 39
147
+ # @return [Integer] the away team losses
148
+ attribute :away_team_losses, Shale::Type::Integer
149
+
150
+ # @!attribute [rw] away_team_score
151
+ # Returns the away team score
152
+ # @api public
153
+ # @example
154
+ # game.away_team_score #=> 108
155
+ # @return [Integer] the away team score
156
+ attribute :away_team_score, Shale::Type::Integer
157
+
158
+ # @!attribute [rw] arena_name
159
+ # Returns the arena name
160
+ # @api public
161
+ # @example
162
+ # game.arena_name #=> "Chase Center"
163
+ # @return [String] the arena name
164
+ attribute :arena_name, Shale::Type::String
165
+
166
+ # @!attribute [rw] arena_city
167
+ # Returns the arena city
168
+ # @api public
169
+ # @example
170
+ # game.arena_city #=> "San Francisco"
171
+ # @return [String] the arena city
172
+ attribute :arena_city, Shale::Type::String
173
+
174
+ # @!attribute [rw] arena_state
175
+ # Returns the arena state
176
+ # @api public
177
+ # @example
178
+ # game.arena_state #=> "CA"
179
+ # @return [String] the arena state
180
+ attribute :arena_state, Shale::Type::String
181
+
182
+ # @!attribute [rw] broadcasters
183
+ # Returns the broadcaster information
184
+ # @api public
185
+ # @example
186
+ # game.broadcasters #=> "TNT"
187
+ # @return [String] the broadcasters
188
+ attribute :broadcasters, Shale::Type::String
189
+
190
+ # Returns the home team object
191
+ #
192
+ # @api public
193
+ # @example
194
+ # game.home_team #=> #<NBA::Team>
195
+ # @return [Team, nil] the home team object
196
+ def home_team
197
+ Teams.find(home_team_id)
198
+ end
199
+
200
+ # Returns the away team object
201
+ #
202
+ # @api public
203
+ # @example
204
+ # game.away_team #=> #<NBA::Team>
205
+ # @return [Team, nil] the away team object
206
+ def away_team
207
+ Teams.find(away_team_id)
208
+ end
209
+
210
+ # Returns whether the game is scheduled
211
+ #
212
+ # @api public
213
+ # @example
214
+ # game.scheduled? #=> true
215
+ # @return [Boolean] true if scheduled
216
+ def scheduled?
217
+ game_status.eql?(1)
218
+ end
219
+
220
+ # Returns whether the game is in progress
221
+ #
222
+ # @api public
223
+ # @example
224
+ # game.in_progress? #=> false
225
+ # @return [Boolean] true if in progress
226
+ def in_progress?
227
+ game_status.eql?(2)
228
+ end
229
+
230
+ # Returns whether the game is final
231
+ #
232
+ # @api public
233
+ # @example
234
+ # game.final? #=> false
235
+ # @return [Boolean] true if final
236
+ def final?
237
+ game_status.eql?(3)
238
+ end
239
+ end
240
+ end