James McCool commited on
Commit
4314382
·
1 Parent(s): fac0021

Add opponent mapping and conflict checks in exposure spread and lineup optimization functions; refactor data loading for MMA projections

Browse files
app.py CHANGED
@@ -42,6 +42,7 @@ pos_parse_mapping = {
42
 
43
  pos_parse_options = list(pos_parse_mapping.keys())
44
 
 
45
  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']
46
  dk_db_nfl_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',
47
  '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',
@@ -74,6 +75,7 @@ fd_db_pga_showdown_selections = ['FD_PGA_SD_seed_frame_Showdown #1', 'FD_PGA_SD_
74
  'FD_PGA_SD_seed_frame_Showdown #7', 'FD_PGA_SD_seed_frame_Showdown #8', 'FD_PGA_SD_seed_frame_Showdown #9', 'FD_PGA_SD_seed_frame_Showdown #10', 'FD_PGA_SD_seed_frame_Showdown #11', 'FD_PGA_SD_seed_frame_Showdown #12', 'FD_PGA_SD_seed_frame_Showdown #13',
75
  'FD_PGA_SD_seed_frame_Showdown #14', 'FD_PGA_SD_seed_frame_Showdown #15']
76
 
 
77
  dk_nfl_showdown_db_translation = dict(zip(showdown_selections, dk_db_nfl_showdown_selections))
78
  fd_nfl_showdown_db_translation = dict(zip(showdown_selections, fd_db_nfl_showdown_selections))
79
  dk_nba_showdown_db_translation = dict(zip(showdown_selections, dk_db_nba_showdown_selections))
@@ -1611,7 +1613,7 @@ if selected_tab == 'Data Load':
1611
  proj_options = st.selectbox("Select a projections source", options=['Paydirt DB', 'User Upload'])
1612
 
1613
  upload_col, template_col = st.columns([3, 1])
1614
-
1615
  with upload_col:
1616
  if 'portfolio' not in st.session_state:
1617
  st.session_state['projections_loaded'] = False
@@ -1620,6 +1622,7 @@ if selected_tab == 'Data Load':
1620
  st.session_state['db_projections_file'] = projections_file
1621
  st.session_state['projections_loaded'] = True
1622
  elif proj_options == 'Paydirt DB':
 
1623
  if st.button("Load from Database"):
1624
  if sport_var == 'NBA':
1625
  if site_var == 'Draftkings':
@@ -1658,11 +1661,13 @@ if selected_tab == 'Data Load':
1658
  if site_var == 'Draftkings':
1659
  if type_var == 'Classic':
1660
  projections_file = init_mma_baselines(type_var, site_var, slate_var3)[0]
 
1661
  elif type_var == 'Showdown':
1662
  projections_file = init_mma_baselines(type_var, site_var, slate_var3)[2]
1663
  elif site_var == 'Fanduel':
1664
  if type_var == 'Classic':
1665
  projections_file = init_mma_baselines(type_var, site_var, slate_var3)[1]
 
1666
  elif type_var == 'Showdown':
1667
  projections_file = init_mma_baselines(type_var, site_var, slate_var3)[3]
1668
  elif sport_var == 'GOLF':
@@ -1795,6 +1800,11 @@ if selected_tab == 'Data Load':
1795
  type_var,
1796
  sport_var
1797
  )
 
 
 
 
 
1798
 
1799
  st.session_state['portfolio'] = st.session_state['portfolio'].astype(str)
1800
  st.session_state['portfolio'] = st.session_state['portfolio'][~st.session_state['portfolio'].isin(['', 'nan', 'None', 'NaN']).any(axis=1)].reset_index(drop=True)
@@ -2879,7 +2889,8 @@ if selected_tab == 'Manage Portfolio':
2879
  sport_var,
2880
  type_var,
2881
  salary_max,
2882
- stacking_sports
 
2883
  )
2884
 
2885
  # Update the original dataframe with the modified rows
@@ -2939,7 +2950,8 @@ if selected_tab == 'Manage Portfolio':
2939
  sport_var,
2940
  type_var,
2941
  salary_max,
2942
- stacking_sports
 
2943
  )
2944
 
2945
  parsed_frame.loc[containing_mask] = modified_rows.values
@@ -2979,7 +2991,24 @@ if selected_tab == 'Manage Portfolio':
2979
  if reg_submitted:
2980
  st.session_state['settings_base'] = False
2981
  working_frame_prepared = prepare_dataframe_for_exposure_spread(st.session_state['working_frame'], st.session_state['player_columns'])
2982
- parsed_frame = exposure_spread(working_frame_prepared, st.session_state['exposure_player'], exposure_target, comp_salary_below, comp_salary_above, ignore_stacks, remove_teams_exposure, specific_replacements, specific_exclusions, specific_columns, st.session_state['portfolio_inc_proj'], sport_var, type_var, salary_max, stacking_sports)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2983
 
2984
  # Use consolidated calculation function
