diff --git a/nba_data/migrations/0012_playersalarydata.py b/nba_data/migrations/0012_playersalarydata.py new file mode 100644 index 0000000..e5b188f --- /dev/null +++ b/nba_data/migrations/0012_playersalarydata.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2 on 2023-05-08 23:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('nba_data', '0011_alter_playerplayoffadvanceddata_bpm_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='PlayerSalaryData', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('player_name', models.CharField(max_length=255, null=True)), + ('salary', models.IntegerField(null=True)), + ], + ), + ] diff --git a/nba_data/migrations/0013_playersalarydata_season_playersalarydata_team.py b/nba_data/migrations/0013_playersalarydata_season_playersalarydata_team.py new file mode 100644 index 0000000..0f600b2 --- /dev/null +++ b/nba_data/migrations/0013_playersalarydata_season_playersalarydata_team.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2 on 2023-05-09 00:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('nba_data', '0012_playersalarydata'), + ] + + operations = [ + migrations.AddField( + model_name='playersalarydata', + name='season', + field=models.IntegerField(null=True), + ), + migrations.AddField( + model_name='playersalarydata', + name='team', + field=models.CharField(default='', max_length=30), + ), + ] diff --git a/nba_data/migrations/0014_playershotchartdata.py b/nba_data/migrations/0014_playershotchartdata.py new file mode 100644 index 0000000..81b0220 --- /dev/null +++ b/nba_data/migrations/0014_playershotchartdata.py @@ -0,0 +1,34 @@ +# Generated by Django 4.2 on 2023-05-10 02:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('nba_data', '0013_playersalarydata_season_playersalarydata_team'), + ] + + operations = [ + migrations.CreateModel( + name='PlayerShotChartData', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('player_name', models.CharField(default='', max_length=255)), + ('top', models.IntegerField(null=True)), + ('left', models.IntegerField(null=True)), + ('date', models.CharField(default='', max_length=255)), + ('qtr', models.CharField(default='', max_length=6)), + ('time_remaining', models.IntegerField(null=True)), + ('result', models.BooleanField(null=True)), + ('shot_type', models.CharField(default='', max_length=6)), + ('distance_ft', models.IntegerField(null=True)), + ('lead', models.BooleanField(null=True)), + ('lebron_team_score', models.IntegerField(null=True)), + ('opponent_team_score', models.IntegerField(null=True)), + ('opponent', models.CharField(default='', max_length=30)), + ('team', models.CharField(default='', max_length=30)), + ('season', models.IntegerField(null=True)), + ], + ), + ] diff --git a/nba_data/migrations/0015_alter_playershotchartdata_time_remaining.py b/nba_data/migrations/0015_alter_playershotchartdata_time_remaining.py new file mode 100644 index 0000000..9c0f2bf --- /dev/null +++ b/nba_data/migrations/0015_alter_playershotchartdata_time_remaining.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2 on 2023-05-10 02:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('nba_data', '0014_playershotchartdata'), + ] + + operations = [ + migrations.AlterField( + model_name='playershotchartdata', + name='time_remaining', + field=models.CharField(default='', max_length=12), + ), + ] diff --git a/nba_data/migrations/0016_alter_playershotchartdata_qtr.py b/nba_data/migrations/0016_alter_playershotchartdata_qtr.py new file mode 100644 index 0000000..337ed34 --- /dev/null +++ b/nba_data/migrations/0016_alter_playershotchartdata_qtr.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2 on 2023-05-10 05:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('nba_data', '0015_alter_playershotchartdata_time_remaining'), + ] + + operations = [ + migrations.AlterField( + model_name='playershotchartdata', + name='qtr', + field=models.CharField(default='', max_length=255), + ), + ] diff --git a/nba_data/migrations/0017_alter_playershotchartdata_result.py b/nba_data/migrations/0017_alter_playershotchartdata_result.py new file mode 100644 index 0000000..400f4ee --- /dev/null +++ b/nba_data/migrations/0017_alter_playershotchartdata_result.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2 on 2023-05-10 05:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('nba_data', '0016_alter_playershotchartdata_qtr'), + ] + + operations = [ + migrations.AlterField( + model_name='playershotchartdata', + name='result', + field=models.BooleanField(default='', null=True), + ), + ] diff --git a/nba_data/models.py b/nba_data/models.py index f7c39ca..810a7b1 100644 --- a/nba_data/models.py +++ b/nba_data/models.py @@ -199,6 +199,40 @@ class PlayerPlayoffAdvancedData(models.Model): def __str__(self): return self.player_name + +class PlayerSalaryData(models.Model): + player_name = models.CharField(max_length=255, null=True) + salary = models.IntegerField(null=True) + team = models.CharField(max_length=30, default='') + season = models.IntegerField(null=True) + + def __str__(self): + return self.player_name + + +class PlayerShotChartData(models.Model): + player_name = models.CharField(max_length=255, default='') + top = models.IntegerField(null=True) + left = models.IntegerField(null=True) + date = models.CharField(max_length=255, default='') + qtr = models.CharField(max_length=255, default='') + time_remaining = models.CharField(max_length=12, default='') + result = models.BooleanField(null=True, default='') + shot_type = models.CharField(max_length=6, default='') + distance_ft = models.IntegerField(null=True) + lead = models.BooleanField(null=True) + lebron_team_score = models.IntegerField(null=True) + opponent_team_score = models.IntegerField(null=True) + opponent = models.CharField(max_length=30, default='') + team = models.CharField(max_length=30, default='') + season = models.IntegerField(null=True) + + def __str__(self): + return self.player_name + + + + diff --git a/nba_data/nba_scraper_advanced.py b/nba_data/nba_scraper_advanced.py index e8e4808..2e7ed4b 100644 --- a/nba_data/nba_scraper_advanced.py +++ b/nba_data/nba_scraper_advanced.py @@ -15,10 +15,19 @@ django.setup() from nba_data.models import PlayerAdvancedData -# If season is 2014, then change CHO to CHA -teams = ['HOU' ,'PHI', 'BOS', 'NYK', 'BRK', 'TOR', 'MEM', 'NOP', 'DAL', 'SAS', 'DEN', 'MIN', 'OKC', 'UTA', 'POR', 'MIL', 'CLE', 'CHI', 'IND', 'DET', 'SAC', 'PHO', 'GSW', 'LAC', 'LAL', 'MIA', 'ATL', 'WAS', 'ORL', 'CHO'] -team_abbreviations = ['ATL', 'BOS', 'BRK', 'CHI', 'CHO', 'CLE', 'DAL', 'DEN', 'DET', 'GSW', 'HOU', 'IND', 'LAC', 'LAL', 'MEM', 'MIA', 'MIL', 'MIN', 'NOP', 'NYK', 'OKC', 'ORL', 'PHI', 'PHO', 'POR', 'SAC', 'SAS', 'TOR', 'UTA', 'WAS'] +# Change CHO to CHA 2014 and prior. +# Change NOP to NOH for 2013 and prior. +# Change BRK to NJN for 2012 and prior. +# Change OKC to SEA for 2008 and prior. +# Change NOH to NOK for 2007 and 2006 +# Change NOK to NOH for 2005 and prior. +# Change NOH to CHH for 2002 and prior. NOLA did not have a team before 2003. +# Change WAS to WSB for 1997 and prior. +# Change MEM to VAN for 2001 and prior. +# Change LAC to SDC for 1984 and prior. + +teams = ['HOU' ,'PHI', 'BOS', 'NYK', 'NJN', 'TOR', 'MEM', 'NOH', 'DAL', 'SAS', 'DEN', 'MIN', 'OKC', 'UTA', 'POR', 'MIL', 'CLE', 'CHI', 'IND', 'DET', 'SAC', 'PHO', 'GSW', 'LAC', 'LAL', 'MIA', 'ATL', 'WAS', 'ORL', 'CHA'] season = input('What season? ') for team in teams: diff --git a/nba_data/nba_scraper_playoffs_totals.py b/nba_data/nba_scraper_playoffs_totals.py index 202fe41..47853ff 100644 --- a/nba_data/nba_scraper_playoffs_totals.py +++ b/nba_data/nba_scraper_playoffs_totals.py @@ -29,6 +29,7 @@ from nba_data.models import PlayerPlayoffTotalsData teams = ['HOU' ,'PHI', 'BOS', 'NYK', 'NJN', 'TOR', 'MEM', 'NOH', 'DAL', 'SAS', 'DEN', 'MIN', 'OKC', 'UTA', 'POR', 'MIL', 'CLE', 'CHI', 'IND', 'DET', 'SAC', 'PHO', 'GSW', 'LAC', 'LAL', 'MIA', 'ATL', 'WAS', 'ORL', 'CHA'] + team_abbreviations = ['ATL', 'BOS', 'BRK', 'CHI', 'CHO', 'CLE', 'DAL', 'DEN', 'DET', 'GSW', 'HOU', 'IND', 'LAC', 'LAL', 'MEM', 'MIA', 'MIL', 'MIN', 'NOP', 'NYK', 'OKC', 'ORL', 'PHI', 'PHO', 'POR', 'SAC', 'SAS', 'TOR', 'UTA', 'WAS'] season = input('What season? ') diff --git a/nba_data/nba_scraper_salary.py b/nba_data/nba_scraper_salary.py new file mode 100644 index 0000000..ad2e1dc --- /dev/null +++ b/nba_data/nba_scraper_salary.py @@ -0,0 +1,104 @@ +import os +import sys +import django +import requests +from bs4 import BeautifulSoup, Comment +import csv +import json +import time +import re + +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'nba_stats.settings') +django.setup() + +from nba_data.models import PlayerSalaryData + +# Change CHO to CHA 2014 and prior. +# Change NOP to NOH for 2013 and prior. +# Change OKC to SEA for 2008 and prior. +# Change NOH to NOK for 2007 and 2006 +# Change NOK to NOH for 2005 and prior. +# Change NOH to CHH for 2002 and prior. NOLA did not have a team before 2003. +# Change WAS to WSB for 1997 and prior. +# Change BRK to NJN for 2012 and prior. +# Change MEM to VAN for 2001 and prior. +# Change LAC to SDC for 1984 and prior. + + +teams = ['HOU' ,'PHI', 'BOS', 'NYK', 'BRK', 'TOR', 'MEM', 'NOP', 'DAL', 'SAS', 'DEN', 'MIN', 'OKC', 'UTA', 'POR', 'MIL', 'CLE', 'CHI', 'IND', 'DET', 'SAC', 'PHO', 'GSW', 'LAC', 'LAL', 'MIA', 'ATL', 'WAS', 'ORL', 'CHO'] +teams_test = ['HOU', 'PHI', 'BOS'] + +season = input('What season? ') + +for team in teams: + url_make = 'https://www.basketball-reference.com/teams/' + team + '/' + season + '.html' + response = requests.get(url_make) + + soup = BeautifulSoup(response.text, "html.parser") + + comment = soup.find(string=lambda text: isinstance(text, Comment) and "salaries2" in text) + table_soup = BeautifulSoup(comment, "html.parser") + table = table_soup.find("table", {'class': 'suppress_all stats_table', 'id': 'salaries2'}) + + if table is None: + print(f"Table not found for team {team} and season {season}.") + # sys.exit(1) + continue + + header_row = table.find("thead").find("tr") + rows = table.find("tbody").find_all("tr") + + + # Extract coloumn headers + headers = ['player_name'] + headers.extend([col.text.strip() for col in header_row.find_all("th")][2:]) + headers.extend(['team', 'season']) # Add 'team' and 'season' fields to headers + + # Extract rows as dictionaries + data = [] + for row in rows: + cols = row.find_all("td") + cols = [col.text.strip() for col in cols] + row_dict = {header: (None if col == "" else col) for header, col in zip(headers, cols)} + row_dict['team'] = team + row_dict['season'] = season + data.append(row_dict) + + + # locate current directory + script_dir = os.path.dirname(os.path.abspath(__file__)) + + # save as CSV file + csv_output_path = os.path.join(script_dir, '..', 'data', f'{team}_{season}_player_salary_output.csv') + with open(csv_output_path, "w", newline="") as csvfile: + writer = csv.DictWriter(csvfile, fieldnames=headers) + writer.writeheader() + for row in data: + writer.writerow(row) + + # Save as JSON + json_output_path = os.path.join(script_dir, '..', 'data', f'{team}_{season}_player_salary_output.json') + with open(json_output_path, 'w') as jsonfile: + json.dump(data, jsonfile) + + # save data to database + + for row in data: + + row['Salary'] = int(row['Salary'].replace(',', '').replace('$', '')) if row['Salary'] else None + + player_data = PlayerSalaryData( + player_name = row['player_name'], + salary = row['Salary'], + team = row['team'], + season = row['season'], + ) + player_data.save() + +print('success') + + + \ No newline at end of file diff --git a/nba_data/nba_scraper_shot_chart.py b/nba_data/nba_scraper_shot_chart.py new file mode 100644 index 0000000..1bb7462 --- /dev/null +++ b/nba_data/nba_scraper_shot_chart.py @@ -0,0 +1,113 @@ +import os +import sys +import django +import requests +from bs4 import BeautifulSoup, Comment +import csv +import re + +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'nba_stats.settings') +django.setup() + +from nba_data.models import PlayerShotChartData + +player_name = 'LeBron James' +season = 2014 +# team = 'LAL' # You might want to find a way to get this from the webpage +url = 'https://www.basketball-reference.com/players/j/jamesle01/shooting/' + \ + str(season) + +response = requests.get(url) +soup = BeautifulSoup(response.text, "html.parser") + +# Find the comment containing the shot chart +comment = soup.find(string=lambda text: isinstance( + text, Comment) and "div_shot-chart" in text) +shot_chart_soup = BeautifulSoup(comment, "html.parser") + +# Now you can find the shot chart in the parsed comment +shot_chart = shot_chart_soup.find("div", {"id": "shot-wrapper"}) + +shots = shot_chart.find_all("div", {"class": ["tooltip make", "tooltip miss"]}) + +# save data to database and CSV +shot_chart_data = [] + +for shot_div in shots: + try: + # Parse shot data + style_values = shot_div['style'].split(';') + top_value = int(style_values[0].split( + ':')[1].replace('px', '').strip()) + left_value = int(style_values[1].split( + ':')[1].replace('px', '').strip()) + + # Parse tip attribute + tip_parts = shot_div['tip'].split('
') + + # Within your loop: + date_and_opponent = re.split(' at | vs ', tip_parts[0]) + date = date_and_opponent[0].strip().split( + ',')[0] + ',' + date_and_opponent[0].strip().split(',')[1] + team = date_and_opponent[0].strip().split(',')[2].strip() + opponent = date_and_opponent[1].strip() + + quarter_and_time = tip_parts[1].split(',') + quarter = quarter_and_time[0].strip() + time_remaining = quarter_and_time[1].strip().split(' ')[0] + + result_and_type_and_distance = tip_parts[2].split(' ') + result = result_and_type_and_distance[0].strip() == 'Made' + shot_type = int(result_and_type_and_distance[1][0]) + distance_ft = int(result_and_type_and_distance[-2].strip()) + + team_score_and_opponent_score = tip_parts[3].split(' ')[-1].split('-') + team_score = int(team_score_and_opponent_score[0].strip()) + opponent_score = int(team_score_and_opponent_score[1].strip()) + + lead = team_score > opponent_score + + shot_chart_row = { + 'player_name': player_name, + 'top': top_value, + 'left': left_value, + 'date': date, + 'qtr': quarter, + 'time_remaining': time_remaining, + 'result': result, + 'shot_type': shot_type, + 'distance_ft': distance_ft, + 'lead': lead, + 'lebron_team_score': team_score, + 'opponent_team_score': opponent_score, + 'opponent': opponent, + 'team': team, + 'season': season + } + shot_chart_data.append(shot_chart_row) + + except Exception as e: + print(f"Error processing shot_div: {shot_div}") + print(f"Exception: {e}") + + +# save as CSV file +script_dir = os.path.dirname(os.path.abspath(__file__)) +csv_output_path = os.path.join( + script_dir, '..', 'data', f'{player_name}_{season}_shot_chart_output.csv') +with open(csv_output_path, "w", newline="") as csvfile: + writer = csv.DictWriter(csvfile, fieldnames=shot_chart_data[0].keys()) + writer.writeheader() + for row in shot_chart_data: + writer.writerow(row) + +# Save the records in the database +try: + PlayerShotChartData.objects.bulk_create( + PlayerShotChartData(**row) for row in shot_chart_data + ) +except Exception as e: + print(f"Error inserting data into database: {e}") + +print('success') diff --git a/nba_data/nba_scraper_totals.py b/nba_data/nba_scraper_totals.py index 8ca132f..600e30a 100644 --- a/nba_data/nba_scraper_totals.py +++ b/nba_data/nba_scraper_totals.py @@ -15,8 +15,19 @@ django.setup() from nba_data.models import PlayerTotalsData -teams = ['HOU' ,'PHI', 'BOS', 'NYK', 'BRK', 'TOR', 'MEM', 'NOP', 'DAL', 'SAS', 'DEN', 'MIN', 'OKC', 'UTA', 'POR', 'MIL', 'CLE', 'CHI', 'IND', 'DET', 'SAC', 'PHO', 'GSW', 'LAC', 'LAL', 'MIA', 'ATL', 'WAS', 'ORL', 'CHO'] -team_abbreviations = ['ATL', 'BOS', 'BRK', 'CHI', 'CHO', 'CLE', 'DAL', 'DEN', 'DET', 'GSW', 'HOU', 'IND', 'LAC', 'LAL', 'MEM', 'MIA', 'MIL', 'MIN', 'NOP', 'NYK', 'OKC', 'ORL', 'PHI', 'PHO', 'POR', 'SAC', 'SAS', 'TOR', 'UTA', 'WAS'] +# Change CHO to CHA 2014 and prior. +# Change NOP to NOH for 2013 and prior. +# Change OKC to SEA for 2008 and prior. +# Change NOH to NOK for 2007 and 2006 +# Change NOK to NOH for 2005 and prior. +# Change NOH to CHH for 2002 and prior. NOLA did not have a team before 2003. +# Change WAS to WSB for 1997 and prior. +# Change BRK to NJN for 2012 and prior. +# Change MEM to VAN for 2001 and prior. +# Change LAC to SDC for 1984 and prior. + +teams = ['HOU' ,'PHI', 'BOS', 'NYK', 'NJN', 'TOR', 'MEM', 'NOH', 'DAL', 'SAS', 'DEN', 'MIN', 'OKC', 'UTA', 'POR', 'MIL', 'CLE', 'CHI', 'IND', 'DET', 'SAC', 'PHO', 'GSW', 'LAC', 'LAL', 'MIA', 'ATL', 'WAS', 'ORL', 'CHA'] + # team = input('What team? ') # OMIT above. Will now only ask for season and iterate thru teams list. diff --git a/nba_data/serializers.py b/nba_data/serializers.py index 9f03285..fd0147c 100644 --- a/nba_data/serializers.py +++ b/nba_data/serializers.py @@ -1,6 +1,6 @@ # Serializers are defined that will convert model instances to JSON from rest_framework import serializers -from .models import PlayerData, PlayerTotalsData, PlayerAdvancedData, PlayerPlayoffTotalsData, PlayerPlayoffAdvancedData +from .models import PlayerData, PlayerTotalsData, PlayerAdvancedData, PlayerPlayoffTotalsData, PlayerPlayoffAdvancedData, PlayerSalaryData, PlayerShotChartData class PlayerDataSerializer(serializers.ModelSerializer): class Meta: @@ -30,5 +30,20 @@ class PlayerPlayoffAdvancedDataSerializer(serializers.ModelSerializer): class Meta: model = PlayerPlayoffAdvancedData fields = '__all__' + +class PlayerSalaryDataSerializer(serializers.ModelSerializer): + class Meta: + model = PlayerSalaryData + fields = '__all__' + +class PlayerShotChartDataSerializer(serializers.ModelSerializer): + color = serializers.SerializerMethodField() + class Meta: + model = PlayerShotChartData + fields = ['top', 'left', 'date', 'qtr', 'time_remaining', 'result', 'shot_type', 'distance_ft', 'lead', 'lebron_team_score', 'opponent_team_score', 'opponent', 'team', 'season', 'color'] + + def get_color(self, obj): + return 'green' if obj.result else 'red' + diff --git a/nba_data/templates/landing.html b/nba_data/templates/landing.html new file mode 100644 index 0000000..47de3e7 --- /dev/null +++ b/nba_data/templates/landing.html @@ -0,0 +1,14 @@ + + + + + + + NBA Data API + + + +

Welcome to NBA Data API!

+ + + \ No newline at end of file diff --git a/nba_data/urls.py b/nba_data/urls.py index acac030..007980c 100644 --- a/nba_data/urls.py +++ b/nba_data/urls.py @@ -1,7 +1,8 @@ from django.urls import path -from .views import PlayerDataList, PlayerDataByTeamList, PlayerDataBySeasonList, PlayerDataByNameList, TopScorersbySeasonList, TopScorersbySeasonListPlayoffs, ThreeTwoTopPointTrends, TopAssistsBySeasonList, TopReboundsBySeasonList, TopBlocksBySeasonList, TopStealsBySeasonList, TopOffensiveReboundsBySeasonList, TopDefensiveReboundsBySeasonList, PointsPerGameHistogramBySeasonList, TopPtsScatterPlotData, TopPtsScatterPlotDataFast, TopPtsScatterPlotDataFast2018, Top20ScorersPost2009WS, Top20ScorersPost2014WS, Top20ScorersPost2018WS, TopAssistsBySeasonListPlayoffs, Top25ScorersPostSeasonPlayoffs, TopPtsScatterPlotDataFastSelect, OverallDBStats, TopScorersbySeasonTotalsList, TopAssistsBySeasonTotalsList, TopReboundsbySeasonTotalsList, TopStealsbySeasonTotalsList, TopBlocksbySeasonTotalsList, TopDefensiveReboundsBySeasonTotalsList, TopOffensiveReboundsbySeasonTotalsList +from .views import PlayerDataList, PlayerDataByTeamList, PlayerDataBySeasonList, PlayerDataByNameList, TopScorersbySeasonList, TopScorersbySeasonListPlayoffs, ThreeTwoTopPointTrends, TopAssistsBySeasonList, TopReboundsBySeasonList, TopBlocksBySeasonList, TopStealsBySeasonList, TopOffensiveReboundsBySeasonList, TopDefensiveReboundsBySeasonList, PointsPerGameHistogramBySeasonList, TopPtsScatterPlotData, TopPtsScatterPlotDataFast, TopPtsScatterPlotDataFast2018, Top20ScorersPost2009WS, Top20ScorersPost2014WS, Top20ScorersPost2018WS, TopAssistsBySeasonListPlayoffs, Top25ScorersPostSeasonPlayoffs, TopPtsScatterPlotDataFastSelect, OverallDBStats, TopScorersbySeasonTotalsList, TopAssistsBySeasonTotalsList, TopReboundsbySeasonTotalsList, TopStealsbySeasonTotalsList, TopBlocksbySeasonTotalsList, TopDefensiveReboundsBySeasonTotalsList, TopOffensiveReboundsbySeasonTotalsList, UsageRateHistogramView, PlayerShotChartDataList, landing_page urlpatterns = [ + path('', landing_page, name='landing'), path('api/playerdata/', PlayerDataList.as_view(), name='playerdata_list'), path('api/playerdata/team//', PlayerDataByTeamList.as_view(), name='playerdata_by_team'), path('api/playerdata/season//', PlayerDataBySeasonList.as_view(), name='playerdata_by_season'), @@ -24,6 +25,7 @@ urlpatterns = [ path('api/top_rebounds_defensive//', TopDefensiveReboundsBySeasonList.as_view(), name='top_rebounds_DRB'), path('api/top_rebounds_defensive/totals//', TopDefensiveReboundsBySeasonTotalsList.as_view(), name='top_rebounds_DRB_totals'), path('api/points_per_game_histogram//', PointsPerGameHistogramBySeasonList.as_view(), name='points_per_game_histogram'), + path('api/usage_histogram//', UsageRateHistogramView.as_view(), name='usage_histogram'), path('api/top_pts_scatter_plot/', TopPtsScatterPlotData.as_view(), name='top_pts_scatter_plot'), path('api/top_pts_scatter_plot_fast/', TopPtsScatterPlotDataFast.as_view(), name='top_pts_scatter_plot_fast'), path('api/top_pts_scatter_plot_fast_2018/', TopPtsScatterPlotDataFast2018.as_view(), name='top_pts_scatter_plot_fast_2018'), @@ -33,4 +35,5 @@ urlpatterns = [ path('api/top_25_PTS_ws/playoffs//', Top25ScorersPostSeasonPlayoffs.as_view(), name='top_25_PTS_ws_playoffs'), path('api/top_25_PTS_ws/season//', TopPtsScatterPlotDataFastSelect.as_view(), name='top_25_PTS_ws_season'), path('api/stats/', OverallDBStats.as_view(), name='overall_db_stats'), + path('api/shot_chart_data///', PlayerShotChartDataList.as_view(), name='player_shot_chart'), ] \ No newline at end of file diff --git a/nba_data/views.py b/nba_data/views.py index ef637b5..7eaf587 100644 --- a/nba_data/views.py +++ b/nba_data/views.py @@ -1,12 +1,12 @@ from django.shortcuts import render from django.http import JsonResponse, Http404 from django.core import serializers -from .models import PlayerData, PlayerTotalsData, PlayerAdvancedData, PlayerPlayoffTotalsData, PlayerPlayoffAdvancedData +from .models import PlayerData, PlayerTotalsData, PlayerAdvancedData, PlayerPlayoffTotalsData, PlayerPlayoffAdvancedData, PlayerShotChartData from rest_framework import generics -from .serializers import PlayerDataSerializer, HistogramDataSerializer, PlayerPlayoffTotalsDataSerializer, PlayerTotalsDataSerializer +from .serializers import PlayerDataSerializer, HistogramDataSerializer, PlayerPlayoffTotalsDataSerializer, PlayerTotalsDataSerializer, PlayerShotChartDataSerializer from rest_framework.views import APIView from rest_framework.response import Response -from django.db.models import Avg, Sum, F, FloatField, Min, Max +from django.db.models import Avg, Sum, F, Min, Max from collections import Counter import math from django.db import connection @@ -245,8 +245,40 @@ class PointsPerGameHistogramBySeasonList(generics.ListAPIView): return histogram_list +# USG Percent Histogram -# Scatter Plot - Top 30 players by total PTS across all seasons in DB. Then map those 30 players on a scatter plot against total PTS/WS for each season. +class UsageRateHistogramView(generics.ListAPIView): + serializer_class = HistogramDataSerializer + + def get_queryset(self): + season = self.kwargs['season'] + queryset = PlayerAdvancedData.objects.filter(season=season) + + # Get the usage rates for each player, ignoring null values + usage_rate_list = [player.usg_percent for player in queryset if player.usg_percent is not None] + + # Define histogram bins for usage rate ranges + bin_ranges = [i * 5 for i in range(21)] # 0, 5, 10, ..., 100 + histogram_data = Counter() + + # Fill the histogram with data + for usage_rate in usage_rate_list: + bin_index = next( + (i for i, r in enumerate(bin_ranges) if r >= usage_rate), -1) + if bin_index >= 1: + bin_label = f"{bin_ranges[bin_index - 1]}-{bin_ranges[bin_index]}" + histogram_data[bin_label] += 1 + + # Convert histogram_data to list of dicts for serialization + histogram_list = [{"range": k, "count": v} + for k, v in histogram_data.items()] + + return histogram_list + + + + +# Scatter Plot - Top 25 players by total PTS across all seasons in DB. Then map those 25 players on a scatter plot against total PTS/WS for each season. class TopPtsScatterPlotData(APIView): def get(self, request, *args, **kwargs): @@ -627,5 +659,14 @@ class OverallDBStats(APIView): return Response(response_data) +class PlayerShotChartDataList(generics.ListAPIView): + serializer_class = PlayerShotChartDataSerializer + + def get_queryset(self): + player_name = self.kwargs['player_name'] + season = self.kwargs['season'] + return PlayerShotChartData.objects.filter(player_name__icontains=player_name, season=season).order_by('id') +def landing_page(request): + return render(request, 'landing.html') \ No newline at end of file