James McCool
Refactor player filtering and calculation logic in app.py for improved functionality
2c57866
| import streamlit as st | |
| st.set_page_config(layout="wide") | |
| import numpy as np | |
| import pandas as pd | |
| import time | |
| from fuzzywuzzy import process | |
| import random | |
| ## import global functions | |
| from global_func.clean_player_name import clean_player_name | |
| from global_func.load_contest_file import load_contest_file | |
| from global_func.load_file import load_file | |
| from global_func.load_ss_file import load_ss_file | |
| from global_func.find_name_mismatches import find_name_mismatches | |
| from global_func.predict_dupes import predict_dupes | |
| from global_func.highlight_rows import highlight_changes, highlight_changes_winners, highlight_changes_losers | |
| from global_func.load_csv import load_csv | |
| from global_func.find_csv_mismatches import find_csv_mismatches | |
| tab1, tab2 = st.tabs(["Data Load", "Contest Analysis"]) | |
| with tab1: | |
| if st.button('Clear data', key='reset1'): | |
| st.session_state.clear() | |
| # Add file uploaders to your app | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.subheader("Contest File") | |
| st.info("Go ahead and upload a Contest file here. Only include player columns and an optional 'Stack' column if you are playing MLB.") | |
| Contest_file = st.file_uploader("Upload Contest File (CSV or Excel)", type=['csv', 'xlsx', 'xls']) | |
| if 'Contest' in st.session_state: | |
| del st.session_state['Contest'] | |
| if Contest_file: | |
| st.session_state['Contest'], st.session_state['ownership_dict'], st.session_state['entry_list'] = load_contest_file(Contest_file) | |
| st.session_state['Contest'] = st.session_state['Contest'].dropna(how='all') | |
| st.session_state['Contest'] = st.session_state['Contest'].reset_index(drop=True) | |
| if st.session_state['Contest'] is not None: | |
| st.success('Contest file loaded successfully!') | |
| st.dataframe(st.session_state['Contest'].head(10)) | |
| with col2: | |
| st.subheader("Projections File") | |
| st.info("upload a projections file that has 'player_names', 'salary', 'median', 'ownership', and 'captain ownership' (Needed for Showdown) columns. Note that the salary for showdown needs to be the FLEX salary, not the captain salary.") | |
| # Create two columns for the uploader and template button | |
| upload_col, template_col = st.columns([3, 1]) | |
| with upload_col: | |
| projections_file = st.file_uploader("Upload Projections File (CSV or Excel)", type=['csv', 'xlsx', 'xls']) | |
| if 'projections_df' in st.session_state: | |
| del st.session_state['projections_df'] | |
| with template_col: | |
| # Create empty DataFrame with required columns | |
| template_df = pd.DataFrame(columns=['player_names', 'position', 'team', 'salary', 'median', 'ownership', 'captain ownership']) | |
| # Add download button for template | |
| st.download_button( | |
| label="Template", | |
| data=template_df.to_csv(index=False), | |
| file_name="projections_template.csv", | |
| mime="text/csv" | |
| ) | |
| if projections_file: | |
| export_projections, projections = load_file(projections_file) | |
| if projections is not None: | |
| st.success('Projections file loaded successfully!') | |
| st.dataframe(projections.head(10)) | |
| if Contest_file and projections_file: | |
| if st.session_state['Contest'] is not None and projections is not None: | |
| st.subheader("Name Matching functions") | |
| # Initialize projections_df in session state if it doesn't exist | |
| if 'projections_df' not in st.session_state: | |
| st.session_state['projections_df'] = projections.copy() | |
| st.session_state['projections_df']['salary'] = (st.session_state['projections_df']['salary'].astype(str).str.replace(',', '').astype(float).astype(int)) | |
| # Run name matching only once when first loading the files | |
| st.session_state['contest_df'], st.session_state['projections_df'] = find_name_mismatches(st.session_state['Contest'], st.session_state['projections_df']) | |
| with tab2: | |
| if st.button('Clear data', key='reset3'): | |
| st.session_state.clear() | |
| if 'contest_df' in st.session_state and 'projections_df' in st.session_state: | |
| col1, col2 = st.columns([1, 8]) | |
| excluded_cols = ['BaseName', 'EntryCount'] | |
| # Create mapping dictionaries | |
| map_dict = { | |
| 'pos_map': dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['position'])), | |
| 'team_map': dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['team'])), | |
| 'salary_map': dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['salary'])), | |
| 'proj_map': dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['median'])), | |
| 'own_map': dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['ownership'])), | |
| 'own_percent_rank': dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['ownership'].rank(pct=True))), | |
| 'cpt_salary_map': dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['salary'])), | |
| 'cpt_proj_map': dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['median'] * 1.5)), | |
| 'cpt_own_map': dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['captain ownership'])) | |
| } | |
| with col1: | |
| with st.form(key='filter_form'): | |
| type_var = st.selectbox("Select Game Type", ['Classic', 'Showdown']) | |
| entry_parse_var = st.selectbox("Do you want to view a specific player(s) or a group of players?", ['All', 'Specific']) | |
| entry_names = st.multiselect("Select players", options=st.session_state['entry_list'], default=[]) | |
| submitted = st.form_submit_button("Submit") | |
| # Create a copy of the dataframe for calculations | |
| working_df = st.session_state['contest_df'].copy() | |
| # Apply filters and calculations only after form submission | |
| if submitted: | |
| # Apply entry name filter if specific entries are selected | |
| if entry_parse_var == 'Specific' and entry_names: | |
| working_df = working_df[working_df['BaseName'].isin(entry_names)] | |
| # Calculate metrics based on game type | |
| if type_var == 'Classic': | |
| working_df['salary'] = working_df.apply(lambda row: sum(map_dict['salary_map'].get(player, 0) for player in row), axis=1) | |
| working_df['median'] = working_df.apply(lambda row: sum(map_dict['proj_map'].get(player, 0) for player in row), axis=1) | |
| working_df['Own'] = working_df.apply(lambda row: sum(map_dict['own_map'].get(player, 0) for player in row), axis=1) | |
| elif type_var == 'Showdown': | |
| working_df['salary'] = working_df.apply( | |
| lambda row: map_dict['cpt_salary_map'].get(row.iloc[0], 0) + | |
| sum(map_dict['salary_map'].get(player, 0) for player in row.iloc[1:]), | |
| axis=1 | |
| ) | |
| working_df['median'] = working_df.apply( | |
| lambda row: map_dict['cpt_proj_map'].get(row.iloc[0], 0) + | |
| sum(map_dict['proj_map'].get(player, 0) for player in row.iloc[1:]), | |
| axis=1 | |
| ) | |
| working_df['Own'] = working_df.apply( | |
| lambda row: map_dict['cpt_own_map'].get(row.iloc[0], 0) + | |
| sum(map_dict['own_map'].get(player, 0) for player in row.iloc[1:]), | |
| axis=1 | |
| ) | |
| # Reset pagination when new filters are applied | |
| st.session_state.current_page = 0 | |
| # Initialize pagination in session state if not exists | |
| if 'current_page' not in st.session_state: | |
| st.session_state.current_page = 0 | |
| # Calculate total pages | |
| rows_per_page = 500 | |
| total_rows = len(working_df) | |
| total_pages = (total_rows + rows_per_page - 1) // rows_per_page | |
| # Create pagination controls in a single row | |
| pagination_cols = st.columns([4, 1, 1, 1, 4]) | |
| with pagination_cols[1]: | |
| if st.button("← Previous", disabled=st.session_state.current_page == 0): | |
| st.session_state.current_page -= 1 | |
| with pagination_cols[2]: | |
| st.markdown(f"**Page {st.session_state.current_page + 1} of {total_pages}**", unsafe_allow_html=True) | |
| with pagination_cols[3]: | |
| if st.button("Next →", disabled=st.session_state.current_page == total_pages - 1): | |
| st.session_state.current_page += 1 | |
| # Calculate start and end indices for current page | |
| start_idx = st.session_state.current_page * rows_per_page | |
| end_idx = min((st.session_state.current_page + 1) * rows_per_page, total_rows) | |
| # Display the paginated dataframe | |
| st.dataframe( | |
| working_df.iloc[start_idx:end_idx].style | |
| .background_gradient(axis=0) | |
| .background_gradient(cmap='RdYlGn') | |
| .format(precision=2), | |
| height=1000, | |
| use_container_width=True, | |
| hide_index=True | |
| ) | |