2985
  parsed_frame = calculate_lineup_metrics(
@@ -3014,7 +3043,24 @@ if selected_tab == 'Manage Portfolio':
3014
  elif exp_submitted:
3015
  st.session_state['settings_base'] = False
3016
  export_base_prepared = prepare_dataframe_for_exposure_spread(st.session_state['export_base'], st.session_state['player_columns'])
3017
- parsed_frame = exposure_spread(export_base_prepared, st.session_state['exposure_player'], exposure_target, comp_salary_below, comp_salary_above, ignore_stacks, remove_teams_exposure, specific_replacements, specific_exclusions, specific_columns, st.session_state['portfolio_inc_proj'], sport_var, type_var, salary_max, stacking_sports)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3018
 
3019
  # Use consolidated calculation function for export
3020
  parsed_frame = calculate_lineup_metrics(
@@ -3231,9 +3277,13 @@ if selected_tab == 'Manage Portfolio':
3231
  st.session_state['export_file'][col] = st.session_state['export_file'][col].map(position_dict)
3232
 
3233
  if 'export_file' in st.session_state:
3234
- download_port, merge_port, clear_export, add_rows_col, remove_rows_col, blank_export_col = st.columns([1, 1, 1, 2, 2, 6])
3235
  with download_port:
3236
- st.download_button(label="Download Portfolio", data=st.session_state['export_file'].to_csv(index=False), file_name="portfolio.csv", mime="text/csv")
 
 
 
 
3237
 
3238
  with merge_port:
3239
  if st.button("Add all to Custom Export"):
@@ -3266,13 +3316,11 @@ if selected_tab == 'Manage Portfolio':
3266
 
3267
  total_rows = len(st.session_state['display_frame'])
3268
  rows_per_page = 500
3269
- total_pages = (total_rows + rows_per_page - 1) // rows_per_page # Ceiling division
3270
 
3271
- # Initialize page number in session state if not exists
3272
  if 'current_page' not in st.session_state:
3273
  st.session_state.current_page = 1
3274
 
3275
- # Display current page range info and pagination control in a single line
3276
  st.write(
3277
  f"Showing rows {(st.session_state.current_page - 1) * rows_per_page + 1} "
3278
  f"to {min(st.session_state.current_page * rows_per_page, total_rows)} of {total_rows}"
 
42
 
43
  pos_parse_options = list(pos_parse_mapping.keys())
44
 
45
+ ## Should just do a loop with some f'strings here I think
46
  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']
47
  dk_db_nfl_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',
48
  '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',
 
75
  'FD_PGA_SD_seed_frame_Showdown #7', 'FD_PGA_SD_seed_frame_Showdown #8', 'FD_PGA_SD_seed_frame_Showdown #9', 'FD_PGA_SD_seed_frame_Showdown #10', 'FD_PGA_SD_seed_frame_Showdown #11', 'FD_PGA_SD_seed_frame_Showdown #12', 'FD_PGA_SD_seed_frame_Showdown #13',
76
  'FD_PGA_SD_seed_frame_Showdown #14', 'FD_PGA_SD_seed_frame_Showdown #15']
77
 
78
+ ## This feels like a bad way to do this?
79
  dk_nfl_showdown_db_translation = dict(zip(showdown_selections, dk_db_nfl_showdown_selections))
80
  fd_nfl_showdown_db_translation = dict(zip(showdown_selections, fd_db_nfl_showdown_selections))
81
  dk_nba_showdown_db_translation = dict(zip(showdown_selections, dk_db_nba_showdown_selections))
 
1613
  proj_options = st.selectbox("Select a projections source", options=['Paydirt DB', 'User Upload'])
1614
 
1615
  upload_col, template_col = st.columns([3, 1])
1616
+
1617
  with upload_col:
1618
  if 'portfolio' not in st.session_state:
1619
  st.session_state['projections_loaded'] = False
 
1622
  st.session_state['db_projections_file'] = projections_file
1623
  st.session_state['projections_loaded'] = True
1624
  elif proj_options == 'Paydirt DB':
1625
+ ## Should make it so this is dynamic instead of having hardcoded things here
1626
  if st.button("Load from Database"):
1627
  if sport_var == 'NBA':
1628
  if site_var == 'Draftkings':
 
1661
  if site_var == 'Draftkings':
1662
  if type_var == 'Classic':
1663
  projections_file = init_mma_baselines(type_var, site_var, slate_var3)[0]
1664
+ opp_map = init_mma_baselines(type_var, site_var, slate_var3)[8]
1665
  elif type_var == 'Showdown':
1666
  projections_file = init_mma_baselines(type_var, site_var, slate_var3)[2]
1667
  elif site_var == 'Fanduel':
1668
  if type_var == 'Classic':
1669
  projections_file = init_mma_baselines(type_var, site_var, slate_var3)[1]
1670
+ opp_map = init_mma_baselines(type_var, site_var, slate_var3)[8]
1671
  elif type_var == 'Showdown':
1672
  projections_file = init_mma_baselines(type_var, site_var, slate_var3)[3]
1673
  elif sport_var == 'GOLF':
 
1800
  type_var,
1801
  sport_var
1802
  )
1803
+
1804
+ if sport_var == 'MMA':
1805
+ st.session_state['map_dict']['opp_map'] = opp_map
1806
+ else:
1807
+ st.session_state['map_dict']['opp_map'] = None
1808
 
1809
  st.session_state['portfolio'] = st.session_state['portfolio'].astype(str)
1810
  st.session_state['portfolio'] = st.session_state['portfolio'][~st.session_state['portfolio'].isin(['', 'nan', 'None', 'NaN']).any(axis=1)].reset_index(drop=True)
 
2889
  sport_var,
2890
  type_var,
2891
  salary_max,
2892
+ stacking_sports,
2893
+ st.session_state['map_dict']['opp_map']
2894
  )
