James McCool commited on
Commit
2131301
Β·
1 Parent(s): 0b825a6

Initial commit from old project

Browse files
Files changed (3) hide show
  1. Dockerfile +5 -0
  2. requirements.txt +8 -3
  3. src/streamlit_app.py +1311 -37
Dockerfile CHANGED
@@ -14,6 +14,11 @@ COPY src/ ./src/
14
 
15
  RUN pip3 install -r requirements.txt
16
 
 
 
 
 
 
17
  EXPOSE 8501
18
 
19
  HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
 
14
 
15
  RUN pip3 install -r requirements.txt
16
 
17
+ # Expose the secret SECRET_EXAMPLE at buildtime and use its value as git remote URL
18
+ RUN --mount=type=secret,id=mongo_uri,mode=0444,required=true \
19
+ git init && \
20
+ git remote add origin $(cat /run/secrets/mongo_uri)
21
+
22
  EXPOSE 8501
23
 
24
  HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
requirements.txt CHANGED
@@ -1,3 +1,8 @@
1
- altair
2
- pandas
3
- streamlit
 
 
 
 
 
 
1
+ streamlit
2
+ openpyxl
3
+ matplotlib
4
+ pulp
5
+ docker
6
+ plotly
7
+ scipy
8
+ pymongo
src/streamlit_app.py CHANGED
@@ -1,40 +1,1314 @@
1
- import altair as alt
2
  import numpy as np
3
  import pandas as pd
4
- import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
1
+ import streamlit as st
2
  import numpy as np
3
  import pandas as pd
