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,217 @@
1
+ require "equalizer"
2
+ require "shale"
3
+
4
+ module NBA
5
+ # Represents career statistics for a player (season averages)
6
+ class CareerStats < Shale::Mapper
7
+ include Equalizer.new(:player_id, :season_id)
8
+
9
+ # @!attribute [rw] player_id
10
+ # Returns the player ID
11
+ # @api public
12
+ # @example
13
+ # stats.player_id #=> 201939
14
+ # @return [Integer] the player ID
15
+ attribute :player_id, Shale::Type::Integer
16
+
17
+ # @!attribute [rw] season_id
18
+ # Returns the season ID
19
+ # @api public
20
+ # @example
21
+ # stats.season_id #=> "2024-25"
22
+ # @return [String] the season ID
23
+ attribute :season_id, Shale::Type::String
24
+
25
+ # @!attribute [rw] team_id
26
+ # Returns the team ID
27
+ # @api public
28
+ # @example
29
+ # stats.team_id #=> 1610612744
30
+ # @return [Integer] the team ID
31
+ attribute :team_id, Shale::Type::Integer
32
+
33
+ # @!attribute [rw] team_abbreviation
34
+ # Returns the team abbreviation
35
+ # @api public
36
+ # @example
37
+ # stats.team_abbreviation #=> "GSW"
38
+ # @return [String] the team abbreviation
39
+ attribute :team_abbreviation, Shale::Type::String
40
+
41
+ # @!attribute [rw] player_age
42
+ # Returns the player's age during the season
43
+ # @api public
44
+ # @example
45
+ # stats.player_age #=> 36
46
+ # @return [Integer] the player's age
47
+ attribute :player_age, Shale::Type::Integer
48
+
49
+ # @!attribute [rw] gp
50
+ # Returns games played
51
+ # @api public
52
+ # @example
53
+ # stats.gp #=> 74
54
+ # @return [Integer] games played
55
+ attribute :gp, Shale::Type::Integer
56
+
57
+ # @!attribute [rw] gs
58
+ # Returns games started
59
+ # @api public
60
+ # @example
61
+ # stats.gs #=> 74
62
+ # @return [Integer] games started
63
+ attribute :gs, Shale::Type::Integer
64
+
65
+ # @!attribute [rw] min
66
+ # Returns minutes per game
67
+ # @api public
68
+ # @example
69
+ # stats.min #=> 32.7
70
+ # @return [Float] minutes per game
71
+ attribute :min, Shale::Type::Float
72
+
73
+ # @!attribute [rw] fgm
74
+ # Returns field goals made per game
75
+ # @api public
76
+ # @example
77
+ # stats.fgm #=> 8.4
78
+ # @return [Float] field goals made per game
79
+ attribute :fgm, Shale::Type::Float
80
+
81
+ # @!attribute [rw] fga
82
+ # Returns field goals attempted per game
83
+ # @api public
84
+ # @example
85
+ # stats.fga #=> 17.9
86
+ # @return [Float] field goals attempted per game
87
+ attribute :fga, Shale::Type::Float
88
+
89
+ # @!attribute [rw] fg_pct
90
+ # Returns field goal percentage
91
+ # @api public
92
+ # @example
93
+ # stats.fg_pct #=> 0.473
94
+ # @return [Float] field goal percentage
95
+ attribute :fg_pct, Shale::Type::Float
96
+
97
+ # @!attribute [rw] fg3m
98
+ # Returns three-pointers made per game
99
+ # @api public
100
+ # @example
101
+ # stats.fg3m #=> 4.8
102
+ # @return [Float] three-pointers made per game
103
+ attribute :fg3m, Shale::Type::Float
104
+
105
+ # @!attribute [rw] fg3a
106
+ # Returns three-pointers attempted per game
107
+ # @api public
108
+ # @example
109
+ # stats.fg3a #=> 11.7
110
+ # @return [Float] three-pointers attempted per game
111
+ attribute :fg3a, Shale::Type::Float
112
+
113
+ # @!attribute [rw] fg3_pct
114
+ # Returns three-point percentage
115
+ # @api public
116
+ # @example
117
+ # stats.fg3_pct #=> 0.408
118
+ # @return [Float] three-point percentage
119
+ attribute :fg3_pct, Shale::Type::Float
120
+
121
+ # @!attribute [rw] ftm
122
+ # Returns free throws made per game
123
+ # @api public
124
+ # @example
125
+ # stats.ftm #=> 4.5
126
+ # @return [Float] free throws made per game
127
+ attribute :ftm, Shale::Type::Float
128
+
129
+ # @!attribute [rw] fta
130
+ # Returns free throws attempted per game
131
+ # @api public
132
+ # @example
133
+ # stats.fta #=> 4.9
134
+ # @return [Float] free throws attempted per game
135
+ attribute :fta, Shale::Type::Float
136
+
137
+ # @!attribute [rw] ft_pct
138
+ # Returns free throw percentage
139
+ # @api public
140
+ # @example
141
+ # stats.ft_pct #=> 0.915
142
+ # @return [Float] free throw percentage
143
+ attribute :ft_pct, Shale::Type::Float
144
+
145
+ # @!attribute [rw] oreb
146
+ # Returns offensive rebounds per game
147
+ # @api public
148
+ # @example
149
+ # stats.oreb #=> 0.5
150
+ # @return [Float] offensive rebounds per game
151
+ attribute :oreb, Shale::Type::Float
152
+
153
+ # @!attribute [rw] dreb
154
+ # Returns defensive rebounds per game
155
+ # @api public
156
+ # @example
157
+ # stats.dreb #=> 4.0
158
+ # @return [Float] defensive rebounds per game
159
+ attribute :dreb, Shale::Type::Float
160
+
161
+ # @!attribute [rw] reb
162
+ # Returns total rebounds per game
163
+ # @api public
164
+ # @example
165
+ # stats.reb #=> 4.5
166
+ # @return [Float] total rebounds per game
167
+ attribute :reb, Shale::Type::Float
168
+
169
+ # @!attribute [rw] ast
170
+ # Returns assists per game
171
+ # @api public
172
+ # @example
173
+ # stats.ast #=> 5.1
174
+ # @return [Float] assists per game
175
+ attribute :ast, Shale::Type::Float
176
+
177
+ # @!attribute [rw] stl
178
+ # Returns steals per game
179
+ # @api public
180
+ # @example
181
+ # stats.stl #=> 0.7
182
+ # @return [Float] steals per game
183
+ attribute :stl, Shale::Type::Float
184
+
185
+ # @!attribute [rw] blk
186
+ # Returns blocks per game
187
+ # @api public
188
+ # @example
189
+ # stats.blk #=> 0.4
190
+ # @return [Float] blocks per game
191
+ attribute :blk, Shale::Type::Float
192
+
193
+ # @!attribute [rw] tov
194
+ # Returns turnovers per game
195
+ # @api public
196
+ # @example
197
+ # stats.tov #=> 3.0
198
+ # @return [Float] turnovers per game
199
+ attribute :tov, Shale::Type::Float
200
+
201
+ # @!attribute [rw] pf
202
+ # Returns personal fouls per game
203
+ # @api public
204
+ # @example
205
+ # stats.pf #=> 1.8
206
+ # @return [Float] personal fouls per game
207
+ attribute :pf, Shale::Type::Float
208
+
209
+ # @!attribute [rw] pts
210
+ # Returns points per game
211
+ # @api public
212
+ # @example
213
+ # stats.pts #=> 26.4
214
+ # @return [Float] points per game
215
+ attribute :pts, Shale::Type::Float
216
+ end
217
+ end
@@ -0,0 +1,98 @@
1
+ module NBA
2
+ class CLI < Thor
3
+ module Display
4
+ # Helper methods for displaying player info
5
+ module PlayerDisplay
6
+ # Displays a list of players from search
7
+ #
8
+ # @api private
9
+ # @param players_list [Collection] the players to display
10
+ # @return [void]
11
+ def display_players(players_list)
12
+ say("Found #{players_list.size} player(s):")
13
+ players_list.each { |player| say(format_player_result(player)) }
14
+ end
15
+
16
+ # Displays detailed information for a single player
17
+ #
18
+ # @api private
19
+ # @param player [Player] the player to display
20
+ # @return [void]
21
+ def display_player(player)
22
+ detail = Players.find(player)
23
+ return say("Player not found") unless detail
24
+
25
+ say(format_label("Name", detail.full_name))
26
+ say(format_label("Status", detail.active? ? "Active" : "Inactive"))
27
+ display_player_physical(detail)
28
+ display_player_draft(detail)
29
+ end
30
+
31
+ # Displays player physical info
32
+ #
33
+ # @api private
34
+ # @param player [Player] the player
35
+ # @return [void]
36
+ def display_player_physical(player)
37
+ display_player_position(player)
38
+ display_player_body(player)
39
+ display_player_origin(player)
40
+ end
41
+
42
+ # Displays player draft info
43
+ #
44
+ # @api private
45
+ # @param player [Player] the player
46
+ # @return [void]
47
+ def display_player_draft(player)
48
+ return say(format_label("Draft", "Undrafted")) unless player.draft_year
49
+
50
+ draft = format_draft_info(player)
51
+ say(format_label("Draft", draft))
52
+ end
53
+
54
+ private
55
+
56
+ # Displays player position
57
+ #
58
+ # @api private
59
+ # @param player [Player] the player
60
+ # @return [void]
61
+ def display_player_position(player)
62
+ say(format_label("Position", player.position&.name))
63
+ end
64
+
65
+ # Displays player body measurements
66
+ #
67
+ # @api private
68
+ # @param player [Player] the player
69
+ # @return [void]
70
+ def display_player_body(player)
71
+ say(format_label("Height", player.height))
72
+ say(format_label("Weight", format_weight(player.weight)))
73
+ end
74
+
75
+ # Displays player origin info
76
+ #
77
+ # @api private
78
+ # @param player [Player] the player
79
+ # @return [void]
80
+ def display_player_origin(player)
81
+ say(format_label("Country", player.country))
82
+ say(format_label("College", player.college))
83
+ end
84
+
85
+ # Formats weight with unit if present
86
+ #
87
+ # @api private
88
+ # @param weight [Integer, nil] the weight value
89
+ # @return [String, nil] formatted weight or nil
90
+ def format_weight(weight)
91
+ return unless weight
92
+
93
+ "#{weight} lbs"
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,178 @@
1
+ require_relative "display/player_display"
2
+
3
+ module NBA
4
+ class CLI < Thor
5
+ # Helper methods for displaying CLI output
6
+ module Display
7
+ include PlayerDisplay
8
+
9
+ # Displays a list of games in aligned columns
10
+ #
11
+ # @api private
12
+ # @param games_list [Collection] the games to display
13
+ # @return [void]
14
+ def display_games(games_list)
15
+ widths = calculate_game_widths(games_list)
16
+ games_list.each { |game| say(format_game_row(game, widths)) }
17
+ end
18
+
19
+ # Displays a list of teams
20
+ #
21
+ # @api private
22
+ # @param teams_list [Collection] the teams to display
23
+ # @param include_roster [Boolean] whether to include roster
24
+ # @param detailed [Boolean] whether to show detailed info
25
+ # @return [void]
26
+ def display_teams(teams_list, include_roster, detailed:)
27
+ return display_team_names(teams_list) unless detailed
28
+
29
+ teams_list.each_with_index do |team, index|
30
+ say unless index.zero?
31
+ display_team(team)
32
+ display_team_roster(team) if include_roster
33
+ end
34
+ end
35
+
36
+ # Displays team names only in alphabetical order
37
+ #
38
+ # @api private
39
+ # @param teams_list [Collection] the teams to display
40
+ # @return [void]
41
+ def display_team_names(teams_list)
42
+ sorted = teams_list.sort_by { |team| team.full_name.to_s }
43
+ sorted.each { |team| say(team.full_name) }
44
+ end
45
+
46
+ # Displays a single team
47
+ #
48
+ # @api private
49
+ # @param team [Team] the team to display
50
+ # @return [void]
51
+ def display_team(team)
52
+ detail = TeamDetails.find(team: team)
53
+ say(format_label("Name", team.full_name))
54
+ say(format_label("Founded", team.year_founded))
55
+ display_team_conference(detail)
56
+ display_team_division(detail)
57
+ say(format_label("Coach", detail&.head_coach))
58
+ display_team_championships(team)
59
+ end
60
+
61
+ # Displays team conference info
62
+ #
63
+ # @api private
64
+ # @param detail [TeamDetail] the team detail
65
+ # @return [void]
66
+ def display_team_conference(detail)
67
+ return say(format_label("Conference", nil)) unless detail
68
+
69
+ conf = conference_name(detail)
70
+ say(format_label("Conference", conf))
71
+ end
72
+
73
+ # Displays team division info
74
+ #
75
+ # @api private
76
+ # @param detail [TeamDetail] the team detail
77
+ # @return [void]
78
+ def display_team_division(detail)
79
+ return say(format_label("Division", nil)) unless detail
80
+
81
+ div = division_name(detail)
82
+ say(format_label("Division", div))
83
+ end
84
+
85
+ # Displays team championships
86
+ #
87
+ # @api private
88
+ # @param team [Team] the team
89
+ # @return [void]
90
+ def display_team_championships(team)
91
+ stats = fetch_team_year_stats(team)
92
+ championships = stats.select { |s| championship_year?(s) }.map(&:year)
93
+ return if championships.empty?
94
+
95
+ say(format_multiline_label("Championships", championships))
96
+ end
97
+
98
+ # Fetches team year-by-year stats safely
99
+ #
100
+ # @api private
101
+ # @param team [Team] the team
102
+ # @return [Collection] the stats collection
103
+ def fetch_team_year_stats(team)
104
+ TeamYearByYearStats.find(team: team)
105
+ rescue JSON::ParserError
106
+ Collection.new
107
+ end
108
+
109
+ # Displays the roster for a team
110
+ #
111
+ # @api private
112
+ # @param team [Team] the team
113
+ # @return [void]
114
+ def display_team_roster(team)
115
+ roster_list = Roster.find(team: team)
116
+ return if roster_list.empty?
117
+
118
+ display_players_list(roster_list)
119
+ end
120
+
121
+ # Displays a list of players as team roster
122
+ #
123
+ # @api private
124
+ # @param players [Collection] the players to display
125
+ # @return [void]
126
+ def display_players_list(players)
127
+ say("Players:")
128
+ players.each { |player| say(format_detailed_roster_player(player)) }
129
+ end
130
+
131
+ # Displays standings in aligned format
132
+ #
133
+ # @api private
134
+ # @param standings_list [Collection] the standings to display
135
+ # @return [void]
136
+ def display_standings(standings_list)
137
+ widths = calculate_standings_widths(standings_list)
138
+ standings_list.each_with_index do |standing, index|
139
+ say(format_standing_row(standing, index + 1, widths))
140
+ end
141
+ end
142
+
143
+ # Displays league leaders in aligned format
144
+ #
145
+ # @api private
146
+ # @param leaders_list [Collection] the leaders to display
147
+ # @param category [String] the stat category
148
+ # @return [void]
149
+ def display_leaders(leaders_list, category)
150
+ say("League Leaders - #{category.upcase}")
151
+ widths = calculate_leader_widths(leaders_list)
152
+ leaders_list.each { |leader| say(format_leader_row(leader, widths)) }
153
+ end
154
+
155
+ # Displays schedule for a team
156
+ #
157
+ # @api private
158
+ # @param schedule_list [Collection] the schedule to display
159
+ # @param team [Team] the team
160
+ # @return [void]
161
+ def display_schedule(schedule_list, team)
162
+ say("Schedule for #{team.full_name}")
163
+ schedule_list.each { |game| say(format_schedule_game(game, team)) }
164
+ end
165
+
166
+ # Displays roster for a team
167
+ #
168
+ # @api private
169
+ # @param roster_list [Collection] the roster to display
170
+ # @param team [Team] the team
171
+ # @return [void]
172
+ def display_roster(roster_list, team)
173
+ say("Roster for #{team.full_name}")
174
+ roster_list.each { |player| say(format_detailed_roster_player(player)) }
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,86 @@
1
+ module NBA
2
+ class CLI < Thor
3
+ module Formatters
4
+ # Formatters for game-related output
5
+ module GameFormatters
6
+ # Formats a game status for display
7
+ #
8
+ # @api private
9
+ # @return [String]
10
+ def format_game_status(game)
11
+ status = game.status
12
+ return "TBD" unless status
13
+
14
+ convert_et_to_local(status.strip)
15
+ end
16
+
17
+ # Formats a game row for tabular display
18
+ #
19
+ # @api private
20
+ # @return [String]
21
+ def format_game_row(game, widths)
22
+ status = center(format_game_status(game), widths.fetch(:status))
23
+ teams = format_game_teams(game, widths)
24
+ scores = format_game_scores(game, widths)
25
+ "#{status} - #{teams.fetch(:home)} #{scores.fetch(:home)} : #{scores.fetch(:away)} #{teams.fetch(:away)}".rstrip
26
+ end
27
+
28
+ # Formats game team names with padding
29
+ #
30
+ # @api private
31
+ # @return [Hash]
32
+ def format_game_teams(game, widths)
33
+ {home: center(team_nickname(game.home_team), widths.fetch(:home)),
34
+ away: center(team_nickname(game.away_team), widths.fetch(:away))}
35
+ end
36
+
37
+ # Formats game scores with padding
38
+ #
39
+ # @api private
40
+ # @return [Hash]
41
+ def format_game_scores(game, widths)
42
+ {home: center(game.home_score || "-", widths.fetch(:home_score)),
43
+ away: center(game.away_score || "-", widths.fetch(:away_score))}
44
+ end
45
+
46
+ # Calculates column widths for game display
47
+ #
48
+ # @api private
49
+ # @return [Hash]
50
+ def calculate_game_widths(games)
51
+ {status: max_length(games.map { |g| format_game_status(g) }),
52
+ home: max_length(games.map { |g| team_nickname(g.home_team) }),
53
+ away: max_length(games.map { |g| team_nickname(g.away_team) }),
54
+ **score_widths(games)}
55
+ end
56
+
57
+ # Calculates score column widths
58
+ #
59
+ # @api private
60
+ # @return [Hash]
61
+ def score_widths(games)
62
+ {home_score: max_length(games.map(&:home_score)),
63
+ away_score: max_length(games.map(&:away_score))}
64
+ end
65
+
66
+ # Formats a scheduled game for display
67
+ #
68
+ # @api private
69
+ # @return [String]
70
+ def format_schedule_game(game, team)
71
+ date = game.game_date&.split("T")&.first || "TBD"
72
+ "#{date}: #{determine_opponent(game, team)}"
73
+ end
74
+
75
+ # Determines the opponent display string
76
+ #
77
+ # @api private
78
+ # @return [String]
79
+ def determine_opponent(game, team)
80
+ home_game = game.home_team_tricode.eql?(team.abbreviation)
81
+ home_game ? "vs #{game.away_team_tricode}" : "@ #{game.home_team_tricode}"
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,26 @@
1
+ module NBA
2
+ class CLI < Thor
3
+ module Formatters
4
+ # Formatters for leaders-related output
5
+ module LeadersFormatters
6
+ # Formats a leader row for tabular display
7
+ #
8
+ # @api private
9
+ # @return [String]
10
+ def format_leader_row(leader, widths)
11
+ rank = leader.rank.to_s.rjust(widths.fetch(:rank))
12
+ name = leader.player_name.ljust(widths.fetch(:name))
13
+ "#{rank}. #{name} #{leader.value}"
14
+ end
15
+
16
+ # Calculates column widths for leader display
17
+ #
18
+ # @api private
19
+ # @return [Hash]
20
+ def calculate_leader_widths(leaders)
21
+ {rank: max_length(leaders.map(&:rank)), name: max_length(leaders.map(&:player_name))}
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,52 @@
1
+ module NBA
2
+ class CLI < Thor
3
+ module Formatters
4
+ # Formatters for player-related output
5
+ module PlayerFormatters
6
+ # Formats a roster player with detailed information
7
+ #
8
+ # @api private
9
+ # @return [String]
10
+ def format_detailed_roster_player(player)
11
+ jersey = format_jersey_number(player.jersey_number)
12
+ name = (player.full_name || "Unknown").ljust(25)
13
+ position = format_position(player.position)
14
+ "##{jersey} #{name} #{position} #{player.height || "?"}"
15
+ end
16
+
17
+ # Formats a jersey number for display
18
+ #
19
+ # @api private
20
+ # @example
21
+ # format_jersey_number(3) #=> " 3"
22
+ # @return [String]
23
+ def format_jersey_number(number) = number&.to_s&.rjust(2) || " ?"
24
+
25
+ # Formats a position abbreviation for display
26
+ #
27
+ # @api private
28
+ # @example
29
+ # format_position(position) #=> "PG "
30
+ # @return [String]
31
+ def format_position(position) = (position&.abbreviation || "?").ljust(3)
32
+
33
+ # Formats a player search result
34
+ #
35
+ # @api private
36
+ # @return [String]
37
+ def format_player_result(player)
38
+ status = player.active? ? "Active" : "Inactive"
39
+ "#{player.full_name} (#{status})"
40
+ end
41
+
42
+ # Formats player draft information
43
+ #
44
+ # @api private
45
+ # @return [String]
46
+ def format_draft_info(player)
47
+ "#{player.draft_year} Round #{player.draft_round}, Pick #{player.draft_number}"
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,26 @@
1
+ module NBA
2
+ class CLI < Thor
3
+ module Formatters
4
+ # Formatters for standings-related output
5
+ module StandingsFormatters
6
+ # Formats a standing row for tabular display
7
+ #
8
+ # @api private
9
+ # @return [String]
10
+ def format_standing_row(standing, rank, widths)
11
+ rank_str = rank.to_s.rjust(widths.fetch(:rank))
12
+ team = standing.team_name.ljust(widths.fetch(:team))
13
+ "#{rank_str}. #{team} #{standing.wins}-#{standing.losses}"
14
+ end
15
+
16
+ # Calculates column widths for standings display
17
+ #
18
+ # @api private
19
+ # @return [Hash]
20
+ def calculate_standings_widths(standings)
21
+ {rank: standings.size.to_s.length, team: max_length(standings.map(&:team_name))}
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end