2895
 
2896
  # Update the original dataframe with the modified rows
 
2950
  sport_var,
2951
  type_var,
2952
  salary_max,
2953
+ stacking_sports,
2954
+ st.session_state['map_dict']['opp_map']
2955
  )
2956
 
2957
  parsed_frame.loc[containing_mask] = modified_rows.values
 
2991
  if reg_submitted:
2992
  st.session_state['settings_base'] = False
2993
  working_frame_prepared = prepare_dataframe_for_exposure_spread(st.session_state['working_frame'], st.session_state['player_columns'])
2994
+ parsed_frame = exposure_spread(
2995
+ working_frame_prepared,
2996
+ st.session_state['exposure_player'],
2997
+ exposure_target,
2998
+ comp_salary_below,
2999
+ comp_salary_above,
3000
+ ignore_stacks,
3001
+ remove_teams_exposure,
3002
+ specific_replacements,
3003
+ specific_exclusions,
3004
+ specific_columns,
3005
+ st.session_state['portfolio_inc_proj'],
3006
+ sport_var,
3007
+ type_var,
3008
+ salary_max,
3009
+ stacking_sports,
3010
+ st.session_state['map_dict']['opp_map']
3011
+ )
3012
 
3013
  # Use consolidated calculation function
3014
  parsed_frame = calculate_lineup_metrics(
 
3043
  elif exp_submitted:
3044
  st.session_state['settings_base'] = False
3045
  export_base_prepared = prepare_dataframe_for_exposure_spread(st.session_state['export_base'], st.session_state['player_columns'])
3046
+ parsed_frame = exposure_spread(
3047
+ export_base_prepared,
3048
+ st.session_state['exposure_player'],
3049
+ exposure_target,
3050
+ comp_salary_below,
3051
+ comp_salary_above,
3052
+ ignore_stacks,
3053
+ remove_teams_exposure,
3054
+ specific_replacements,
3055
+ specific_exclusions,
3056
+ specific_columns,
3057
+ st.session_state['portfolio_inc_proj'],
3058
+ sport_var,
3059
+ type_var,
3060
+ salary_max,
3061
+ stacking_sports,
3062
+ st.session_state['map_dict']['opp_map']
3063
+ )
3064
 
3065
  # Use consolidated calculation function for export
3066
  parsed_frame = calculate_lineup_metrics(
 
3277
  st.session_state['export_file'][col] = st.session_state['export_file'][col].map(position_dict)
3278
 
3279
  if 'export_file' in st.session_state:
3280
+ download_port, merge_port, clear_export, add_rows_col, remove_rows_col, blank_export_col = st.columns([1, 1, 1, 2, 2, 4])
3281
  with download_port:
3282
+ st.selectbox("Download as:", options=['Names+IDs', 'Names Only'], key='download_format')
3283
+ if st.session_state['download_format'] == 'Names+IDs':
3284
+ st.download_button(label="Download Portfolio", data=st.session_state['export_file'].to_csv(index=False), file_name="portfolio.csv", mime="text/csv")
3285
+ else:
3286
+ st.download_button(label="Download Portfolio", data=st.session_state['display_frame'].to_csv(index=False), file_name="portfolio.csv", mime="text/csv")
3287
 
3288
  with merge_port:
3289
  if st.button("Add all to Custom Export"):
 
3316
 
3317
  total_rows = len(st.session_state['display_frame'])
3318
  rows_per_page = 500
3319
+ total_pages = (total_rows + rows_per_page - 1) // rows_per_page
3320
 
 
3321
  if 'current_page' not in st.session_state:
3322
  st.session_state.current_page = 1
3323
 
 
3324
  st.write(
3325
  f"Showing rows {(st.session_state.current_page - 1) * rows_per_page + 1} "
3326
  f"to {min(st.session_state.current_page * rows_per_page, total_rows)} of {total_rows}"
database_queries.py CHANGED
@@ -466,7 +466,6 @@ def init_nba_baselines(type_var: str, site_var: str, slate_var: str):
466
  fd_sd_id_map = dict(zip(fd_sd_roo_raw['Player'], fd_sd_roo_raw['player_ID']))
467
  fd_sd_roo_raw['player_ID'] = fd_sd_roo_raw['player_ID'].astype(str)
468
  fd_sd_roo_raw['player_ID'] = fd_sd_roo_raw['player_ID'].str.rsplit('-', n=1).str[0].astype(str)
469
- dk_sd_roo_raw = dk_sd_roo_raw[dk_sd_roo_raw['slate'] == slate_var]
470
 
471
  dk_sd_roo_raw = dk_sd_roo_raw.drop(columns=['player_ID', 'slate', 'version', 'timestamp', 'site'])
472
  fd_sd_roo_raw = fd_sd_roo_raw.drop(columns=['player_ID', 'slate', 'version', 'timestamp', 'site'])
@@ -501,6 +500,15 @@ def init_nba_baselines(type_var: str, site_var: str, slate_var: str):
501
  dk_roo_raw = dk_roo_raw.rename(columns={'Player': 'player_names', 'Position': 'position', 'Team': 'team', 'Salary': 'salary', 'Median': 'median', 'Own': 'ownership', 'CPT_Own': 'captain ownership'})
502
  fd_roo_raw = fd_roo_raw.rename(columns={'Player': 'player_names', 'Position': 'position', 'Team': 'team', 'Salary': 'salary', 'Median': 'median', 'Own': 'ownership', 'CPT_Own': 'captain ownership'})
503
 
 
 
 
 
 
 
 
 
 
504
  dk_sd_roo_raw = None
505
  fd_sd_roo_raw = None
506
  dk_sd_id_map = None
@@ -1267,6 +1275,7 @@ def init_mma_baselines(type_var: str, site_var: str, slate_var: str):
1267
 
1268
  dk_roo_raw = None
1269
  fd_roo_raw = None
 
1270
  dk_id_map = None
1271
  fd_id_map = None
1272
 
@@ -1275,7 +1284,7 @@ def init_mma_baselines(type_var: str, site_var: str, slate_var: str):
1275
  cursor = collection.find()
1276
 
1277
  raw_display = pd.DataFrame(list(cursor))
1278
- raw_display = raw_display[['Player', 'Position', 'Salary', 'Median', 'Own', 'CPT_Own', 'player_id']]
1279
  raw_display['Team'] = 'MMA'
1280
  raw_display['Site'] = site_var
1281
  raw_display = raw_display.rename(columns={"player_id": "player_ID"})
@@ -1285,10 +1294,11 @@ def init_mma_baselines(type_var: str, site_var: str, slate_var: str):
1285
  fd_roo_raw = raw_display[raw_display['Site'] == 'Draftkings']
1286
  dk_id_map = dict(zip(dk_roo_raw['Player'], dk_roo_raw['player_ID']))
1287
  fd_id_map = dict(zip(fd_roo_raw['Player'], fd_roo_raw['player_ID']))
 
1288
  raw_display = raw_display.apply(pd.to_numeric, errors='ignore')
1289
 
1290
- dk_roo_raw = dk_roo_raw.drop(columns=['player_ID'])
1291
- fd_roo_raw = fd_roo_raw.drop(columns=['player_ID'])
1292
 
1293
  dk_roo_raw = dk_roo_raw.rename(columns={'Player': 'player_names', 'Position': 'position', 'Team': 'team', 'Salary': 'salary', 'Median': 'median', 'Own': 'ownership', 'CPT_Own': 'captain ownership'})
1294
  fd_roo_raw = fd_roo_raw.rename(columns={'Player': 'player_names', 'Position': 'position', 'Team': 'team', 'Salary': 'salary', 'Median': 'median', 'Own': 'ownership', 'CPT_Own': 'captain ownership'})
@@ -1298,7 +1308,7 @@ def init_mma_baselines(type_var: str, site_var: str, slate_var: str):
1298
  dk_sd_id_map = None
1299
  fd_sd_id_map = None
1300
 
1301
- return 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
1302
 
1303
  def init_DK_MMA_lineups(type_var, slate_var, prio_var, prio_mix, lineup_num, salary_min, salary_max, player_var2):
1304
 
 
466
  fd_sd_id_map = dict(zip(fd_sd_roo_raw['Player'], fd_sd_roo_raw['player_ID']))
467
  fd_sd_roo_raw['player_ID'] = fd_sd_roo_raw['player_ID'].astype(str)
468
  fd_sd_roo_raw['player_ID'] = fd_sd_roo_raw['player_ID'].str.rsplit('-', n=1).str[0].astype(str)
 
469
 
470
  dk_sd_roo_raw = dk_sd_roo_raw.drop(columns=['player_ID', 'slate', 'version', 'timestamp', 'site'])
471
  fd_sd_roo_raw = fd_sd_roo_raw.drop(columns=['player_ID', 'slate', 'version', 'timestamp', 'site'])
 
500
  dk_roo_raw = dk_roo_raw.rename(columns={'Player': 'player_names', 'Position': 'position', 'Team': 'team', 'Salary': 'salary', 'Median': 'median', 'Own': 'ownership', 'CPT_Own': 'captain ownership'})
501
  fd_roo_raw = fd_roo_raw.rename(columns={'Player': 'player_names', 'Position': 'position', 'Team': 'team', 'Salary': 'salary', 'Median': 'median', 'Own': 'ownership', 'CPT_Own': 'captain ownership'})
502
 
503
+ collection = nba_db["Live_Projections"]
504
+ cursor = collection.find()
505
+
506
+ raw_display = pd.DataFrame(list(cursor))
507
+ live_dict = dict(zip(raw_display['Player'], raw_display['Live_Proj']))
508
+
509
+ dk_roo_raw['median'] = dk_roo_raw['player_names'].map(live_dict)
510
+ fd_roo_raw['median'] = fd_roo_raw['player_names'].map(live_dict)
511
+
512
  dk_sd_roo_raw = None
513
  fd_sd_roo_raw = None
514
  dk_sd_id_map = None
 
1275
 
1276
  dk_roo_raw = None
1277
  fd_roo_raw = None
1278
+ opp_map = None
1279
  dk_id_map = None
1280
  fd_id_map = None
1281
 
 
1284
  cursor = collection.find()
1285
 
1286
  raw_display = pd.DataFrame(list(cursor))
1287
+ raw_display = raw_display[['Player', 'Opp', 'Position', 'Salary', 'Median', 'Own', 'CPT_Own', 'player_id']]
1288
  raw_display['Team'] = 'MMA'
1289
  raw_display['Site'] = site_var
1290
  raw_display = raw_display.rename(columns={"player_id": "player_ID"})
 
1294
  fd_roo_raw = raw_display[raw_display['Site'] == 'Draftkings']
1295
  dk_id_map = dict(zip(dk_roo_raw['Player'], dk_roo_raw['player_ID']))
1296
  fd_id_map = dict(zip(fd_roo_raw['Player'], fd_roo_raw['player_ID']))
1297
+ opp_map = dict(zip(dk_roo_raw['Player'], dk_roo_raw['Opp']))
1298
  raw_display = raw_display.apply(pd.to_numeric, errors='ignore')
1299
 
1300
+ dk_roo_raw = dk_roo_raw.drop(columns=['player_ID', 'Opp'])
1301
+ fd_roo_raw = fd_roo_raw.drop(columns=['player_ID', 'Opp'])
1302
 
1303
  dk_roo_raw = dk_roo_raw.rename(columns={'Player': 'player_names', 'Position': 'position', 'Team': 'team', 'Salary': 'salary', 'Median': 'median', 'Own': 'ownership', 'CPT_Own': 'captain ownership'})
1304
  fd_roo_raw = fd_roo_raw.rename(columns={'Player': 'player_names', 'Position': 'position', 'Team': 'team', 'Salary': 'salary', 'Median': 'median', 'Own': 'ownership', 'CPT_Own': 'captain ownership'})
 
1308
  dk_sd_id_map = None
1309
  fd_sd_id_map = None
1310
 
1311
+ return 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, opp_map
1312
 
1313
  def init_DK_MMA_lineups(type_var, slate_var, prio_var, prio_mix, lineup_num, salary_min, salary_max, player_var2):
1314
 
global_func/exposure_spread.py CHANGED
@@ -173,7 +173,12 @@ def check_salary_eligibility(current_lineup_salary, current_player, new_player,
173
  salary_diff = calculate_salary_difference(current_player, new_player, column_name, projections_df, type_var)
174
  return current_lineup_salary + salary_diff <= salary_max
175
 
176
- def exposure_spread(working_frame, exposure_player, exposure_target, comp_salary_below, comp_salary_above, ignore_stacks, remove_teams, specific_replacements, specific_exclusions, specific_columns, projections_df, sport_var, type_var, salary_max, stacking_sports):
 
 
 
 
 
177
  if specific_exclusions != []:
178
  projections_df = projections_df[~projections_df['player_names'].isin(specific_exclusions)]
179
  comparable_players = projections_df[projections_df['player_names'] == exposure_player]
@@ -191,8 +196,7 @@ def exposure_spread(working_frame, exposure_player, exposure_target, comp_salary
191
  comp_projection_low = comparable_players['median'][0] - (comparable_players['median'][0] * .5)
192
  else:
193
  comp_projection_low = comparable_players['median'][0] - (comparable_players['median'][0] * .5)
194
- # players can be eligible at multiple positions, so we need to find all the positions the player is eligible at
195
- # the position column can have positions designated as 1B/OF which means they are eligible at 1B and OF
196
  comp_player_position = comparable_players['position'].tolist()
197
  comp_team = comparable_players['team'].tolist()
198
  try:
@@ -206,6 +210,23 @@ def exposure_spread(working_frame, exposure_player, exposure_target, comp_salary
206
  player_pos_list = player_positions.split('/')
207
  return any(pos in target_positions for pos in player_pos_list)
208
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  # find the exposure rate of the player in the working frame
210
  if specific_columns != []:
211
  player_mask = working_frame[specific_columns].apply(
@@ -249,15 +270,9 @@ def exposure_spread(working_frame, exposure_player, exposure_target, comp_salary
249
  random.shuffle(random_row_indices_insert)
250
  random.shuffle(random_row_indices_replace)
251
 
252
- # for each row to the the number of lineups to remove, replace with random choice from comparable player list if they can be inserted
253
-
254
- # we will need to use two separate functions here, one for an exposure player who has a lineups to remove above 0 and one for below 0
255
- # key concept here is if they have a lineups to remove above 0 it means that we are trying to replace them with comparable players
256
- # if the lineups to remove is below zero it means we want to find comparable players and replace them with the exposure player
257
  if lineups_to_remove > 0:
258
- # Keep trying until we've made enough successful replacements
259
  while change_counter < math.ceil(lineups_to_remove) and random_row_indices_insert:
260
- # Get the next row to try
261
  row = random_row_indices_insert.pop(0)
262
 
263
  if specific_replacements != []:
@@ -296,7 +311,6 @@ def exposure_spread(working_frame, exposure_player, exposure_target, comp_salary
296
  comparable_player_list = []
297
 
298
  if comparable_player_list:
299
- # Find which column contains the exposure_player
300
  if specific_columns != []:
301
  row_data = working_frame.iloc[row][specific_columns]
302
  working_columns = specific_columns
@@ -304,25 +318,23 @@ def exposure_spread(working_frame, exposure_player, exposure_target, comp_salary
304
  row_data = working_frame.iloc[row]
305
  working_columns = working_frame.columns
306
 
307
- # Track if we successfully made a replacement
308
  replacement_made = False
309
 
310
- # For exposure_target == 0, replace ALL occurrences of exposure_player in this row
311
  if exposure_target == 0:
312
  for col in working_columns:
313
  if row_data[col] == exposure_player:
314
- # Try to find a suitable replacement for this specific column
315
  suitable_replacements = []
316
  for candidate in comparable_player_list:
317
- # Get the replacement player's positions
 
 
 
318
  replacement_player_positions = projections_df[projections_df['player_names'] == candidate]['position'].iloc[0].split('/')
319
 
320
- # Check if the replacement player is eligible for this column
321
  if type_var == 'Classic':
322
  if check_position_eligibility(sport_var, col, replacement_player_positions):
323
  suitable_replacements.append(candidate)
324
  else:
325
- # For non-Classic types, check salary eligibility using helper function
326
  current_lineup_salary = working_frame.iloc[row]['salary']
327
  if check_salary_eligibility(current_lineup_salary, exposure_player, candidate, col, projections_df, type_var, salary_max):
328
  suitable_replacements.append(candidate)
@@ -331,39 +343,55 @@ def exposure_spread(working_frame, exposure_player, exposure_target, comp_salary
331
  insert_player = random.choice(suitable_replacements)
332
  working_frame.at[row, col] = insert_player
333
  replacement_made = True
334
- # Remove this player from the list to avoid duplicates in the same row
335
  comparable_player_list = [p for p in comparable_player_list if p != insert_player]
336
  else:
337
  for col in working_columns:
338
  if row_data[col] == exposure_player:
339
- insert_player = random.choice(comparable_player_list)
340
- # Get the replacement player's positions
 
 
 
 
 
 
 
 
341
  replacement_player_positions = projections_df[projections_df['player_names'] == insert_player]['position'].iloc[0].split('/')
342
 
343
- # Check if the replacement player is eligible for this column
344
  if type_var == 'Classic':
345
  if check_position_eligibility(sport_var, col, replacement_player_positions):
346
  working_frame.at[row, col] = insert_player
347
  replacement_made = True
348
  break
349
  else:
350
- # For non-Classic types, check salary eligibility using helper function
351
  current_lineup_salary = working_frame.iloc[row]['salary']
352
  if check_salary_eligibility(current_lineup_salary, exposure_player, insert_player, col, projections_df, type_var, salary_max):
353
  working_frame.at[row, col] = insert_player
354
  replacement_made = True
355
  break
356
 
357
- # Only increment counter if we actually made a replacement
358
  if replacement_made:
359
  change_counter += 1
360
 
361
- # If we've run out of rows to try, break to avoid infinite loop
362
  if not random_row_indices_insert:
363
  break
 
 
364
  else:
365
  while change_counter < math.ceil(lineups_to_add) and random_row_indices_replace:
366
  row = random_row_indices_replace.pop(0)
 
 
 
 
 
 
 
 
 
 
 
367
  if specific_replacements != []:
368
  comparable_players = projections_df[(projections_df['player_names'].isin(specific_replacements))
369
  ]
@@ -394,14 +422,12 @@ def exposure_spread(working_frame, exposure_player, exposure_target, comp_salary
394
 
395
  comparable_players = comparable_players[comparable_players['player_names'] != exposure_player]
396
 
397
- # Create a list of comparable players
398
  comparable_player_list = comparable_players['player_names'].tolist()
399
 
400
  if exposure_player in working_frame.iloc[row].values:
401
  comparable_player_list = []
402
 
403
  if comparable_player_list:
404
- # Find which column contains the exposure_player
405
  if specific_columns != []:
406
  row_data = working_frame.iloc[row][specific_columns]
407
  working_columns = specific_columns
@@ -414,19 +440,17 @@ def exposure_spread(working_frame, exposure_player, exposure_target, comp_salary
414
  current_lineup_salary = working_frame.iloc[row]['salary']
415
  current_player = row_data[col]
416
 
417
- # Check salary eligibility using helper function
418
  if check_salary_eligibility(current_lineup_salary, current_player, exposure_player, col, projections_df, type_var, salary_max):
419
  if type_var == 'Classic':
420
- # For Classic types, also check position eligibility
421
  exposure_player_positions = projections_df[projections_df['player_names'] == exposure_player]['position'].iloc[0].split('/')
422
  if check_position_eligibility(sport_var, col, exposure_player_positions):
423
  working_frame.at[row, col] = exposure_player
424
  change_counter += 1
425
  break
426
  else:
427
- # For non-Classic types, salary check is sufficient (position eligibility handled elsewhere)
428
  working_frame.at[row, col] = exposure_player
429
  change_counter += 1
430
  break
 
431
  return working_frame
432
 
 
173
  salary_diff = calculate_salary_difference(current_player, new_player, column_name, projections_df, type_var)
174
  return current_lineup_salary + salary_diff <= salary_max
175
 
176
+ def exposure_spread(working_frame, exposure_player, exposure_target, comp_salary_below, comp_salary_above, ignore_stacks, remove_teams, specific_replacements, specific_exclusions, specific_columns, projections_df, sport_var, type_var, salary_max, stacking_sports, opp_map=None):
177
+ """
178
+ Added parameter:
179
+ opp_map: Dictionary mapping player names to their opponents (optional)
180
+ """
181
+
182
  if specific_exclusions != []:
183
  projections_df = projections_df[~projections_df['player_names'].isin(specific_exclusions)]
184
  comparable_players = projections_df[projections_df['player_names'] == exposure_player]
 
196
  comp_projection_low = comparable_players['median'][0] - (comparable_players['median'][0] * .5)
197
  else:
198
  comp_projection_low = comparable_players['median'][0] - (comparable_players['median'][0] * .5)
199
+
 
200
  comp_player_position = comparable_players['position'].tolist()
201
  comp_team = comparable_players['team'].tolist()
202
  try:
 
210
  player_pos_list = player_positions.split('/')
211
  return any(pos in target_positions for pos in player_pos_list)
212
 
213
+ def check_opponent_conflict(candidate_player, current_row_data, opp_map):
214
+ """Check if candidate player is an opponent of any player in the current row"""
215
+ if not opp_map:
216
+ return False # No conflicts if opp_map doesn't exist
217
+
218
+ # Get all players currently in the row
219
+ existing_players = set(current_row_data.values)
220
+
221
+ # Check if candidate is opponent of any existing player
222
+ for existing_player in existing_players:
223
+ if opp_map.get(existing_player) == candidate_player:
224
+ return True # Conflict found
225
+ if opp_map.get(candidate_player) == existing_player:
226
+ return True # Conflict found
227
+
228
+ return False # No conflicts
229
+
230
  # find the exposure rate of the player in the working frame
231
  if specific_columns != []:
232
  player_mask = working_frame[specific_columns].apply(
 
270
  random.shuffle(random_row_indices_insert)
271
  random.shuffle(random_row_indices_replace)
272
 
273
+ # REMOVING EXPOSURE_PLAYER (lineups_to_remove > 0)
 
 
 
 
274
  if lineups_to_remove > 0:
 
275
  while change_counter < math.ceil(lineups_to_remove) and random_row_indices_insert:
 
276
  row = random_row_indices_insert.pop(0)
277
 
278
  if specific_replacements != []:
 
311
  comparable_player_list = []
312
 
313
  if comparable_player_list:
 
314
  if specific_columns != []:
315
  row_data = working_frame.iloc[row][specific_columns]
316
  working_columns = specific_columns
 
318
  row_data = working_frame.iloc[row]
319
  working_columns = working_frame.columns
320
 
 
321
  replacement_made = False
322
 
 
323
  if exposure_target == 0:
324
  for col in working_columns:
325
  if row_data[col] == exposure_player:
 
326
  suitable_replacements = []
327
  for candidate in comparable_player_list:
328
+ # NEW: Check for opponent conflicts
329
+ if check_opponent_conflict(candidate, current_row_data, opp_map):
330
+ continue # Skip this candidate
331
+
332
  replacement_player_positions = projections_df[projections_df['player_names'] == candidate]['position'].iloc[0].split('/')
333
 
 
334
  if type_var == 'Classic':
335
  if check_position_eligibility(sport_var, col, replacement_player_positions):
336
  suitable_replacements.append(candidate)
337
  else:
 
338
  current_lineup_salary = working_frame.iloc[row]['salary']
339
  if check_salary_eligibility(current_lineup_salary, exposure_player, candidate, col, projections_df, type_var, salary_max):
340
  suitable_replacements.append(candidate)
 
343
  insert_player = random.choice(suitable_replacements)
344
  working_frame.at[row, col] = insert_player
345
  replacement_made = True
 
346
  comparable_player_list = [p for p in comparable_player_list if p != insert_player]
347
  else:
348
  for col in working_columns:
349
  if row_data[col] == exposure_player:
350
+ # Filter candidates to exclude opponent conflicts
351
+ valid_candidates = [
352
+ candidate for candidate in comparable_player_list
353
+ if not check_opponent_conflict(candidate, current_row_data, opp_map)
354
+ ]
355
+
356
+ if not valid_candidates:
357
+ break # No valid replacements available
358
+
359
+ insert_player = random.choice(valid_candidates)
360
  replacement_player_positions = projections_df[projections_df['player_names'] == insert_player]['position'].iloc[0].split('/')
361
 
 
362
  if type_var == 'Classic':
363
  if check_position_eligibility(sport_var, col, replacement_player_positions):
364
  working_frame.at[row, col] = insert_player
365
  replacement_made = True
366
  break
367
  else:
 
368
  current_lineup_salary = working_frame.iloc[row]['salary']
369
  if check_salary_eligibility(current_lineup_salary, exposure_player, insert_player, col, projections_df, type_var, salary_max):
370
  working_frame.at[row, col] = insert_player
371
  replacement_made = True
372
  break
373
 
 
374
  if replacement_made:
375
  change_counter += 1
376
 
 
377
  if not random_row_indices_insert:
378
  break
379
+
380
+ # ADDING EXPOSURE_PLAYER (lineups_to_remove < 0)
381
  else:
382
  while change_counter < math.ceil(lineups_to_add) and random_row_indices_replace:
383
  row = random_row_indices_replace.pop(0)
384
+
385
+ # Get current row data first to check for opponent conflicts
386
+ if specific_columns != []:
387
+ current_row_data = working_frame.iloc[row][specific_columns]
388
+ else:
389
+ current_row_data = working_frame.iloc[row]
390
+
391
+ # NEW: Check if exposure_player conflicts with existing players
392
+ if check_opponent_conflict(exposure_player, current_row_data, opp_map):
393
+ continue # Skip this row, can't add exposure_player here
394
+
395
  if specific_replacements != []:
396
  comparable_players = projections_df[(projections_df['player_names'].isin(specific_replacements))
397
  ]
 
422
 
423
  comparable_players = comparable_players[comparable_players['player_names'] != exposure_player]
424
 
 
425
  comparable_player_list = comparable_players['player_names'].tolist()
426
 
427
  if exposure_player in working_frame.iloc[row].values:
428
  comparable_player_list = []
429
 
430
  if comparable_player_list:
 
431
  if specific_columns != []:
432
  row_data = working_frame.iloc[row][specific_columns]
433
  working_columns = specific_columns
 
440
  current_lineup_salary = working_frame.iloc[row]['salary']
441
  current_player = row_data[col]
442
 
 
443
  if check_salary_eligibility(current_lineup_salary, current_player, exposure_player, col, projections_df, type_var, salary_max):
444
  if type_var == 'Classic':
 
445
  exposure_player_positions = projections_df[projections_df['player_names'] == exposure_player]['position'].iloc[0].split('/')
446
  if check_position_eligibility(sport_var, col, exposure_player_positions):
447
  working_frame.at[row, col] = exposure_player
448
  change_counter += 1
449
  break
450
  else:
 
451
  working_frame.at[row, col] = exposure_player
452
  change_counter += 1
453
  break
454
+
455
  return working_frame
456
 
global_func/optimize_lineup.py CHANGED
@@ -146,12 +146,49 @@ def optimize_single_lineup(
146
  for i in range(num_players):
147
  solver.Add(sum(x[i, j] for j in range(num_open_cols)) <= 1)
148
 
149
- # Constraint 3: Players already LOCKED in the row cannot be selected again
150
- for i, player in enumerate(player_list):
151
- player_name = player['player_names']
152
- if player_name in locked_player_names:
153
- for j in range(num_open_cols):
154
- solver.Add(x[i, j] == 0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
  # Constraint 4: Position eligibility
157
  for i, player in enumerate(player_list):
 
146
  for i in range(num_players):
147
  solver.Add(sum(x[i, j] for j in range(num_open_cols)) <= 1)
148
 
149
+ # Constraint 3 & Opponent Exclusion: Handle locked players and opponents
150
+ opp_map = map_dict.get('opp_map', {})
151
+
152
+ if opp_map:
153
+ # When opp_map exists, use opponent-based constraints
154
+
155
+ # Build a mapping of locked opponents that cannot be selected
156
+ locked_opponents = set()
157
+ for locked_player in locked_player_names:
158
+ opponent = opp_map.get(locked_player)
159
+ if opponent:
160
+ locked_opponents.add(opponent)
161
+
162
+ # Prevent locked opponents from being selected
163
+ for i, player in enumerate(player_list):
164
+ player_name = player['player_names']
165
+ if player_name in locked_opponents:
166
+ for j in range(num_open_cols):
167
+ solver.Add(x[i, j] == 0)
168
+
169
+ # Prevent opponents from being selected together in open positions
170
+ player_name_to_idx = {player['player_names']: i for i, player in enumerate(player_list)}
171
+
172
+ for i, player in enumerate(player_list):
173
+ player_name = player['player_names']
174
+ opponent_name = opp_map.get(player_name)
175
+
176
+ if opponent_name and opponent_name in player_name_to_idx:
177
+ opponent_idx = player_name_to_idx[opponent_name]
178
+
179
+ # If player i is selected, opponent cannot be selected
180
+ solver.Add(
181
+ sum(x[i, j] for j in range(num_open_cols)) +
182
+ sum(x[opponent_idx, j] for j in range(num_open_cols)) <= 1
183
+ )
184
+ else:
185
+ # When opp_map doesn't exist, use standard locked player constraint
186
+ # Constraint 3: Players already LOCKED in the row cannot be selected again
187
+ for i, player in enumerate(player_list):
188
+ player_name = player['player_names']
189
+ if player_name in locked_player_names:
190
+ for j in range(num_open_cols):
191
+ solver.Add(x[i, j] == 0)
192
 
193
  # Constraint 4: Position eligibility
194
  for i, player in enumerate(player_list):