4
+ import pymongo
5
+ import re
6
+
7
+ print(f"Streamlit version: {st.__version__}")
8
+
9
+ st.set_page_config(layout="wide")
10
+
11
+ @st.cache_resource
12
+ def init_conn():
13
+ uri = st.secrets['mongo_uri']
14
+ client = pymongo.MongoClient(uri, retryWrites=True, serverSelectionTimeoutMS=500000)
15
+ db = client["NFL_Database"]
16
+
17
+ return db
18
+
19
+ db = init_conn()
20
+
21
+ game_format = {'Win Percentage': '{:.2%}','First Inning Lead Percentage': '{:.2%}',
22
+ 'Fifth Inning Lead Percentage': '{:.2%}', '8+ runs': '{:.2%}', 'DK LevX': '{:.2%}', 'FD LevX': '{:.2%}'}
23
+
24
+ player_roo_format = {'Top_finish': '{:.2%}','Top_5_finish': '{:.2%}', 'Top_10_finish': '{:.2%}', '20+%': '{:.2%}', '2x%': '{:.2%}', '3x%': '{:.2%}',
25
+ '4x%': '{:.2%}','GPP%': '{:.2%}'}
26
+
27
+ dk_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']
28
+ fd_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']
29
+ dk_hb_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']
30
+ fd_hb_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']
31
+ dk_sd_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']
32
+ fd_sd_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']
33
+
34
+ st.markdown("""
35
+ <style>
36
+ /* Tab styling */
37
+ .stTabs [data-baseweb="tab-list"] {
38
+ gap: 8px;
39
+ padding: 4px;
40
+ }
41
+ .stTabs [data-baseweb="tab"] {
42
+ height: 50px;
43
+ white-space: pre-wrap;
44
+ background-color: #DAA520;
45
+ color: white;
46
+ border-radius: 10px;
47
+ gap: 1px;
48
+ padding: 10px 20px;
49
+ font-weight: bold;
50
+ transition: all 0.3s ease;
51
+ }
52
+ .stTabs [aria-selected="true"] {
53
+ background-color: #DAA520;
54
+ border: 3px solid #FFD700;
55
+ color: white;
56
+ }
57
+ .stTabs [data-baseweb="tab"]:hover {
58
+ background-color: #FFD700;
59
+ cursor: pointer;
60
+ }
61
+
62
+ div[data-baseweb="select"] > div {
63
+ background-color: #DAA520;
64
+ color: white;
65
+ }
66
+
67
+ div{
68
+ box-sizing: content-box !important;
69
+ }
70
+
71
+ </style>""", unsafe_allow_html=True)
72
+
73
+ @st.cache_resource(ttl=60)
74
+ def init_baselines():
75
+
76
+ collection = db["Player_Baselines"]
77
+ cursor = collection.find()
78
+
79
+ raw_display = pd.DataFrame(list(cursor))
80
+ 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',
81
+ 'rush_att', 'rush_yards', 'rush_tds', 'targets', 'rec', 'rec_yards', 'rec_tds', 'PPR', 'Half_PPR', 'Own']]
82
+ player_stats = raw_display[raw_display['Position'] != 'K']
83
+
84
+ collection = db["DK_NFL_ROO"]
85
+ cursor = collection.find()
86
+
87
+ raw_display = pd.DataFrame(list(cursor))
88
+ raw_display = raw_display.rename(columns={'player_ID': 'player_id'})
89
+ raw_display = raw_display[['Player', 'Position', 'Team', 'Opp', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '2x%', '3x%', '4x%',
90
+ 'Own', 'Small_Field_Own', 'Large_Field_Own', 'Cash_Field_Own', 'CPT_Own', 'LevX', 'version', 'slate', 'timestamp', 'player_id', 'site']]
91
+ load_display = raw_display[raw_display['Position'] != 'K']
92
+ dk_roo_raw = load_display.dropna(subset=['Median'])
93
+
94
+ dk_id_map = dict(zip(dk_roo_raw['Player'], dk_roo_raw['player_id']))
95
+
96
+ collection = db["FD_NFL_ROO"]
97
+ cursor = collection.find()
98
+
99
+ raw_display = pd.DataFrame(list(cursor))
100
+ raw_display = raw_display.rename(columns={'player_ID': 'player_id'})
101
+ raw_display = raw_display[['Player', 'Position', 'Team', 'Opp', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '2x%', '3x%', '4x%',
102
+ 'Own', 'Small_Field_Own', 'Large_Field_Own', 'Cash_Field_Own', 'CPT_Own', 'LevX', 'version', 'slate', 'timestamp', 'player_id', 'site']]
103
+ load_display = raw_display[raw_display['Position'] != 'K']
104
+ fd_roo_raw = load_display.dropna(subset=['Median'])
105
+
106
+ fd_id_map = dict(zip(fd_roo_raw['Player'], fd_roo_raw['player_id']))
107
+
108
+ collection = db["DK_SD_NFL_ROO"]
109
+ cursor = collection.find()
110
+
111
+ raw_display = pd.DataFrame(list(cursor))
112
+ raw_display = raw_display.rename(columns={'player_ID': 'player_id'})
113
+ raw_display = raw_display[['Player', 'Position', 'Team', 'Opp', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '2x%', '3x%', '4x%',
114
+ 'Own', 'Small_Field_Own', 'Large_Field_Own', 'Cash_Field_Own', 'CPT_Own', 'LevX', 'version', 'slate', 'timestamp', 'player_id', 'site']]
115
+ load_display = raw_display[raw_display['Position'] != 'K']
116
+ dk_sd_roo_raw = load_display.dropna(subset=['Median'])
117
+
118
+ dk_sd_id_map = dict(zip(dk_sd_roo_raw['Player'], dk_sd_roo_raw['player_id']))
119
+
120
+ collection = db["FD_SD_NFL_ROO"]
121
+ cursor = collection.find()
122
+
123
+ raw_display = pd.DataFrame(list(cursor))
124
+ raw_display = raw_display.rename(columns={'player_ID': 'player_id'})
125
+ raw_display = raw_display[['Player', 'Position', 'Team', 'Opp', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '2x%', '3x%', '4x%',
126
+ 'Own', 'Small_Field_Own', 'Large_Field_Own', 'Cash_Field_Own', 'CPT_Own', 'LevX', 'version', 'slate', 'timestamp', 'player_id', 'site']]
127
+ load_display = raw_display[raw_display['Position'] != 'K']
128
+ fd_sd_roo_raw = load_display.dropna(subset=['Median'])
129
+
130
+ fd_sd_id_map = dict(zip(fd_sd_roo_raw['Player'], fd_sd_roo_raw['player_id']))
131
+
132
+ collection = db["DK_DFS_Stacks"]
133
+ cursor = collection.find()
134
+
135
+ raw_display = pd.DataFrame(list(cursor))
136
+ raw_display = raw_display[['Team', 'QB', 'WR1_TE', 'WR2_TE', 'Total', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '60+%', '2x%', '3x%', '4x%', 'Own', 'LevX', 'slate', 'version']]
137
+ dk_stacks_raw = raw_display.copy()
138
+
139
+ collection = db["FD_DFS_Stacks"]
140
+ cursor = collection.find()
141
+
142
+ raw_display = pd.DataFrame(list(cursor))
143
+ raw_display = raw_display[['Team', 'QB', 'WR1_TE', 'WR2_TE', 'Total', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '60+%', '2x%', '3x%', '4x%', 'Own', 'LevX', 'slate', 'version']]
144
+ fd_stacks_raw = raw_display.copy()
145
+
146
+ 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
147
+
148
+ @st.cache_resource(ttl = 60)
149
+ def init_DK_lineups(type_var, slate_var):
150
+
151
+ if type_var == 'Regular':
152
+ if slate_var == 'Main':
153
+ collection = db['DK_NFL_name_map']
154
+ cursor = collection.find()
155
+ raw_data = pd.DataFrame(list(cursor))
156
+ names_dict = dict(zip(raw_data['key'], raw_data['value']))
157
+
158
+ collection = db['DK_NFL_seed_frame']
159
+ cursor = collection.find().limit(10000)
160
+
161
+ raw_display = pd.DataFrame(list(cursor))
162
+ raw_display = raw_display[['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']]
163
+ dict_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']
164
+ # Map names
165
+ raw_display[dict_columns] = raw_display[dict_columns].apply(lambda x: x.map(names_dict))
166
+ elif slate_var == 'Secondary':
167
+ collection = db['DK_NFL_Secondary_name_map']
168
+ cursor = collection.find()
169
+ raw_data = pd.DataFrame(list(cursor))
170
+ names_dict = dict(zip(raw_data['key'], raw_data['value']))
171
+
172
+ collection = db['DK_NFL_Secondary_seed_frame']
173
+ cursor = collection.find().limit(10000)
174
+
175
+ raw_display = pd.DataFrame(list(cursor))
176
+ raw_display = raw_display[['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']]
177
+ dict_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']
178
+ # Map names
179
+ raw_display[dict_columns] = raw_display[dict_columns].apply(lambda x: x.map(names_dict))
180
+ elif slate_var == 'Auxiliary':
181
+ collection = db['DK_NFL_Turbo_name_map']
182
+ cursor = collection.find()
183
+ raw_data = pd.DataFrame(list(cursor))
184
+ names_dict = dict(zip(raw_data['key'], raw_data['value']))
185
+
186
+ collection = db['DK_NFL_Turbo_seed_frame']
187
+ cursor = collection.find().limit(10000)
188
+
189
+ raw_display = pd.DataFrame(list(cursor))
190
+ raw_display = raw_display[['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']]
191
+ dict_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']
192
+ # Map names
193
+ raw_display[dict_columns] = raw_display[dict_columns].apply(lambda x: x.map(names_dict))
194
+ elif type_var == 'Showdown':
195
+ if slate_var == 'Main':
196
+ collection = db['DK_NFL_SD1_seed_frame']
197
+ cursor = collection.find().limit(10000)
198
+
199
+ raw_display = pd.DataFrame(list(cursor))
200
+ raw_display = raw_display[['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']]
201
+ elif slate_var == 'Secondary':
202
+ collection = db['DK_NFL_SD2_seed_frame']
203
+ cursor = collection.find().limit(10000)
204
+
205
+ raw_display = pd.DataFrame(list(cursor))
206
+ raw_display = raw_display[['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']]
207
+ elif slate_var == 'Auxiliary':
208
+ collection = db['DK_NFL_SD3_seed_frame']
209
+ cursor = collection.find().limit(10000)
210
+
211
+ raw_display = pd.DataFrame(list(cursor))
212
+ raw_display = raw_display[['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']]
213
+
214
+ DK_seed = raw_display.to_numpy()
215
+
216
+ return DK_seed
217
+
218
+ @st.cache_resource(ttl = 60)
219
+ def init_FD_lineups(type_var, slate_var):
220
+
221
+ if type_var == 'Regular':
222
+ if slate_var == 'Main':
223
+ collection = db['FD_NFL_name_map']
224
+ cursor = collection.find()
225
+ raw_data = pd.DataFrame(list(cursor))
226
+ names_dict = dict(zip(raw_data['key'], raw_data['value']))
227
+
228
+ collection = db['FD_NFL_seed_frame']
229
+ cursor = collection.find().limit(10000)
230
+
231
+ raw_display = pd.DataFrame(list(cursor))
232
+ raw_display = raw_display[['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']]
233
+ dict_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']
234
+ # Map names
235
+ raw_display[dict_columns] = raw_display[dict_columns].apply(lambda x: x.map(names_dict))
236
+ elif slate_var == 'Secondary':
237
+ collection = db['FD_NFL_Secondary_name_map']
238
+ cursor = collection.find()
239
+ raw_data = pd.DataFrame(list(cursor))
240
+ names_dict = dict(zip(raw_data['key'], raw_data['value']))
241
+
242
+ collection = db['FD_NFL_Secondary_seed_frame']
243
+ cursor = collection.find().limit(10000)
244
+
245
+ raw_display = pd.DataFrame(list(cursor))
246
+ raw_display = raw_display[['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']]
247
+ dict_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']
248
+ # Map names
249
+ raw_display[dict_columns] = raw_display[dict_columns].apply(lambda x: x.map(names_dict))
250
+ elif slate_var == 'Auxiliary':
251
+ collection = db['FD_NFL_Turbo_name_map']
252
+ cursor = collection.find()
253
+ raw_data = pd.DataFrame(list(cursor))
254
+ names_dict = dict(zip(raw_data['key'], raw_data['value']))
255
+
256
+ collection = db['FD_NFL_Turbo_seed_frame']
257
+ cursor = collection.find().limit(10000)
258
+
259
+ raw_display = pd.DataFrame(list(cursor))
260
+ raw_display = raw_display[['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']]
261
+ dict_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']
262
+ # Map names
263
+ raw_display[dict_columns] = raw_display[dict_columns].apply(lambda x: x.map(names_dict))
264
+
265
+ elif type_var == 'Showdown':
266
+ if slate_var == 'Main':
267
+ collection = db['FD_NFL_SD1_seed_frame']
268
+ cursor = collection.find().limit(10000)
269
+
270
+ raw_display = pd.DataFrame(list(cursor))
271
+ raw_display = raw_display[['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']]
272
+ elif slate_var == 'Secondary':
273
+ collection = db['FD_NFL_SD2_seed_frame']
274
+ cursor = collection.find().limit(10000)
275
+
276
+ raw_display = pd.DataFrame(list(cursor))
277
+ raw_display = raw_display[['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']]
278
+ elif slate_var == 'Auxiliary':
279
+ collection = db['FD_NFL_SD3_seed_frame']
280
+ cursor = collection.find().limit(10000)
281
+
282
+ raw_display = pd.DataFrame(list(cursor))
283
+ raw_display = raw_display[['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5', 'salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own']]
284
+
285
+ FD_seed = raw_display.to_numpy()
286
+
287
+ return FD_seed
288
+
289
+ @st.cache_data
290
+ def convert_df_to_csv(df):
291
+ return df.to_csv().encode('utf-8')
292
+
293
+ @st.cache_data
294
+ def convert_df(array):
295
+ array = pd.DataFrame(array, columns=column_names)
296
+ return array.to_csv().encode('utf-8')
297
+
298
+ @st.cache_data
299
+ def convert_pm_df(array):
300
+ array = pd.DataFrame(array)
301
+ return array.to_csv().encode('utf-8')
302
+
303
+ @st.cache_data
304
+ def convert_hb_df(array, column_names):
305
+ array = pd.DataFrame(array, columns=column_names)
306
+ return array.to_csv().encode('utf-8')
307
+
308
+ col1, col2 = st.columns([1, 9])
309
+ with col1:
310
+ if st.button("Load/Reset Data", key='reset'):
311
+ st.cache_data.clear()
312
+ 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()
313
+ dk_lineups = init_DK_lineups('Regular', 'Main')
314
+ fd_lineups = init_FD_lineups('Regular', 'Main')
315
+ for key in st.session_state.keys():
316
+ del st.session_state[key]
317
+ with col2:
318
+ with st.container():
319
+ col1, col2 = st.columns([3, 3])
320
+ with col1:
321
+ view_var = st.selectbox("Select view", ["Simple", "Advanced"], key='view_var')
322
+ with col2:
323
+ site_var = st.selectbox("What site do you want to view?", ('Draftkings', 'Fanduel'), key='site_var')
324
+
325
+ 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()
326
+
327
+ t_stamp = f"Last Update: " + str(dk_roo_raw['timestamp'][0]) + f" CST"
328
+
329
+ tab1, tab2, tab3, tab4 = st.tabs(["Stacks ROO", "Player ROO", "Optimals", "Handbuilder"])
330
+
331
+ with tab1:
332
+ with st.expander("Info and Filters"):
333
+ st.info(t_stamp)
334
+ with st.container():
335
+ slate_var1 = st.radio("Which data are you loading?", ('Main Slate', 'Secondary Slate', 'Late Slate', 'Thurs-Mon Slate'), key='slate_var1')
336
+ split_var1 = st.radio("Would you like to view the whole slate or just specific games?", ('Full Slate Run', 'Specific Games'), key='split_var1')
337
+ if site_var == 'Draftkings':
338
+ raw_baselines = dk_stacks_raw[dk_stacks_raw['slate'] == str(slate_var1)]
339
+ raw_baselines = raw_baselines[raw_baselines['version'] == 'overall']
340
+ raw_baselines = raw_baselines.iloc[:,:-2]
341
+ elif site_var == 'Fanduel':
342
+ raw_baselines = fd_stacks_raw[fd_stacks_raw['slate'] == str(slate_var1)]
343
+ raw_baselines = raw_baselines[raw_baselines['version'] == 'overall']
344
+ raw_baselines = raw_baselines.iloc[:,:-2]
345
+ if split_var1 == 'Specific Games':
346
+ team_var1 = st.multiselect('Which teams would you like to include in the ROO?', options = raw_baselines['Team'].unique(), key='team_var1')
347
+ elif split_var1 == 'Full Slate Run':
348
+ team_var1 = raw_baselines.Team.values.tolist()
349
+
350
+ final_stacks = raw_baselines[raw_baselines['Team'].isin(team_var1)]
351
+ if view_var == 'Simple':
352
+ final_stacks = final_stacks[['Team', 'QB', 'WR1_TE', 'WR2_TE', 'Salary', 'Median', '60+%', '4x%']]
353
+ elif view_var == 'Advanced':
354
+ final_stacks = final_stacks[['Team', 'QB', 'WR1_TE', 'WR2_TE', 'Total', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish',
355
+ 'Top_10_finish', '60+%', '2x%', '3x%', '4x%', 'Own', 'LevX']]
356
+ st.dataframe(final_stacks.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(player_roo_format, precision=2), height=750, use_container_width = True)
357
+ st.download_button(
358
+ label="Export Tables",
359
+ data=convert_df_to_csv(final_stacks),
360
+ file_name='NFL_stacks_export.csv',
361
+ mime='text/csv',
362
+ )
363
+
364
+ with tab2:
365
+ with st.expander("Info and Filters"):
366
+ st.info(t_stamp)
367
+ slate_var2 = st.radio("Which data are you loading?", ('Main Slate', 'Secondary Slate', 'Late Slate', 'Thurs-Mon Slate'), key='slate_var2')
368
+ if site_var == 'Draftkings':
369
+ raw_baselines = dk_roo_raw[dk_roo_raw['slate'] == str(slate_var2)]
370
+
371
+ raw_baselines = raw_baselines.iloc[:,:-2]
372
+ elif site_var == 'Fanduel':
373
+ raw_baselines = fd_roo_raw[fd_roo_raw['slate'] == str(slate_var2)]
374
+ raw_baselines = raw_baselines.iloc[:,:-2]
375
+ split_var2 = st.radio("Would you like to view the whole slate or just specific games?", ('Full Slate Run', 'Specific Games'), key='split_var2')
376
+ if split_var2 == 'Specific Games':
377
+ team_var2 = st.multiselect('Which teams would you like to include in the ROO?', options = raw_baselines['Team'].unique(), key='team_var2')
378
+ elif split_var2 == 'Full Slate Run':
379
+ team_var2 = raw_baselines.Team.values.tolist()
380
+ pos_split2 = st.selectbox('What Position table would you like to view?', options = ['Overall', 'QB', 'RB', 'WR', 'TE'], key='pos_split2')
381
+ pos_combos2 = st.multiselect('If Overall, specific positions?', options = ['QB', 'RB', 'WR', 'TE', 'DST'], default = ['QB', 'RB', 'WR', 'TE', 'DST'], key='pos_combos2')
382
+ sal_var2 = st.slider("Is there a certain price range you want to view?", 2000, 15000, (2000, 15000), key='sal_var2')
383
+
384
+ if pos_split2 == 'Overall':
385
+ raw_baselines = raw_baselines[raw_baselines['version'] == 'overall']
386
+ elif pos_split2 == 'QB':
387
+ if site_var == 'Draftkings':
388
+ raw_baselines = raw_baselines[raw_baselines['version'] == 'dk_qbs']
389
+ elif site_var == 'Fanduel':
390
+ raw_baselines = raw_baselines[raw_baselines['version'] == 'fd_qbs']
391
+ elif pos_split2 == 'RB':
392
+ if site_var == 'Draftkings':
393
+ raw_baselines = raw_baselines[raw_baselines['version'] == 'dk_rbs']
394
+ elif site_var == 'Fanduel':
395
+ raw_baselines = raw_baselines[raw_baselines['version'] == 'fd_rbs']
396
+ elif pos_split2 == 'WR':
397
+ if site_var == 'Draftkings':
398
+ raw_baselines = raw_baselines[raw_baselines['version'] == 'dk_wrs']
399
+ elif site_var == 'Fanduel':
400
+ raw_baselines = raw_baselines[raw_baselines['version'] == 'fd_wrs']
401
+ elif pos_split2 == 'TE':
402
+ if site_var == 'Draftkings':
403
+ raw_baselines = raw_baselines[raw_baselines['version'] == 'dk_tes']
404
+ elif site_var == 'Fanduel':
405
+ raw_baselines = raw_baselines[raw_baselines['version'] == 'fd_tes']
406
+ raw_baselines = raw_baselines[raw_baselines['Position'].str.contains('|'.join(pos_combos2))]
407
+ final_Proj = raw_baselines[raw_baselines['Team'].isin(team_var2)]
408
+ final_Proj = final_Proj[final_Proj['Salary'] >= sal_var2[0]]
409
+ final_Proj = final_Proj[final_Proj['Salary'] <= sal_var2[1]]
410
+
411
+ if view_var == 'Simple':
412
+ final_Proj = final_Proj[['Player', 'Position', 'Team', 'Salary', 'Median', 'Top_5_finish', '4x%']]
413
+ disp_proj = final_Proj.set_index('Player')
414
+ elif view_var == 'Advanced':
415
+ final_Proj = final_Proj[['Player', 'Position', 'Team', 'Opp', 'Salary', 'Floor', 'Median', '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']]
416
+ disp_proj = final_Proj.set_index('Player')
417
+ st.dataframe(disp_proj.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(player_roo_format, precision=2), height=750, use_container_width = True)
418
+ st.download_button(
419
+ label="Export Tables",
420
+ data=convert_df_to_csv(final_Proj),
421
+ file_name='NFL_ROO_export.csv',
422
+ mime='text/csv',
423
+ )
424
+
425
+ with tab3:
426
+ st.header("Optimals")
427
+ with st.expander("Info and Filters"):
428
+ st.info("These filters will display various optimals in the table below to pick from. If you want to export the entire set of 10,000 optimals, hit the 'Prepare full data export' button. If you would like to apply the filters here to the 10,000 optimals before you export, use the 'Prepare full data export (Filter)' button.")
429
+ col1, col2, col3, col4 = st.columns(4)
430
+ with col1:
431
+ slate_type_var3 = st.radio("Which slate type are you loading?", ('Regular', 'Showdown'), key='slate_type_var3')
432
+ if slate_type_var3 == 'Regular':
433
+ if site_var == 'Draftkings':
434
+ raw_baselines = dk_roo_raw
435
+ elif site_var == 'Fanduel':
436
+ raw_baselines = fd_roo_raw
437
+ elif slate_type_var3 == 'Showdown':
438
+ if site_var == 'Draftkings':
439
+ raw_baselines = dk_sd_roo_raw
440
+ elif site_var == 'Fanduel':
441
+ raw_baselines = fd_sd_roo_raw
442
+ slate_var3 = st.radio("Which slate data are you loading?", ('Main', 'Secondary', 'Auxiliary'), key='slate_var3')
443
+ if slate_type_var3 == 'Regular':
444
+ if site_var == 'Draftkings':
445
+ dk_lineups = init_DK_lineups(slate_type_var3, slate_var3)
446
+ elif site_var == 'Fanduel':
447
+ fd_lineups = init_FD_lineups(slate_type_var3, slate_var3)
448
+ elif slate_type_var3 == 'Showdown':
449
+ if site_var == 'Draftkings':
450
+ dk_lineups = init_DK_lineups(slate_type_var3, slate_var3)
451
+ elif site_var == 'Fanduel':
452
+ fd_lineups = init_FD_lineups(slate_type_var3, slate_var3)
453
+ with col2:
454
+ lineup_num_var = st.number_input("How many lineups do you want to display?", min_value=1, max_value=1000, value=150, step=1)
455
+ player_var1 = st.radio("Do you want a frame with specific Players?", ('Full Slate', 'Specific Players'), key='player_var1')
456
+ if player_var1 == 'Specific Players':
457
+ player_var2 = st.multiselect('Which players do you want?', options = raw_baselines['Player'].unique())
458
+ elif player_var1 == 'Full Slate':
459
+ player_var2 = raw_baselines.Player.values.tolist()
460
+ with col3:
461
+ if site_var == 'Draftkings':
462
+ salary_min_var = st.number_input("Minimum salary used", min_value = 0, max_value = 50000, value = 49000, step = 100, key = 'salary_min_var')
463
+ salary_max_var = st.number_input("Maximum salary used", min_value = 0, max_value = 50000, value = 50000, step = 100, key = 'salary_max_var')
464
+ elif site_var == 'Fanduel':
465
+ salary_min_var = st.number_input("Minimum salary used", min_value = 0, max_value = 60000, value = 59000, step = 100, key = 'salary_min_var')
466
+ salary_max_var = st.number_input("Maximum salary used", min_value = 0, max_value = 60000, value = 60000, step = 100, key = 'salary_max_var')
467
+ with col4:
468
+ if site_var == 'Draftkings':
469
+ min_stacks_var = st.number_input("Minimum stacks used", min_value = 0, max_value = 5, value = 1, step = 1, key = 'min_stacks_var')
470
+ max_stacks_var = st.number_input("Maximum stacks used", min_value = 0, max_value = 5, value = 5, step = 1, key = 'max_stacks_var')
471
+ elif site_var == 'Fanduel':
472
+ min_stacks_var = st.number_input("Minimum stacks used", min_value = 0, max_value = 4, value = 1, step = 1, key = 'min_stacks_var')
473
+ max_stacks_var = st.number_input("Maximum stacks used", min_value = 0, max_value = 4, value = 4, step = 1, key = 'max_stacks_var')
474
+
475
+
476
+ if site_var == 'Draftkings':
477
+ raw_baselines = dk_roo_raw
478
+ if slate_type_var3 == 'Regular':
479
+ ROO_slice = raw_baselines
480
+ player_salaries = dict(zip(ROO_slice['Player'], ROO_slice['Salary']))
481
+ column_names = dk_columns
482
+ elif slate_type_var3 == 'Showdown':
483
+ player_salaries = dict(zip(raw_baselines['Player'], raw_baselines['Salary']))
484
+ column_names = dk_sd_columns
485
+
486
+
487
+ elif site_var == 'Fanduel':
488
+ raw_baselines = fd_roo_raw
489
+ if slate_type_var3 == 'Regular':
490
+ ROO_slice = raw_baselines
491
+ player_salaries = dict(zip(ROO_slice['Player'], ROO_slice['Salary']))
492
+ column_names = fd_columns
493
+ elif slate_type_var3 == 'Showdown':
494
+ player_salaries = dict(zip(raw_baselines['Player'], raw_baselines['Salary']))
495
+ column_names = fd_sd_columns
496
+
497
+ reg_dl_col, filtered_dl_col, blank_dl_col = st.columns([2, 2, 6])
498
+ with reg_dl_col:
499
+ if st.button("Prepare full data export", key='data_export'):
500
+ name_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names)
501
+ data_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names)
502
+ if site_var == 'Draftkings':
503
+ if slate_type_var3 == 'Regular':
504
+ map_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']
505
+ for col_idx in map_columns:
506
+ data_export[col_idx] = data_export[col_idx].map(dk_id_map)
507
+ elif slate_type_var3 == 'Showdown':
508
+ map_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5']
509
+ for col_idx in map_columns:
510
+ data_export[col_idx] = data_export[col_idx].map(dk_sd_id_map)
511
+ elif site_var == 'Fanduel':
512
+ if slate_type_var3 == 'Regular':
513
+ map_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']
514
+ for col_idx in map_columns:
515
+ data_export[col_idx] = data_export[col_idx].map(fd_id_map)
516
+ elif slate_type_var3 == 'Showdown':
517
+ map_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5']
518
+ for col_idx in map_columns:
519
+ data_export[col_idx] = data_export[col_idx].map(fd_sd_id_map)
520
+ reg_opt_col, pm_opt_col = st.columns(2)
521
+ with reg_opt_col:
522
+ st.download_button(
523
+ label="Export optimals set (IDs)",
524
+ data=convert_df(data_export),
525
+ file_name='NFL_optimals_export.csv',
526
+ mime='text/csv',
527
+ )
528
+ st.download_button(
529
+ label="Export optimals set (Names)",
530
+ data=convert_df(name_export),
531
+ file_name='NFL_optimals_export.csv',
532
+ mime='text/csv',
533
+ )
534
+ with pm_opt_col:
535
+ if site_var == 'Draftkings':
536
+ if slate_type_var3 == 'Regular':
537
+ data_export = data_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1)
538
+ elif slate_type_var3 == 'Showdown':
539
+ data_export = data_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1)
540
+ elif site_var == 'Fanduel':
541
+ if slate_type_var3 == 'Regular':
542
+ data_export = data_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1)
543
+ elif slate_type_var3 == 'Showdown':
544
+ data_export = data_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1)
545
+ st.download_button(
546
+ label="Portfolio Manager Export (IDs)",
547
+ data=convert_pm_df(data_export),
548
+ file_name='NFL_optimals_export.csv',
549
+ mime='text/csv',
550
+ )
551
+
552
+ if site_var == 'Draftkings':
553
+ if slate_type_var3 == 'Regular':
554
+ name_export = name_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1)
555
+ elif slate_type_var3 == 'Showdown':
556
+ name_export = name_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1)
557
+ elif site_var == 'Fanduel':
558
+ if slate_type_var3 == 'Regular':
559
+ name_export = name_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1)
560
+ elif slate_type_var3 == 'Showdown':
561
+ name_export = name_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1)
562
+ st.download_button(
563
+ label="Portfolio Manager Export (Names)",
564
+ data=convert_pm_df(name_export),
565
+ file_name='NFL_optimals_export.csv',
566
+ mime='text/csv',
567
+ )
568
+ with filtered_dl_col:
569
+ if st.button("Prepare full data export (Filtered)", key='data_export_filtered'):
570
+ name_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names)
571
+ data_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names)
572
+ if site_var == 'Draftkings':
573
+ if slate_type_var3 == 'Regular':
574
+ map_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']
575
+ elif slate_type_var3 == 'Showdown':
576
+ map_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5']
577
+ for col_idx in map_columns:
578
+ data_export[col_idx] = data_export[col_idx].map(dk_id_map)
579
+ elif site_var == 'Fanduel':
580
+ if slate_type_var3 == 'Regular':
581
+ map_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']
582
+ elif slate_type_var3 == 'Showdown':
583
+ map_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5']
584
+ for col_idx in map_columns:
585
+ data_export[col_idx] = data_export[col_idx].map(fd_id_map)
586
+ data_export = data_export[data_export['salary'] >= salary_min_var]
587
+ data_export = data_export[data_export['salary'] <= salary_max_var]
588
+ data_export = data_export[data_export['Team_count'] >= min_stacks_var]
589
+ data_export = data_export[data_export['Team_count'] <= max_stacks_var]
590
+
591
+ name_export = name_export[name_export['salary'] >= salary_min_var]
592
+ name_export = name_export[name_export['salary'] <= salary_max_var]
593
+ name_export = name_export[name_export['Team_count'] >= min_stacks_var]
594
+ name_export = name_export[name_export['Team_count'] <= max_stacks_var]
595
+
596
+ reg_opt_col, pm_opt_col = st.columns(2)
597
+ with reg_opt_col:
598
+ st.download_button(
599
+ label="Export optimals set (IDs)",
600
+ data=convert_df(data_export),
601
+ file_name='NFL_optimals_export.csv',
602
+ mime='text/csv',
603
+ )
604
+ st.download_button(
605
+ label="Export optimals set (Names)",
606
+ data=convert_df(name_export),
607
+ file_name='NFL_optimals_export.csv',
608
+ mime='text/csv',
609
+ )
610
+ with pm_opt_col:
611
+ if site_var == 'Draftkings':
612
+ if slate_type_var3 == 'Regular':
613
+ data_export = data_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1)
614
+ elif slate_type_var3 == 'Showdown':
615
+ data_export = data_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1)
616
+ elif site_var == 'Fanduel':
617
+ if slate_type_var3 == 'Regular':
618
+ data_export = data_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1)
619
+ elif slate_type_var3 == 'Showdown':
620
+ data_export = data_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1)
621
+ st.download_button(
622
+ label="Portfolio Manager Export (IDs)",
623
+ data=convert_pm_df(data_export),
624
+ file_name='NFL_optimals_export.csv',
625
+ mime='text/csv',
626
+ )
627
+
628
+ if site_var == 'Draftkings':
629
+ if slate_type_var3 == 'Regular':
630
+ name_export = name_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1)
631
+ elif slate_type_var3 == 'Showdown':
632
+ name_export = name_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1)
633
+ elif site_var == 'Fanduel':
634
+ if slate_type_var3 == 'Regular':
635
+ name_export = name_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1)
636
+ elif slate_type_var3 == 'Showdown':
637
+ name_export = name_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1)
638
+ st.download_button(
639
+ label="Portfolio Manager Export (Names)",
640
+ data=convert_pm_df(name_export),
641
+ file_name='NFL_optimals_export.csv',
642
+ mime='text/csv',
643
+ )
644
+
645
+ if site_var == 'Draftkings':
646
+ if 'working_seed' in st.session_state:
647
+ st.session_state.working_seed = st.session_state.working_seed
648
+ if player_var1 == 'Specific Players':
649
+ st.session_state.working_seed = st.session_state.working_seed[np.equal.outer(st.session_state.working_seed, player_var2).any(axis=1).all(axis=1)]
650
+ elif player_var1 == 'Full Slate':
651
+ st.session_state.working_seed = dk_lineups.copy()
652
+ st.session_state.data_export_display = pd.DataFrame(st.session_state.working_seed[0:lineup_num_var], columns=column_names)
653
+ elif 'working_seed' not in st.session_state:
654
+ st.session_state.working_seed = dk_lineups.copy()
655
+ st.session_state.working_seed = st.session_state.working_seed
656
+ if player_var1 == 'Specific Players':
657
+ st.session_state.working_seed = st.session_state.working_seed[np.equal.outer(st.session_state.working_seed, player_var2).any(axis=1).all(axis=1)]
658
+ elif player_var1 == 'Full Slate':
659
+ st.session_state.working_seed = dk_lineups.copy()
660
+ st.session_state.data_export_display = pd.DataFrame(st.session_state.working_seed[0:lineup_num_var], columns=column_names)
661
+
662
+ elif site_var == 'Fanduel':
663
+ if 'working_seed' in st.session_state:
664
+ st.session_state.working_seed = st.session_state.working_seed
665
+ if player_var1 == 'Specific Players':
666
+ st.session_state.working_seed = st.session_state.working_seed[np.equal.outer(st.session_state.working_seed, player_var2).any(axis=1).all(axis=1)]
667
+ elif player_var1 == 'Full Slate':
668
+ st.session_state.working_seed = fd_lineups.copy()
669
+ st.session_state.data_export_display = pd.DataFrame(st.session_state.working_seed[0:lineup_num_var], columns=column_names)
670
+ elif 'working_seed' not in st.session_state:
671
+ st.session_state.working_seed = fd_lineups.copy()
672
+ st.session_state.working_seed = st.session_state.working_seed
673
+ if player_var1 == 'Specific Players':
674
+ st.session_state.working_seed = st.session_state.working_seed[np.equal.outer(st.session_state.working_seed, player_var2).any(axis=1).all(axis=1)]
675
+ elif player_var1 == 'Full Slate':
676
+ st.session_state.working_seed = fd_lineups.copy()
677
+ st.session_state.data_export_display = pd.DataFrame(st.session_state.working_seed[0:lineup_num_var], columns=column_names)
678
+ st.session_state.data_export_display = st.session_state.data_export_display[st.session_state.data_export_display['salary'] >= salary_min_var]
679
+ st.session_state.data_export_display = st.session_state.data_export_display[st.session_state.data_export_display['salary'] <= salary_max_var]
680
+ st.session_state.data_export_display = st.session_state.data_export_display[st.session_state.data_export_display['Team_count'] >= min_stacks_var]
681
+ st.session_state.data_export_display = st.session_state.data_export_display[st.session_state.data_export_display['Team_count'] <= max_stacks_var]
682
+ export_file = st.session_state.data_export_display.copy()
683
+ name_export = st.session_state.data_export_display.copy()
684
+ if site_var == 'Draftkings':
685
+ if slate_type_var3 == 'Regular':
686
+ map_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']
687
+ elif slate_type_var3 == 'Showdown':
688
+ map_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5']
689
+ for col_idx in map_columns:
690
+ export_file[col_idx] = export_file[col_idx].map(dk_id_map)
691
+ elif site_var == 'Fanduel':
692
+ if slate_type_var3 == 'Regular':
693
+ map_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST']
694
+ elif slate_type_var3 == 'Showdown':
695
+ map_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5']
696
+ for col_idx in map_columns:
697
+ export_file[col_idx] = export_file[col_idx].map(fd_id_map)
698
+
699
+ with st.container():
700
+ if st.button("Reset Optimals", key='reset3'):
701
+ for key in st.session_state.keys():
702
+ del st.session_state[key]
703
+ if site_var == 'Draftkings':
704
+ st.session_state.working_seed = dk_lineups.copy()
705
+ elif site_var == 'Fanduel':
706
+ st.session_state.working_seed = fd_lineups.copy()
707
+ if 'data_export_display' in st.session_state:
708
+ st.dataframe(st.session_state.data_export_display.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(precision=2), height=500, use_container_width = True)
709
+ st.download_button(
710
+ label="Export display optimals (IDs)",
711
+ data=convert_df(export_file),
712
+ file_name='NFL_display_optimals.csv',
713
+ mime='text/csv',
714
+ )
715
+ st.download_button(
716
+ label="Export display optimals (Names)",
717
+ data=convert_df(name_export),
718
+ file_name='NFL_display_optimals.csv',
719
+ mime='text/csv',
720
+ )
721
+
722
+ with st.container():
723
+ if slate_type_var3 == 'Regular':
724
+ if 'working_seed' in st.session_state:
725
+ # Create a new dataframe with summary statistics
726
+ if site_var == 'Draftkings':
727
+ summary_df = pd.DataFrame({
728
+ 'Metric': ['Min', 'Average', 'Max', 'STDdev'],
729
+ 'Salary': [
730
+ np.min(st.session_state.working_seed[:,9]),
731
+ np.mean(st.session_state.working_seed[:,9]),
732
+ np.max(st.session_state.working_seed[:,9]),
733
+ np.std(st.session_state.working_seed[:,9])
734
+ ],
735
+ 'Proj': [
736
+ np.min(st.session_state.working_seed[:,10]),
737
+ np.mean(st.session_state.working_seed[:,10]),
738
+ np.max(st.session_state.working_seed[:,10]),
739
+ np.std(st.session_state.working_seed[:,10])
740
+ ],
741
+ 'Own': [
742
+ np.min(st.session_state.working_seed[:,15]),
743
+ np.mean(st.session_state.working_seed[:,15]),
744
+ np.max(st.session_state.working_seed[:,15]),
745
+ np.std(st.session_state.working_seed[:,15])
746
+ ]
747
+ })
748
+ elif site_var == 'Fanduel':
749
+ summary_df = pd.DataFrame({
750
+ 'Metric': ['Min', 'Average', 'Max', 'STDdev'],
751
+ 'Salary': [
752
+ np.min(st.session_state.working_seed[:,9]),
753
+ np.mean(st.session_state.working_seed[:,9]),
754
+ np.max(st.session_state.working_seed[:,9]),
755
+ np.std(st.session_state.working_seed[:,9])
756
+ ],
757
+ 'Proj': [
758
+ np.min(st.session_state.working_seed[:,10]),
759
+ np.mean(st.session_state.working_seed[:,10]),
760
+ np.max(st.session_state.working_seed[:,10]),
761
+ np.std(st.session_state.working_seed[:,10])
762
+ ],
763
+ 'Own': [
764
+ np.min(st.session_state.working_seed[:,15]),
765
+ np.mean(st.session_state.working_seed[:,15]),
766
+ np.max(st.session_state.working_seed[:,15]),
767
+ np.std(st.session_state.working_seed[:,15])
768
+ ]
769
+ })
770
+ elif slate_type_var3 == 'Showdown':
771
+ if 'working_seed' in st.session_state:
772
+ # Create a new dataframe with summary statistics
773
+ if site_var == 'Draftkings':
774
+ summary_df = pd.DataFrame({
775
+ 'Metric': ['Min', 'Average', 'Max', 'STDdev'],
776
+ 'Salary': [
777
+ np.min(st.session_state.working_seed[:,6]),
778
+ np.mean(st.session_state.working_seed[:,6]),
779
+ np.max(st.session_state.working_seed[:,6]),
780
+ np.std(st.session_state.working_seed[:,6])
781
+ ],
782
+ 'Proj': [
783
+ np.min(st.session_state.working_seed[:,7]),
784
+ np.mean(st.session_state.working_seed[:,7]),
785
+ np.max(st.session_state.working_seed[:,7]),
786
+ np.std(st.session_state.working_seed[:,7])
787
+ ],
788
+ 'Own': [
789
+ np.min(st.session_state.working_seed[:,12]),
790
+ np.mean(st.session_state.working_seed[:,12]),
791
+ np.max(st.session_state.working_seed[:,12]),
792
+ np.std(st.session_state.working_seed[:,12])
793
+ ]
794
+ })
795
+ elif site_var == 'Fanduel':
796
+ summary_df = pd.DataFrame({
797
+ 'Metric': ['Min', 'Average', 'Max', 'STDdev'],
798
+ 'Salary': [
799
+ np.min(st.session_state.working_seed[:,6]),
800
+ np.mean(st.session_state.working_seed[:,6]),
801
+ np.max(st.session_state.working_seed[:,6]),
802
+ np.std(st.session_state.working_seed[:,6])
803
+ ],
804
+ 'Proj': [
805
+ np.min(st.session_state.working_seed[:,7]),
806
+ np.mean(st.session_state.working_seed[:,7]),
807
+ np.max(st.session_state.working_seed[:,7]),
808
+ np.std(st.session_state.working_seed[:,7])
809
+ ],
810
+ 'Own': [
811
+ np.min(st.session_state.working_seed[:,12]),
812
+ np.mean(st.session_state.working_seed[:,12]),
813
+ np.max(st.session_state.working_seed[:,12]),
814
+ np.std(st.session_state.working_seed[:,12])
815
+ ]
816
+ })
817
+
818
+ # Set the index of the summary dataframe as the "Metric" column
819
+ summary_df = summary_df.set_index('Metric')
820
+
821
+ # Display the summary dataframe
822
+ st.subheader("Optimal Statistics")
823
+ st.dataframe(summary_df.style.format({
824
+ 'Salary': '{:.2f}',
825
+ 'Proj': '{:.2f}',
826
+ 'Own': '{:.2f}'
827
+ }).background_gradient(cmap='RdYlGn', axis=0, subset=['Salary', 'Proj', 'Own']), use_container_width=True)
828
+
829
+ with st.container():
830
+ tab1, tab2 = st.tabs(["Display Frequency", "Seed Frame Frequency"])
831
+ with tab1:
832
+ if 'data_export_display' in st.session_state:
833
+ if site_var == 'Draftkings':
834
+ if slate_type_var3 == 'Regular':
835
+ player_columns = st.session_state.data_export_display.iloc[:, :9]
836
+ elif slate_type_var3 == 'Showdown':
837
+ player_columns = st.session_state.data_export_display.iloc[:, :6]
838
+ elif site_var == 'Fanduel':
839
+ if slate_type_var3 == 'Regular':
840
+ player_columns = st.session_state.data_export_display.iloc[:, :9]
841
+ elif slate_type_var3 == 'Showdown':
842
+ player_columns = st.session_state.data_export_display.iloc[:, :6]
843
+
844
+ # Flatten the DataFrame and count unique values
845
+ value_counts = player_columns.values.flatten().tolist()
846
+ value_counts = pd.Series(value_counts).value_counts()
847
+
848
+ percentages = (value_counts / lineup_num_var * 100).round(2)
849
+
850
+ # Create a DataFrame with the results
851
+ summary_df = pd.DataFrame({
852
+ 'Player': value_counts.index,
853
+ 'Frequency': value_counts.values,
854
+ 'Percentage': percentages.values
855
+ })
856
+
857
+ # Sort by frequency in descending order
858
+ summary_df['Salary'] = summary_df['Player'].map(player_salaries)
859
+ summary_df = summary_df[['Player', 'Salary', 'Frequency', 'Percentage']]
860
+ summary_df = summary_df.sort_values('Frequency', ascending=False)
861
+ summary_df = summary_df.set_index('Player')
862
+
863
+ # Display the table
864
+ st.write("Player Frequency Table:")
865
+ st.dataframe(summary_df.style.format({'Percentage': '{:.2f}%'}), height=500, use_container_width=True)
866
+
867
+ st.download_button(
868
+ label="Export player frequency",
869
+ data=convert_df_to_csv(summary_df),
870
+ file_name='NFL_player_frequency.csv',
871
+ mime='text/csv',
872
+ )
873
+ with tab2:
874
+ if 'working_seed' in st.session_state:
875
+ if site_var == 'Draftkings':
876
+ if slate_type_var3 == 'Regular':
877
+ player_columns = st.session_state.working_seed[:, :9]
878
+ elif slate_type_var3 == 'Showdown':
879
+ player_columns = st.session_state.working_seed[:, :6]
880
+ elif site_var == 'Fanduel':
881
+ if slate_type_var3 == 'Regular':
882
+ player_columns = st.session_state.working_seed[:, :9]
883
+ elif slate_type_var3 == 'Showdown':
884
+ player_columns = st.session_state.working_seed[:, :6]
885
+
886
+ # Flatten the DataFrame and count unique values
887
+ value_counts = player_columns.flatten().tolist()
888
+ value_counts = pd.Series(value_counts).value_counts()
889
+
890
+ percentages = (value_counts / len(st.session_state.working_seed) * 100).round(2)
891
+ # Create a DataFrame with the results
892
+ summary_df = pd.DataFrame({
893
+ 'Player': value_counts.index,
894
+ 'Frequency': value_counts.values,
895
+ 'Percentage': percentages.values
896
+ })
897
+
898
+ # Sort by frequency in descending order
899
+ summary_df['Salary'] = summary_df['Player'].map(player_salaries)
900
+ summary_df = summary_df[['Player', 'Salary', 'Frequency', 'Percentage']]
901
+ summary_df = summary_df.sort_values('Frequency', ascending=False)
902
+ summary_df = summary_df.set_index('Player')
903
+
904
+ # Display the table
905
+ st.write("Seed Frame Frequency Table:")
906
+ st.dataframe(summary_df.style.format({'Percentage': '{:.2f}%'}), height=500, use_container_width=True)
907
+
908
+ st.download_button(
909
+ label="Export seed frame frequency",
910
+ data=convert_df_to_csv(summary_df),
911
+ file_name='NFL_seed_frame_frequency.csv',
912
+ mime='text/csv',
913
+ )
914
+
915
+ with tab4:
916
+ col1, col2 = st.columns(2)
917
+ with col1:
918
+ st.header("Handbuilder")
919
+ with col2:
920
+ slate_var3 = st.selectbox("Slate Selection", options=['Main', 'Secondary', 'Auxiliary'])
921
+ if site_var == 'Draftkings':
922
+ if slate_var3 == 'Main':
923
+ handbuild_roo = dk_roo_raw[dk_roo_raw['slate'] == 'Main Slate']
924
+ elif slate_var3 == 'Secondary':
925
+ handbuild_roo = dk_roo_raw[dk_roo_raw['slate'] == 'Secondary Slate']
926
+ elif slate_var3 == 'Auxiliary':
927
+ handbuild_roo = dk_roo_raw[dk_roo_raw['slate'] == 'Late Slate']
928
+ elif site_var == 'Fanduel':
929
+ if slate_var3 == 'Main':
930
+ handbuild_roo = fd_roo_raw[fd_roo_raw['slate'] == 'Main Slate']
931
+ elif slate_var3 == 'Secondary':
932
+ handbuild_roo = fd_roo_raw[fd_roo_raw['slate'] == 'Secondary Slate']
933
+ elif slate_var3 == 'Auxiliary':
934
+ handbuild_roo = fd_roo_raw[fd_roo_raw['slate'] == 'Late Slate']
935
+
936
+ # --- POSITION LIMITS ---
937
+ if site_var == 'Draftkings':
938
+ position_limits = {
939
+ 'QB': 1,
940
+ 'RB': 2,
941
+ 'WR': 3,
942
+ 'TE': 1,
943
+ 'UTIL': 1,
944
+ 'DST': 1,
945
+ # Add more as needed
946
+ }
947
+ max_salary = 50000
948
+ max_players = 9
949
+ else:
950
+ position_limits = {
951
+ 'QB': 1,
952
+ 'RB': 2,
953
+ 'WR': 3,
954
+ 'TE': 1,
955
+ 'UTIL': 1,
956
+ 'DST': 1,
957
+ # Add more as needed
958
+ }
959
+ max_salary = 60000
960
+ max_players = 9
961
+
962
+ # --- LINEUP STATE ---
963
+ if 'handbuilder_lineup' not in st.session_state:
964
+ st.session_state['handbuilder_lineup'] = pd.DataFrame(columns=['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own'])
965
+ if 'handbuilder_select_key' not in st.session_state:
966
+ st.session_state['handbuilder_select_key'] = 0
967
+
968
+ # Count positions in the current lineup
969
+ lineup = st.session_state['handbuilder_lineup']
970
+ slot_counts = lineup['Slot'].value_counts() if not lineup.empty else {}
971
+
972
+ # --- PLAYER FILTERS ---
973
+ with st.expander("Player Filters"):
974
+ col1, col2 = st.columns(2)
975
+ with col1:
976
+ pos_select3 = st.multiselect("Select your position(s)", options=['QB', 'RB', 'WR', 'TE', 'UTIL', 'DST'], key='pos_select3')
977
+ with col2:
978
+ salary_var = st.number_input("Salary Max", min_value = 0, max_value = 20000, value = 20000, step = 100)
979
+
980
+ # --- TEAM FILTER UI ---
981
+ with st.expander("Team Filters"):
982
+ all_teams = sorted(handbuild_roo['Team'].unique())
983
+ st.markdown("**Toggle teams to include:**")
984
+ team_cols = st.columns(len(all_teams) // 2 + 1)
985
+
986
+ selected_teams = []
987
+ for idx, team in enumerate(all_teams):
988
+ col = team_cols[idx % len(team_cols)]
989
+ if f"handbuilder_team_{team}" not in st.session_state:
990
+ st.session_state[f"handbuilder_team_{team}"] = False
991
+ checked = col.toggle(team, value=st.session_state[f"handbuilder_team_{team}"], key=f"handbuilder_team_{team}")
992
+ if checked:
993
+ selected_teams.append(team)
994
+
995
+ # If no teams selected, show all teams
996
+ if selected_teams:
997
+ player_select_df = handbuild_roo[
998
+ handbuild_roo['Team'].isin(selected_teams)
999
+ ][['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own']].drop_duplicates(subset=['Player', 'Team']).copy()
1000
+ else:
1001
+ player_select_df = handbuild_roo[['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own']].drop_duplicates(subset=['Player', 'Team']).copy()
1002
+
1003
+ # If no teams selected, show all teams
1004
+ if pos_select3:
1005
+ position_mask_2 = handbuild_roo['Position'].apply(lambda x: any(pos in x for pos in pos_select3))
1006
+ player_select_df = player_select_df[position_mask_2][['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own']].drop_duplicates(subset=['Player', 'Team']).copy()
1007
+ else:
1008
+ player_select_df = player_select_df[['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own']].drop_duplicates(subset=['Player', 'Team']).copy()
1009
+
1010
+ player_select_df = player_select_df[player_select_df['Salary'] <= salary_var]
1011
+
1012
+
1013
+ with st.expander("Quick Fill Options"):
1014
+ auto_team_var = st.selectbox("Auto Fill Team", options=all_teams)
1015
+ auto_size_var = st.selectbox("Auto Fill Size", options=[3, 4, 5])
1016
+ auto_range_var = st.selectbox("Auto Fill Options", options=['QB/WR', 'RB/WR/TE', 'QB/WR/TE/RB'])
1017
+ # --- QUICK FILL LOGIC ---
1018
+ if st.button("Quick Fill", key="quick_fill"):
1019
+ # 1. Get all eligible players from the selected team, not already in the lineup
1020
+ current_players = set(st.session_state['handbuilder_lineup']['Player'])
1021
+ team_players = player_select_df[
1022
+ (player_select_df['Team'] == auto_team_var) &
1023
+ (~player_select_df['Player'].isin(current_players))
1024
+ ].copy()
1025
+
1026
+ # 2. Sort by Order
1027
+ team_players = team_players.sort_values(by='Median', ascending=False)
1028
+
1029
+ # 3. Select the order range
1030
+ if auto_range_var == 'QB/WR':
1031
+ selected_players = team_players[team_players['Position'] == 'QB'].head(1)
1032
+ selected_players = pd.concat([selected_players, team_players[team_players['Position'] == 'WR'].head(auto_size_var - 1)])
1033
+ if len(selected_players) < auto_size_var:
1034
+ team_players = player_select_df[
1035
+ (player_select_df['Team'] == auto_team_var) &
1036
+ (~player_select_df['Player'].isin(current_players))
1037
+ ].copy()
1038
+
1039
+ # 2. Sort by Order
1040
+ team_players = team_players.sort_values(by='Median', ascending=False)
1041
+ selected_players = team_players.head(auto_size_var + 1)
1042
+ elif auto_range_var == 'QB/WR/TE':
1043
+ selected_players = team_players[team_players['Position'] == 'QB'].head(1)
1044
+ selected_players = pd.concat([selected_players, team_players[team_players['Position'].isin(['WR', 'TE'])].head(auto_size_var - 1)])
1045
+ if len(selected_players) < auto_size_var:
1046
+ team_players = player_select_df[
1047
+ (player_select_df['Team'] == auto_team_var) &
1048
+ (~player_select_df['Player'].isin(current_players))
1049
+ ].copy()
1050
+
1051
+ # 2. Sort by Order
1052
+ team_players = team_players.sort_values(by='Median', ascending=False)
1053
+ selected_players = team_players.head(auto_size_var + 1)
1054
+ elif auto_range_var == 'QB/WR/TE/RB':
1055
+ selected_players = team_players[team_players['Position'] == 'QB'].head(1)
1056
+ selected_players = pd.concat([selected_players, team_players[team_players['Position'].isin(['RB', 'WR', 'TE'])].head(auto_size_var - 1)])
1057
+ if len(selected_players) < auto_size_var:
1058
+ team_players = player_select_df[
1059
+ (player_select_df['Team'] == auto_team_var) &
1060
+ (~player_select_df['Player'].isin(current_players))
1061
+ ].copy()
1062
+
1063
+ # 2. Sort by Order
1064
+ team_players = team_players.sort_values(by='Median', ascending=False)
1065
+ selected_players = team_players.head(auto_size_var + 1)
1066
+ else:
1067
+ selected_players = team_players.head(auto_size_var)
1068
+
1069
+ # 4. Add each player to the lineup, filling the first available eligible slot
1070
+ for _, player_row in selected_players.iterrows():
1071
+ eligible_positions = re.split(r'[/, ]+', player_row['Position'])
1072
+ slot_to_fill = None
1073
+
1074
+ for slot in ['QB', 'RB', 'WR', 'TE', 'UTIL', 'DST']:
1075
+ if slot_counts.get(slot, 0) < position_limits.get(slot, 0):
1076
+ if slot == 'UTIL':
1077
+ if 'DST' not in eligible_positions and 'QB' not in eligible_positions:
1078
+ slot_to_fill = slot
1079
+ break
1080
+ elif slot in eligible_positions:
1081
+ slot_to_fill = slot
1082
+ break
1083
+
1084
+ if slot_to_fill is not None:
1085
+ # Avoid duplicates
1086
+ if player_row['Player'] not in st.session_state['handbuilder_lineup']['Player'].values:
1087
+ add_row = player_row.copy()
1088
+ add_row['Slot'] = slot_to_fill
1089
+ st.session_state['handbuilder_lineup'] = pd.concat(
1090
+ [st.session_state['handbuilder_lineup'], pd.DataFrame([add_row[[
1091
+ 'Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own', 'Slot'
1092
+ ]]])],
1093
+ ignore_index=True
1094
+ )
1095
+ # Update slot_counts for next player
1096
+ slot_counts[slot_to_fill] = slot_counts.get(slot_to_fill, 0) + 1
1097
+ st.rerun()
1098
+
1099
+ # --- FILTER OUT PLAYERS WHOSE ALL ELIGIBLE POSITIONS ARE FILLED ---
1100
+ def is_player_eligible(row):
1101
+ eligible_positions = re.split(r'[/, ]+', row['Position'])
1102
+ # Player is eligible if at least one of their positions is not at max
1103
+ for pos in eligible_positions:
1104
+ if slot_counts.get(pos, 0) < position_limits.get(pos, 0):
1105
+ return True
1106
+ return False
1107
+
1108
+ # player_select_df = player_select_df[player_select_df.apply(is_player_eligible, axis=1)]
1109
+ print(player_select_df.head(10))
1110
+
1111
+ col1, col2 = st.columns([1, 2])
1112
+ with col2:
1113
+ st.subheader("Player Select")
1114
+ event = st.dataframe(
1115
+ player_select_df.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').background_gradient(cmap='RdYlGn_r', subset=['Salary', 'Own']).format(precision=2),
1116
+ on_select="rerun",
1117
+ selection_mode=["single-row"],
1118
+ key=f"handbuilder_select_{st.session_state['handbuilder_select_key']}",
1119
+ height=500,
1120
+ hide_index=True
1121
+ )
1122
+ # If a row is selected, add that player to the lineup and reset selection
1123
+ if event and "rows" in event.selection and len(event.selection["rows"]) > 0:
1124
+ idx = event.selection["rows"][0]
1125
+ player_row = player_select_df.iloc[[idx]]
1126
+ eligible_positions = re.split(r'[/, ]+', player_row['Position'].iloc[0])
1127
+ # Find the first eligible slot that is not full
1128
+ slot_to_fill = None
1129
+
1130
+ for slot in ['QB', 'RB', 'WR', 'TE', 'UTIL', 'DST']:
1131
+ if slot_counts.get(slot, 0) < position_limits.get(slot, 0):
1132
+ if slot == 'UTIL':
1133
+ if 'DST' not in eligible_positions and 'QB' not in eligible_positions:
1134
+ slot_to_fill = slot
1135
+ break
1136
+ elif slot in eligible_positions:
1137
+ slot_to_fill = slot
1138
+ break
1139
+
1140
+ if slot_to_fill is not None:
1141
+ # Avoid duplicates
1142
+ if not player_row['Player'].iloc[0] in st.session_state['handbuilder_lineup']['Player'].values:
1143
+ # Add the slot info
1144
+ player_row = player_row.assign(Slot=slot_to_fill)
1145
+ st.session_state['handbuilder_lineup'] = pd.concat(
1146
+ [st.session_state['handbuilder_lineup'], player_row[['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own', 'Slot']]],
1147
+ ignore_index=True
1148
+ )
1149
+ st.session_state['handbuilder_select_key'] += 1
1150
+ st.rerun()
1151
+
1152
+
1153
+ with col1:
1154
+ st.subheader("Lineup Build")
1155
+
1156
+ # --- EXPLICIT LINEUP ORDER ---
1157
+ if site_var == 'Draftkings':
1158
+ lineup_slots = ['QB', 'RB', 'RB', 'WR', 'WR', 'WR', 'TE', 'UTIL', 'DST']
1159
+ else:
1160
+ lineup_slots = ['QB', 'RB', 'RB', 'WR', 'WR', 'WR', 'TE', 'UTIL', 'DST']
1161
+ display_columns = ['Slot', 'Player', 'Position', 'Team', 'Salary', 'Median', 'Own']
1162
+
1163
+ filled_lineup = st.session_state['handbuilder_lineup']
1164
+ display_rows = []
1165
+ used_indices = set()
1166
+ if not filled_lineup.empty:
1167
+ for slot in lineup_slots:
1168
+ match = filled_lineup[(filled_lineup['Slot'] == slot) & (~filled_lineup.index.isin(used_indices))]
1169
+ if not match.empty:
1170
+ row = match.iloc[0]
1171
+ used_indices.add(match.index[0])
1172
+ display_rows.append({
1173
+ 'Slot': slot,
1174
+ 'Player': row['Player'],
1175
+ 'Position': row['Position'],
1176
+ 'Team': row['Team'],
1177
+ 'Salary': row['Salary'],
1178
+ 'Median': row['Median'],
1179
+ '2x%': row['2x%'],
1180
+ 'Own': row['Own']
1181
+ })
1182
+ else:
1183
+ display_rows.append({
1184
+ 'Slot': slot,
1185
+ 'Player': '',
1186
+ 'Position': '',
1187
+ 'Team': '',
1188
+ 'Salary': np.nan,
1189
+ 'Median': np.nan,
1190
+ '2x%': np.nan,
1191
+ 'Own': np.nan
1192
+ })
1193
+
1194
+ lineup_display_df = pd.DataFrame(display_rows, columns=display_columns)
1195
+
1196
+ # Show the lineup table with single-row selection for removal
1197
+ event_remove = st.dataframe(
1198
+ 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),
1199
+ on_select="rerun",
1200
+ selection_mode=["single-row"],
1201
+ key="lineup_remove",
1202
+ height=445,
1203
+ hide_index=True
1204
+ )
1205
+
1206
+ # If a row is selected and not blank, remove that player from the lineup
1207
+ if event_remove and "rows" in event_remove.selection and len(event_remove.selection["rows"]) > 0:
1208
+ idx = event_remove.selection["rows"][0]
1209
+ player_to_remove = lineup_display_df.iloc[idx]['Player']
1210
+ slot_to_remove = lineup_display_df.iloc[idx]['Slot']
1211
+ if player_to_remove: # Only remove if not blank
1212
+ st.session_state['handbuilder_lineup'] = filled_lineup[
1213
+ ~((filled_lineup['Player'] == player_to_remove) & (filled_lineup['Slot'] == slot_to_remove))
1214
+ ]
1215
+ st.rerun()
1216
+
1217
+ # --- SUMMARY ROW ---
1218
+ if not filled_lineup.empty:
1219
+ total_salary = filled_lineup['Salary'].sum()
1220
+ total_median = filled_lineup['Median'].sum()
1221
+ avg_2x = filled_lineup['2x%'].mean()
1222
+ total_own = filled_lineup['Own'].sum()
1223
+ most_common_team = filled_lineup['Team'].mode()[0] if not filled_lineup['Team'].mode().empty else ""
1224
+
1225
+ summary_row = pd.DataFrame({
1226
+ 'Slot': [''],
1227
+ 'Player': ['TOTAL'],
1228
+ 'Position': [''],
1229
+ 'Team': [most_common_team],
1230
+ 'Salary': [total_salary],
1231
+ 'Median': [total_median],
1232
+ '2x%': [avg_2x],
1233
+ 'Own': [total_own]
1234
+ })
1235
+ summary_row = summary_row[['Salary', 'Median', 'Own']].head(max_players)
1236
+
1237
+ col1, col3 = st.columns([2, 3])
1238
+
1239
+ with col1:
1240
+ if (max_players - len(filled_lineup)) > 0:
1241
+ st.markdown(f"""
1242
+ <div style='text-align: left; vertical-align: top; margin-top: 0; padding-top: 0;''>
1243
+ <b>πŸ’° Per Player:</b> ${round((max_salary - total_salary) / (max_players - len(filled_lineup)), 0)} &nbsp;
1244
+ </div>
1245
+ """,
1246
+ unsafe_allow_html=True)
1247
+ else:
1248
+ st.markdown(f"""
1249
+ <div style='text-align: left; vertical-align: top; margin-top: 0; padding-top: 0;''>
1250
+ <b>πŸ’° Leftover:</b> ${round(max_salary - total_salary, 0)} &nbsp;
1251
+ </div>
1252
+ """,
1253
+ unsafe_allow_html=True)
1254
+
1255
+ with col3:
1256
+ if total_salary <= max_salary:
1257
+ st.markdown(
1258
+ f"""
1259
+ <div style='text-align: right; vertical-align: top; margin-top: 0; padding-top: 0;''>
1260
+ <b>πŸ’° Salary:</b> ${round(total_salary, 0)} &nbsp;
1261
+ <b>πŸ”₯ Median:</b> {round(total_median, 2)} &nbsp;
1262
+ </div>
1263
+ """,
1264
+ unsafe_allow_html=True
1265
+ )
1266
+ else:
1267
+ st.markdown(
1268
+ f"""
1269
+ <div style='text-align: right; vertical-align: top; margin-top: 0; padding-top: 0;''>
1270
+ <b>❌ Salary:</b> ${round(total_salary, 0)} &nbsp;
1271
+ <b>πŸ”₯ Median:</b> {round(total_median, 2)} &nbsp;
1272
+ </div>
1273
+ """,
1274
+ unsafe_allow_html=True
1275
+ )
1276
 
