BlendMMM commited on
Commit
92a33be
·
verified ·
1 Parent(s): 0ddfcf7

Update utilities_with_panel.py

Browse files
Files changed (1) hide show
  1. utilities_with_panel.py +1025 -1025
utilities_with_panel.py CHANGED
@@ -1,1025 +1,1025 @@
1
- from numerize.numerize import numerize
2
- import streamlit as st
3
- import pandas as pd
4
- import json
5
- from classes import Channel, Scenario
6
- import numpy as np
7
- from plotly.subplots import make_subplots
8
- import plotly.graph_objects as go
9
- from classes import class_to_dict
10
- from collections import OrderedDict
11
- import io
12
- import plotly
13
- from pathlib import Path
14
- import pickle
15
- import streamlit_authenticator as stauth
16
- import yaml
17
- from yaml import SafeLoader
18
- from streamlit.components.v1 import html
19
- import smtplib
20
- from scipy.optimize import curve_fit
21
- from sklearn.metrics import r2_score
22
- from classes import class_from_dict
23
- import os
24
- import base64
25
-
26
-
27
-
28
-
29
- color_palette = ['#001f78', '#00b5db', '#f03d14', '#fa6e0a', '#ffbf45']
30
-
31
-
32
- CURRENCY_INDICATOR = '$'
33
-
34
- def load_authenticator():
35
- with open('config.yaml') as file:
36
- config = yaml.load(file, Loader=SafeLoader)
37
- st.session_state['config'] = config
38
- authenticator = stauth.Authenticate(
39
- config['credentials'],
40
- config['cookie']['name'],
41
- config['cookie']['key'],
42
- config['cookie']['expiry_days'],
43
- config['preauthorized']
44
- )
45
- st.session_state['authenticator'] = authenticator
46
- return authenticator
47
-
48
- def nav_page(page_name, timeout_secs=3):
49
- nav_script = """
50
- <script type="text/javascript">
51
- function attempt_nav_page(page_name, start_time, timeout_secs) {
52
- var links = window.parent.document.getElementsByTagName("a");
53
- for (var i = 0; i < links.length; i++) {
54
- if (links[i].href.toLowerCase().endsWith("/" + page_name.toLowerCase())) {
55
- links[i].click();
56
- return;
57
- }
58
- }
59
- var elasped = new Date() - start_time;
60
- if (elasped < timeout_secs * 1000) {
61
- setTimeout(attempt_nav_page, 100, page_name, start_time, timeout_secs);
62
- } else {
63
- alert("Unable to navigate to page '" + page_name + "' after " + timeout_secs + " second(s).");
64
- }
65
- }
66
- window.addEventListener("load", function() {
67
- attempt_nav_page("%s", new Date(), %d);
68
- });
69
- </script>
70
- """ % (page_name, timeout_secs)
71
- html(nav_script)
72
-
73
-
74
- # def load_local_css(file_name):
75
- # with open(file_name) as f:
76
- # st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
77
-
78
-
79
- # def set_header():
80
- # return st.markdown(f"""<div class='main-header'>
81
- # <h1>MMM LiME</h1>
82
- # <img src="https://assets-global.website-files.com/64c8fffb0e95cbc525815b79/64df84637f83a891c1473c51_Vector%20(Stroke).svg ">
83
- # </div>""", unsafe_allow_html=True)
84
-
85
- path = os.path.dirname(__file__)
86
-
87
- file_ = open(f"{path}/mastercard_logo.png", "rb")
88
-
89
- contents = file_.read()
90
-
91
- data_url = base64.b64encode(contents).decode("utf-8")
92
-
93
- file_.close()
94
-
95
-
96
-
97
- DATA_PATH = './data'
98
-
99
- IMAGES_PATH = './data/images_224_224'
100
-
101
- # New - Sprint 2
102
- if 'bin_dict' not in st.session_state:
103
-
104
- with open("data_import.pkl", "rb") as f:
105
- data = pickle.load(f)
106
-
107
- st.session_state['bin_dict'] = data["bin_dict"]
108
-
109
- # panel_col = [col.lower().replace('.','_').replace('@','_').replace(" ", "_").replace('-', '').replace(':', '').replace("__", "_") for col in st.session_state['bin_dict']['Panel Level 1'] ] [0]# set the panel column
110
-
111
- panel_col="Panel"
112
-
113
- is_panel = True if len(panel_col)>0 else False
114
-
115
- date_col='Date'
116
- #is_panel = False # flag if set to true - do panel level response curves
117
-
118
- def load_local_css(file_name):
119
-
120
- with open(file_name) as f:
121
-
122
- st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
123
-
124
-
125
-
126
-
127
-
128
- # def set_header():
129
-
130
- # return st.markdown(f"""<div class='main-header'>
131
-
132
- # <h1>H & M Recommendations</h1>
133
-
134
- # <img src="data:image;base64,{data_url}", alt="Logo">
135
-
136
- # </div>""", unsafe_allow_html=True)
137
- path1 = os.path.dirname(__file__)
138
-
139
- file_1 = open(f"{path}/ALDI_2017.png", "rb")
140
-
141
- contents1 = file_1.read()
142
-
143
- data_url1 = base64.b64encode(contents1).decode("utf-8")
144
-
145
- file_1.close()
146
-
147
-
148
-
149
- DATA_PATH1 = './data'
150
-
151
- IMAGES_PATH1 = './data/images_224_224'
152
-
153
-
154
-
155
-
156
-
157
- def set_header():
158
- return st.markdown(f"""<div class='main-header'>
159
- <!-- <h1></h1> -->
160
- <div >
161
- <img class='blend-logo' src="data:image;base64,{data_url1}", alt="Logo">
162
- </div>""", unsafe_allow_html=True)
163
-
164
- # def set_header():
165
- # logo_path = "./path/to/your/local/LIME_logo.png" # Replace with the actual file path
166
- # text = "LiME"
167
- # return st.markdown(f"""<div class='main-header'>
168
- # <img src="data:image/png;base64,{data_url}" alt="Logo" style="float: left; margin-right: 10px; width: 100px; height: auto;">
169
- # <h1>{text}</h1>
170
- # </div>""", unsafe_allow_html=True)
171
-
172
-
173
- def s_curve(x,K,b,a,x0):
174
- return K / (1 + b * np.exp(-a*(x-x0)))
175
-
176
-
177
- def overview_test_data_prep_panel(X, df, spends_X, date_col, panel_col, target_col):
178
- '''
179
- function to create the data which is used in initialize data fn
180
- X : X test with contributions
181
- df : originally uploaded data (media data) which has raw vars
182
- spends_X : spends of dates in X test
183
- '''
184
-
185
- # define channels
186
- channels = {'paid_search': ['paid_search_impressions', 'paid_search_clicks'],
187
-
188
- 'fb_level_achieved_tier_1': ['fb_level_achieved_tier_1_impressions'], #, 'fb:_level_achieved_-_tier_1_clicks'],
189
-
190
- 'fb_level_achieved_tier_2': ['fb:_level_achieved_tier_2_impressions',
191
- 'fb_level_achieved_tier_2_clicks'],
192
-
193
- 'paid_social_others' : ['paid_social_others_impressions', 'paid_social_others_clicks'],
194
-
195
- 'ga_app': ['ga_app_impressions', 'ga_app_clicks'],
196
-
197
- 'digital_tactic_others': ['digital_tactic_others_impressions', 'digital_tactic_others_clicks'],
198
-
199
- 'kwai': ['kwai_impressions', 'kwai_clicks'],
200
-
201
- 'programmatic': ['programmatic_impressions', 'programmatic_clicks'],
202
-
203
- # 'affiliates':['affiliates_clicks'],
204
- #
205
- # "indicacao":['indicacao_clicks'],
206
- #
207
- # "infleux":['infleux_clicks'],
208
- #
209
- # "influencer":['influencer_clicks']
210
- }
211
-
212
- channel_list = list(channels.keys())
213
-
214
- # map transformed variable to raw variable name & channel name
215
- # mapping eg : paid_search_clicks_lag_2 (transformed var) --> paid_search_clicks (raw var) --> paid_search (channel)
216
- variables = {}
217
- channel_and_variables = {}
218
- new_variables = {}
219
- new_channels_and_variables = {}
220
-
221
- for transformed_var in [col for col in
222
- X.drop(columns=[date_col, panel_col, target_col, 'pred', 'panel_effect']).columns if
223
- "_contr" not in col]:
224
- if len([col for col in df.columns if col in transformed_var]) == 1:
225
- raw_var = [col for col in df.columns if col in transformed_var][0]
226
- variables[transformed_var] = raw_var
227
- channel_and_variables[raw_var] = [channel for channel, raw_vars in channels.items() if raw_var in raw_vars][
228
- 0]
229
- else:
230
- new_variables[transformed_var] = transformed_var
231
- new_channels_and_variables[transformed_var] = 'base'
232
-
233
- # Raw DF
234
- raw_X = pd.merge(X[[date_col, panel_col]], df[[date_col, panel_col] + list(variables.values())], how='left',
235
- on=[date_col, panel_col])
236
- assert len(raw_X) == len(X)
237
-
238
- raw_X_cols = []
239
- for i in raw_X.columns:
240
- if i in channel_and_variables.keys():
241
- raw_X_cols.append(channel_and_variables[i])
242
- else:
243
- raw_X_cols.append(i)
244
- raw_X.columns = raw_X_cols
245
-
246
- # Contribution DF
247
- contr_X = X[[date_col, panel_col, 'panel_effect'] + [col for col in X.columns if
248
- "_contr" in col and "sum_" not in col]].copy()
249
- new_variables = [col for col in contr_X.columns if
250
- "_flag" in col.lower() or "trend" in col.lower() or "sine" in col.lower()]
251
- if len(new_variables) > 0:
252
- contr_X['const'] = contr_X[['panel_effect'] + new_variables].sum(axis=1)
253
- contr_X.drop(columns=['panel_effect'], inplace=True)
254
- contr_X.drop(columns=new_variables, inplace=True)
255
- else:
256
- contr_X.rename(columns={'panel_effect': 'const'}, inplace=True)
257
-
258
- new_contr_X_cols = []
259
- for col in contr_X.columns:
260
- col_clean = col.replace("_contr", "")
261
- new_contr_X_cols.append(col_clean)
262
- contr_X.columns = new_contr_X_cols
263
-
264
- contr_X_cols = []
265
- for i in contr_X.columns:
266
- if i in variables.keys():
267
- contr_X_cols.append(channel_and_variables[variables[i]])
268
- else:
269
- contr_X_cols.append(i)
270
- contr_X.columns = contr_X_cols
271
-
272
- # Spends DF
273
- spends_X.columns = [col.replace("_cost", "") for col in spends_X.columns]
274
-
275
- raw_X.rename(columns={"date": "Date"}, inplace=True)
276
- contr_X.rename(columns={"date": "Date"}, inplace=True)
277
- spends_X.rename(columns={'date': 'Week'}, inplace=True)
278
-
279
- # Create excel
280
- file_name = "data_test_overview_panel_#" + target_col + ".xlsx"
281
- with pd.ExcelWriter(file_name) as writer:
282
- raw_X.to_excel(writer, sheet_name="RAW DATA MMM", index=False)
283
- contr_X.to_excel(writer, sheet_name="CONTRIBUTION MMM", index=False)
284
- spends_X.to_excel(writer, sheet_name="SPEND INPUT", index=False)
285
-
286
-
287
- def overview_test_data_prep_nonpanel(X, df, spends_X, date_col, target_col):
288
- '''
289
- function to create the data which is used in initialize data fn
290
- X : X test with contributions
291
- df : originally uploaded data (media data) which has raw vars
292
- spends_X : spends of dates in X test
293
- '''
294
- # define channels
295
- channels = {'paid_search': ['paid_search_impressions', 'paid_search_clicks'],
296
-
297
- 'fb_level_achieved_tier_1': ['fb_level_achieved_tier_1_impressions', 'fb_level_achieved_tier_1_clicks'],
298
-
299
- 'fb_level_achieved_tier_2': ['fb_level_achieved_tier_2_impressions',
300
- 'fb_level_achieved_tier_2_clicks'],
301
-
302
- 'paid_social_others' : ['paid_social_others_impressions', 'paid_social_others_clicks'],
303
-
304
- 'ga_app_will_and_cid_pequena_baixo_risco': ['ga_app_will_and_cid_pequena_baixo_risco_impressions', 'ga_app_will_and_cid_pequena_baixo_risco_clicks'],
305
-
306
- 'digital_tactic_others': ['digital_tactic_others_impressions', 'digital_tactic_others_clicks'],
307
-
308
- 'kwai': ['kwai_impressions', 'kwai_clicks'],
309
-
310
- 'programmatic': ['programmatic_impressions', 'programmatic_clicks'],
311
-
312
- 'affiliates':['affiliates_clicks', 'affiliates_impressions'],
313
-
314
- "indicacao":['indicacao_clicks', 'indicacao_impressions'],
315
-
316
- "infleux":['infleux_clicks', 'infleux_impressions'],
317
-
318
- "influencer":['influencer_clicks', 'influencer_impressions']
319
- }
320
-
321
- channel_list = list(channels.keys())
322
-
323
- # map transformed variable to raw variable name & channel name
324
- # mapping eg : paid_search_clicks_lag_2 (transformed var) --> paid_search_clicks (raw var) --> paid_search (channel)
325
- variables = {}
326
- channel_and_variables = {}
327
- new_variables = {}
328
- new_channels_and_variables = {}
329
-
330
- cols_to_del = list(set([date_col, target_col, 'pred']).intersection((set(X.columns))))
331
- for transformed_var in [col for col in
332
- X.drop(columns=cols_to_del).columns if
333
- "_contr" not in col]: # also has 'const'
334
- if len([col for col in df.columns if col in transformed_var]) == 1: # col is raw var
335
- raw_var = [col for col in df.columns if col in transformed_var][0]
336
- variables[transformed_var] = raw_var
337
- channel_and_variables[raw_var] = [channel for channel, raw_vars in channels.items() if raw_var in raw_vars][0]
338
- else: # when no corresponding raw var then base
339
- new_variables[transformed_var] = transformed_var
340
- new_channels_and_variables[transformed_var] = 'base'
341
-
342
- # Raw DF
343
- raw_X = pd.merge(X[[date_col]], df[[date_col] + list(variables.values())], how='left',
344
- on=[date_col])
345
- assert len(raw_X) == len(X)
346
-
347
- raw_X_cols = []
348
- for i in raw_X.columns:
349
- if i in channel_and_variables.keys():
350
- raw_X_cols.append(channel_and_variables[i])
351
- else:
352
- raw_X_cols.append(i)
353
- raw_X.columns = raw_X_cols
354
-
355
- # Contribution DF
356
- contr_X = X[[date_col] + [col for col in X.columns if "_contr" in col and "sum_" not in col]].copy()
357
- # st.write(contr_X.columns)
358
- new_variables = [col for col in contr_X.columns if
359
- "_flag" in col.lower() or "trend" in col.lower() or "sine" in col.lower()]
360
- if len(new_variables) > 0: # if new vars are available, their contributions should be added to base (called const)
361
- contr_X['const_contr'] = contr_X[['const_contr'] + new_variables].sum(axis=1)
362
- contr_X.drop(columns=new_variables, inplace=True)
363
-
364
-
365
- new_contr_X_cols = []
366
- for col in contr_X.columns:
367
- col_clean = col.replace("_contr", "")
368
- new_contr_X_cols.append(col_clean)
369
- contr_X.columns = new_contr_X_cols
370
-
371
- contr_X_cols = []
372
- for i in contr_X.columns:
373
- if i in variables.keys():
374
- contr_X_cols.append(channel_and_variables[variables[i]])
375
- else:
376
- contr_X_cols.append(i)
377
- contr_X.columns = contr_X_cols
378
-
379
- # Spends DF
380
- spends_X.columns = [col.replace("_cost", "").replace("_spends", '').replace("_spend", "") for col in spends_X.columns]
381
-
382
- raw_X.rename(columns={"date": "Date"}, inplace=True)
383
- contr_X.rename(columns={"date": "Date"}, inplace=True)
384
- spends_X.rename(columns={'date': 'Week'}, inplace=True)
385
-
386
- # Create excel
387
- file_name = "data_test_overview_panel_#" + target_col + ".xlsx"
388
- with pd.ExcelWriter(file_name) as writer:
389
- raw_X.to_excel(writer, sheet_name="RAW DATA MMM", index=False)
390
- contr_X.to_excel(writer, sheet_name="CONTRIBUTION MMM", index=False)
391
- spends_X.to_excel(writer, sheet_name="SPEND INPUT", index=False)
392
-
393
-
394
- def initialize_data(target_col,selected_markets):
395
- # uopx_conv_rates = {'streaming_impressions' : 0.007,'digital_impressions' : 0.007,'search_clicks' : 0.00719,'tv_impressions' : 0.000173,
396
- # "digital_clicks":0.005,"streaming_clicks":0.004,'streaming_spends':1,"tv_spends":1,"search_spends":1,
397
- # "digital_spends":1}
398
- #print('State initialized')
399
- # excel = pd.read_excel("data_test_overview_panel.xlsx",sheet_name=None)
400
- #excel = pd.read_excel(r"metrics_level_data\Overview_data_test_panel@#revenue.xlsx" + target_col + ".xlsx",sheet_name=None)
401
-
402
- excel = pd.read_excel(r"metrics_level_data\Overview_data_test_panel@#revenue.xlsx",sheet_name=None)
403
-
404
- raw_df = excel['RAW DATA MMM']
405
-
406
- spend_df = excel['SPEND INPUT']
407
- contri_df = excel['CONTRIBUTION MMM']
408
-
409
- #st.write(raw_df)
410
- if selected_markets!= "Total Market":
411
-
412
- raw_df=raw_df[raw_df['Panel']==selected_markets]
413
- spend_df=spend_df[spend_df['Panel']==selected_markets]
414
- contri_df=contri_df[contri_df['Panel']==selected_markets]
415
-
416
- else:
417
- raw_df=raw_df.groupby('Date').sum().reset_index()
418
- spend_df=spend_df.groupby('Week').sum().reset_index()
419
- contri_df=contri_df.groupby('Date').sum().reset_index()
420
- #Revenue_df = excel['Revenue']
421
-
422
- ## remove sesonalities, indices etc ...
423
- exclude_columns = ['Date', 'Week','Panel',date_col, panel_col,'Others'
424
- ]
425
-
426
- # Aggregate all 3 dfs to date level (from date-panel level)
427
- raw_df[date_col]=pd.to_datetime(raw_df[date_col])
428
- raw_df_aggregations = {c:'sum' for c in raw_df.columns if c not in exclude_columns}
429
- raw_df = raw_df.groupby(date_col).agg(raw_df_aggregations).reset_index()
430
-
431
- contri_df[date_col]=pd.to_datetime(contri_df[date_col])
432
- contri_df_aggregations = {c:'sum' for c in contri_df.columns if c not in exclude_columns}
433
- contri_df = contri_df.groupby(date_col).agg(contri_df_aggregations).reset_index()
434
-
435
- input_df = raw_df.sort_values(by=[date_col])
436
-
437
- output_df = contri_df.sort_values(by=[date_col])
438
-
439
- spend_df['Week'] = pd.to_datetime(spend_df['Week'], format='%Y-%m-%d', errors='coerce')
440
- spend_df_aggregations = {c: 'sum' for c in spend_df.columns if c not in exclude_columns}
441
- spend_df = spend_df.groupby('Week').agg(spend_df_aggregations).reset_index()
442
- # spend_df['Week'] = pd.to_datetime(spend_df['Week'], errors='coerce')
443
- # spend_df = spend_df.sort_values(by='Week')
444
-
445
-
446
- channel_list = [col for col in input_df.columns if col not in exclude_columns]
447
-
448
- response_curves = {}
449
- mapes = {}
450
- rmses = {}
451
- upper_limits = {}
452
- powers = {}
453
- r2 = {}
454
- conv_rates = {}
455
- output_cols = []
456
- channels = {}
457
- sales = None
458
- dates = input_df.Date.values
459
- actual_output_dic = {}
460
- actual_input_dic = {}
461
-
462
- # ONLY FOR TESTING
463
- # channel_list=['programmatic']
464
- infeasible_channels = [c for c in contri_df.select_dtypes(include=['float', 'int']).columns if contri_df[c].sum()<=0]
465
- # st.write(infeasible_channels)
466
- channel_list=list(set(channel_list)-set(infeasible_channels))
467
-
468
-
469
- for inp_col in channel_list:
470
- #st.write(inp_col)
471
-
472
- # # New - Sprint 2
473
- # if is_panel:
474
- # input_df1 = input_df.groupby([date_col]).agg({inp_col:'sum'}).reset_index() # aggregate spends on date
475
- # spends = input_df1[inp_col].values
476
- # else :
477
- # spends = input_df[inp_col].values
478
- spends = spend_df[inp_col].values
479
-
480
- x = spends.copy()
481
- # upper limit for penalty
482
- upper_limits[inp_col] = 2*x.max()
483
-
484
-
485
-
486
- # contribution
487
- # New - Sprint 2
488
- out_col = [_col for _col in output_df.columns if _col.startswith(inp_col)][0]
489
- if is_panel :
490
- output_df1 = output_df.groupby([date_col]).agg({out_col:'sum'}).reset_index()
491
- y = output_df1[out_col].values.copy()
492
- else :
493
- y = output_df[out_col].values.copy()
494
-
495
- actual_output_dic[inp_col] = y.copy()
496
- actual_input_dic[inp_col] = x.copy()
497
- ##output cols aggregation
498
- output_cols.append(out_col)
499
-
500
- ## scale the input
501
- power = (np.ceil(np.log(x.max()) / np.log(10) )- 3)
502
- if power >= 0 :
503
- x = x / 10**power
504
-
505
-
506
- x = x.astype('float64')
507
- y = y.astype('float64')
508
- #print('#printing yyyyyyyyy')
509
- #print(inp_col)
510
- #print(x.max())
511
- #print(y.max())
512
- # st.write(y.max(),x.max())
513
- print(y.max(),x.max())
514
- if y.max()<=0.01:
515
- if x.max()<=0.01 :
516
- st.write("here-here")
517
- bounds = ((0, 0, 0, 0), (3 * 0.01, 1000, 1, 0.01))
518
-
519
- else :
520
- st.write("here")
521
- bounds = ((0, 0, 0, 0), (3 * 0.01, 1000, 1, 0.01))
522
- else :
523
- bounds = ((0, 0, 0, 0), (3 * y.max(), 1000, 1, x.max()))
524
- #bounds = ((y.max(), 3*y.max()),(0,1000),(0,1),(0,x.max()))
525
- params,_ = curve_fit(s_curve,x,y,p0=(2*y.max(),0.01,1e-5,x.max()),
526
- bounds=bounds,
527
- maxfev=int(1e5))
528
- mape = (100 * abs(1 - s_curve(x, *params) / y.clip(min=1))).mean()
529
- rmse = np.sqrt(((y - s_curve(x,*params))**2).mean())
530
- r2_ = r2_score(y, s_curve(x,*params))
531
-
532
- response_curves[inp_col] = {'K' : params[0], 'b' : params[1], 'a' : params[2], 'x0' : params[3]}
533
- mapes[inp_col] = mape
534
- rmses[inp_col] = rmse
535
- r2[inp_col] = r2_
536
- powers[inp_col] = power
537
-
538
-
539
- ## conversion rates
540
- spend_col = [_col for _col in spend_df.columns if _col.startswith(inp_col.rsplit('_',1)[0])][0]
541
-
542
- #print('#printing spendssss')
543
- #print(spend_col)
544
- conv = (spend_df.set_index('Week')[spend_col] / input_df.set_index('Date')[inp_col].clip(lower=1)).reset_index()
545
- conv.rename(columns={'index':'Week'},inplace=True)
546
- conv['year'] = conv.Week.dt.year
547
- conv_rates[inp_col] = list(conv.drop('Week',axis=1).mean().to_dict().values())[0]
548
- ##print('Before',conv_rates[inp_col])
549
- # conv_rates[inp_col] = uopx_conv_rates[inp_col]
550
- ##print('After',(conv_rates[inp_col]))
551
-
552
-
553
- channel = Channel(name=inp_col,dates=dates,
554
- spends=spends,
555
- # conversion_rate = np.mean(list(conv_rates[inp_col].values())),
556
- conversion_rate = conv_rates[inp_col],
557
- response_curve_type='s-curve',
558
- response_curve_params={'K' : params[0], 'b' : params[1], 'a' : params[2], 'x0' : params[3]},
559
- bounds=np.array([-10,10]))
560
- channels[inp_col] = channel
561
- if sales is None:
562
- sales = channel.actual_sales
563
- else:
564
- sales += channel.actual_sales
565
- # st.write(inp_col, channel.actual_sales)
566
- # st.write(output_cols)
567
- other_contributions = output_df.drop([*output_cols], axis=1).sum(axis=1, numeric_only = True).values
568
- correction = output_df.drop(['Date'],axis=1).sum(axis=1).values - (sales + other_contributions)
569
-
570
- scenario_test_df=pd.DataFrame(columns=['other_contributions','correction', 'sales'])
571
- scenario_test_df['other_contributions']=other_contributions
572
- scenario_test_df['correction']=correction
573
- scenario_test_df['sales']=sales
574
- scenario_test_df.to_csv("test/scenario_test_df.csv",index=False)
575
- output_df.to_csv("test/output_df.csv",index=False)
576
-
577
- scenario = Scenario(name='default', channels=channels, constant=other_contributions, correction = correction)
578
- ## setting session variables
579
- st.session_state['initialized'] = True
580
- st.session_state['actual_df'] = input_df
581
- st.session_state['raw_df'] = raw_df
582
- st.session_state['contri_df'] = output_df
583
- default_scenario_dict = class_to_dict(scenario)
584
- st.session_state['default_scenario_dict'] = default_scenario_dict
585
- st.session_state['scenario'] = scenario
586
- st.session_state['channels_list'] = channel_list
587
- st.session_state['optimization_channels'] = {channel_name : False for channel_name in channel_list}
588
- st.session_state['rcs'] = response_curves
589
- st.session_state['powers'] = powers
590
- st.session_state['actual_contribution_df'] = pd.DataFrame(actual_output_dic)
591
- st.session_state['actual_input_df'] = pd.DataFrame(actual_input_dic)
592
-
593
- for channel in channels.values():
594
- st.session_state[channel.name] = numerize(channel.actual_total_spends * channel.conversion_rate,1)
595
-
596
- st.session_state['xlsx_buffer'] = io.BytesIO()
597
-
598
-
599
- if Path('../saved_scenarios.pkl').exists():
600
- with open('../saved_scenarios.pkl','rb') as f:
601
- st.session_state['saved_scenarios'] = pickle.load(f)
602
- else:
603
- st.session_state['saved_scenarios'] = OrderedDict()
604
-
605
- st.session_state['total_spends_change'] = 0
606
- st.session_state['optimization_channels'] = {channel_name : False for channel_name in channel_list}
607
- st.session_state['disable_download_button'] = True
608
-
609
- # def initialize_data():
610
- # # fetch data from excel
611
- # output = pd.read_excel('data.xlsx',sheet_name=None)
612
- # raw_df = output['RAW DATA MMM']
613
- # contribution_df = output['CONTRIBUTION MMM']
614
- # Revenue_df = output['Revenue']
615
-
616
- # ## channels to be shows
617
- # channel_list = []
618
- # for col in raw_df.columns:
619
- # if 'click' in col.lower() or 'spend' in col.lower() or 'imp' in col.lower():
620
- # ##print(col)
621
- # channel_list.append(col)
622
- # else:
623
- # pass
624
-
625
- # ## NOTE : Considered only Desktop spends for all calculations
626
- # acutal_df = raw_df[raw_df.Region == 'Desktop'].copy()
627
- # ## NOTE : Considered one year of data
628
- # acutal_df = acutal_df[acutal_df.Date>'2020-12-31']
629
- # actual_df = acutal_df.drop('Region',axis=1).sort_values(by='Date')[[*channel_list,'Date']]
630
-
631
- # ##load response curves
632
- # with open('./grammarly_response_curves.json','r') as f:
633
- # response_curves = json.load(f)
634
-
635
- # ## create channel dict for scenario creation
636
- # dates = actual_df.Date.values
637
- # channels = {}
638
- # rcs = {}
639
- # constant = 0.
640
- # for i,info_dict in enumerate(response_curves):
641
- # name = info_dict.get('name')
642
- # response_curve_type = info_dict.get('response_curve')
643
- # response_curve_params = info_dict.get('params')
644
- # rcs[name] = response_curve_params
645
- # if name != 'constant':
646
- # spends = actual_df[name].values
647
- # channel = Channel(name=name,dates=dates,
648
- # spends=spends,
649
- # response_curve_type=response_curve_type,
650
- # response_curve_params=response_curve_params,
651
- # bounds=np.array([-30,30]))
652
-
653
- # channels[name] = channel
654
- # else:
655
- # constant = info_dict.get('value',0.) * len(dates)
656
-
657
- # ## create scenario
658
- # scenario = Scenario(name='default', channels=channels, constant=constant)
659
- # default_scenario_dict = class_to_dict(scenario)
660
-
661
-
662
- # ## setting session variables
663
- # st.session_state['initialized'] = True
664
- # st.session_state['actual_df'] = actual_df
665
- # st.session_state['raw_df'] = raw_df
666
- # st.session_state['default_scenario_dict'] = default_scenario_dict
667
- # st.session_state['scenario'] = scenario
668
- # st.session_state['channels_list'] = channel_list
669
- # st.session_state['optimization_channels'] = {channel_name : False for channel_name in channel_list}
670
- # st.session_state['rcs'] = rcs
671
- # for channel in channels.values():
672
- # if channel.name not in st.session_state:
673
- # st.session_state[channel.name] = float(channel.actual_total_spends)
674
-
675
- # if 'xlsx_buffer' not in st.session_state:
676
- # st.session_state['xlsx_buffer'] = io.BytesIO()
677
-
678
- # ## for saving scenarios
679
- # if 'saved_scenarios' not in st.session_state:
680
- # if Path('../saved_scenarios.pkl').exists():
681
- # with open('../saved_scenarios.pkl','rb') as f:
682
- # st.session_state['saved_scenarios'] = pickle.load(f)
683
-
684
- # else:
685
- # st.session_state['saved_scenarios'] = OrderedDict()
686
-
687
- # if 'total_spends_change' not in st.session_state:
688
- # st.session_state['total_spends_change'] = 0
689
-
690
- # if 'optimization_channels' not in st.session_state:
691
- # st.session_state['optimization_channels'] = {channel_name : False for channel_name in channel_list}
692
-
693
- # if 'disable_download_button' not in st.session_state:
694
- # st.session_state['disable_download_button'] = True
695
- def create_channel_summary(scenario):
696
- summary_columns = []
697
-
698
- actual_spends_rows = []
699
-
700
- actual_sales_rows = []
701
-
702
- actual_roi_rows = []
703
-
704
- for channel in scenario.channels.values():
705
-
706
- name_mod = channel.name.replace('_', ' ')
707
-
708
- if name_mod.lower().endswith(' imp'):
709
- name_mod = name_mod.replace('Imp', ' Impressions')
710
-
711
- print(name_mod, channel.actual_total_spends, channel.conversion_rate,
712
- channel.actual_total_spends * channel.conversion_rate)
713
-
714
- summary_columns.append(name_mod)
715
-
716
- actual_spends_rows.append(format_numbers(float(channel.actual_total_spends * channel.conversion_rate)))
717
-
718
- actual_sales_rows.append(format_numbers((float(channel.actual_total_sales))))
719
-
720
- actual_roi_rows.append(decimal_formater(
721
- format_numbers((channel.actual_total_sales) / (channel.actual_total_spends * channel.conversion_rate),
722
- include_indicator=False, n_decimals=4), n_decimals=4))
723
-
724
- actual_summary_df = pd.DataFrame([summary_columns, actual_spends_rows, actual_sales_rows, actual_roi_rows]).T
725
-
726
- actual_summary_df.columns = ['Channel', 'Spends', 'Revenue', 'ROI']
727
-
728
- actual_summary_df['Revenue'] = actual_summary_df['Revenue'].map(lambda x: str(x)[1:])
729
-
730
- return actual_summary_df
731
-
732
-
733
- # def create_channel_summary(scenario):
734
- #
735
- # # Provided data
736
- # data = {
737
- # 'Channel': ['Paid Search', 'Ga will cid baixo risco', 'Digital tactic others', 'Fb la tier 1', 'Fb la tier 2', 'Paid social others', 'Programmatic', 'Kwai', 'Indicacao', 'Infleux', 'Influencer'],
738
- # 'Spends': ['$ 11.3K', '$ 155.2K', '$ 50.7K', '$ 125.4K', '$ 125.2K', '$ 105K', '$ 3.3M', '$ 47.5K', '$ 55.9K', '$ 632.3K', '$ 48.3K'],
739
- # 'Revenue': ['558.0K', '3.5M', '5.2M', '3.1M', '3.1M', '2.1M', '20.8M', '1.6M', '728.4K', '22.9M', '4.8M']
740
- # }
741
- #
742
- # # Create DataFrame
743
- # df = pd.DataFrame(data)
744
- #
745
- # # Convert currency strings to numeric values
746
- # df['Spends'] = df['Spends'].replace({'\$': '', 'K': '*1e3', 'M': '*1e6'}, regex=True).map(pd.eval).astype(int)
747
- # df['Revenue'] = df['Revenue'].replace({'\$': '', 'K': '*1e3', 'M': '*1e6'}, regex=True).map(pd.eval).astype(int)
748
- #
749
- # # Calculate ROI
750
- # df['ROI'] = ((df['Revenue'] - df['Spends']) / df['Spends'])
751
- #
752
- # # Format columns
753
- # format_currency = lambda x: f"${x:,.1f}"
754
- # format_roi = lambda x: f"{x:.1f}"
755
- #
756
- # df['Spends'] = ['$ 11.3K', '$ 155.2K', '$ 50.7K', '$ 125.4K', '$ 125.2K', '$ 105K', '$ 3.3M', '$ 47.5K', '$ 55.9K', '$ 632.3K', '$ 48.3K']
757
- # df['Revenue'] = ['$ 536.3K', '$ 3.4M', '$ 5M', '$ 3M', '$ 3M', '$ 2M', '$ 20M', '$ 1.5M', '$ 7.1M', '$ 22M', '$ 4.6M']
758
- # df['ROI'] = df['ROI'].apply(format_roi)
759
- #
760
- # return df
761
-
762
-
763
- #@st.cache_data()
764
- def create_contribution_pie(scenario):
765
- #c1f7dc
766
-
767
- light_blue = 'rgba(0, 31, 120, 0.7)'
768
- light_orange = 'rgba(0, 181, 219, 0.7)'
769
- light_green = 'rgba(240, 61, 20, 0.7)'
770
- light_red = 'rgba(250, 110, 10, 0.7)'
771
- light_purple = 'rgba(255, 191, 69, 0.7)'
772
-
773
- colors_map = {col:color for col,color in zip(st.session_state['channels_list'],plotly.colors.n_colors(plotly.colors.hex_to_rgb('#BE6468'), plotly.colors.hex_to_rgb('#E7B8B7'),23))}
774
- total_contribution_fig = make_subplots(rows=1, cols=2,subplot_titles=['Media Spends','Revenue Contribution'],specs=[[{"type": "pie"}, {"type": "pie"}]])
775
- total_contribution_fig.add_trace(
776
- go.Pie(labels=[channel_name_formating(channel_name) for channel_name in st.session_state['channels_list']] + ['Non Media'],
777
- values= [round(scenario.channels[channel_name].actual_total_spends * scenario.channels[channel_name].conversion_rate,1) for channel_name in st.session_state['channels_list']] + [0],
778
- marker_colors=[light_blue, light_orange, light_green, light_red, light_purple],
779
- hole=0.3),
780
- row=1, col=1)
781
-
782
- total_contribution_fig.add_trace(
783
- go.Pie(labels=[channel_name_formating(channel_name) for channel_name in st.session_state['channels_list']] + ['Non Media'],
784
- values= [scenario.channels[channel_name].actual_total_sales for channel_name in st.session_state['channels_list']] + [scenario.correction.sum() + scenario.constant.sum()],
785
- hole=0.3),
786
- row=1, col=2)
787
-
788
- total_contribution_fig.update_traces(textposition='inside',texttemplate='%{percent:.1%}')
789
- total_contribution_fig.update_layout(uniformtext_minsize=12,title='', uniformtext_mode='hide')
790
- return total_contribution_fig
791
-
792
- #@st.cache_data()
793
-
794
- # def create_contribuion_stacked_plot(scenario):
795
- # weekly_contribution_fig = make_subplots(rows=1, cols=2,subplot_titles=['Spends','Revenue'],specs=[[{"type": "bar"}, {"type": "bar"}]])
796
- # raw_df = st.session_state['raw_df']
797
- # df = raw_df.sort_values(by='Date')
798
- # x = df.Date
799
- # weekly_spends_data = []
800
- # weekly_sales_data = []
801
- # for channel_name in st.session_state['channels_list']:
802
- # weekly_spends_data.append((go.Bar(x=x,
803
- # y=scenario.channels[channel_name].actual_spends * scenario.channels[channel_name].conversion_rate,
804
- # name=channel_name_formating(channel_name),
805
- # hovertemplate="Date:%{x}<br>Spend:%{y:$.2s}",
806
- # legendgroup=channel_name)))
807
- # weekly_sales_data.append((go.Bar(x=x,
808
- # y=scenario.channels[channel_name].actual_sales,
809
- # name=channel_name_formating(channel_name),
810
- # hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}",
811
- # legendgroup=channel_name, showlegend=False)))
812
- # for _d in weekly_spends_data:
813
- # weekly_contribution_fig.add_trace(_d, row=1, col=1)
814
- # for _d in weekly_sales_data:
815
- # weekly_contribution_fig.add_trace(_d, row=1, col=2)
816
- # weekly_contribution_fig.add_trace(go.Bar(x=x,
817
- # y=scenario.constant + scenario.correction,
818
- # name='Non Media',
819
- # hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}"), row=1, col=2)
820
- # weekly_contribution_fig.update_layout(barmode='stack', title='Channel contribuion by week', xaxis_title='Date')
821
- # weekly_contribution_fig.update_xaxes(showgrid=False)
822
- # weekly_contribution_fig.update_yaxes(showgrid=False)
823
- # return weekly_contribution_fig
824
-
825
- # @st.cache_data(allow_output_mutation=True)
826
- # def create_channel_spends_sales_plot(channel):
827
- # if channel is not None:
828
- # x = channel.dates
829
- # _spends = channel.actual_spends * channel.conversion_rate
830
- # _sales = channel.actual_sales
831
- # channel_sales_spends_fig = make_subplots(specs=[[{"secondary_y": True}]])
832
- # channel_sales_spends_fig.add_trace(go.Bar(x=x, y=_sales,marker_color='#c1f7dc',name='Revenue', hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}"), secondary_y = False)
833
- # channel_sales_spends_fig.add_trace(go.Scatter(x=x, y=_spends,line=dict(color='#005b96'),name='Spends',hovertemplate="Date:%{x}<br>Spend:%{y:$.2s}"), secondary_y = True)
834
- # channel_sales_spends_fig.update_layout(xaxis_title='Date',yaxis_title='Revenue',yaxis2_title='Spends ($)',title='Channel spends and Revenue week wise')
835
- # channel_sales_spends_fig.update_xaxes(showgrid=False)
836
- # channel_sales_spends_fig.update_yaxes(showgrid=False)
837
- # else:
838
- # raw_df = st.session_state['raw_df']
839
- # df = raw_df.sort_values(by='Date')
840
- # x = df.Date
841
- # scenario = class_from_dict(st.session_state['default_scenario_dict'])
842
- # _sales = scenario.constant + scenario.correction
843
- # channel_sales_spends_fig = make_subplots(specs=[[{"secondary_y": True}]])
844
- # channel_sales_spends_fig.add_trace(go.Bar(x=x, y=_sales,marker_color='#c1f7dc',name='Revenue', hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}"), secondary_y = False)
845
- # # channel_sales_spends_fig.add_trace(go.Scatter(x=x, y=_spends,line=dict(color='#15C39A'),name='Spends',hovertemplate="Date:%{x}<br>Spend:%{y:$.2s}"), secondary_y = True)
846
- # channel_sales_spends_fig.update_layout(xaxis_title='Date',yaxis_title='Revenue',yaxis2_title='Spends ($)',title='Channel spends and Revenue week wise')
847
- # channel_sales_spends_fig.update_xaxes(showgrid=False)
848
- # channel_sales_spends_fig.update_yaxes(showgrid=False)
849
- # return channel_sales_spends_fig
850
-
851
-
852
- # Define a shared color palette
853
-
854
-
855
- # def create_contribution_pie():
856
- # color_palette = ['#F3F3F0', '#5E7D7E', '#2FA1FF', '#00EDED', '#00EAE4', '#304550', '#EDEBEB', '#7FBEFD', '#003059', '#A2F3F3', '#E1D6E2', '#B6B6B6']
857
- # total_contribution_fig = make_subplots(rows=1, cols=2, subplot_titles=['Spends', 'Revenue'], specs=[[{"type": "pie"}, {"type": "pie"}]])
858
- #
859
- # channels_list = ['Paid Search', 'Ga will cid baixo risco', 'Digital tactic others', 'Fb la tier 1', 'Fb la tier 2', 'Paid social others', 'Programmatic', 'Kwai', 'Indicacao', 'Infleux', 'Influencer', 'Non Media']
860
- #
861
- # # Assign colors from the limited palette to channels
862
- # colors_map = {col: color_palette[i % len(color_palette)] for i, col in enumerate(channels_list)}
863
- # colors_map['Non Media'] = color_palette[5] # Assign fixed green color for 'Non Media'
864
- #
865
- # # Hardcoded values for Spends and Revenue
866
- # spends_values = [0.5, 3.36, 1.1, 2.7, 2.7, 2.27, 70.6, 1, 1, 13.7, 1, 0]
867
- # revenue_values = [1, 4, 5, 3, 3, 2, 50.8, 1.5, 0.7, 13, 0, 16]
868
- #
869
- # # Add trace for Spends pie chart
870
- # total_contribution_fig.add_trace(
871
- # go.Pie(
872
- # labels=[channel_name for channel_name in channels_list],
873
- # values=spends_values,
874
- # marker=dict(colors=[colors_map[channel_name] for channel_name in channels_list]),
875
- # hole=0.3
876
- # ),
877
- # row=1, col=1
878
- # )
879
- #
880
- # # Add trace for Revenue pie chart
881
- # total_contribution_fig.add_trace(
882
- # go.Pie(
883
- # labels=[channel_name for channel_name in channels_list],
884
- # values=revenue_values,
885
- # marker=dict(colors=[colors_map[channel_name] for channel_name in channels_list]),
886
- # hole=0.3
887
- # ),
888
- # row=1, col=2
889
- # )
890
- #
891
- # total_contribution_fig.update_traces(textposition='inside', texttemplate='%{percent:.1%}')
892
- # total_contribution_fig.update_layout(uniformtext_minsize=12, title='Channel contribution', uniformtext_mode='hide')
893
- # return total_contribution_fig
894
-
895
- def create_contribuion_stacked_plot(scenario):
896
- weekly_contribution_fig = make_subplots(rows=1, cols=2, subplot_titles=['Spends', 'Revenue'], specs=[[{"type": "bar"}, {"type": "bar"}]])
897
- raw_df = st.session_state['raw_df']
898
- df = raw_df.sort_values(by='Date')
899
- x = df.Date
900
- weekly_spends_data = []
901
- weekly_sales_data = []
902
-
903
- for i, channel_name in enumerate(st.session_state['channels_list']):
904
- color = color_palette[i % len(color_palette)]
905
-
906
- weekly_spends_data.append(go.Bar(
907
- x=x,
908
- y=scenario.channels[channel_name].actual_spends * scenario.channels[channel_name].conversion_rate,
909
- name=channel_name_formating(channel_name),
910
- hovertemplate="Date:%{x}<br>Spend:%{y:$.2s}",
911
- legendgroup=channel_name,
912
- marker_color=color,
913
- ))
914
-
915
- weekly_sales_data.append(go.Bar(
916
- x=x,
917
- y=scenario.channels[channel_name].actual_sales,
918
- name=channel_name_formating(channel_name),
919
- hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}",
920
- legendgroup=channel_name,
921
- showlegend=False,
922
- marker_color=color,
923
- ))
924
-
925
- for _d in weekly_spends_data:
926
- weekly_contribution_fig.add_trace(_d, row=1, col=1)
927
- for _d in weekly_sales_data:
928
- weekly_contribution_fig.add_trace(_d, row=1, col=2)
929
-
930
- weekly_contribution_fig.add_trace(go.Bar(
931
- x=x,
932
- y=scenario.constant + scenario.correction,
933
- name='Non Media',
934
- hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}",
935
- marker_color=color_palette[-1],
936
- ), row=1, col=2)
937
-
938
- weekly_contribution_fig.update_layout(barmode='stack', title='Channel contribution by week', xaxis_title='Date')
939
- weekly_contribution_fig.update_xaxes(showgrid=False)
940
- weekly_contribution_fig.update_yaxes(showgrid=False)
941
- return weekly_contribution_fig
942
-
943
- def create_channel_spends_sales_plot(channel):
944
- if channel is not None:
945
- x = channel.dates
946
- _spends = channel.actual_spends * channel.conversion_rate
947
- _sales = channel.actual_sales
948
- channel_sales_spends_fig = make_subplots(specs=[[{"secondary_y": True}]])
949
- channel_sales_spends_fig.add_trace(go.Bar(
950
- x=x,
951
- y=_sales,
952
- marker_color=color_palette[1], # You can choose a color from the palette
953
- name='Revenue',
954
- hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}",
955
- ), secondary_y=False)
956
-
957
- channel_sales_spends_fig.add_trace(go.Scatter(
958
- x=x,
959
- y=_spends,
960
- line=dict(color=color_palette[3]), # You can choose another color from the palette
961
- name='Spends',
962
- hovertemplate="Date:%{x}<br>Spend:%{y:$.2s}",
963
- ), secondary_y=True)
964
-
965
- channel_sales_spends_fig.update_layout(xaxis_title='Date', yaxis_title='Revenue', yaxis2_title='Spends ($)', title='Channel spends and Revenue week-wise')
966
- channel_sales_spends_fig.update_xaxes(showgrid=False)
967
- channel_sales_spends_fig.update_yaxes(showgrid=False)
968
- else:
969
- raw_df = st.session_state['raw_df']
970
- df = raw_df.sort_values(by='Date')
971
- x = df.Date
972
- scenario = class_from_dict(st.session_state['default_scenario_dict'])
973
- _sales = scenario.constant + scenario.correction
974
- channel_sales_spends_fig = make_subplots(specs=[[{"secondary_y": True}]])
975
- channel_sales_spends_fig.add_trace(go.Bar(
976
- x=x,
977
- y=_sales,
978
- marker_color=color_palette[0], # You can choose a color from the palette
979
- name='Revenue',
980
- hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}",
981
- ), secondary_y=False)
982
-
983
- channel_sales_spends_fig.update_layout(xaxis_title='Date', yaxis_title='Revenue', yaxis2_title='Spends ($)', title='Channel spends and Revenue week-wise')
984
- channel_sales_spends_fig.update_xaxes(showgrid=False)
985
- channel_sales_spends_fig.update_yaxes(showgrid=False)
986
-
987
- return channel_sales_spends_fig
988
-
989
- def format_numbers(value, n_decimals=1,include_indicator = True):
990
- if include_indicator:
991
- return f'{CURRENCY_INDICATOR} {numerize(value,n_decimals)}'
992
- else:
993
- return f'{numerize(value,n_decimals)}'
994
-
995
-
996
- def decimal_formater(num_string,n_decimals=1):
997
- parts = num_string.split('.')
998
- if len(parts) == 1:
999
- return num_string+'.' + '0'*n_decimals
1000
- else:
1001
- to_be_padded = n_decimals - len(parts[-1])
1002
- if to_be_padded > 0 :
1003
- return num_string+'0'*to_be_padded
1004
- else:
1005
- return num_string
1006
-
1007
-
1008
- def channel_name_formating(channel_name):
1009
- name_mod = channel_name.replace('_', ' ')
1010
- if name_mod.lower().endswith(' imp'):
1011
- name_mod = name_mod.replace('Imp','Spend')
1012
- elif name_mod.lower().endswith(' clicks'):
1013
- name_mod = name_mod.replace('Clicks','Spend')
1014
- return name_mod
1015
-
1016
-
1017
- def send_email(email,message):
1018
- s = smtplib.SMTP('smtp.gmail.com', 587)
1019
- s.starttls()
1020
- s.login("geethu4444@gmail.com", "jgydhpfusuremcol")
1021
- s.sendmail("geethu4444@gmail.com", email, message)
1022
- s.quit()
1023
-
1024
- if __name__ == "__main__":
1025
- initialize_data()
 
