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.
- checksums.yaml +5 -5
- data/AGENTS.md +362 -0
- data/CHANGELOG.md +169 -0
- data/CLAUDE.md +1 -0
- data/LICENSE +21 -0
- data/README.md +501 -101
- data/bin/console +10 -0
- data/bin/setup +6 -0
- data/exe/nba +8 -0
- data/lib/nba/all_time_leader.rb +77 -0
- data/lib/nba/all_time_leaders.rb +185 -0
- data/lib/nba/assist_leader.rb +92 -0
- data/lib/nba/assist_leaders.rb +64 -0
- data/lib/nba/assist_tracker.rb +108 -0
- data/lib/nba/assist_tracker_entry.rb +206 -0
- data/lib/nba/award.rb +128 -0
- data/lib/nba/box_score.rb +2 -0
- data/lib/nba/box_score_advanced.rb +114 -0
- data/lib/nba/box_score_advanced_player_stat.rb +297 -0
- data/lib/nba/box_score_advanced_team_stat.rb +237 -0
- data/lib/nba/box_score_advanced_v3.rb +124 -0
- data/lib/nba/box_score_defensive_player_stat.rb +281 -0
- data/lib/nba/box_score_defensive_team_stat.rb +85 -0
- data/lib/nba/box_score_defensive_v2.rb +190 -0
- data/lib/nba/box_score_four_factors.rb +91 -0
- data/lib/nba/box_score_four_factors_player_stat.rb +185 -0
- data/lib/nba/box_score_four_factors_team_stat.rb +141 -0
- data/lib/nba/box_score_four_factors_v3.rb +133 -0
- data/lib/nba/box_score_hustle.rb +226 -0
- data/lib/nba/box_score_hustle_player_stat.rb +233 -0
- data/lib/nba/box_score_hustle_team_stat.rb +189 -0
- data/lib/nba/box_score_matchup_stat.rb +417 -0
- data/lib/nba/box_score_matchups_v3.rb +184 -0
- data/lib/nba/box_score_misc.rb +100 -0
- data/lib/nba/box_score_misc_player_stat.rb +217 -0
- data/lib/nba/box_score_misc_team_stat.rb +173 -0
- data/lib/nba/box_score_misc_v3.rb +163 -0
- data/lib/nba/box_score_player_stat.rb +273 -0
- data/lib/nba/box_score_player_track.rb +223 -0
- data/lib/nba/box_score_player_track_stat.rb +273 -0
- data/lib/nba/box_score_player_track_team_stat.rb +229 -0
- data/lib/nba/box_score_scoring.rb +103 -0
- data/lib/nba/box_score_scoring_player_stat.rb +241 -0
- data/lib/nba/box_score_scoring_team_stat.rb +197 -0
- data/lib/nba/box_score_scoring_v3.rb +170 -0
- data/lib/nba/box_score_similarity_score.rb +119 -0
- data/lib/nba/box_score_similarity_stat.rb +76 -0
- data/lib/nba/box_score_starter_bench_stat.rb +257 -0
- data/lib/nba/box_score_summary.rb +285 -0
- data/lib/nba/box_score_summary_v2.rb +202 -0
- data/lib/nba/box_score_summary_v3.rb +120 -0
- data/lib/nba/box_score_summary_v3_data.rb +419 -0
- data/lib/nba/box_score_team_stat.rb +229 -0
- data/lib/nba/box_score_traditional.rb +101 -0
- data/lib/nba/box_score_traditional_v3.rb +195 -0
- data/lib/nba/box_score_usage.rb +102 -0
- data/lib/nba/box_score_usage_player_stat.rb +265 -0
- data/lib/nba/box_score_usage_team_stat.rb +221 -0
- data/lib/nba/box_score_usage_v3.rb +169 -0
- data/lib/nba/box_score_v3_helpers.rb +144 -0
- data/lib/nba/career_stats.rb +217 -0
- data/lib/nba/cli/display/player_display.rb +98 -0
- data/lib/nba/cli/display.rb +178 -0
- data/lib/nba/cli/formatters/game_formatters.rb +86 -0
- data/lib/nba/cli/formatters/leaders_formatters.rb +26 -0
- data/lib/nba/cli/formatters/player_formatters.rb +52 -0
- data/lib/nba/cli/formatters/standings_formatters.rb +26 -0
- data/lib/nba/cli/formatters/team_formatters.rb +67 -0
- data/lib/nba/cli/formatters/time_formatters.rb +82 -0
- data/lib/nba/cli/formatters.rb +56 -0
- data/lib/nba/cli/helpers.rb +135 -0
- data/lib/nba/cli.rb +171 -20
- data/lib/nba/client.rb +35 -0
- data/lib/nba/collection.rb +89 -0
- data/lib/nba/college_player_stat.rb +200 -0
- data/lib/nba/common_player_info.rb +142 -0
- data/lib/nba/common_playoff_series.rb +90 -0
- data/lib/nba/common_team_years.rb +113 -0
- data/lib/nba/conference.rb +39 -0
- data/lib/nba/connection.rb +84 -0
- data/lib/nba/cume_stats_player.rb +358 -0
- data/lib/nba/cume_stats_player_game.rb +217 -0
- data/lib/nba/cume_stats_player_games.rb +99 -0
- data/lib/nba/cume_stats_player_games_entry.rb +25 -0
- data/lib/nba/cume_stats_player_total.rb +481 -0
- data/lib/nba/cume_stats_team.rb +349 -0
- data/lib/nba/cume_stats_team_games.rb +145 -0
- data/lib/nba/cume_stats_team_games_entry.rb +25 -0
- data/lib/nba/cume_stats_team_player.rb +485 -0
- data/lib/nba/cume_stats_team_total.rb +267 -0
- data/lib/nba/data.rb +73 -0
- data/lib/nba/defense_hub.rb +109 -0
- data/lib/nba/defense_hub_stat.rb +57 -0
- data/lib/nba/defensive_shot_stat.rb +102 -0
- data/lib/nba/division.rb +49 -0
- data/lib/nba/draft_board.rb +126 -0
- data/lib/nba/draft_board_pick.rb +173 -0
- data/lib/nba/draft_combine_anthro_measurement.rb +163 -0
- data/lib/nba/draft_combine_drill_result.rb +115 -0
- data/lib/nba/draft_combine_drill_results.rb +112 -0
- data/lib/nba/draft_combine_non_stationary_shooting.rb +268 -0
- data/lib/nba/draft_combine_non_stationary_shooting_result.rb +355 -0
- data/lib/nba/draft_combine_player_anthro.rb +133 -0
- data/lib/nba/draft_combine_spot_shooting.rb +243 -0
- data/lib/nba/draft_combine_spot_shooting_result.rb +419 -0
- data/lib/nba/draft_combine_stat.rb +211 -0
- data/lib/nba/draft_combine_stats.rb +160 -0
- data/lib/nba/draft_history.rb +142 -0
- data/lib/nba/draft_pick.rb +154 -0
- data/lib/nba/dunk_score_leader.rb +93 -0
- data/lib/nba/dunk_score_leaders.rb +77 -0
- data/lib/nba/estimated_metrics_stat.rb +152 -0
- data/lib/nba/fantasy_profile_stat.rb +142 -0
- data/lib/nba/fantasy_widget.rb +72 -0
- data/lib/nba/fantasy_widget_player.rb +98 -0
- data/lib/nba/found_game.rb +260 -0
- data/lib/nba/franchise.rb +136 -0
- data/lib/nba/franchise_history.rb +142 -0
- data/lib/nba/franchise_leader.rb +147 -0
- data/lib/nba/franchise_leaders.rb +162 -0
- data/lib/nba/franchise_player.rb +224 -0
- data/lib/nba/franchise_players.rb +147 -0
- data/lib/nba/game.rb +80 -64
- data/lib/nba/game_log.rb +349 -0
- data/lib/nba/game_rotation.rb +152 -0
- data/lib/nba/game_streak.rb +102 -0
- data/lib/nba/games.rb +46 -0
- data/lib/nba/home_page_leader.rb +99 -0
- data/lib/nba/home_page_leaders.rb +75 -0
- data/lib/nba/home_page_stat.rb +57 -0
- data/lib/nba/home_page_v2.rb +110 -0
- data/lib/nba/hustle_stats_box_score.rb +182 -0
- data/lib/nba/infographic_fan_duel_player.rb +139 -0
- data/lib/nba/infographic_fan_duel_player_stat.rb +311 -0
- data/lib/nba/ist_standing.rb +167 -0
- data/lib/nba/ist_standings.rb +81 -0
- data/lib/nba/leader.rb +103 -0
- data/lib/nba/leaders.rb +110 -0
- data/lib/nba/leaders_tile.rb +57 -0
- data/lib/nba/leaders_tiles.rb +90 -0
- data/lib/nba/league.rb +37 -0
- data/lib/nba/league_dash_lineup_stat.rb +270 -0
- data/lib/nba/league_dash_lineups.rb +177 -0
- data/lib/nba/league_dash_opp_pt_shot.rb +150 -0
- data/lib/nba/league_dash_player_bio_stat.rb +217 -0
- data/lib/nba/league_dash_player_bio_stats.rb +164 -0
- data/lib/nba/league_dash_player_clutch.rb +212 -0
- data/lib/nba/league_dash_player_clutch_stat.rb +271 -0
- data/lib/nba/league_dash_player_pt_shot.rb +152 -0
- data/lib/nba/league_dash_player_pt_shot_stat.rb +193 -0
- data/lib/nba/league_dash_player_shot_location_stat.rb +265 -0
- data/lib/nba/league_dash_player_shot_locations.rb +210 -0
- data/lib/nba/league_dash_player_stat.rb +306 -0
- data/lib/nba/league_dash_player_stats.rb +176 -0
- data/lib/nba/league_dash_pt_defend.rb +160 -0
- data/lib/nba/league_dash_pt_defend_stat.rb +145 -0
- data/lib/nba/league_dash_pt_stats.rb +152 -0
- data/lib/nba/league_dash_pt_stats_stat.rb +169 -0
- data/lib/nba/league_dash_pt_team_defend.rb +158 -0
- data/lib/nba/league_dash_pt_team_defend_stat.rb +110 -0
- data/lib/nba/league_dash_team_clutch.rb +211 -0
- data/lib/nba/league_dash_team_clutch_stat.rb +237 -0
- data/lib/nba/league_dash_team_pt_shot.rb +150 -0
- data/lib/nba/league_dash_team_pt_shot_stat.rb +166 -0
- data/lib/nba/league_dash_team_shot_location_stat.rb +230 -0
- data/lib/nba/league_dash_team_shot_locations.rb +208 -0
- data/lib/nba/league_dash_team_stat.rb +275 -0
- data/lib/nba/league_dash_team_stats.rb +172 -0
- data/lib/nba/league_game_finder.rb +170 -0
- data/lib/nba/league_game_log.rb +224 -0
- data/lib/nba/league_hustle_stats_player.rb +161 -0
- data/lib/nba/league_hustle_stats_player_stat.rb +253 -0
- data/lib/nba/league_hustle_stats_team.rb +157 -0
- data/lib/nba/league_hustle_stats_team_stat.rb +179 -0
- data/lib/nba/league_lineup_viz.rb +184 -0
- data/lib/nba/league_lineup_viz_stat.rb +214 -0
- data/lib/nba/league_player_on_details.rb +175 -0
- data/lib/nba/league_player_on_details_stat.rb +313 -0
- data/lib/nba/league_season_matchup_stat.rb +241 -0
- data/lib/nba/league_season_matchups.rb +181 -0
- data/lib/nba/league_standing.rb +284 -0
- data/lib/nba/league_standings.rb +159 -0
- data/lib/nba/league_wide_shot_stat.rb +62 -0
- data/lib/nba/live_action.rb +240 -0
- data/lib/nba/live_box_score.rb +143 -0
- data/lib/nba/live_connection.rb +84 -0
- data/lib/nba/live_game.rb +230 -0
- data/lib/nba/live_play_by_play.rb +120 -0
- data/lib/nba/live_player_stat.rb +276 -0
- data/lib/nba/live_scoreboard.rb +102 -0
- data/lib/nba/matchup_rollup.rb +98 -0
- data/lib/nba/matchups_rollup.rb +81 -0
- data/lib/nba/pass_stat.rb +209 -0
- data/lib/nba/play.rb +258 -0
- data/lib/nba/play_by_play.rb +85 -0
- data/lib/nba/play_by_play_v3.rb +91 -0
- data/lib/nba/play_type_stat.rb +206 -0
- data/lib/nba/player.rb +242 -24
- data/lib/nba/player_awards.rb +110 -0
- data/lib/nba/player_career_by_college.rb +86 -0
- data/lib/nba/player_career_by_college_rollup.rb +143 -0
- data/lib/nba/player_career_stats.rb +77 -0
- data/lib/nba/player_compare.rb +156 -0
- data/lib/nba/player_comparison_stat.rb +242 -0
- data/lib/nba/player_dash_pt_pass.rb +164 -0
- data/lib/nba/player_dash_pt_reb.rb +235 -0
- data/lib/nba/player_dash_pt_shot_defend.rb +119 -0
- data/lib/nba/player_dash_pt_shots.rb +279 -0
- data/lib/nba/player_dashboard.rb +259 -0
- data/lib/nba/player_dashboard_stat.rb +248 -0
- data/lib/nba/player_estimated_metrics.rb +84 -0
- data/lib/nba/player_fantasy_profile_bar_graph.rb +147 -0
- data/lib/nba/player_game_log.rb +72 -0
- data/lib/nba/player_game_logs.rb +117 -0
- data/lib/nba/player_game_streak_finder.rb +108 -0
- data/lib/nba/player_index.rb +135 -0
- data/lib/nba/player_index_entry.rb +266 -0
- data/lib/nba/player_info.rb +225 -0
- data/lib/nba/player_next_n_games.rb +64 -0
- data/lib/nba/player_profile_v2.rb +169 -0
- data/lib/nba/player_vs_player.rb +153 -0
- data/lib/nba/players.rb +107 -0
- data/lib/nba/playoff_matchup.rb +84 -0
- data/lib/nba/playoff_picture.rb +98 -0
- data/lib/nba/playoff_series.rb +76 -0
- data/lib/nba/position.rb +48 -0
- data/lib/nba/rebound_stat.rb +189 -0
- data/lib/nba/response_parser.rb +116 -0
- data/lib/nba/roster.rb +74 -0
- data/lib/nba/rotation_entry.rb +154 -0
- data/lib/nba/schedule.rb +183 -0
- data/lib/nba/schedule_international.rb +182 -0
- data/lib/nba/scheduled_game.rb +240 -0
- data/lib/nba/scoreboard.rb +183 -0
- data/lib/nba/scoreboard_v3.rb +104 -0
- data/lib/nba/shot.rb +208 -0
- data/lib/nba/shot_chart.rb +75 -0
- data/lib/nba/shot_chart_league_wide.rb +102 -0
- data/lib/nba/shot_chart_lineup_detail.rb +109 -0
- data/lib/nba/shot_stat.rb +174 -0
- data/lib/nba/standing.rb +129 -0
- data/lib/nba/standings.rb +75 -0
- data/lib/nba/static.rb +107 -0
- data/lib/nba/synergy_play_types.rb +211 -0
- data/lib/nba/team.rb +203 -127
- data/lib/nba/team_and_players_vs_players.rb +227 -0
- data/lib/nba/team_and_players_vs_players_stat.rb +155 -0
- data/lib/nba/team_dash_pt_pass.rb +157 -0
- data/lib/nba/team_dash_pt_reb.rb +216 -0
- data/lib/nba/team_dash_pt_shots.rb +244 -0
- data/lib/nba/team_dashboard.rb +275 -0
- data/lib/nba/team_dashboard_stat.rb +248 -0
- data/lib/nba/team_detail.rb +117 -0
- data/lib/nba/team_details.rb +173 -0
- data/lib/nba/team_estimated_metrics.rb +91 -0
- data/lib/nba/team_estimated_metrics_stat.rb +146 -0
- data/lib/nba/team_game_log.rb +143 -0
- data/lib/nba/team_game_log_entry.rb +246 -0
- data/lib/nba/team_game_log_stat.rb +275 -0
- data/lib/nba/team_game_logs.rb +163 -0
- data/lib/nba/team_game_streak.rb +111 -0
- data/lib/nba/team_game_streak_finder.rb +109 -0
- data/lib/nba/team_historical_leader.rb +207 -0
- data/lib/nba/team_historical_leaders.rb +98 -0
- data/lib/nba/team_historical_record.rb +139 -0
- data/lib/nba/team_info.rb +150 -0
- data/lib/nba/team_info_common.rb +177 -0
- data/lib/nba/team_on_off_overall_stat.rb +477 -0
- data/lib/nba/team_on_off_player_stat.rb +523 -0
- data/lib/nba/team_on_off_player_summary.rb +135 -0
- data/lib/nba/team_pass_stat.rb +183 -0
- data/lib/nba/team_player_dashboard.rb +212 -0
- data/lib/nba/team_player_on_off_details.rb +218 -0
- data/lib/nba/team_player_on_off_summary.rb +214 -0
- data/lib/nba/team_player_stat.rb +275 -0
- data/lib/nba/team_rebound_stat.rb +189 -0
- data/lib/nba/team_season_rank.rb +110 -0
- data/lib/nba/team_shot_stat.rb +173 -0
- data/lib/nba/team_vs_player.rb +151 -0
- data/lib/nba/team_vs_player_stat.rb +157 -0
- data/lib/nba/team_year.rb +55 -0
- data/lib/nba/team_year_by_year_stats.rb +152 -0
- data/lib/nba/team_year_stat.rb +282 -0
- data/lib/nba/teams.rb +33 -0
- data/lib/nba/upcoming_game.rb +115 -0
- data/lib/nba/utils.rb +94 -0
- data/lib/nba/version.rb +5 -2
- data/lib/nba/video_detail.rb +103 -0
- data/lib/nba/video_details.rb +118 -0
- data/lib/nba/video_details_asset.rb +115 -0
- data/lib/nba/video_details_asset_entry.rb +91 -0
- data/lib/nba/video_event.rb +83 -0
- data/lib/nba/video_event_asset.rb +91 -0
- data/lib/nba/video_events.rb +106 -0
- data/lib/nba/video_events_asset.rb +107 -0
- data/lib/nba/video_status.rb +129 -0
- data/lib/nba/video_status_entry.rb +161 -0
- data/lib/nba/vs_player_stat.rb +156 -0
- data/lib/nba/win_probability.rb +117 -0
- data/lib/nba/win_probability_point.rb +140 -0
- data/lib/nba.rb +249 -5
- data/sig/equalizer.rbs +3 -0
- data/sig/nba.rbs +7297 -0
- data/sig/shale.rbs +24 -0
- data/sig/thor.rbs +19 -0
- metadata +324 -95
- data/.gitignore +0 -18
- data/.travis.yml +0 -22
- data/Gemfile +0 -23
- data/LICENSE.md +0 -22
- data/Rakefile +0 -18
- data/bin/nba +0 -7
- data/cache/teams.json +0 -16529
- data/lib/faraday_middleware/scrape_game.rb +0 -41
- data/lib/nba/request.rb +0 -37
- data/nba.gemspec +0 -28
- data/spec/fixtures/games.html +0 -785
- data/spec/fixtures/teams.json +0 -16529
- data/spec/game_spec.rb +0 -40
- data/spec/spec_helper.rb +0 -25
- data/spec/team_spec.rb +0 -93
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require_relative "client"
|
|
3
|
+
require_relative "collection"
|
|
4
|
+
require_relative "cume_stats_team_player"
|
|
5
|
+
require_relative "cume_stats_team_total"
|
|
6
|
+
require_relative "utils"
|
|
7
|
+
|
|
8
|
+
module NBA
|
|
9
|
+
# Provides methods to retrieve cumulative team statistics
|
|
10
|
+
module CumeStatsTeam
|
|
11
|
+
# Result set name for game by game stats
|
|
12
|
+
# @return [String] the result set name
|
|
13
|
+
GAME_BY_GAME_STATS = "GameByGameStats".freeze
|
|
14
|
+
|
|
15
|
+
# Result set name for total team stats
|
|
16
|
+
# @return [String] the result set name
|
|
17
|
+
TOTAL_TEAM_STATS = "TotalTeamStats".freeze
|
|
18
|
+
|
|
19
|
+
# Retrieves cumulative team statistics for specific games
|
|
20
|
+
#
|
|
21
|
+
# @api public
|
|
22
|
+
# @example
|
|
23
|
+
# result = NBA::CumeStatsTeam.find(team: 1610612744, game_ids: ["0022400001", "0022400002"], season: 2024)
|
|
24
|
+
# result[:game_by_game].each { |p| puts "#{p.player_name}: #{p.pts} pts" }
|
|
25
|
+
# result[:total].pts #=> 220
|
|
26
|
+
# @param team [Integer, Team] the team ID or Team object
|
|
27
|
+
# @param game_ids [Array<String>, String] game IDs (array or pipe-separated string)
|
|
28
|
+
# @param season [Integer] the season year
|
|
29
|
+
# @param season_type [String] the season type
|
|
30
|
+
# @param league [String, League] the league ID or League object
|
|
31
|
+
# @param client [Client] the API client to use
|
|
32
|
+
# @return [Hash, nil] hash with :game_by_game (Collection) and :total (CumeStatsTeamTotal) keys, or nil
|
|
33
|
+
def self.find(team:, game_ids:, season:, season_type: "Regular Season", league: League::NBA, client: CLIENT)
|
|
34
|
+
team_id = Utils.extract_id(team)
|
|
35
|
+
return unless team_id
|
|
36
|
+
|
|
37
|
+
game_ids_str = normalize_game_ids(game_ids)
|
|
38
|
+
return unless game_ids_str
|
|
39
|
+
|
|
40
|
+
path = build_path(team_id, game_ids_str, season, season_type, league)
|
|
41
|
+
response = client.get(path)
|
|
42
|
+
parse_response(response)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Normalizes game IDs to pipe-separated string
|
|
46
|
+
#
|
|
47
|
+
# @api private
|
|
48
|
+
# @param game_ids [Array<String>, String] game IDs
|
|
49
|
+
# @return [String, nil] pipe-separated game IDs or nil
|
|
50
|
+
def self.normalize_game_ids(game_ids)
|
|
51
|
+
return if game_ids.nil?
|
|
52
|
+
return game_ids if game_ids.instance_of?(String)
|
|
53
|
+
return if game_ids.empty?
|
|
54
|
+
|
|
55
|
+
game_ids.join("|")
|
|
56
|
+
end
|
|
57
|
+
private_class_method :normalize_game_ids
|
|
58
|
+
|
|
59
|
+
# Builds the API request path
|
|
60
|
+
#
|
|
61
|
+
# @api private
|
|
62
|
+
# @param team_id [Integer] the team ID
|
|
63
|
+
# @param game_ids [String] pipe-separated game IDs
|
|
64
|
+
# @param season [Integer] the season year
|
|
65
|
+
# @param season_type [String] the season type
|
|
66
|
+
# @param league [String, League] the league ID or League object
|
|
67
|
+
# @return [String] the API request path
|
|
68
|
+
def self.build_path(team_id, game_ids, season, season_type, league)
|
|
69
|
+
season_str = Utils.format_season(season)
|
|
70
|
+
league_id = Utils.extract_id(league)
|
|
71
|
+
"cumestatsteam?TeamID=#{team_id}&GameIDs=#{game_ids}&LeagueID=#{league_id}&Season=#{season_str}&SeasonType=#{season_type}"
|
|
72
|
+
end
|
|
73
|
+
private_class_method :build_path
|
|
74
|
+
|
|
75
|
+
# Parses the API response
|
|
76
|
+
#
|
|
77
|
+
# @api private
|
|
78
|
+
# @param response [String, nil] the JSON response
|
|
79
|
+
# @return [Hash, nil] parsed result hash or nil
|
|
80
|
+
def self.parse_response(response)
|
|
81
|
+
return unless response
|
|
82
|
+
|
|
83
|
+
data = JSON.parse(response)
|
|
84
|
+
return unless data
|
|
85
|
+
|
|
86
|
+
game_by_game = parse_game_by_game(data)
|
|
87
|
+
total = parse_total(data)
|
|
88
|
+
|
|
89
|
+
return unless game_by_game || total
|
|
90
|
+
|
|
91
|
+
{game_by_game: game_by_game, total: total}
|
|
92
|
+
rescue JSON::ParserError
|
|
93
|
+
nil
|
|
94
|
+
end
|
|
95
|
+
private_class_method :parse_response
|
|
96
|
+
|
|
97
|
+
# Parses game by game stats
|
|
98
|
+
#
|
|
99
|
+
# @api private
|
|
100
|
+
# @param data [Hash] the parsed JSON data
|
|
101
|
+
# @return [Collection, nil] collection of player stats or nil
|
|
102
|
+
def self.parse_game_by_game(data)
|
|
103
|
+
result_set = find_result_set(data, GAME_BY_GAME_STATS)
|
|
104
|
+
return unless result_set
|
|
105
|
+
|
|
106
|
+
build_player_collection(result_set)
|
|
107
|
+
end
|
|
108
|
+
private_class_method :parse_game_by_game
|
|
109
|
+
|
|
110
|
+
# Parses total team stats
|
|
111
|
+
#
|
|
112
|
+
# @api private
|
|
113
|
+
# @param data [Hash] the parsed JSON data
|
|
114
|
+
# @return [CumeStatsTeamTotal, nil] total stats or nil
|
|
115
|
+
def self.parse_total(data)
|
|
116
|
+
result_set = find_result_set(data, TOTAL_TEAM_STATS)
|
|
117
|
+
return unless result_set
|
|
118
|
+
|
|
119
|
+
build_total(result_set)
|
|
120
|
+
end
|
|
121
|
+
private_class_method :parse_total
|
|
122
|
+
|
|
123
|
+
# Finds a result set by name
|
|
124
|
+
#
|
|
125
|
+
# @api private
|
|
126
|
+
# @param data [Hash] the parsed JSON data
|
|
127
|
+
# @param name [String] the result set name
|
|
128
|
+
# @return [Hash, nil] the result set or nil
|
|
129
|
+
def self.find_result_set(data, name)
|
|
130
|
+
result_sets = data["resultSets"]
|
|
131
|
+
return unless result_sets
|
|
132
|
+
|
|
133
|
+
result_sets.find { |rs| rs["name"].eql?(name) }
|
|
134
|
+
end
|
|
135
|
+
private_class_method :find_result_set
|
|
136
|
+
|
|
137
|
+
# Builds player collection from result set
|
|
138
|
+
#
|
|
139
|
+
# @api private
|
|
140
|
+
# @param result_set [Hash] the result set
|
|
141
|
+
# @return [Collection] collection of player stats
|
|
142
|
+
def self.build_player_collection(result_set)
|
|
143
|
+
headers = result_set["headers"]
|
|
144
|
+
rows = result_set["rowSet"]
|
|
145
|
+
return Collection.new unless headers && rows
|
|
146
|
+
|
|
147
|
+
players = rows.map { |row| build_player(headers, row) }
|
|
148
|
+
Collection.new(players)
|
|
149
|
+
end
|
|
150
|
+
private_class_method :build_player_collection
|
|
151
|
+
|
|
152
|
+
# Builds a player stat object
|
|
153
|
+
#
|
|
154
|
+
# @api private
|
|
155
|
+
# @param headers [Array<String>] the headers
|
|
156
|
+
# @param row [Array] the row data
|
|
157
|
+
# @return [CumeStatsTeamPlayer] the player stat
|
|
158
|
+
def self.build_player(headers, row)
|
|
159
|
+
data = headers.zip(row).to_h
|
|
160
|
+
CumeStatsTeamPlayer.new(**PlayerAttributes.extract(data))
|
|
161
|
+
end
|
|
162
|
+
private_class_method :build_player
|
|
163
|
+
|
|
164
|
+
# Builds total stat object
|
|
165
|
+
#
|
|
166
|
+
# @api private
|
|
167
|
+
# @param result_set [Hash] the result set
|
|
168
|
+
# @return [CumeStatsTeamTotal, nil] total stat or nil
|
|
169
|
+
def self.build_total(result_set)
|
|
170
|
+
headers = result_set["headers"]
|
|
171
|
+
row = result_set.dig("rowSet", 0)
|
|
172
|
+
return unless headers && row
|
|
173
|
+
|
|
174
|
+
data = headers.zip(row).to_h
|
|
175
|
+
CumeStatsTeamTotal.new(**TotalAttributes.extract(data))
|
|
176
|
+
end
|
|
177
|
+
private_class_method :build_total
|
|
178
|
+
|
|
179
|
+
# Extracts player attributes from data
|
|
180
|
+
# @api private
|
|
181
|
+
module PlayerAttributes
|
|
182
|
+
# Extracts all player attributes from data
|
|
183
|
+
# @api private
|
|
184
|
+
# @param data [Hash] the row data
|
|
185
|
+
# @return [Hash] extracted attributes
|
|
186
|
+
def self.extract(data)
|
|
187
|
+
identity(data).merge(stats(data)).merge(averages(data)).merge(per_min(data))
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Extracts identity attributes from data
|
|
191
|
+
# @api private
|
|
192
|
+
# @param data [Hash] the row data
|
|
193
|
+
# @return [Hash] identity attributes
|
|
194
|
+
def self.identity(data)
|
|
195
|
+
{person_id: data["PERSON_ID"], player_name: data["PLAYER_NAME"],
|
|
196
|
+
jersey_num: data["JERSEY_NUM"], team_id: data["TEAM_ID"]}
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Extracts stat attributes from data
|
|
200
|
+
# @api private
|
|
201
|
+
# @param data [Hash] the row data
|
|
202
|
+
# @return [Hash] stat attributes
|
|
203
|
+
def self.stats(data)
|
|
204
|
+
time_stats(data).merge(shooting_stats(data)).merge(other_stats(data))
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Extracts time stat attributes from data
|
|
208
|
+
# @api private
|
|
209
|
+
# @param data [Hash] the row data
|
|
210
|
+
# @return [Hash] time stat attributes
|
|
211
|
+
def self.time_stats(data)
|
|
212
|
+
{gp: data["GP"], gs: data["GS"],
|
|
213
|
+
actual_minutes: data["ACTUAL_MINUTES"], actual_seconds: data["ACTUAL_SECONDS"]}
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Extracts shooting stat attributes from data
|
|
217
|
+
# @api private
|
|
218
|
+
# @param data [Hash] the row data
|
|
219
|
+
# @return [Hash] shooting stat attributes
|
|
220
|
+
def self.shooting_stats(data)
|
|
221
|
+
{fgm: data["FGM"], fga: data["FGA"], fg_pct: data["FG_PCT"],
|
|
222
|
+
fg3m: data["FG3M"], fg3a: data["FG3A"], fg3_pct: data["FG3_PCT"],
|
|
223
|
+
ftm: data["FTM"], fta: data["FTA"], ft_pct: data["FT_PCT"]}
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Extracts other stat attributes from data
|
|
227
|
+
# @api private
|
|
228
|
+
# @param data [Hash] the row data
|
|
229
|
+
# @return [Hash] other stat attributes
|
|
230
|
+
def self.other_stats(data)
|
|
231
|
+
{oreb: data["OREB"], dreb: data["DREB"], tot_reb: data["TOT_REB"],
|
|
232
|
+
ast: data["AST"], pf: data["PF"], stl: data["STL"],
|
|
233
|
+
tov: data["TOV"], blk: data["BLK"], pts: data["PTS"]}
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Extracts average stat attributes from data
|
|
237
|
+
# @api private
|
|
238
|
+
# @param data [Hash] the row data
|
|
239
|
+
# @return [Hash] average stat attributes
|
|
240
|
+
def self.averages(data)
|
|
241
|
+
{avg_minutes: data["AVG_MINUTES"], fgm_pg: data["FGM_PG"],
|
|
242
|
+
fga_pg: data["FGA_PG"], fg3m_pg: data["FG3M_PG"],
|
|
243
|
+
fg3a_pg: data["FG3A_PG"], ftm_pg: data["FTM_PG"],
|
|
244
|
+
fta_pg: data["FTA_PG"], oreb_pg: data["OREB_PG"],
|
|
245
|
+
dreb_pg: data["DREB_PG"], reb_pg: data["REB_PG"],
|
|
246
|
+
ast_pg: data["AST_PG"], pf_pg: data["PF_PG"],
|
|
247
|
+
stl_pg: data["STL_PG"], tov_pg: data["TOV_PG"],
|
|
248
|
+
blk_pg: data["BLK_PG"], pts_pg: data["PTS_PG"]}
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Extracts per-minute stat attributes from data
|
|
252
|
+
# @api private
|
|
253
|
+
# @param data [Hash] the row data
|
|
254
|
+
# @return [Hash] per-minute stat attributes
|
|
255
|
+
def self.per_min(data)
|
|
256
|
+
{fgm_per_min: data["FGM_PER_MIN"], fga_per_min: data["FGA_PER_MIN"],
|
|
257
|
+
fg3m_per_min: data["FG3M_PER_MIN"], fg3a_per_min: data["FG3A_PER_MIN"],
|
|
258
|
+
ftm_per_min: data["FTM_PER_MIN"], fta_per_min: data["FTA_PER_MIN"],
|
|
259
|
+
oreb_per_min: data["OREB_PER_MIN"], dreb_per_min: data["DREB_PER_MIN"],
|
|
260
|
+
reb_per_min: data["REB_PER_MIN"], ast_per_min: data["AST_PER_MIN"],
|
|
261
|
+
pf_per_min: data["PF_PER_MIN"], stl_per_min: data["STL_PER_MIN"],
|
|
262
|
+
tov_per_min: data["TOV_PER_MIN"], blk_per_min: data["BLK_PER_MIN"],
|
|
263
|
+
pts_per_min: data["PTS_PER_MIN"]}
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# Extracts total attributes from data
|
|
268
|
+
# @api private
|
|
269
|
+
module TotalAttributes
|
|
270
|
+
# Extracts all total attributes from data
|
|
271
|
+
# @api private
|
|
272
|
+
# @param data [Hash] the row data
|
|
273
|
+
# @return [Hash] extracted attributes
|
|
274
|
+
def self.extract(data)
|
|
275
|
+
identity(data).merge(record(data)).merge(stats(data))
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# Extracts identity attributes from data
|
|
279
|
+
# @api private
|
|
280
|
+
# @param data [Hash] the row data
|
|
281
|
+
# @return [Hash] identity attributes
|
|
282
|
+
def self.identity(data)
|
|
283
|
+
{team_id: data["TEAM_ID"], city: data["CITY"], nickname: data["NICKNAME"]}
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Extracts record attributes from data
|
|
287
|
+
# @api private
|
|
288
|
+
# @param data [Hash] the row data
|
|
289
|
+
# @return [Hash] record attributes
|
|
290
|
+
def self.record(data)
|
|
291
|
+
games(data).merge(wins_losses(data)).merge(other_record(data))
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Extracts game attributes from data
|
|
295
|
+
# @api private
|
|
296
|
+
# @param data [Hash] the row data
|
|
297
|
+
# @return [Hash] game attributes
|
|
298
|
+
def self.games(data)
|
|
299
|
+
{gp: data["GP"], gs: data["GS"]}
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# Extracts win and loss attributes from data
|
|
303
|
+
# @api private
|
|
304
|
+
# @param data [Hash] the row data
|
|
305
|
+
# @return [Hash] win and loss attributes
|
|
306
|
+
def self.wins_losses(data)
|
|
307
|
+
{w: data["W"], l: data["L"],
|
|
308
|
+
w_home: data["W_HOME"], l_home: data["L_HOME"],
|
|
309
|
+
w_road: data["W_ROAD"], l_road: data["L_ROAD"]}
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Extracts other record attributes from data
|
|
313
|
+
# @api private
|
|
314
|
+
# @param data [Hash] the row data
|
|
315
|
+
# @return [Hash] other record attributes
|
|
316
|
+
def self.other_record(data)
|
|
317
|
+
{team_turnovers: data["TEAM_TURNOVERS"], team_rebounds: data["TEAM_REBOUNDS"]}
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# Extracts stat attributes from data
|
|
321
|
+
# @api private
|
|
322
|
+
# @param data [Hash] the row data
|
|
323
|
+
# @return [Hash] stat attributes
|
|
324
|
+
def self.stats(data)
|
|
325
|
+
shooting(data).merge(other(data))
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# Extracts shooting attributes from data
|
|
329
|
+
# @api private
|
|
330
|
+
# @param data [Hash] the row data
|
|
331
|
+
# @return [Hash] shooting attributes
|
|
332
|
+
def self.shooting(data)
|
|
333
|
+
{fgm: data["FGM"], fga: data["FGA"], fg_pct: data["FG_PCT"],
|
|
334
|
+
fg3m: data["FG3M"], fg3a: data["FG3A"], fg3_pct: data["FG3_PCT"],
|
|
335
|
+
ftm: data["FTM"], fta: data["FTA"], ft_pct: data["FT_PCT"]}
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Extracts other stat attributes from data
|
|
339
|
+
# @api private
|
|
340
|
+
# @param data [Hash] the row data
|
|
341
|
+
# @return [Hash] other stat attributes
|
|
342
|
+
def self.other(data)
|
|
343
|
+
{oreb: data["OREB"], dreb: data["DREB"], tot_reb: data["TOT_REB"],
|
|
344
|
+
ast: data["AST"], pf: data["PF"], stl: data["STL"],
|
|
345
|
+
tov: data["TOV"], blk: data["BLK"], pts: data["PTS"]}
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require_relative "client"
|
|
3
|
+
require_relative "collection"
|
|
4
|
+
require_relative "cume_stats_team_games_entry"
|
|
5
|
+
require_relative "utils"
|
|
6
|
+
|
|
7
|
+
module NBA
|
|
8
|
+
# Provides methods to retrieve cumulative stats team games
|
|
9
|
+
module CumeStatsTeamGames
|
|
10
|
+
# Result set name
|
|
11
|
+
# @return [String] the result set name
|
|
12
|
+
RESULTS = "CumeStatsTeamGames".freeze
|
|
13
|
+
|
|
14
|
+
# Regular season type
|
|
15
|
+
# @return [String] the season type
|
|
16
|
+
REGULAR_SEASON = "Regular Season".freeze
|
|
17
|
+
|
|
18
|
+
# Playoffs season type
|
|
19
|
+
# @return [String] the season type
|
|
20
|
+
PLAYOFFS = "Playoffs".freeze
|
|
21
|
+
|
|
22
|
+
# Retrieves cumulative stats team games
|
|
23
|
+
#
|
|
24
|
+
# @api public
|
|
25
|
+
# @example
|
|
26
|
+
# games = NBA::CumeStatsTeamGames.all(team: 1610612747, season: 2023)
|
|
27
|
+
# games.each { |g| puts "#{g.matchup}: #{g.game_id}" }
|
|
28
|
+
# @param team [Integer, String, Team] the team ID or Team object
|
|
29
|
+
# @param season [Integer] the season year
|
|
30
|
+
# @param season_type [String] the season type (Regular Season, Playoffs)
|
|
31
|
+
# @param league [String, League] the league ID or League object (default NBA)
|
|
32
|
+
# @param location [String, nil] the location filter (Home, Road)
|
|
33
|
+
# @param outcome [String, nil] the outcome filter (W, L)
|
|
34
|
+
# @param season_id [String, nil] the season ID
|
|
35
|
+
# @param vs_conference [String, nil] the vs conference filter (East, West)
|
|
36
|
+
# @param vs_division [String, nil] the vs division filter
|
|
37
|
+
# @param vs_team [Integer, String, Team, nil] the vs team ID or Team object
|
|
38
|
+
# @param client [Client] the API client to use
|
|
39
|
+
# @return [Collection] a collection of cumulative stats team games entries
|
|
40
|
+
def self.all(team:, season:, season_type: REGULAR_SEASON, league: League::NBA, location: nil, outcome: nil,
|
|
41
|
+
season_id: nil, vs_conference: nil, vs_division: nil, vs_team: nil, client: CLIENT)
|
|
42
|
+
opts = {team: team, season: season, season_type: season_type, league: league, location: location,
|
|
43
|
+
outcome: outcome, season_id: season_id, vs_conference: vs_conference,
|
|
44
|
+
vs_division: vs_division, vs_team: vs_team}
|
|
45
|
+
path = PathBuilder.build(opts)
|
|
46
|
+
response = client.get(path)
|
|
47
|
+
parse_response(response)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Builds API path from options
|
|
51
|
+
# @api private
|
|
52
|
+
module PathBuilder
|
|
53
|
+
# Builds the complete API path from options
|
|
54
|
+
# @api private
|
|
55
|
+
# @param opts [Hash] the options hash
|
|
56
|
+
# @return [String] the complete API path
|
|
57
|
+
def self.build(opts)
|
|
58
|
+
"cumestatsteamgames?#{Utils.build_query(**build_params(opts))}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Builds all parameters from options
|
|
62
|
+
# @api private
|
|
63
|
+
# @param opts [Hash] the options hash
|
|
64
|
+
# @return [Hash] the merged parameters
|
|
65
|
+
def self.build_params(opts)
|
|
66
|
+
required_params(opts).merge(optional_params(opts))
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Builds required parameters from options
|
|
70
|
+
# @api private
|
|
71
|
+
# @param opts [Hash] the options hash
|
|
72
|
+
# @return [Hash] the required parameters
|
|
73
|
+
def self.required_params(opts)
|
|
74
|
+
{
|
|
75
|
+
TeamID: Utils.extract_id(opts.fetch(:team)),
|
|
76
|
+
LeagueID: Utils.extract_league_id(opts.fetch(:league)),
|
|
77
|
+
Season: Utils.format_season(opts.fetch(:season)),
|
|
78
|
+
SeasonType: opts.fetch(:season_type)
|
|
79
|
+
}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Builds optional parameters from options
|
|
83
|
+
# @api private
|
|
84
|
+
# @param opts [Hash] the options hash
|
|
85
|
+
# @return [Hash] the optional parameters
|
|
86
|
+
def self.optional_params(opts)
|
|
87
|
+
{
|
|
88
|
+
Location: opts.fetch(:location), Outcome: opts.fetch(:outcome), SeasonID: opts.fetch(:season_id),
|
|
89
|
+
VsConference: opts.fetch(:vs_conference), VsDivision: opts.fetch(:vs_division),
|
|
90
|
+
VsTeamID: Utils.extract_id(opts.fetch(:vs_team))
|
|
91
|
+
}
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Parses the API response into entry objects
|
|
96
|
+
# @api private
|
|
97
|
+
# @return [Collection] collection of entries
|
|
98
|
+
def self.parse_response(response)
|
|
99
|
+
return Collection.new unless response
|
|
100
|
+
|
|
101
|
+
data = JSON.parse(response)
|
|
102
|
+
result_set = find_result_set(data)
|
|
103
|
+
return Collection.new unless result_set
|
|
104
|
+
|
|
105
|
+
headers = result_set["headers"]
|
|
106
|
+
rows = result_set["rowSet"]
|
|
107
|
+
return Collection.new unless headers && rows
|
|
108
|
+
|
|
109
|
+
entries = rows.map { |row| build_entry(headers, row) }
|
|
110
|
+
Collection.new(entries)
|
|
111
|
+
end
|
|
112
|
+
private_class_method :parse_response
|
|
113
|
+
|
|
114
|
+
# Finds the result set in the response
|
|
115
|
+
# @api private
|
|
116
|
+
# @return [Hash, nil] the result set hash
|
|
117
|
+
def self.find_result_set(data)
|
|
118
|
+
result_sets = data["resultSets"]
|
|
119
|
+
return unless result_sets
|
|
120
|
+
|
|
121
|
+
result_sets.find { |rs| rs["name"].eql?(RESULTS) }
|
|
122
|
+
end
|
|
123
|
+
private_class_method :find_result_set
|
|
124
|
+
|
|
125
|
+
# Builds a CumeStatsTeamGamesEntry object from raw data
|
|
126
|
+
# @api private
|
|
127
|
+
# @return [CumeStatsTeamGamesEntry] the entry object
|
|
128
|
+
def self.build_entry(headers, row)
|
|
129
|
+
data = headers.zip(row).to_h
|
|
130
|
+
CumeStatsTeamGamesEntry.new(**entry_attributes(data))
|
|
131
|
+
end
|
|
132
|
+
private_class_method :build_entry
|
|
133
|
+
|
|
134
|
+
# Extracts entry attributes from data
|
|
135
|
+
# @api private
|
|
136
|
+
# @return [Hash] the entry attributes
|
|
137
|
+
def self.entry_attributes(data)
|
|
138
|
+
{
|
|
139
|
+
matchup: data["MATCHUP"],
|
|
140
|
+
game_id: data["GAME_ID"]
|
|
141
|
+
}
|
|
142
|
+
end
|
|
143
|
+
private_class_method :entry_attributes
|
|
144
|
+
end
|
|
145
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require "equalizer"
|
|
2
|
+
require "shale"
|
|
3
|
+
|
|
4
|
+
module NBA
|
|
5
|
+
# Represents a cumulative stats team games entry
|
|
6
|
+
class CumeStatsTeamGamesEntry < Shale::Mapper
|
|
7
|
+
include Equalizer.new(:game_id)
|
|
8
|
+
|
|
9
|
+
# @!attribute [rw] game_id
|
|
10
|
+
# Returns the game ID
|
|
11
|
+
# @api public
|
|
12
|
+
# @example
|
|
13
|
+
# entry.game_id #=> 22300001
|
|
14
|
+
# @return [Integer] the game ID
|
|
15
|
+
attribute :game_id, Shale::Type::Integer
|
|
16
|
+
|
|
17
|
+
# @!attribute [rw] matchup
|
|
18
|
+
# Returns the game matchup
|
|
19
|
+
# @api public
|
|
20
|
+
# @example
|
|
21
|
+
# entry.matchup #=> "LAL @ DEN"
|
|
22
|
+
# @return [String] the matchup
|
|
23
|
+
attribute :matchup, Shale::Type::String
|
|
24
|
+
end
|
|
25
|
+
end
|