Spaces:
Sleeping
Sleeping
Pragya Jatav
commited on
Commit
·
69b14e2
1
Parent(s):
85d2c7e
Delete two files
Browse files- pages/8_Scenario_Planner.py +0 -1515
- pages/9_Saved_Scenarios.py +0 -280
pages/8_Scenario_Planner.py
DELETED
@@ -1,1515 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
from numerize.numerize import numerize
|
3 |
-
import numpy as np
|
4 |
-
from functools import partial
|
5 |
-
from collections import OrderedDict
|
6 |
-
from plotly.subplots import make_subplots
|
7 |
-
import plotly.graph_objects as go
|
8 |
-
from utilities import (
|
9 |
-
format_numbers,format_numbers_f,
|
10 |
-
load_local_css,
|
11 |
-
set_header,
|
12 |
-
initialize_data,
|
13 |
-
load_authenticator,
|
14 |
-
send_email,
|
15 |
-
channel_name_formating,
|
16 |
-
)
|
17 |
-
from classes import class_from_dict, class_to_dict
|
18 |
-
import pickle
|
19 |
-
import streamlit_authenticator as stauth
|
20 |
-
import yaml
|
21 |
-
from yaml import SafeLoader
|
22 |
-
import re
|
23 |
-
import pandas as pd
|
24 |
-
import plotly.express as px
|
25 |
-
# import response_curves_model_quality as rc
|
26 |
-
|
27 |
-
st.set_page_config(layout="wide")
|
28 |
-
load_local_css("styles.css")
|
29 |
-
set_header()
|
30 |
-
|
31 |
-
|
32 |
-
for k, v in st.session_state.items():
|
33 |
-
if k not in ["logout", "login", "config"] and not k.startswith("FormSubmitter"):
|
34 |
-
st.session_state[k] = v
|
35 |
-
# ======================================================== #
|
36 |
-
# ======================= Functions ====================== #
|
37 |
-
# ======================================================== #
|
38 |
-
|
39 |
-
|
40 |
-
def optimize(key, status_placeholder):
|
41 |
-
"""
|
42 |
-
Optimize the spends for the sales
|
43 |
-
"""
|
44 |
-
|
45 |
-
channel_list = [
|
46 |
-
key for key, value in st.session_state["optimization_channels"].items() if value
|
47 |
-
]
|
48 |
-
|
49 |
-
if len(channel_list) > 0:
|
50 |
-
scenario = st.session_state["scenario"]
|
51 |
-
if key.lower() == "media spends":
|
52 |
-
with status_placeholder:
|
53 |
-
with st.spinner("Optimizing"):
|
54 |
-
result = st.session_state["scenario"].optimize(
|
55 |
-
st.session_state["total_spends_change"], channel_list
|
56 |
-
)
|
57 |
-
# elif key.lower() == "revenue":
|
58 |
-
else:
|
59 |
-
with status_placeholder:
|
60 |
-
with st.spinner("Optimizing"):
|
61 |
-
|
62 |
-
result = st.session_state["scenario"].optimize_spends(
|
63 |
-
st.session_state["total_sales_change"], channel_list
|
64 |
-
)
|
65 |
-
for channel_name, modified_spends in result:
|
66 |
-
|
67 |
-
st.session_state[channel_name] = numerize(
|
68 |
-
modified_spends * scenario.channels[channel_name].conversion_rate,
|
69 |
-
1,
|
70 |
-
)
|
71 |
-
prev_spends = (
|
72 |
-
st.session_state["scenario"].channels[channel_name].actual_total_spends
|
73 |
-
)
|
74 |
-
st.session_state[f"{channel_name}_change"] = round(
|
75 |
-
100 * (modified_spends - prev_spends) / prev_spends, 2
|
76 |
-
)
|
77 |
-
|
78 |
-
|
79 |
-
def save_scenario(scenario_name):
|
80 |
-
"""
|
81 |
-
Save the current scenario with the mentioned name in the session state
|
82 |
-
|
83 |
-
Parameters
|
84 |
-
----------
|
85 |
-
scenario_name
|
86 |
-
Name of the scenario to be saved
|
87 |
-
"""
|
88 |
-
if "saved_scenarios" not in st.session_state:
|
89 |
-
st.session_state = OrderedDict()
|
90 |
-
|
91 |
-
# st.session_state['saved_scenarios'][scenario_name] = st.session_state['scenario'].save()
|
92 |
-
st.session_state["saved_scenarios"][scenario_name] = class_to_dict(
|
93 |
-
st.session_state["scenario"]
|
94 |
-
)
|
95 |
-
st.session_state["scenario_input"] = ""
|
96 |
-
# print(type(st.session_state['saved_scenarios']))
|
97 |
-
with open("../saved_scenarios.pkl", "wb") as f:
|
98 |
-
pickle.dump(st.session_state["saved_scenarios"], f)
|
99 |
-
|
100 |
-
|
101 |
-
if "allow_spends_update" not in st.session_state:
|
102 |
-
st.session_state["allow_spends_update"] = True
|
103 |
-
|
104 |
-
if "allow_sales_update" not in st.session_state:
|
105 |
-
st.session_state["allow_sales_update"] = True
|
106 |
-
|
107 |
-
|
108 |
-
def update_sales_abs_slider():
|
109 |
-
actual_sales = _scenario.actual_total_sales
|
110 |
-
if validate_input(st.session_state["total_sales_change_abs_slider"]):
|
111 |
-
modified_sales = extract_number_for_string(
|
112 |
-
st.session_state["total_sales_change_abs_slider"]
|
113 |
-
)
|
114 |
-
st.session_state["total_sales_change"] = round(
|
115 |
-
((modified_sales / actual_sales) - 1) * 100
|
116 |
-
)
|
117 |
-
st.session_state["total_sales_change_abs"] = numerize(modified_sales, 1)
|
118 |
-
|
119 |
-
|
120 |
-
def update_sales_abs():
|
121 |
-
if (
|
122 |
-
st.session_state["total_sales_change_abs"]
|
123 |
-
in st.session_state["total_sales_change_abs_slider_options"]
|
124 |
-
):
|
125 |
-
st.session_state["allow_sales_update"] = True
|
126 |
-
else:
|
127 |
-
st.session_state["allow_sales_update"] = False
|
128 |
-
|
129 |
-
actual_sales = _scenario.actual_total_sales
|
130 |
-
if (
|
131 |
-
validate_input(st.session_state["total_sales_change_abs"])
|
132 |
-
and st.session_state["allow_sales_update"]
|
133 |
-
):
|
134 |
-
modified_sales = extract_number_for_string(
|
135 |
-
st.session_state["total_sales_change_abs"]
|
136 |
-
)
|
137 |
-
st.session_state["total_sales_change"] = round(
|
138 |
-
((modified_sales / actual_sales) - 1) * 100
|
139 |
-
)
|
140 |
-
st.session_state["total_sales_change_abs_slider"] = numerize(modified_sales, 1)
|
141 |
-
|
142 |
-
|
143 |
-
def update_sales():
|
144 |
-
st.session_state["total_sales_change_abs"] = numerize(
|
145 |
-
(1 + st.session_state["total_sales_change"] / 100)
|
146 |
-
* _scenario.actual_total_sales,
|
147 |
-
1,
|
148 |
-
)
|
149 |
-
st.session_state["total_sales_change_abs_slider"] = numerize(
|
150 |
-
(1 + st.session_state["total_sales_change"] / 100)
|
151 |
-
* _scenario.actual_total_sales,
|
152 |
-
1,
|
153 |
-
)
|
154 |
-
|
155 |
-
|
156 |
-
def update_all_spends_abs_slider():
|
157 |
-
actual_spends = _scenario.actual_total_spends
|
158 |
-
if validate_input(st.session_state["total_spends_change_abs_slider"]):
|
159 |
-
modified_spends = extract_number_for_string(
|
160 |
-
st.session_state["total_spends_change_abs_slider"]
|
161 |
-
)
|
162 |
-
st.session_state["total_spends_change"] = round(
|
163 |
-
((modified_spends / actual_spends) - 1) * 100
|
164 |
-
)
|
165 |
-
st.session_state["total_spends_change_abs"] = numerize(modified_spends, 1)
|
166 |
-
|
167 |
-
update_all_spends()
|
168 |
-
|
169 |
-
|
170 |
-
# def update_all_spends_abs_slider():
|
171 |
-
# actual_spends = _scenario.actual_total_spends
|
172 |
-
# if validate_input(st.session_state["total_spends_change_abs_slider"]):
|
173 |
-
# print("#" * 100)
|
174 |
-
# print(st.session_state["total_spends_change_abs_slider"])C:\Users\PragyaJatav\Downloads\Untitled Folder 2\simulatorAldi\pages\8_Scenario_Planner.py
|
175 |
-
# print("#" * 100)
|
176 |
-
|
177 |
-
# modified_spends = extract_number_for_string(
|
178 |
-
# st.session_state["total_spends_change_abs_slider"]
|
179 |
-
# )
|
180 |
-
# st.session_state["total_spends_change"] = (
|
181 |
-
# (modified_spends / actual_spends) - 1
|
182 |
-
# ) * 100
|
183 |
-
# st.session_state["total_spends_change_abs"] = st.session_state[
|
184 |
-
# "total_spends_change_abs_slider"
|
185 |
-
# ]
|
186 |
-
|
187 |
-
# update_all_spends()
|
188 |
-
|
189 |
-
|
190 |
-
def update_all_spends_abs():
|
191 |
-
if (
|
192 |
-
st.session_state["total_spends_change_abs"]
|
193 |
-
in st.session_state["total_spends_change_abs_slider_options"]
|
194 |
-
):
|
195 |
-
st.session_state["allow_spends_update"] = True
|
196 |
-
else:
|
197 |
-
st.session_state["allow_spends_update"] = False
|
198 |
-
|
199 |
-
actual_spends = _scenario.actual_total_spends
|
200 |
-
if (
|
201 |
-
validate_input(st.session_state["total_spends_change_abs"])
|
202 |
-
and st.session_state["allow_spends_update"]
|
203 |
-
):
|
204 |
-
modified_spends = extract_number_for_string(
|
205 |
-
st.session_state["total_spends_change_abs"]
|
206 |
-
)
|
207 |
-
st.session_state["total_spends_change"] = (
|
208 |
-
(modified_spends / actual_spends) - 1
|
209 |
-
) * 100
|
210 |
-
st.session_state["total_spends_change_abs_slider"] = st.session_state[
|
211 |
-
"total_spends_change_abs"
|
212 |
-
]
|
213 |
-
|
214 |
-
update_all_spends()
|
215 |
-
|
216 |
-
|
217 |
-
def update_spends():
|
218 |
-
st.session_state["total_spends_change_abs"] = numerize(
|
219 |
-
(1 + st.session_state["total_spends_change"] / 100)
|
220 |
-
* _scenario.actual_total_spends,
|
221 |
-
1,
|
222 |
-
)
|
223 |
-
st.session_state["total_spends_change_abs_slider"] = numerize(
|
224 |
-
(1 + st.session_state["total_spends_change"] / 100)
|
225 |
-
* _scenario.actual_total_spends,
|
226 |
-
1,
|
227 |
-
)
|
228 |
-
|
229 |
-
update_all_spends()
|
230 |
-
|
231 |
-
|
232 |
-
def update_all_spends():
|
233 |
-
"""
|
234 |
-
Updates spends for all the channels with the given overall spends change
|
235 |
-
"""
|
236 |
-
percent_change = st.session_state["total_spends_change"]
|
237 |
-
|
238 |
-
for channel_name in st.session_state["channels_list"]:
|
239 |
-
channel = st.session_state["scenario"].channels[channel_name]
|
240 |
-
current_spends = channel.actual_total_spends
|
241 |
-
modified_spends = (1 + percent_change / 100) * current_spends
|
242 |
-
st.session_state["scenario"].update(channel_name, modified_spends)
|
243 |
-
st.session_state[channel_name] = numerize(
|
244 |
-
modified_spends * channel.conversion_rate, 1
|
245 |
-
)
|
246 |
-
st.session_state[f"{channel_name}_change"] = percent_change
|
247 |
-
|
248 |
-
|
249 |
-
def extract_number_for_string(string_input):
|
250 |
-
string_input = string_input.upper()
|
251 |
-
if string_input.endswith("K"):
|
252 |
-
return float(string_input[:-1]) * 10**3
|
253 |
-
elif string_input.endswith("M"):
|
254 |
-
return float(string_input[:-1]) * 10**6
|
255 |
-
elif string_input.endswith("B"):
|
256 |
-
return float(string_input[:-1]) * 10**9
|
257 |
-
|
258 |
-
|
259 |
-
def validate_input(string_input):
|
260 |
-
pattern = r"\d+\.?\d*[K|M|B]$"
|
261 |
-
match = re.match(pattern, string_input)
|
262 |
-
if match is None:
|
263 |
-
return False
|
264 |
-
return True
|
265 |
-
|
266 |
-
|
267 |
-
def update_data_by_percent(channel_name):
|
268 |
-
prev_spends = (
|
269 |
-
st.session_state["scenario"].channels[channel_name].actual_total_spends
|
270 |
-
* st.session_state["scenario"].channels[channel_name].conversion_rate
|
271 |
-
)
|
272 |
-
modified_spends = prev_spends * (
|
273 |
-
1 + st.session_state[f"{channel_name}_change"] / 100
|
274 |
-
)
|
275 |
-
st.session_state[channel_name] = numerize(modified_spends, 1)
|
276 |
-
st.session_state["scenario"].update(
|
277 |
-
channel_name,
|
278 |
-
modified_spends
|
279 |
-
/ st.session_state["scenario"].channels[channel_name].conversion_rate,
|
280 |
-
)
|
281 |
-
|
282 |
-
|
283 |
-
def update_data(channel_name):
|
284 |
-
"""
|
285 |
-
Updates the spends for the given channel
|
286 |
-
"""
|
287 |
-
|
288 |
-
if validate_input(st.session_state[channel_name]):
|
289 |
-
modified_spends = extract_number_for_string(st.session_state[channel_name])
|
290 |
-
prev_spends = (
|
291 |
-
st.session_state["scenario"].channels[channel_name].actual_total_spends
|
292 |
-
* st.session_state["scenario"].channels[channel_name].conversion_rate
|
293 |
-
)
|
294 |
-
st.session_state[f"{channel_name}_change"] = round(
|
295 |
-
100 * (modified_spends - prev_spends) / prev_spends, 2
|
296 |
-
)
|
297 |
-
st.session_state["scenario"].update(
|
298 |
-
channel_name,
|
299 |
-
modified_spends
|
300 |
-
/ st.session_state["scenario"].channels[channel_name].conversion_rate,
|
301 |
-
)
|
302 |
-
# st.session_state['scenario'].update(channel_name, modified_spends)
|
303 |
-
# else:
|
304 |
-
# try:
|
305 |
-
# modified_spends = float(st.session_state[channel_name])
|
306 |
-
# prev_spends = st.session_state['scenario'].channels[channel_name].actual_total_spends * st.session_state['scenario'].channels[channel_name].conversion_rate
|
307 |
-
# st.session_state[f'{channel_name}_change'] = round(100*(modified_spends - prev_spends) / prev_spends,2)
|
308 |
-
# st.session_state['scenario'].update(channel_name, modified_spends/st.session_state['scenario'].channels[channel_name].conversion_rate)
|
309 |
-
# st.session_state[f'{channel_name}'] = numerize(modified_spends,1)
|
310 |
-
# except ValueError:
|
311 |
-
# st.write('Invalid input')
|
312 |
-
|
313 |
-
|
314 |
-
def select_channel_for_optimization(channel_name):
|
315 |
-
"""
|
316 |
-
Marks the given channel for optimization
|
317 |
-
"""
|
318 |
-
st.session_state["optimization_channels"][channel_name] = st.session_state[
|
319 |
-
f"{channel_name}_selected"
|
320 |
-
]
|
321 |
-
|
322 |
-
|
323 |
-
def select_all_channels_for_optimization():
|
324 |
-
"""
|
325 |
-
Marks all the channel for optimization
|
326 |
-
"""
|
327 |
-
for channel_name in st.session_state["optimization_channels"].keys():
|
328 |
-
st.session_state[f"{channel_name}_selected"] = st.session_state[
|
329 |
-
"optimze_all_channels"
|
330 |
-
]
|
331 |
-
st.session_state["optimization_channels"][channel_name] = st.session_state[
|
332 |
-
"optimze_all_channels"
|
333 |
-
]
|
334 |
-
|
335 |
-
|
336 |
-
def update_penalty():
|
337 |
-
"""
|
338 |
-
Updates the penalty flag for sales calculation
|
339 |
-
"""
|
340 |
-
st.session_state["scenario"].update_penalty(st.session_state["apply_penalty"])
|
341 |
-
|
342 |
-
|
343 |
-
def reset_scenario(panel_selected, file_selected, updated_rcs):
|
344 |
-
# #print(st.session_state['default_scenario_dict'])
|
345 |
-
# st.session_state['scenario'] = class_from_dict(st.session_state['default_scenario_dict'])
|
346 |
-
# for channel in st.session_state['scenario'].channels.values():
|
347 |
-
# st.session_state[channel.name] = float(channel.actual_total_spends * channel.conversion_rate)
|
348 |
-
# initialize_data()
|
349 |
-
|
350 |
-
if panel_selected == "Total Market":
|
351 |
-
initialize_data(
|
352 |
-
panel=panel_selected,
|
353 |
-
target_file=file_selected,
|
354 |
-
updated_rcs=updated_rcs,
|
355 |
-
metrics=metrics_selected,
|
356 |
-
)
|
357 |
-
panel = None
|
358 |
-
else:
|
359 |
-
initialize_data(
|
360 |
-
panel=panel_selected,
|
361 |
-
target_file=file_selected,
|
362 |
-
updated_rcs=updated_rcs,
|
363 |
-
metrics=metrics_selected,
|
364 |
-
)
|
365 |
-
|
366 |
-
for channel_name in st.session_state["channels_list"]:
|
367 |
-
st.session_state[f"{channel_name}_selected"] = False
|
368 |
-
st.session_state[f"{channel_name}_change"] = 0
|
369 |
-
st.session_state["optimze_all_channels"] = False
|
370 |
-
|
371 |
-
st.session_state["total_sales_change"] = 0
|
372 |
-
|
373 |
-
update_spends()
|
374 |
-
update_sales()
|
375 |
-
|
376 |
-
reset_inputs()
|
377 |
-
|
378 |
-
# st.rerun()
|
379 |
-
|
380 |
-
|
381 |
-
def format_number(num):
|
382 |
-
if num >= 1_000_000:
|
383 |
-
return f"{num / 1_000_000:.2f}M"
|
384 |
-
elif num >= 1_000:
|
385 |
-
return f"{num / 1_000:.0f}K"
|
386 |
-
else:
|
387 |
-
return f"{num:.2f}"
|
388 |
-
|
389 |
-
|
390 |
-
def summary_plot(data, x, y, title, text_column):
|
391 |
-
fig = px.bar(
|
392 |
-
data,
|
393 |
-
x=x,
|
394 |
-
y=y,
|
395 |
-
orientation="h",
|
396 |
-
title=title,
|
397 |
-
text=text_column,
|
398 |
-
color="Channel_name",
|
399 |
-
)
|
400 |
-
|
401 |
-
# Convert text_column to numeric values
|
402 |
-
data[text_column] = pd.to_numeric(data[text_column], errors="coerce")
|
403 |
-
|
404 |
-
# Update the format of the displayed text based on magnitude
|
405 |
-
fig.update_traces(
|
406 |
-
texttemplate="%{text:.2s}",
|
407 |
-
textposition="outside",
|
408 |
-
hovertemplate="%{x:.2s}",
|
409 |
-
)
|
410 |
-
|
411 |
-
fig.update_layout(xaxis_title=x, yaxis_title="Channel Name", showlegend=False)
|
412 |
-
return fig
|
413 |
-
|
414 |
-
|
415 |
-
def s_curve(x, K, b, a, x0):
|
416 |
-
return K / (1 + b * np.exp(-a * (x - x0)))
|
417 |
-
|
418 |
-
|
419 |
-
def find_segment_value(x, roi, mroi):
|
420 |
-
start_value = x[0]
|
421 |
-
end_value = x[len(x) - 1]
|
422 |
-
|
423 |
-
# Condition for green region: Both MROI and ROI > 1
|
424 |
-
green_condition = (roi > 1) & (mroi > 1)
|
425 |
-
left_indices = np.where(green_condition)[0]
|
426 |
-
left_value = x[left_indices[0]] if left_indices.size > 0 else x[0]
|
427 |
-
|
428 |
-
right_indices = np.where(green_condition)[0]
|
429 |
-
right_value = x[right_indices[-1]] if right_indices.size > 0 else x[0]
|
430 |
-
|
431 |
-
return start_value, end_value, left_value, right_value
|
432 |
-
|
433 |
-
|
434 |
-
def calculate_rgba(
|
435 |
-
start_value, end_value, left_value, right_value, current_channel_spends
|
436 |
-
):
|
437 |
-
# Initialize alpha to None for clarity
|
438 |
-
alpha = None
|
439 |
-
|
440 |
-
# Determine the color and calculate relative_position and alpha based on the point's position
|
441 |
-
if start_value <= current_channel_spends <= left_value:
|
442 |
-
color = "yellow"
|
443 |
-
relative_position = (current_channel_spends - start_value) / (
|
444 |
-
left_value - start_value
|
445 |
-
)
|
446 |
-
alpha = 0.8 - (0.6 * relative_position) # Alpha decreases from start to end
|
447 |
-
|
448 |
-
elif left_value < current_channel_spends <= right_value:
|
449 |
-
color = "green"
|
450 |
-
relative_position = (current_channel_spends - left_value) / (
|
451 |
-
right_value - left_value
|
452 |
-
)
|
453 |
-
alpha = 0.8 - (0.6 * relative_position) # Alpha decreases from start to end
|
454 |
-
|
455 |
-
elif right_value < current_channel_spends <= end_value:
|
456 |
-
color = "red"
|
457 |
-
relative_position = (current_channel_spends - right_value) / (
|
458 |
-
end_value - right_value
|
459 |
-
)
|
460 |
-
alpha = 0.2 + (0.6 * relative_position) # Alpha increases from start to end
|
461 |
-
|
462 |
-
else:
|
463 |
-
# Default case, if the spends are outside the defined ranges
|
464 |
-
return "rgba(136, 136, 136, 0.5)" # Grey for values outside the range
|
465 |
-
|
466 |
-
# Ensure alpha is within the intended range in case of any calculation overshoot
|
467 |
-
alpha = max(0.2, min(alpha, 0.8))
|
468 |
-
|
469 |
-
# Define color codes for RGBA
|
470 |
-
color_codes = {
|
471 |
-
"yellow": "255, 255, 0", # RGB for yellow
|
472 |
-
"green": "0, 128, 0", # RGB for green
|
473 |
-
"red": "255, 0, 0", # RGB for red
|
474 |
-
}
|
475 |
-
|
476 |
-
rgba = f"rgba({color_codes[color]}, {alpha})"
|
477 |
-
return rgba
|
478 |
-
|
479 |
-
|
480 |
-
def debug_temp(x_test, power, K, b, a, x0):
|
481 |
-
print("*" * 100)
|
482 |
-
# Calculate the count of bins
|
483 |
-
count_lower_bin = sum(1 for x in x_test if x <= 2524)
|
484 |
-
count_center_bin = sum(1 for x in x_test if x > 2524 and x <= 3377)
|
485 |
-
count_ = sum(1 for x in x_test if x > 3377)
|
486 |
-
|
487 |
-
print(
|
488 |
-
f"""
|
489 |
-
lower : {count_lower_bin}
|
490 |
-
center : {count_center_bin}
|
491 |
-
upper : {count_}
|
492 |
-
"""
|
493 |
-
)
|
494 |
-
|
495 |
-
|
496 |
-
# @st.cache
|
497 |
-
def plot_response_curves(summary_df_sorted):
|
498 |
-
# cols = 3
|
499 |
-
# rows = (
|
500 |
-
# len(channels_list) // cols
|
501 |
-
# if len(channels_list) % cols == 0
|
502 |
-
# else len(channels_list) // cols + 1
|
503 |
-
# )
|
504 |
-
# rcs = st.session_state["rcs"]
|
505 |
-
# shapes = []
|
506 |
-
# fig = make_subplots(rows=rows, cols=cols, subplot_titles=channels_list)
|
507 |
-
channel_cols = [
|
508 |
-
'BroadcastTV',
|
509 |
-
'CableTV',
|
510 |
-
'Connected&OTTTV',
|
511 |
-
'DisplayProspecting',
|
512 |
-
'DisplayRetargeting',
|
513 |
-
'Video',
|
514 |
-
'SocialProspecting',
|
515 |
-
'SocialRetargeting',
|
516 |
-
'SearchBrand',
|
517 |
-
'SearchNon-brand',
|
518 |
-
'DigitalPartners',
|
519 |
-
'Audio',
|
520 |
-
'Email']
|
521 |
-
summary_df_sorted.index = summary_df_sorted["Channel_name"]
|
522 |
-
for i in range(0, len(channels_list)):
|
523 |
-
col = channels_list[i]
|
524 |
-
if col == "Panel":
|
525 |
-
continue
|
526 |
-
st.write(col)
|
527 |
-
x_modified = summary_df_sorted["Optimized_spend"][col]/104
|
528 |
-
y_modified = summary_df_sorted["New_sales"][col]/104
|
529 |
-
st.plotly_chart(rc.response_curves(col,x_modified,y_modified))
|
530 |
-
|
531 |
-
|
532 |
-
# @st.cache
|
533 |
-
# def plot_response_curves():
|
534 |
-
# cols = 4
|
535 |
-
# rcs = st.session_state["rcs"]
|
536 |
-
# shapes = []
|
537 |
-
# fig = make_subplots(rows=6, cols=cols, subplot_titles=channels_list)
|
538 |
-
# for i in range(0, len(channels_list)):
|
539 |
-
# col = channels_list[i]
|
540 |
-
# x = st.session_state["actual_df"][col].values
|
541 |
-
# spends = x.sum()
|
542 |
-
# power = np.ceil(np.log(x.max()) / np.log(10)) - 3
|
543 |
-
# x = np.linspace(0, 3 * x.max(), 200)
|
544 |
-
|
545 |
-
# K = rcs[col]["K"]
|
546 |
-
# b = rcs[col]["b"]
|
547 |
-
# a = rcs[col]["a"]
|
548 |
-
# x0 = rcs[col]["x0"]
|
549 |
-
|
550 |
-
# y = s_curve(x / 10**power, K, b, a, x0)
|
551 |
-
# roi = y / x
|
552 |
-
# marginal_roi = a * (y) * (1 - y / K)
|
553 |
-
# fig.add_trace(
|
554 |
-
# go.Scatter(
|
555 |
-
# x=52
|
556 |
-
# * x
|
557 |
-
# * st.session_state["scenario"].channels[col].conversion_rate,
|
558 |
-
# y=52 * y,
|
559 |
-
# name=col,
|
560 |
-
# customdata=np.stack((roi, marginal_roi), axis=-1),
|
561 |
-
# hovertemplate="Spend:%{x:$.2s}<br>Sale:%{y:$.2s}<br>ROI:%{customdata[0]:.3f}<br>MROI:%{customdata[1]:.3f}",
|
562 |
-
# ),
|
563 |
-
# row=1 + (i) // cols,
|
564 |
-
# col=i % cols + 1,
|
565 |
-
# )
|
566 |
-
|
567 |
-
# fig.add_trace(
|
568 |
-
# go.Scatter(
|
569 |
-
# x=[
|
570 |
-
# spends
|
571 |
-
# * st.session_state["scenario"]
|
572 |
-
# .channels[col]
|
573 |
-
# .conversion_rate
|
574 |
-
# ],
|
575 |
-
# y=[52 * s_curve(spends / (10**power * 52), K, b, a, x0)],
|
576 |
-
# name=col,
|
577 |
-
# legendgroup=col,
|
578 |
-
# showlegend=False,
|
579 |
-
# marker=dict(color=["black"]),
|
580 |
-
# ),
|
581 |
-
# row=1 + (i) // cols,
|
582 |
-
# col=i % cols + 1,
|
583 |
-
# )
|
584 |
-
|
585 |
-
# shapes.append(
|
586 |
-
# go.layout.Shape(
|
587 |
-
# type="line",
|
588 |
-
# x0=0,
|
589 |
-
# y0=52 * s_curve(spends / (10**power * 52), K, b, a, x0),
|
590 |
-
# x1=spends
|
591 |
-
# * st.session_state["scenario"].channels[col].conversion_rate,
|
592 |
-
# y1=52 * s_curve(spends / (10**power * 52), K, b, a, x0),
|
593 |
-
# line_width=1,
|
594 |
-
# line_dash="dash",
|
595 |
-
# line_color="black",
|
596 |
-
# xref=f"x{i+1}",
|
597 |
-
# yref=f"y{i+1}",
|
598 |
-
# )
|
599 |
-
# )
|
600 |
-
|
601 |
-
# shapes.append(
|
602 |
-
# go.layout.Shape(
|
603 |
-
# type="line",
|
604 |
-
# x0=spends
|
605 |
-
# * st.session_state["scenario"].channels[col].conversion_rate,
|
606 |
-
# y0=0,
|
607 |
-
# x1=spends
|
608 |
-
# * st.session_state["scenario"].channels[col].conversion_rate,
|
609 |
-
# y1=52 * s_curve(spends / (10**power * 52), K, b, a, x0),
|
610 |
-
# line_width=1,
|
611 |
-
# line_dash="dash",
|
612 |
-
# line_color="black",
|
613 |
-
# xref=f"x{i+1}",
|
614 |
-
# yref=f"y{i+1}",
|
615 |
-
# )
|
616 |
-
# )
|
617 |
-
|
618 |
-
# fig.update_layout(
|
619 |
-
# height=1500,
|
620 |
-
# width=1000,
|
621 |
-
# title_text="Response Curves",
|
622 |
-
# showlegend=False,
|
623 |
-
# shapes=shapes,
|
624 |
-
# )
|
625 |
-
# fig.update_annotations(font_size=10)
|
626 |
-
# fig.update_xaxes(title="Spends")
|
627 |
-
# fig.update_yaxes(title=target)
|
628 |
-
# return fig
|
629 |
-
|
630 |
-
|
631 |
-
# ======================================================== #
|
632 |
-
# ==================== HTML Components =================== #
|
633 |
-
# ======================================================== #
|
634 |
-
|
635 |
-
|
636 |
-
def generate_spending_header(heading):
|
637 |
-
return st.markdown(
|
638 |
-
f"""<h2 class="spends-header">{heading}</h2>""", unsafe_allow_html=True
|
639 |
-
)
|
640 |
-
|
641 |
-
|
642 |
-
# ======================================================== #
|
643 |
-
# =================== Session variables ================== #
|
644 |
-
# ======================================================== #
|
645 |
-
|
646 |
-
with open("config.yaml") as file:
|
647 |
-
config = yaml.load(file, Loader=SafeLoader)
|
648 |
-
st.session_state["config"] = config
|
649 |
-
|
650 |
-
authenticator = stauth.Authenticate(
|
651 |
-
config["credentials"],
|
652 |
-
config["cookie"]["name"],
|
653 |
-
config["cookie"]["key"],
|
654 |
-
config["cookie"]["expiry_days"],
|
655 |
-
config["preauthorized"],
|
656 |
-
)
|
657 |
-
st.session_state["authenticator"] = authenticator
|
658 |
-
name, authentication_status, username = authenticator.login("Login", "main")
|
659 |
-
auth_status = st.session_state.get("authentication_status")
|
660 |
-
|
661 |
-
import os
|
662 |
-
import glob
|
663 |
-
|
664 |
-
|
665 |
-
def get_excel_names(directory):
|
666 |
-
# Create a list to hold the final parts of the filenames
|
667 |
-
last_portions = []
|
668 |
-
|
669 |
-
# Patterns to match Excel files (.xlsx and .xls) that contain @#
|
670 |
-
patterns = [
|
671 |
-
os.path.join(directory, "*@#*.xlsx"),
|
672 |
-
os.path.join(directory, "*@#*.xls"),
|
673 |
-
]
|
674 |
-
|
675 |
-
# Process each pattern
|
676 |
-
for pattern in patterns:
|
677 |
-
files = glob.glob(pattern)
|
678 |
-
|
679 |
-
# Extracting the last portion after @# for each file
|
680 |
-
for file in files:
|
681 |
-
base_name = os.path.basename(file)
|
682 |
-
last_portion = base_name.split("@#")[-1]
|
683 |
-
last_portion = last_portion.replace(".xlsx", "").replace(
|
684 |
-
".xls", ""
|
685 |
-
) # Removing extensions
|
686 |
-
last_portions.append(last_portion)
|
687 |
-
|
688 |
-
return last_portions
|
689 |
-
|
690 |
-
|
691 |
-
def name_formating(channel_name):
|
692 |
-
# Replace underscores with spaces
|
693 |
-
name_mod = channel_name.replace("_", " ")
|
694 |
-
|
695 |
-
# Capitalize the first letter of each word
|
696 |
-
name_mod = name_mod.title()
|
697 |
-
|
698 |
-
return name_mod
|
699 |
-
|
700 |
-
|
701 |
-
@st.cache_data(show_spinner=False)
|
702 |
-
def panel_fetch(file_selected):
|
703 |
-
raw_data_mmm_df = pd.read_excel(file_selected, sheet_name="RAW DATA MMM")
|
704 |
-
|
705 |
-
# if "Panel" in raw_data_mmm_df.columns:
|
706 |
-
# panel = list(set(raw_data_mmm_df["Panel"]))
|
707 |
-
# else:
|
708 |
-
# raw_data_mmm_df = None
|
709 |
-
# panel = None
|
710 |
-
# raw_data_mmm_df = None
|
711 |
-
panel = None
|
712 |
-
return panel
|
713 |
-
|
714 |
-
|
715 |
-
def reset_inputs():
|
716 |
-
if "total_spends_change_abs" in st.session_state:
|
717 |
-
del st.session_state.total_spends_change_abs
|
718 |
-
if "total_spends_change" in st.session_state:
|
719 |
-
del st.session_state.total_spends_change
|
720 |
-
if "total_spends_change_abs_slider" in st.session_state:
|
721 |
-
del st.session_state.total_spends_change_abs_slider
|
722 |
-
|
723 |
-
if "total_sales_change_abs" in st.session_state:
|
724 |
-
del st.session_state.total_sales_change_abs
|
725 |
-
if "total_sales_change" in st.session_state:
|
726 |
-
del st.session_state.total_sales_change
|
727 |
-
if "total_sales_change_abs_slider" in st.session_state:
|
728 |
-
del st.session_state.total_sales_change_abs_slider
|
729 |
-
|
730 |
-
st.session_state["initialized"] = False
|
731 |
-
|
732 |
-
|
733 |
-
if auth_status == True:
|
734 |
-
authenticator.logout("Logout", "main")
|
735 |
-
st.header("Scenario Planner")
|
736 |
-
def scenario_planner_plots():
|
737 |
-
|
738 |
-
with st.expander('Optimized Spends Overview'):
|
739 |
-
# if st.button('Refresh'):
|
740 |
-
# st.experimental_rerun()
|
741 |
-
|
742 |
-
import plotly.graph_objects as go
|
743 |
-
from plotly.subplots import make_subplots
|
744 |
-
|
745 |
-
# Define light colors for bars
|
746 |
-
import plotly.graph_objects as go
|
747 |
-
from plotly.subplots import make_subplots
|
748 |
-
|
749 |
-
st.empty()
|
750 |
-
#st.header('Model Result Analysis')
|
751 |
-
spends_data=pd.read_excel('Overview_data_test.xlsx')
|
752 |
-
|
753 |
-
with open('summary_df.pkl', 'rb') as file:
|
754 |
-
summary_df_sorted = pickle.load(file)
|
755 |
-
#st.write(summary_df_sorted)
|
756 |
-
|
757 |
-
# selected_scenario= st.selectbox('Select Saved Scenarios',['S1','S2'])
|
758 |
-
summary_df_sorted=summary_df_sorted.sort_values(by=['Optimized_spend'],ascending=False)
|
759 |
-
summary_df_sorted['old_efficiency']=(summary_df_sorted['Old_sales']/summary_df_sorted['Old_sales'].sum())/(summary_df_sorted['Actual_spend']/summary_df_sorted['Actual_spend'].sum())
|
760 |
-
summary_df_sorted['new_efficiency']=(summary_df_sorted['New_sales']/summary_df_sorted['New_sales'].sum())/(summary_df_sorted['Optimized_spend']/summary_df_sorted['Optimized_spend'].sum())
|
761 |
-
|
762 |
-
summary_df_sorted['old_roi']=summary_df_sorted['Old_sales']/summary_df_sorted['Actual_spend']
|
763 |
-
summary_df_sorted['new_roi']=summary_df_sorted['New_sales']/summary_df_sorted['Optimized_spend']
|
764 |
-
|
765 |
-
total_actual_spend = summary_df_sorted['Actual_spend'].sum()
|
766 |
-
total_optimized_spend = summary_df_sorted['Optimized_spend'].sum()
|
767 |
-
|
768 |
-
actual_spend_percentage = (summary_df_sorted['Actual_spend'] / total_actual_spend) * 100
|
769 |
-
optimized_spend_percentage = (summary_df_sorted['Optimized_spend'] / total_optimized_spend) * 100
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
light_blue = 'rgba(0, 31, 120, 0.7)'
|
774 |
-
light_orange = 'rgba(0, 181, 219, 0.7)'
|
775 |
-
light_green = 'rgba(240, 61, 20, 0.7)'
|
776 |
-
light_red = 'rgba(250, 110, 10, 0.7)'
|
777 |
-
light_purple = 'rgba(255, 191, 69, 0.7)'
|
778 |
-
|
779 |
-
|
780 |
-
# # Create subplots with one row and two columns
|
781 |
-
# fig = make_subplots(rows=3, cols=1, subplot_titles=("Actual vs. Optimized Spend", "Actual vs. Optimized Contribution", "Actual vs. Optimized ROI"))
|
782 |
-
|
783 |
-
# # Add actual vs optimized spend bars
|
784 |
-
|
785 |
-
|
786 |
-
# fig.add_trace(go.Bar(y=summary_df_sorted['Channel_name'], x=summary_df_sorted['Actual_spend'], name='Actual',
|
787 |
-
# text=summary_df_sorted['Actual_spend'].apply(format_number) + ' '+' (' + actual_spend_percentage.round(2).astype(str) + '%)',
|
788 |
-
# marker_color=light_blue, orientation='h'),
|
789 |
-
# row=1,
|
790 |
-
# col=1)
|
791 |
-
|
792 |
-
|
793 |
-
# fig.add_trace(go.Bar(y=summary_df_sorted['Channel_name'], x=summary_df_sorted['Optimized_spend'], name='Optimized',
|
794 |
-
# text=summary_df_sorted['Optimized_spend'].apply(format_number) + ' (' + optimized_spend_percentage.round(2).astype(str) + '%)',
|
795 |
-
# marker_color=light_orange,
|
796 |
-
# orientation='h'),
|
797 |
-
# row=1,
|
798 |
-
# col=1)
|
799 |
-
|
800 |
-
# fig.update_xaxes(title_text="Amount", row=1, col=1)
|
801 |
-
|
802 |
-
# # Add actual vs optimized Contribution
|
803 |
-
# fig.add_trace(go.Bar(y=summary_df_sorted['Channel_name'], x=summary_df_sorted['New_sales'],
|
804 |
-
# name='Optimized Contribution',text=summary_df_sorted['New_sales'].apply(format_number),
|
805 |
-
# marker_color=light_orange, orientation='h',showlegend=False), row=2, col=1)
|
806 |
-
|
807 |
-
# fig.add_trace(go.Bar(y=summary_df_sorted['Channel_name'], x=summary_df_sorted['Old_sales'],
|
808 |
-
# name='Actual Contribution',text=summary_df_sorted['Old_sales'].apply(format_number),
|
809 |
-
# marker_color=light_blue, orientation='h',showlegend=False), row=2, col=1)
|
810 |
-
|
811 |
-
|
812 |
-
# fig.update_xaxes(title_text="Contribution", row=2, col=1)
|
813 |
-
|
814 |
-
# # Add actual vs optimized ROI bars
|
815 |
-
|
816 |
-
# fig.add_trace(go.Bar(y=summary_df_sorted['Channel_name'], x=summary_df_sorted['new_roi'],
|
817 |
-
# name='Optimized ROI',text=summary_df_sorted['new_roi'].apply(format_number) ,
|
818 |
-
# marker_color=light_orange, orientation='h',showlegend=False), row=3, col=1)
|
819 |
-
|
820 |
-
# fig.add_trace(go.Bar(y=summary_df_sorted['Channel_name'], x=summary_df_sorted['old_roi'],
|
821 |
-
# name='Actual ROI', text=summary_df_sorted['old_roi'].apply(format_number) ,
|
822 |
-
# marker_color=light_blue, orientation='h',showlegend=False), row=3, col=1)
|
823 |
-
|
824 |
-
# fig.update_xaxes(title_text="ROI", row=3, col=1)
|
825 |
-
|
826 |
-
# # Update layout
|
827 |
-
# fig.update_layout(title_text="Actual vs. Optimized Metrics for Media Channels",
|
828 |
-
# showlegend=True, yaxis=dict(title='Media Channels', autorange="reversed"))
|
829 |
-
|
830 |
-
# st.plotly_chart(fig,use_container_width=True)
|
831 |
-
|
832 |
-
# Create subplots with one row and two columns
|
833 |
-
fig = go.Figure()
|
834 |
-
# Add actual vs optimized spend bars
|
835 |
-
|
836 |
-
|
837 |
-
fig.add_trace(go.Bar(x=summary_df_sorted['Channel_name'], y=summary_df_sorted['Actual_spend'], name='Actual',
|
838 |
-
text=summary_df_sorted['Actual_spend'].apply(format_number) + ' '
|
839 |
-
# +
|
840 |
-
# ' '+
|
841 |
-
# '</br> (' + actual_spend_percentage.astype(int).astype(str) + '%)'
|
842 |
-
,textposition='outside',#textfont=dict(size=30),
|
843 |
-
marker_color=light_blue))
|
844 |
-
|
845 |
-
|
846 |
-
fig.add_trace(go.Bar(x=summary_df_sorted['Channel_name'], y=summary_df_sorted['Optimized_spend'], name='Optimized',
|
847 |
-
text=summary_df_sorted['Optimized_spend'].apply(format_number) + ' '
|
848 |
-
# +
|
849 |
-
# '</br> (' + optimized_spend_percentage.astype(int).astype(str) + '%)'
|
850 |
-
,textposition='outside',#textfont=dict(size=30),
|
851 |
-
marker_color=light_orange))
|
852 |
-
|
853 |
-
fig.update_xaxes(title_text="Channels")
|
854 |
-
fig.update_yaxes(title_text="Spends ($)")
|
855 |
-
fig.update_layout(
|
856 |
-
title = "Actual vs. Optimized Spends",
|
857 |
-
margin=dict(t=40, b=40, l=40, r=40)
|
858 |
-
)
|
859 |
-
|
860 |
-
st.plotly_chart(fig,use_container_width=True)
|
861 |
-
|
862 |
-
# Add actual vs optimized Contribution
|
863 |
-
fig = go.Figure()
|
864 |
-
fig.add_trace(go.Bar(x=summary_df_sorted['Channel_name'], y=summary_df_sorted['Old_sales'],
|
865 |
-
name='Actual Contribution',text=summary_df_sorted['Old_sales'].apply(format_number),textposition='outside',
|
866 |
-
marker_color=light_blue,showlegend=True))
|
867 |
-
|
868 |
-
fig.add_trace(go.Bar(x=summary_df_sorted['Channel_name'], y=summary_df_sorted['New_sales'],
|
869 |
-
name='Optimized Contribution',text=summary_df_sorted['New_sales'].apply(format_number),textposition='outside',
|
870 |
-
marker_color=light_orange, showlegend=True))
|
871 |
-
|
872 |
-
|
873 |
-
|
874 |
-
fig.update_yaxes(title_text="Contribution")
|
875 |
-
fig.update_xaxes(title_text="Channels")
|
876 |
-
fig.update_layout(
|
877 |
-
title = "Actual vs. Optimized Contributions",
|
878 |
-
margin=dict(t=40, b=40, l=40, r=40)
|
879 |
-
# yaxis=dict(range=[0, 0.002]),
|
880 |
-
)
|
881 |
-
st.plotly_chart(fig,use_container_width=True)
|
882 |
-
|
883 |
-
# Add actual vs optimized Efficiency bars
|
884 |
-
fig = go.Figure()
|
885 |
-
summary_df_sorted_p = summary_df_sorted[summary_df_sorted['Channel_name']!="Panel"]
|
886 |
-
fig.add_trace(go.Bar(x=summary_df_sorted_p['Channel_name'], y=summary_df_sorted_p['old_efficiency'],
|
887 |
-
name='Actual Efficiency', text=summary_df_sorted_p['old_efficiency'].apply(format_number) ,textposition='outside',
|
888 |
-
marker_color=light_blue,showlegend=True))
|
889 |
-
fig.add_trace(go.Bar(x=summary_df_sorted_p['Channel_name'], y=summary_df_sorted_p['new_efficiency'],
|
890 |
-
name='Optimized Efficiency',text=summary_df_sorted_p['new_efficiency'].apply(format_number),textposition='outside' ,
|
891 |
-
marker_color=light_orange,showlegend=True))
|
892 |
-
|
893 |
-
fig.update_xaxes(title_text="Channels")
|
894 |
-
fig.update_yaxes(title_text="ROI")
|
895 |
-
fig.update_layout(
|
896 |
-
title = "Actual vs. Optimized ROI",
|
897 |
-
margin=dict(t=40, b=40, l=40, r=40),
|
898 |
-
# yaxis=dict(range=[0, 0.002]),
|
899 |
-
)
|
900 |
-
|
901 |
-
st.plotly_chart(fig,use_container_width=True)
|
902 |
-
|
903 |
-
|
904 |
-
# Response Metrics
|
905 |
-
directory = "metrics_level_data"
|
906 |
-
metrics_list = get_excel_names(directory)
|
907 |
-
|
908 |
-
# metrics_selected = col1.selectbox(
|
909 |
-
# "Response Metrics",
|
910 |
-
# metrics_list,
|
911 |
-
# format_func=name_formating,
|
912 |
-
# index=0,
|
913 |
-
# on_change=reset_inputs,
|
914 |
-
# )
|
915 |
-
|
916 |
-
metrics_selected='prospects'
|
917 |
-
# Target
|
918 |
-
target = name_formating(metrics_selected)
|
919 |
-
|
920 |
-
file_selected = (
|
921 |
-
f"Overview_data_test_panel@#{metrics_selected}.xlsx"
|
922 |
-
)
|
923 |
-
|
924 |
-
# Panel List
|
925 |
-
panel_list = panel_fetch(file_selected)
|
926 |
-
|
927 |
-
# # Panel Selected
|
928 |
-
# panel_selected = st.selectbox(
|
929 |
-
# "Markets",
|
930 |
-
# ["Total Market"] + panel_list,
|
931 |
-
# index=0,
|
932 |
-
# on_change=reset_inputs,
|
933 |
-
# )
|
934 |
-
|
935 |
-
# st.write(panel_selected)
|
936 |
-
panel_selected = "Total Market"
|
937 |
-
st.session_state['selected_markets']=panel_selected
|
938 |
-
|
939 |
-
if "update_rcs" in st.session_state:
|
940 |
-
updated_rcs = st.session_state["update_rcs"]
|
941 |
-
else:
|
942 |
-
updated_rcs = None
|
943 |
-
|
944 |
-
if "first_time" not in st.session_state:
|
945 |
-
st.session_state["first_time"] = True
|
946 |
-
|
947 |
-
# Check if state is initiaized
|
948 |
-
is_state_initiaized = st.session_state.get("initialized", False)
|
949 |
-
if not is_state_initiaized or st.session_state["first_time"]:
|
950 |
-
# initialize_data()
|
951 |
-
if panel_selected == "Total Market":
|
952 |
-
initialize_data(
|
953 |
-
panel=panel_selected,
|
954 |
-
target_file=file_selected,
|
955 |
-
updated_rcs=updated_rcs,
|
956 |
-
metrics=metrics_selected,
|
957 |
-
)
|
958 |
-
panel = None
|
959 |
-
else:
|
960 |
-
initialize_data(
|
961 |
-
panel=panel_selected,
|
962 |
-
target_file=file_selected,
|
963 |
-
updated_rcs=updated_rcs,
|
964 |
-
metrics=metrics_selected,
|
965 |
-
)
|
966 |
-
st.session_state["initialized"] = True
|
967 |
-
st.session_state["first_time"] = False
|
968 |
-
|
969 |
-
# initialize_data(
|
970 |
-
# panel=panel_selected,
|
971 |
-
# target_file=file_selected,
|
972 |
-
# updated_rcs=updated_rcs,
|
973 |
-
# metrics=metrics_selected,
|
974 |
-
# )
|
975 |
-
# st.session_state["initialized"] = True
|
976 |
-
# st.session_state["first_time"] = False
|
977 |
-
|
978 |
-
# Channels List
|
979 |
-
channels_list = st.session_state["channels_list"]
|
980 |
-
|
981 |
-
# ======================================================== #
|
982 |
-
# ========================== UI ========================== #
|
983 |
-
# ======================================================== #
|
984 |
-
|
985 |
-
# print(list(st.session_state.keys()))
|
986 |
-
main_header = st.columns((2, 2))
|
987 |
-
sub_header = st.columns((1, 1, 1, 1))
|
988 |
-
_scenario = st.session_state["scenario"]
|
989 |
-
|
990 |
-
if "total_spends_change" not in st.session_state:
|
991 |
-
st.session_state.total_spends_change = 0
|
992 |
-
|
993 |
-
if "total_sales_change" not in st.session_state:
|
994 |
-
st.session_state.total_sales_change = 0
|
995 |
-
|
996 |
-
if "total_spends_change_abs" not in st.session_state:
|
997 |
-
st.session_state["total_spends_change_abs"] = numerize(
|
998 |
-
_scenario.actual_total_spends, 1
|
999 |
-
)
|
1000 |
-
|
1001 |
-
if "total_sales_change_abs" not in st.session_state:
|
1002 |
-
st.session_state["total_sales_change_abs"] = numerize(
|
1003 |
-
_scenario.actual_total_sales, 1
|
1004 |
-
)
|
1005 |
-
|
1006 |
-
if "total_spends_change_abs_slider" not in st.session_state:
|
1007 |
-
st.session_state.total_spends_change_abs_slider = numerize(
|
1008 |
-
_scenario.actual_total_spends, 1
|
1009 |
-
)
|
1010 |
-
|
1011 |
-
if "total_sales_change_abs_slider" not in st.session_state:
|
1012 |
-
st.session_state.total_sales_change_abs_slider = numerize(
|
1013 |
-
_scenario.actual_total_sales, 1
|
1014 |
-
)
|
1015 |
-
|
1016 |
-
with main_header[0]:
|
1017 |
-
st.subheader("Actual")
|
1018 |
-
|
1019 |
-
with main_header[-1]:
|
1020 |
-
st.subheader("Simulated")
|
1021 |
-
|
1022 |
-
with sub_header[0]:
|
1023 |
-
st.metric(label="Spends", value=format_numbers(_scenario.actual_total_spends))
|
1024 |
-
|
1025 |
-
with sub_header[1]:
|
1026 |
-
st.metric(
|
1027 |
-
label=target,
|
1028 |
-
value=format_numbers_f(
|
1029 |
-
float(_scenario.actual_total_sales)
|
1030 |
-
),
|
1031 |
-
)
|
1032 |
-
|
1033 |
-
with sub_header[2]:
|
1034 |
-
st.metric(
|
1035 |
-
label="Spends",
|
1036 |
-
value=format_numbers(_scenario.modified_total_spends),
|
1037 |
-
delta=numerize(_scenario.delta_spends, 1),
|
1038 |
-
)
|
1039 |
-
|
1040 |
-
with sub_header[3]:
|
1041 |
-
st.metric(
|
1042 |
-
label=target,
|
1043 |
-
value=format_numbers_f(
|
1044 |
-
float(_scenario.modified_total_sales)
|
1045 |
-
),
|
1046 |
-
delta=numerize(_scenario.delta_sales, 1),
|
1047 |
-
)
|
1048 |
-
|
1049 |
-
with st.expander("Channel Spends Simulator", expanded=True):
|
1050 |
-
_columns1 = st.columns((2, 2, 1, 1))
|
1051 |
-
with _columns1[0]:
|
1052 |
-
optimization_selection = st.selectbox(
|
1053 |
-
"Optimize", options=["Media Spends", target], key="optimization_key"
|
1054 |
-
)
|
1055 |
-
|
1056 |
-
with _columns1[1]:
|
1057 |
-
st.markdown("#")
|
1058 |
-
# if st.checkbox(
|
1059 |
-
# label="Optimize all Channels",
|
1060 |
-
# key="optimze_all_channels",
|
1061 |
-
# value=False,
|
1062 |
-
# # on_change=select_all_channels_for_optimization,
|
1063 |
-
# ):
|
1064 |
-
# select_all_channels_for_optimization()
|
1065 |
-
|
1066 |
-
st.checkbox(
|
1067 |
-
label="Optimize all Channels",
|
1068 |
-
key="optimze_all_channels",
|
1069 |
-
value=False,
|
1070 |
-
on_change=select_all_channels_for_optimization,
|
1071 |
-
)
|
1072 |
-
|
1073 |
-
with _columns1[2]:
|
1074 |
-
st.markdown("#")
|
1075 |
-
# st.button(
|
1076 |
-
# "Optimize",
|
1077 |
-
# on_click=optimize,
|
1078 |
-
# args=(st.session_state["optimization_key"]),
|
1079 |
-
# use_container_width=True,
|
1080 |
-
# )
|
1081 |
-
|
1082 |
-
optimize_placeholder = st.empty()
|
1083 |
-
|
1084 |
-
with _columns1[3]:
|
1085 |
-
st.markdown("#")
|
1086 |
-
st.button(
|
1087 |
-
"Reset",
|
1088 |
-
on_click=reset_scenario,
|
1089 |
-
args=(panel_selected, file_selected, updated_rcs),
|
1090 |
-
# use_container_width=True,
|
1091 |
-
)
|
1092 |
-
# st.write(target)
|
1093 |
-
|
1094 |
-
|
1095 |
-
_columns2 = st.columns((2, 2, 2))
|
1096 |
-
if st.session_state["optimization_key"] == "Media Spends":
|
1097 |
-
with _columns2[0]:
|
1098 |
-
spend_input = st.text_input(
|
1099 |
-
"Absolute",
|
1100 |
-
key="total_spends_change_abs",
|
1101 |
-
# label_visibility="collapsed",
|
1102 |
-
on_change=update_all_spends_abs,
|
1103 |
-
)
|
1104 |
-
|
1105 |
-
with _columns2[1]:
|
1106 |
-
st.number_input(
|
1107 |
-
"Percent Change",
|
1108 |
-
key="total_spends_change",
|
1109 |
-
min_value=-50,
|
1110 |
-
max_value=50,
|
1111 |
-
step=1,
|
1112 |
-
on_change=update_spends,
|
1113 |
-
)
|
1114 |
-
|
1115 |
-
with _columns2[2]:
|
1116 |
-
min_value = round(_scenario.actual_total_spends * 0.5)
|
1117 |
-
max_value = round(_scenario.actual_total_spends * 1.5)
|
1118 |
-
st.session_state["total_spends_change_abs_slider_options"] = [
|
1119 |
-
numerize(value, 1)
|
1120 |
-
for value in range(min_value, max_value + 1, int(1e4))
|
1121 |
-
]
|
1122 |
-
|
1123 |
-
# st.select_slider(
|
1124 |
-
# "Absolute Slider",
|
1125 |
-
# options=st.session_state["total_spends_change_abs_slider_options"],
|
1126 |
-
# key="total_spends_change_abs_slider",
|
1127 |
-
# on_change=update_all_spends_abs_slider,
|
1128 |
-
# )
|
1129 |
-
|
1130 |
-
elif st.session_state["optimization_key"] == target:
|
1131 |
-
# st.write(target)
|
1132 |
-
with _columns2[0]:
|
1133 |
-
sales_input = st.text_input(
|
1134 |
-
"Absolute",
|
1135 |
-
key="total_sales_change_abs",
|
1136 |
-
on_change=update_sales_abs,
|
1137 |
-
)
|
1138 |
-
|
1139 |
-
with _columns2[1]:
|
1140 |
-
st.number_input(
|
1141 |
-
"Percent Change",
|
1142 |
-
key="total_sales_change",
|
1143 |
-
min_value=-50,
|
1144 |
-
max_value=50,
|
1145 |
-
step=1,
|
1146 |
-
on_change=update_sales,
|
1147 |
-
)
|
1148 |
-
with _columns2[2]:
|
1149 |
-
min_value = round(_scenario.actual_total_sales * 0.5)
|
1150 |
-
max_value = round(_scenario.actual_total_sales * 1.5)
|
1151 |
-
st.write(min_value)
|
1152 |
-
st.write(max_value)
|
1153 |
-
# for value in range(min_value, max_value + 1, int(100)):
|
1154 |
-
# st.write(numerize(value, 1))
|
1155 |
-
st.session_state["total_sales_change_abs_slider_options"] = [
|
1156 |
-
numerize(value, 1)
|
1157 |
-
for value in range(min_value, max_value + 1, int(100))
|
1158 |
-
]
|
1159 |
-
|
1160 |
-
st.select_slider(
|
1161 |
-
"Absolute Slider",
|
1162 |
-
options=st.session_state["total_sales_change_abs_slider_options"],
|
1163 |
-
key="total_sales_change_abs_slider",
|
1164 |
-
on_change=update_sales_abs_slider,
|
1165 |
-
# value=numerize(min_value, 1)
|
1166 |
-
)
|
1167 |
-
|
1168 |
-
if (
|
1169 |
-
not st.session_state["allow_sales_update"]
|
1170 |
-
and optimization_selection == target
|
1171 |
-
):
|
1172 |
-
st.warning("Invalid Input")
|
1173 |
-
|
1174 |
-
if (
|
1175 |
-
not st.session_state["allow_spends_update"]
|
1176 |
-
and optimization_selection == "Media Spends"
|
1177 |
-
):
|
1178 |
-
st.warning("Invalid Input")
|
1179 |
-
|
1180 |
-
status_placeholder = st.empty()
|
1181 |
-
|
1182 |
-
# if optimize_placeholder.button("Optimize", use_container_width=True):
|
1183 |
-
# optimize(st.session_state["optimization_key"], status_placeholder)
|
1184 |
-
# st.rerun()
|
1185 |
-
|
1186 |
-
optimize_placeholder.button(
|
1187 |
-
"Optimize",
|
1188 |
-
on_click=optimize,
|
1189 |
-
args=(st.session_state["optimization_key"], status_placeholder),
|
1190 |
-
# use_container_width=True,
|
1191 |
-
)
|
1192 |
-
|
1193 |
-
st.markdown("""<hr class="spends-heading-seperator">""", unsafe_allow_html=True)
|
1194 |
-
_columns = st.columns((2.5, 2, 1.5, 1.5, 1))
|
1195 |
-
with _columns[0]:
|
1196 |
-
generate_spending_header("Channel")
|
1197 |
-
with _columns[1]:
|
1198 |
-
generate_spending_header("Spends Input")
|
1199 |
-
with _columns[2]:
|
1200 |
-
generate_spending_header("Spends")
|
1201 |
-
with _columns[3]:
|
1202 |
-
generate_spending_header(target)
|
1203 |
-
with _columns[4]:
|
1204 |
-
generate_spending_header("Optimize")
|
1205 |
-
|
1206 |
-
st.markdown("""<hr class="spends-heading-seperator">""", unsafe_allow_html=True)
|
1207 |
-
|
1208 |
-
if "acutual_predicted" not in st.session_state:
|
1209 |
-
st.session_state["acutual_predicted"] = {
|
1210 |
-
"Channel_name": [],
|
1211 |
-
"Actual_spend": [],
|
1212 |
-
"Optimized_spend": [],
|
1213 |
-
"Delta": [],
|
1214 |
-
"New_sales":[],
|
1215 |
-
"Old_sales":[]
|
1216 |
-
}
|
1217 |
-
for i, channel_name in enumerate(channels_list):
|
1218 |
-
# st.write(channel_name)
|
1219 |
-
_channel_class = st.session_state["scenario"].channels[channel_name]
|
1220 |
-
_columns = st.columns((2.5, 1.5, 1.5, 1.5, 1))
|
1221 |
-
with _columns[0]:
|
1222 |
-
st.write(channel_name_formating(channel_name))
|
1223 |
-
bin_placeholder = st.container()
|
1224 |
-
|
1225 |
-
with _columns[1]:
|
1226 |
-
channel_bounds = _channel_class.bounds
|
1227 |
-
channel_spends = float(_channel_class.actual_total_spends)
|
1228 |
-
min_value = float((1 + channel_bounds[0] / 100) * channel_spends)
|
1229 |
-
max_value = float((1 + channel_bounds[1] / 100) * channel_spends)
|
1230 |
-
##print(st.session_state[channel_name])
|
1231 |
-
spend_input = st.text_input(
|
1232 |
-
channel_name,
|
1233 |
-
key=channel_name,
|
1234 |
-
label_visibility="collapsed",
|
1235 |
-
on_change=partial(update_data, channel_name),
|
1236 |
-
)
|
1237 |
-
if not validate_input(spend_input):
|
1238 |
-
st.error("Invalid input")
|
1239 |
-
|
1240 |
-
channel_name_current = f"{channel_name}_change"
|
1241 |
-
|
1242 |
-
st.number_input(
|
1243 |
-
"Percent Change",
|
1244 |
-
key=channel_name_current,
|
1245 |
-
step=1,
|
1246 |
-
on_change=partial(update_data_by_percent, channel_name),
|
1247 |
-
)
|
1248 |
-
|
1249 |
-
with _columns[2]:
|
1250 |
-
# spends
|
1251 |
-
current_channel_spends = float(
|
1252 |
-
_channel_class.modified_total_spends
|
1253 |
-
* _channel_class.conversion_rate
|
1254 |
-
)
|
1255 |
-
actual_channel_spends = float(
|
1256 |
-
_channel_class.actual_total_spends * _channel_class.conversion_rate
|
1257 |
-
)
|
1258 |
-
spends_delta = float(
|
1259 |
-
_channel_class.delta_spends * _channel_class.conversion_rate
|
1260 |
-
)
|
1261 |
-
st.session_state["acutual_predicted"]["Channel_name"].append(
|
1262 |
-
channel_name
|
1263 |
-
)
|
1264 |
-
st.session_state["acutual_predicted"]["Actual_spend"].append(
|
1265 |
-
actual_channel_spends
|
1266 |
-
)
|
1267 |
-
st.session_state["acutual_predicted"]["Optimized_spend"].append(
|
1268 |
-
current_channel_spends
|
1269 |
-
)
|
1270 |
-
st.session_state["acutual_predicted"]["Delta"].append(spends_delta)
|
1271 |
-
## REMOVE
|
1272 |
-
st.metric(
|
1273 |
-
"Spends",
|
1274 |
-
format_numbers(current_channel_spends),
|
1275 |
-
delta=numerize(spends_delta, 1),
|
1276 |
-
label_visibility="collapsed",
|
1277 |
-
)
|
1278 |
-
|
1279 |
-
with _columns[3]:
|
1280 |
-
# sales
|
1281 |
-
current_channel_sales = float(_channel_class.modified_total_sales)
|
1282 |
-
actual_channel_sales = float(_channel_class.actual_total_sales)
|
1283 |
-
sales_delta = float(_channel_class.delta_sales)
|
1284 |
-
st.session_state["acutual_predicted"]["Old_sales"].append(actual_channel_sales)
|
1285 |
-
st.session_state["acutual_predicted"]["New_sales"].append(current_channel_sales)
|
1286 |
-
#st.write(actual_channel_sales)
|
1287 |
-
|
1288 |
-
st.metric(
|
1289 |
-
target,
|
1290 |
-
format_numbers_f(current_channel_sales),
|
1291 |
-
delta=numerize(sales_delta, 1),
|
1292 |
-
label_visibility="collapsed",
|
1293 |
-
)
|
1294 |
-
|
1295 |
-
with _columns[4]:
|
1296 |
-
|
1297 |
-
# if st.checkbox(
|
1298 |
-
# label="select for optimization",
|
1299 |
-
# key=f"{channel_name}_selected",
|
1300 |
-
# value=False,
|
1301 |
-
# # on_change=partial(select_channel_for_optimization, channel_name),
|
1302 |
-
# label_visibility="collapsed",
|
1303 |
-
# ):
|
1304 |
-
# select_channel_for_optimization(channel_name)
|
1305 |
-
|
1306 |
-
st.checkbox(
|
1307 |
-
label="select for optimization",
|
1308 |
-
key=f"{channel_name}_selected",
|
1309 |
-
value=False,
|
1310 |
-
on_change=partial(select_channel_for_optimization, channel_name),
|
1311 |
-
label_visibility="collapsed",
|
1312 |
-
)
|
1313 |
-
|
1314 |
-
st.markdown(
|
1315 |
-
"""<hr class="spends-child-seperator">""",
|
1316 |
-
unsafe_allow_html=True,
|
1317 |
-
)
|
1318 |
-
|
1319 |
-
# Bins
|
1320 |
-
col = channels_list[i]
|
1321 |
-
x_actual = st.session_state["scenario"].channels[col].actual_spends
|
1322 |
-
x_modified = st.session_state["scenario"].channels[col].modified_spends
|
1323 |
-
# x_modified_total = 0
|
1324 |
-
# for c in channels_list:
|
1325 |
-
# # st.write(c)
|
1326 |
-
# # st.write(st.session_state["scenario"].channels[c].modified_spends)
|
1327 |
-
# x_modified_total = x_modified_total + st.session_state["scenario"].channels[c].modified_spends.sum()
|
1328 |
-
# st.write(x_modified_total)
|
1329 |
-
|
1330 |
-
x_total = x_modified.sum()
|
1331 |
-
power = np.ceil(np.log(x_actual.max()) / np.log(10)) - 3
|
1332 |
-
|
1333 |
-
updated_rcs_key = f"{metrics_selected}#@{panel_selected}#@{channel_name}"
|
1334 |
-
|
1335 |
-
if updated_rcs and updated_rcs_key in list(updated_rcs.keys()):
|
1336 |
-
K = updated_rcs[updated_rcs_key]["K"]
|
1337 |
-
b = updated_rcs[updated_rcs_key]["b"]
|
1338 |
-
a = updated_rcs[updated_rcs_key]["a"]
|
1339 |
-
x0 = updated_rcs[updated_rcs_key]["x0"]
|
1340 |
-
else:
|
1341 |
-
K = st.session_state["rcs"][col]["K"]
|
1342 |
-
b = st.session_state["rcs"][col]["b"]
|
1343 |
-
a = st.session_state["rcs"][col]["a"]
|
1344 |
-
x0 = st.session_state["rcs"][col]["x0"]
|
1345 |
-
|
1346 |
-
x_plot = np.linspace(0, 5 * x_actual.sum(), 200)
|
1347 |
-
|
1348 |
-
# Append current_channel_spends to the end of x_plot
|
1349 |
-
x_plot = np.append(x_plot, current_channel_spends)
|
1350 |
-
|
1351 |
-
x, y, marginal_roi = [], [], []
|
1352 |
-
for x_p in x_plot:
|
1353 |
-
x.append(x_p * x_actual / x_actual.sum())
|
1354 |
-
|
1355 |
-
for index in range(len(x_plot)):
|
1356 |
-
y.append(s_curve(x[index] / 10**power, K, b, a, x0))
|
1357 |
-
|
1358 |
-
for index in range(len(x_plot)):
|
1359 |
-
marginal_roi.append(
|
1360 |
-
a * y[index] * (1 - y[index] / np.maximum(K, np.finfo(float).eps))
|
1361 |
-
)
|
1362 |
-
|
1363 |
-
x = (
|
1364 |
-
np.sum(x, axis=1)
|
1365 |
-
* st.session_state["scenario"].channels[col].conversion_rate
|
1366 |
-
)
|
1367 |
-
y = np.sum(y, axis=1)
|
1368 |
-
marginal_roi = (
|
1369 |
-
np.average(marginal_roi, axis=1)
|
1370 |
-
/ st.session_state["scenario"].channels[col].conversion_rate
|
1371 |
-
)
|
1372 |
-
|
1373 |
-
roi = y / np.maximum(x, np.finfo(float).eps)
|
1374 |
-
# roi = (y/np.sum(y))/(x/np.sum(x))
|
1375 |
-
# st.write(x)
|
1376 |
-
# st.write(y)
|
1377 |
-
# st.write(roi)
|
1378 |
-
|
1379 |
-
# st.write(roi[-1])
|
1380 |
-
|
1381 |
-
roi_current, marginal_roi_current = roi[-1], marginal_roi[-1]
|
1382 |
-
x, y, roi, marginal_roi = (
|
1383 |
-
x[:-1],
|
1384 |
-
y[:-1],
|
1385 |
-
roi[:-1],
|
1386 |
-
marginal_roi[:-1],
|
1387 |
-
) # Drop data for current spends
|
1388 |
-
|
1389 |
-
# roi_current =
|
1390 |
-
|
1391 |
-
start_value, end_value, left_value, right_value = find_segment_value(
|
1392 |
-
x,
|
1393 |
-
roi,
|
1394 |
-
marginal_roi,
|
1395 |
-
)
|
1396 |
-
|
1397 |
-
#st.write(roi_current)
|
1398 |
-
|
1399 |
-
rgba = calculate_rgba(
|
1400 |
-
start_value,
|
1401 |
-
end_value,
|
1402 |
-
left_value,
|
1403 |
-
right_value,
|
1404 |
-
current_channel_spends,
|
1405 |
-
)
|
1406 |
-
|
1407 |
-
summary_df = pd.DataFrame(st.session_state["acutual_predicted"])
|
1408 |
-
# st.dataframe(summary_df)
|
1409 |
-
summary_df.drop_duplicates(subset="Channel_name", keep="last", inplace=True)
|
1410 |
-
# st.dataframe(summary_df)
|
1411 |
-
|
1412 |
-
summary_df_sorted = summary_df.sort_values(by="Delta", ascending=False)
|
1413 |
-
summary_df_sorted["Delta_percent"] = np.round(
|
1414 |
-
((summary_df_sorted["Optimized_spend"] / summary_df_sorted["Actual_spend"]) - 1)
|
1415 |
-
* 100,
|
1416 |
-
2,
|
1417 |
-
)
|
1418 |
-
|
1419 |
-
summary_df_sorted=summary_df_sorted.sort_values(by=['Optimized_spend'],ascending=False)
|
1420 |
-
summary_df_sorted['old_efficiency']=(summary_df_sorted['Old_sales']/summary_df_sorted['Old_sales'].sum())/(summary_df_sorted['Actual_spend']/summary_df_sorted['Actual_spend'].sum())
|
1421 |
-
summary_df_sorted['new_efficiency']=(summary_df_sorted['New_sales']/summary_df_sorted['New_sales'].sum())/(summary_df_sorted['Optimized_spend']/summary_df_sorted['Optimized_spend'].sum())
|
1422 |
-
|
1423 |
-
a = (summary_df_sorted[summary_df_sorted['Channel_name']== col]).reset_index()['new_efficiency'][0]
|
1424 |
-
# st.write(a)
|
1425 |
-
|
1426 |
-
with bin_placeholder:
|
1427 |
-
st.markdown(
|
1428 |
-
f"""
|
1429 |
-
<div style="
|
1430 |
-
border-radius: 12px;
|
1431 |
-
background-color: {rgba};
|
1432 |
-
padding: 10px;
|
1433 |
-
text-align: center;
|
1434 |
-
color: #006EC0;
|
1435 |
-
">
|
1436 |
-
<p style="margin: 0; font-size: 20px;">Efficiency: {round(a,2)}</p>
|
1437 |
-
<!--<p style="margin: 0; font-size: 20px;">Marginal ROI: {round(marginal_roi_current,1)}</p>-->
|
1438 |
-
</div>
|
1439 |
-
""",
|
1440 |
-
unsafe_allow_html=True,
|
1441 |
-
)
|
1442 |
-
|
1443 |
-
# with st.expander("See Response Curves", expanded=True):
|
1444 |
-
# fig = plot_response_curves(summary_df_sorted)
|
1445 |
-
# # st.plotly_chart(rc.response_curves(col))
|
1446 |
-
# # st.plotly_chart(fig, use_container_width=True)
|
1447 |
-
|
1448 |
-
summary_df = pd.DataFrame(st.session_state["acutual_predicted"])
|
1449 |
-
# st.dataframe(summary_df)
|
1450 |
-
summary_df.drop_duplicates(subset="Channel_name", keep="last", inplace=True)
|
1451 |
-
# st.dataframe(summary_df)
|
1452 |
-
|
1453 |
-
summary_df_sorted = summary_df.sort_values(by="Delta", ascending=False)
|
1454 |
-
summary_df_sorted["Delta_percent"] = np.round(
|
1455 |
-
((summary_df_sorted["Optimized_spend"] / summary_df_sorted["Actual_spend"]) - 1)
|
1456 |
-
* 100,
|
1457 |
-
2,
|
1458 |
-
)
|
1459 |
-
|
1460 |
-
|
1461 |
-
|
1462 |
-
|
1463 |
-
|
1464 |
-
with open("summary_df.pkl", "wb") as f:
|
1465 |
-
pickle.dump(summary_df_sorted, f)
|
1466 |
-
# st.dataframe(summary_df_sorted)
|
1467 |
-
# ___columns=st.columns(3)
|
1468 |
-
# with ___columns[2]:
|
1469 |
-
# fig=summary_plot(summary_df_sorted, x='Delta_percent', y='Channel_name', title='Delta', text_column='Delta_percent')
|
1470 |
-
# st.plotly_chart(fig,use_container_width=True)
|
1471 |
-
# with ___columns[0]:
|
1472 |
-
# fig=summary_plot(summary_df_sorted, x='Actual_spend', y='Channel_name', title='Actual Spend', text_column='Actual_spend')
|
1473 |
-
# st.plotly_chart(fig,use_container_width=True)
|
1474 |
-
# with ___columns[1]:
|
1475 |
-
# fig=summary_plot(summary_df_sorted, x='Optimized_spend', y='Channel_name', title='Planned Spend', text_column='Optimized_spend')
|
1476 |
-
# st.plotly_chart(fig,use_container_width=True)
|
1477 |
-
|
1478 |
-
scenario_planner_plots()
|
1479 |
-
|
1480 |
-
_columns = st.columns(2)
|
1481 |
-
# with _columns[0]:
|
1482 |
-
st.subheader("Save Scenario")
|
1483 |
-
scenario_name = st.text_input(
|
1484 |
-
"Scenario name",
|
1485 |
-
key="scenario_input",
|
1486 |
-
placeholder="Scenario name",
|
1487 |
-
label_visibility="collapsed",
|
1488 |
-
)
|
1489 |
-
st.button(
|
1490 |
-
"Save",
|
1491 |
-
on_click=lambda: save_scenario(scenario_name),
|
1492 |
-
disabled=len(st.session_state["scenario_input"]) == 0,#use_container_width=True
|
1493 |
-
)
|
1494 |
-
|
1495 |
-
|
1496 |
-
|
1497 |
-
elif auth_status == False:
|
1498 |
-
st.error("Username/Password is incorrect")
|
1499 |
-
|
1500 |
-
if auth_status != True:
|
1501 |
-
try:
|
1502 |
-
username_forgot_pw, email_forgot_password, random_password = (
|
1503 |
-
authenticator.forgot_password("Forgot password")
|
1504 |
-
)
|
1505 |
-
if username_forgot_pw:
|
1506 |
-
st.session_state["config"]["credentials"]["usernames"][username_forgot_pw][
|
1507 |
-
"password"
|
1508 |
-
] = stauth.Hasher([random_password]).generate()[0]
|
1509 |
-
send_email(email_forgot_password, random_password)
|
1510 |
-
st.success("New password sent securely")
|
1511 |
-
# Random password to be transferred to user securely
|
1512 |
-
elif username_forgot_pw == False:
|
1513 |
-
st.error("Username not found")
|
1514 |
-
except Exception as e:
|
1515 |
-
st.error(e)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pages/9_Saved_Scenarios.py
DELETED
@@ -1,280 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
from numerize.numerize import numerize
|
3 |
-
import io
|
4 |
-
import pandas as pd
|
5 |
-
from utilities import (format_numbers,decimal_formater,
|
6 |
-
channel_name_formating,
|
7 |
-
load_local_css,set_header,
|
8 |
-
initialize_data,
|
9 |
-
load_authenticator)
|
10 |
-
from openpyxl import Workbook
|
11 |
-
from openpyxl.styles import Alignment,Font,PatternFill
|
12 |
-
import pickle
|
13 |
-
import streamlit_authenticator as stauth
|
14 |
-
import yaml
|
15 |
-
from yaml import SafeLoader
|
16 |
-
from classes import class_from_dict
|
17 |
-
|
18 |
-
st.set_page_config(layout='wide')
|
19 |
-
load_local_css('styles.css')
|
20 |
-
set_header()
|
21 |
-
|
22 |
-
# for k, v in st.session_state.items():
|
23 |
-
# if k not in ['logout', 'login','config'] and not k.startswith('FormSubmitter'):
|
24 |
-
# st.session_state[k] = v
|
25 |
-
|
26 |
-
def create_scenario_summary(scenario_dict):
|
27 |
-
summary_rows = []
|
28 |
-
for channel_dict in scenario_dict['channels']:
|
29 |
-
name_mod = channel_name_formating(channel_dict['name'])
|
30 |
-
summary_rows.append([name_mod,
|
31 |
-
channel_dict.get('actual_total_spends') * channel_dict.get('conversion_rate'),
|
32 |
-
channel_dict.get('modified_total_spends') * channel_dict.get('conversion_rate'),
|
33 |
-
channel_dict.get('actual_total_sales') ,
|
34 |
-
channel_dict.get('modified_total_sales'),
|
35 |
-
# channel_dict.get('actual_total_sales') / (channel_dict.get('actual_total_spends') * channel_dict.get('conversion_rate')),
|
36 |
-
# channel_dict.get('modified_total_sales') / (channel_dict.get('modified_total_spends') * channel_dict.get('conversion_rate')),
|
37 |
-
# channel_dict.get('actual_mroi'),
|
38 |
-
# channel_dict.get('modified_mroi'),
|
39 |
-
# channel_dict.get('actual_total_spends') * channel_dict.get('conversion_rate') / channel_dict.get('actual_total_sales'),
|
40 |
-
# channel_dict.get('modified_total_spends') * channel_dict.get('conversion_rate') / channel_dict.get('modified_total_sales')
|
41 |
-
])
|
42 |
-
|
43 |
-
summary_rows.append(['Total',
|
44 |
-
scenario_dict.get('actual_total_spends'),
|
45 |
-
scenario_dict.get('modified_total_spends'),
|
46 |
-
scenario_dict.get('actual_total_sales'),
|
47 |
-
scenario_dict.get('modified_total_sales'),
|
48 |
-
# scenario_dict.get('actual_total_sales') / scenario_dict.get('actual_total_spends'),
|
49 |
-
# scenario_dict.get('modified_total_sales') / scenario_dict.get('modified_total_spends'),
|
50 |
-
# '-',
|
51 |
-
# '-',
|
52 |
-
# scenario_dict.get('actual_total_spends') / scenario_dict.get('actual_total_sales'),
|
53 |
-
# scenario_dict.get('modified_total_spends') / scenario_dict.get('modified_total_sales')
|
54 |
-
])
|
55 |
-
|
56 |
-
columns_index = pd.MultiIndex.from_product([[''],['Channel']], names=["first", "second"])
|
57 |
-
columns_index = columns_index.append(pd.MultiIndex.from_product([['Spends','Prospects'
|
58 |
-
#,'ROI','MROI','Spend per NRPU'
|
59 |
-
],['Actual','Simulated']], names=["first", "second"]))
|
60 |
-
return pd.DataFrame(summary_rows, columns=columns_index)
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
def summary_df_to_worksheet(df, ws):
|
65 |
-
heading_fill = PatternFill(fill_type='solid',start_color='FF11B6BD',end_color='FF11B6BD')
|
66 |
-
for j,header in enumerate(df.columns.values):
|
67 |
-
col = j + 1
|
68 |
-
for i in range(1,3):
|
69 |
-
ws.cell(row=i, column=j + 1, value=header[i - 1]).font = Font(bold=True, color='FF11B6BD')
|
70 |
-
ws.cell(row=i,column=j+1).fill = heading_fill
|
71 |
-
if col > 1 and (col - 6)%5==0:
|
72 |
-
ws.merge_cells(start_row=1, end_row=1, start_column = col-3, end_column=col)
|
73 |
-
ws.cell(row=1,column=col).alignment = Alignment(horizontal='center')
|
74 |
-
for i,row in enumerate(df.itertuples()):
|
75 |
-
for j,value in enumerate(row):
|
76 |
-
if j == 0:
|
77 |
-
continue
|
78 |
-
elif (j-2)%4 == 0 or (j-3)%4 == 0:
|
79 |
-
ws.cell(row=i+3, column = j, value=value).number_format = '$#,##0.0'
|
80 |
-
else:
|
81 |
-
ws.cell(row=i+3, column = j, value=value)
|
82 |
-
|
83 |
-
from openpyxl.utils import get_column_letter
|
84 |
-
from openpyxl.styles import Font, PatternFill
|
85 |
-
import logging
|
86 |
-
|
87 |
-
def scenario_df_to_worksheet(df, ws):
|
88 |
-
heading_fill = PatternFill(start_color='FF11B6BD', end_color='FF11B6BD', fill_type='solid')
|
89 |
-
|
90 |
-
for j, header in enumerate(df.columns.values):
|
91 |
-
cell = ws.cell(row=1, column=j + 1, value=header)
|
92 |
-
cell.font = Font(bold=True, color='FF11B6BD')
|
93 |
-
cell.fill = heading_fill
|
94 |
-
|
95 |
-
for i, row in enumerate(df.itertuples()):
|
96 |
-
for j, value in enumerate(row[1:], start=1): # Start from index 1 to skip the index column
|
97 |
-
try:
|
98 |
-
cell = ws.cell(row=i + 2, column=j, value=value)
|
99 |
-
if isinstance(value, (int, float)):
|
100 |
-
cell.number_format = '$#,##0.0'
|
101 |
-
elif isinstance(value, str):
|
102 |
-
cell.value = value[:32767]
|
103 |
-
else:
|
104 |
-
cell.value = str(value)
|
105 |
-
except ValueError as e:
|
106 |
-
logging.error(f"Error assigning value '{value}' to cell {get_column_letter(j)}{i+2}: {e}")
|
107 |
-
cell.value = None # Assign None to the cell where the error occurred
|
108 |
-
|
109 |
-
return ws
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
def download_scenarios():
|
117 |
-
"""
|
118 |
-
Makes a excel with all saved scenarios and saves it locally
|
119 |
-
"""
|
120 |
-
## create summary page
|
121 |
-
if len(scenarios_to_download) == 0:
|
122 |
-
return
|
123 |
-
wb = Workbook()
|
124 |
-
wb.iso_dates = True
|
125 |
-
wb.remove(wb.active)
|
126 |
-
st.session_state['xlsx_buffer'] = io.BytesIO()
|
127 |
-
summary_df = None
|
128 |
-
#print(scenarios_to_download)
|
129 |
-
for scenario_name in scenarios_to_download:
|
130 |
-
scenario_dict = st.session_state['saved_scenarios'][scenario_name]
|
131 |
-
_spends = []
|
132 |
-
column_names = ['Date']
|
133 |
-
_sales = None
|
134 |
-
dates = None
|
135 |
-
summary_rows = []
|
136 |
-
for channel in scenario_dict['channels']:
|
137 |
-
if dates is None:
|
138 |
-
dates = channel.get('dates')
|
139 |
-
_spends.append(dates)
|
140 |
-
if _sales is None:
|
141 |
-
_sales = channel.get('modified_sales')
|
142 |
-
else:
|
143 |
-
_sales += channel.get('modified_sales')
|
144 |
-
_spends.append(channel.get('modified_spends') * channel.get('conversion_rate'))
|
145 |
-
column_names.append(channel.get('name'))
|
146 |
-
|
147 |
-
name_mod = channel_name_formating(channel['name'])
|
148 |
-
summary_rows.append([name_mod,
|
149 |
-
channel.get('modified_total_spends') * channel.get('conversion_rate') ,
|
150 |
-
channel.get('modified_total_sales'),
|
151 |
-
channel.get('modified_total_sales') / channel.get('modified_total_spends') * channel.get('conversion_rate'),
|
152 |
-
channel.get('modified_mroi'),
|
153 |
-
channel.get('modified_total_sales') / channel.get('modified_total_spends') * channel.get('conversion_rate')])
|
154 |
-
_spends.append(_sales)
|
155 |
-
column_names.append('NRPU')
|
156 |
-
scenario_df = pd.DataFrame(_spends).T
|
157 |
-
scenario_df.columns = column_names
|
158 |
-
## write to sheet
|
159 |
-
ws = wb.create_sheet(scenario_name)
|
160 |
-
scenario_df_to_worksheet(scenario_df, ws)
|
161 |
-
summary_rows.append(['Total',
|
162 |
-
scenario_dict.get('modified_total_spends') ,
|
163 |
-
scenario_dict.get('modified_total_sales'),
|
164 |
-
scenario_dict.get('modified_total_sales') / scenario_dict.get('modified_total_spends'),
|
165 |
-
'-',
|
166 |
-
scenario_dict.get('modified_total_spends') / scenario_dict.get('modified_total_sales')])
|
167 |
-
columns_index = pd.MultiIndex.from_product([[''],['Channel']], names=["first", "second"])
|
168 |
-
columns_index = columns_index.append(pd.MultiIndex.from_product([[scenario_name],['Spends','NRPU','ROI','MROI','Spends per NRPU']], names=["first", "second"]))
|
169 |
-
if summary_df is None:
|
170 |
-
summary_df = pd.DataFrame(summary_rows, columns = columns_index)
|
171 |
-
summary_df = summary_df.set_index(('','Channel'))
|
172 |
-
else:
|
173 |
-
_df = pd.DataFrame(summary_rows, columns = columns_index)
|
174 |
-
_df = _df.set_index(('','Channel'))
|
175 |
-
summary_df = summary_df.merge(_df, left_index=True, right_index=True)
|
176 |
-
ws = wb.create_sheet('Summary',0)
|
177 |
-
summary_df_to_worksheet(summary_df.reset_index(), ws)
|
178 |
-
wb.save(st.session_state['xlsx_buffer'])
|
179 |
-
st.session_state['disable_download_button'] = False
|
180 |
-
|
181 |
-
def disable_download_button():
|
182 |
-
st.session_state['disable_download_button'] =True
|
183 |
-
|
184 |
-
def transform(x):
|
185 |
-
if x.name == ("",'Channel'):
|
186 |
-
return x
|
187 |
-
elif x.name[0] == 'ROI' or x.name[0] == 'MROI':
|
188 |
-
return x.apply(lambda y : y if isinstance(y,str) else decimal_formater(format_numbers(y,include_indicator=False,n_decimals=4),n_decimals=4))
|
189 |
-
else:
|
190 |
-
return x.apply(lambda y : y if isinstance(y,str) else format_numbers(y))
|
191 |
-
|
192 |
-
def delete_scenario():
|
193 |
-
if selected_scenario in st.session_state['saved_scenarios']:
|
194 |
-
del st.session_state['saved_scenarios'][selected_scenario]
|
195 |
-
with open('../saved_scenarios.pkl', 'wb') as f:
|
196 |
-
pickle.dump(st.session_state['saved_scenarios'],f)
|
197 |
-
|
198 |
-
def load_scenario():
|
199 |
-
if selected_scenario in st.session_state['saved_scenarios']:
|
200 |
-
st.session_state['scenario'] = class_from_dict(selected_scenario_details)
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
authenticator = st.session_state.get('authenticator')
|
205 |
-
if authenticator is None:
|
206 |
-
authenticator = load_authenticator()
|
207 |
-
|
208 |
-
name, authentication_status, username = authenticator.login('Login', 'main')
|
209 |
-
auth_status = st.session_state.get('authentication_status')
|
210 |
-
|
211 |
-
if auth_status == True:
|
212 |
-
is_state_initiaized = st.session_state.get('initialized',False)
|
213 |
-
if not is_state_initiaized:
|
214 |
-
#print("Scenario page state reloaded")
|
215 |
-
initialize_data()
|
216 |
-
|
217 |
-
|
218 |
-
saved_scenarios = st.session_state['saved_scenarios']
|
219 |
-
|
220 |
-
|
221 |
-
if len(saved_scenarios) ==0:
|
222 |
-
st.header('No saved scenarios')
|
223 |
-
|
224 |
-
else:
|
225 |
-
|
226 |
-
with st.sidebar:
|
227 |
-
selected_scenario = st.radio(
|
228 |
-
'Pick a scenario to view details',
|
229 |
-
list(saved_scenarios.keys())
|
230 |
-
)
|
231 |
-
st.markdown("""<hr>""", unsafe_allow_html=True)
|
232 |
-
scenarios_to_download = st.multiselect('Select scenarios to download',
|
233 |
-
list(saved_scenarios.keys()))
|
234 |
-
|
235 |
-
st.button('Prepare download',on_click=download_scenarios)
|
236 |
-
st.download_button(
|
237 |
-
label="Download Scenarios",
|
238 |
-
data=st.session_state['xlsx_buffer'].getvalue(),
|
239 |
-
file_name="scenarios.xlsx",
|
240 |
-
mime="application/vnd.ms-excel",
|
241 |
-
disabled= st.session_state['disable_download_button'],
|
242 |
-
on_click= disable_download_button
|
243 |
-
)
|
244 |
-
|
245 |
-
column_1, column_2,column_3 = st.columns((6,1,1))
|
246 |
-
with column_1:
|
247 |
-
st.header(selected_scenario)
|
248 |
-
with column_2:
|
249 |
-
st.button('Delete scenarios', on_click=delete_scenario)
|
250 |
-
with column_3:
|
251 |
-
st.button('Load Scenario', on_click=load_scenario)
|
252 |
-
|
253 |
-
selected_scenario_details = saved_scenarios[selected_scenario]
|
254 |
-
|
255 |
-
pd.set_option('display.max_colwidth', 100)
|
256 |
-
|
257 |
-
st.markdown(create_scenario_summary(selected_scenario_details).transform(transform).style.set_table_styles(
|
258 |
-
[{
|
259 |
-
'selector': 'th',
|
260 |
-
'props': [('background-color', '#11B6BD')]
|
261 |
-
},
|
262 |
-
{
|
263 |
-
'selector' : 'tr:nth-child(even)',
|
264 |
-
'props' : [('background-color', '#11B6BD')]
|
265 |
-
}
|
266 |
-
]).to_html(),unsafe_allow_html=True)
|
267 |
-
|
268 |
-
elif auth_status == False:
|
269 |
-
st.error('Username/Password is incorrect')
|
270 |
-
|
271 |
-
if auth_status != True:
|
272 |
-
try:
|
273 |
-
username_forgot_pw, email_forgot_password, random_password = authenticator.forgot_password('Forgot password')
|
274 |
-
if username_forgot_pw:
|
275 |
-
st.success('New password sent securely')
|
276 |
-
# Random password to be transferred to user securely
|
277 |
-
elif username_forgot_pw == False:
|
278 |
-
st.error('Username not found')
|
279 |
-
except Exception as e:
|
280 |
-
st.error(e)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|