Spaces:
Sleeping
Sleeping
Download app.py from acetwo3ee/DFS_NBA_TEST: direct link, hf CLI and curl.
- Browser
- Download file 17.1 kB
-
https://huggingface.co/spaces/acetwo3ee/DFS_NBA_TEST/resolve/main/app.py
- Command line
-
hf download hf://spaces/acetwo3ee/DFS_NBA_TEST/app.py
-
curl -L -o app.py https://huggingface.co/spaces/acetwo3ee/DFS_NBA_TEST/resolve/main/app.py
17.1 kB
| import streamlit as st | |
| import pandas as pd | |
| import gspread | |
| from google.oauth2.service_account import Credentials | |
| from collections import Counter | |
| from data_processor import DataProcessor | |
| from optimizer import DFSOptimizer | |
| # 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" | |
| } | |
| # Initialize session state variables | |
| if 'lineups_generated' not in st.session_state: | |
| st.session_state.lineups_generated = False | |
| if 'df_lineups' not in st.session_state: | |
| st.session_state.df_lineups = None | |
| if 'filtered_lineups' not in st.session_state: | |
| st.session_state.filtered_lineups = None | |
| # 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 | |
| return df | |
| except Exception as e: | |
| st.error(f"Error fetching player data: {str(e)}") | |
| return None | |
| # 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()) | |
| return True | |
| except Exception as e: | |
| st.error(f"Error saving to {sheet_name}: {str(e)}") | |
| return False | |
| # 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.copy() | |
| player_columns = [f"Player {i}" for i in range(1, 9)] | |
| if filter_type == "Include Players": | |
| # Include only lineups where ALL selected players are present | |
| for player in players: | |
| filtered = filtered[filtered[player_columns].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_columns].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 | |
| with st.sidebar: | |
| st.header("Optimization Settings") | |
| # Removed stacking controls | |
| max_players_per_team = st.slider("Max players from same team", 1, 5, 5) | |
| max_exposure = st.slider("Max player exposure (%)", 10, 100, 60) | |
| min_salary = st.slider("Min Salary", 48000, 49900, 48500, 100) | |
| max_salary = st.slider("Max Salary", min_salary, 50000, 50000, 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 | |
| with st.spinner("Processing data and generating lineups..."): | |
| try: | |
| # Process data using the data processor | |
| data_processor.load_data(df) | |
| # Generate lineups using the optimizer | |
| optimizer.setup_model(data_processor.players_df, max_salary) | |
| # Add team constraint without stacking requirement | |
| # Only enforce maximum players per team | |
| teams = data_processor.players_df["Team Name"].unique() | |
| for team in teams: | |
| team_vars = [optimizer.player_vars[f"player_{idx}"] | |
| for idx, row in data_processor.players_df.iterrows() | |
| if row["Team Name"] == team] | |
| if team_vars: | |
| optimizer.model += lpSum(team_vars) <= max_players_per_team | |
| optimizer.add_exposure_constraints(data_processor.players_df, max_exposure/100) | |
| # Generate the specified number of lineups | |
| lineups = [] | |
| for i in range(num_lineups): | |
| try: | |
| # Setup optimizer for each lineup | |
| if i > 0: | |
| optimizer.setup_model(data_processor.players_df, max_salary) | |
| # Add team constraint without stacking | |
| for team in teams: | |
| team_vars = [optimizer.player_vars[f"player_{idx}"] | |
| for idx, row in data_processor.players_df.iterrows() | |
| if row["Team Name"] == team] | |
| if team_vars: | |
| optimizer.model += lpSum(team_vars) <= max_players_per_team | |
| # Add exposure constraint | |
| optimizer.add_exposure_constraints(data_processor.players_df, max_exposure/100) | |
| # Add minimum salary constraint | |
| optimizer.model += lpSum([optimizer.player_vars[f"player_{idx}"] * row["Credits"] | |
| for idx, row in data_processor.players_df.iterrows()]) >= min_salary | |
| # Add uniqueness constraint | |
| for prev_lineup in lineups: | |
| prev_players = [p["Player"] for p in prev_lineup["players"]] | |
| overlap_vars = [optimizer.player_vars[f"player_{idx}"] | |
| for idx, row in data_processor.players_df.iterrows() | |
| if row["Player"] in prev_players] | |
| if overlap_vars: | |
| optimizer.model += lpSum(overlap_vars) <= 6 # Allow max 6 players overlap | |
| # Solve the optimization model | |
| from pulp import LpStatus, lpSum, value | |
| optimizer.model.solve() | |
| # Check if a solution was found | |
| if optimizer.model.status != 1: # 1 means optimal solution found | |
| st.warning(f"No optimal solution found for lineup {i+1}. Stopping generation.") | |
| break | |
| # Get the selected players from the solution | |
| selected_players = [] | |
| total_points = 0 | |
| total_salary = 0 | |
| for var_name, var in optimizer.player_vars.items(): | |
| if var.value() > 0: | |
| player_idx = int(var_name.split('_')[1]) | |
| player_data = data_processor.players_df.iloc[player_idx] | |
| selected_players.append({ | |
| "Player": player_data["Player"], | |
| "Position": player_data["Position"], | |
| "Team": player_data["Team Name"], | |
| "Salary": player_data["Credits"], | |
| "Projected": player_data["Projected Points"] | |
| }) | |
| total_points += player_data["Projected Points"] | |
| total_salary += player_data["Credits"] | |
| lineups.append({ | |
| "players": selected_players, | |
| "total_points": total_points, | |
| "total_salary": total_salary | |
| }) | |
| except Exception as e: | |
| st.error(f"Error generating lineup {i+1}: {str(e)}") | |
| continue | |
| if not lineups: | |
| st.error("No valid lineups found with given constraints") | |
| return | |
| # Convert lineups to DataFrame format | |
| lineup_data = [] | |
| team_headers = sorted(set(df["Team Name"])) | |
| for lineup in lineups: | |
| players = lineup["players"] | |
| total_credits = lineup["total_salary"] | |
| total_points = lineup["total_points"] | |
| # Count positions | |
| pos_counts = Counter(player["Position"] for player in players) | |
| pos_counts_str = f"{pos_counts.get('PG', 0)}-{pos_counts.get('SG', 0)}-" \ | |
| f"{pos_counts.get('SF', 0)}-{pos_counts.get('PF', 0)}-{pos_counts.get('C', 0)}" | |
| # Count lineup status | |
| yes_count = sum(1 for player in players if | |
| df[df["Player"] == player["Player"]]["Lineup Status"].values[0] == "Yes") | |
| no_count = sum(1 for player in players if | |
| df[df["Player"] == player["Player"]]["Lineup Status"].values[0] == "No") | |
| # Count teams | |
| team_counts = Counter(player["Team"] for player in players) | |
| team_columns = {team: team_counts.get(team, 0) for team in team_headers} | |
| # Create lineup row | |
| row = [player["Player"] for player in players] | |
| row += [total_credits, total_points, pos_counts_str, yes_count, no_count] | |
| row += list(team_columns.values()) | |
| lineup_data.append(row) | |
| columns = [f"Player {i}" for i in range(1, 9)] | |
| columns += ["Credits", "FP", "Pos_Count", "Yes", "No"] + team_headers | |
| # Store in session state | |
| st.session_state.df_lineups = pd.DataFrame(lineup_data, columns=columns) | |
| st.session_state.filtered_lineups = st.session_state.df_lineups.copy() | |
| st.session_state.lineups_generated = True | |
| # Generate player usage for ownership percentages | |
| all_players = [player for lineup in lineups for player in lineup["players"]] | |
| player_counter = Counter(player["Player"] for player in all_players) | |
| # Save lineups to Google Sheets | |
| save_to_google_sheets("Optimized_Lineups", st.session_state.df_lineups) | |
| # Generate and save Boom/Bust Sheet | |
| boom_bust_df = data_processor.get_boom_bust_analysis() | |
| save_to_google_sheets("Boom_Bust", boom_bust_df) | |
| # Generate and save Ownership Sheet | |
| ownership_df = data_processor.calculate_ownership(lineups) | |
| save_to_google_sheets("Ownership%", ownership_df) | |
| except Exception as e: | |
| st.error(f"Error during optimization: {str(e)}") | |
| import traceback | |
| st.code(traceback.format_exc()) | |
| return | |
| # 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 = data_processor.players_df["Player"].tolist() if 'data_processor' in locals() and hasattr(data_processor, 'players_df') else [] | |
| if not total_players and 'df' in locals(): | |
| total_players = 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.success(f"✅ Generated {len(st.session_state.df_lineups)} lineups (showing {len(filtered_lineups)} after filtering) and saved to Google Sheets!") | |
| if __name__ == "__main__": | |
| from pulp import LpVariable, LpProblem, LpMaximize, LpBinary, lpSum, LpStatus | |
| main() |