Spaces:
Sleeping
Sleeping
Download appBackup.py from acetwo3ee/DFS_NBA_TEST: direct link, hf CLI and curl.
- Browser
- Download file 15.7 kB
-
https://huggingface.co/spaces/acetwo3ee/DFS_NBA_TEST/resolve/main/appBackup.py
- Command line
-
hf download hf://spaces/acetwo3ee/DFS_NBA_TEST/appBackup.py
-
curl -L -o appBackup.py https://huggingface.co/spaces/acetwo3ee/DFS_NBA_TEST/resolve/main/appBackup.py
15.7 kB
| import streamlit as st | |
| import pandas as pd | |
| import numpy as np | |
| import gspread | |
| from google.oauth2.service_account import Credentials | |
| from itertools import combinations | |
| from collections import Counter | |
| from tqdm import tqdm | |
| from data_processor import DataProcessor | |
| # Set page config to wide layout | |
| st.set_page_config(layout="wide") | |
| # Google Sheets Config | |
| SHEET_ID = "1PCugIPyYM1lF6KOCPUY5hL8xbGI5PWd7gVvCyudrB5c" | |
| SHEET_NAME = "Dream11 NBA Data" | |
| OUTPUT_SHEETS = ["Optimized_Lineups", "Boom_Bust", "Ownership%"] | |
| # Service Account Info (unchanged) | |
| SERVICE_ACCOUNT_INFO = { | |
| "type": "service_account", | |
| "project_id": "abiding-state-451516-e6", | |
| "private_key_id": "668f5f97d95b999d0ad706445e1a9a624b6bb263", | |
| "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCCIBM+5nspxseN\nD/Fls62ZRrCpPhqLX2TS+EDBFwhOtwlLmFVWGUUBBGVXhLRzrLS550p5toyhfOIU\nR2Bd0copSmLkglEwuUyClbswz+MJD4JCVuKbLugm5rMxJSuCwH/vXyvV8LtJESjK\nsxfQZ5uLkXS6i+S+SlNgR5TcdxskUa0/ymCEjiLojrbaZ64eiM75b/gakljI40ee\nZMWV6awSe2+EQnQ2W27H0uk4B2cMjEA0q1SHDRyLhwMNeaW3DIrCmUZntUev/KdD\nsamjgPPA8Z2VMvYh0ONr131EFlXq8atbUwHeefWLsRxeNmpzruJtASv10Y8fa/wl\nzADUaU+9AgMBAAECggEAJxI2dSaDSLH07PgihWUD9Qfyt358Pejwvx/nYZLm3au7\n0MT2qpl/zKRjQXoJLMpooYE/LXiQlVSIFYqw9rqtNuSs+OvgMP8SaAtJX4isJOZR\nOFYaSHo3HqcamuEMRYBDab5rzRsxQOk0FFjeB6d0Xt3X7MNgdXqfukMEAYaJ0cd+\n4LOhlMaDQ8jXdnthXh55Gz7Qyk0W5AjnckWdlaaNZ2y1MVI9zdJ4qDBdreua9p5g\n3mgBe93s2KbCEKBSzCTsoU1GbGw5sBP2w7MyPvXuyIgk2BNdDH5Sz+u/8Wdm7gIh\n8EXwdxWcDJRYxm5tTErqgGDDEvCB/bXaU0Yt2IiKSQKBgQC2mmmUGEUM87b7hOtC\n1+hpEf/iucHaGBrkxW0Fcz2/Gi5mPeb60gbMtbxmFMNPdDaqKQxi5KjhLP/iZgRk\nVWJJsJb/P8xuL64y8sI6NUdTGqTUxqB6CJOriMkLI0b6JVK/vTj3Hd88VthdYwNC\nFWvFi0ckT9Ab3Fp9+309oav6ZwKBgQC2bcTjuwsiXRKzTYoODoF/Z/+NIPCI4BZ5\nwUAwY/otQ0/dQB7B5+DruLnIWCVM4PzXoPvpLgrs/iiyFPDGTlYtUa2rBTfKHr13\nUA0Y97uRvBd6004RAbM/xHoWeX2uSRtD8kzs5TZGlzgPyS0FT7b9nb+1iWPrcMJy\naoQI3hhWOwKBgAlqLrVfBvhi/HU9Zi/dF/OK5QmYkEEhawmOkE9uoKlgvTGyUV1v\nRMB7Qkp1GZjYIhVl5miW54osmsjef614pYetHivs6ypnNPEViSQdKR9EyWjbEN0Z\n4hs/AzaazAcLs5eFNxGurZrb024FM97NYfAefT2HC/DTzKqgM9XgpxBTAoGBAKEW\nQLuO2IAZi3/EwlkzOepU3PWz1nIm3fHZUWLphWoefBE0o3Q52wSK85ITM6y1Js8J\nGiiHPaKTsz+8+fdCCRqvexj+3IqfplIK3+XiK0C+xETy/YZK6Asw5VWfJfmWGHth\nI8gzhj/DG//kdtqvzzgX6cbVcOPfh/QcVka2RustAoGBAJHVNd9QSSd5Z8aFTHmf\nv+oKjnQKzGY7j+K+bU3yxPHxfDrN5ticodaHxbho2VFaI17qcFVHkkX12k2I2O1Z\nV8Nve8i2JkFVEftVpSQ/01AhsbZflDm8ORJet2M8R2o5bcZ4Yc8/shI/j7Sz8BeM\nYMSoAW+xwwELc6a+5m8u60ZU\n-----END PRIVATE KEY-----\n", | |
| "client_email": "dream11-optimizer@abiding-state-451516-e6.iam.gserviceaccount.com", | |
| "client_id": "102537539700971787452", | |
| "auth_uri": "https://accounts.google.com/o/oauth2/auth", | |
| "token_uri": "https://oauth2.googleapis.com/token", | |
| "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", | |
| "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/dream11-optimizer%40abiding-state-451516-e6.iam.gserviceaccount.com", | |
| "universe_domain": "googleapis.com" | |
| } | |
| # Authenticate Google Sheets | |
| def connect_google_sheets(): | |
| try: | |
| scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"] | |
| creds = Credentials.from_service_account_info(SERVICE_ACCOUNT_INFO, scopes=scope) | |
| client = gspread.authorize(creds) | |
| return client | |
| except Exception as e: | |
| st.error(f"Failed to connect to Google Sheets: {str(e)}") | |
| return None | |
| # Fetch player data from Google Sheets | |
| def fetch_player_data(): | |
| client = connect_google_sheets() | |
| if not client: | |
| return None | |
| try: | |
| sheet = client.open_by_key(SHEET_ID).worksheet(SHEET_NAME) | |
| df = pd.DataFrame(sheet.get_all_records()) | |
| if df.empty: | |
| st.error("No data found in Google Sheet") | |
| return None | |
| required_columns = ["Player", "Position", "Credits", "Projected Points", "Team Name", "Lineup Status"] | |
| missing_required = [col for col in required_columns if col not in df.columns] | |
| if missing_required: | |
| raise ValueError(f"Missing required columns: {missing_required}") | |
| df["Credits"] = pd.to_numeric(df["Credits"], errors="coerce").fillna(0) | |
| df["Projected Points"] = pd.to_numeric(df["Projected Points"], errors="coerce").fillna(0) | |
| if "Std Dev" in df.columns: | |
| df["Std Dev"] = pd.to_numeric(df["Std Dev"], errors="coerce").fillna(0) | |
| else: | |
| df["Std Dev"] = 0 | |
| return df | |
| except Exception as e: | |
| st.error(f"Error fetching player data: {str(e)}") | |
| return None | |
| # Generate valid lineups with progress bar | |
| def generate_valid_lineups(players, min_stack=0, max_exposure=100, num_lineups=10): | |
| valid_lineups = [] | |
| player_combinations = combinations(players, 8) | |
| player_usage = Counter() | |
| progress_bar = st.progress(0) | |
| total_combinations = min(num_lineups, sum(1 for _ in player_combinations)) | |
| player_combinations = combinations(players, 8) # Recreate iterator | |
| for i, lineup in enumerate(tqdm(player_combinations, total=total_combinations, desc="Generating Lineups")): | |
| lineup_df = pd.DataFrame(lineup) | |
| lineup_df["Position"] = lineup_df["Position"].str.strip() | |
| total_credits = lineup_df["Credits"].sum() | |
| total_points = lineup_df["Projected Points"].sum() | |
| # Position constraints | |
| position_counts = {"PG": 0, "SG": 0, "SF": 0, "PF": 0, "C": 0} | |
| for pos in lineup_df["Position"]: | |
| if pos in position_counts: | |
| position_counts[pos] += 1 | |
| if not all(1 <= position_counts[pos] <= 4 for pos in position_counts): | |
| continue | |
| # Team constraints | |
| team_counts = lineup_df["Team Name"].value_counts() | |
| if min_stack > 0 and team_counts.max() < min_stack: | |
| continue | |
| if team_counts.max() > 5: | |
| continue | |
| # Salary cap | |
| if not (48500 <= total_credits <= 50000): | |
| continue | |
| # Exposure check | |
| if any(player_usage.get(p, 0) / (len(valid_lineups) + 1) * 100 > max_exposure for p in lineup_df["Player"]): | |
| continue | |
| valid_lineups.append((lineup_df, total_credits, total_points, position_counts, team_counts)) | |
| for player in lineup_df["Player"]: | |
| player_usage[player] += 1 | |
| progress_bar.progress(min((i + 1) / total_combinations, 1.0)) | |
| if len(valid_lineups) >= num_lineups: | |
| break | |
| progress_bar.empty() | |
| return sorted(valid_lineups, key=lambda x: x[2], reverse=True), player_usage | |
| # Generate accurate Boom/Bust analysis | |
| def generate_boom_bust_sheet(players_df): | |
| boom_bust_data = [] | |
| for _, row in players_df.iterrows(): | |
| proj_points = row["Projected Points"] | |
| std_dev = row["Std Dev"] | |
| if std_dev == 0: | |
| pos_factor = {"PG": 0.25, "SG": 0.22, "SF": 0.20, "PF": 0.18, "C": 0.15}.get(row["Position"], 0.20) | |
| std_dev = proj_points * pos_factor | |
| boom_points = proj_points + 1.5 * std_dev | |
| bust_points = max(0, proj_points - 1.5 * std_dev) | |
| boom_prob = 1 - 0.9332 | |
| bust_prob = 0.0668 | |
| boom_bust_data.append([ | |
| row["Player"], row["Team Name"], proj_points, | |
| round(boom_points, 1), round(bust_points, 1), | |
| round(boom_prob * 100, 1), round(bust_prob * 100, 1) | |
| ]) | |
| return pd.DataFrame(boom_bust_data, columns=[ | |
| "Player", "Team", "Projected Points", "Boom Points", "Bust Points", | |
| "Boom Probability %", "Bust Probability %" | |
| ]) | |
| # Save data to Google Sheets | |
| def save_to_google_sheets(sheet_name, df): | |
| client = connect_google_sheets() | |
| if not client: | |
| return | |
| try: | |
| sheet = client.open_by_key(SHEET_ID).worksheet(sheet_name) | |
| sheet.clear() | |
| sheet.update([df.columns.tolist()] + df.values.tolist()) | |
| except Exception as e: | |
| st.error(f"Error saving to {sheet_name}: {str(e)}") | |
| # Generate Ownership % Data with Player Count and Corrected Value Score | |
| def generate_ownership_sheet(players_df, player_usage, total_lineups): | |
| ownership_data = [] | |
| for _, row in players_df.iterrows(): | |
| player_name = row["Player"] | |
| player_count = player_usage.get(player_name, 0) | |
| ownership_pct = (player_count / total_lineups) * 100 if total_lineups > 0 else 0 | |
| value_score = (row["Projected Points"] * 500) / row["Credits"] if row["Credits"] > 0 else 0 | |
| ownership_data.append([ | |
| player_name, row["Team Name"], row["Credits"], row["Projected Points"], | |
| player_count, round(ownership_pct, 2), round(value_score, 2) | |
| ]) | |
| return pd.DataFrame(ownership_data, columns=[ | |
| "Player", "Team", "Credits", "Projected Points", "Count", "Ownership %", "Value" | |
| ]) | |
| # Filter lineups based on include or exclude logic | |
| def filter_lineups(df_lineups, players, filter_type): | |
| if not players or len(players) == 0: | |
| return df_lineups | |
| filtered = df_lineups | |
| if filter_type == "Include Players": | |
| # Include only lineups where ALL selected players are present | |
| for player in players: | |
| filtered = filtered[filtered[["Player 1", "Player 2", "Player 3", "Player 4", | |
| "Player 5", "Player 6", "Player 7", "Player 8"]].apply( | |
| lambda row: player in row.values, axis=1)] | |
| elif filter_type == "Exclude Players": | |
| # Exclude lineups where ANY of the selected players are present | |
| for player in players: | |
| filtered = filtered[~filtered[["Player 1", "Player 2", "Player 3", "Player 4", | |
| "Player 5", "Player 6", "Player 7", "Player 8"]].apply( | |
| lambda row: player in row.values, axis=1)] | |
| return filtered | |
| def main(): | |
| st.title("NBA DFS Optimizer") | |
| # Initialize components | |
| data_processor = DataProcessor() | |
| optimizer = DFSOptimizer() | |
| # Sidebar controls | |
| st.sidebar.header("Optimization Settings") | |
| num_lineups = st.sidebar.slider("Number of Lineups", 1, 50, 10) | |
| min_stack = st.sidebar.slider("Minimum Stack", 1, 5, 2) | |
| max_stack = st.sidebar.slider("Maximum Stack", min_stack, 5, 4) | |
| max_exposure = st.sidebar.slider("Maximum Exposure %", 20, 100, 60) / 100 | |
| # Load data | |
| df = fetch_player_data() | |
| if df is None: | |
| st.error("Failed to load player data") | |
| return | |
| try: | |
| data_processor.load_data(df) | |
| except ValueError as e: | |
| st.error(str(e)) | |
| return | |
| # Sidebar controls | |
| with st.sidebar: | |
| st.header("Optimization Settings") | |
| min_stack = st.slider("Min players from same team (stacking)", 0, 5, 0) | |
| max_exposure = st.slider("Max player exposure (%)", 10, 100, 100) | |
| num_lineups = st.number_input("Number of lineups to generate", 1, 10000, 10) | |
| if st.button("Run Optimization"): | |
| with st.spinner("Fetching data..."): | |
| df = fetch_player_data() | |
| if df is None: | |
| return | |
| st.session_state.df = df | |
| players = df.to_dict("records") | |
| st.write(f"Generating {num_lineups} Optimized Lineups...") | |
| # Generate Lineups | |
| valid_lineups, player_usage = generate_valid_lineups(players, min_stack, max_exposure, num_lineups) | |
| if not valid_lineups: | |
| st.error("No valid lineups found with given constraints") | |
| return | |
| # Prepare lineup data | |
| lineup_data = [] | |
| team_headers = sorted(set(df["Team Name"])) | |
| for lineup_df, credits, points, pos_counts, team_counts in valid_lineups: | |
| pos_counts_str = f"{pos_counts['PG']}-{pos_counts['SG']}-{pos_counts['SF']}-{pos_counts['PF']}-{pos_counts['C']}" | |
| yes_count = sum(lineup_df["Lineup Status"] == "Yes") | |
| no_count = sum(lineup_df["Lineup Status"] == "No") | |
| team_columns = {team: team_counts.get(team, 0) for team in team_headers} | |
| row = lineup_df["Player"].tolist() + [credits, points, pos_counts_str, yes_count, no_count] + list(team_columns.values()) | |
| lineup_data.append(row) | |
| # Store in session state | |
| st.session_state.df_lineups = pd.DataFrame(lineup_data, columns=["Player 1", "Player 2", "Player 3", "Player 4", "Player 5", | |
| "Player 6", "Player 7", "Player 8", "Credits", "FP", | |
| "Pos_Count", "Yes", "No"] + team_headers) | |
| st.session_state.filtered_lineups = st.session_state.df_lineups.copy() | |
| st.session_state.player_usage = player_usage | |
| st.session_state.lineups_generated = True | |
| # Save all lineups to Google Sheets | |
| save_to_google_sheets("Optimized_Lineups", st.session_state.df_lineups) | |
| # Generate and save Boom/Bust Sheet | |
| boom_bust_df = generate_boom_bust_sheet(df) | |
| save_to_google_sheets("Boom_Bust", boom_bust_df) | |
| # Generate and save Ownership Sheet | |
| ownership_df = generate_ownership_sheet(df, player_usage, len(valid_lineups)) | |
| save_to_google_sheets("Ownership%", ownership_df) | |
| # Display results if lineups are generated | |
| if st.session_state.lineups_generated and st.session_state.df_lineups is not None: | |
| # Player filter in sidebar with include/exclude options | |
| with st.sidebar: | |
| st.header("Filter Lineups") | |
| total_players = st.session_state.df["Player"].tolist() | |
| filter_option = st.selectbox("Filter by", ["All", "Include Players", "Exclude Players"], key="filter_option") | |
| if filter_option in ["Include Players", "Exclude Players"]: | |
| players_to_filter = st.multiselect( | |
| f"Which players would you like to {'include in' if filter_option == 'Include Players' else 'exclude from'} the lineups?", | |
| options=total_players, | |
| key="player_filter" | |
| ) | |
| else: | |
| players_to_filter = [] | |
| # Apply filter based on selected option | |
| filtered_lineups = filter_lineups(st.session_state.df_lineups, players_to_filter, filter_option) | |
| st.session_state.filtered_lineups = filtered_lineups | |
| # Display Filtered Optimized Lineups with vertical scroll | |
| st.subheader("Optimized Lineups") | |
| if len(filtered_lineups) > 0: | |
| st.dataframe( | |
| filtered_lineups.style.format({ | |
| "Credits": "{:.0f}", | |
| "FP": "{:.1f}", | |
| "Yes": "{:.0f}", | |
| "No": "{:.0f}" | |
| }), | |
| use_container_width=True, | |
| height=400 # Fixed height with vertical scrollbar | |
| ) | |
| else: | |
| st.warning(f"No lineups match the selected filter ({filter_option}).") | |
| st.write("Generated Lineups Sample:", st.session_state.df_lineups.head()) | |
| st.write(f"{'Players to Include' if filter_option == 'Include Players' else 'Players to Exclude' if filter_option == 'Exclude Players' else 'No Filter'}:", players_to_filter) | |
| st.success(f"✅ Generated {len(st.session_state.df_lineups)} lineups (showing {len(filtered_lineups)} after filtering) and saved to Google Sheets!") | |
| if __name__ == "__main__": | |
| main() |