1277
+ # Optionally, add a button to clear the lineup
1278
+ clear_col, save_col, export_col, clear_saved_col, blank_col = st.columns([2, 2, 2, 2, 12])
1279
+ with clear_col:
1280
+ if st.button("Clear Lineup", key='clear_lineup'):
1281
+ st.session_state['handbuilder_lineup'] = pd.DataFrame(columns=['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own', 'Slot'])
1282
+ st.rerun()
1283
+ with save_col:
1284
+ if st.button("Save Lineup", key='save_lineup'):
1285
+ if 'saved_lineups' in st.session_state:
1286
+ st.session_state['saved_lineups'].append(lineup_display_df['Player'].tolist())
1287
+ print(st.session_state['saved_lineups'])
1288
+ else:
1289
+ st.session_state['saved_lineups'] = [lineup_display_df['Player'].tolist()]
1290
+ print(st.session_state['saved_lineups'])
1291
+ st.rerun()
1292
+ with export_col:
1293
+ if 'saved_lineups' in st.session_state and st.session_state['saved_lineups']:
1294
+ # Convert list of lists to numpy array
1295
+ saved_lineups_array = np.array(st.session_state['saved_lineups'])
1296
+ st.download_button(
1297
+ label="Export Handbuilds",
1298
+ data=convert_hb_df(saved_lineups_array, dk_hb_columns if site_var == 'Draftkings' else fd_hb_columns),
1299
+ file_name='handbuilds_export.csv',
1300
+ mime='text/csv',
1301
+ )
1302
+ else:
1303
+ st.write("No saved lineups to export")
1304
+
1305
+ if 'saved_lineups' in st.session_state:
1306
+ st.table(pd.DataFrame(st.session_state['saved_lineups'], columns=dk_hb_columns if site_var == 'Draftkings' else fd_hb_columns))
1307
+ else:
1308
+ st.write("No saved lineups")
1309
+
1310
+ with clear_saved_col:
1311
+ if st.button("Clear Saved", key='clear_saved'):
1312
+ if 'saved_lineups' in st.session_state:
1313
+ del st.session_state['saved_lineups']
1314
+ st.rerun()