1
+ from numerize.numerize import numerize
2
+ import streamlit as st
3
+ import pandas as pd
4
+ import json
5
+ from classes import Channel, Scenario
6
+ import numpy as np
7
+ from plotly.subplots import make_subplots
8
+ import plotly.graph_objects as go
9
+ from classes import class_to_dict
10
+ from collections import OrderedDict
11
+ import io
12
+ import plotly
13
+ from pathlib import Path
14
+ import pickle
15
+ import streamlit_authenticator as stauth
16
+ import yaml
17
+ from yaml import SafeLoader
18
+ from streamlit.components.v1 import html
19
+ import smtplib
20
+ from scipy.optimize import curve_fit
21
+ from sklearn.metrics import r2_score
22
+ from classes import class_from_dict
23
+ import os
24
+ import base64
25
+
26
+
27
+
28
+
29
+ color_palette = ['#001f78', '#00b5db', '#f03d14', '#fa6e0a', '#ffbf45']
30
+
31
+
32
+ CURRENCY_INDICATOR = '$'
33
+
34
+ def load_authenticator():
35
+ with open('config.yaml') as file:
36
+ config = yaml.load(file, Loader=SafeLoader)
37
+ st.session_state['config'] = config
38
+ authenticator = stauth.Authenticate(
39
+ config['credentials'],
40
+ config['cookie']['name'],
41
+ config['cookie']['key'],
42
+ config['cookie']['expiry_days'],
43
+ config['preauthorized']
44
+ )
45
+ st.session_state['authenticator'] = authenticator
46
+ return authenticator
47
+
48
+ def nav_page(page_name, timeout_secs=3):
49
+ nav_script = """
50
+ <script type="text/javascript">
51
+ function attempt_nav_page(page_name, start_time, timeout_secs) {
52
+ var links = window.parent.document.getElementsByTagName("a");
53
+ for (var i = 0; i < links.length; i++) {
54
+ if (links[i].href.toLowerCase().endsWith("/" + page_name.toLowerCase())) {
55
+ links[i].click();
56
+ return;
57
+ }
58
+ }
59
+ var elasped = new Date() - start_time;
60
+ if (elasped < timeout_secs * 1000) {
61
+ setTimeout(attempt_nav_page, 100, page_name, start_time, timeout_secs);
62
+ } else {
63
+ alert("Unable to navigate to page '" + page_name + "' after " + timeout_secs + " second(s).");
64
+ }
65
+ }
66
+ window.addEventListener("load", function() {
67
+ attempt_nav_page("%s", new Date(), %d);
68
+ });
69
+ </script>
70
+ """ % (page_name, timeout_secs)
71
+ html(nav_script)
72
+
73
+
74
+ # def load_local_css(file_name):
75
+ # with open(file_name) as f:
76
+ # st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
77
+
78
+
79
+ # def set_header():
80
+ # return st.markdown(f"""<div class='main-header'>
81
+ # <h1>MMM LiME</h1>
82
+ # <img src="https://assets-global.website-files.com/64c8fffb0e95cbc525815b79/64df84637f83a891c1473c51_Vector%20(Stroke).svg ">
83
+ # </div>""", unsafe_allow_html=True)
84
+
85
+ path = os.path.dirname(__file__)
86
+
87
+ file_ = open(f"{path}/mastercard_logo.png", "rb")
88
+
89
+ contents = file_.read()
90
+
91
+ data_url = base64.b64encode(contents).decode("utf-8")
92
+
93
+ file_.close()
94
+
95
+
96
+
97
+ DATA_PATH = './data'
98
+
99
+ IMAGES_PATH = './data/images_224_224'
100
+
101
+ # New - Sprint 2
102
+ if 'bin_dict' not in st.session_state:
103
+
104
+ with open("data_import.pkl", "rb") as f:
105
+ data = pickle.load(f)
106
+
107
+ st.session_state['bin_dict'] = data["bin_dict"]
108
+
109
+ # panel_col = [col.lower().replace('.','_').replace('@','_').replace(" ", "_").replace('-', '').replace(':', '').replace("__", "_") for col in st.session_state['bin_dict']['Panel Level 1'] ] [0]# set the panel column
110
+
111
+ panel_col="Panel"
112
+
113
+ is_panel = True if len(panel_col)>0 else False
114
+
115
+ date_col='Date'
116
+ #is_panel = False # flag if set to true - do panel level response curves
117
+
118
+ def load_local_css(file_name):
119
+
120
+ with open(file_name) as f:
121
+
122
+ st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
123
+
124
+
125
+
126
+
127
+
128
+ # def set_header():
129
+
130
+ # return st.markdown(f"""<div class='main-header'>
131
+
132
+ # <h1>H & M Recommendations</h1>
133
+
134
+ # <img src="data:image;base64,{data_url}", alt="Logo">
135
+
136
+ # </div>""", unsafe_allow_html=True)
137
+ path1 = os.path.dirname(__file__)
138
+
139
+ file_1 = open(f"{path}/ALDI_2017.png", "rb")
140
+
141
+ contents1 = file_1.read()
142
+
143
+ data_url1 = base64.b64encode(contents1).decode("utf-8")
144
+
145
+ file_1.close()
146
+
147
+
148
+
149
+ DATA_PATH1 = './data'
150
+
151
+ IMAGES_PATH1 = './data/images_224_224'
152
+
153
+
154
+
155
+
156
+
157
+ def set_header():
158
+ return st.markdown(f"""<div class='main-header'>
159
+ <!-- <h1></h1> -->
160
+ <div >
161
+ <img class='blend-logo' src="data:image;base64,{data_url1}", alt="Logo">
162
+ </div>""", unsafe_allow_html=True)
163
+
164
+ # def set_header():
165
+ # logo_path = "./path/to/your/local/LIME_logo.png" # Replace with the actual file path
166
+ # text = "LiME"
167
+ # return st.markdown(f"""<div class='main-header'>
168
+ # <img src="data:image/png;base64,{data_url}" alt="Logo" style="float: left; margin-right: 10px; width: 100px; height: auto;">
169
+ # <h1>{text}</h1>
170
+ # </div>""", unsafe_allow_html=True)
171
+
172
+
173
+ def s_curve(x,K,b,a,x0):
174
+ return K / (1 + b * np.exp(-a*(x-x0)))
175
+
176
+
177
+ def overview_test_data_prep_panel(X, df, spends_X, date_col, panel_col, target_col):
178
+ '''
179
+ function to create the data which is used in initialize data fn
180
+ X : X test with contributions
181
+ df : originally uploaded data (media data) which has raw vars
182
+ spends_X : spends of dates in X test
183
+ '''
184
+
185
+ # define channels
186
+ channels = {'paid_search': ['paid_search_impressions', 'paid_search_clicks'],
187
+
188
+ 'fb_level_achieved_tier_1': ['fb_level_achieved_tier_1_impressions'], #, 'fb:_level_achieved_-_tier_1_clicks'],
189
+
190
+ 'fb_level_achieved_tier_2': ['fb:_level_achieved_tier_2_impressions',
191
+ 'fb_level_achieved_tier_2_clicks'],
192
+
193
+ 'paid_social_others' : ['paid_social_others_impressions', 'paid_social_others_clicks'],
194
+
195
+ 'ga_app': ['ga_app_impressions', 'ga_app_clicks'],
196
+
197
+ 'digital_tactic_others': ['digital_tactic_others_impressions', 'digital_tactic_others_clicks'],
198
+
199
+ 'kwai': ['kwai_impressions', 'kwai_clicks'],
200
+
201
+ 'programmatic': ['programmatic_impressions', 'programmatic_clicks'],
202
+
203
+ # 'affiliates':['affiliates_clicks'],
204
+ #
205
+ # "indicacao":['indicacao_clicks'],
206
+ #
207
+ # "infleux":['infleux_clicks'],
208
+ #
209
+ # "influencer":['influencer_clicks']
210
+ }
211
+
212
+ channel_list = list(channels.keys())
213
+
214
+ # map transformed variable to raw variable name & channel name
215
+ # mapping eg : paid_search_clicks_lag_2 (transformed var) --> paid_search_clicks (raw var) --> paid_search (channel)
216
+ variables = {}
217
+ channel_and_variables = {}
218
+ new_variables = {}
219
+ new_channels_and_variables = {}
220
+
221
+ for transformed_var in [col for col in
222
+ X.drop(columns=[date_col, panel_col, target_col, 'pred', 'panel_effect']).columns if
223
+ "_contr" not in col]:
224
+ if len([col for col in df.columns if col in transformed_var]) == 1:
225
+ raw_var = [col for col in df.columns if col in transformed_var][0]
226
+ variables[transformed_var] = raw_var
227
+ channel_and_variables[raw_var] = [channel for channel, raw_vars in channels.items() if raw_var in raw_vars][
228
+ 0]
229
+ else:
230
+ new_variables[transformed_var] = transformed_var
231
+ new_channels_and_variables[transformed_var] = 'base'
232
+
233
+ # Raw DF
234
+ raw_X = pd.merge(X[[date_col, panel_col]], df[[date_col, panel_col] + list(variables.values())], how='left',
235
+ on=[date_col, panel_col])
236
+ assert len(raw_X) == len(X)
237
+
238
+ raw_X_cols = []
239
+ for i in raw_X.columns:
240
+ if i in channel_and_variables.keys():
241
+ raw_X_cols.append(channel_and_variables[i])
242
+ else:
243
+ raw_X_cols.append(i)
244
+ raw_X.columns = raw_X_cols
245
+
246
+ # Contribution DF
247
+ contr_X = X[[date_col, panel_col, 'panel_effect'] + [col for col in X.columns if
248
+ "_contr" in col and "sum_" not in col]].copy()
249
+ new_variables = [col for col in contr_X.columns if
250
+ "_flag" in col.lower() or "trend" in col.lower() or "sine" in col.lower()]
251
+ if len(new_variables) > 0:
252
+ contr_X['const'] = contr_X[['panel_effect'] + new_variables].sum(axis=1)
253
+ contr_X.drop(columns=['panel_effect'], inplace=True)
254
+ contr_X.drop(columns=new_variables, inplace=True)
255
+ else:
256
+ contr_X.rename(columns={'panel_effect': 'const'}, inplace=True)
257
+
258
+ new_contr_X_cols = []
259
+ for col in contr_X.columns:
260
+ col_clean = col.replace("_contr", "")
261
+ new_contr_X_cols.append(col_clean)
262
+ contr_X.columns = new_contr_X_cols
263
+
264
+ contr_X_cols = []
265
+ for i in contr_X.columns:
266
+ if i in variables.keys():
267
+ contr_X_cols.append(channel_and_variables[variables[i]])
268
+ else:
269
+ contr_X_cols.append(i)
270
+ contr_X.columns = contr_X_cols
271
+
272
+ # Spends DF
273
+ spends_X.columns = [col.replace("_cost", "") for col in spends_X.columns]
274
+
275
+ raw_X.rename(columns={"date": "Date"}, inplace=True)
276
+ contr_X.rename(columns={"date": "Date"}, inplace=True)
277
+ spends_X.rename(columns={'date': 'Week'}, inplace=True)
278
+
279
+ # Create excel
280
+ file_name = "data_test_overview_panel_#" + target_col + ".xlsx"
281
+ with pd.ExcelWriter(file_name) as writer:
282
+ raw_X.to_excel(writer, sheet_name="RAW DATA MMM", index=False)
283
+ contr_X.to_excel(writer, sheet_name="CONTRIBUTION MMM", index=False)
284
+ spends_X.to_excel(writer, sheet_name="SPEND INPUT", index=False)
285
+
286
+
287
+ def overview_test_data_prep_nonpanel(X, df, spends_X, date_col, target_col):
288
+ '''
289
+ function to create the data which is used in initialize data fn
290
+ X : X test with contributions
291
+ df : originally uploaded data (media data) which has raw vars
292
+ spends_X : spends of dates in X test
293
+ '''
294
+ # define channels
295
+ channels = {'paid_search': ['paid_search_impressions', 'paid_search_clicks'],
296
+
297
+ 'fb_level_achieved_tier_1': ['fb_level_achieved_tier_1_impressions', 'fb_level_achieved_tier_1_clicks'],
298
+
299
+ 'fb_level_achieved_tier_2': ['fb_level_achieved_tier_2_impressions',
300
+ 'fb_level_achieved_tier_2_clicks'],
301
+
302
+ 'paid_social_others' : ['paid_social_others_impressions', 'paid_social_others_clicks'],
303
+
304
+ 'ga_app_will_and_cid_pequena_baixo_risco': ['ga_app_will_and_cid_pequena_baixo_risco_impressions', 'ga_app_will_and_cid_pequena_baixo_risco_clicks'],
305
+
306
+ 'digital_tactic_others': ['digital_tactic_others_impressions', 'digital_tactic_others_clicks'],
307
+
308
+ 'kwai': ['kwai_impressions', 'kwai_clicks'],
309
+
310
+ 'programmatic': ['programmatic_impressions', 'programmatic_clicks'],
311
+
312
+ 'affiliates':['affiliates_clicks', 'affiliates_impressions'],
313
+
314
+ "indicacao":['indicacao_clicks', 'indicacao_impressions'],
315
+
316
+ "infleux":['infleux_clicks', 'infleux_impressions'],
317
+
318
+ "influencer":['influencer_clicks', 'influencer_impressions']
319
+ }
320
+
321
+ channel_list = list(channels.keys())
322
+
323
+ # map transformed variable to raw variable name & channel name
324
+ # mapping eg : paid_search_clicks_lag_2 (transformed var) --> paid_search_clicks (raw var) --> paid_search (channel)
325
+ variables = {}
326
+ channel_and_variables = {}
327
+ new_variables = {}
328
+ new_channels_and_variables = {}
329
+
330
+ cols_to_del = list(set([date_col, target_col, 'pred']).intersection((set(X.columns))))
331
+ for transformed_var in [col for col in
332
+ X.drop(columns=cols_to_del).columns if
333
+ "_contr" not in col]: # also has 'const'
334
+ if len([col for col in df.columns if col in transformed_var]) == 1: # col is raw var
335
+ raw_var = [col for col in df.columns if col in transformed_var][0]
336
+ variables[transformed_var] = raw_var
337
+ channel_and_variables[raw_var] = [channel for channel, raw_vars in channels.items() if raw_var in raw_vars][0]
338
+ else: # when no corresponding raw var then base
339
+ new_variables[transformed_var] = transformed_var
340
+ new_channels_and_variables[transformed_var] = 'base'
341
+
342
+ # Raw DF
343
+ raw_X = pd.merge(X[[date_col]], df[[date_col] + list(variables.values())], how='left',
344
+ on=[date_col])
345
+ assert len(raw_X) == len(X)
346
+
347
+ raw_X_cols = []
348
+ for i in raw_X.columns:
349
+ if i in channel_and_variables.keys():
350
+ raw_X_cols.append(channel_and_variables[i])
351
+ else:
352
+ raw_X_cols.append(i)
353
+ raw_X.columns = raw_X_cols
354
+
355
+ # Contribution DF
356
+ contr_X = X[[date_col] + [col for col in X.columns if "_contr" in col and "sum_" not in col]].copy()
357
+ # st.write(contr_X.columns)
358
+ new_variables = [col for col in contr_X.columns if
359
+ "_flag" in col.lower() or "trend" in col.lower() or "sine" in col.lower()]
360
+ if len(new_variables) > 0: # if new vars are available, their contributions should be added to base (called const)
361
+ contr_X['const_contr'] = contr_X[['const_contr'] + new_variables].sum(axis=1)
362
+ contr_X.drop(columns=new_variables, inplace=True)
363
+
364
+
365
+ new_contr_X_cols = []
366
+ for col in contr_X.columns:
367
+ col_clean = col.replace("_contr", "")
368
+ new_contr_X_cols.append(col_clean)
369
+ contr_X.columns = new_contr_X_cols
370
+
371
+ contr_X_cols = []
372
+ for i in contr_X.columns:
373
+ if i in variables.keys():
374
+ contr_X_cols.append(channel_and_variables[variables[i]])
375
+ else:
376
+ contr_X_cols.append(i)
377
+ contr_X.columns = contr_X_cols
378
+
379
+ # Spends DF
380
+ spends_X.columns = [col.replace("_cost", "").replace("_spends", '').replace("_spend", "") for col in spends_X.columns]
381
+
382
+ raw_X.rename(columns={"date": "Date"}, inplace=True)
383
+ contr_X.rename(columns={"date": "Date"}, inplace=True)
384
+ spends_X.rename(columns={'date': 'Week'}, inplace=True)
385
+
386
+ # Create excel
387
+ file_name = "data_test_overview_panel_#" + target_col + ".xlsx"
388
+ with pd.ExcelWriter(file_name) as writer:
389
+ raw_X.to_excel(writer, sheet_name="RAW DATA MMM", index=False)
390
+ contr_X.to_excel(writer, sheet_name="CONTRIBUTION MMM", index=False)
391
+ spends_X.to_excel(writer, sheet_name="SPEND INPUT", index=False)
392
+
393
+
394
+ def initialize_data(target_col,selected_markets):
395
+ # uopx_conv_rates = {'streaming_impressions' : 0.007,'digital_impressions' : 0.007,'search_clicks' : 0.00719,'tv_impressions' : 0.000173,
396
+ # "digital_clicks":0.005,"streaming_clicks":0.004,'streaming_spends':1,"tv_spends":1,"search_spends":1,
397
+ # "digital_spends":1}
398
+ #print('State initialized')
399
+ # excel = pd.read_excel("data_test_overview_panel.xlsx",sheet_name=None)
400
+ #excel = pd.read_excel("Overview_data_test_panel@#revenue.xlsx" + target_col + ".xlsx",sheet_name=None)
401
+
402
+ excel = pd.read_excel("Overview_data_test_panel@#revenue.xlsx",sheet_name=None)
403
+
404
+ raw_df = excel['RAW DATA MMM']
405
+
406
+ spend_df = excel['SPEND INPUT']
407
+ contri_df = excel['CONTRIBUTION MMM']
408
+
409
+ #st.write(raw_df)
410
+ if selected_markets!= "Total Market":
411
+
412
+ raw_df=raw_df[raw_df['Panel']==selected_markets]
413
+ spend_df=spend_df[spend_df['Panel']==selected_markets]
414
+ contri_df=contri_df[contri_df['Panel']==selected_markets]
415
+
416
+ else:
417
+ raw_df=raw_df.groupby('Date').sum().reset_index()
418
+ spend_df=spend_df.groupby('Week').sum().reset_index()
419
+ contri_df=contri_df.groupby('Date').sum().reset_index()
420
+ #Revenue_df = excel['Revenue']
421
+
422
+ ## remove sesonalities, indices etc ...
423
+ exclude_columns = ['Date', 'Week','Panel',date_col, panel_col,'Others'
424
+ ]
425
+
426
+ # Aggregate all 3 dfs to date level (from date-panel level)
427
+ raw_df[date_col]=pd.to_datetime(raw_df[date_col])
428
+ raw_df_aggregations = {c:'sum' for c in raw_df.columns if c not in exclude_columns}
429
+ raw_df = raw_df.groupby(date_col).agg(raw_df_aggregations).reset_index()
430
+
431
+ contri_df[date_col]=pd.to_datetime(contri_df[date_col])
432
+ contri_df_aggregations = {c:'sum' for c in contri_df.columns if c not in exclude_columns}
433
+ contri_df = contri_df.groupby(date_col).agg(contri_df_aggregations).reset_index()
434
+
435
+ input_df = raw_df.sort_values(by=[date_col])
436
+
437
+ output_df = contri_df.sort_values(by=[date_col])
438
+
439
+ spend_df['Week'] = pd.to_datetime(spend_df['Week'], format='%Y-%m-%d', errors='coerce')
440
+ spend_df_aggregations = {c: 'sum' for c in spend_df.columns if c not in exclude_columns}
441
+ spend_df = spend_df.groupby('Week').agg(spend_df_aggregations).reset_index()
442
+ # spend_df['Week'] = pd.to_datetime(spend_df['Week'], errors='coerce')
443
+ # spend_df = spend_df.sort_values(by='Week')
444
+
445
+
446
+ channel_list = [col for col in input_df.columns if col not in exclude_columns]
447
+
448
+ response_curves = {}
449
+ mapes = {}
450
+ rmses = {}
451
+ upper_limits = {}
452
+ powers = {}
453
+ r2 = {}
454
+ conv_rates = {}
455
+ output_cols = []
456
+ channels = {}
457
+ sales = None
458
+ dates = input_df.Date.values
459
+ actual_output_dic = {}
460
+ actual_input_dic = {}
461
+
462
+ # ONLY FOR TESTING
463
+ # channel_list=['programmatic']
464
+ infeasible_channels = [c for c in contri_df.select_dtypes(include=['float', 'int']).columns if contri_df[c].sum()<=0]
465
+ # st.write(infeasible_channels)
466
+ channel_list=list(set(channel_list)-set(infeasible_channels))
467
+
468
+
469
+ for inp_col in channel_list:
470
+ #st.write(inp_col)
471
+
472
+ # # New - Sprint 2
473
+ # if is_panel:
474
+ # input_df1 = input_df.groupby([date_col]).agg({inp_col:'sum'}).reset_index() # aggregate spends on date
475
+ # spends = input_df1[inp_col].values
476
+ # else :
477
+ # spends = input_df[inp_col].values
478
+ spends = spend_df[inp_col].values
479
+
480
+ x = spends.copy()
481
+ # upper limit for penalty
482
+ upper_limits[inp_col] = 2*x.max()
483
+
484
+
485
+
486
+ # contribution
487
+ # New - Sprint 2
488
+ out_col = [_col for _col in output_df.columns if _col.startswith(inp_col)][0]
489
+ if is_panel :
490
+ output_df1 = output_df.groupby([date_col]).agg({out_col:'sum'}).reset_index()
491
+ y = output_df1[out_col].values.copy()
492
+ else :
493
+ y = output_df[out_col].values.copy()
494
+
495
+ actual_output_dic[inp_col] = y.copy()
496
+ actual_input_dic[inp_col] = x.copy()
497
+ ##output cols aggregation
498
+ output_cols.append(out_col)
499
+
500
+ ## scale the input
501
+ power = (np.ceil(np.log(x.max()) / np.log(10) )- 3)
502
+ if power >= 0 :
503
+ x = x / 10**power
504
+
505
+
506
+ x = x.astype('float64')
507
+ y = y.astype('float64')
508
+ #print('#printing yyyyyyyyy')
509
+ #print(inp_col)
510
+ #print(x.max())
511
+ #print(y.max())
512
+ # st.write(y.max(),x.max())
513
+ print(y.max(),x.max())
514
+ if y.max()<=0.01:
515
+ if x.max()<=0.01 :
516
+ st.write("here-here")
517
+ bounds = ((0, 0, 0, 0), (3 * 0.01, 1000, 1, 0.01))
518
+
519
+ else :
520
+ st.write("here")
521
+ bounds = ((0, 0, 0, 0), (3 * 0.01, 1000, 1, 0.01))
522
+ else :
523
+ bounds = ((0, 0, 0, 0), (3 * y.max(), 1000, 1, x.max()))
524
+ #bounds = ((y.max(), 3*y.max()),(0,1000),(0,1),(0,x.max()))
525
+ params,_ = curve_fit(s_curve,x,y,p0=(2*y.max(),0.01,1e-5,x.max()),
526
+ bounds=bounds,
527
+ maxfev=int(1e5))
528
+ mape = (100 * abs(1 - s_curve(x, *params) / y.clip(min=1))).mean()
529
+ rmse = np.sqrt(((y - s_curve(x,*params))**2).mean())
530
+ r2_ = r2_score(y, s_curve(x,*params))
531
+
532
+ response_curves[inp_col] = {'K' : params[0], 'b' : params[1], 'a' : params[2], 'x0' : params[3]}
533
+ mapes[inp_col] = mape
534
+ rmses[inp_col] = rmse
535
+ r2[inp_col] = r2_
536
+ powers[inp_col] = power
537
+
538
+
539
+ ## conversion rates
540
+ spend_col = [_col for _col in spend_df.columns if _col.startswith(inp_col.rsplit('_',1)[0])][0]
541
+
542
+ #print('#printing spendssss')
543
+ #print(spend_col)
544
+ conv = (spend_df.set_index('Week')[spend_col] / input_df.set_index('Date')[inp_col].clip(lower=1)).reset_index()
545
+ conv.rename(columns={'index':'Week'},inplace=True)
546
+ conv['year'] = conv.Week.dt.year
547
+ conv_rates[inp_col] = list(conv.drop('Week',axis=1).mean().to_dict().values())[0]
548
+ ##print('Before',conv_rates[inp_col])
549
+ # conv_rates[inp_col] = uopx_conv_rates[inp_col]
550
+ ##print('After',(conv_rates[inp_col]))
551
+
552
+
553
+ channel = Channel(name=inp_col,dates=dates,
554
+ spends=spends,
555
+ # conversion_rate = np.mean(list(conv_rates[inp_col].values())),
556
+ conversion_rate = conv_rates[inp_col],
557
+ response_curve_type='s-curve',
558
+ response_curve_params={'K' : params[0], 'b' : params[1], 'a' : params[2], 'x0' : params[3]},
559
+ bounds=np.array([-10,10]))
560
+ channels[inp_col] = channel
561
+ if sales is None:
562
+ sales = channel.actual_sales
563
+ else:
564
+ sales += channel.actual_sales
565
+ # st.write(inp_col, channel.actual_sales)
566
+ # st.write(output_cols)
567
+ other_contributions = output_df.drop([*output_cols], axis=1).sum(axis=1, numeric_only = True).values
568
+ correction = output_df.drop(['Date'],axis=1).sum(axis=1).values - (sales + other_contributions)
569
+
570
+ scenario_test_df=pd.DataFrame(columns=['other_contributions','correction', 'sales'])
571
+ scenario_test_df['other_contributions']=other_contributions
572
+ scenario_test_df['correction']=correction
573
+ scenario_test_df['sales']=sales
574
+ scenario_test_df.to_csv("test/scenario_test_df.csv",index=False)
575
+ output_df.to_csv("test/output_df.csv",index=False)
576
+
577
+ scenario = Scenario(name='default', channels=channels, constant=other_contributions, correction = correction)
578
+ ## setting session variables
579
+ st.session_state['initialized'] = True
580
+ st.session_state['actual_df'] = input_df
581
+ st.session_state['raw_df'] = raw_df
582
+ st.session_state['contri_df'] = output_df
583
+ default_scenario_dict = class_to_dict(scenario)
584
+ st.session_state['default_scenario_dict'] = default_scenario_dict
585
+ st.session_state['scenario'] = scenario
586
+ st.session_state['channels_list'] = channel_list
587
+ st.session_state['optimization_channels'] = {channel_name : False for channel_name in channel_list}
588
+ st.session_state['rcs'] = response_curves
589
+ st.session_state['powers'] = powers
590
+ st.session_state['actual_contribution_df'] = pd.DataFrame(actual_output_dic)
591
+ st.session_state['actual_input_df'] = pd.DataFrame(actual_input_dic)
592
+
593
+ for channel in channels.values():
594
+ st.session_state[channel.name] = numerize(channel.actual_total_spends * channel.conversion_rate,1)
595
+
596
+ st.session_state['xlsx_buffer'] = io.BytesIO()
597
+
598
+
599
+ if Path('../saved_scenarios.pkl').exists():
600
+ with open('../saved_scenarios.pkl','rb') as f:
601
+ st.session_state['saved_scenarios'] = pickle.load(f)
602
+ else:
603
+ st.session_state['saved_scenarios'] = OrderedDict()
604
+
605
+ st.session_state['total_spends_change'] = 0
606
+ st.session_state['optimization_channels'] = {channel_name : False for channel_name in channel_list}
607
+ st.session_state['disable_download_button'] = True
608
+
609
+ # def initialize_data():
610
+ # # fetch data from excel
611
+ # output = pd.read_excel('data.xlsx',sheet_name=None)
612
+ # raw_df = output['RAW DATA MMM']
613
+ # contribution_df = output['CONTRIBUTION MMM']
614
+ # Revenue_df = output['Revenue']
615
+
616
+ # ## channels to be shows
617
+ # channel_list = []
618
+ # for col in raw_df.columns:
619
+ # if 'click' in col.lower() or 'spend' in col.lower() or 'imp' in col.lower():
620
+ # ##print(col)
621
+ # channel_list.append(col)
622
+ # else:
623
+ # pass
624
+
625
+ # ## NOTE : Considered only Desktop spends for all calculations
626
+ # acutal_df = raw_df[raw_df.Region == 'Desktop'].copy()
627
+ # ## NOTE : Considered one year of data
628
+ # acutal_df = acutal_df[acutal_df.Date>'2020-12-31']
629
+ # actual_df = acutal_df.drop('Region',axis=1).sort_values(by='Date')[[*channel_list,'Date']]
630
+
631
+ # ##load response curves
632
+ # with open('./grammarly_response_curves.json','r') as f:
633
+ # response_curves = json.load(f)
634
+
635
+ # ## create channel dict for scenario creation
636
+ # dates = actual_df.Date.values
637
+ # channels = {}
638
+ # rcs = {}
639
+ # constant = 0.
640
+ # for i,info_dict in enumerate(response_curves):
641
+ # name = info_dict.get('name')
642
+ # response_curve_type = info_dict.get('response_curve')
643
+ # response_curve_params = info_dict.get('params')
644
+ # rcs[name] = response_curve_params
645
+ # if name != 'constant':
646
+ # spends = actual_df[name].values
647
+ # channel = Channel(name=name,dates=dates,
648
+ # spends=spends,
649
+ # response_curve_type=response_curve_type,
650
+ # response_curve_params=response_curve_params,
651
+ # bounds=np.array([-30,30]))
652
+
653
+ # channels[name] = channel
654
+ # else:
655
+ # constant = info_dict.get('value',0.) * len(dates)
656
+
657
+ # ## create scenario
658
+ # scenario = Scenario(name='default', channels=channels, constant=constant)
659
+ # default_scenario_dict = class_to_dict(scenario)
660
+
661
+
662
+ # ## setting session variables
663
+ # st.session_state['initialized'] = True
664
+ # st.session_state['actual_df'] = actual_df
665
+ # st.session_state['raw_df'] = raw_df
666
+ # st.session_state['default_scenario_dict'] = default_scenario_dict
667
+ # st.session_state['scenario'] = scenario
668
+ # st.session_state['channels_list'] = channel_list
669
+ # st.session_state['optimization_channels'] = {channel_name : False for channel_name in channel_list}
670
+ # st.session_state['rcs'] = rcs
671
+ # for channel in channels.values():
672
+ # if channel.name not in st.session_state:
673
+ # st.session_state[channel.name] = float(channel.actual_total_spends)
674
+
675
+ # if 'xlsx_buffer' not in st.session_state:
676
+ # st.session_state['xlsx_buffer'] = io.BytesIO()
677
+
678
+ # ## for saving scenarios
679
+ # if 'saved_scenarios' not in st.session_state:
680
+ # if Path('../saved_scenarios.pkl').exists():
681
+ # with open('../saved_scenarios.pkl','rb') as f:
682
+ # st.session_state['saved_scenarios'] = pickle.load(f)
683
+
684
+ # else:
685
+ # st.session_state['saved_scenarios'] = OrderedDict()
686
+
687
+ # if 'total_spends_change' not in st.session_state:
688
+ # st.session_state['total_spends_change'] = 0
689
+
690
+ # if 'optimization_channels' not in st.session_state:
691
+ # st.session_state['optimization_channels'] = {channel_name : False for channel_name in channel_list}
692
+
693
+ # if 'disable_download_button' not in st.session_state:
694
+ # st.session_state['disable_download_button'] = True
695
+ def create_channel_summary(scenario):
696
+ summary_columns = []
697
+
698
+ actual_spends_rows = []
699
+
700
+ actual_sales_rows = []
701
+
702
+ actual_roi_rows = []
703
+
704
+ for channel in scenario.channels.values():
705
+
706
+ name_mod = channel.name.replace('_', ' ')
707
+
708
+ if name_mod.lower().endswith(' imp'):
709
+ name_mod = name_mod.replace('Imp', ' Impressions')
710
+
711
+ print(name_mod, channel.actual_total_spends, channel.conversion_rate,
712
+ channel.actual_total_spends * channel.conversion_rate)
713
+
714
+ summary_columns.append(name_mod)
715
+
716
+ actual_spends_rows.append(format_numbers(float(channel.actual_total_spends * channel.conversion_rate)))
717
+
718
+ actual_sales_rows.append(format_numbers((float(channel.actual_total_sales))))
719
+
720
+ actual_roi_rows.append(decimal_formater(
721
+ format_numbers((channel.actual_total_sales) / (channel.actual_total_spends * channel.conversion_rate),
722
+ include_indicator=False, n_decimals=4), n_decimals=4))
723
+
724
+ actual_summary_df = pd.DataFrame([summary_columns, actual_spends_rows, actual_sales_rows, actual_roi_rows]).T
725
+
726
+ actual_summary_df.columns = ['Channel', 'Spends', 'Revenue', 'ROI']
727
+
728
+ actual_summary_df['Revenue'] = actual_summary_df['Revenue'].map(lambda x: str(x)[1:])
729
+
730
+ return actual_summary_df
731
+
732
+
733
+ # def create_channel_summary(scenario):
734
+ #
735
+ # # Provided data
736
+ # data = {
737
+ # 'Channel': ['Paid Search', 'Ga will cid baixo risco', 'Digital tactic others', 'Fb la tier 1', 'Fb la tier 2', 'Paid social others', 'Programmatic', 'Kwai', 'Indicacao', 'Infleux', 'Influencer'],
738
+ # 'Spends': ['$ 11.3K', '$ 155.2K', '$ 50.7K', '$ 125.4K', '$ 125.2K', '$ 105K', '$ 3.3M', '$ 47.5K', '$ 55.9K', '$ 632.3K', '$ 48.3K'],
739
+ # 'Revenue': ['558.0K', '3.5M', '5.2M', '3.1M', '3.1M', '2.1M', '20.8M', '1.6M', '728.4K', '22.9M', '4.8M']
740
+ # }
741
+ #
742
+ # # Create DataFrame
743
+ # df = pd.DataFrame(data)
744
+ #
745
+ # # Convert currency strings to numeric values
746
+ # df['Spends'] = df['Spends'].replace({'\$': '', 'K': '*1e3', 'M': '*1e6'}, regex=True).map(pd.eval).astype(int)
747
+ # df['Revenue'] = df['Revenue'].replace({'\$': '', 'K': '*1e3', 'M': '*1e6'}, regex=True).map(pd.eval).astype(int)
748
+ #
749
+ # # Calculate ROI
750
+ # df['ROI'] = ((df['Revenue'] - df['Spends']) / df['Spends'])
751
+ #
752
+ # # Format columns
753
+ # format_currency = lambda x: f"${x:,.1f}"
754
+ # format_roi = lambda x: f"{x:.1f}"
755
+ #
756
+ # df['Spends'] = ['$ 11.3K', '$ 155.2K', '$ 50.7K', '$ 125.4K', '$ 125.2K', '$ 105K', '$ 3.3M', '$ 47.5K', '$ 55.9K', '$ 632.3K', '$ 48.3K']
757
+ # df['Revenue'] = ['$ 536.3K', '$ 3.4M', '$ 5M', '$ 3M', '$ 3M', '$ 2M', '$ 20M', '$ 1.5M', '$ 7.1M', '$ 22M', '$ 4.6M']
758
+ # df['ROI'] = df['ROI'].apply(format_roi)
759
+ #
760
+ # return df
761
+
762
+
763
+ #@st.cache_data()
764
+ def create_contribution_pie(scenario):
765
+ #c1f7dc
766
+
767
+ light_blue = 'rgba(0, 31, 120, 0.7)'
768
+ light_orange = 'rgba(0, 181, 219, 0.7)'
769
+ light_green = 'rgba(240, 61, 20, 0.7)'
770
+ light_red = 'rgba(250, 110, 10, 0.7)'
771
+ light_purple = 'rgba(255, 191, 69, 0.7)'
772
+
773
+ colors_map = {col:color for col,color in zip(st.session_state['channels_list'],plotly.colors.n_colors(plotly.colors.hex_to_rgb('#BE6468'), plotly.colors.hex_to_rgb('#E7B8B7'),23))}
774
+ total_contribution_fig = make_subplots(rows=1, cols=2,subplot_titles=['Media Spends','Revenue Contribution'],specs=[[{"type": "pie"}, {"type": "pie"}]])
775
+ total_contribution_fig.add_trace(
776
+ go.Pie(labels=[channel_name_formating(channel_name) for channel_name in st.session_state['channels_list']] + ['Non Media'],
777
+ values= [round(scenario.channels[channel_name].actual_total_spends * scenario.channels[channel_name].conversion_rate,1) for channel_name in st.session_state['channels_list']] + [0],
778
+ marker_colors=[light_blue, light_orange, light_green, light_red, light_purple],
779
+ hole=0.3),
780
+ row=1, col=1)
781
+
782
+ total_contribution_fig.add_trace(
783
+ go.Pie(labels=[channel_name_formating(channel_name) for channel_name in st.session_state['channels_list']] + ['Non Media'],
784
+ values= [scenario.channels[channel_name].actual_total_sales for channel_name in st.session_state['channels_list']] + [scenario.correction.sum() + scenario.constant.sum()],
785
+ hole=0.3),
786
+ row=1, col=2)
787
+
788
+ total_contribution_fig.update_traces(textposition='inside',texttemplate='%{percent:.1%}')
789
+ total_contribution_fig.update_layout(uniformtext_minsize=12,title='', uniformtext_mode='hide')
790
+ return total_contribution_fig
791
+
792
+ #@st.cache_data()
793
+
794
+ # def create_contribuion_stacked_plot(scenario):
795
+ # weekly_contribution_fig = make_subplots(rows=1, cols=2,subplot_titles=['Spends','Revenue'],specs=[[{"type": "bar"}, {"type": "bar"}]])
796
+ # raw_df = st.session_state['raw_df']
797
+ # df = raw_df.sort_values(by='Date')
798
+ # x = df.Date
799
+ # weekly_spends_data = []
800
+ # weekly_sales_data = []
801
+ # for channel_name in st.session_state['channels_list']:
802
+ # weekly_spends_data.append((go.Bar(x=x,
803
+ # y=scenario.channels[channel_name].actual_spends * scenario.channels[channel_name].conversion_rate,
804
+ # name=channel_name_formating(channel_name),
805
+ # hovertemplate="Date:%{x}<br>Spend:%{y:$.2s}",
806
+ # legendgroup=channel_name)))
807
+ # weekly_sales_data.append((go.Bar(x=x,
808
+ # y=scenario.channels[channel_name].actual_sales,
809
+ # name=channel_name_formating(channel_name),
810
+ # hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}",
811
+ # legendgroup=channel_name, showlegend=False)))
812
+ # for _d in weekly_spends_data:
813
+ # weekly_contribution_fig.add_trace(_d, row=1, col=1)
814
+ # for _d in weekly_sales_data:
815
+ # weekly_contribution_fig.add_trace(_d, row=1, col=2)
816
+ # weekly_contribution_fig.add_trace(go.Bar(x=x,
817
+ # y=scenario.constant + scenario.correction,
818
+ # name='Non Media',
819
+ # hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}"), row=1, col=2)
820
+ # weekly_contribution_fig.update_layout(barmode='stack', title='Channel contribuion by week', xaxis_title='Date')
821
+ # weekly_contribution_fig.update_xaxes(showgrid=False)
822
+ # weekly_contribution_fig.update_yaxes(showgrid=False)
823
+ # return weekly_contribution_fig
824
+
825
+ # @st.cache_data(allow_output_mutation=True)
826
+ # def create_channel_spends_sales_plot(channel):
827
+ # if channel is not None:
828
+ # x = channel.dates
829
+ # _spends = channel.actual_spends * channel.conversion_rate
830
+ # _sales = channel.actual_sales
831
+ # channel_sales_spends_fig = make_subplots(specs=[[{"secondary_y": True}]])
832
+ # channel_sales_spends_fig.add_trace(go.Bar(x=x, y=_sales,marker_color='#c1f7dc',name='Revenue', hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}"), secondary_y = False)
833
+ # channel_sales_spends_fig.add_trace(go.Scatter(x=x, y=_spends,line=dict(color='#005b96'),name='Spends',hovertemplate="Date:%{x}<br>Spend:%{y:$.2s}"), secondary_y = True)
834
+ # channel_sales_spends_fig.update_layout(xaxis_title='Date',yaxis_title='Revenue',yaxis2_title='Spends ($)',title='Channel spends and Revenue week wise')
835
+ # channel_sales_spends_fig.update_xaxes(showgrid=False)
836
+ # channel_sales_spends_fig.update_yaxes(showgrid=False)
837
+ # else:
838
+ # raw_df = st.session_state['raw_df']
839
+ # df = raw_df.sort_values(by='Date')
840
+ # x = df.Date
841
+ # scenario = class_from_dict(st.session_state['default_scenario_dict'])
842
+ # _sales = scenario.constant + scenario.correction
843
+ # channel_sales_spends_fig = make_subplots(specs=[[{"secondary_y": True}]])
844
+ # channel_sales_spends_fig.add_trace(go.Bar(x=x, y=_sales,marker_color='#c1f7dc',name='Revenue', hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}"), secondary_y = False)
845
+ # # channel_sales_spends_fig.add_trace(go.Scatter(x=x, y=_spends,line=dict(color='#15C39A'),name='Spends',hovertemplate="Date:%{x}<br>Spend:%{y:$.2s}"), secondary_y = True)
846
+ # channel_sales_spends_fig.update_layout(xaxis_title='Date',yaxis_title='Revenue',yaxis2_title='Spends ($)',title='Channel spends and Revenue week wise')
847
+ # channel_sales_spends_fig.update_xaxes(showgrid=False)
848
+ # channel_sales_spends_fig.update_yaxes(showgrid=False)
849
+ # return channel_sales_spends_fig
850
+
851
+
852
+ # Define a shared color palette
853
+
854
+
855
+ # def create_contribution_pie():
856
+ # color_palette = ['#F3F3F0', '#5E7D7E', '#2FA1FF', '#00EDED', '#00EAE4', '#304550', '#EDEBEB', '#7FBEFD', '#003059', '#A2F3F3', '#E1D6E2', '#B6B6B6']
857
+ # total_contribution_fig = make_subplots(rows=1, cols=2, subplot_titles=['Spends', 'Revenue'], specs=[[{"type": "pie"}, {"type": "pie"}]])
858
+ #
859
+ # channels_list = ['Paid Search', 'Ga will cid baixo risco', 'Digital tactic others', 'Fb la tier 1', 'Fb la tier 2', 'Paid social others', 'Programmatic', 'Kwai', 'Indicacao', 'Infleux', 'Influencer', 'Non Media']
860
+ #
861
+ # # Assign colors from the limited palette to channels
862
+ # colors_map = {col: color_palette[i % len(color_palette)] for i, col in enumerate(channels_list)}
863
+ # colors_map['Non Media'] = color_palette[5] # Assign fixed green color for 'Non Media'
864
+ #
865
+ # # Hardcoded values for Spends and Revenue
866
+ # spends_values = [0.5, 3.36, 1.1, 2.7, 2.7, 2.27, 70.6, 1, 1, 13.7, 1, 0]
867
+ # revenue_values = [1, 4, 5, 3, 3, 2, 50.8, 1.5, 0.7, 13, 0, 16]
868
+ #
869
+ # # Add trace for Spends pie chart
870
+ # total_contribution_fig.add_trace(
871
+ # go.Pie(
872
+ # labels=[channel_name for channel_name in channels_list],
873
+ # values=spends_values,
874
+ # marker=dict(colors=[colors_map[channel_name] for channel_name in channels_list]),
875
+ # hole=0.3
876
+ # ),
877
+ # row=1, col=1
878
+ # )
879
+ #
880
+ # # Add trace for Revenue pie chart
881
+ # total_contribution_fig.add_trace(
882
+ # go.Pie(
883
+ # labels=[channel_name for channel_name in channels_list],
884
+ # values=revenue_values,
885
+ # marker=dict(colors=[colors_map[channel_name] for channel_name in channels_list]),
886
+ # hole=0.3
887
+ # ),
888
+ # row=1, col=2
889
+ # )
890
+ #
891
+ # total_contribution_fig.update_traces(textposition='inside', texttemplate='%{percent:.1%}')
892
+ # total_contribution_fig.update_layout(uniformtext_minsize=12, title='Channel contribution', uniformtext_mode='hide')
893
+ # return total_contribution_fig
894
+
895
+ def create_contribuion_stacked_plot(scenario):
896
+ weekly_contribution_fig = make_subplots(rows=1, cols=2, subplot_titles=['Spends', 'Revenue'], specs=[[{"type": "bar"}, {"type": "bar"}]])
897
+ raw_df = st.session_state['raw_df']
898
+ df = raw_df.sort_values(by='Date')
899
+ x = df.Date
900
+ weekly_spends_data = []
901
+ weekly_sales_data = []
902
+
903
+ for i, channel_name in enumerate(st.session_state['channels_list']):
904
+ color = color_palette[i % len(color_palette)]
905
+
906
+ weekly_spends_data.append(go.Bar(
907
+ x=x,
908
+ y=scenario.channels[channel_name].actual_spends * scenario.channels[channel_name].conversion_rate,
909
+ name=channel_name_formating(channel_name),
910
+ hovertemplate="Date:%{x}<br>Spend:%{y:$.2s}",
911
+ legendgroup=channel_name,
912
+ marker_color=color,
913
+ ))
914
+
915
+ weekly_sales_data.append(go.Bar(
916
+ x=x,
917
+ y=scenario.channels[channel_name].actual_sales,
918
+ name=channel_name_formating(channel_name),
919
+ hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}",
920
+ legendgroup=channel_name,
921
+ showlegend=False,
922
+ marker_color=color,
923
+ ))
924
+
925
+ for _d in weekly_spends_data:
926
+ weekly_contribution_fig.add_trace(_d, row=1, col=1)
927
+ for _d in weekly_sales_data:
928
+ weekly_contribution_fig.add_trace(_d, row=1, col=2)
929
+
930
+ weekly_contribution_fig.add_trace(go.Bar(
931
+ x=x,
932
+ y=scenario.constant + scenario.correction,
933
+ name='Non Media',
934
+ hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}",
935
+ marker_color=color_palette[-1],
936
+ ), row=1, col=2)
937
+
938
+ weekly_contribution_fig.update_layout(barmode='stack', title='Channel contribution by week', xaxis_title='Date')
939
+ weekly_contribution_fig.update_xaxes(showgrid=False)
940
+ weekly_contribution_fig.update_yaxes(showgrid=False)
941
+ return weekly_contribution_fig
942
+
943
+ def create_channel_spends_sales_plot(channel):
944
+ if channel is not None:
945
+ x = channel.dates
946
+ _spends = channel.actual_spends * channel.conversion_rate
947
+ _sales = channel.actual_sales
948
+ channel_sales_spends_fig = make_subplots(specs=[[{"secondary_y": True}]])
949
+ channel_sales_spends_fig.add_trace(go.Bar(
950
+ x=x,
951
+ y=_sales,
952
+ marker_color=color_palette[1], # You can choose a color from the palette
953
+ name='Revenue',
954
+ hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}",
955
+ ), secondary_y=False)
956
+
957
+ channel_sales_spends_fig.add_trace(go.Scatter(
958
+ x=x,
959
+ y=_spends,
960
+ line=dict(color=color_palette[3]), # You can choose another color from the palette
961
+ name='Spends',
962
+ hovertemplate="Date:%{x}<br>Spend:%{y:$.2s}",
963
+ ), secondary_y=True)
964
+
965
+ channel_sales_spends_fig.update_layout(xaxis_title='Date', yaxis_title='Revenue', yaxis2_title='Spends ($)', title='Channel spends and Revenue week-wise')
966
+ channel_sales_spends_fig.update_xaxes(showgrid=False)
967
+ channel_sales_spends_fig.update_yaxes(showgrid=False)
968
+ else:
969
+ raw_df = st.session_state['raw_df']
970
+ df = raw_df.sort_values(by='Date')
971
+ x = df.Date
972
+ scenario = class_from_dict(st.session_state['default_scenario_dict'])
973
+ _sales = scenario.constant + scenario.correction
974
+ channel_sales_spends_fig = make_subplots(specs=[[{"secondary_y": True}]])
975
+ channel_sales_spends_fig.add_trace(go.Bar(
976
+ x=x,
977
+ y=_sales,
978
+ marker_color=color_palette[0], # You can choose a color from the palette
979
+ name='Revenue',
980
+ hovertemplate="Date:%{x}<br>Revenue:%{y:$.2s}",
981
+ ), secondary_y=False)
982
+
983
+ channel_sales_spends_fig.update_layout(xaxis_title='Date', yaxis_title='Revenue', yaxis2_title='Spends ($)', title='Channel spends and Revenue week-wise')
984
+ channel_sales_spends_fig.update_xaxes(showgrid=False)
985
+ channel_sales_spends_fig.update_yaxes(showgrid=False)
986
+
987
+ return channel_sales_spends_fig
988
+
989
+ def format_numbers(value, n_decimals=1,include_indicator = True):
990
+ if include_indicator:
991
+ return f'{CURRENCY_INDICATOR} {numerize(value,n_decimals)}'
992
+ else:
993
+ return f'{numerize(value,n_decimals)}'
994
+
995
+
996
+ def decimal_formater(num_string,n_decimals=1):
997
+ parts = num_string.split('.')
998
+ if len(parts) == 1:
999
+ return num_string+'.' + '0'*n_decimals
1000
+ else:
1001
+ to_be_padded = n_decimals - len(parts[-1])
1002
+ if to_be_padded > 0 :
1003
+ return num_string+'0'*to_be_padded
1004
+ else:
1005
+ return num_string
1006
+
1007
+
1008
+ def channel_name_formating(channel_name):
1009
+ name_mod = channel_name.replace('_', ' ')
1010
+ if name_mod.lower().endswith(' imp'):
1011
+ name_mod = name_mod.replace('Imp','Spend')
1012
+ elif name_mod.lower().endswith(' clicks'):
1013
+ name_mod = name_mod.replace('Clicks','Spend')
1014
+ return name_mod
1015
+
1016
+
1017
+ def send_email(email,message):
1018
+ s = smtplib.SMTP('smtp.gmail.com', 587)
1019
+ s.starttls()
1020
+ s.login("geethu4444@gmail.com", "jgydhpfusuremcol")
1021
+ s.sendmail("geethu4444@gmail.com", email, message)
1022
+ s.quit()
1023
+
1024
+ if __name__ == "__main__":
1025
+ initialize_data()