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,157 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require_relative "client"
|
|
3
|
+
require_relative "collection"
|
|
4
|
+
require_relative "utils"
|
|
5
|
+
|
|
6
|
+
module NBA
|
|
7
|
+
# Provides methods to retrieve league-wide team hustle statistics
|
|
8
|
+
module LeagueHustleStatsTeam
|
|
9
|
+
# Result set name for hustle stats
|
|
10
|
+
# @return [String] the result set name
|
|
11
|
+
HUSTLE_STATS_TEAM = "HustleStatsTeam".freeze
|
|
12
|
+
|
|
13
|
+
# Season type constant for regular season
|
|
14
|
+
# @return [String] the season type
|
|
15
|
+
REGULAR_SEASON = "Regular Season".freeze
|
|
16
|
+
|
|
17
|
+
# Season type constant for playoffs
|
|
18
|
+
# @return [String] the season type
|
|
19
|
+
PLAYOFFS = "Playoffs".freeze
|
|
20
|
+
|
|
21
|
+
# Per mode constant for per game stats
|
|
22
|
+
# @return [String] the per mode
|
|
23
|
+
PER_GAME = "PerGame".freeze
|
|
24
|
+
|
|
25
|
+
# Per mode constant for totals
|
|
26
|
+
# @return [String] the per mode
|
|
27
|
+
TOTALS = "Totals".freeze
|
|
28
|
+
|
|
29
|
+
# Retrieves league-wide team hustle statistics
|
|
30
|
+
#
|
|
31
|
+
# @api public
|
|
32
|
+
# @example
|
|
33
|
+
# stats = NBA::LeagueHustleStatsTeam.all(season: 2024)
|
|
34
|
+
# stats.each { |s| puts "#{s.team_name}: #{s.deflections} deflections" }
|
|
35
|
+
# @param season [Integer] the season year
|
|
36
|
+
# @param season_type [String] the season type
|
|
37
|
+
# @param per_mode [String] the per mode
|
|
38
|
+
# @param client [Client] the API client to use
|
|
39
|
+
# @return [Collection] a collection of team hustle stats
|
|
40
|
+
def self.all(season: Utils.current_season, season_type: REGULAR_SEASON, per_mode: PER_GAME, client: CLIENT)
|
|
41
|
+
path = build_path(season, season_type, per_mode)
|
|
42
|
+
response = client.get(path)
|
|
43
|
+
parse_response(response)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Builds the API path
|
|
47
|
+
#
|
|
48
|
+
# @api private
|
|
49
|
+
# @param season [Integer] the season year
|
|
50
|
+
# @param season_type [String] the season type
|
|
51
|
+
# @param per_mode [String] the per mode
|
|
52
|
+
# @return [String] the API path
|
|
53
|
+
def self.build_path(season, season_type, per_mode)
|
|
54
|
+
season_str = Utils.format_season(season)
|
|
55
|
+
"leaguehustlestatsteam?LeagueID=00&Season=#{season_str}&SeasonType=#{season_type}&PerMode=#{per_mode}"
|
|
56
|
+
end
|
|
57
|
+
private_class_method :build_path
|
|
58
|
+
|
|
59
|
+
# Parses the API response
|
|
60
|
+
#
|
|
61
|
+
# @api private
|
|
62
|
+
# @param response [String] the JSON response body
|
|
63
|
+
# @return [Collection] a collection of team hustle stats
|
|
64
|
+
def self.parse_response(response)
|
|
65
|
+
return Collection.new unless response
|
|
66
|
+
|
|
67
|
+
data = JSON.parse(response)
|
|
68
|
+
result_set = find_result_set(data)
|
|
69
|
+
return Collection.new unless result_set
|
|
70
|
+
|
|
71
|
+
headers = result_set["headers"]
|
|
72
|
+
rows = result_set["rowSet"]
|
|
73
|
+
return Collection.new unless headers && rows
|
|
74
|
+
|
|
75
|
+
stats = rows.map { |row| build_team_stat(headers, row) }
|
|
76
|
+
Collection.new(stats)
|
|
77
|
+
end
|
|
78
|
+
private_class_method :parse_response
|
|
79
|
+
|
|
80
|
+
# Finds the result set
|
|
81
|
+
#
|
|
82
|
+
# @api private
|
|
83
|
+
# @param data [Hash] the parsed JSON data
|
|
84
|
+
# @return [Hash, nil] the result set
|
|
85
|
+
def self.find_result_set(data)
|
|
86
|
+
result_sets = data["resultSets"]
|
|
87
|
+
return unless result_sets
|
|
88
|
+
|
|
89
|
+
result_sets.find { |rs| rs["name"].eql?(HUSTLE_STATS_TEAM) }
|
|
90
|
+
end
|
|
91
|
+
private_class_method :find_result_set
|
|
92
|
+
|
|
93
|
+
# Builds a team stat from a row
|
|
94
|
+
#
|
|
95
|
+
# @api private
|
|
96
|
+
# @param headers [Array<String>] the column headers
|
|
97
|
+
# @param row [Array] the row data
|
|
98
|
+
# @return [LeagueHustleStatsTeamStat] the team stat object
|
|
99
|
+
def self.build_team_stat(headers, row)
|
|
100
|
+
data = headers.zip(row).to_h
|
|
101
|
+
LeagueHustleStatsTeamStat.new(**team_stat_attributes(data))
|
|
102
|
+
end
|
|
103
|
+
private_class_method :build_team_stat
|
|
104
|
+
|
|
105
|
+
# Extracts team stat attributes from row data
|
|
106
|
+
#
|
|
107
|
+
# @api private
|
|
108
|
+
# @param data [Hash] the team stat row data
|
|
109
|
+
# @return [Hash] the team stat attributes
|
|
110
|
+
def self.team_stat_attributes(data)
|
|
111
|
+
identity_attributes(data).merge(hustle_attributes(data), box_out_attributes(data))
|
|
112
|
+
end
|
|
113
|
+
private_class_method :team_stat_attributes
|
|
114
|
+
|
|
115
|
+
# Extracts identity attributes
|
|
116
|
+
#
|
|
117
|
+
# @api private
|
|
118
|
+
# @param data [Hash] the team stat data
|
|
119
|
+
# @return [Hash] the identity attributes
|
|
120
|
+
def self.identity_attributes(data)
|
|
121
|
+
{team_id: data["TEAM_ID"], team_name: data["TEAM_NAME"],
|
|
122
|
+
min: data["MIN"]}
|
|
123
|
+
end
|
|
124
|
+
private_class_method :identity_attributes
|
|
125
|
+
|
|
126
|
+
# Extracts hustle attributes
|
|
127
|
+
#
|
|
128
|
+
# @api private
|
|
129
|
+
# @param data [Hash] the team stat data
|
|
130
|
+
# @return [Hash] the hustle attributes
|
|
131
|
+
def self.hustle_attributes(data)
|
|
132
|
+
{contested_shots: data["CONTESTED_SHOTS"],
|
|
133
|
+
contested_shots_2pt: data["CONTESTED_SHOTS_2PT"],
|
|
134
|
+
contested_shots_3pt: data["CONTESTED_SHOTS_3PT"],
|
|
135
|
+
deflections: data["DEFLECTIONS"], charges_drawn: data["CHARGES_DRAWN"],
|
|
136
|
+
screen_assists: data["SCREEN_ASSISTS"], screen_ast_pts: data["SCREEN_AST_PTS"],
|
|
137
|
+
off_loose_balls_recovered: data["OFF_LOOSE_BALLS_RECOVERED"],
|
|
138
|
+
def_loose_balls_recovered: data["DEF_LOOSE_BALLS_RECOVERED"],
|
|
139
|
+
loose_balls_recovered: data["LOOSE_BALLS_RECOVERED"],
|
|
140
|
+
pct_loose_balls_recovered_off: data["PCT_LOOSE_BALLS_RECOVERED_OFF"],
|
|
141
|
+
pct_loose_balls_recovered_def: data["PCT_LOOSE_BALLS_RECOVERED_DEF"]}
|
|
142
|
+
end
|
|
143
|
+
private_class_method :hustle_attributes
|
|
144
|
+
|
|
145
|
+
# Extracts box out attributes
|
|
146
|
+
#
|
|
147
|
+
# @api private
|
|
148
|
+
# @param data [Hash] the team stat data
|
|
149
|
+
# @return [Hash] the box out attributes
|
|
150
|
+
def self.box_out_attributes(data)
|
|
151
|
+
{off_boxouts: data["OFF_BOXOUTS"], def_boxouts: data["DEF_BOXOUTS"],
|
|
152
|
+
box_outs: data["BOX_OUTS"],
|
|
153
|
+
pct_box_outs_off: data["PCT_BOX_OUTS_OFF"], pct_box_outs_def: data["PCT_BOX_OUTS_DEF"]}
|
|
154
|
+
end
|
|
155
|
+
private_class_method :box_out_attributes
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
require "equalizer"
|
|
2
|
+
require "shale"
|
|
3
|
+
|
|
4
|
+
module NBA
|
|
5
|
+
# Represents league-wide hustle statistics for a team
|
|
6
|
+
class LeagueHustleStatsTeamStat < Shale::Mapper
|
|
7
|
+
include Equalizer.new(:team_id)
|
|
8
|
+
|
|
9
|
+
# @!attribute [rw] team_id
|
|
10
|
+
# Returns the team ID
|
|
11
|
+
# @api public
|
|
12
|
+
# @example
|
|
13
|
+
# stat.team_id #=> 1610612744
|
|
14
|
+
# @return [Integer] the team ID
|
|
15
|
+
attribute :team_id, Shale::Type::Integer
|
|
16
|
+
|
|
17
|
+
# @!attribute [rw] team_name
|
|
18
|
+
# Returns the team name
|
|
19
|
+
# @api public
|
|
20
|
+
# @example
|
|
21
|
+
# stat.team_name #=> "Golden State Warriors"
|
|
22
|
+
# @return [String] the team name
|
|
23
|
+
attribute :team_name, Shale::Type::String
|
|
24
|
+
|
|
25
|
+
# @!attribute [rw] min
|
|
26
|
+
# Returns total minutes played
|
|
27
|
+
# @api public
|
|
28
|
+
# @example
|
|
29
|
+
# stat.min #=> 19680.0
|
|
30
|
+
# @return [Float] total minutes
|
|
31
|
+
attribute :min, Shale::Type::Float
|
|
32
|
+
|
|
33
|
+
# @!attribute [rw] contested_shots
|
|
34
|
+
# Returns contested shots
|
|
35
|
+
# @api public
|
|
36
|
+
# @example
|
|
37
|
+
# stat.contested_shots #=> 2856
|
|
38
|
+
# @return [Integer] contested shots
|
|
39
|
+
attribute :contested_shots, Shale::Type::Integer
|
|
40
|
+
|
|
41
|
+
# @!attribute [rw] contested_shots_2pt
|
|
42
|
+
# Returns contested 2-point shots
|
|
43
|
+
# @api public
|
|
44
|
+
# @example
|
|
45
|
+
# stat.contested_shots_2pt #=> 1842
|
|
46
|
+
# @return [Integer] contested 2-point shots
|
|
47
|
+
attribute :contested_shots_2pt, Shale::Type::Integer
|
|
48
|
+
|
|
49
|
+
# @!attribute [rw] contested_shots_3pt
|
|
50
|
+
# Returns contested 3-point shots
|
|
51
|
+
# @api public
|
|
52
|
+
# @example
|
|
53
|
+
# stat.contested_shots_3pt #=> 1014
|
|
54
|
+
# @return [Integer] contested 3-point shots
|
|
55
|
+
attribute :contested_shots_3pt, Shale::Type::Integer
|
|
56
|
+
|
|
57
|
+
# @!attribute [rw] deflections
|
|
58
|
+
# Returns deflections
|
|
59
|
+
# @api public
|
|
60
|
+
# @example
|
|
61
|
+
# stat.deflections #=> 1024
|
|
62
|
+
# @return [Integer] deflections
|
|
63
|
+
attribute :deflections, Shale::Type::Integer
|
|
64
|
+
|
|
65
|
+
# @!attribute [rw] charges_drawn
|
|
66
|
+
# Returns charges drawn
|
|
67
|
+
# @api public
|
|
68
|
+
# @example
|
|
69
|
+
# stat.charges_drawn #=> 42
|
|
70
|
+
# @return [Integer] charges drawn
|
|
71
|
+
attribute :charges_drawn, Shale::Type::Integer
|
|
72
|
+
|
|
73
|
+
# @!attribute [rw] screen_assists
|
|
74
|
+
# Returns screen assists
|
|
75
|
+
# @api public
|
|
76
|
+
# @example
|
|
77
|
+
# stat.screen_assists #=> 1856
|
|
78
|
+
# @return [Integer] screen assists
|
|
79
|
+
attribute :screen_assists, Shale::Type::Integer
|
|
80
|
+
|
|
81
|
+
# @!attribute [rw] screen_ast_pts
|
|
82
|
+
# Returns points from screen assists
|
|
83
|
+
# @api public
|
|
84
|
+
# @example
|
|
85
|
+
# stat.screen_ast_pts #=> 3712
|
|
86
|
+
# @return [Integer] screen assist points
|
|
87
|
+
attribute :screen_ast_pts, Shale::Type::Integer
|
|
88
|
+
|
|
89
|
+
# @!attribute [rw] off_loose_balls_recovered
|
|
90
|
+
# Returns offensive loose balls recovered
|
|
91
|
+
# @api public
|
|
92
|
+
# @example
|
|
93
|
+
# stat.off_loose_balls_recovered #=> 312
|
|
94
|
+
# @return [Integer] offensive loose balls recovered
|
|
95
|
+
attribute :off_loose_balls_recovered, Shale::Type::Integer
|
|
96
|
+
|
|
97
|
+
# @!attribute [rw] def_loose_balls_recovered
|
|
98
|
+
# Returns defensive loose balls recovered
|
|
99
|
+
# @api public
|
|
100
|
+
# @example
|
|
101
|
+
# stat.def_loose_balls_recovered #=> 428
|
|
102
|
+
# @return [Integer] defensive loose balls recovered
|
|
103
|
+
attribute :def_loose_balls_recovered, Shale::Type::Integer
|
|
104
|
+
|
|
105
|
+
# @!attribute [rw] loose_balls_recovered
|
|
106
|
+
# Returns total loose balls recovered
|
|
107
|
+
# @api public
|
|
108
|
+
# @example
|
|
109
|
+
# stat.loose_balls_recovered #=> 740
|
|
110
|
+
# @return [Integer] loose balls recovered
|
|
111
|
+
attribute :loose_balls_recovered, Shale::Type::Integer
|
|
112
|
+
|
|
113
|
+
# @!attribute [rw] pct_loose_balls_recovered_off
|
|
114
|
+
# Returns percentage of offensive loose balls recovered
|
|
115
|
+
# @api public
|
|
116
|
+
# @example
|
|
117
|
+
# stat.pct_loose_balls_recovered_off #=> 0.422
|
|
118
|
+
# @return [Float] percentage of offensive loose balls recovered
|
|
119
|
+
attribute :pct_loose_balls_recovered_off, Shale::Type::Float
|
|
120
|
+
|
|
121
|
+
# @!attribute [rw] pct_loose_balls_recovered_def
|
|
122
|
+
# Returns percentage of defensive loose balls recovered
|
|
123
|
+
# @api public
|
|
124
|
+
# @example
|
|
125
|
+
# stat.pct_loose_balls_recovered_def #=> 0.578
|
|
126
|
+
# @return [Float] percentage of defensive loose balls recovered
|
|
127
|
+
attribute :pct_loose_balls_recovered_def, Shale::Type::Float
|
|
128
|
+
|
|
129
|
+
# @!attribute [rw] off_boxouts
|
|
130
|
+
# Returns offensive box outs
|
|
131
|
+
# @api public
|
|
132
|
+
# @example
|
|
133
|
+
# stat.off_boxouts #=> 245
|
|
134
|
+
# @return [Integer] offensive box outs
|
|
135
|
+
attribute :off_boxouts, Shale::Type::Integer
|
|
136
|
+
|
|
137
|
+
# @!attribute [rw] def_boxouts
|
|
138
|
+
# Returns defensive box outs
|
|
139
|
+
# @api public
|
|
140
|
+
# @example
|
|
141
|
+
# stat.def_boxouts #=> 1256
|
|
142
|
+
# @return [Integer] defensive box outs
|
|
143
|
+
attribute :def_boxouts, Shale::Type::Integer
|
|
144
|
+
|
|
145
|
+
# @!attribute [rw] box_outs
|
|
146
|
+
# Returns total box outs
|
|
147
|
+
# @api public
|
|
148
|
+
# @example
|
|
149
|
+
# stat.box_outs #=> 1501
|
|
150
|
+
# @return [Integer] total box outs
|
|
151
|
+
attribute :box_outs, Shale::Type::Integer
|
|
152
|
+
|
|
153
|
+
# @!attribute [rw] pct_box_outs_off
|
|
154
|
+
# Returns percentage of offensive box outs
|
|
155
|
+
# @api public
|
|
156
|
+
# @example
|
|
157
|
+
# stat.pct_box_outs_off #=> 0.163
|
|
158
|
+
# @return [Float] percentage of offensive box outs
|
|
159
|
+
attribute :pct_box_outs_off, Shale::Type::Float
|
|
160
|
+
|
|
161
|
+
# @!attribute [rw] pct_box_outs_def
|
|
162
|
+
# Returns percentage of defensive box outs
|
|
163
|
+
# @api public
|
|
164
|
+
# @example
|
|
165
|
+
# stat.pct_box_outs_def #=> 0.837
|
|
166
|
+
# @return [Float] percentage of defensive box outs
|
|
167
|
+
attribute :pct_box_outs_def, Shale::Type::Float
|
|
168
|
+
|
|
169
|
+
# Returns the team object
|
|
170
|
+
#
|
|
171
|
+
# @api public
|
|
172
|
+
# @example
|
|
173
|
+
# stat.team #=> #<NBA::Team>
|
|
174
|
+
# @return [Team, nil] the team object
|
|
175
|
+
def team
|
|
176
|
+
Teams.find(team_id)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require_relative "client"
|
|
3
|
+
require_relative "collection"
|
|
4
|
+
require_relative "league_lineup_viz_stat"
|
|
5
|
+
require_relative "utils"
|
|
6
|
+
|
|
7
|
+
module NBA
|
|
8
|
+
# Provides methods to retrieve league lineup visualization statistics
|
|
9
|
+
#
|
|
10
|
+
# @api public
|
|
11
|
+
module LeagueLineupViz
|
|
12
|
+
# Result set name for lineup visualization
|
|
13
|
+
# @return [String] the result set name
|
|
14
|
+
LEAGUE_LINEUP_VIZ = "LeagueLineupViz".freeze
|
|
15
|
+
|
|
16
|
+
# Regular season type constant
|
|
17
|
+
# @return [String] the season type
|
|
18
|
+
REGULAR_SEASON = "Regular Season".freeze
|
|
19
|
+
|
|
20
|
+
# Playoffs season type constant
|
|
21
|
+
# @return [String] the season type
|
|
22
|
+
PLAYOFFS = "Playoffs".freeze
|
|
23
|
+
|
|
24
|
+
# Per game mode constant
|
|
25
|
+
# @return [String] the per mode
|
|
26
|
+
PER_GAME = "PerGame".freeze
|
|
27
|
+
|
|
28
|
+
# Totals mode constant
|
|
29
|
+
# @return [String] the per mode
|
|
30
|
+
TOTALS = "Totals".freeze
|
|
31
|
+
|
|
32
|
+
# Base measure type constant
|
|
33
|
+
# @return [String] the measure type
|
|
34
|
+
BASE = "Base".freeze
|
|
35
|
+
|
|
36
|
+
# Advanced measure type constant
|
|
37
|
+
# @return [String] the measure type
|
|
38
|
+
ADVANCED = "Advanced".freeze
|
|
39
|
+
|
|
40
|
+
# Group quantity for 5-man lineups
|
|
41
|
+
# @return [Integer] the group quantity
|
|
42
|
+
FIVE_MAN = 5
|
|
43
|
+
|
|
44
|
+
# Group quantity for 4-man lineups
|
|
45
|
+
# @return [Integer] the group quantity
|
|
46
|
+
FOUR_MAN = 4
|
|
47
|
+
|
|
48
|
+
# Group quantity for 3-man lineups
|
|
49
|
+
# @return [Integer] the group quantity
|
|
50
|
+
THREE_MAN = 3
|
|
51
|
+
|
|
52
|
+
# Group quantity for 2-man lineups
|
|
53
|
+
# @return [Integer] the group quantity
|
|
54
|
+
TWO_MAN = 2
|
|
55
|
+
|
|
56
|
+
# Retrieves all league lineup visualization statistics
|
|
57
|
+
#
|
|
58
|
+
# @api public
|
|
59
|
+
# @example
|
|
60
|
+
# stats = NBA::LeagueLineupViz.all(season: 2024)
|
|
61
|
+
# stats.first.group_name #=> "S. Curry - K. Thompson - ..."
|
|
62
|
+
# @param season [Integer] the season year
|
|
63
|
+
# @param season_type [String] the season type
|
|
64
|
+
# @param per_mode [String] the per mode
|
|
65
|
+
# @param measure_type [String] the measure type
|
|
66
|
+
# @param group_quantity [Integer] the lineup size
|
|
67
|
+
# @param minutes_min [Integer] minimum minutes filter
|
|
68
|
+
# @param client [Client] the API client to use
|
|
69
|
+
# @return [Collection] a collection of lineup visualization statistics
|
|
70
|
+
def self.all(season: Utils.current_season, season_type: REGULAR_SEASON, per_mode: PER_GAME,
|
|
71
|
+
measure_type: BASE, group_quantity: FIVE_MAN, minutes_min: 0, client: CLIENT)
|
|
72
|
+
path = build_path(season, season_type: season_type, per_mode: per_mode, measure_type: measure_type,
|
|
73
|
+
group_quantity: group_quantity, minutes_min: minutes_min)
|
|
74
|
+
response = client.get(path)
|
|
75
|
+
parse_response(response)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Builds the API request path
|
|
79
|
+
#
|
|
80
|
+
# @api private
|
|
81
|
+
# @return [String] the request path
|
|
82
|
+
def self.build_path(season, opts)
|
|
83
|
+
season_str = Utils.format_season(season)
|
|
84
|
+
"leaguelineupviz?LeagueID=00&Season=#{season_str}&SeasonType=#{opts.fetch(:season_type)}" \
|
|
85
|
+
"&PerMode=#{opts.fetch(:per_mode)}&MeasureType=#{opts.fetch(:measure_type)}" \
|
|
86
|
+
"&GroupQuantity=#{opts.fetch(:group_quantity)}&MinutesMin=#{opts.fetch(:minutes_min)}"
|
|
87
|
+
end
|
|
88
|
+
private_class_method :build_path
|
|
89
|
+
|
|
90
|
+
# Parses the API response into stat objects
|
|
91
|
+
#
|
|
92
|
+
# @api private
|
|
93
|
+
# @return [Collection] collection of lineup visualization stats
|
|
94
|
+
def self.parse_response(response)
|
|
95
|
+
return Collection.new if response.nil? || response.empty?
|
|
96
|
+
|
|
97
|
+
data = JSON.parse(response)
|
|
98
|
+
result_set = find_result_set(data)
|
|
99
|
+
build_stats(result_set)
|
|
100
|
+
end
|
|
101
|
+
private_class_method :parse_response
|
|
102
|
+
|
|
103
|
+
# Finds the result set by name
|
|
104
|
+
#
|
|
105
|
+
# @api private
|
|
106
|
+
# @return [Hash, nil] the result set hash or nil if not found
|
|
107
|
+
def self.find_result_set(data)
|
|
108
|
+
result_sets = data["resultSets"]
|
|
109
|
+
return unless result_sets
|
|
110
|
+
|
|
111
|
+
result_sets.find { |rs| rs["name"].eql?(LEAGUE_LINEUP_VIZ) }
|
|
112
|
+
end
|
|
113
|
+
private_class_method :find_result_set
|
|
114
|
+
|
|
115
|
+
# Builds stats collection from result set
|
|
116
|
+
#
|
|
117
|
+
# @api private
|
|
118
|
+
# @return [Collection] the stats collection
|
|
119
|
+
def self.build_stats(result_set)
|
|
120
|
+
return Collection.new unless result_set
|
|
121
|
+
|
|
122
|
+
headers = result_set["headers"]
|
|
123
|
+
rows = result_set["rowSet"]
|
|
124
|
+
return Collection.new unless headers && rows
|
|
125
|
+
|
|
126
|
+
Collection.new(rows.map { |row| build_stat(headers.zip(row).to_h) })
|
|
127
|
+
end
|
|
128
|
+
private_class_method :build_stats
|
|
129
|
+
|
|
130
|
+
# Builds a single stat object from API data
|
|
131
|
+
#
|
|
132
|
+
# @api private
|
|
133
|
+
# @return [LeagueLineupVizStat] the stat object
|
|
134
|
+
def self.build_stat(data)
|
|
135
|
+
LeagueLineupVizStat.new(**identity_info(data), **rating_info(data), **shooting_info(data),
|
|
136
|
+
**opponent_info(data))
|
|
137
|
+
end
|
|
138
|
+
private_class_method :build_stat
|
|
139
|
+
|
|
140
|
+
# Extracts identity information from data
|
|
141
|
+
#
|
|
142
|
+
# @api private
|
|
143
|
+
# @return [Hash] the identity information hash
|
|
144
|
+
def self.identity_info(data)
|
|
145
|
+
{group_id: data["GROUP_ID"], group_name: data["GROUP_NAME"],
|
|
146
|
+
team_id: data["TEAM_ID"], team_abbreviation: data["TEAM_ABBREVIATION"]}
|
|
147
|
+
end
|
|
148
|
+
private_class_method :identity_info
|
|
149
|
+
|
|
150
|
+
# Extracts rating information from data
|
|
151
|
+
#
|
|
152
|
+
# @api private
|
|
153
|
+
# @return [Hash] the rating information hash
|
|
154
|
+
def self.rating_info(data)
|
|
155
|
+
{min: data["MIN"], off_rating: data["OFF_RATING"],
|
|
156
|
+
def_rating: data["DEF_RATING"], net_rating: data["NET_RATING"],
|
|
157
|
+
pace: data["PACE"], ts_pct: data["TS_PCT"],
|
|
158
|
+
fta_rate: data["FTA_RATE"], tm_ast_pct: data["TM_AST_PCT"]}
|
|
159
|
+
end
|
|
160
|
+
private_class_method :rating_info
|
|
161
|
+
|
|
162
|
+
# Extracts shooting information from data
|
|
163
|
+
#
|
|
164
|
+
# @api private
|
|
165
|
+
# @return [Hash] the shooting information hash
|
|
166
|
+
def self.shooting_info(data)
|
|
167
|
+
{pct_fga_2pt: data["PCT_FGA_2PT"], pct_fga_3pt: data["PCT_FGA_3PT"],
|
|
168
|
+
pct_pts_2pt_mr: data["PCT_PTS_2PT_MR"], pct_pts_fb: data["PCT_PTS_FB"],
|
|
169
|
+
pct_pts_ft: data["PCT_PTS_FT"], pct_pts_paint: data["PCT_PTS_PAINT"],
|
|
170
|
+
pct_ast_fgm: data["PCT_AST_FGM"], pct_uast_fgm: data["PCT_UAST_FGM"]}
|
|
171
|
+
end
|
|
172
|
+
private_class_method :shooting_info
|
|
173
|
+
|
|
174
|
+
# Extracts opponent stats information from data
|
|
175
|
+
#
|
|
176
|
+
# @api private
|
|
177
|
+
# @return [Hash] the opponent stats information hash
|
|
178
|
+
def self.opponent_info(data)
|
|
179
|
+
{opp_fg3_pct: data["OPP_FG3_PCT"], opp_efg_pct: data["OPP_EFG_PCT"],
|
|
180
|
+
opp_fta_rate: data["OPP_FTA_RATE"], opp_tov_pct: data["OPP_TOV_PCT"]}
|
|
181
|
+
end
|
|
182
|
+
private_class_method :opponent_info
|
|
183
|
+
end
|
|
184
|
+
end
|