Spaces:
Sleeping
Sleeping
import streamlit as st | |
from numerize.numerize import numerize | |
import numpy as np | |
from functools import partial | |
from collections import OrderedDict | |
from plotly.subplots import make_subplots | |
import plotly.graph_objects as go | |
from datetime import datetime, timedelta | |
from dateutil.relativedelta import relativedelta | |
import time | |
import Streamlit_functions as sf | |
from utilities import ( | |
format_numbers,format_numbers_f, | |
load_local_css, | |
set_header, | |
initialize_data, | |
load_authenticator, | |
send_email, | |
channel_name_formating, | |
) | |
from io import BytesIO | |
# import xlsxwriter | |
import warnings | |
# Suppress specific warnings if necessary | |
warnings.filterwarnings("ignore") | |
warnings.filterwarnings("ignore", category=UserWarning, message="The widget with key") | |
# for i in : | |
# warnings.filterwarnings("ignore",) | |
from classes import class_from_dict, class_to_dict | |
import pickle | |
import streamlit_authenticator as stauth | |
import yaml | |
from yaml import SafeLoader | |
import re | |
import pandas as pd | |
import plotly.express as px | |
import response_curves_model_quality as rc | |
st.set_page_config(layout="wide") | |
load_local_css("styles.css") | |
set_header() | |
for k, v in st.session_state.items(): | |
if k not in ["logout", "login", "config"] and not k.startswith("FormSubmitter"): | |
st.session_state[k] = v | |
# ======================================================== # | |
# ======================= Functions ====================== # | |
# ======================================================== # | |
def optimize(key, status_placeholder): | |
""" | |
Optimize the spends for the sales | |
""" | |
channel_list = [ | |
key for key, value in st.session_state["optimization_channels"].items() if value | |
] | |
if len(channel_list) > 0: | |
scenario = st.session_state["scenario"] | |
if key.lower() == "media spends": | |
with status_placeholder: | |
with st.spinner("Optimizing"): | |
# # print(channel_list) | |
# # print(st.session_state["total_spends_change"]) | |
result = st.session_state["scenario"].optimize( | |
st.session_state["total_spends_change"], channel_list | |
# result = st.session_state["scenario"].spends_optimisation( | |
# st.session_state["total_spends_change"], channel_list | |
) | |
# elif key.lower() == "revenue": | |
else: | |
with status_placeholder: | |
with st.spinner("Optimizing"): | |
result = st.session_state["scenario"].optimize_spends( | |
st.session_state["total_sales_change"], channel_list | |
) | |
for channel_name, modified_spends in result: | |
st.session_state[channel_name] = numerize( | |
modified_spends * scenario.channels[channel_name].conversion_rate, | |
1, | |
) | |
prev_spends = ( | |
st.session_state["scenario"].channels[channel_name].actual_total_spends | |
) | |
st.session_state[f"{channel_name}_change"] = round( | |
100 * (modified_spends - prev_spends) / prev_spends, 2 | |
) | |
def save_scenario(scenario_name): | |
""" | |
Save the current scenario with the mentioned name in the session state | |
Parameters | |
---------- | |
scenario_name | |
Name of the scenario to be saved | |
""" | |
if "saved_scenarios" not in st.session_state: | |
st.session_state = OrderedDict() | |
# st.session_state['saved_scenarios'][scenario_name] = st.session_state['scenario'].save() | |
st.session_state["saved_scenarios"][scenario_name] = class_to_dict( | |
st.session_state["scenario"] | |
) | |
st.session_state["scenario_input"] = "" | |
# # print(type(st.session_state['saved_scenarios'])) | |
with open("../saved_scenarios.pkl", "wb") as f: | |
pickle.dump(st.session_state["saved_scenarios"], f) | |
if "allow_spends_update" not in st.session_state: | |
st.session_state["allow_spends_update"] = True | |
if "allow_sales_update" not in st.session_state: | |
st.session_state["allow_sales_update"] = True | |
def update_sales_abs_slider(): | |
actual_sales = _scenario.actual_total_sales | |
if validate_input(st.session_state["total_sales_change_abs_slider"]): | |
modified_sales = extract_number_for_string( | |
st.session_state["total_sales_change_abs_slider"] | |
) | |
st.session_state["total_sales_change"] = round( | |
((modified_sales / actual_sales) - 1) * 100 | |
) | |
st.session_state["total_sales_change_abs"] = numerize(modified_sales, 1) | |
def update_sales_abs(): | |
if ( | |
st.session_state["total_sales_change_abs"] | |
in st.session_state["total_sales_change_abs_slider_options"] | |
): | |
st.session_state["allow_sales_update"] = True | |
else: | |
st.session_state["allow_sales_update"] = False | |
actual_sales = _scenario.actual_total_sales | |
if ( | |
validate_input(st.session_state["total_sales_change_abs"]) | |
and st.session_state["allow_sales_update"] | |
): | |
modified_sales = extract_number_for_string( | |
st.session_state["total_sales_change_abs"] | |
) | |
st.session_state["total_sales_change"] = round( | |
((modified_sales / actual_sales) - 1) * 100 | |
) | |
st.session_state["total_sales_change_abs_slider"] = numerize(modified_sales, 1) | |
def update_sales(): | |
st.session_state["total_sales_change_abs"] = numerize( | |
(1 + st.session_state["total_sales_change"] / 100) | |
* _scenario.actual_total_sales, | |
1, | |
) | |
st.session_state["total_sales_change_abs_slider"] = numerize( | |
(1 + st.session_state["total_sales_change"] / 100) | |
* _scenario.actual_total_sales, | |
1, | |
) | |
# def update_all_spends_abs_slider(): | |
# actual_spends = _scenario.actual_total_spends | |
# if validate_input(st.session_state["total_spends_change_abs_slider"]): | |
# modified_spends = extract_number_for_string( | |
# st.session_state["total_spends_change_abs_slider"] | |
# ) | |
# st.session_state["total_spends_change"] = round( | |
# ((modified_spends / actual_spends) - 1) * 100 | |
# ) | |
# st.session_state["total_spends_change_abs"] = numerize(modified_spends, 1) | |
# update_all_spends() | |
# def update_all_spends_abs_slider(): | |
# actual_spends = _scenario.actual_total_spends | |
# if validate_input(st.session_state["total_spends_change_abs_slider"]): | |
# # print("#" * 100) | |
# # print(st.session_state["total_spends_change_abs_slider"])C:\Users\PragyaJatav\Downloads\Untitled Folder 2\simulatorAldi\pages\8_Scenario_Planner.py | |
# # print("#" * 100) | |
# modified_spends = extract_number_for_string( | |
# st.session_state["total_spends_change_abs_slider"] | |
# ) | |
# st.session_state["total_spends_change"] = ( | |
# (modified_spends / actual_spends) - 1 | |
# ) * 100 | |
# st.session_state["total_spends_change_abs"] = st.session_state[ | |
# "total_spends_change_abs_slider" | |
# ] | |
# update_all_spends() | |
def update_all_spends_abs(): | |
if ( | |
st.session_state["total_spends_change_abs"] | |
in st.session_state["total_spends_change_abs_slider_options"] | |
): | |
st.session_state["allow_spends_update"] = True | |
else: | |
st.session_state["allow_spends_update"] = False | |
actual_spends = _scenario.actual_total_spends | |
if ( | |
validate_input(st.session_state["total_spends_change_abs"]) | |
and st.session_state["allow_spends_update"] | |
): | |
modified_spends = extract_number_for_string( | |
st.session_state["total_spends_change_abs"] | |
) | |
st.session_state["total_spends_change"] = ( | |
(modified_spends / actual_spends) - 1 | |
) * 100 | |
st.session_state["total_spends_change_abs_slider"] = st.session_state[ | |
"total_spends_change_abs" | |
] | |
update_all_spends() | |
def update_spends(): | |
st.session_state["total_spends_change_abs"] = numerize( | |
(1 + st.session_state["total_spends_change"] / 100) | |
* _scenario.actual_total_spends, | |
1, | |
) | |
st.session_state["total_spends_change_abs_slider"] = numerize( | |
(1 + st.session_state["total_spends_change"] / 100) | |
* _scenario.actual_total_spends, | |
1, | |
) | |
update_all_spends() | |
def update_all_spends(): | |
""" | |
Updates spends for all the channels with the given overall spends change | |
""" | |
percent_change = st.session_state["total_spends_change"] | |
for channel_name in st.session_state["channels_list"]: | |
channel = st.session_state["scenario"].channels[channel_name] | |
current_spends = channel.actual_total_spends | |
modified_spends = (1 + percent_change / 100) * current_spends | |
st.session_state["scenario"].update(channel_name, modified_spends) | |
st.session_state[channel_name] = numerize( | |
modified_spends * channel.conversion_rate, 1 | |
) | |
st.session_state[f"{channel_name}_change"] = percent_change | |
def extract_number_for_string(string_input): | |
string_input = string_input.upper() | |
if string_input.endswith("K"): | |
return float(string_input[:-1]) * 10**3 | |
elif string_input.endswith("M"): | |
return float(string_input[:-1]) * 10**6 | |
elif string_input.endswith("B"): | |
return float(string_input[:-1]) * 10**9 | |
def validate_input(string_input): | |
pattern = r"\d+\.?\d*[K|M|B]$" | |
match = re.match(pattern, string_input) | |
if match is None: | |
return False | |
return True | |
def update_data_bound_min(channel_name): | |
""" | |
Updates the bounds for the given channel | |
""" | |
modified_bounds = st.session_state[f"{channel_name}_lower_bound"] | |
# st.session_state['scenario']['channels'][channel_name].channel_bounds_min = st.session_state[f"{channel_name}_lower_bound"] | |
st.session_state['scenario'].update_bounds_min(channel_name,modified_bounds) | |
st.write(st.session_state["scenario"].channels[channel_name].channel_bounds_min) | |
# st.write(st.session_state["scenario"].channels[channel_name]) | |
def update_data_bound_max(channel_name): | |
""" | |
Updates the bounds for the given channel | |
""" | |
modified_bounds = st.session_state[f"{channel_name}_upper_bound"] | |
# st.session_state['scenario']['channels'][channel_name].channel_bounds_min = st.session_state[f"{channel_name}_lower_bound"] | |
st.session_state['scenario'].update_bounds_max(channel_name,modified_bounds) | |
# st.write(st.session_state["scenario"].channels[channel_name].channel_bounds_max) | |
# st.write(st.session_state["scenario"].channels[channel_name]) | |
def update_data_by_percent(channel_name): | |
prev_spends = ( | |
st.session_state["scenario"].channels[channel_name].actual_total_spends | |
* st.session_state["scenario"].channels[channel_name].conversion_rate | |
) | |
modified_spends = prev_spends * ( | |
1 + st.session_state[f"{channel_name}_change"] / 100 | |
) | |
st.session_state[channel_name] = numerize(modified_spends, 1) | |
st.session_state["scenario"].update( | |
channel_name, | |
modified_spends | |
/ st.session_state["scenario"].channels[channel_name].conversion_rate, | |
) | |
def update_data(channel_name): | |
""" | |
Updates the spends for the given channel | |
""" | |
if validate_input(st.session_state[channel_name]): | |
modified_spends = extract_number_for_string(st.session_state[channel_name]) | |
prev_spends = ( | |
st.session_state["scenario"].channels[channel_name].actual_total_spends | |
* st.session_state["scenario"].channels[channel_name].conversion_rate | |
) | |
st.session_state[f"{channel_name}_change"] = round( | |
100 * (modified_spends - prev_spends) / prev_spends, 2 | |
) | |
st.session_state["scenario"].update( | |
channel_name, | |
modified_spends | |
/ st.session_state["scenario"].channels[channel_name].conversion_rate, | |
) | |
# st.write(hasattr(st.session_state["scenario"], 'update_bounds_min')) | |
# st.session_state['scenario'].update(channel_name, modified_spends) | |
# else: | |
# try: | |
# modified_spends = float(st.session_state[channel_name]) | |
# prev_spends = st.session_state['scenario'].channels[channel_name].actual_total_spends * st.session_state['scenario'].channels[channel_name].conversion_rate | |
# st.session_state[f'{channel_name}_change'] = round(100*(modified_spends - prev_spends) / prev_spends,2) | |
# st.session_state['scenario'].update(channel_name, modified_spends/st.session_state['scenario'].channels[channel_name].conversion_rate) | |
# st.session_state[f'{channel_name}'] = numerize(modified_spends,1) | |
# except ValueError: | |
# st.write('Invalid input') | |
def select_channel_for_optimization(channel_name): | |
""" | |
Marks the given channel for optimization | |
""" | |
st.session_state["optimization_channels"][channel_name] = st.session_state[ | |
f"{channel_name}_selected" | |
] | |
# if not all(st.session_state["optimization_channels"].values()): | |
# st.session_state["optimize_all_channels"] = False | |
def select_all_channels_for_optimization(): | |
""" | |
Marks all the channel for optimization | |
""" | |
for channel_name in st.session_state["optimization_channels"].keys(): | |
st.session_state[f"{channel_name}_selected"] = st.session_state[ | |
"optimze_all_channels" | |
] | |
st.session_state["optimization_channels"][channel_name] = st.session_state[ | |
"optimze_all_channels" | |
] | |
def update_penalty(): | |
""" | |
Updates the penalty flag for sales calculation | |
""" | |
st.session_state["scenario"].update_penalty(st.session_state["apply_penalty"]) | |
def reset_scenario(panel_selected, file_selected, updated_rcs): | |
# ## print(st.session_state['default_scenario_dict']) | |
# st.session_state['scenario'] = class_from_dict(st.session_state['default_scenario_dict']) | |
# for channel in st.session_state['scenario'].channels.values(): | |
# st.session_state[channel.name] = float(channel.actual_total_spends * channel.conversion_rate) | |
# initialize_data() | |
if panel_selected == "Total Market": | |
initialize_data( | |
target_file=file_selected, | |
panel=panel_selected, | |
updated_rcs=updated_rcs, | |
metrics=metrics_selected, | |
) | |
panel = None | |
else: | |
initialize_data( | |
target_file=file_selected, | |
panel=panel_selected, | |
updated_rcs=updated_rcs, | |
metrics=metrics_selected, | |
) | |
for channel_name in st.session_state["channels_list"]: | |
st.session_state[f"{channel_name}_selected"] = False | |
st.session_state[f"{channel_name}_change"] = 0 | |
st.session_state["optimze_all_channels"] = False | |
st.session_state["total_sales_change"] = 0 | |
update_spends() | |
update_sales() | |
reset_inputs() | |
# st.rerun() | |
def format_number(num): | |
if num >= 1_000_000: | |
return f"{num / 1_000_000:.2f}M" | |
elif num >= 1_000: | |
return f"{num / 1_000:.0f}K" | |
else: | |
return f"{num:.2f}" | |
def summary_plot(data, x, y, title, text_column): | |
fig = px.bar( | |
data, | |
x=x, | |
y=y, | |
orientation="h", | |
title=title, | |
text=text_column, | |
color="Channel_name", | |
) | |
# Convert text_column to numeric values | |
data[text_column] = pd.to_numeric(data[text_column], errors="coerce") | |
# Update the format of the displayed text based on magnitude | |
fig.update_traces( | |
texttemplate="%{text:.2s}", | |
textposition="outside", | |
hovertemplate="%{x:.2s}", | |
) | |
fig.update_layout(xaxis_title=x, yaxis_title="Channel Name", showlegend=False) | |
return fig | |
def s_curve(x, K, b, a, x0): | |
return K / (1 + b * np.exp(-a * (x - x0))) | |
def find_segment_value(x, roi, mroi): | |
start_value = x[0] | |
end_value = x[len(x) - 1] | |
# Condition for green region: Both MROI and ROI > 1 | |
green_condition = (roi > 1) & (mroi > 1) | |
left_indices = np.where(green_condition)[0] | |
left_value = x[left_indices[0]] if left_indices.size > 0 else x[0] | |
right_indices = np.where(green_condition)[0] | |
right_value = x[right_indices[-1]] if right_indices.size > 0 else x[0] | |
return start_value, end_value, left_value, right_value | |
def calculate_rgba( | |
start_value, end_value, left_value, right_value, current_channel_spends | |
): | |
# Initialize alpha to None for clarity | |
alpha = None | |
# Determine the color and calculate relative_position and alpha based on the point's position | |
if start_value <= current_channel_spends <= left_value: | |
color = "yellow" | |
relative_position = (current_channel_spends - start_value) / ( | |
left_value - start_value | |
) | |
alpha = 0.8 - (0.6 * relative_position) # Alpha decreases from start to end | |
elif left_value < current_channel_spends <= right_value: | |
color = "green" | |
relative_position = (current_channel_spends - left_value) / ( | |
right_value - left_value | |
) | |
alpha = 0.8 - (0.6 * relative_position) # Alpha decreases from start to end | |
elif right_value < current_channel_spends <= end_value: | |
color = "red" | |
relative_position = (current_channel_spends - right_value) / ( | |
end_value - right_value | |
) | |
alpha = 0.2 + (0.6 * relative_position) # Alpha increases from start to end | |
else: | |
# Default case, if the spends are outside the defined ranges | |
return "rgba(136, 136, 136, 0.5)" # Grey for values outside the range | |
# Ensure alpha is within the intended range in case of any calculation overshoot | |
alpha = max(0.2, min(alpha, 0.8)) | |
# Define color codes for RGBA | |
color_codes = { | |
"yellow": "255, 255, 0", # RGB for yellow | |
"green": "0, 128, 0", # RGB for green | |
"red": "255, 0, 0", # RGB for red | |
} | |
rgba = f"rgba({color_codes[color]}, {alpha})" | |
return rgba | |
def debug_temp(x_test, power, K, b, a, x0): | |
# # print("*" * 100) | |
# Calculate the count of bins | |
count_lower_bin = sum(1 for x in x_test if x <= 2524) | |
count_center_bin = sum(1 for x in x_test if x > 2524 and x <= 3377) | |
count_ = sum(1 for x in x_test if x > 3377) | |
# # print( | |
# f""" | |
# lower : {count_lower_bin} | |
# center : {count_center_bin} | |
# upper : {count_} | |
# """ | |
# ) | |
# @st.cache | |
def plot_response_curves(summary_df_sorted): | |
# rows = ( | |
# len(channels_list) // cols | |
# if len(channels_list) % cols == 0 | |
# else len(channels_list) // cols + 1 | |
# ) | |
# rcs = st.session_state["rcs"] | |
# shapes = [] | |
# fig = make_subplots(rows=rows, cols=cols, subplot_titles=channels_list) | |
channel_cols = [ | |
'BroadcastTV', | |
'CableTV', | |
'Connected&OTTTV', | |
'DisplayProspecting', | |
'DisplayRetargeting', | |
'Video', | |
'SocialProspecting', | |
'SocialRetargeting', | |
'SearchBrand', | |
'SearchNon-brand', | |
'DigitalPartners', | |
'Audio', | |
'Email'] | |
summary_df_sorted.index = summary_df_sorted["Channel_name"] | |
figures = [rc.response_curves(channels_list[i], summary_df_sorted["Optimized_spend"][channels_list[i]]/104, summary_df_sorted["New_sales"][channels_list[i]]/104) for i in range(13)] | |
# for i in range() | |
# Display figures in a grid layout | |
cols = st.columns(3) # 4 columns for the grid | |
for idx, fig in enumerate(figures): | |
col = cols[idx % 3] | |
with col: | |
st.plotly_chart(fig, use_container_width=True) | |
# ======================================================== # | |
# ==================== HTML Components =================== # | |
# ======================================================== # | |
def generate_spending_header(heading): | |
return st.markdown( | |
f"""<h4 class="spends-header"><bold>{heading}<bold></h4>""", unsafe_allow_html=True | |
) | |
# ======================================================== # | |
# =================== Session variables ================== # | |
# ======================================================== # | |
with open("config.yaml") as file: | |
config = yaml.load(file, Loader=SafeLoader) | |
st.session_state["config"] = config | |
authenticator = stauth.Authenticate( | |
config["credentials"], | |
config["cookie"]["name"], | |
config["cookie"]["key"], | |
config["cookie"]["expiry_days"], | |
config["preauthorized"], | |
) | |
st.session_state["authenticator"] = authenticator | |
name, authentication_status, username = authenticator.login("Login", "main") | |
auth_status = st.session_state.get("authentication_status") | |
import os | |
import glob | |
def upload_file_prospects_calc(df): | |
df["Prospects"] = 0 | |
params = pd.read_excel(r"response_curves_parameters.xlsx",index_col = "channel") | |
param_dicts = {col: params[col].to_dict() for col in params.columns} | |
df.index = df.channel | |
# # print(param_dicts) | |
for col in df.channel: | |
x = (df["Spends"][col]/104 - param_dicts["x_min"][col])/(param_dicts["x_max"][col]-param_dicts["x_min"][col]) | |
x_out = x**param_dicts["n"][col]/(param_dicts["Kd"][col]**param_dicts["n"][col]+ x**param_dicts["n"][col]) | |
x_out_inv = (x_out*(param_dicts["y_max"][col]-param_dicts["y_min"][col])+param_dicts["y_min"][col])*104 | |
df["Prospects"][col] = x_out_inv | |
# # print(df) | |
return df | |
def upload_file_format(df): | |
df1 = df.transpose() | |
df1.reset_index(inplace = True) | |
df1.columns = df1.iloc[0] | |
df1 = df1[1:] | |
df1["channel"] = pd.to_datetime('1999-08-06').date() | |
df1.rename(columns = {"channel":"Date"},inplace = True) | |
df2 = df1.rename(columns = {"Date":"Week"}) | |
df3 =upload_file_prospects_calc(df) | |
df3 = df3[["Prospects"]].transpose().reset_index() | |
df3["index"] = pd.to_datetime('1999-08-06').date() | |
df3.rename(columns = {"index":"Date"},inplace = True) | |
df3.insert(1, 'const', [0]) | |
# Create a buffer to hold the Excel file | |
import io | |
output = io.BytesIO() | |
# Write the dataframes to an Excel file | |
with pd.ExcelWriter(output, engine='openpyxl') as writer: | |
df1.to_excel(writer, index=False, sheet_name='RAW DATA MMM') | |
df2.to_excel(writer, index=False, sheet_name='SPEND INPUT') | |
df3.to_excel(writer, index=False, sheet_name='CONTRIBUTION MMM') | |
# Seek to the beginning of the stream | |
output.seek(0) | |
with open('Overview_data_uploaded.xlsx', 'wb') as f: | |
f.write(output.getvalue()) | |
return | |
def get_excel_names(directory): | |
# Create a list to hold the final parts of the filenames | |
last_portions = [] | |
# Patterns to match Excel files (.xlsx and .xls) that contain @# | |
patterns = [ | |
os.path.join(directory, "*@#*.xlsx"), | |
os.path.join(directory, "*@#*.xls"), | |
] | |
# Process each pattern | |
for pattern in patterns: | |
files = glob.glob(pattern) | |
# Extracting the last portion after @# for each file | |
for file in files: | |
base_name = os.path.basename(file) | |
last_portion = base_name.split("@#")[-1] | |
last_portion = last_portion.replace(".xlsx", "").replace( | |
".xls", "" | |
) # Removing extensions | |
last_portions.append(last_portion) | |
return last_portions | |
def name_formating(channel_name): | |
# Replace underscores with spaces | |
name_mod = channel_name.replace("_", " ") | |
# Capitalize the first letter of each word | |
name_mod = name_mod.title() | |
return name_mod | |
def panel_fetch(file_selected): | |
raw_data_mmm_df = pd.read_excel(file_selected, sheet_name="RAW DATA MMM") | |
# if "Panel" in raw_data_mmm_df.columns: | |
# panel = list(set(raw_data_mmm_df["Panel"])) | |
# else: | |
# raw_data_mmm_df = None | |
# panel = None | |
# raw_data_mmm_df = None | |
panel = None | |
return panel | |
def reset_inputs(): | |
if "total_spends_change_abs" in st.session_state: | |
del st.session_state.total_spends_change_abs | |
if "total_spends_change" in st.session_state: | |
del st.session_state.total_spends_change | |
if "total_spends_change_abs_slider" in st.session_state: | |
del st.session_state.total_spends_change_abs_slider | |
if "total_sales_change_abs" in st.session_state: | |
del st.session_state.total_sales_change_abs | |
if "total_sales_change" in st.session_state: | |
del st.session_state.total_sales_change | |
if "total_sales_change_abs_slider" in st.session_state: | |
del st.session_state.total_sales_change_abs_slider | |
st.session_state["initialized"] = False | |
def scenario_planner_plots(): | |
with st.expander('Optimized Spends Overview'): | |
# if st.button('Refresh'): | |
# st.experimental_rerun() | |
import plotly.graph_objects as go | |
from plotly.subplots import make_subplots | |
# Define light colors for bars | |
import plotly.graph_objects as go | |
from plotly.subplots import make_subplots | |
st.empty() | |
#st.header('Model Result Analysis') | |
spends_data=pd.read_excel('Overview_data_test.xlsx') | |
with open('summary_df.pkl', 'rb') as file: | |
summary_df_sorted = pickle.load(file) | |
#st.write(summary_df_sorted) | |
# selected_scenario= st.selectbox('Select Saved Scenarios',['S1','S2']) | |
summary_df_sorted=summary_df_sorted.sort_values(by=['Optimized_spend'],ascending=False) | |
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()) | |
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()) | |
summary_df_sorted['old_roi']=summary_df_sorted['Old_sales']/summary_df_sorted['Actual_spend'] | |
summary_df_sorted['new_roi']=summary_df_sorted['New_sales']/summary_df_sorted['Optimized_spend'] | |
total_actual_spend = summary_df_sorted['Actual_spend'].sum() | |
total_optimized_spend = summary_df_sorted['Optimized_spend'].sum() | |
actual_spend_percentage = (summary_df_sorted['Actual_spend'] / total_actual_spend) * 100 | |
optimized_spend_percentage = (summary_df_sorted['Optimized_spend'] / total_optimized_spend) * 100 | |
light_blue = 'rgba(0, 31, 120, 0.7)' | |
light_orange = 'rgba(0, 181, 219, 0.7)' | |
light_green = 'rgba(240, 61, 20, 0.7)' | |
light_red = 'rgba(250, 110, 10, 0.7)' | |
light_purple = 'rgba(255, 191, 69, 0.7)' | |
fig = go.Figure() | |
# Add actual vs optimized spend bars | |
fig.add_trace(go.Bar(x=summary_df_sorted['Channel_name'].apply(channel_name_formating), y=summary_df_sorted['Actual_spend'], name='Actual', | |
text=summary_df_sorted['Actual_spend'].apply(format_number) + ' ' | |
# + | |
# ' '+ | |
# '</br> (' + actual_spend_percentage.astype(int).astype(str) + '%)' | |
,textposition='outside',#textfont=dict(size=30), | |
marker_color=light_blue)) | |
fig.add_trace(go.Bar(x=summary_df_sorted['Channel_name'].apply(channel_name_formating), y=summary_df_sorted['Optimized_spend'], name='Optimized', | |
text=summary_df_sorted['Optimized_spend'].apply(format_number) + ' ' | |
# + | |
# '</br> (' + optimized_spend_percentage.astype(int).astype(str) + '%)' | |
,textposition='outside',#textfont=dict(size=30), | |
marker_color=light_orange)) | |
fig.update_xaxes(title_text="Channels") | |
fig.update_yaxes(title_text="Spends ($)") | |
fig.update_layout( | |
title = "Actual vs. Optimized Spends", | |
margin=dict(t=40, b=40, l=40, r=40) | |
) | |
st.plotly_chart(fig,use_container_width=True) | |
# Add actual vs optimized Contribution | |
fig = go.Figure() | |
fig.add_trace(go.Bar(x=summary_df_sorted['Channel_name'].apply(channel_name_formating), y=summary_df_sorted['Old_sales'], | |
name='Actual Contribution',text=summary_df_sorted['Old_sales'].apply(format_number),textposition='outside', | |
marker_color=light_blue,showlegend=True)) | |
fig.add_trace(go.Bar(x=summary_df_sorted['Channel_name'].apply(channel_name_formating), y=summary_df_sorted['New_sales'], | |
name='Optimized Contribution',text=summary_df_sorted['New_sales'].apply(format_number),textposition='outside', | |
marker_color=light_orange, showlegend=True)) | |
fig.update_yaxes(title_text="Contribution") | |
fig.update_xaxes(title_text="Channels") | |
fig.update_layout( | |
title = "Actual vs. Optimized Contributions", | |
margin=dict(t=40, b=40, l=40, r=40) | |
# yaxis=dict(range=[0, 0.002]), | |
) | |
st.plotly_chart(fig,use_container_width=True) | |
# Add actual vs optimized Efficiency bars | |
fig = go.Figure() | |
summary_df_sorted_p = summary_df_sorted[summary_df_sorted['Channel_name']!="Panel"] | |
fig.add_trace(go.Bar(x=summary_df_sorted_p['Channel_name'].apply(channel_name_formating), y=summary_df_sorted_p['old_efficiency'], | |
name='Actual Efficiency', text=summary_df_sorted_p['old_efficiency'].apply(format_number) ,textposition='outside', | |
marker_color=light_blue,showlegend=True)) | |
fig.add_trace(go.Bar(x=summary_df_sorted_p['Channel_name'].apply(channel_name_formating), y=summary_df_sorted_p['new_efficiency'], | |
name='Optimized Efficiency',text=summary_df_sorted_p['new_efficiency'].apply(format_number),textposition='outside' , | |
marker_color=light_orange,showlegend=True)) | |
fig.update_xaxes(title_text="Channels") | |
fig.update_yaxes(title_text="Efficiency") | |
fig.update_layout( | |
title = "Actual vs. Optimized Efficiency", | |
margin=dict(t=40, b=40, l=40, r=40), | |
# yaxis=dict(range=[0, 0.002]), | |
) | |
st.plotly_chart(fig,use_container_width=True) | |
if auth_status == True: | |
authenticator.logout("Logout", "main") | |
st.header("Scenario Planner") | |
data_selected = st.selectbox( | |
"Select base data for optimisation", options=["Optimise Actual Spends", "Optimise Uploaded Spends"], | |
key = "data_upload_key",index = 1) | |
# st.text_input("") | |
# Response Metrics | |
directory = "metrics_level_data" | |
metrics_list = get_excel_names(directory) | |
# metrics_selected = col1.selectbox( | |
# "Response Metrics", | |
# metrics_list, | |
# format_func=name_formating, | |
# index=0, | |
# on_change=reset_inputs, | |
# ) | |
metrics_selected='prospects' | |
# Target | |
target = name_formating(metrics_selected) | |
# file_selected = ( | |
# f"Overview_data_test_panel@#{metrics_selected}.xlsx" | |
# ) | |
file_selected = None | |
if data_selected == "Optimise Uploaded Spends": | |
st.write("Select a file to upload") | |
uploaded_file = st.file_uploader("Choose an Excel file", type=["xlsx", "xls"]) | |
if uploaded_file: | |
try: | |
# Read the Excel file using pandas | |
df = pd.read_excel(uploaded_file, engine='openpyxl') | |
upload_file_format(df) | |
file_selected = "Overview_data_uploaded.xlsx" | |
# initialize_data( | |
# target_file=file_selected, | |
# panel="Total Market", | |
# updated_rcs=None, | |
# metrics=metrics_selected, | |
# ) | |
except Exception as e: | |
st.error(f"Error reading the file: {e}") | |
elif data_selected == "Optimise Actual Spends": | |
file_selected = ( | |
f"Overview_data_test_panel@#{metrics_selected}.xlsx" | |
) | |
# initialize_data( | |
# target_file=file_selected, | |
# panel="Total Market", | |
# updated_rcs=None, | |
# metrics=metrics_selected, | |
# ) | |
else : | |
st.write("") | |
if file_selected: | |
st.session_state['file_selected']=file_selected | |
# Panel List | |
panel_list = panel_fetch(file_selected) | |
# # Panel Selected | |
# panel_selected = st.selectbox( | |
# "Markets", | |
# ["Total Market"] + panel_list, | |
# index=0, | |
# on_change=reset_inputs, | |
# ) | |
# st.write(panel_selected) | |
panel_selected = "Total Market" | |
st.session_state['selected_markets']=panel_selected | |
if "update_rcs" in st.session_state: | |
updated_rcs = st.session_state["update_rcs"] | |
else: | |
updated_rcs = None | |
if "first_time" not in st.session_state: | |
st.session_state["first_time"] = True | |
# Check if state is initiaized | |
is_state_initiaized = st.session_state.get("initialized", False) | |
if not is_state_initiaized or st.session_state["first_time"]: | |
initialize_data( | |
target_file=file_selected, | |
panel=panel_selected, | |
updated_rcs=updated_rcs, | |
metrics=metrics_selected, | |
) | |
st.session_state["initialized"] = True | |
st.session_state["first_time"] = False | |
# initialize_data( | |
# panel=panel_selected, | |
# target_file=file_selected, | |
# updated_rcs=updated_rcs, | |
# metrics=metrics_selected, | |
# ) | |
# st.session_state["initialized"] = True | |
# st.session_state["first_time"] = False | |
# Channels List | |
channels_list = st.session_state["channels_list"] | |
# ======================================================== # | |
# ========================== UI ========================== # | |
# ======================================================== # | |
# # print(list(st.session_state.keys())) | |
main_header = st.columns((2, 2)) | |
sub_header = st.columns((1, 1, 1, 1)) | |
_scenario = st.session_state["scenario"] | |
if "total_spends_change" not in st.session_state: | |
st.session_state.total_spends_change = 0 | |
if "total_sales_change" not in st.session_state: | |
st.session_state.total_sales_change = 0 | |
if "total_spends_change_abs" not in st.session_state: | |
st.session_state["total_spends_change_abs"] = numerize( | |
_scenario.actual_total_spends, 1 | |
) | |
# st.write(_scenario.actual_total_sales) | |
if "total_sales_change_abs" not in st.session_state: | |
st.session_state["total_sales_change_abs"] = numerize( | |
_scenario.actual_total_sales, 1 | |
) | |
if "total_spends_change_abs_slider" not in st.session_state: | |
st.session_state.total_spends_change_abs_slider = numerize( | |
_scenario.actual_total_spends, 1 | |
) | |
if "total_sales_change_abs_slider" not in st.session_state: | |
st.session_state.total_sales_change_abs_slider = numerize( | |
_scenario.actual_total_sales, 1 | |
) | |
if "lower_bound_key" not in st.session_state: | |
st.session_state["lower_bound_key"] = 10 | |
if "upper_bound_key" not in st.session_state: | |
st.session_state["upper_bound_key"] = 10 | |
# st.write(_scenario.modified_total_sales) | |
header_df = pd.DataFrame(index=["Actual","Simulated","Change","Percent Change"],columns=["Spends","Prospects"]) | |
header_df["Spends"]["Actual"] = format_numbers(_scenario.actual_total_spends) | |
header_df["Spends"]["Simulated"] = format_numbers(_scenario.modified_total_spends) | |
header_df["Spends"]["Change"] = format_numbers(_scenario.delta_spends) #_scenario.modified_total_spends -_scenario.actual_total_spends | |
header_df["Spends"]["Percent Change"] = numerize(100*(_scenario.delta_spends/_scenario.actual_total_spends))+'%' | |
header_df["Prospects"]["Actual"] = format_numbers_f(float(_scenario.actual_total_sales)) | |
header_df["Prospects"]["Simulated"] = format_numbers_f(float(_scenario.modified_total_sales)) | |
header_df["Prospects"]["Change"] = format_numbers_f(_scenario.delta_sales) | |
header_df["Prospects"]["Percent Change"] = numerize(100*(_scenario.delta_sales/_scenario.actual_total_sales),1)+'%' | |
st.markdown("""<hr class="spends-heading-seperator">""", unsafe_allow_html=True) | |
_columns = st.columns((1, 1, 1, 1, 1)) | |
st.markdown( | |
""" | |
<style> | |
.custom-text_head { | |
font-size: 24px; /* Adjust font size */ | |
color: 'blue' ; /* Adjust text color */ | |
font-weight: bold; | |
} | |
</style> | |
""", | |
unsafe_allow_html=True | |
) | |
with _columns[0]: | |
st.markdown(f'<p class="custom-text_head">{"Metrics"}</p>', unsafe_allow_html=True) | |
# generate_spending_header("Metric") | |
with _columns[1]: | |
st.markdown(f'<p class="custom-text_head">{"Actual"}</p>', unsafe_allow_html=True) | |
# generate_spending_header("Actual") | |
with _columns[2]: | |
st.markdown(f'<p class="custom-text_head">{"Simulated"}</p>', unsafe_allow_html=True) | |
# generate_spending_header("Optimised") | |
with _columns[3]: | |
st.markdown(f'<p class="custom-text_head">{"Change"}</p>', unsafe_allow_html=True) | |
# generate_spending_header("Change") | |
with _columns[4]: | |
st.markdown(f'<p class="custom-text_head">{"Change Percent"}</p>', unsafe_allow_html=True) | |
# generate_spending_header("Change Percent") | |
st.markdown("""<hr class="spends-heading-seperator">""", unsafe_allow_html=True) | |
_columns = st.columns((1, 1, 1, 1, 1)) | |
with _columns[0]: | |
st.markdown("""<h4> Spends</h4>""",unsafe_allow_html=True) | |
# st.write("Spends") | |
with _columns[1]: | |
st.markdown(f"""<h4>{header_df["Spends"]["Actual"]}</h4>""",unsafe_allow_html=True) | |
# st.metric(label="", value=header_df["Spends"]["Actual"]) | |
with _columns[2]: | |
st.markdown(f"""<h4>{header_df["Spends"]["Simulated"]}</h4>""",unsafe_allow_html=True) | |
if _scenario.delta_spends < 0: | |
st.markdown( | |
""" | |
<style> | |
.custom-text { | |
font-size: 24px; /* Adjust font size */ | |
color: #6bbf6b ; /* Adjust text color */ | |
} | |
</style> | |
""", | |
unsafe_allow_html=True | |
) | |
else: | |
st.markdown( | |
""" | |
<style> | |
.custom-text { | |
font-size: 24px; /* Adjust font size */ | |
color: #ff6868; /* Adjust text color */ | |
} | |
</style> | |
""", | |
unsafe_allow_html=True | |
) | |
# st.metric(label="", value=header_df["Spends"]["Simulated"]) | |
with _columns[3]: | |
# Apply custom styles to text | |
st.markdown(f'<h4 class="custom-text">{header_df["Spends"]["Change"]}</h4>', unsafe_allow_html=True) | |
with _columns[4]: | |
# Apply custom styles to text | |
# st.markdown(f'<p></hr></p>', unsafe_allow_html=True) | |
st.markdown(f'<h4 class="custom-text">{header_df["Spends"]["Percent Change"]}</h4>', unsafe_allow_html=True) | |
st.markdown( | |
"""<hr class="spends-child-seperator">""", | |
unsafe_allow_html=True, | |
) | |
_columns = st.columns((1, 1, 1, 1, 1)) | |
with _columns[0]: | |
# st.header("Prospects") | |
st.markdown("""<h4> Prospects</h4>""",unsafe_allow_html=True) | |
with _columns[1]: | |
st.markdown(f"""<h4>{header_df["Prospects"]["Actual"]}</h4>""",unsafe_allow_html=True) | |
# st.metric(label="", value=header_df["Prospects"]["Actual"]) | |
with _columns[2]: | |
st.markdown(f"""<h4>{header_df["Prospects"]["Simulated"]}</h4>""",unsafe_allow_html=True) | |
# st.metric(label="", value=header_df["Prospects"]["Simulated"]) | |
if _scenario.delta_sales >= 0: | |
st.markdown( | |
""" | |
<style> | |
.custom-text { | |
font-size: 24px; /* Adjust font size */ | |
color:#6bbf6b ; /* Adjust text color */ | |
} | |
</style> | |
""", | |
unsafe_allow_html=True | |
) | |
else: | |
st.markdown("""<style>.custom-text {font-size: 24px; /* Adjust font size */color: #ff6868; /* Adjust text color */}</style>""",unsafe_allow_html=True) | |
with _columns[3]: | |
# Apply custom styles to text | |
st.markdown(f'<h4 class="custom-text">{header_df["Prospects"]["Change"]}</h4>', unsafe_allow_html=True) | |
# st.markdown(f'<p style="color: red;">{st.metric(label="", value=header_df["Prospects"]["Change"])}</p>', unsafe_allow_html=True) | |
# st.markdown(f'<p style="color: red;">{header_df["Prospects"]["Change"]}</p>', unsafe_allow_html=True) | |
with _columns[4]: | |
# st.markdown(f'<p></hr></p>', unsafe_allow_html=True) | |
# Apply custom styles to text | |
st.markdown(f'<h4 class="custom-text">{header_df["Prospects"]["Percent Change"]}</h4>', unsafe_allow_html=True) | |
st.markdown( | |
"""<hr class="spends-child-seperator">""", | |
unsafe_allow_html=True, | |
) | |
_columns = st.columns((1, 1, 1, 1, 1)) | |
ef1 = (_scenario.actual_total_spends/_scenario.actual_total_sales) | |
ef2 = (_scenario.modified_total_spends/_scenario.modified_total_sales) | |
with _columns[0]: | |
st.markdown("""<h4>Cost Per Prospect</h4>""",unsafe_allow_html=True) | |
# st.header("Cost Per Prospect") | |
with _columns[1]: | |
st.markdown(f"""<h4>{'$ '+numerize(ef1,0)}</h4>""",unsafe_allow_html=True) | |
# st.metric(label="", value='$ '+numerize(ef1,0)) | |
with _columns[2]: | |
st.markdown(f"""<h4>{'$ '+numerize(ef2,0)}</h4>""",unsafe_allow_html=True) | |
# st.metric(label="", value='$ '+numerize(ef2,0)) | |
if ef2 <= ef1: | |
st.markdown( | |
""" | |
<style> | |
.custom-text1 { | |
font-size: 24px; /* Adjust font size */ | |
color:#6bbf6b ; /* Adjust text color */ | |
} | |
</style> | |
""", | |
unsafe_allow_html=True | |
) | |
else: | |
st.markdown( | |
""" | |
<style> | |
.custom-text1 { | |
font-size: 24px; /* Adjust font size */ | |
color: #ff6868; /* Adjust text color */ | |
} | |
</style> | |
""", | |
unsafe_allow_html=True | |
) | |
with _columns[3]: | |
# Apply custom styles to text | |
st.markdown(f'<h4 class="custom-text1">{"$ "+numerize(ef2-ef1,0)}</h4>', unsafe_allow_html=True) | |
# st.markdown(f'<p style="color: red;">{st.metric(label="", value=header_df["Prospects"]["Change"])}</p>', unsafe_allow_html=True) | |
# st.markdown(f'<p style="color: red;">{header_df["Prospects"]["Change"]}</p>', unsafe_allow_html=True) | |
with _columns[4]: | |
# st.markdown(f'<p></hr></p>', unsafe_allow_html=True) | |
# Apply custom styles to text | |
st.markdown(f'<h4 class="custom-text1">{round((ef2-ef1)/ef1*100,2)}%</h4>', unsafe_allow_html=True) | |
st.markdown("""<hr class="spends-child-seperator">""",unsafe_allow_html=True) | |
# st.markdown("""<hr class="spends-heading-seperator">""", unsafe_allow_html=True) | |
# header_df.reset_index(inplace=True) | |
# # Function to color the index | |
# def highlight_index(s): | |
# return ['background-color: lightblue' for _ in s] | |
# # Function to color the header | |
# def highlight_header(s): | |
# return ['background-color: lightgreen' for _ in s] | |
# # Applying the styles | |
# styled_df = header_df.style \ | |
# .apply(highlight_index, axis=0, subset=pd.IndexSlice[:, :]) \ | |
# .set_table_styles({ | |
# 'A': [{'selector': 'th', 'props': [('background-color', 'lightgreen')]}], | |
# 'B': [{'selector': 'th', 'props': [('background-color', 'lightgreen')]}], | |
# 'C': [{'selector': 'th', 'props': [('background-color', 'lightgreen')]}] | |
# }) | |
# # Function to apply arrows based on value | |
# def format_arrows(val): | |
# if val > 0: | |
# return '<span style="color: green;">▲</span>' # Green up arrow | |
# elif val < 0: | |
# return '<span style="color: red;">▼</span>' # Red down arrow | |
# return '' # No arrow for zero | |
# # Function to format specific rows and exclude the first column | |
# def apply_row_formatting(df, rows): | |
# def format_cell(val, row_idx, col_idx): | |
# if row_idx in rows and col_idx > 0: # Exclude the first column (col_idx > 0) | |
# return format_arrows(val) | |
# return '' # No formatting for other cells | |
# return df.style.apply(lambda x: [format_cell(val, i, col) for i, (val, col) in enumerate(zip(x, range(len(x))))], axis=1) | |
# # Apply formatting to 3rd and 4th rows (index 2 and 3) | |
# styled_df = apply_row_formatting(header_df, [2, 3]) | |
# st.markdown(styled_df.to_html(escape=False), unsafe_allow_html=True) | |
# st.markdown(header_df.style.set_table_styles | |
# ([{'selector': 'th', | |
# 'props': [('background-color', '#D3D3D3'), | |
# ('font-size', '25px')]}, | |
# { | |
# 'selector' : 'td:first-child', | |
# 'props' : [('background-color', '#D3D3D3'), | |
# ('font-size', '25px')] | |
# } | |
# , | |
# {'selector': 'tbody td', | |
# 'props': [('font-size', '20px')]} | |
# ]).to_html(),unsafe_allow_html=True) | |
# styled_df = header_df.style.apply(highlight_first_col, axis=1) | |
# st.table(styled_df) | |
# with main_header[0]: | |
# st.subheader("Actual") | |
# with main_header[-1]: | |
# st.subheader("Simulated") | |
# with sub_header[0]: | |
# st.metric(label="Spends", value=format_numbers(_scenario.actual_total_spends)) | |
# with sub_header[1]: | |
# st.metric( | |
# label=target, | |
# value=format_numbers_f( | |
# float(_scenario.actual_total_sales) | |
# ), | |
# ) | |
# with sub_header[2]: | |
# st.metric( | |
# label="Spends", | |
# value=format_numbers(_scenario.modified_total_spends), | |
# delta=numerize(_scenario.delta_spends, 1), | |
# ) | |
# with sub_header[3]: | |
# st.metric( | |
# label=target, | |
# value=format_numbers_f( | |
# float(_scenario.modified_total_sales) | |
# ), | |
# delta=numerize(_scenario.delta_sales, 1), | |
# ) | |
with st.expander("Channel Spends Simulator", expanded=True): | |
_columns1 = st.columns((1.5, 1.5, 1,1)) | |
with _columns1[0]: | |
optimization_selection = st.selectbox( | |
"Optimize", options=["Media Spends", target], key="optimization_key" | |
) | |
with _columns1[1]: | |
# st.markdown("#") | |
# if st.checkbox( | |
# label="Optimize all Channels", | |
# key="optimze_all_channels", | |
# value=False, | |
# # on_change=select_all_channels_for_optimization, | |
# ): | |
# select_all_channels_for_optimization() | |
st.checkbox( | |
label="Optimize all Channels", | |
key="optimze_all_channels", | |
value=False, | |
on_change=select_all_channels_for_optimization, | |
) | |
with _columns1[2]: | |
# st.button( | |
# "Optimize", | |
# on_click=optimize, | |
# args=(st.session_state["optimization_key"]), | |
# use_container_width=True, | |
# ) | |
optimize_placeholder = st.empty() | |
with _columns1[3]: | |
# st.markdown("#") | |
st.button( | |
"Reset", | |
on_click=reset_scenario, | |
args=(panel_selected, file_selected, updated_rcs), | |
# use_container_width=True, | |
) | |
# st.write(target) | |
_columns2 = st.columns((2, 2, 2,2)) | |
if st.session_state["optimization_key"] == "Media Spends": | |
with _columns2[0]: | |
spend_input = st.text_input( | |
"Absolute", | |
key="total_spends_change_abs", | |
# label_visibility="collapsed", | |
on_change=update_all_spends_abs, | |
) | |
with _columns2[1]: | |
st.number_input( | |
"Percent Change", | |
key="total_spends_change", | |
min_value=-50.00, | |
max_value=50.00, | |
step=1.00, | |
value=0.00, | |
on_change=update_spends, | |
) | |
with _columns2[2]: | |
overall_lower_bound = st.number_input( | |
"Overall Lower Bound for Spends", | |
value = 50 | |
# key = overall_lower_bound, | |
# on_change=partial(update_data_bound_min_overall) | |
) | |
with _columns2[3]: | |
overall_upper_bound = st.number_input( | |
"Overall Upper Bound for Spends", | |
value = 50 | |
# key = overall_upper_bound, | |
# on_change=partial(update_data_bound_max_overall) | |
) | |
min_value = round(_scenario.actual_total_spends * (1-overall_lower_bound/100)) | |
max_value = round(_scenario.actual_total_spends * (1-overall_upper_bound/100)) | |
st.session_state["total_spends_change_abs_slider_options"] = [ | |
numerize(value, 1) | |
for value in range(min_value, max_value + 1, int(1e4)) | |
] | |
# st.select_slider( | |
# "Absolute Slider", | |
# options=st.session_state["total_spends_change_abs_slider_options"], | |
# key="total_spends_change_abs_slider", | |
# on_change=update_all_spends_abs_slider, | |
# ) | |
elif st.session_state["optimization_key"] == target: | |
# st.write(target) | |
with _columns2[0]: | |
sales_input = st.text_input( | |
"Absolute", | |
key="total_sales_change_abs", | |
on_change=update_sales_abs, | |
) | |
with _columns2[1]: | |
st.number_input( | |
"Percent Change", | |
key="total_sales_change", | |
min_value=-50.00, | |
max_value=50.00, | |
step=1.00, | |
value=0.00, | |
on_change=update_sales, | |
) | |
with _columns2[2]: | |
overall_lower_bound = st.number_input( | |
"Overall Lower Bound for Spends", | |
value = 50 | |
) | |
with _columns2[3]: | |
overall_upper_bound = st.number_input( | |
"Overall Upper Bound for Spends", | |
value = 50 | |
) | |
min_value = round(_scenario.actual_total_sales * (1-overall_lower_bound/100)) | |
max_value = round(_scenario.actual_total_sales * (1+overall_upper_bound/100)) | |
# st.write(min_value) | |
# st.write(max_value) | |
# for value in range(min_value, max_value + 1, int(100)): | |
# st.write(numerize(value, 1)) | |
# st.session_state["total_sales_change_abs_slider_options"] = [ | |
# numerize(value, 1) | |
# for value in range(min_value, max_value + 1, int(100)) | |
# ] | |
# st.select_slider( | |
# "Absolute Slider", | |
# options=st.session_state["total_sales_change_abs_slider_options"], | |
# key="total_sales_change_abs_slider", | |
# on_change=update_sales_abs_slider, | |
# # value=numerize(min_value, 1) | |
# ) | |
if ( | |
not st.session_state["allow_sales_update"] | |
and optimization_selection == target | |
): | |
st.warning("Invalid Input") | |
if ( | |
not st.session_state["allow_spends_update"] | |
and optimization_selection == "Media Spends" | |
): | |
st.warning("Invalid Input") | |
status_placeholder = st.empty() | |
# if optimize_placeholder.button("Optimize", use_container_width=True): | |
# optimize(st.session_state["optimization_key"], status_placeholder) | |
# st.rerun() | |
optimize_placeholder.button( | |
"Optimize", | |
on_click=optimize, | |
args=(st.session_state["optimization_key"], status_placeholder), | |
# use_container_width=True, | |
) | |
st.markdown("""<hr class="spends-heading-seperator">""", unsafe_allow_html=True) | |
_columns = st.columns((1.5,2.5,2,2, 1)) | |
with _columns[0]: | |
generate_spending_header("Channel") | |
with _columns[1]: | |
generate_spending_header("Spends Input") | |
with _columns[2]: | |
generate_spending_header("Spends") | |
with _columns[3]: | |
generate_spending_header(target) | |
with _columns[4]: | |
generate_spending_header("Optimize") | |
st.markdown("""<hr class="spends-heading-seperator">""", unsafe_allow_html=True) | |
if "acutual_predicted" not in st.session_state: | |
st.session_state["acutual_predicted"] = { | |
"Channel_name": [], | |
"Actual_spend": [], | |
"Optimized_spend": [], | |
"Delta": [], | |
"New_sales":[], | |
"Old_sales":[] | |
} | |
for i, channel_name in enumerate(channels_list): | |
# st.write(channel_name) | |
_channel_class = st.session_state["scenario"].channels[channel_name] | |
# st.write(st.session_state["scenario"].channels[channel_name]) | |
# st.write(st.session_state["scenario"].channels[channel_name].actual_total_sales) | |
# st.write(st.session_state["scenario"].channels[channel_name].actual_total_spends) | |
# st.write(st.session_state["scenario"].channels[channel_name].modified_total_sales) | |
# st.write(st.session_state["scenario"].channels[channel_name].modified_total_spends) | |
# st.write(st.session_state["scenario"].channels[channel_name].bounds) | |
# st.write(st.session_state["scenario"].channels[channel_name].channel_bounds_min) | |
_columns = st.columns((1.5,2.5,2,2, 1)) | |
response_curve_params = pd.read_excel("response_curves_parameters.xlsx",index_col = "channel") | |
param_dicts = {col: response_curve_params[col].to_dict() for col in response_curve_params.columns} | |
with _columns[0]: | |
st.write(channel_name_formating(channel_name)) | |
bin_placeholder = st.container() | |
with _columns[1]: | |
channel_bounds = _channel_class.bounds | |
# st.write(channel_bounds) | |
channel_spends = float(_channel_class.actual_total_spends) | |
channel_bounds_min = float(_channel_class.channel_bounds_min) | |
channel_bounds_max = float(_channel_class.channel_bounds_max) | |
min_value = float((1 - channel_bounds_min / 100) * channel_spends) | |
max_value = float((1 + channel_bounds_max / 100) * channel_spends) | |
# st.write(channel_spends) | |
# st.write(min_value) | |
# st.write(max_value) | |
### print(st.session_state[channel_name]) | |
_columns_min = st.columns(2) | |
with _columns_min[0]: | |
spend_input = st.text_input( | |
"Absolute", | |
key=channel_name, | |
# label_visibility="collapsed", | |
on_change=partial(update_data, channel_name), | |
) | |
channel_name_lower_bound = f"{channel_name}_lower_bound" | |
if channel_name_lower_bound not in st.session_state: | |
st.session_state[channel_name_lower_bound] = str(round(param_dicts["x_min"][channel_name]*10400/param_dicts["current_spends"][channel_name])) | |
# st.write(st.session_state[channel_name_lower_bound]) | |
channel_bounds_min = st.text_input( | |
"Lower Bound Percentage", | |
key = channel_name_lower_bound, | |
on_change=partial(update_data_bound_min,channel_name) | |
) | |
# st.write(st.session_state[channel_name_lower_bound]) | |
if not validate_input(spend_input): | |
st.error("Invalid input") | |
channel_name_current = f"{channel_name}_change" | |
with _columns_min[1]: | |
st.number_input( | |
"Percent Change", | |
key=channel_name_current, | |
step=1.00,value=0.00, | |
on_change=partial(update_data_by_percent, channel_name), | |
) | |
channel_name_upper_bound = f"{channel_name}_upper_bound" | |
if channel_name_upper_bound not in st.session_state: | |
st.session_state[channel_name_upper_bound] = str(10) | |
channel_bounds_max = st.text_input( | |
"Upper Bound Percentage", | |
key = channel_name_upper_bound, | |
on_change=partial(update_data_bound_max,channel_name) | |
) | |
with _columns[2]: | |
# spends | |
current_channel_spends = float( | |
_channel_class.modified_total_spends | |
* _channel_class.conversion_rate | |
) | |
actual_channel_spends = float( | |
_channel_class.actual_total_spends * _channel_class.conversion_rate | |
) | |
spends_delta = float( | |
_channel_class.delta_spends * _channel_class.conversion_rate | |
) | |
st.session_state["acutual_predicted"]["Channel_name"].append( | |
channel_name | |
) | |
st.session_state["acutual_predicted"]["Actual_spend"].append( | |
actual_channel_spends | |
) | |
st.session_state["acutual_predicted"]["Optimized_spend"].append( | |
current_channel_spends | |
) | |
st.session_state["acutual_predicted"]["Delta"].append(spends_delta) | |
_spend_cols = st.columns(2) | |
with _spend_cols[0]: | |
# st.write("Actual") | |
st.markdown(f'<p> Actual <h5>{format_numbers(actual_channel_spends)}</h5> </p>', unsafe_allow_html=True) | |
# st.metric( | |
# label="Actual Spends", | |
# value=format_numbers(actual_channel_spends), | |
# # delta=numerize(spends_delta, 1), | |
# # label_visibility="collapsed", | |
# ) | |
# st.write("Actual") | |
st.markdown(f'<p> Change <h5>{format_numbers(spends_delta)}</h5> </p>', unsafe_allow_html=True) | |
# st.markdown(f'<h4 class="custom-text1">{format_numbers(spends_delta)}%</h4>', unsafe_allow_html=True) | |
# st.metric( | |
# label="Change", | |
# value= format_numbers_f(spends_delta), | |
# delta=numerize(spends_delta, 1), | |
# # label_visibility="collapsed", | |
# ) | |
with _spend_cols[1]: | |
st.markdown(f'<p> Simulated <h5>{format_numbers(current_channel_spends)}</h5> </p>', unsafe_allow_html=True) | |
st.markdown(f'<p>Percent<h5>{numerize(( spends_delta/actual_channel_spends)*100,0) +"%"}</h5> </p>', unsafe_allow_html=True) | |
# st.metric( | |
# label="Simulated Spends", | |
# value=format_numbers(current_channel_spends), | |
# # delta=numerize(spends_delta, 1), | |
# # label_visibility="collapsed", | |
# ) | |
# st.metric( | |
# label="Percent Change", | |
# value= numerize(( spends_delta/actual_channel_spends)*100,0) +"%", | |
# delta=numerize(spends_delta, 1), | |
# # label_visibility="collapsed", | |
# ) | |
with _columns[3]: | |
# sales | |
current_channel_sales = float(_channel_class.modified_total_sales) | |
actual_channel_sales = float(_channel_class.actual_total_sales) | |
sales_delta = float(_channel_class.delta_sales) | |
st.session_state["acutual_predicted"]["Old_sales"].append(actual_channel_sales) | |
st.session_state["acutual_predicted"]["New_sales"].append(current_channel_sales) | |
#st.write(actual_channel_sales) | |
_prospect_cols = st.columns(2) | |
with _prospect_cols[0]: | |
# st.write("Actual") | |
st.markdown(f'<p> Actual <h5>{format_numbers(actual_channel_sales)}</h5> </p>', unsafe_allow_html=True) | |
st.markdown(f'<p> Change <h5>{format_numbers(sales_delta)}</h5> </p>', unsafe_allow_html=True) | |
# st.metric( | |
# # target, | |
# label="Actual Prospects", | |
# value= format_numbers_f(actual_channel_sales), | |
# # delta=numerize(sales_delta, 1), | |
# # label_visibility="collapsed", | |
# ) | |
# st.metric( | |
# label="Change", | |
# value= format_numbers_f(_channel_class.delta_sales), | |
# delta=numerize(sales_delta, 1), | |
# # label_visibility="collapsed", | |
# ) | |
with _prospect_cols[1]: | |
st.markdown(f'<p> Simulated <h5>{format_numbers(current_channel_sales)}</h5> </p>', unsafe_allow_html=True) | |
st.markdown(f'<p>Percent<h5>{numerize(( _channel_class.delta_sales/actual_channel_sales)*100,0) +"%"}</h5> </p>', unsafe_allow_html=True) | |
# st.metric( | |
# label="Simulated Prospects", | |
# value= format_numbers_f(current_channel_sales), | |
# # delta=numerize(sales_delta, 1), | |
# # label_visibility="collapsed", | |
# ) | |
# st.metric( | |
# label="Percent Change", | |
# value= numerize((_channel_class.delta_sales/actual_channel_sales)*100,0) +"%", | |
# delta=numerize(sales_delta, 1), | |
# # label_visibility="collapsed", | |
# ) | |
with _columns[4]: | |
# if st.checkbox( | |
# label="select for optimization", | |
# key=f"{channel_name}_selected", | |
# value=False, | |
# # on_change=partial(select_channel_for_optimization, channel_name), | |
# label_visibility="collapsed", | |
# ): | |
# select_channel_for_optimization(channel_name) | |
st.checkbox( | |
label="select for optimization", | |
key=f"{channel_name}_selected", | |
value=False, | |
on_change=partial(select_channel_for_optimization, channel_name), | |
label_visibility="collapsed", | |
) | |
st.markdown( | |
"""<hr class="spends-child-seperator">""", | |
unsafe_allow_html=True, | |
) | |
# Bins | |
col = channels_list[i] | |
x_actual = st.session_state["scenario"].channels[col].actual_spends | |
x_modified = st.session_state["scenario"].channels[col].modified_spends | |
# x_modified_total = 0 | |
# for c in channels_list: | |
# # st.write(c) | |
# # st.write(st.session_state["scenario"].channels[c].modified_spends) | |
# x_modified_total = x_modified_total + st.session_state["scenario"].channels[c].modified_spends.sum() | |
# st.write(x_modified_total) | |
x_total = x_modified.sum() | |
power = np.ceil(np.log(x_actual.max()) / np.log(10)) - 3 | |
updated_rcs_key = f"{metrics_selected}#@{panel_selected}#@{channel_name}" | |
# if updated_rcs and updated_rcs_key in list(updated_rcs.keys()): | |
# K = updated_rcs[updated_rcs_key]["K"] | |
# b = updated_rcs[updated_rcs_key]["b"] | |
# a = updated_rcs[updated_rcs_key]["a"] | |
# x0 = updated_rcs[updated_rcs_key]["x0"] | |
# else: | |
# K = st.session_state["rcs"][col]["K"] | |
# b = st.session_state["rcs"][col]["b"] | |
# a = st.session_state["rcs"][col]["a"] | |
# x0 = st.session_state["rcs"][col]["x0"] | |
# x_plot = np.linspace(0, 5 * x_actual.sum(), 200) | |
# # Append current_channel_spends to the end of x_plot | |
# x_plot = np.append(x_plot, current_channel_spends) | |
# x, y, marginal_roi = [], [], [] | |
# for x_p in x_plot: | |
# x.append(x_p * x_actual / x_actual.sum()) | |
# for index in range(len(x_plot)): | |
# y.append(s_curve(x[index] / 10**power, K, b, a, x0)) | |
# for index in range(len(x_plot)): | |
# marginal_roi.append( | |
# a * y[index] * (1 - y[index] / np.maximum(K, np.finfo(float).eps)) | |
# ) | |
# x = ( | |
# np.sum(x, axis=1) | |
# * st.session_state["scenario"].channels[col].conversion_rate | |
# ) | |
# y = np.sum(y, axis=1) | |
# marginal_roi = ( | |
# np.average(marginal_roi, axis=1) | |
# / st.session_state["scenario"].channels[col].conversion_rate | |
# ) | |
# roi = y / np.maximum(x, np.finfo(float).eps) | |
# # roi = (y/np.sum(y))/(x/np.sum(x)) | |
# # st.write(x) | |
# # st.write(y) | |
# # st.write(roi) | |
# # st.write(roi[-1]) | |
# roi_current, marginal_roi_current = roi[-1], marginal_roi[-1] | |
# x, y, roi, marginal_roi = ( | |
# x[:-1], | |
# y[:-1], | |
# roi[:-1], | |
# marginal_roi[:-1], | |
# ) # Drop data for current spends | |
# # roi_current = | |
# start_value, end_value, left_value, right_value = find_segment_value( | |
# x, | |
# roi, | |
# marginal_roi, | |
# ) | |
#st.write(roi_current) | |
# rgba = calculate_rgba( | |
# start_value, | |
# end_value, | |
# left_value, | |
# right_value, | |
# current_channel_spends, | |
# ) | |
# # print(st.session_state["acutual_predicted"]) | |
summary_df = pd.DataFrame(st.session_state["acutual_predicted"]) | |
# st.dataframe(summary_df) | |
summary_df.drop_duplicates(subset="Channel_name", keep="last", inplace=True) | |
# st.dataframe(summary_df) | |
summary_df_sorted = summary_df.sort_values(by="Delta", ascending=False) | |
summary_df_sorted["Delta_percent"] = np.round( | |
((summary_df_sorted["Optimized_spend"] / summary_df_sorted["Actual_spend"]) - 1) | |
* 100, | |
2, | |
) | |
summary_df_sorted=summary_df_sorted.sort_values(by=['Optimized_spend'],ascending=False) | |
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()) | |
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()) | |
a = (summary_df_sorted[summary_df_sorted['Channel_name']== col]).reset_index()['new_efficiency'][0] | |
b = (summary_df_sorted[summary_df_sorted['Channel_name']== col]).reset_index()['old_efficiency'][0] | |
# st.write(a) | |
with bin_placeholder: | |
if a> 1: | |
fill_color_box = "#6bbf6b" | |
elif a <1: | |
fill_color_box = "#ff6868" | |
else: | |
fill_color_box = "#ff6868" | |
st.markdown( | |
f""" | |
<div style=" | |
border-radius: 12px; | |
background-color: {fill_color_box}; | |
padding: 10px; | |
text-align: center; | |
color: {'black'}; | |
"> | |
<p style="margin: 0; font-size: 16px;">Simulated Efficiency: {round(a,2)} </br> Actual Efficiency: {round(b,2)} </p> | |
<!--<p style="margin: 0; font-size: 16px;">Marginal ROI: {round(b,1)}</p>--> | |
</div> | |
""", | |
unsafe_allow_html=True, | |
) | |
# <p style="margin: 0; font-size: 16px;">Simulated Efficiency: {round(a,2)} </br> Actual Efficiency: {round(b,2)} </p> | |
# <!--<p style="margin: 0; font-size: 16px;">Marginal ROI: {round(b,1)}</p>--> | |
with st.expander("See Response Curves", expanded=True): | |
fig = plot_response_curves(summary_df_sorted) | |
# st.plotly_chart(rc.response_curves(col)) | |
# st.plotly_chart(fig, use_container_width=True) | |
summary_df = pd.DataFrame(st.session_state["acutual_predicted"]) | |
# st.dataframe(summary_df) | |
summary_df.drop_duplicates(subset="Channel_name", keep="last", inplace=True) | |
# st.dataframe(summary_df) | |
summary_df_sorted = summary_df.sort_values(by="Delta", ascending=False) | |
summary_df_sorted["Delta_percent"] = np.round( | |
((summary_df_sorted["Optimized_spend"] / summary_df_sorted["Actual_spend"]) - 1) | |
* 100, | |
2, | |
) | |
with open("summary_df.pkl", "wb") as f: | |
pickle.dump(summary_df_sorted, f) | |
# st.dataframe(summary_df_sorted) | |
# ___columns=st.columns(3) | |
# with ___columns[2]: | |
# fig=summary_plot(summary_df_sorted, x='Delta_percent', y='Channel_name', title='Delta', text_column='Delta_percent') | |
# st.plotly_chart(fig,use_container_width=True) | |
# with ___columns[0]: | |
# fig=summary_plot(summary_df_sorted, x='Actual_spend', y='Channel_name', title='Actual Spend', text_column='Actual_spend') | |
# st.plotly_chart(fig,use_container_width=True) | |
# with ___columns[1]: | |
# fig=summary_plot(summary_df_sorted, x='Optimized_spend', y='Channel_name', title='Planned Spend', text_column='Optimized_spend') | |
# st.plotly_chart(fig,use_container_width=True) | |
scenario_planner_plots() | |
with st.expander ("View Forecasted spends"): | |
col1, col2, col3 = st.columns([1,1,0.75]) | |
# List of 12 months | |
months_start = ["January", "February", "March", "April", "May", "June", | |
"July", "August", "September", "October", "November", "December"] | |
years_start = range(2022,2100) | |
months_end = ["January", "February", "March", "April", "May", "June", | |
"July", "August", "September", "October", "November", "December"] | |
years_end = range(2022,2100) | |
with col1: | |
from datetime import date | |
st.write ("Select Start Time Period") | |
sc1,sc2 = st.columns([1,1]) | |
with sc1: | |
# Create a dropdown (selectbox) for months | |
start_date_mon = st.selectbox("Select Start Month:", months_start) | |
with sc2: | |
start_date_year = st.selectbox("Select Start Year:", years_start,index=2) | |
start_date1 = date(start_date_year, months_start.index(start_date_mon)+1, 1)- relativedelta(years=1) | |
# st.write(start_date1) | |
# default_Month = "January" | |
# start_date_mon = st.text_input("Select Start Month: ",value=default_Month) | |
# default_Year = 2024 | |
# start_date_year = st.number_input("Select Start Year: ",value=default_Year) | |
with col2: | |
st.write ("Select End Time Period") | |
ec1,ec2 = st.columns([1,1]) | |
with ec1: | |
end_date_mon = st.selectbox("Select End Month:", months_end,index=1) | |
with ec2: | |
end_date_year = st.selectbox("Select End Year:", years_end,index=2) | |
end_date1 = date(end_date_year, months_end.index(end_date_mon)+2, 1)- relativedelta(years=1) | |
# st.write(end_date1) | |
# default_Month = "February" | |
# end_date_mon = st.text_input("Select End Month: ",value=default_Month) | |
# default_Year = 2024 | |
# end_date_year = st.number_input("Select End Year: ",value=default_Year) | |
# end_date1 = st.date_input("Select End Date: ",value=default_date) - relativedelta(years=1) | |
with col3: | |
current_date = datetime.now() | |
c1, c2, c3 = st.columns(3) | |
with c1: | |
if st.button('Next Month'): | |
start_date1 = current_date- relativedelta(years=1) | |
end_date1 = current_date + relativedelta(months=1)- relativedelta(years=1) | |
with c2: | |
if st.button('Next Quarter'): | |
start_date1 = current_date- relativedelta(years=1) | |
end_date1 = current_date + relativedelta(months = 3)- relativedelta(years=1) | |
with c3: | |
if st.button('Next Year'): | |
start_date1 = current_date- relativedelta(years=1) | |
end_date1 = current_date + relativedelta(months = 12)- relativedelta(years=1) | |
forecasted_table_df = sf.scenario_spend_forecasting(summary_df_sorted,start_date1,end_date1) | |
st.dataframe(forecasted_table_df) | |
forecasted_table_df2 = sf.scenario_spend_forecasting2(summary_df_sorted,start_date1,end_date1) | |
st.dataframe(forecasted_table_df2) | |
st.markdown(""" | |
<style> | |
.yellow-container { | |
background-color: #FFFF99; | |
border: 1px solid #FFD700; | |
padding: 10px; | |
border-radius: 5px; | |
margin-bottom: 10px; | |
} | |
</style> | |
""", unsafe_allow_html=True) | |
def save_report_forecast(forecasted_table_df,forecasted_table_df2): | |
# Convert the DataFrame to an Excel file in memory | |
excel_file = BytesIO() | |
with pd.ExcelWriter(excel_file, engine='openpyxl') as writer: | |
forecasted_table_df.to_excel(writer, index=True, sheet_name='Forecasted Spends') | |
forecasted_table_df2.to_excel(writer, sheet_name='Monthly Breakdown',index = True) | |
# Seek to the beginning of the BytesIO buffer | |
excel_file.seek(0) | |
return excel_file | |
st.subheader("Download Report") | |
report_name = st.text_input( | |
"Report name", | |
key="report_input", | |
placeholder="Report name", | |
label_visibility="collapsed", | |
) | |
st.download_button( | |
"Download Report", | |
data = save_report_forecast(forecasted_table_df,forecasted_table_df2), | |
file_name = report_name+".xlsx", | |
mime="application/vnd.ms-excel", | |
# on_click=lambda: save_report_forecast(forecasted_table_df,report_name), | |
disabled=len(st.session_state["report_input"]) == 0,#use_container_width=True | |
) | |
# filename = st.text_input("Save Report: ",placeholder="Report name") | |
# if st.button("Download Report",disabled= (filename != "Report name")): | |
# excel_file_path = filename+ '.xlsx' | |
# forecasted_table_df.to_excel(excel_file_path, index=False) | |
# message_container = st.empty() | |
# with message_container: | |
# st.write(f'<div class="yellow-container">{"Report Saved!"}</div>', unsafe_allow_html=True) | |
# time.sleep(0.5) | |
# st.empty() | |
# on_click=lambda: save_scenario(scenario_name), | |
# disabled=len(st.session_state["scenario_input"]) == 0,#use_container_width=True | |
_columns = st.columns(2) | |
# with _columns[0]: | |
st.subheader("Save Scenario") | |
scenario_name = st.text_input( | |
"Scenario name", | |
key="scenario_input", | |
placeholder="Scenario name", | |
label_visibility="collapsed", | |
) | |
st.button( | |
"Save", | |
on_click=lambda: save_scenario(scenario_name), | |
disabled=len(st.session_state["scenario_input"]) == 0,#use_container_width=True | |
) | |
elif auth_status == False: | |
st.error("Username/Password is incorrect") | |
if auth_status != True: | |
try: | |
username_forgot_pw, email_forgot_password, random_password = ( | |
authenticator.forgot_password("Forgot password") | |
) | |
if username_forgot_pw: | |
st.session_state["config"]["credentials"]["usernames"][username_forgot_pw][ | |
"password" | |
] = stauth.Hasher([random_password]).generate()[0] | |
send_email(email_forgot_password, random_password) | |
st.success("New password sent securely") | |
# Random password to be transferred to user securely | |
elif username_forgot_pw == False: | |
st.error("Username not found") | |
except Exception as e: | |
st.error(e) | |