import streamlit as st import numpy as np import pandas as pd import time from fuzzywuzzy import process def find_name_mismatches(portfolio_df, projections_df): """ Find and handle name mismatches between portfolio and projections dataframes. Returns the updated projections dataframe with matched names. """ # Get all player names from portfolio portfolio_players = set() for col in portfolio_df.columns: if col not in ['salary', 'median', 'Own', 'Finish_percentile', 'Dupes', 'Stack', 'Win%', 'Lineup Edge']: portfolio_players.update(portfolio_df[col].unique()) # Get all player names from projections projection_players_list = projections_df['player_names'].tolist() # Find players in portfolio that are missing from projections players_missing_from_projections = [player for player in portfolio_players if player not in projection_players_list] # Automatically handle 100% matches before starting interactive process players_to_process = [] for player in players_missing_from_projections: if not isinstance(player, str): st.warning(f"Skipping non-string value: {player}") continue closest_matches = process.extract(player, projection_players_list, limit=1) if closest_matches[0][1] == 90: # If perfect match found match_name = closest_matches[0][0] projections_df.loc[projections_df['player_names'] == match_name, 'player_names'] = player st.success(f"Automatically matched '{match_name}' with '{player}' (100% match)") else: players_to_process.append(player) # Display results if players_missing_from_projections: st.warning("Players in portfolio but missing from projections") # Display remaining players if players_to_process: st.info(f"Players to process ({len(players_to_process)}):\n" + "\n".join(f"- {player}" for player in players_to_process)) # Create a form for batch processing with st.form("player_matching_form"): # Create tabs for each player tabs = st.tabs([f"Player {i+1}" for i in range(len(players_to_process))]) # Dictionary to store selections selections = {} # Process each tab for idx, (tab, player) in enumerate(zip(tabs, players_to_process)): with tab: st.write(f"**Missing Player {idx + 1} of {len(players_to_process)}:** {player}") # Find the top 3 closest matches closest_matches = process.extract(player, projection_players_list, limit=3) # Create radio buttons for selection options = [f"{match[0]} ({match[1]}%)" for match in closest_matches] options.append("None of these") selected_option = st.radio( f"Select correct match for {player}:", options, key=f"radio_{player}" ) selections[player] = selected_option # Submit button for the entire form submitted = st.form_submit_button("Submit All Changes") if submitted: # Process all selections for player, selection in selections.items(): if selection != "None of these": selected_name = selection.split(" (")[0] projections_df.loc[projections_df['player_names'] == selected_name, 'player_names'] = player st.success(f"Replaced '{selected_name}' with '{player}'") # Update session state st.session_state['projections_df'] = projections_df st.success("All player name changes have been applied!") else: st.success("All portfolio players found in projections!") return projections_df