diff --git "a/src/streamlit_app.py" "b/src/streamlit_app.py" --- "a/src/streamlit_app.py" +++ "b/src/streamlit_app.py" @@ -350,230 +350,171 @@ selected_tab = st.segmented_control( ) if selected_tab == 'Handbuilder': - handbuilder_container = st.container() - handbuilder_container.empty() - with handbuilder_container: - 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" - - # 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 --- + 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" + + # 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': - 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']) - if 'handbuilder_select_key' not in st.session_state: - st.session_state['handbuilder_select_key'] = 0 - - # 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 = 20000, value = 20000, 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: - 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: - 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)) - player_select_df = player_select_df[position_mask_2][['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own']].drop_duplicates(subset=['Player', 'Team']).copy() - else: - player_select_df = player_select_df[['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own']].drop_duplicates(subset=['Player', 'Team']).copy() - - player_select_df = player_select_df[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 = player_select_df[ - (player_select_df['Team'] == auto_team_var) & - (~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 = player_select_df[ - (player_select_df['Team'] == auto_team_var) & - (~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 = player_select_df[ - (player_select_df['Team'] == auto_team_var) & - (~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 = player_select_df[ - (player_select_df['Team'] == auto_team_var) & - (~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 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']) + if 'handbuilder_select_key' not in st.session_state: + st.session_state['handbuilder_select_key'] = 0 + + # 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 = 20000, value = 20000, 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: + 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: + 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)) + player_select_df = player_select_df[position_mask_2][['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own']].drop_duplicates(subset=['Player', 'Team']).copy() + else: + player_select_df = player_select_df[['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own']].drop_duplicates(subset=['Player', 'Team']).copy() + + player_select_df = player_select_df[player_select_df['Salary'] <= salary_var] - 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 - st.rerun() + + 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 = player_select_df[ + (player_select_df['Team'] == auto_team_var) & + (~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 = player_select_df[ + (player_select_df['Team'] == auto_team_var) & + (~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 = player_select_df[ + (player_select_df['Team'] == auto_team_var) & + (~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 = player_select_df[ + (player_select_df['Team'] == auto_team_var) & + (~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) - # --- 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 - - # player_select_df = player_select_df[player_select_df.apply(is_player_eligible, axis=1)] - print(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") - event = st.dataframe( - 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=f"handbuilder_select_{st.session_state['handbuilder_select_key']}", - height=500, - hide_index=True - ) - # If a row is selected, add that player to the lineup and reset selection - if event and "rows" in event.selection and len(event.selection["rows"]) > 0: - idx = event.selection["rows"][0] - player_row = player_select_df.iloc[[idx]] - eligible_positions = re.split(r'[/, ]+', player_row['Position'].iloc[0]) - # Find the first eligible slot that is not full + # 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']: @@ -588,792 +529,839 @@ if selected_tab == 'Handbuilder': 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) + 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'], player_row[['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own', 'Slot']]], + [st.session_state['handbuilder_lineup'], pd.DataFrame([add_row[[ + 'Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own', 'Slot' + ]]])], ignore_index=True ) - st.session_state['handbuilder_select_key'] += 1 - st.rerun() + # Update slot_counts for next player + slot_counts[slot_to_fill] = slot_counts.get(slot_to_fill, 0) + 1 + st.rerun() + + # --- 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 + + # player_select_df = player_select_df[player_select_df.apply(is_player_eligible, axis=1)] + print(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") + event = st.dataframe( + 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=f"handbuilder_select_{st.session_state['handbuilder_select_key']}", + height=500, + hide_index=True + ) + # If a row is selected, add that player to the lineup and reset selection + if event and "rows" in event.selection and len(event.selection["rows"]) > 0: + idx = event.selection["rows"][0] + player_row = 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.session_state['handbuilder_select_key'] += 1 + st.rerun() - with handbuilder_lineup_build_column: - st.subheader("Lineup Build") + 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 - }) - - lineup_display_df = pd.DataFrame(display_rows, columns=display_columns) - - # Show the lineup table with single-row selection for removal - event_remove = st.dataframe( - 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), - on_select="rerun", - selection_mode=["single-row"], - key="lineup_remove_dataframe", - height=445, - hide_index=True - ) + # --- 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 + }) - # If a row is selected and not blank, remove that player from the lineup - if event_remove and "rows" in event_remove.selection and len(event_remove.selection["rows"]) > 0: - idx = event_remove.selection["rows"][0] - player_to_remove = lineup_display_df.iloc[idx]['Player'] - slot_to_remove = lineup_display_df.iloc[idx]['Slot'] - if player_to_remove: # Only remove if not blank - st.session_state['handbuilder_lineup'] = filled_lineup[ - ~((filled_lineup['Player'] == player_to_remove) & (filled_lineup['Slot'] == slot_to_remove)) - ] - st.rerun() - - # --- 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""" -