import streamlit as st import numpy as np import pandas as pd import re import math from database import db st.set_page_config(layout="wide") print(f"Streamlit version: {st.__version__}") game_format = {'Win Percentage': '{:.2%}','First Inning Lead Percentage': '{:.2%}', 'Fifth Inning Lead Percentage': '{:.2%}', '8+ runs': '{:.2%}', 'DK LevX': '{:.2%}', 'FD LevX': '{:.2%}'} player_roo_format = {'Top_finish': '{:.2%}','Top_5_finish': '{:.2%}', 'Top_10_finish': '{:.2%}', '20+%': '{:.2%}', '2x%': '{:.2%}', '3x%': '{:.2%}', '4x%': '{:.2%}','GPP%': '{:.2%}'} dk_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'] fd_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'] dk_hb_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] fd_hb_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] dk_sd_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'] fd_sd_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'] showdown_selections = ['Showdown #1', 'Showdown #2', 'Showdown #3', 'Showdown #4', 'Showdown #5', 'Showdown #6', 'Showdown #7', 'Showdown #8', 'Showdown #9', 'Showdown #10', 'Showdown #11', 'Showdown #12', 'Showdown #13', 'Showdown #14', 'Showdown #15'] dk_db_showdown_selections = ['DK_NFL_SD_seed_frame_Showdown #1', 'DK_NFL_SD_seed_frame_Showdown #2', 'DK_NFL_SD_seed_frame_Showdown #3', 'DK_NFL_SD_seed_frame_Showdown #4', 'DK_NFL_SD_seed_frame_Showdown #5', 'DK_NFL_SD_seed_frame_Showdown #6', 'DK_NFL_SD_seed_frame_Showdown #7', 'DK_NFL_SD_seed_frame_Showdown #8', 'DK_NFL_SD_seed_frame_Showdown #9', 'DK_NFL_SD_seed_frame_Showdown #10', 'DK_NFL_SD_seed_frame_Showdown #11', 'DK_NFL_SD_seed_frame_Showdown #12', 'DK_NFL_SD_seed_frame_Showdown #13', 'DK_NFL_SD_seed_frame_Showdown #14', 'DK_NFL_SD_seed_frame_Showdown #15'] fd_db_showdown_selections = ['FD_NFL_SD_seed_frame_Showdown #1', 'FD_NFL_SD_seed_frame_Showdown #2', 'FD_NFL_SD_seed_frame_Showdown #3', 'FD_NFL_SD_seed_frame_Showdown #4', 'FD_NFL_SD_seed_frame_Showdown #5', 'FD_NFL_SD_seed_frame_Showdown #6', 'FD_NFL_SD_seed_frame_Showdown #7', 'FD_NFL_SD_seed_frame_Showdown #8', 'FD_NFL_SD_seed_frame_Showdown #9', 'FD_NFL_SD_seed_frame_Showdown #10', 'FD_NFL_SD_seed_frame_Showdown #11', 'FD_NFL_SD_seed_frame_Showdown #12', 'FD_NFL_SD_seed_frame_Showdown #13', 'FD_NFL_SD_seed_frame_Showdown #14', 'FD_NFL_SD_seed_frame_Showdown #15'] dk_showdown_db_translation = dict(zip(showdown_selections, dk_db_showdown_selections)) fd_showdown_db_translation = dict(zip(showdown_selections, fd_db_showdown_selections)) # Probably should have done this in a dictionary to start with wrong_team_names = ['Denver Broncos', 'Washington Commanders', 'Cincinnati Bengals', 'Arizona Cardinals', 'Los Angeles Rams', 'Pittsburgh Steelers', 'Jacksonville Jaguars', 'New England Patriots', 'Tampa Bay Buccaneers', 'San Francisco 49ers', 'Green Bay Packers', 'New York Jets', 'Indianapolis Colts', 'Miami Dolphins', 'Detroit Lions', 'Las Vegas Raiders', 'Atlanta Falcons', 'Seattle Seahawks', 'Houston Texans', 'New Orleans Saints', 'Carolina Panthers', 'New York Giants', 'Cleveland Browns', 'Tennessee Titans', 'Philadelphia Eagles', 'Dallas Cowboys', 'Kansas City Chiefs', 'Los Angeles Chargers', 'Baltimore Ravens', 'Buffalo Bills', 'Minnesota Vikings', 'Chicago Bears'] right_name_teams = ['Broncos', 'Commanders', 'Bengals', 'Cardinals', 'Rams', 'Steelers', 'Jaguars', 'Patriots', 'Buccaneers', '49ers', 'Packers', 'Jets', 'Colts', 'Dolphins', 'Lions', 'Raiders', 'Falcons', 'Seahawks', 'Texans', 'Saints', 'Panthers', 'Giants', 'Browns', 'Titans', 'Eagles', 'Cowboys', 'Chiefs', 'Chargers', 'Ravens', 'Bills', 'Vikings', 'Bears'] st.markdown(""" """, unsafe_allow_html=True) @st.cache_resource(ttl=60) def define_dk_showdown_slates(): collection = db["DK_SD_NFL_ROO"] cursor = collection.find() raw_display = pd.DataFrame(list(cursor)) unique_slates = raw_display['slate'].unique() slate_names = [] for slate in unique_slates: slate_data = raw_display[raw_display['slate'] == slate] slate_name = slate_data.iloc[0]['Team'] + ' vs. ' + slate_data.iloc[0]['Opp'] slate_names.append(slate_name) slate_name_lookup = dict(zip(slate_names, unique_slates)) return slate_names, slate_name_lookup @st.cache_resource(ttl=60) def define_fd_showdown_slates(): collection = db["FD_SD_NFL_ROO"] cursor = collection.find() raw_display = pd.DataFrame(list(cursor)) unique_slates = raw_display['slate'].unique() slate_names = [] for slate in unique_slates: slate_data = raw_display[raw_display['slate'] == slate] slate_name = slate_data.iloc[0]['Team'] + ' vs. ' + slate_data.iloc[0]['Opp'] slate_names.append(slate_name) slate_name_lookup = dict(zip(slate_names, unique_slates)) return slate_names, slate_name_lookup @st.cache_resource(ttl=60) def init_handbuilder_data(site_var): """Load only the data needed for handbuilder""" if site_var == 'Draftkings': collection = db["DK_NFL_ROO"] cursor = collection.find() raw_display = pd.DataFrame(list(cursor)) raw_display = raw_display.rename(columns={'player_ID': 'player_id'}) raw_display = raw_display[['Player', 'Position', 'Team', 'Opp', 'Salary', 'Floor', 'Median', 'exFPTS', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '2x%', '3x%', '4x%', 'Own', 'Small_Field_Own', 'Large_Field_Own', 'Cash_Field_Own', 'CPT_Own', 'LevX', 'version', 'slate', 'timestamp', 'player_id', 'site']] load_display = raw_display[raw_display['Position'] != 'K'] load_display['Player'] = load_display['Player'].map(dict(zip(wrong_team_names, right_name_teams)), na_action='ignore').fillna(load_display['Player']) return load_display.dropna(subset=['Median']) else: collection = db["FD_NFL_ROO"] cursor = collection.find() raw_display = pd.DataFrame(list(cursor)) raw_display = raw_display.rename(columns={'player_ID': 'player_id'}) raw_display = raw_display[['Player', 'Position', 'Team', 'Opp', 'Salary', 'Floor', 'Median', 'exFPTS', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '2x%', '3x%', '4x%', 'Own', 'Small_Field_Own', 'Large_Field_Own', 'Cash_Field_Own', 'CPT_Own', 'LevX', 'version', 'slate', 'timestamp', 'player_id', 'site']] load_display = raw_display[raw_display['Position'] != 'K'] load_display['Player'] = load_display['Player'].map(dict(zip(wrong_team_names, right_name_teams)), na_action='ignore').fillna(load_display['Player']) return load_display.dropna(subset=['Median']) @st.cache_resource(ttl=60) def init_baselines(type_var: str): collection = db["Player_Baselines"] cursor = collection.find() raw_display = pd.DataFrame(list(cursor)) raw_display = raw_display[['name', 'Team', 'Opp', 'Position', 'Salary', 'team_plays', 'team_pass', 'team_rush', 'team_tds', 'team_pass_tds', 'team_rush_tds', 'dropbacks', 'pass_yards', 'pass_tds', 'rush_att', 'rush_yards', 'rush_tds', 'targets', 'rec', 'rec_yards', 'rec_tds', 'PPR', 'Half_PPR', 'Own']] raw_display['name'] = raw_display['name'].map(dict(zip(wrong_team_names, right_name_teams)), na_action='ignore').fillna(raw_display['name']) player_stats = raw_display[raw_display['Position'] != 'K'] if type_var == 'Regular': collection = db["DK_NFL_ROO"] cursor = collection.find() raw_display = pd.DataFrame(list(cursor)) raw_display = raw_display.rename(columns={'player_ID': 'player_id'}) raw_display = raw_display[['Player', 'Position', 'Team', 'Opp', 'Salary', 'Floor', 'Median', 'exFPTS', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '2x%', '3x%', '4x%', 'Own', 'Small_Field_Own', 'Large_Field_Own', 'Cash_Field_Own', 'CPT_Own', 'LevX', 'version', 'slate', 'timestamp', 'player_id', 'site']] raw_display['Player'] = raw_display['Player'].map(dict(zip(wrong_team_names, right_name_teams)), na_action='ignore').fillna(raw_display['Player']) load_display = raw_display[raw_display['Position'] != 'K'] dk_roo_raw = load_display.dropna(subset=['Median']) dk_id_map = dict(zip(dk_roo_raw['Player'], dk_roo_raw['player_id'])) collection = db["FD_NFL_ROO"] cursor = collection.find() raw_display = pd.DataFrame(list(cursor)) raw_display = raw_display.rename(columns={'player_ID': 'player_id'}) raw_display = raw_display[['Player', 'Position', 'Team', 'Opp', 'Salary', 'Floor', 'Median', 'exFPTS', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '2x%', '3x%', '4x%', 'Own', 'Small_Field_Own', 'Large_Field_Own', 'Cash_Field_Own', 'CPT_Own', 'LevX', 'version', 'slate', 'timestamp', 'player_id', 'site']] raw_display['Player'] = raw_display['Player'].map(dict(zip(wrong_team_names, right_name_teams)), na_action='ignore').fillna(raw_display['Player']) load_display = raw_display[raw_display['Position'] != 'K'] fd_roo_raw = load_display.dropna(subset=['Median']) fd_id_map = dict(zip(fd_roo_raw['Player'], fd_roo_raw['player_id'])) collection = db["DK_DFS_Stacks"] cursor = collection.find() raw_display = pd.DataFrame(list(cursor)) raw_display = raw_display[['Team', 'QB', 'WR1_TE', 'WR2_TE', 'Total', 'Salary', 'Floor', 'Median', 'exFPTS', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '60+%', '2x%', '3x%', '4x%', 'Own', 'LevX', 'slate']] dk_stacks_raw = raw_display.copy() collection = db["FD_DFS_Stacks"] cursor = collection.find() raw_display = pd.DataFrame(list(cursor)) raw_display = raw_display[['Team', 'QB', 'WR1_TE', 'WR2_TE', 'Total', 'Salary', 'Floor', 'Median', 'exFPTS', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '60+%', '2x%', '3x%', '4x%', 'Own', 'LevX', 'slate']] fd_stacks_raw = raw_display.copy() dk_sd_roo_raw = None fd_sd_roo_raw = None dk_sd_id_map = None fd_sd_id_map = None elif type_var == 'Showdown': collection = db["DK_SD_NFL_ROO"] cursor = collection.find() raw_display = pd.DataFrame(list(cursor)) raw_display = raw_display.rename(columns={'player_ID': 'player_id'}) raw_display = raw_display[['Player', 'Position', 'Team', 'Opp', 'Salary', 'Floor', 'Median', 'exFPTS', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '2x%', '3x%', '4x%', 'Own', 'Small_Field_Own', 'Large_Field_Own', 'Cash_Field_Own', 'CPT_Own', 'LevX', 'version', 'slate', 'timestamp', 'player_id', 'site']] raw_display['Player'] = raw_display['Player'].map(dict(zip(wrong_team_names, right_name_teams)), na_action='ignore').fillna(raw_display['Player']) # load_display = raw_display[raw_display['Position'] != 'K'] dk_sd_roo_raw = raw_display.dropna(subset=['Median']) dk_sd_id_map = dict(zip(dk_sd_roo_raw['Player'], dk_sd_roo_raw['player_id'])) collection = db["FD_SD_NFL_ROO"] cursor = collection.find() raw_display = pd.DataFrame(list(cursor)) raw_display = raw_display.rename(columns={'player_ID': 'player_id'}) raw_display = raw_display[['Player', 'Position', 'Team', 'Opp', 'Salary', 'Floor', 'Median', 'exFPTS', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '2x%', '3x%', '4x%', 'Own', 'Small_Field_Own', 'Large_Field_Own', 'Cash_Field_Own', 'CPT_Own', 'LevX', 'version', 'slate', 'timestamp', 'player_id', 'site']] raw_display['Player'] = raw_display['Player'].map(dict(zip(wrong_team_names, right_name_teams)), na_action='ignore').fillna(raw_display['Player']) # load_display = raw_display[raw_display['Position'] != 'K'] fd_sd_roo_raw = raw_display.dropna(subset=['Median']) fd_sd_id_map = dict(zip(fd_sd_roo_raw['Player'], fd_sd_roo_raw['player_id'])) dk_roo_raw = None fd_roo_raw = None dk_id_map = None fd_id_map = None dk_stacks_raw = None fd_stacks_raw = None return player_stats, dk_stacks_raw, fd_stacks_raw, dk_roo_raw, fd_roo_raw, dk_sd_roo_raw, fd_sd_roo_raw, dk_id_map, fd_id_map, dk_sd_id_map, fd_sd_id_map @st.cache_resource(ttl = 60) def init_DK_lineups(type_var, slate_var, prio_var, prio_mix, db_translation, lineup_num, player_var2): if prio_var == 'Mix': prio_var = None if type_var == 'Regular': if slate_var == 'Main': collection = db['DK_NFL_name_map'] cursor = collection.find() raw_data = pd.DataFrame(list(cursor)) raw_data['value'] = raw_data['value'].map(dict(zip(wrong_team_names, right_name_teams)), na_action='ignore').fillna(raw_data['value']) names_dict = dict(zip(raw_data['key'], raw_data['value'])) collection = db['DK_NFL_seed_frame_Main Slate'] if prio_var == None: if player_var2 != []: player_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] query_conditions = [] for player in player_var2: # Create a condition for each player to check if they appear in any column player_condition = {'$or': [{col: player} for col in player_columns]} query_conditions.append(player_condition) # Combine all player conditions with $or if query_conditions: filter_query = {'$or': query_conditions} cursor1 = collection.find(filter_query).limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find(filter_query).sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) else: cursor1 = collection.find().limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find().sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) raw_display = pd.concat([pd.DataFrame(list(cursor1)), pd.DataFrame(list(cursor2))]) else: cursor1 = collection.find().limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find().sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) raw_display = pd.concat([pd.DataFrame(list(cursor1)), pd.DataFrame(list(cursor2))]) else: cursor = collection.find().sort(prio_var, -1).limit(lineup_num) raw_display = pd.DataFrame(list(cursor)) raw_display = raw_display.drop_duplicates(subset=['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']) raw_display = raw_display[['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']] dict_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] # Map names raw_display[dict_columns] = raw_display[dict_columns].apply(lambda x: x.map(names_dict)) elif slate_var == 'Secondary': collection = db['DK_NFL_Secondary_name_map'] cursor = collection.find() raw_data = pd.DataFrame(list(cursor)) raw_data['value'] = raw_data['value'].map(dict(zip(wrong_team_names, right_name_teams)), na_action='ignore').fillna(raw_data['value']) names_dict = dict(zip(raw_data['key'], raw_data['value'])) collection = db['DK_NFL_seed_frame_Secondary Slate'] if prio_var == None: if player_var2 != []: player_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] query_conditions = [] for player in player_var2: # Create a condition for each player to check if they appear in any column player_condition = {'$or': [{col: player} for col in player_columns]} query_conditions.append(player_condition) # Combine all player conditions with $or if query_conditions: filter_query = {'$or': query_conditions} cursor1 = collection.find(filter_query).limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find(filter_query).sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) else: cursor1 = collection.find().limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find().sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) raw_display = pd.concat([pd.DataFrame(list(cursor1)), pd.DataFrame(list(cursor2))]) else: cursor1 = collection.find().limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find().sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) raw_display = pd.concat([pd.DataFrame(list(cursor1)), pd.DataFrame(list(cursor2))]) else: cursor = collection.find().sort(prio_var, -1).limit(lineup_num) raw_display = pd.DataFrame(list(cursor)) raw_display = raw_display.drop_duplicates(subset=['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']) raw_display = raw_display[['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']] dict_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] # Map names raw_display[dict_columns] = raw_display[dict_columns].apply(lambda x: x.map(names_dict)) elif slate_var == 'Auxiliary': collection = db['DK_NFL_Late_name_map'] cursor = collection.find() raw_data = pd.DataFrame(list(cursor)) raw_data['value'] = raw_data['value'].map(dict(zip(wrong_team_names, right_name_teams)), na_action='ignore').fillna(raw_data['value']) names_dict = dict(zip(raw_data['key'], raw_data['value'])) collection = db['DK_NFL_seed_frame_Late Slate'] if prio_var == None: if player_var2 != []: player_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] query_conditions = [] for player in player_var2: # Create a condition for each player to check if they appear in any column player_condition = {'$or': [{col: player} for col in player_columns]} query_conditions.append(player_condition) # Combine all player conditions with $or if query_conditions: filter_query = {'$or': query_conditions} cursor1 = collection.find(filter_query).limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find(filter_query).sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) else: cursor1 = collection.find().limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find().sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) raw_display = pd.concat([pd.DataFrame(list(cursor1)), pd.DataFrame(list(cursor2))]) else: cursor1 = collection.find().limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find().sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) raw_display = pd.concat([pd.DataFrame(list(cursor1)), pd.DataFrame(list(cursor2))]) else: cursor = collection.find().sort(prio_var, -1).limit(lineup_num) raw_display = pd.DataFrame(list(cursor)) raw_display = raw_display.drop_duplicates(subset=['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']) raw_display = raw_display[['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']] dict_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] # Map names raw_display[dict_columns] = raw_display[dict_columns].apply(lambda x: x.map(names_dict)) elif type_var == 'Showdown': collection = db[db_translation[slate_var]] if prio_var == None: if player_var2 != []: player_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5'] query_conditions = [] for player in player_var2: # Create a condition for each player to check if they appear in any column player_condition = {'$or': [{col: player} for col in player_columns]} query_conditions.append(player_condition) # Combine all player conditions with $or if query_conditions: filter_query = {'$or': query_conditions} cursor1 = collection.find(filter_query).limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find(filter_query).sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) else: cursor1 = collection.find().limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find().sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) raw_display = pd.concat([pd.DataFrame(list(cursor1)), pd.DataFrame(list(cursor2))]) else: cursor1 = collection.find().limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find().sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) raw_display = pd.concat([pd.DataFrame(list(cursor1)), pd.DataFrame(list(cursor2))]) else: cursor = collection.find().sort(prio_var, -1).limit(lineup_num) raw_display = pd.DataFrame(list(cursor)) raw_display = raw_display.drop_duplicates(subset=['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5']) raw_display = raw_display[['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']] for column in ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5']: raw_display[column] = raw_display[column].map(dict(zip(wrong_team_names, right_name_teams)), na_action='ignore').fillna(raw_display[column]) DK_seed = raw_display.to_numpy() return DK_seed @st.cache_resource(ttl = 60) def init_FD_lineups(type_var, slate_var, prio_var, prio_mix, db_translation, lineup_num, player_var2): if prio_var == 'Mix': prio_var = None if type_var == 'Regular': if slate_var == 'Main': collection = db['FD_NFL_name_map'] cursor = collection.find() raw_data = pd.DataFrame(list(cursor)) raw_data['value'] = raw_data['value'].map(dict(zip(wrong_team_names, right_name_teams)), na_action='ignore').fillna(raw_data['value']) names_dict = dict(zip(raw_data['key'], raw_data['value'])) collection = db['FD_NFL_seed_frame_Main Slate'] if prio_var == None: if player_var2 != []: player_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] query_conditions = [] for player in player_var2: # Create a condition for each player to check if they appear in any column player_condition = {'$or': [{col: player} for col in player_columns]} query_conditions.append(player_condition) # Combine all player conditions with $or if query_conditions: filter_query = {'$or': query_conditions} cursor1 = collection.find(filter_query).limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find(filter_query).sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) else: cursor1 = collection.find().limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find().sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) raw_display = pd.concat([pd.DataFrame(list(cursor1)), pd.DataFrame(list(cursor2))]) else: cursor1 = collection.find().limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find().sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) raw_display = pd.concat([pd.DataFrame(list(cursor1)), pd.DataFrame(list(cursor2))]) else: cursor = collection.find().sort(prio_var, -1).limit(lineup_num) raw_display = pd.DataFrame(list(cursor)) raw_display = raw_display.drop_duplicates(subset=['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']) raw_display = raw_display[['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']] dict_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] # Map names raw_display[dict_columns] = raw_display[dict_columns].apply(lambda x: x.map(names_dict)) elif slate_var == 'Secondary': collection = db['FD_NFL_Secondary_name_map'] cursor = collection.find() raw_data = pd.DataFrame(list(cursor)) raw_data['value'] = raw_data['value'].map(dict(zip(wrong_team_names, right_name_teams)), na_action='ignore').fillna(raw_data['value']) names_dict = dict(zip(raw_data['key'], raw_data['value'])) collection = db['FD_NFL_Secondary_seed_frame_Secondary Slate'] if prio_var == None: if player_var2 != []: player_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] query_conditions = [] for player in player_var2: # Create a condition for each player to check if they appear in any column player_condition = {'$or': [{col: player} for col in player_columns]} query_conditions.append(player_condition) # Combine all player conditions with $or if query_conditions: filter_query = {'$or': query_conditions} cursor1 = collection.find(filter_query).limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find(filter_query).sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) else: cursor1 = collection.find().limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find().sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) raw_display = pd.concat([pd.DataFrame(list(cursor1)), pd.DataFrame(list(cursor2))]) else: cursor1 = collection.find().limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find().sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) raw_display = pd.concat([pd.DataFrame(list(cursor1)), pd.DataFrame(list(cursor2))]) else: cursor = collection.find().sort(prio_var, -1).limit(lineup_num) raw_display = pd.DataFrame(list(cursor)) raw_display = raw_display.drop_duplicates(subset=['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']) raw_display = raw_display[['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']] dict_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] # Map names raw_display[dict_columns] = raw_display[dict_columns].apply(lambda x: x.map(names_dict)) elif slate_var == 'Auxiliary': collection = db['FD_NFL_Late_name_map'] cursor = collection.find() raw_data = pd.DataFrame(list(cursor)) raw_data['value'] = raw_data['value'].map(dict(zip(wrong_team_names, right_name_teams)), na_action='ignore').fillna(raw_data['value']) names_dict = dict(zip(raw_data['key'], raw_data['value'])) collection = db['FD_NFL_Late_seed_frame_Late Slate'] if prio_var == None: if player_var2 != []: player_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] query_conditions = [] for player in player_var2: # Create a condition for each player to check if they appear in any column player_condition = {'$or': [{col: player} for col in player_columns]} query_conditions.append(player_condition) # Combine all player conditions with $or if query_conditions: filter_query = {'$or': query_conditions} cursor1 = collection.find(filter_query).limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find(filter_query).sort('Own', -1).limit(math.ceil(lineup_num = ((100 - prio_mix) / 100))) else: cursor1 = collection.find().limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find().sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) raw_display = pd.concat([pd.DataFrame(list(cursor1)), pd.DataFrame(list(cursor2))]) else: cursor1 = collection.find().limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find().sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) raw_display = pd.concat([pd.DataFrame(list(cursor1)), pd.DataFrame(list(cursor2))]) else: cursor = collection.find().sort(prio_var, -1).limit(lineup_num) raw_display = pd.DataFrame(list(cursor)) raw_display = raw_display.drop_duplicates(subset=['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']) raw_display = raw_display[['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']] dict_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] # Map names raw_display[dict_columns] = raw_display[dict_columns].apply(lambda x: x.map(names_dict)) elif type_var == 'Showdown': collection = db[db_translation[slate_var]] if prio_var == None: if player_var2 != []: player_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5'] query_conditions = [] for player in player_var2: # Create a condition for each player to check if they appear in any column player_condition = {'$or': [{col: player} for col in player_columns]} query_conditions.append(player_condition) # Combine all player conditions with $or if query_conditions: filter_query = {'$or': query_conditions} cursor1 = collection.find(filter_query).limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find(filter_query).sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) else: cursor1 = collection.find().limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find().sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) raw_display = pd.concat([pd.DataFrame(list(cursor1)), pd.DataFrame(list(cursor2))]) else: cursor1 = collection.find().limit(math.ceil(lineup_num * (prio_mix / 100))) cursor2 = collection.find().sort('Own', -1).limit(math.ceil(lineup_num * ((100 - prio_mix) / 100))) raw_display = pd.concat([pd.DataFrame(list(cursor1)), pd.DataFrame(list(cursor2))]) else: cursor = collection.find().sort(prio_var, -1).limit(lineup_num) raw_display = pd.DataFrame(list(cursor)) raw_display = raw_display.drop_duplicates(subset=['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5']) raw_display = raw_display[['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']] for column in ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5']: raw_display[column] = raw_display[column].map(dict(zip(wrong_team_names, right_name_teams)), na_action='ignore').fillna(raw_display[column]) FD_seed = raw_display.to_numpy() return FD_seed @st.cache_data def convert_df_to_csv(df): return df.to_csv().encode('utf-8') @st.cache_data def convert_df(array): array = pd.DataFrame(array, columns=column_names) return array.to_csv().encode('utf-8') @st.cache_data def convert_pm_df(array): array = pd.DataFrame(array) return array.to_csv().encode('utf-8') @st.cache_data def convert_hb_df(array, column_names): array = pd.DataFrame(array, columns=column_names) return array.to_csv().encode('utf-8') try: slate_names_dk, slate_name_lookup_dk = define_dk_showdown_slates() except: slate_names_dk = [] slate_name_lookup_dk = {} try: slate_names_fd, slate_name_lookup_fd = define_fd_showdown_slates() except: slate_names_fd = [] slate_name_lookup_fd = {} app_load_reset_column, app_view_site_column, = st.columns([1, 9]) with app_load_reset_column: if st.button("Load/Reset Data", key='reset_data_button'): st.cache_data.clear() slate_names_dk, slate_name_lookup_dk = define_dk_showdown_slates() slate_names_fd, slate_name_lookup_fd = define_fd_showdown_slates() player_stats, dk_stacks_raw, fd_stacks_raw, dk_roo_raw, fd_roo_raw, dk_sd_roo_raw, fd_sd_roo_raw, dk_id_map, fd_id_map, dk_sd_id_map, fd_sd_id_map = init_baselines('Regular') dk_lineups = init_DK_lineups('Regular', 'Main Slate', 'proj', 50, dk_showdown_db_translation, 25000, []) fd_lineups = init_FD_lineups('Regular', 'Main Slate', 'proj', 50, fd_showdown_db_translation, 25000, []) for key in st.session_state.keys(): del st.session_state[key] with app_view_site_column: with st.container(): app_view_column, app_site_column, app_type_column = st.columns([3, 3, 3]) with app_view_column: view_var = st.selectbox("Select view", ["Simple", "Advanced"], key='view_selectbox') with app_site_column: site_var = st.selectbox("What site do you want to view?", ('Draftkings', 'Fanduel'), key='site_selectbox') with app_type_column: type_var = st.selectbox("What type of data do you want to view?", ('Regular', 'Showdown'), key='type_selectbox') selected_tab = st.segmented_control( "Select Tab", options=["Player ROO", "Stacks ROO", "Handbuilder", "Optimals"], selection_mode='single', default='Player ROO', width='stretch', label_visibility='collapsed', key='tab_selector' ) if selected_tab == 'Handbuilder': if 'handbuilder_data' not in st.session_state: st.session_state['handbuilder_data'] = init_handbuilder_data(site_var) t_stamp = f"Last Update: " + str(st.session_state['handbuilder_data']['timestamp'][0]) + f" CST" else: t_stamp = f"Last Update: " + str(st.session_state['handbuilder_data']['timestamp'][0]) + f" CST" # Use the lightweight handbuilder data if site_var == 'Draftkings': handbuild_roo = st.session_state['handbuilder_data'][st.session_state['handbuilder_data']['site'] == 'Draftkings'] else: handbuild_roo = st.session_state['handbuilder_data'][st.session_state['handbuilder_data']['site'] == 'Fanduel'] handbuilder_header_column, handbuilder_slate_column = st.columns(2) with handbuilder_header_column: st.header("Handbuilder") with handbuilder_slate_column: slate_var3 = st.selectbox("Slate Selection", options=['Main', 'Secondary', 'Auxiliary'], key='handbuilder_slate_selectbox') if site_var == 'Draftkings': if slate_var3 == 'Main': handbuild_roo = handbuild_roo[handbuild_roo['slate'] == 'Main Slate'] elif slate_var3 == 'Secondary': handbuild_roo = handbuild_roo[handbuild_roo['slate'] == 'Secondary Slate'] elif slate_var3 == 'Auxiliary': handbuild_roo = handbuild_roo[handbuild_roo['slate'] == 'Late Slate'] elif site_var == 'Fanduel': if slate_var3 == 'Main': handbuild_roo = handbuild_roo[handbuild_roo['slate'] == 'Main Slate'] elif slate_var3 == 'Secondary': handbuild_roo = handbuild_roo[handbuild_roo['slate'] == 'Secondary Slate'] elif slate_var3 == 'Auxiliary': handbuild_roo = handbuild_roo[handbuild_roo['slate'] == 'Late Slate'] # --- POSITION LIMITS --- if site_var == 'Draftkings': position_limits = { 'QB': 1, 'RB': 2, 'WR': 3, 'TE': 1, 'UTIL': 1, 'DST': 1, # Add more as needed } max_salary = 50000 max_players = 9 else: position_limits = { 'QB': 1, 'RB': 2, 'WR': 3, 'TE': 1, 'UTIL': 1, 'DST': 1, # Add more as needed } max_salary = 60000 max_players = 9 # --- LINEUP STATE --- if 'handbuilder_lineup' not in st.session_state: st.session_state['handbuilder_lineup'] = pd.DataFrame(columns=['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own']) # Count positions in the current lineup lineup = st.session_state['handbuilder_lineup'] slot_counts = lineup['Slot'].value_counts() if not lineup.empty else {} # --- PLAYER FILTERS --- with st.expander("Player Filters"): handbuilder_player_filters_column, handbuilder_player_filters_salary_column = st.columns(2) with handbuilder_player_filters_column: pos_select3 = st.multiselect("Select your position(s)", options=['QB', 'RB', 'WR', 'TE', 'UTIL', 'DST'], key='pos_select3_multiselect') with handbuilder_player_filters_salary_column: salary_var = st.number_input("Salary Max", min_value = 0, max_value = 25000, value = 25000, step = 100, key='handbuilder_salary_max_input') # --- TEAM FILTER UI --- with st.expander("Team Filters"): all_teams = sorted(handbuild_roo['Team'].unique()) st.markdown("**Toggle teams to include:**") team_cols = st.columns(len(all_teams) // 2 + 1) selected_teams = [] for idx, team in enumerate(all_teams): col = team_cols[idx % len(team_cols)] if f"handbuilder_team_{team}" not in st.session_state: st.session_state[f"handbuilder_team_{team}"] = False checked = col.toggle(team, value=st.session_state[f"handbuilder_team_{team}"], key=f"handbuilder_team_{team}_toggle") if checked: selected_teams.append(team) # If no teams selected, show all teams if selected_teams: st.session_state['player_select_df'] = handbuild_roo[ handbuild_roo['Team'].isin(selected_teams) ][['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own']].drop_duplicates(subset=['Player', 'Team']).copy() else: st.session_state['player_select_df'] = handbuild_roo[['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own']].drop_duplicates(subset=['Player', 'Team']).copy() # If no teams selected, show all teams if pos_select3: position_mask_2 = handbuild_roo['Position'].apply(lambda x: any(pos in x for pos in pos_select3)) st.session_state['player_select_df'] = st.session_state['player_select_df'][position_mask_2][['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own']].drop_duplicates(subset=['Player', 'Team']).copy() else: st.session_state['player_select_df'] = st.session_state['player_select_df'][['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own']].drop_duplicates(subset=['Player', 'Team']).copy() st.session_state['player_select_df'] = st.session_state['player_select_df'][st.session_state['player_select_df']['Salary'] <= salary_var] with st.expander("Quick Fill Options"): auto_team_var = st.selectbox("Auto Fill Team", options=all_teams, key='auto_team_selectbox') auto_size_var = st.selectbox("Auto Fill Size", options=[3, 4, 5], key='auto_size_selectbox') auto_range_var = st.selectbox("Auto Fill Options", options=['QB/WR', 'RB/WR/TE', 'QB/WR/TE/RB'], key='auto_range_selectbox') # --- QUICK FILL LOGIC --- if st.button("Quick Fill", key="quick_fill_button"): # 1. Get all eligible players from the selected team, not already in the lineup current_players = set(st.session_state['handbuilder_lineup']['Player']) team_players = st.session_state['player_select_df'][ (st.session_state['player_select_df']['Team'] == auto_team_var) & (~st.session_state['player_select_df']['Player'].isin(current_players)) ].copy() # 2. Sort by Order team_players = team_players.sort_values(by='Median', ascending=False) # 3. Select the order range if auto_range_var == 'QB/WR': selected_players = team_players[team_players['Position'] == 'QB'].head(1) selected_players = pd.concat([selected_players, team_players[team_players['Position'] == 'WR'].head(auto_size_var - 1)]) if len(selected_players) < auto_size_var: team_players = st.session_state['player_select_df'][ (st.session_state['player_select_df']['Team'] == auto_team_var) & (~st.session_state['player_select_df']['Player'].isin(current_players)) ].copy() # 2. Sort by Order team_players = team_players.sort_values(by='Median', ascending=False) selected_players = team_players.head(auto_size_var + 1) elif auto_range_var == 'QB/WR/TE': selected_players = team_players[team_players['Position'] == 'QB'].head(1) selected_players = pd.concat([selected_players, team_players[team_players['Position'].isin(['WR', 'TE'])].head(auto_size_var - 1)]) if len(selected_players) < auto_size_var: team_players = st.session_state['player_select_df'][ (st.session_state['player_select_df']['Team'] == auto_team_var) & (~st.session_state['player_select_df']['Player'].isin(current_players)) ].copy() # 2. Sort by Order team_players = team_players.sort_values(by='Median', ascending=False) selected_players = team_players.head(auto_size_var + 1) elif auto_range_var == 'QB/WR/TE/RB': selected_players = team_players[team_players['Position'] == 'QB'].head(1) selected_players = pd.concat([selected_players, team_players[team_players['Position'].isin(['RB', 'WR', 'TE'])].head(auto_size_var - 1)]) if len(selected_players) < auto_size_var: team_players = st.session_state['player_select_df'][ (st.session_state['player_select_df']['Team'] == auto_team_var) & (~st.session_state['player_select_df']['Player'].isin(current_players)) ].copy() # 2. Sort by Order team_players = team_players.sort_values(by='Median', ascending=False) selected_players = team_players.head(auto_size_var + 1) else: selected_players = team_players.head(auto_size_var) # 4. Add each player to the lineup, filling the first available eligible slot for _, player_row in selected_players.iterrows(): eligible_positions = re.split(r'[/, ]+', player_row['Position']) slot_to_fill = None for slot in ['QB', 'RB', 'WR', 'TE', 'UTIL', 'DST']: if slot_counts.get(slot, 0) < position_limits.get(slot, 0): if slot == 'UTIL': if 'DST' not in eligible_positions and 'QB' not in eligible_positions: slot_to_fill = slot break elif slot in eligible_positions: slot_to_fill = slot break if slot_to_fill is not None: # Avoid duplicates if player_row['Player'] not in st.session_state['handbuilder_lineup']['Player'].values: add_row = player_row.copy() add_row['Slot'] = slot_to_fill st.session_state['handbuilder_lineup'] = pd.concat( [st.session_state['handbuilder_lineup'], pd.DataFrame([add_row[[ 'Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own', 'Slot' ]]])], ignore_index=True ) # Update slot_counts for next player slot_counts[slot_to_fill] = slot_counts.get(slot_to_fill, 0) + 1 # --- FILTER OUT PLAYERS WHOSE ALL ELIGIBLE POSITIONS ARE FILLED --- def is_player_eligible(row): eligible_positions = re.split(r'[/, ]+', row['Position']) # Player is eligible if at least one of their positions is not at max for pos in eligible_positions: if slot_counts.get(pos, 0) < position_limits.get(pos, 0): return True return False # st.session_state['player_select_df'] = st.session_state['player_select_df'][st.session_state['player_select_df'].apply(is_player_eligible, axis=1)] print(st.session_state['player_select_df'].head(10)) handbuilder_lineup_build_column, handbuilder_player_select_column = st.columns([1, 2]) with handbuilder_player_select_column: st.subheader("Player Select") # Display player selection dataframe with single row selection event = st.dataframe( st.session_state['player_select_df'].style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').background_gradient(cmap='RdYlGn_r', subset=['Salary', 'Own']).format(precision=2), on_select="rerun", selection_mode="single-row", key="player_select_dataframe", height=500, hide_index=True ) # Add Player button if st.button("Add Selected Player to Lineup", key="add_player_button"): if event and "rows" in event.selection and len(event.selection["rows"]) > 0: idx = event.selection["rows"][0] player_row = st.session_state['player_select_df'].iloc[[idx]] eligible_positions = re.split(r'[/, ]+', player_row['Position'].iloc[0]) # Find the first eligible slot that is not full slot_to_fill = None for slot in ['QB', 'RB', 'WR', 'TE', 'UTIL', 'DST']: if slot_counts.get(slot, 0) < position_limits.get(slot, 0): if slot == 'UTIL': if 'DST' not in eligible_positions and 'QB' not in eligible_positions: slot_to_fill = slot break elif slot in eligible_positions: slot_to_fill = slot break if slot_to_fill is not None: # Avoid duplicates if not player_row['Player'].iloc[0] in st.session_state['handbuilder_lineup']['Player'].values: # Add the slot info player_row = player_row.assign(Slot=slot_to_fill) st.session_state['handbuilder_lineup'] = pd.concat( [st.session_state['handbuilder_lineup'], player_row[['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own', 'Slot']]], ignore_index=True ) st.success(f"Added {player_row['Player'].iloc[0]} to {slot_to_fill} slot") else: st.warning(f"{player_row['Player'].iloc[0]} is already in the lineup") else: st.error("No available slots for this player") else: st.warning("Please select a player first") with handbuilder_lineup_build_column: st.subheader("Lineup Build") # --- EXPLICIT LINEUP ORDER --- if site_var == 'Draftkings': lineup_slots = ['QB', 'RB', 'RB', 'WR', 'WR', 'WR', 'TE', 'UTIL', 'DST'] else: lineup_slots = ['QB', 'RB', 'RB', 'WR', 'WR', 'WR', 'TE', 'UTIL', 'DST'] display_columns = ['Slot', 'Player', 'Position', 'Team', 'Salary', 'Median', 'Own'] filled_lineup = st.session_state['handbuilder_lineup'] display_rows = [] used_indices = set() if not filled_lineup.empty: for slot in lineup_slots: match = filled_lineup[(filled_lineup['Slot'] == slot) & (~filled_lineup.index.isin(used_indices))] if not match.empty: row = match.iloc[0] used_indices.add(match.index[0]) display_rows.append({ 'Slot': slot, 'Player': row['Player'], 'Position': row['Position'], 'Team': row['Team'], 'Salary': row['Salary'], 'Median': row['Median'], '2x%': row['2x%'], 'Own': row['Own'] }) else: display_rows.append({ 'Slot': slot, 'Player': '', 'Position': '', 'Team': '', 'Salary': np.nan, 'Median': np.nan, '2x%': np.nan, 'Own': np.nan }) st.session_state['lineup_display_df'] = pd.DataFrame(display_rows, columns=display_columns) # Show the lineup table as a static display st.dataframe( st.session_state['lineup_display_df'].style.background_gradient(axis=0).background_gradient(cmap='RdYlGn', subset=['Median']).background_gradient(cmap='RdYlGn_r', subset=['Salary', 'Own']).format(precision=2), height=445, hide_index=True ) # --- SUMMARY ROW --- if not filled_lineup.empty: total_salary = filled_lineup['Salary'].sum() total_median = filled_lineup['Median'].sum() avg_2x = filled_lineup['2x%'].mean() total_own = filled_lineup['Own'].sum() most_common_team = filled_lineup['Team'].mode()[0] if not filled_lineup['Team'].mode().empty else "" summary_row = pd.DataFrame({ 'Slot': [''], 'Player': ['TOTAL'], 'Position': [''], 'Team': [most_common_team], 'Salary': [total_salary], 'Median': [total_median], '2x%': [avg_2x], 'Own': [total_own] }) summary_row = summary_row[['Salary', 'Median', 'Own']].head(max_players) handbuilder_lineup_build_salary_column, handbuilder_lineup_build_median_column = st.columns([2, 3]) with handbuilder_lineup_build_salary_column: if (max_players - len(filled_lineup)) > 0: st.markdown(f"""