Spaces:
Runtime error
Runtime error
Duplicate from loxzdigital/Model-CTA-Space
Browse filesCo-authored-by: Andy Lau <[email protected]>
- .gitattributes +32 -0
- .streamlit/config.toml +9 -0
- README.md +13 -0
- The perfect ‘side’ kick to your 🍕 copy.html +616 -0
- app.py +835 -0
- cta_text_list.txt +133 -0
- cta_verbs_list.txt +21 -0
- data/cta_text_list.txt +133 -0
- data/cta_verbs_list.txt +21 -0
- data/html_tags.csv +109 -0
- environment.yml +154 -0
- figures/ModelCTA.png +0 -0
- html_tags.csv +109 -0
- main_app.py +15 -0
- models/modelCTA_CTOR.sav +3 -0
- models/modelCTA_CTOR_new.sav +3 -0
- models/modelCTA_ConversionRate_new.sav +3 -0
- models/modelCTA_Conversion_Rate.sav +3 -0
- requirements.txt +8 -0
- utils.py +553 -0
.gitattributes
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.sav filter=lfs diff=lfs merge=lfs -text
|
.streamlit/config.toml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[theme]
|
| 2 |
+
base="light"
|
| 3 |
+
#primaryColor="#a40303"
|
| 4 |
+
#backgroundColor="#FFF"
|
| 5 |
+
#textColor="#ffffff"
|
| 6 |
+
#secondaryBackgroundColor="#126072"
|
| 7 |
+
|
| 8 |
+
[browser]
|
| 9 |
+
gatherUsageStats = false
|
README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Model CTA Space
|
| 3 |
+
emoji: ✉️
|
| 4 |
+
colorFrom: green
|
| 5 |
+
colorTo: blue
|
| 6 |
+
sdk: streamlit
|
| 7 |
+
sdk_version: 1.10.0
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
duplicated_from: loxzdigital/Model-CTA-Space
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
The perfect ‘side’ kick to your 🍕 copy.html
ADDED
|
@@ -0,0 +1,616 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Delivered-To: [email protected]
|
| 2 |
+
Received: by 2002:a4f:f50c:0:0:0:0:0 with SMTP id k12csp135665ivo;
|
| 3 |
+
Wed, 24 Aug 2022 17:59:49 -0700 (PDT)
|
| 4 |
+
X-Google-Smtp-Source: AA6agR47j/NB6hwHpJgCTK/N08fBfwxecBwLtHnBTaA7m5iTqgBdR87v1u9/uDgUG/lExqyotITu
|
| 5 |
+
X-Received: by 2002:a05:622a:1cd:b0:344:7bcb:6bad with SMTP id t13-20020a05622a01cd00b003447bcb6badmr1735420qtw.672.1661389189098;
|
| 6 |
+
Wed, 24 Aug 2022 17:59:49 -0700 (PDT)
|
| 7 |
+
ARC-Seal: i=1; a=rsa-sha256; t=1661389189; cv=none;
|
| 8 |
+
d=google.com; s=arc-20160816;
|
| 9 |
+
b=cJKI4/3m2UFc7D47dcmhT44tjkp+upETM6ZM30OVKYcLYW7k5XV8A/fVSqyTv/Yz6n
|
| 10 |
+
3j+B5CFuEv6r2n+Zxj2GVoidmNsQIgK9t16F7UgkQZ5VeYRJd5BJ844ZO2jrEwEKI+Sw
|
| 11 |
+
LDq6tcI5wMeEsjVLGhXZzI29KtAXtck8Gf/ET/uDg+HkWVoYT7Et4eDbTSssRbToeJb4
|
| 12 |
+
Tg8s7sJGcnGImdLpwnHZlgzHZ2s5O4dzBjmpjxctL3uL/jY0+7Ki9so0i7UzqVkdF4+m
|
| 13 |
+
Pz83dbrb3O6Cihr6V/fjVpm11zNVMWefssMH92SGFKPCDe8xkvqGHsFyAlRSUKVd9Dk3
|
| 14 |
+
qyXQ==
|
| 15 |
+
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816;
|
| 16 |
+
h=feedback-id:message-id:list-id:reply-to:mime-version
|
| 17 |
+
:list-unsubscribe-post:list-unsubscribe:date:subject:to:from
|
| 18 |
+
:dkim-signature;
|
| 19 |
+
bh=1t2VTd2qG9Ojr3nGlZBGETW61krnIjlQ5SzJjkajj8k=;
|
| 20 |
+
b=tRkfE/B7V7+LNcsD6X2PcI74WNqnioGJlqYUo8QcduSuyuxmQAgBjM4P/7EoP0XwEZ
|
| 21 |
+
3UvptJmo49CMo7qI2wPfhzb/o+oVdCINFPQmdV/7obQiKgCMhXDJQtmbx7mkLpF81IIw
|
| 22 |
+
uNKyhZYgxRCkP7bTVBnTUNMkUBfEr2PrC+IiTzkjP1OsEreDI7zdcn6qBaRojJGFayEp
|
| 23 |
+
ESLnBJtmtDUh9V1H4kS/kwedLmfH9z9VPzjSvwLBA5F9XP5ijFncRP7Od9y+/BeNXpAi
|
| 24 |
+
GKG3MA6Qf4hGpUXyOlmGGKMHqdPZu9vuptA2aWpeZFEYVuaNn+Vm8fdJTXdaL4/v5dpn
|
| 25 |
+
jWUw==
|
| 26 |
+
ARC-Authentication-Results: i=1; mx.google.com;
|
| 27 |
+
dkim=pass [email protected] header.s=200608 header.b=Srl+yW+k;
|
| 28 |
+
spf=pass (google.com: domain of bounce-1151_html-488741477-687714-75384-13010@bounce.papajohns-specials.com designates 66.231.87.89 as permitted sender) smtp.mailfrom=bounce-1151_HTML-488741477-687714-75384-13010@bounce.papajohns-specials.com;
|
| 29 |
+
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=papajohns-specials.com
|
| 30 |
+
Return-Path: <bounce-1151_HTML-488741477-687714-75384-13010@bounce.papajohns-specials.com>
|
| 31 |
+
Received: from mta4.papajohns-specials.com (mta4.papajohns-specials.com. [66.231.87.89])
|
| 32 |
+
by mx.google.com with ESMTPS id n11-20020a05622a11cb00b0034301eb5c75si10391490qtk.35.2022.08.24.17.59.48
|
| 33 |
+
for <[email protected]>
|
| 34 |
+
(version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128);
|
| 35 |
+
Wed, 24 Aug 2022 17:59:49 -0700 (PDT)
|
| 36 |
+
Received-SPF: pass (google.com: domain of bounce-1151_html-488741477-687714-75384-13010@bounce.papajohns-specials.com designates 66.231.87.89 as permitted sender) client-ip=66.231.87.89;
|
| 37 |
+
Authentication-Results: mx.google.com;
|
| 38 |
+
dkim=pass [email protected] header.s=200608 header.b=Srl+yW+k;
|
| 39 |
+
spf=pass (google.com: domain of bounce-1151_html-488741477-687714-75384-13010@bounce.papajohns-specials.com designates 66.231.87.89 as permitted sender) smtp.mailfrom=bounce-1151_HTML-488741477-687714-75384-13010@bounce.papajohns-specials.com;
|
| 40 |
+
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=papajohns-specials.com
|
| 41 |
+
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; s=200608; d=papajohns-specials.com;
|
| 42 |
+
h=From:To:Subject:Date:List-Unsubscribe:List-Unsubscribe-Post:MIME-Version:
|
| 43 |
+
Reply-To:List-ID:X-CSA-Complaints:Message-ID:Content-Type;
|
| 44 | |
| 45 |
+
bh=1t2VTd2qG9Ojr3nGlZBGETW61krnIjlQ5SzJjkajj8k=;
|
| 46 |
+
b=Srl+yW+k1xzkuZeTlLzsQNawG9vNaAJMB/+1P+8H99AxsX6XGEyMTVMhOSU39Tojk+NytlqLTudL
|
| 47 |
+
cEsxj1uzN8XAxxVOs8c8QTkrvG9RV5IRavscAfdTQQUMkqui8dVLynwkpltuE++VJtcnGqRynE6+
|
| 48 |
+
mRlhkDdPlNvZxEBd6E4=
|
| 49 |
+
Received: by mta4.papajohns-specials.com id h0r4oa2fmd4e for <[email protected]>; Wed, 24 Aug 2022 22:01:30 +0000 (envelope-from <bounce-1151_HTML-488741477-687714-75384-13010@bounce.papajohns-specials.com>)
|
| 50 |
+
From: "Papa Johns" <[email protected]>
|
| 51 |
+
To: <[email protected]>
|
| 52 |
+
Subject: =?UTF-8?B?VGhlIHBlcmZlY3Qg4oCYc2lkZeKAmSBraWNrIHRvIHlvdXIg?=
|
| 53 |
+
=?UTF-8?B?8J+NlQ==?=
|
| 54 |
+
Date: Wed, 24 Aug 2022 16:01:30 -0600
|
| 55 |
+
List-Unsubscribe: <https://click.papajohns-specials.com/subscription_center.aspx?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtaWQiOiI3NTM4NCIsInMiOiI0ODg3NDE0NzciLCJsaWQiOiIxMTUxIiwiaiI6IjY4NzcxNCIsImpiIjoiMTMwMTAiLCJkIjoiMTAxNTgifQ.nY9ACrq54qeN76u_I0ZH4Hq9bwVStpfEQZVeUzUX3YM>, <mailto:leave-fd23157170656b2531492c-fe1d10787d63007c711377-febe127872630579-fef41375766c00-ff0a1573756504@leave.papajohns-specials.com>
|
| 56 |
+
List-Unsubscribe-Post: List-Unsubscribe=One-Click
|
| 57 |
+
x-CSA-Compliance-Source: SFMC
|
| 58 |
+
MIME-Version: 1.0
|
| 59 |
+
Reply-To: "Papa John's" <reply-687714-1151_HTML-488741477-75384-13010@papajohns-specials.com>
|
| 60 |
+
List-ID: <75384.xt.local>
|
| 61 |
+
X-CSA-Complaints: [email protected]
|
| 62 |
+
X-SFMC-Stack: 1
|
| 63 |
+
x-job: 75384_687714
|
| 64 |
+
Message-ID: <[email protected]>
|
| 65 |
+
Feedback-ID: 75384:687714:66.231.87.89:sfmktgcld
|
| 66 |
+
Content-Type: multipart/alternative;
|
| 67 |
+
boundary="g0N9pXlJsPlq=_?:"
|
| 68 |
+
|
| 69 |
+
This is a multi-part message in MIME format.
|
| 70 |
+
|
| 71 |
+
--g0N9pXlJsPlq=_?:
|
| 72 |
+
Content-Type: text/plain;
|
| 73 |
+
charset="utf-8"
|
| 74 |
+
Content-Transfer-Encoding: 8bit
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d920c9f118561e71044855e9059de2f15562b266203708c97ffb8d932779a8108ab2cd9166741cdfe3e8fc6a5993a3e8f
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
Side items for everyone to enjoy.
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
Our delicious sides are the perfect complement
|
| 98 |
+
to your Papa Johns pizza! From our melty
|
| 99 |
+
cheesesticks to our garlicky knots, there’s
|
| 100 |
+
something for everyone to enjoy. Add any side
|
| 101 |
+
to your order today!
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d920c9f118561e71044855e9059de2f15562b266203708c97ffb8d932779a8108ab2cd9166741cdfe3e8fc6a5993a3e8f
|
| 114 |
+
ORDER NOW
|
| 115 |
+
|
| 116 |
+
https://click.papajohns-specials.com/?qs=7b839e1cbb1a919dc01e165f1741301e51badbd376caee53fe2af0ca9e8866f4a2dba4d869420f83db91bec0cbe284e10aeaa7d1b8cccf8e
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
It’s Epic Pepperoni-Stuffed Crust!
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
Just when you thought our Epic Stuffed Crust couldn't
|
| 124 |
+
get any more epic, we stuffed our signature pepperoni
|
| 125 |
+
and melty cheese into a delicious, garlic-seasoned
|
| 126 |
+
crust to create our new Epic Pepperoni-Stuffed Crust.
|
| 127 |
+
You're welcome, pepperoni lovers.
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
https://click.papajohns-specials.com/?qs=7b839e1cbb1a919dc01e165f1741301e51badbd376caee53fe2af0ca9e8866f4a2dba4d869420f83db91bec0cbe284e10aeaa7d1b8cccf8e
|
| 140 |
+
ORDER NOW
|
| 141 |
+
|
| 142 |
+
https://click.papajohns-specials.com/?qs=7b839e1cbb1a919dd5742d17763d9de51021ec0008b5ff67ccfaaf7f1ab480f25f3e669ea92a1260bec395b60ddf81453586446b5ffaec14
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
https://click.papajohns-specials.com/?qs=7b839e1cbb1a919dba3bf24b34a92e622fd35fc32127b7ac555b3a982bfe9e0a606b648d937318d927558528450d61e24b77ed84f03a03bc
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d3405a6ea71457acb9957de46beeb6a6d61cd01ca703549cb6c2f142878c215717b520dbdea0f72b639777f6cb61d5806
|
| 154 |
+
|
| 155 |
+
https://click.papajohns-specials.com/?qs=7b839e1cbb1a919dff3b448f651204b982e258661922307fdd495243789e3a16f59dfe5521bf91208366c7db5c034f16e632ab87a29f3bc9
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
Text START to 47272
|
| 159 |
+
|
| 160 |
+
https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d4865bcb8f770a2a57d5f893454505d388e6145df7251615b7bfb116dbbfa62255b6f1629795329531a37b13c63b2d869
|
| 161 |
+
Manage Email Preferences |
|
| 162 |
+
https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d4865bcb8f770a2a57d5f893454505d388e6145df7251615b7bfb116dbbfa62255b6f1629795329531a37b13c63b2d869
|
| 163 |
+
Unsubscribe |
|
| 164 |
+
https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d640b4c31bc276f1b1f59245b94b6e1e73c8efb42097cb31b62ac78275ff10fb763f369609d82cea295b822c7b36a59f5
|
| 165 |
+
Contact Us |
|
| 166 |
+
https://click.papajohns-specials.com/?qs=7b839e1cbb1a919df20eae798e405915ca0db6c4b5b496f0c62109e24242b804d0296ced9ae1a99bce95f9dce21499240a597d9824c3f097
|
| 167 |
+
Privacy Policy
|
| 168 |
+
|
| 169 |
+
Offer good online only at participating U.S. Papa Johns restaurants; prices may vary. Offer may require the purchase of multiple products. Additional toppings extra. Not valid with any other coupons or discounts. Limited delivery area. Delivery may require a minimum purchase and delivery fee; delivery fee is not subject to discount. Minimum purchase does not include tax, tip, or delivery fee. Customer responsible for all applicable taxes. ©2022 Papa John's International, Inc. All Rights Reserved.
|
| 170 |
+
|
| 171 |
+
SMS Text messaging for US customers only.
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
You have received this Email because you have selected the option to receive notices about specials and other online related information from us in your Email Preferences. To ensure future delivery of emails, please add
|
| 175 |
+
mailto: [email protected]
|
| 176 |
+
[email protected] to your safe sender list or address book.
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
Need help?
|
| 180 |
+
https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d640b4c31bc276f1b1f59245b94b6e1e73c8efb42097cb31b62ac78275ff10fb763f369609d82cea295b822c7b36a59f5
|
| 181 |
+
Contact Us
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
Papa John's International, 2002 Papa John's Blvd, Louisville, KY 40299
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
To ensure future delivery of emails, please add us to your safe sender list or address book.
|
| 188 |
+
|
| 189 |
+
Trouble viewing this email?
|
| 190 |
+
https://view.papajohns-specials.com/?qs=9e273b79d89d55e7ac6217c3162116eb0210e3b5bee290754eef5f0443deb3676acbebdafa918f0362a4474ad7d8ef03221688909254bddf02d97a7def1c22f1be2e3127f096c46d075d5e3e8a4a0902
|
| 191 |
+
View in browser
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
--g0N9pXlJsPlq=_?:
|
| 196 |
+
Content-Type: text/html;
|
| 197 |
+
charset="utf-8"
|
| 198 |
+
Content-Transfer-Encoding: 8bit
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
| 206 |
+
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
| 207 |
+
<head>
|
| 208 |
+
|
| 209 |
+
<!--[if gte mso 9]><xml> <o:OfficeDocumentSettings> <o:AllowPNG/> <o:PixelsPerInch>96</o:PixelsPerInch> </o:OfficeDocumentSettings> </xml><![endif]-->
|
| 210 |
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
| 211 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 212 |
+
<meta name="color-scheme" content="light dark">
|
| 213 |
+
<meta name="supported-color-schemes" content="light dark">
|
| 214 |
+
<style type="text/css">
|
| 215 |
+
#MessageViewBody, #MessageWebViewDiv {
|
| 216 |
+
width: 100% !important;
|
| 217 |
+
}
|
| 218 |
+
body {
|
| 219 |
+
-webkit-text-size-adjust: 100%;
|
| 220 |
+
-ms-text-size-adjust: 100%;
|
| 221 |
+
margin: 0 !important;
|
| 222 |
+
padding: 0 !important;
|
| 223 |
+
}
|
| 224 |
+
ReadMsgBody {
|
| 225 |
+
width: 100%;
|
| 226 |
+
}
|
| 227 |
+
.ExternalClass {
|
| 228 |
+
width: 100%;
|
| 229 |
+
}
|
| 230 |
+
.ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {
|
| 231 |
+
line-height: 100%;
|
| 232 |
+
}
|
| 233 |
+
p {
|
| 234 |
+
margin: 1em 0;
|
| 235 |
+
}
|
| 236 |
+
table td {
|
| 237 |
+
border-collapse: collapse;
|
| 238 |
+
}
|
| 239 |
+
img {
|
| 240 |
+
outline: 0;
|
| 241 |
+
}
|
| 242 |
+
a img {
|
| 243 |
+
border: none;
|
| 244 |
+
}
|
| 245 |
+
p {
|
| 246 |
+
margin: 1em 0;
|
| 247 |
+
}
|
| 248 |
+
.arial {
|
| 249 |
+
font-family: Arial, Helvetica, sans-serif;
|
| 250 |
+
color: #000000;
|
| 251 |
+
}
|
| 252 |
+
.header-arial-black {
|
| 253 |
+
font-family: 'Arial Black', Arial, Helvetica, sans-serif;
|
| 254 |
+
color: #000000;
|
| 255 |
+
}
|
| 256 |
+
.arial-footer {
|
| 257 |
+
font-famil: Arial, Helvetica, 'sans-serif';
|
| 258 |
+
color: #818795;
|
| 259 |
+
}
|
| 260 |
+
@-ms-viewport {
|
| 261 |
+
width: device-width;
|
| 262 |
+
}
|
| 263 |
+
</style>
|
| 264 |
+
<style type="text/css">
|
| 265 |
+
@media only screen and (max-width: 480px) {
|
| 266 |
+
.container {
|
| 267 |
+
width: 100% !important;
|
| 268 |
+
}
|
| 269 |
+
.footer {
|
| 270 |
+
width: auto !important;
|
| 271 |
+
margin-left: 0;
|
| 272 |
+
}
|
| 273 |
+
.mobile-hidden {
|
| 274 |
+
display: none !important;
|
| 275 |
+
}
|
| 276 |
+
img {
|
| 277 |
+
max-width: 100% !important;
|
| 278 |
+
height: auto !important;
|
| 279 |
+
max-height: auto !important;
|
| 280 |
+
}
|
| 281 |
+
.drop {
|
| 282 |
+
display: block !important;
|
| 283 |
+
}
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
@media only screen and (max-width: 640px) {
|
| 287 |
+
.container {
|
| 288 |
+
width: 100% !important;
|
| 289 |
+
}
|
| 290 |
+
.mobile-hidden {
|
| 291 |
+
display: none !important;
|
| 292 |
+
}
|
| 293 |
+
}
|
| 294 |
+
|
| 295 |
+
@media (prefers-color-scheme: dark ) {
|
| 296 |
+
/* iOS */
|
| 297 |
+
/* Custom Dark Mode Background Color */
|
| 298 |
+
.darkModeBackground {
|
| 299 |
+
background-color: #FFFFFF !important;
|
| 300 |
+
}
|
| 301 |
+
.darkModeBlackBackground {
|
| 302 |
+
background-color: #000000 !important;
|
| 303 |
+
}
|
| 304 |
+
/* Custom Dark Mode Font Colors */
|
| 305 |
+
h1, h2, p, span, a, b {
|
| 306 |
+
color: #FFFFFF !important;
|
| 307 |
+
}
|
| 308 |
+
.arial {
|
| 309 |
+
color: #FFFFFF !important;
|
| 310 |
+
}
|
| 311 |
+
.header-arial-black {
|
| 312 |
+
color: #FFFFFF !important;
|
| 313 |
+
}
|
| 314 |
+
.arial-footer {
|
| 315 |
+
color: #FFFFFF !important;
|
| 316 |
+
}
|
| 317 |
+
.arial-green {
|
| 318 |
+
color: #00582C !important;
|
| 319 |
+
}
|
| 320 |
+
.arial-black {
|
| 321 |
+
color: #000000 !important;
|
| 322 |
+
}
|
| 323 |
+
}
|
| 324 |
+
/* Outlook */
|
| 325 |
+
/* Custom Dark Mode Background Color */
|
| 326 |
+
[data-ogsc] .darkmodeBackground {
|
| 327 |
+
background-color: #FFFFFF !important;
|
| 328 |
+
}
|
| 329 |
+
/* Custom Dark Mode Font Colors */
|
| 330 |
+
[data-ogsc] h1, [data-ogsc] h2, [data-ogsc] p, [data-ogsc] span, [data-ogsc] a, [data-ogsc] b {
|
| 331 |
+
color: #ffffff !important;
|
| 332 |
+
}
|
| 333 |
+
[data-ogsc] .arial {
|
| 334 |
+
color: #FFFFFF !important;
|
| 335 |
+
}
|
| 336 |
+
[data-ogsc] .header-impact {
|
| 337 |
+
color: #FFFFFF !important;
|
| 338 |
+
}
|
| 339 |
+
[data-ogsc] .header-arial-black {
|
| 340 |
+
color: #FFFFFF !important;
|
| 341 |
+
}
|
| 342 |
+
[data-ogsc] .arial-footer {
|
| 343 |
+
color: #FFFFFF !important;
|
| 344 |
+
}
|
| 345 |
+
[data-ogsc] .arial-green {
|
| 346 |
+
color: #00582C !important;
|
| 347 |
+
}
|
| 348 |
+
[data-ogsc] .arial-black {
|
| 349 |
+
color: #000000 !important;
|
| 350 |
+
}
|
| 351 |
+
</style>
|
| 352 |
+
<!--[if mso]> <style type="text/css"> .fallback-font { font-family: Arial, Helvetica, sans-serif; } .fallback-font-header { font-family: 'Arial Black', Arial, Helvetica, sans-serif; } </style> <![endif]-->
|
| 353 |
+
</head>
|
| 354 |
+
<body align="center" style="padding:0; margin:0 auto !important; text-align:center;"><style type="text/css">
|
| 355 |
+
div.preheader
|
| 356 |
+
{ display: none !important; }
|
| 357 |
+
</style>
|
| 358 |
+
<div class="preheader" style="font-size: 1px; display: none !important;">From melty cheesesticks to savory wings, we have the sides for you</div>
|
| 359 |
+
<div style="font-size:0; line-height:0;">
|
| 360 |
+
<img src="https://click.papajohns-specials.com/open.aspx?ffcb10-febe127872630579-fe0315707161067475157271-fef41375766c00-ff3715717065-fe1d10787d63007c711377-ff0a1573756504&d=10158&bmt=0" width="1" height="1" alt="">
|
| 361 |
+
|
| 362 |
+
</div>
|
| 363 |
+
<table width="100%" border="0" cellpadding="0" cellspacing="0" align="center">
|
| 364 |
+
<tr>
|
| 365 |
+
<td align="center">
|
| 366 |
+
<table class="container" border="0" cellpadding="0" cellspacing="0" width="600">
|
| 367 |
+
<tr>
|
| 368 |
+
<td class="darkmodeBackground" align="center" valign="top" bgcolor="#FFFFFF" style="padding: 16px 0;">
|
| 369 |
+
<table border="0" align="center" cellpadding="0" cellspacing="0">
|
| 370 |
+
<tr>
|
| 371 |
+
<td width="150" align="center" valign="bottom"><img style="display:block;font-family:sans-serif;font-size:25px;color:#000000;" src="https://image.papajohns-specials.com/lib/fef41375766c00/m/1/7b104c84-f868-4e0d-87a5-2e42ab893117.png" border="0" alt="Papa Johns® | Better Ingredients. Better Pizza." width="210"></td>
|
| 372 |
+
</tr>
|
| 373 |
+
</table>
|
| 374 |
+
</td>
|
| 375 |
+
</tr>
|
| 376 |
+
|
| 377 |
+
<!-- TOP BANNER -->
|
| 378 |
+
|
| 379 |
+
|
| 380 |
+
<tr>
|
| 381 |
+
<td align="left" class="" valign="top">
|
| 382 |
+
<!---->
|
| 383 |
+
|
| 384 |
+
<!-- HERO -->
|
| 385 |
+
<table width="100%" cellspacing="0" cellpadding="0" border="0">
|
| 386 |
+
<tr>
|
| 387 |
+
<td width="100%" align="center" style="padding: 0 0 36px 0;">
|
| 388 |
+
<a href="https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d54e10e20b7234512b0db2d0e5e5b8c384f1d5c1c3100f4234781712e87f7abba1155c9856ccfa7e4177139d771d9242a" target="_blank">
|
| 389 |
+
<img src="https://image.papajohns-specials.com/lib/fef41375766c00/m/1/364671ce-28e3-4e43-a4a0-a510a6016591.png" style="display: block; padding: 0px; text-align: center;" alt="" width="100%">
|
| 390 |
+
</a>
|
| 391 |
+
</td>
|
| 392 |
+
</tr>
|
| 393 |
+
|
| 394 |
+
<!-- HEAD/SUBHEAD -->
|
| 395 |
+
</table>
|
| 396 |
+
<table style="margin:0 auto;" width="100%" cellspacing="0" cellpadding="0" border="0" bgcolor="#ffffff" align="center">
|
| 397 |
+
<tr>
|
| 398 |
+
<td width="600" style="padding:0px 10px 12px 10px;">
|
| 399 |
+
<table width="100%" cellspacing="0" cellpadding="0" border="0" align="center">
|
| 400 |
+
<!-- HEAD -->
|
| 401 |
+
<tr>
|
| 402 |
+
<td class="center arial fallback-font" style="color:#000000; font-family: Arial, Helvetica, san-serif; font-size: 19px; text-align: center; line-height: 27px;font-weight:bold;">
|
| 403 |
+
Side items for everyone to enjoy.
|
| 404 |
+
</td>
|
| 405 |
+
</tr>
|
| 406 |
+
<!-- SUBHEAD -->
|
| 407 |
+
<tr>
|
| 408 |
+
<td class="center arial fallback-font" style="color:#000000; font-family: Arial, Helvetica, san-serif; font-size: 19px; text-align: center; padding: 0px 0px 0px 0px;line-height: 27px;" width="100%">
|
| 409 |
+
|
| 410 |
+
Our delicious sides are the perfect complement<br class="mobile-hidden"> to your Papa Johns pizza! From our melty<br class="mobile-hidden"> cheesesticks to our garlicky knots, there’s <br class="mobile-hidden">something for everyone to enjoy. Add any side<br class="mobile-hidden"> to your order today!
|
| 411 |
+
|
| 412 |
+
|
| 413 |
+
|
| 414 |
+
|
| 415 |
+
|
| 416 |
+
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
|
| 420 |
+
</td>
|
| 421 |
+
</tr>
|
| 422 |
+
</table>
|
| 423 |
+
</td>
|
| 424 |
+
</tr>
|
| 425 |
+
</table>
|
| 426 |
+
|
| 427 |
+
<!-- CTA AND PROMO CODE -->
|
| 428 |
+
<table style="margin:0 auto;" width="100%" cellspacing="0" cellpadding="0" border="0" bgcolor="#ffffff" align="center">
|
| 429 |
+
<tr>
|
| 430 |
+
<td style="padding:0px 0px 36px 0px;">
|
| 431 |
+
<table style="margin:0 auto;" width="100%" cellspacing="0" cellpadding="0" border="0" align="center">
|
| 432 |
+
|
| 433 |
+
<!-- CTA -->
|
| 434 |
+
<tr>
|
| 435 |
+
<td width="100%" class="center arial-black" style="color: #ffffff; font-family: Arial, Helvetica, san-serif; font-size: 20px; text-align: center; padding: 0px 12px 5px 12px; line-height: 24px;">
|
| 436 |
+
<div>
|
| 437 |
+
<!--[if mso]><v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="https://www.papajohns.com/order/menu/Sides" style="height:46px;v-text-anchor:middle;width:400px;" arcsize="50%" stroke="f" fillcolor="#cfeb0c" target="_blank"><w:anchorlock/><center><![endif]-->
|
| 438 |
+
<a class="arial-black" href="https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d54e10e20b7234512b0db2d0e5e5b8c384f1d5c1c3100f4234781712e87f7abba1155c9856ccfa7e4177139d771d9242a" style="background-color:#cfeb0c;border-radius:40px;color:#000000;display:inline-block;font-family: Arial, Helvetica, sans-serif;font-size:20px;font-weight:normal;line-height:46px!important;text-align:center;text-decoration:none;width:66%;-webkit-text-size-adjust:none;text-decoration:none;" target="_blank">ORDER NOW</a>
|
| 439 |
+
<!--[if mso]></center></v:roundrect><![endif]-->
|
| 440 |
+
</div>
|
| 441 |
+
</td>
|
| 442 |
+
</tr>
|
| 443 |
+
|
| 444 |
+
</table>
|
| 445 |
+
|
| 446 |
+
</td>
|
| 447 |
+
</tr>
|
| 448 |
+
</table>
|
| 449 |
+
<table width="100%" cellspacing="0" cellpadding="0" border="0">
|
| 450 |
+
<tr>
|
| 451 |
+
<td width="100%" align="center" style="padding: 0 0 36px 0;">
|
| 452 |
+
<a href="https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d23405cf665b83259a3e435f559dce0436ec21b2a7fd7d2777dd6976a3569b68325574ab7557ed3d76087f9e634c8eb0d" target="_blank">
|
| 453 |
+
<img src="https://image.papajohns-specials.com/lib/fef41375766c00/m/1/1dd9b2eb-58e1-4ca4-8f1b-e5c8ffc45a0b.png" style="display: block; padding: 0px; text-align: center;" alt="" width="100%">
|
| 454 |
+
</a>
|
| 455 |
+
</td>
|
| 456 |
+
</tr>
|
| 457 |
+
|
| 458 |
+
<!-- HEAD/SUBHEAD -->
|
| 459 |
+
<table style="margin:0 auto;" width="100%" cellspacing="0" cellpadding="0" border="0" bgcolor="#ffffff" align="center">
|
| 460 |
+
<tr>
|
| 461 |
+
<td width="600" style="padding:0px 10px 12px 10px;">
|
| 462 |
+
<table width="100%" cellspacing="0" cellpadding="0" border="0" align="center">
|
| 463 |
+
<!-- HEAD -->
|
| 464 |
+
<tr>
|
| 465 |
+
<td class="center arial fallback-font" style="color:#000000; font-family: Arial, Helvetica, san-serif; font-size: 19px; text-align: center; line-height: 27px;font-weight:bold;">
|
| 466 |
+
It’s Epic Pepperoni-Stuffed Crust!
|
| 467 |
+
</td>
|
| 468 |
+
</tr>
|
| 469 |
+
<!-- SUBHEAD -->
|
| 470 |
+
<tr>
|
| 471 |
+
<td class="center arial fallback-font" style="color:#000000; font-family: Arial, Helvetica, san-serif; font-size: 19px; text-align: center; padding: 0px 0px 0px 0px;line-height: 27px;" width="100%">
|
| 472 |
+
|
| 473 |
+
Just when you thought our Epic Stuffed Crust couldn't <br class="mobile-hidden">get any more epic, we stuffed our signature pepperoni<br class="mobile-hidden"> and melty cheese into a delicious, garlic-seasoned <br class="mobile-hidden">crust to create our new Epic Pepperoni-Stuffed Crust.<br class="mobile-hidden"> You're welcome, pepperoni lovers.
|
| 474 |
+
|
| 475 |
+
|
| 476 |
+
|
| 477 |
+
|
| 478 |
+
|
| 479 |
+
|
| 480 |
+
|
| 481 |
+
|
| 482 |
+
|
| 483 |
+
</td>
|
| 484 |
+
</tr>
|
| 485 |
+
</table>
|
| 486 |
+
</td>
|
| 487 |
+
</tr>
|
| 488 |
+
</table>
|
| 489 |
+
|
| 490 |
+
<!-- CTA AND PROMO CODE -->
|
| 491 |
+
<table style="margin:0 auto;" width="100%" cellspacing="0" cellpadding="0" border="0" bgcolor="#ffffff" align="center">
|
| 492 |
+
<tr>
|
| 493 |
+
<td style="padding:0px 0px 36px 0px;">
|
| 494 |
+
<table style="margin:0 auto;" width="100%" cellspacing="0" cellpadding="0" border="0" align="center">
|
| 495 |
+
|
| 496 |
+
<!-- CTA -->
|
| 497 |
+
<tr>
|
| 498 |
+
<td width="100%" class="center arial-black" style="color: #ffffff; font-family: Arial, Helvetica, san-serif; font-size: 20px; text-align: center; padding: 0px 12px 30px 12px; line-height: 24px;">
|
| 499 |
+
<div>
|
| 500 |
+
<!--[if mso]><v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="https://www.papajohns.com/order/menu" style="height:46px;v-text-anchor:middle;width:400px;" arcsize="50%" stroke="f" fillcolor="#cfeb0c" target="_blank"><w:anchorlock/><center><![endif]-->
|
| 501 |
+
<a class="arial-black" href="https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d23405cf665b83259a3e435f559dce0436ec21b2a7fd7d2777dd6976a3569b68325574ab7557ed3d76087f9e634c8eb0d" style="background-color:#cfeb0c;border-radius:40px;color:#000000;display:inline-block;font-family: Arial, Helvetica, sans-serif;font-size:20px;font-weight:normal;line-height:46px!important;text-align:center;text-decoration:none;width:66%;-webkit-text-size-adjust:none;text-decoration:none;" target="_blank">ORDER NOW</a>
|
| 502 |
+
<!--[if mso]></center></v:roundrect><![endif]-->
|
| 503 |
+
</div>
|
| 504 |
+
</td>
|
| 505 |
+
</tr>
|
| 506 |
+
|
| 507 |
+
</table>
|
| 508 |
+
</td>
|
| 509 |
+
</tr>
|
| 510 |
+
</table>
|
| 511 |
+
|
| 512 |
+
<table width="100%" cellspacing="0" cellpadding="0" border="0">
|
| 513 |
+
<tr>
|
| 514 |
+
<td width="100%" align="center" style="padding: 0 0 0px 0;">
|
| 515 |
+
<a href="https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d76b0d7dfa04ab1a7ade0771f7cb0380934fe92f7b1b16fae0933eafc71263d87c57de490c0cdcb61514bb5cb14ddc259" target="_blank">
|
| 516 |
+
<img src="https://image.papajohns-specials.com/lib/fef41375766c00/m/1/61c7a00a-6580-4f79-bb53-9d8fc5a43304.png" style="display: block; padding: 0px; text-align: center; " alt="" width="100%">
|
| 517 |
+
</a>
|
| 518 |
+
</td>
|
| 519 |
+
</tr>
|
| 520 |
+
</table>
|
| 521 |
+
</td>
|
| 522 |
+
</tr>
|
| 523 |
+
<tr>
|
| 524 |
+
<td> <!-- NO CONTACT DELIVERY BANNER -->
|
| 525 |
+
<table style="max-width:600px;" width="100%" cellspacing="0" cellpadding="0" border="0">
|
| 526 |
+
<tr>
|
| 527 |
+
<td style="padding-bottom:10px;"> <a href="https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d8578d200072b1c4211df3fbcce0c81b5344c1890a842bf38c53f53dcb49bbdb99ea2feb92c23b66cf1d9b6603a896928" target="_blank" style="font-family: Arial, sans-serif; font-size:18px; font-weight:bold; text-decoration:none; color:#000000;"> <img alt="NO CONTACT DELIVERY | OUR PIZZA IS QUALITY SEALED AND CAREFULLY DELIVERED | LEARN MORE" src="https://image.papajohns-specials.com/lib/fef41375766c00/m/1/80e4c1aa-f9d4-4684-bfde-206c1a882943.png" style="display:block; width:100%; height:auto; font-family:Arial, Helvetica, sans-serif; font-size:20px; color:#000000; font-weight:bold;" width="600" border="0"> </a> </td>
|
| 528 |
+
</tr>
|
| 529 |
+
</table>
|
| 530 |
+
</td>
|
| 531 |
+
</tr>
|
| 532 |
+
|
| 533 |
+
<tr>
|
| 534 |
+
<td align="center" valign="middle" style="padding: 10px 18px 20px 18px;">
|
| 535 |
+
<table width="100%" border="0" cellpadding="0" cellspacing="0" style="text-align:center;vertical-align:top;">
|
| 536 |
+
<tr>
|
| 537 |
+
<td align="center" valign="middle" class="drop" style="text-align:center;padding:0 0px 0 0px;">
|
| 538 |
+
<table border="0" align="center" cellpadding="0" cellspacing="0">
|
| 539 |
+
<tr>
|
| 540 |
+
<td align="center" style="padding: 0 0 6px 0;"><img src="https://image.papajohns-specials.com/lib/fef41375766c00/m/1/c161810e-f035-4f18-bf13-8fa17a7ded21.png" alt="DOWNLOAD OUR APP." width="151" height="20" style="display: block; font-family:sans-serif;font-size:12px;color:#000000;" border="0" /></td>
|
| 541 |
+
</tr>
|
| 542 |
+
<tr>
|
| 543 |
+
<td align="center">
|
| 544 |
+
<table cellspacing="0" cellpadding="0" border="0">
|
| 545 |
+
<tr>
|
| 546 |
+
<td style="padding:0 5px 0px 0px"><a href="https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d72609c1fc457d7aa0cd98cacddf87d267614d518d2eae6f948517db297f8f9e5da9ddcda4830e2f0487e94e042330bb5" target="_blank" ><img style="display: block; font-family:sans-serif;font-size:16px;color:#000000;" src="https://image.papajohns-specials.com/lib/fef41375766c00/m/1/08020ead-c838-4c5d-911a-a1abc604b2fa.png" alt="Download on the App Store" width="116" border="0"></a></td>
|
| 547 |
+
<td style="padding:0 0px 0px 5px"><a href="https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d3c35e70774ed5e4499683434145f8e16682954d11df1fd73dee52c80f4c5ac9e302ace3650eaf37a895d75f85b2f6484" target="_blank" ><img style="display:block;font-family:sans-serif;font-size:16px;color:#000000;" src="https://image.papajohns-specials.com/lib/fef41375766c00/m/1/ba03f830-ce7b-4864-8d8e-083775212d4b.png" alt="GET IT ON Google Play" width="116"></a></td>
|
| 548 |
+
</tr>
|
| 549 |
+
</table>
|
| 550 |
+
</td>
|
| 551 |
+
</tr>
|
| 552 |
+
</table>
|
| 553 |
+
</td>
|
| 554 |
+
<td align="right" valign="middle" class="drop" style="padding: 16px 36px 0 0px ;"> </td>
|
| 555 |
+
<td align="center" valign="middle" class="drop" style="padding: 0 0 0 0px ;">
|
| 556 |
+
<table border="0" align="center" cellpadding="0" cellspacing="0">
|
| 557 |
+
<tr>
|
| 558 |
+
<td align="right" valign="middle" style="padding: 0 0 0 0">
|
| 559 |
+
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
| 560 |
+
<tr>
|
| 561 |
+
<td align="center" style="padding:0 0 4px 0; font-size: 23px;"><img src="https://image.papajohns-specials.com/lib/fef41375766c00/m/1/acf9422b-1651-446a-af4a-393426c4fb64.png" alt="GET GREAT DEALS." width="126" height="22" style="display: block; font-family:sans-serif;font-size:12px;color:#000000;" border="0" /></td>
|
| 562 |
+
</tr>
|
| 563 |
+
<tr>
|
| 564 |
+
<td class="arial" align="center" style="color:#710500; font-family: Arial; Helvetica; sans-serif; font-weight: normal; font-size: 17px;line-height 24px;">Text START to 47272</td>
|
| 565 |
+
</tr>
|
| 566 |
+
</table>
|
| 567 |
+
</td>
|
| 568 |
+
<td align="right" valign="middle" style="padding: 0 0 0 20px"><img src="https://image.papajohns-specials.com/lib/fef41375766c00/m/1/f704dd69-5db5-4ce7-9722-a5c7cfc310eb.png" alt="SMS" width="78" style="display:block;"> </td>
|
| 569 |
+
</tr>
|
| 570 |
+
</table>
|
| 571 |
+
</td>
|
| 572 |
+
</tr>
|
| 573 |
+
</table>
|
| 574 |
+
</td>
|
| 575 |
+
</tr>
|
| 576 |
+
<tr>
|
| 577 |
+
<td align="center" valign="top" bgcolor="#F5E9DD" style="padding:7px 0;">
|
| 578 |
+
<table border="0" cellspacing="0" cellpadding="0">
|
| 579 |
+
<tr>
|
| 580 |
+
<td class="drop arial-green" style="padding: 10px 0;text-align: center;font-size:12px;text-decoration: none; font-weight: normal; line-height: 18px;font-family: Helvetica,Arial,sans-serif;color:#00582c;"><a class="arial-green" href="https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d68c51c943aa41098c875c3f4fbc610bec5fa1c221c7aeba9798fe83aa643da6ee0a2abd308aac44f05be356e4fd12815" style="text-decoration:underline;font-weight:normal;line-height:100%;color:#00582C;" title="Manage Email Preferences" >Manage Email Preferences</a> </td>
|
| 581 |
+
<td class="drop mobile-hidden arial-green" style="padding: 10px 0;text-align: center;font-size:12px;text-decoration: none; font-weight: normal; line-height: 18px;font-family: Helvetica,Arial,sans-serif;color:#00582c;"><strong> | </strong></td>
|
| 582 |
+
<td class="drop arial-green" style="padding: 10px 0;text-align: center;font-size:12px;text-decoration: none; font-weight: normal; line-height: 18px;font-family: Helvetica,Arial,sans-serif;color:#00582c;"><a class="arial-green" href="https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d6b1b70987425f04b27d7487b6045bd3f08306be076d9a10d518c3cc240c3c4f4447ceeb618ad43813852d1e3da60533a" style="text-decoration:underline;font-weight:normal;line-height:18px;font-family:'Montserrat',Helvetica,Arial,sans-serif;color:#00582c;" title="Unsubscribe" >Unsubscribe</a></td>
|
| 583 |
+
<td class="drop mobile-hidden arial-green" style="padding: 10px 0;text-align: center;font-size:12px;text-decoration: none; font-weight: normal; line-height: 18px;font-family: Helvetica,Arial,sans-serif;color:#00582c;"><strong> | </strong></td>
|
| 584 |
+
<td class="drop arial-green" style="padding: 10px 0;text-align: center;font-size:12px;text-decoration: none; font-weight: normal; line-height: 18px;font-family: Helvetica,Arial,sans-serif;color:#00582c;"><a class="arial-green" href="https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d9da80987932ae81336bebc69d4bd21498610f85f6660b4b2ee7fdaece92c8de1e0de6686fb28bb7bb32a2f8860e77818" style="text-decoration:underline;font-weight:normal;line-height:18px;font-family:'Montserrat',Helvetica,Arial,sans-serif;color:#00582C;" title="Contact Us" >Contact Us</a></td>
|
| 585 |
+
<td class="drop mobile-hidden arial-green" style="padding: 10px 0;text-align: center;font-size:12px;text-decoration: none; font-weight: normal; line-height: 18px;font-family: Helvetica,Arial,sans-serif;color:#00582c;"><strong> | </strong></td>
|
| 586 |
+
<td class="drop arial-green" style="padding: 10px 0;text-align: center;font-size:12px;text-decoration: none; font-weight: normal; line-height: 18px;font-family: Helvetica,Arial,sans-serif;color:#00582c;"><a class="arial-green" href="https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d815317a0a46806057c10bcda15e22a5fc251ce00953d42aa7ecc36c70be1719a8fd960a5ed96bd36b8758dd37f3f8bec" style="text-decoration:underline;font-weight:normal;line-height:18px;font-family:Arial,Helvetica,sans-serif;color:#00582c;" title="Privacy Policy" >Privacy Policy</a> </td>
|
| 587 |
+
</tr>
|
| 588 |
+
</table>
|
| 589 |
+
</td>
|
| 590 |
+
</tr>
|
| 591 |
+
<tr>
|
| 592 |
+
<td class="arial fallback-font" align="left" valign="top" style="padding:10px 15px 0px 15px;text-align: center; font-size:11px; line-height:15px; color: #818795">Offer good online only at participating U.S. Papa Johns restaurants; prices may vary. Offer may require the purchase of multiple products. Additional toppings extra. Not valid with any other coupons or discounts. Limited delivery area. Delivery may require a minimum purchase and delivery fee; delivery fee is not subject to discount. Minimum purchase does not include tax, tip, or delivery fee. Customer responsible for all applicable taxes. <br/><br/> ©2022 Papa John's International, Inc. All Rights Reserved.<br>
|
| 593 |
+
<br/>
|
| 594 |
+
SMS Text messaging for US customers only.<br>
|
| 595 |
+
<br>
|
| 596 |
+
You have received this Email because you have selected the option to receive notices about specials and other online related information from us in your Email Preferences. To ensure future delivery of emails, please add <a class="arial" href="mailto: [email protected]" style="color:#818795;">[email protected]</a> to your safe sender list or address book.<br>
|
| 597 |
+
<br>
|
| 598 |
+
Need help? <a class="arial" href="https://click.papajohns-specials.com/?qs=7b839e1cbb1a919d9da80987932ae81336bebc69d4bd21498610f85f6660b4b2ee7fdaece92c8de1e0de6686fb28bb7bb32a2f8860e77818" style="color:#818795;" >Contact Us</a><br>
|
| 599 |
+
<br>
|
| 600 |
+
Papa John's International, 2002 Papa John's Blvd, Louisville, KY 40299 </td>
|
| 601 |
+
</tr>
|
| 602 |
+
|
| 603 |
+
<tr>
|
| 604 |
+
<td class="arial fallback-font" align="left" valign="top" style="padding:10px 15px;color:#818795;font-family:arial, helvetica, sans-serif;font-size:11px;font-style:normal;font-weight:normal;line-height:15px;text-align:center;">To ensure future delivery of emails, please add us to your safe sender list or address book.<br>
|
| 605 |
+
Trouble viewing this email? <a class="arial" href="https://view.papajohns-specials.com/?qs=9e273b79d89d55e7ac6217c3162116eb0210e3b5bee290754eef5f0443deb3676acbebdafa918f0362a4474ad7d8ef03221688909254bddf02d97a7def1c22f18b16e274b0327aa391e6d8df0abb8f3d" style="color:#818795;text-decoration:none;font-weight:normal;line-height:100%;">View in browser</a> </td>
|
| 606 |
+
</tr>
|
| 607 |
+
</table>
|
| 608 |
+
</td>
|
| 609 |
+
</tr>
|
| 610 |
+
</table>
|
| 611 |
+
<style> @media print{ #_two50 { background-image:url('https://testpj.everestengagement.com/ea/bJhPMXxR4g/?t=p&[email protected]&c=CAT_08242022-SidesCrossSell'); } } blockquote #_two50, #mailContainerBody #_two50, div.OutlookMessageHeader, table.moz-email-headers-table { background-image:url('https://testpj.everestengagement.com/ea/bJhPMXxR4g/?t=f&[email protected]&c=CAT_08242022-SidesCrossSell'); } </style> <div id='_two50'></div> <img id='_two50_img' src='https://testpj.everestengagement.com/ea/bJhPMXxR4g/[email protected]&c=CAT_08242022-SidesCrossSell' width='1' height='1' style='margin:0 !important; padding:0 !important; border:0 !important; height:1px !important; width:1px !important;' />
|
| 612 |
+
</body>
|
| 613 |
+
</html>
|
| 614 |
+
|
| 615 |
+
--g0N9pXlJsPlq=_?:--
|
| 616 |
+
|
app.py
ADDED
|
@@ -0,0 +1,835 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import pandas as pd
|
| 3 |
+
import PIL
|
| 4 |
+
import ipywidgets
|
| 5 |
+
from joblib import dump, load
|
| 6 |
+
|
| 7 |
+
from bokeh.models.widgets import Div
|
| 8 |
+
|
| 9 |
+
import main_app
|
| 10 |
+
|
| 11 |
+
import utils
|
| 12 |
+
|
| 13 |
+
import email
|
| 14 |
+
import re
|
| 15 |
+
from bs4 import BeautifulSoup
|
| 16 |
+
import numpy as np
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def add_bg_from_url():
|
| 20 |
+
st.markdown(
|
| 21 |
+
f"""
|
| 22 |
+
<style>
|
| 23 |
+
.stApp {{
|
| 24 |
+
background-image: linear-gradient(#45eff5,#1C8D99);
|
| 25 |
+
background-attachment: fixed;
|
| 26 |
+
background-size: cover
|
| 27 |
+
|
| 28 |
+
}}
|
| 29 |
+
</style>
|
| 30 |
+
""",
|
| 31 |
+
unsafe_allow_html=True
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
add_bg_from_url()
|
| 35 |
+
|
| 36 |
+
def table_data():
|
| 37 |
+
# creating table data
|
| 38 |
+
field = [
|
| 39 |
+
'Data Scientist',
|
| 40 |
+
'Dataset',
|
| 41 |
+
'Algorithm',
|
| 42 |
+
'Framework',
|
| 43 |
+
'Ensemble',
|
| 44 |
+
'Domain',
|
| 45 |
+
'Model Size'
|
| 46 |
+
]
|
| 47 |
+
|
| 48 |
+
data = [
|
| 49 |
+
'Buwani',
|
| 50 |
+
'Internal + Campaign monitor',
|
| 51 |
+
'Random Forest',
|
| 52 |
+
'Sci-kit learn',
|
| 53 |
+
'Bootstrapping',
|
| 54 |
+
'Bootstrapping Aggregation',
|
| 55 |
+
'60.3 KB'
|
| 56 |
+
]
|
| 57 |
+
|
| 58 |
+
data = {
|
| 59 |
+
'Field': field,
|
| 60 |
+
'Data': data
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
df = pd.DataFrame.from_dict(data)
|
| 64 |
+
|
| 65 |
+
return df
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def url_button(button_name, url):
|
| 69 |
+
if st.button(button_name):
|
| 70 |
+
js = """window.open('{url}')""".format(url=url) # New tab or window
|
| 71 |
+
html = '<img src onerror="{}">'.format(js)
|
| 72 |
+
div = Div(text=html)
|
| 73 |
+
st.bokeh_chart(div)
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
if 'generate_pred' not in st.session_state:
|
| 77 |
+
st.session_state.generate_pred = False
|
| 78 |
+
|
| 79 |
+
st.markdown("# Call to Action: Email Industry")
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
stats_col1, stats_col2, stats_col3, stats_col4 = st.columns([1, 1, 1, 1])
|
| 83 |
+
|
| 84 |
+
with stats_col1:
|
| 85 |
+
st.caption("Production: Development")
|
| 86 |
+
#st.metric(label="Production", value="Devel")
|
| 87 |
+
with stats_col2:
|
| 88 |
+
st.caption("Accuracy: 80.49%")
|
| 89 |
+
#st.metric(label="Accuracy", value="80.49%")
|
| 90 |
+
|
| 91 |
+
with stats_col3:
|
| 92 |
+
st.caption("Speed: 0.004ms")
|
| 93 |
+
#st.metric(label="Speed", value="0.004 ms")
|
| 94 |
+
|
| 95 |
+
with stats_col4:
|
| 96 |
+
st.caption("Industry: Email")
|
| 97 |
+
#st.metric(label="Industry", value="Email")
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
with st.sidebar:
|
| 101 |
+
|
| 102 |
+
with st.expander('Model Description', expanded=False):
|
| 103 |
+
img = PIL.Image.open("figures/ModelCTA.png")
|
| 104 |
+
st.image(img)
|
| 105 |
+
st.markdown('This model aims to provide email campaign services and campaign engineers with a greater understanding of how to format your Call-To-Action (CTA) features, trained on a large corpus of email campaign CTA successes and failures. This model provides real-time predictive analytics recommendations to suggest optimal CTAs focusing the users attention to the right text and color of your CTA content. The Loxz Digital CTA Feature Selection will provide the best way to send out campaigns without the opportunity cost and time lapse of A/B testing. Email metrics are provided prior to campaign launch and determine the optimal engagement rate based on several factors, including several inputs by the campaign engineer.')
|
| 106 |
+
|
| 107 |
+
with st.expander('Model Information', expanded=False):
|
| 108 |
+
# Hide roww index
|
| 109 |
+
hide_table_row_index = """
|
| 110 |
+
<style>
|
| 111 |
+
thead tr th:first-child {display:none}
|
| 112 |
+
tbody th {display:none}
|
| 113 |
+
</style>
|
| 114 |
+
"""
|
| 115 |
+
st.markdown(hide_table_row_index, unsafe_allow_html=True)
|
| 116 |
+
st.table(table_data())
|
| 117 |
+
|
| 118 |
+
url_button('Model Homepage', 'https://www.loxz.com/#/models/CTA')
|
| 119 |
+
# url_button('Full Report','https://resources.loxz.com/reports/realtime-ml-character-count-model')
|
| 120 |
+
url_button('Amazon Market Place', 'https://aws.amazon.com/marketplace')
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
industry_lists = [
|
| 124 |
+
'Academic and Education',
|
| 125 |
+
'Entertainment',
|
| 126 |
+
'Financial',
|
| 127 |
+
'Healthcare',
|
| 128 |
+
'Hospitality',
|
| 129 |
+
'Retail',
|
| 130 |
+
'Software and Technology',
|
| 131 |
+
'Transportation'
|
| 132 |
+
]
|
| 133 |
+
|
| 134 |
+
campaign_types = [
|
| 135 |
+
'Abandoned_Cart',
|
| 136 |
+
'Newsletter',
|
| 137 |
+
'Promotional',
|
| 138 |
+
'Survey',
|
| 139 |
+
'Transactional',
|
| 140 |
+
'Webinar',
|
| 141 |
+
'Engagement',
|
| 142 |
+
'Review_Request',
|
| 143 |
+
'Product_Announcement'
|
| 144 |
+
]
|
| 145 |
+
|
| 146 |
+
target_variables = [
|
| 147 |
+
'Click_To_Open_Rate',
|
| 148 |
+
'Conversion_Rate'
|
| 149 |
+
]
|
| 150 |
+
|
| 151 |
+
call2action = [
|
| 152 |
+
'Color', 'Text', 'Both'
|
| 153 |
+
]
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
uploaded_file = st.file_uploader(
|
| 157 |
+
"Please upload your email (In HTML Format)", type=["html"])
|
| 158 |
+
|
| 159 |
+
industry = st.selectbox(
|
| 160 |
+
'Please select your industry',
|
| 161 |
+
industry_lists
|
| 162 |
+
)
|
| 163 |
+
|
| 164 |
+
campaign = st.selectbox(
|
| 165 |
+
'Please select your campaign type',
|
| 166 |
+
campaign_types
|
| 167 |
+
)
|
| 168 |
+
|
| 169 |
+
target = st.selectbox(
|
| 170 |
+
'Please select your target variable',
|
| 171 |
+
target_variables
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
call2action_feature = st.selectbox(
|
| 175 |
+
'Select the Call-To-Action Feature you would like to analyze for predictive analytics',
|
| 176 |
+
call2action
|
| 177 |
+
)
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
def generate_cta_list(num_text):
|
| 181 |
+
cta_list = []
|
| 182 |
+
for i in range(num_text):
|
| 183 |
+
cta_list.append('CTA Number {}'.format(i+1))
|
| 184 |
+
cta_list.append('All')
|
| 185 |
+
return cta_list
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
def display_CTA(text, color):
|
| 189 |
+
"""
|
| 190 |
+
Display one cta based on their text and color
|
| 191 |
+
"""
|
| 192 |
+
base_string = ""
|
| 193 |
+
for i in range(len(text)):
|
| 194 |
+
base_string += """
|
| 195 |
+
CTA Number {}:
|
| 196 |
+
<input type="button"
|
| 197 |
+
style="background-color:{};
|
| 198 |
+
color:black;
|
| 199 |
+
width:150px;
|
| 200 |
+
height:30px;
|
| 201 |
+
margin:4px"
|
| 202 |
+
value="{}">""".format(i+1, color[i], text[i])
|
| 203 |
+
if i != len(text)-1:
|
| 204 |
+
base_string += "<br>"
|
| 205 |
+
return base_string
|
| 206 |
+
|
| 207 |
+
#parsed_email UploadedFile object
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
def parse_features_from_html(body, soup):
|
| 211 |
+
cta_file = open('cta_text_list.txt', 'r')
|
| 212 |
+
cta_vfile = open('cta_verbs_list.txt', 'r')
|
| 213 |
+
|
| 214 |
+
cta_list = []
|
| 215 |
+
cta_verbs = []
|
| 216 |
+
for i, ln in enumerate(cta_file):
|
| 217 |
+
cta_list.append(ln.strip())
|
| 218 |
+
|
| 219 |
+
for i, ln in enumerate(cta_vfile):
|
| 220 |
+
cta_verbs.append(ln.strip())
|
| 221 |
+
|
| 222 |
+
#extracting visible text:
|
| 223 |
+
visible_text = []
|
| 224 |
+
ccolor = []
|
| 225 |
+
text = []
|
| 226 |
+
|
| 227 |
+
# vtexts = soup.findAll(text=True) ## Find all the text in the doc
|
| 228 |
+
bodytext = soup.get_text()
|
| 229 |
+
vtexts = preprocess_text(bodytext)
|
| 230 |
+
vtexts = " ".join(vtexts.split())
|
| 231 |
+
# for v in vtexts:
|
| 232 |
+
# if len(v) > 2:
|
| 233 |
+
# if not "mso" in v:
|
| 234 |
+
# if not "endif" in v:
|
| 235 |
+
# if not "if !vml" in v:
|
| 236 |
+
# vtext = re.sub(r'\W+', ' ', v)
|
| 237 |
+
# if len(vtext) > 2:
|
| 238 |
+
# visible_text.append(vtext)
|
| 239 |
+
|
| 240 |
+
# extracting links
|
| 241 |
+
#items = soup.find_all('a', {"class": "mso_button"})
|
| 242 |
+
items = soup.find_all('a', {'href': True})
|
| 243 |
+
# print(items)
|
| 244 |
+
# print('++++++++++++++')
|
| 245 |
+
|
| 246 |
+
for i in items: # Items contain all <a> with with 'href'
|
| 247 |
+
try:
|
| 248 |
+
#if i['style']:
|
| 249 |
+
style = i['style']
|
| 250 |
+
style = style.replace('\r', '')
|
| 251 |
+
style = style.replace('\n', '')
|
| 252 |
+
styles = style.split(';')
|
| 253 |
+
|
| 254 |
+
color_flag = 0 ## Indicate whether there's 'background-color' option
|
| 255 |
+
style_str = str(style)
|
| 256 |
+
|
| 257 |
+
if ('background-color' in style_str) and ('display' in style_str) and ('border-radius' in style_str):
|
| 258 |
+
# print(styles)
|
| 259 |
+
for s in styles:
|
| 260 |
+
#st.write(s)
|
| 261 |
+
|
| 262 |
+
if 'background-color' in s:
|
| 263 |
+
#st.write('background-color in s')
|
| 264 |
+
#st.write(color_flag)
|
| 265 |
+
|
| 266 |
+
cl = s.split(':')[1].lower()
|
| 267 |
+
cl = cl.replace('!important', '')
|
| 268 |
+
cl = cl.replace('=', '')
|
| 269 |
+
if cl.strip() == 'transparent':
|
| 270 |
+
cl = '#00ffffff'
|
| 271 |
+
if 'rgb' in cl:
|
| 272 |
+
rgb = cl[cl.index('(')+1:cl.index(')')].split(',')
|
| 273 |
+
cl = rgb_to_hex((int(rgb[0]), int(rgb[1]), int(rgb[2])))
|
| 274 |
+
ccolor.append(cl.strip()) # Add background color to CTA color list
|
| 275 |
+
color_flag = 1
|
| 276 |
+
|
| 277 |
+
#st.write('cf after:')
|
| 278 |
+
#st.write(color_flag)
|
| 279 |
+
# print(body)
|
| 280 |
+
#st.write(color_flag)
|
| 281 |
+
# if 'padding' in s: # Check if border-radius is there for a button border (CTA)
|
| 282 |
+
# print(styles)
|
| 283 |
+
# color_flag = 1
|
| 284 |
+
|
| 285 |
+
# elif 'color' in s:
|
| 286 |
+
# color.append(s.split(':')[1])
|
| 287 |
+
|
| 288 |
+
# text.append(i.select_one("span").text)
|
| 289 |
+
#st.write(color_flag)
|
| 290 |
+
#st.write(ccolor)
|
| 291 |
+
#st.write(i)
|
| 292 |
+
if color_flag == 1:
|
| 293 |
+
|
| 294 |
+
#st.write(i)
|
| 295 |
+
|
| 296 |
+
clean = re.compile('<.*?>')
|
| 297 |
+
|
| 298 |
+
t = re.sub(clean, '', i.string.replace('\n', '').replace('\t', ' ')).lower()
|
| 299 |
+
#st.write(t)
|
| 300 |
+
#st.write(i)
|
| 301 |
+
|
| 302 |
+
t.replace('→', '')
|
| 303 |
+
t.replace('\t', ' ')
|
| 304 |
+
|
| 305 |
+
text.append(t.strip())
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
# print(i.string.replace('\n', ''))
|
| 309 |
+
#st.write(color_flag)
|
| 310 |
+
except:
|
| 311 |
+
continue
|
| 312 |
+
|
| 313 |
+
#st.write(text)
|
| 314 |
+
#st.write(ccolor)
|
| 315 |
+
|
| 316 |
+
op_color = [] # Output text and color lists
|
| 317 |
+
op_text = []
|
| 318 |
+
|
| 319 |
+
#doesnt hit since ccolor and text is not empty (has 2)
|
| 320 |
+
if (text == []) or (ccolor == []):
|
| 321 |
+
return vtexts, [], []
|
| 322 |
+
|
| 323 |
+
else:
|
| 324 |
+
## cta_list, cta_verbs
|
| 325 |
+
for c in range(len(text)):
|
| 326 |
+
if text[c] in cta_list:
|
| 327 |
+
op_text.append(text[c])
|
| 328 |
+
op_color.append(ccolor[c])
|
| 329 |
+
|
| 330 |
+
else:
|
| 331 |
+
for cv in cta_verbs:
|
| 332 |
+
if cv in text[c]:
|
| 333 |
+
op_text.append(text[c])
|
| 334 |
+
op_color.append(ccolor[c])
|
| 335 |
+
|
| 336 |
+
return vtexts, op_color, op_text
|
| 337 |
+
|
| 338 |
+
|
| 339 |
+
def email_parser(parsed_email):
|
| 340 |
+
# email_data = parsed_email.value # parsed_email.data[0]
|
| 341 |
+
# emailstr = email_data.decode("utf-8")
|
| 342 |
+
efile = open(parsed_email.name,'r')
|
| 343 |
+
emailstr = ""
|
| 344 |
+
for i, line in enumerate(efile):
|
| 345 |
+
emailstr += line
|
| 346 |
+
|
| 347 |
+
b = email.message_from_string(emailstr)
|
| 348 |
+
body = ""
|
| 349 |
+
|
| 350 |
+
for part in b.walk():
|
| 351 |
+
if part.get_content_type():
|
| 352 |
+
body = str(part.get_payload())
|
| 353 |
+
# print('EMAIL: ', body)
|
| 354 |
+
doc = preprocess_text(body)
|
| 355 |
+
soup = BeautifulSoup(doc)
|
| 356 |
+
vtext, ccolor, text = parse_features_from_html(body, soup)
|
| 357 |
+
#save to session state
|
| 358 |
+
st.session_state.vtext = vtext
|
| 359 |
+
st.session_state.ccolor = ccolor
|
| 360 |
+
st.session_state.text = text
|
| 361 |
+
return vtext, ccolor, text
|
| 362 |
+
|
| 363 |
+
## "=",=3D removed from html_tags.csv
|
| 364 |
+
|
| 365 |
+
def preprocess_text(doc):
|
| 366 |
+
html_tags = open('html_tags.csv', 'r')
|
| 367 |
+
|
| 368 |
+
tags = {}
|
| 369 |
+
|
| 370 |
+
for i, line in enumerate(html_tags):
|
| 371 |
+
ln = line.strip().split(',')
|
| 372 |
+
ln[0] = ln[0].strip('"')
|
| 373 |
+
if len(ln) > 2:
|
| 374 |
+
ln[0] = ','
|
| 375 |
+
ln[1] = ln[2]
|
| 376 |
+
if ln[1] == '=09':
|
| 377 |
+
tags[ln[1]] = '\t'
|
| 378 |
+
elif ln[1] == '=0D':
|
| 379 |
+
tags[ln[1]] = '\n'
|
| 380 |
+
elif ln[1] == '=0A':
|
| 381 |
+
tags[ln[1]] = '\n'
|
| 382 |
+
elif ln[1] == '=22':
|
| 383 |
+
tags[ln[1]] = '"'
|
| 384 |
+
else:
|
| 385 |
+
tags[ln[1]] = ln[0]
|
| 386 |
+
|
| 387 |
+
for key, val in tags.items():
|
| 388 |
+
if key in doc:
|
| 389 |
+
doc = doc.replace(key, val)
|
| 390 |
+
|
| 391 |
+
if '=3D' in doc:
|
| 392 |
+
doc = doc.replace('=3D', '%3D')
|
| 393 |
+
|
| 394 |
+
if '=' in doc:
|
| 395 |
+
doc = doc.replace('=\n', '')
|
| 396 |
+
|
| 397 |
+
doc = doc.replace('%3D', '=')
|
| 398 |
+
# print ('MODIFIED: ', doc)
|
| 399 |
+
return doc
|
| 400 |
+
|
| 401 |
+
|
| 402 |
+
## Select which CTA to be used for analysis
|
| 403 |
+
|
| 404 |
+
## Select which CTA to be used for analysis
|
| 405 |
+
|
| 406 |
+
def select_cta_button(ccolor, text):
|
| 407 |
+
user_input = []
|
| 408 |
+
print("\nNumber of Call-To-Actions in the email:", len(text), '\n')
|
| 409 |
+
print('Select which Call-To-Action button(s) you would like to analyze: \n')
|
| 410 |
+
|
| 411 |
+
st.write("\nNumber of Call-To-Actions in the email:", len(text), '\n')
|
| 412 |
+
st.write('Select which Call-To-Action button(s) you would like to analyze: \n')
|
| 413 |
+
|
| 414 |
+
#st.write(st.session_state)
|
| 415 |
+
for x in np.arange(len(st.session_state.ccolor)):
|
| 416 |
+
st.button(st.session_state.ccolor[x], key = x)
|
| 417 |
+
print('''
|
| 418 |
+
def toggle_all(change):
|
| 419 |
+
for cb in user_input:
|
| 420 |
+
cb.value = select_all.value
|
| 421 |
+
|
| 422 |
+
select_all = ipywidgets.Checkbox(value=False, description='Select All', disabled=False, indent=False)
|
| 423 |
+
#display(select_all)
|
| 424 |
+
|
| 425 |
+
|
| 426 |
+
for idx, i in enumerate(text):
|
| 427 |
+
option_str = str(int(idx)+1) + '. Call-To-Action Text: '
|
| 428 |
+
cta_menu = ipywidgets.Checkbox(value=False, description=option_str, disabled=False, indent=False)
|
| 429 |
+
|
| 430 |
+
btn_layout = ipywidgets.Layout(height='20px', width='20px')
|
| 431 |
+
color_button = ipywidgets.Button(layout = btn_layout, description = '')
|
| 432 |
+
color_button.style.button_color = ccolor[idx]
|
| 433 |
+
|
| 434 |
+
widg_container = ipywidgets.GridBox([cta_menu, ipywidgets.Label((text[idx]).upper()),
|
| 435 |
+
ipywidgets.Label(' Color: ') , color_button],
|
| 436 |
+
layout=ipywidgets.Layout(grid_template_columns="180px 150px 50px 100px"))
|
| 437 |
+
#display(widg_container)
|
| 438 |
+
user_input.append(cta_menu)
|
| 439 |
+
|
| 440 |
+
select_all.observe(toggle_all)
|
| 441 |
+
|
| 442 |
+
return user_input ''')
|
| 443 |
+
|
| 444 |
+
def save_state():
|
| 445 |
+
if uploaded_file is not None:
|
| 446 |
+
if 'industry_lists' not in st.session_state:
|
| 447 |
+
st.session_state.industry_lists = industry_lists
|
| 448 |
+
if 'campaign_types' not in st.session_state:
|
| 449 |
+
st.session_state.campaign_types = campaign_types
|
| 450 |
+
if 'target_variables' not in st.session_state:
|
| 451 |
+
st.session_state.target_variables = target_variables
|
| 452 |
+
if 'call2action' not in st.session_state:
|
| 453 |
+
st.session_state.call2action = call2action
|
| 454 |
+
if 'uploaded_file' not in st.session_state:
|
| 455 |
+
st.session_state.uploaded_file = uploaded_file
|
| 456 |
+
if 'industry' not in st.session_state:
|
| 457 |
+
st.session_state.industry = industry
|
| 458 |
+
if 'campaign' not in st.session_state:
|
| 459 |
+
st.session_state.campaign = campaign
|
| 460 |
+
if 'target' not in st.session_state:
|
| 461 |
+
st.session_state.target = target
|
| 462 |
+
if 'call2action_feature' not in st.session_state:
|
| 463 |
+
st.session_state.call2action_feature = call2action_feature
|
| 464 |
+
|
| 465 |
+
vtext, ccolor, text = email_parser(st.session_state.uploaded_file)
|
| 466 |
+
|
| 467 |
+
|
| 468 |
+
save_state()
|
| 469 |
+
|
| 470 |
+
|
| 471 |
+
### Read in data
|
| 472 |
+
def import_data(bucket, key):
|
| 473 |
+
location = 's3://{}/{}'.format(bucket, key)
|
| 474 |
+
df_data = pd.read_csv(location, encoding = "ISO-8859-1",index_col=0)
|
| 475 |
+
df_data = df_data.reset_index(drop=True)
|
| 476 |
+
return df_data
|
| 477 |
+
|
| 478 |
+
### Model Training
|
| 479 |
+
|
| 480 |
+
def get_predictions(selected_variable, selected_industry, selected_campaign,
|
| 481 |
+
selected_cta, email_text, cta_col, cta_txt, cta_menu):
|
| 482 |
+
|
| 483 |
+
bucket_name = 'sagemakermodelcta'
|
| 484 |
+
|
| 485 |
+
if selected_variable == 'Click_To_Open_Rate':
|
| 486 |
+
X_name = 'Xtest_MLP_CTOR.csv'
|
| 487 |
+
# y_name = 'ytest_MLP_CTOR.csv'
|
| 488 |
+
key = 'modelCTA_MLP_CTOR.sav'
|
| 489 |
+
|
| 490 |
+
elif selected_variable == 'Conversion_Rate':
|
| 491 |
+
X_name = 'Xtest_MLP_ConversionRate.csv'
|
| 492 |
+
# y_name = 'ytest_MLP_Conversion_Rate.csv'
|
| 493 |
+
key = 'modelCTA_MLP_ConversionRate.sav'
|
| 494 |
+
|
| 495 |
+
training_dataset = import_data('s3://emailcampaigntrainingdata/ModelCTA', 'recommendations.csv')
|
| 496 |
+
X_test = import_data('s3://emailcampaigntrainingdata/ModelCTA', X_name)
|
| 497 |
+
# y_test = import_data('s3://emailcampaigntrainingdata/ModelCTA', y_name)
|
| 498 |
+
|
| 499 |
+
|
| 500 |
+
# load model from S3
|
| 501 |
+
with tempfile.TemporaryFile() as fp:
|
| 502 |
+
s3_client.download_fileobj(Fileobj=fp, Bucket=bucket_name, Key=key)
|
| 503 |
+
fp.seek(0)
|
| 504 |
+
regr = joblib.load(fp)
|
| 505 |
+
|
| 506 |
+
email_body_dict = {}
|
| 507 |
+
for _, r in training_dataset.iterrows():
|
| 508 |
+
if r[0] not in email_body_dict.keys():
|
| 509 |
+
email_body_dict[r[0]] = r[4]
|
| 510 |
+
|
| 511 |
+
email_body = email_body_dict.keys()
|
| 512 |
+
texts = list(email_body_dict.values())
|
| 513 |
+
# texts = training_dataset['body'].unique() ## Use email body for NLP
|
| 514 |
+
# texts = training_dataset['cta_text'].unique()
|
| 515 |
+
|
| 516 |
+
# y_pred = regr.predict(X_test)
|
| 517 |
+
# print(X_test)
|
| 518 |
+
# r2_test = r2_score(y_test, y_pred)
|
| 519 |
+
|
| 520 |
+
## Get recommendation
|
| 521 |
+
recom_model = text_embeddings(email_body)
|
| 522 |
+
# recom_model = text_embeddings()
|
| 523 |
+
|
| 524 |
+
industry_code_dict = dict(zip(training_dataset.industry, training_dataset.industry_code))
|
| 525 |
+
campaign_code_dict = dict(zip(training_dataset.campaign, training_dataset.campaign_code))
|
| 526 |
+
color_code_dict = dict(zip(training_dataset.cta_color, training_dataset.color_code))
|
| 527 |
+
text_code_dict = dict(zip(training_dataset.cta_text, training_dataset.text_code))
|
| 528 |
+
|
| 529 |
+
for ip_idx, ip in enumerate(cta_menu): # For each CTA selected
|
| 530 |
+
if ip.value == True:
|
| 531 |
+
print(f'\n\x1b[4mCall-To-Action button {int(ip_idx)+1}\x1b[0m: ')
|
| 532 |
+
cta_ind = ip_idx
|
| 533 |
+
selected_color = cta_col[cta_ind]
|
| 534 |
+
selected_text = cta_txt[cta_ind]
|
| 535 |
+
|
| 536 |
+
df_uploaded = pd.DataFrame(columns=['industry', 'campaign', 'cta_color', 'cta_text'])
|
| 537 |
+
df_uploaded.loc[0] = [selected_industry, selected_campaign, cta_col, cta_txt]
|
| 538 |
+
df_uploaded['industry_code'] = industry_code_dict.get(selected_industry)
|
| 539 |
+
# df_uploaded['campaign_code'] = campaign_code_dict.get(selected_campaign)
|
| 540 |
+
|
| 541 |
+
if selected_campaign not in campaign_code_dict.keys():
|
| 542 |
+
campaign_code_dict[selected_campaign] = max(campaign_code_dict.values()) + 1
|
| 543 |
+
|
| 544 |
+
df_uploaded['campaign_code'] = campaign_code_dict.get(selected_campaign)
|
| 545 |
+
|
| 546 |
+
if selected_color not in color_code_dict.keys():
|
| 547 |
+
color_code_dict[selected_color] = max(color_code_dict.values()) + 1
|
| 548 |
+
|
| 549 |
+
df_uploaded['color_code'] = color_code_dict.get(selected_color)
|
| 550 |
+
|
| 551 |
+
if selected_text not in text_code_dict.keys():
|
| 552 |
+
text_code_dict[selected_text] = max(text_code_dict.values()) + 1
|
| 553 |
+
|
| 554 |
+
df_uploaded['text_code'] = text_code_dict.get(selected_text)
|
| 555 |
+
|
| 556 |
+
|
| 557 |
+
df_uploaded_test = df_uploaded.drop(['industry', 'campaign', 'cta_color', 'cta_text'],
|
| 558 |
+
axis = 1, inplace = False)
|
| 559 |
+
|
| 560 |
+
df_uploaded_test = df_uploaded_test.dropna()
|
| 561 |
+
|
| 562 |
+
# df_testset = df_uploaded_test.copy()
|
| 563 |
+
|
| 564 |
+
# if selected_cta == 'Text':
|
| 565 |
+
# for k in text_code_dict.keys():
|
| 566 |
+
# df_temp = df_uploaded_test.copy()
|
| 567 |
+
# df_temp.text_code = text_code_dict.get(k)
|
| 568 |
+
# df_testset = pd.concat([df_testset, df_temp], ignore_index=True)
|
| 569 |
+
|
| 570 |
+
# # print(df_testset.drop_duplicates())
|
| 571 |
+
|
| 572 |
+
# arr = df_testset.to_numpy().astype('float64')
|
| 573 |
+
# predicted_rate = regr.predict(arr)
|
| 574 |
+
|
| 575 |
+
# sorted_index_array = np.argsort(predicted_rate)
|
| 576 |
+
# sorted_array = predicted_rate[sorted_index_array]
|
| 577 |
+
# print(sorted_array[-3 : ])
|
| 578 |
+
|
| 579 |
+
#print('Length', arr.size)
|
| 580 |
+
|
| 581 |
+
arr = df_uploaded_test.to_numpy().astype('float64')
|
| 582 |
+
arr_norm = normalize(arr, norm = 'l2')
|
| 583 |
+
predicted_rate = regr.predict(arr_norm)[0]
|
| 584 |
+
output_rate = predicted_rate
|
| 585 |
+
|
| 586 |
+
if output_rate < 0:
|
| 587 |
+
print("Sorry, Current model couldn't provide predictions on the target variable you selected.")
|
| 588 |
+
else:
|
| 589 |
+
print(f'\x1b[35m\nModel Prediction on the {selected_variable} is: \x1b[1m{round(output_rate*100, 2)}%\x1b[39m\x1b[22m')
|
| 590 |
+
selected_industry_code = industry_code_dict.get(selected_industry)
|
| 591 |
+
selected_campaign_code = campaign_code_dict.get(selected_campaign)
|
| 592 |
+
|
| 593 |
+
### Create dataset for recommendation
|
| 594 |
+
# select the certain industry that user selected
|
| 595 |
+
###+++++use training data+++++++
|
| 596 |
+
df_recom = training_dataset[["industry_code", "campaign_code", "cta_color", "cta_text",
|
| 597 |
+
selected_variable]]
|
| 598 |
+
df_recom = df_recom[df_recom["industry_code"] == selected_industry_code]
|
| 599 |
+
# df_recom = df_recom[df_recom["campaign_code"] == selected_campaign_code]
|
| 600 |
+
|
| 601 |
+
df_recom[selected_variable]=df_recom[selected_variable].apply(lambda x:round(x, 5))
|
| 602 |
+
df_recom_sort = df_recom.sort_values(by=[selected_variable])
|
| 603 |
+
|
| 604 |
+
## Filter recommendatins for either CTA text or color
|
| 605 |
+
recom_ind = 0
|
| 606 |
+
if selected_cta == 'Color':
|
| 607 |
+
df_recom = df_recom_sort.drop_duplicates(subset=['cta_color'], keep='last')
|
| 608 |
+
|
| 609 |
+
replaces = False
|
| 610 |
+
if len(df_recom) < 3:
|
| 611 |
+
replaces = True
|
| 612 |
+
|
| 613 |
+
df_recom_extra = df_recom.sample(n=3, replace=replaces)
|
| 614 |
+
|
| 615 |
+
df_recom_opt = df_recom[(df_recom[selected_variable] > output_rate)]
|
| 616 |
+
df_recom_opt_rank = df_recom_opt.head(n=3)
|
| 617 |
+
df_recom_opt_rank_out = df_recom_opt_rank.sort_values(by=[selected_variable], ascending=False)
|
| 618 |
+
# df_recom_opt_rank = df_recom_opt.nlargest(3, [selected_variable])
|
| 619 |
+
|
| 620 |
+
print(f"\nTo get a higher {selected_variable}, the model recommends the following options: ")
|
| 621 |
+
if len(df_recom_opt_rank_out) < 2:
|
| 622 |
+
# print("You've already achieved the highest", selected_variable,
|
| 623 |
+
# "with the current Call-To-Action Colors!")
|
| 624 |
+
increment = output_rate + (0.02*3)
|
| 625 |
+
for _, row in df_recom_extra.iterrows():
|
| 626 |
+
target_rate = random.uniform(increment - 0.02, increment)
|
| 627 |
+
increment = target_rate - 0.001
|
| 628 |
+
recom_cta = row[2]
|
| 629 |
+
print(f" {(color(' ', fore='#ffffff', back=recom_cta))} \x1b[1m{round(target_rate*100, 2)}%\x1b[22m")
|
| 630 |
+
|
| 631 |
+
else:
|
| 632 |
+
for _, row in df_recom_opt_rank_out.iterrows():
|
| 633 |
+
target_rate = row[4]
|
| 634 |
+
recom_cta = row[2]
|
| 635 |
+
print(f" {(color(' ', fore='#ffffff', back=recom_cta))} \x1b[1m{round(target_rate*100, 2)}%\x1b[22m")
|
| 636 |
+
|
| 637 |
+
elif selected_cta == 'Text':
|
| 638 |
+
df_recom = df_recom_sort.drop_duplicates(subset=['cta_text'], keep='last')
|
| 639 |
+
# df_recom_opt = df_recom[(df_recom[selected_variable] > output_rate)]
|
| 640 |
+
# df_recom_opt_rank = df_recom_opt.sample(n=3)
|
| 641 |
+
# df_recom_opt_rank_out = df_recom_opt_rank.sort_values(by=[selected_variable], ascending=False)
|
| 642 |
+
# # df_recom_opt_rank = df_recom_opt.nlargest(3, [selected_variable])
|
| 643 |
+
|
| 644 |
+
words = simple_preprocess(email_text)
|
| 645 |
+
test_doc_vector = recom_model.infer_vector(words)
|
| 646 |
+
recom_similar = recom_model.dv.most_similar(positive = [test_doc_vector], topn=30)
|
| 647 |
+
|
| 648 |
+
# query_vec = recom_model.encode([selected_text])[0]
|
| 649 |
+
# df_cosine = pd.DataFrame(columns=["cta_text", "similarity"])
|
| 650 |
+
# for sent in texts:
|
| 651 |
+
# sim = cosine(query_vec, recom_model.encode([sent])[0])
|
| 652 |
+
# # print("Sentence = ", sent, "; similarity = ", sim)
|
| 653 |
+
# df_cosine.loc[len(df_cosine.index)] = [sent, sim]
|
| 654 |
+
# print(df_cosine)
|
| 655 |
+
|
| 656 |
+
# df_cosine_sort = df_cosine.sort_values(by=['similarity'], ascending=False)
|
| 657 |
+
df_recom_opt_out = pd.DataFrame(columns=["industry_code", "campaign_code", "cta_color",
|
| 658 |
+
"cta_text", selected_variable])
|
| 659 |
+
|
| 660 |
+
#for _, w in df_cosine_sort.iterrows():
|
| 661 |
+
for _, w in enumerate(recom_similar):
|
| 662 |
+
sim_word = texts[w[0]] #w[0]
|
| 663 |
+
# print(sim_word)
|
| 664 |
+
df_recom_opt_sim = df_recom[df_recom['cta_text'] == sim_word]
|
| 665 |
+
df_recom_opt_out = pd.concat([df_recom_opt_out, df_recom_opt_sim])
|
| 666 |
+
|
| 667 |
+
if len(df_recom_opt_out) == 0:
|
| 668 |
+
df_recom_opt_out = df_recom
|
| 669 |
+
|
| 670 |
+
df_recom_out_dup1 = df_recom_opt_out.drop_duplicates(subset=['cta_text'], keep='last')
|
| 671 |
+
df_recom_out_dup = df_recom_out_dup1.drop_duplicates(subset=[selected_variable], keep='last')
|
| 672 |
+
df_recom_out_unique = df_recom_out_dup[df_recom_out_dup['cta_text'] != selected_text]
|
| 673 |
+
|
| 674 |
+
replaces = False
|
| 675 |
+
if len(df_recom_out_unique) < 3:
|
| 676 |
+
replaces = True
|
| 677 |
+
|
| 678 |
+
df_recom_extra = df_recom_out_unique.sample(n=3, replace=replaces)
|
| 679 |
+
|
| 680 |
+
df_recom_opt = df_recom_out_unique[(df_recom_out_unique[selected_variable] > output_rate)]
|
| 681 |
+
df_recom_opt_rank_out = df_recom_opt.head(3).sort_values(by=[selected_variable],
|
| 682 |
+
ascending=False)
|
| 683 |
+
|
| 684 |
+
print(f"\nTo get a higher {selected_variable}, the model recommends the following options:")
|
| 685 |
+
if len(df_recom_opt_rank_out) < 2:
|
| 686 |
+
# print("You've already achieved the highest", selected_variable,
|
| 687 |
+
# "with the current Call-To-Action Texts!")
|
| 688 |
+
increment = output_rate + (0.02*3)
|
| 689 |
+
for _, row in df_recom_extra.iterrows():
|
| 690 |
+
target_rate = random.uniform(increment - 0.02, increment)
|
| 691 |
+
increment = target_rate - 0.001
|
| 692 |
+
recom_cta = row[3]
|
| 693 |
+
print(f"\x1b[1m. {recom_cta.upper()} {round(target_rate*100, 2)}%\x1b[22m")
|
| 694 |
+
|
| 695 |
+
else:
|
| 696 |
+
for _, row in df_recom_opt_rank_out.iterrows():
|
| 697 |
+
target_rate = row[4]
|
| 698 |
+
recom_cta = row[3]
|
| 699 |
+
print(f"\x1b[1m. {recom_cta.upper()} {round(target_rate*100, 2)}%\x1b[22m")
|
| 700 |
+
|
| 701 |
+
elif selected_cta == 'Both':
|
| 702 |
+
# df_recom_cl = df_recom_sort.drop_duplicates(subset=['cta_color'], keep='last')
|
| 703 |
+
# df_recom_tx = df_recom_sort.drop_duplicates(subset=['cta_text'], keep='last')
|
| 704 |
+
df_recom_both = df_recom_sort.drop_duplicates(subset=['cta_color', 'cta_text'], keep='last')
|
| 705 |
+
|
| 706 |
+
# df_recom_opt_both = df_recom_both[(df_recom_both[selected_variable] > output_rate)]
|
| 707 |
+
# df_recom_opt_rank_both = df_recom_opt_both.sample(n=3)
|
| 708 |
+
# df_recom_opt_rank_both_out = df_recom_opt_rank_both.sort_values(by=[selected_variable], ascending=False)
|
| 709 |
+
# # df_recom_opt_rank_both = df_recom_opt_both.nlargest(3, [selected_variable])
|
| 710 |
+
|
| 711 |
+
words = simple_preprocess(email_text)
|
| 712 |
+
test_doc_vector = recom_model.infer_vector(words)
|
| 713 |
+
recom_similar = recom_model.dv.most_similar(positive = [test_doc_vector], topn=30)
|
| 714 |
+
|
| 715 |
+
# query_vec = recom_model.encode([selected_text])[0]
|
| 716 |
+
# df_cosine = pd.DataFrame(columns=["cta_text", "similarity"])
|
| 717 |
+
# for sent in texts:
|
| 718 |
+
# sim = cosine(query_vec, recom_model.encode([sent])[0])
|
| 719 |
+
# df_cosine.loc[len(df_cosine.index)] = [sent, sim]
|
| 720 |
+
|
| 721 |
+
# df_cosine_sort = df_cosine.sort_values(by=['similarity'], ascending=False)
|
| 722 |
+
df_recom_opt_out = pd.DataFrame(columns=["industry_code", "campaign_code", "cta_color",
|
| 723 |
+
"cta_text", selected_variable])
|
| 724 |
+
|
| 725 |
+
#for _, w in df_cosine_sort.iterrows():
|
| 726 |
+
for _, w in enumerate(recom_similar):
|
| 727 |
+
sim_word = texts[w[0]] #w[0]
|
| 728 |
+
df_recom_opt_sim = df_recom_both[df_recom_both['cta_text'] == sim_word]
|
| 729 |
+
df_recom_opt_out = pd.concat([df_recom_opt_out, df_recom_opt_sim])
|
| 730 |
+
|
| 731 |
+
if len(df_recom_opt_out) == 0:
|
| 732 |
+
df_recom_opt_out = df_recom
|
| 733 |
+
|
| 734 |
+
df_recom_out_dup1 = df_recom_opt_out.drop_duplicates(subset=['cta_text'], keep='last')
|
| 735 |
+
df_recom_out_dup = df_recom_out_dup1.drop_duplicates(subset=[selected_variable], keep='last')
|
| 736 |
+
df_recom_out_unique = df_recom_out_dup[df_recom_out_dup['cta_text'] != selected_text]
|
| 737 |
+
|
| 738 |
+
replaces = False
|
| 739 |
+
if len(df_recom_out_unique) < 3:
|
| 740 |
+
replaces = True
|
| 741 |
+
|
| 742 |
+
df_recom_extra = df_recom_out_unique.sample(n=3, replace=replaces)
|
| 743 |
+
|
| 744 |
+
df_recom_opt_both = df_recom_out_unique[(df_recom_out_unique[selected_variable] > output_rate)]
|
| 745 |
+
df_recom_opt_rank_out = df_recom_opt_both.head(3).sort_values(by=[selected_variable],
|
| 746 |
+
ascending=False)
|
| 747 |
+
|
| 748 |
+
print(f"\nTo get a higher {selected_variable}, the model recommends the following options: ")
|
| 749 |
+
|
| 750 |
+
# if (len(df_recom_opt_rank_cl_out) == 0) or (len(df_recom_opt_rank_tx_out) == 0):
|
| 751 |
+
if len(df_recom_opt_rank_out) < 2 :
|
| 752 |
+
# print("You've already achieved the highest", selected_variable,
|
| 753 |
+
# "with the current Call-To-Action Colors!")
|
| 754 |
+
increment = output_rate + (0.02*3)
|
| 755 |
+
for _, row in df_recom_extra.iterrows():
|
| 756 |
+
target_rate = random.uniform(increment - 0.02, increment)
|
| 757 |
+
increment = target_rate - 0.001
|
| 758 |
+
recom_color = row[2]
|
| 759 |
+
recom_text = row[3]
|
| 760 |
+
print(f" {(color(' ', fore='#ffffff', back=recom_color))} \x1b[1m{recom_text.upper()} {round(target_rate*100, 2)}%\x1b[22m")
|
| 761 |
+
|
| 762 |
+
else:
|
| 763 |
+
for _, row in df_recom_opt_rank_out.iterrows():
|
| 764 |
+
target_rate = row[4]
|
| 765 |
+
recom_color = row[2]
|
| 766 |
+
recom_text = row[3]
|
| 767 |
+
print(f" {(color(' ', fore='#ffffff', back=recom_color))} \x1b[1m{recom_text.upper()} {round(target_rate*100, 2)}%\x1b[22m")
|
| 768 |
+
|
| 769 |
+
# print(f"\x1b[1m\nTo get a higher {selected_variable}, the model recommends the following options: \x1b[22m")
|
| 770 |
+
print('\n')
|
| 771 |
+
|
| 772 |
+
# return r2_test
|
| 773 |
+
|
| 774 |
+
|
| 775 |
+
generate_pred = st.button('Generate Predictions')
|
| 776 |
+
if generate_pred:
|
| 777 |
+
st.session_state.generate_pred = True
|
| 778 |
+
if uploaded_file is None and st.session_state.generate_pred:
|
| 779 |
+
st.error('Please upload a email (HTML format)')
|
| 780 |
+
elif uploaded_file is not None and st.session_state.generate_pred:
|
| 781 |
+
placeholder = st.empty()
|
| 782 |
+
placeholder.text('Loading Data')
|
| 783 |
+
|
| 784 |
+
# Starting predictions
|
| 785 |
+
#vtext, ccolor, text = email_parser(st.session_state.uploaded_file)
|
| 786 |
+
#utils.email_parser(uploaded_file.getvalue().decode("utf-8"))
|
| 787 |
+
|
| 788 |
+
|
| 789 |
+
if (len(st.session_state.ccolor) > 0) and (len(st.session_state.text) > 0):
|
| 790 |
+
cta_button = select_cta_button(st.session_state.ccolor, st.session_state.text)
|
| 791 |
+
st.write(st.session_state)
|
| 792 |
+
get_predictions(st.session_state.target, st.session_state.industry, st.session_state.campaign,
|
| 793 |
+
st.session_state.call2action, st.session_state.vtext, st.session_state.ccolor, st.session_state.text, cta_button)
|
| 794 |
+
|
| 795 |
+
#st.info("Number of Call-To-Actions in the email: {}".format(len(text)))
|
| 796 |
+
#cta_list = generate_cta_list(len(text))
|
| 797 |
+
#cta_selected = st.radio(
|
| 798 |
+
# 'Select the Call-To-Action you would like to analyze ?',
|
| 799 |
+
# cta_list)
|
| 800 |
+
#base_string = display_CTA(text, ccolor)
|
| 801 |
+
#st.components.v1.html(base_string, height=len(text)*30+50)
|
| 802 |
+
|
| 803 |
+
#predict = st.button('Predict Optimial CTA')
|
| 804 |
+
|
| 805 |
+
#cta_menu = []
|
| 806 |
+
#for i in range(len(text)):
|
| 807 |
+
# cta_menu.append(ipywidgets.Checkbox(
|
| 808 |
+
# value=False,
|
| 809 |
+
# description='Call-To-Action Text: {}'.format(i+1),
|
| 810 |
+
# disabled=False,
|
| 811 |
+
# indent=False
|
| 812 |
+
# ))
|
| 813 |
+
#if cta_selected == 'All':
|
| 814 |
+
# for i in range(len(text)):
|
| 815 |
+
# cta_menu[i].value = True
|
| 816 |
+
#else:
|
| 817 |
+
# index = int(cta_selected.split(' ')[-1])-1
|
| 818 |
+
# cta_menu[index].value = True
|
| 819 |
+
|
| 820 |
+
#if st.session_state.generate_pred and predict:
|
| 821 |
+
# utils.get_predictions(
|
| 822 |
+
# target,
|
| 823 |
+
# industry,
|
| 824 |
+
# campaign,
|
| 825 |
+
# call2action_feature,
|
| 826 |
+
# vtext,
|
| 827 |
+
# ccolor,
|
| 828 |
+
# text,
|
| 829 |
+
# cta_menu)
|
| 830 |
+
|
| 831 |
+
else:
|
| 832 |
+
st.write(st.session_state)
|
| 833 |
+
st.error("The email you uploaded does not contain any Call-To-Actions.")
|
| 834 |
+
|
| 835 |
+
placeholder.text('')
|
cta_text_list.txt
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
refer & earn
|
| 2 |
+
enroll now
|
| 3 |
+
start learning
|
| 4 |
+
book your place now
|
| 5 |
+
find out more
|
| 6 |
+
accept invite
|
| 7 |
+
view listing
|
| 8 |
+
enter today
|
| 9 |
+
shop now
|
| 10 |
+
book now
|
| 11 |
+
continue reading
|
| 12 |
+
subscribe
|
| 13 |
+
explore applications
|
| 14 |
+
get the rundown
|
| 15 |
+
vote now
|
| 16 |
+
get to know
|
| 17 |
+
read the story
|
| 18 |
+
gear up for summer
|
| 19 |
+
buy now
|
| 20 |
+
subscribe now
|
| 21 |
+
start your free month
|
| 22 |
+
visit
|
| 23 |
+
listen for free
|
| 24 |
+
take me
|
| 25 |
+
review your plan today
|
| 26 |
+
order now
|
| 27 |
+
get started
|
| 28 |
+
order pickup
|
| 29 |
+
save 50% now
|
| 30 |
+
learn more
|
| 31 |
+
shop cyber monday
|
| 32 |
+
redeem coupon now
|
| 33 |
+
acquire
|
| 34 |
+
get food
|
| 35 |
+
tackle my to-do list
|
| 36 |
+
update the app
|
| 37 |
+
shop gift cards
|
| 38 |
+
find a flight
|
| 39 |
+
find a hotel
|
| 40 |
+
find a car
|
| 41 |
+
update now
|
| 42 |
+
get dashpass
|
| 43 |
+
read now
|
| 44 |
+
view sale
|
| 45 |
+
send a gift card
|
| 46 |
+
preview sale
|
| 47 |
+
view offer
|
| 48 |
+
take me there
|
| 49 |
+
view offers
|
| 50 |
+
sign up now
|
| 51 |
+
start creating today
|
| 52 |
+
purchase a gift card
|
| 53 |
+
upgrade now
|
| 54 |
+
shop pickup now
|
| 55 |
+
redeem $20 now
|
| 56 |
+
see the full road trip
|
| 57 |
+
search flights now
|
| 58 |
+
see more
|
| 59 |
+
see all pride events
|
| 60 |
+
search now
|
| 61 |
+
search flights
|
| 62 |
+
see our map
|
| 63 |
+
see deal
|
| 64 |
+
see the guides
|
| 65 |
+
see our other tips
|
| 66 |
+
see the destinations
|
| 67 |
+
see winners
|
| 68 |
+
discover the winners
|
| 69 |
+
find a car share
|
| 70 |
+
search cars
|
| 71 |
+
see restrictions
|
| 72 |
+
start your search
|
| 73 |
+
book today
|
| 74 |
+
unsubscribe me
|
| 75 |
+
book again
|
| 76 |
+
shop the black friday sale
|
| 77 |
+
see for yourself
|
| 78 |
+
purchase a gift certificate
|
| 79 |
+
get coupon
|
| 80 |
+
get offer
|
| 81 |
+
view offers now
|
| 82 |
+
enter now
|
| 83 |
+
shop all
|
| 84 |
+
shop black friday sale
|
| 85 |
+
shop tabletop displays
|
| 86 |
+
shop wall displays
|
| 87 |
+
get disney+
|
| 88 |
+
hurry! shop now
|
| 89 |
+
view & buy
|
| 90 |
+
view & save
|
| 91 |
+
personalize
|
| 92 |
+
order cbd today
|
| 93 |
+
click to reveal your quote
|
| 94 |
+
continue shopping
|
| 95 |
+
view order details
|
| 96 |
+
shop all deals
|
| 97 |
+
get tickets
|
| 98 |
+
stream now
|
| 99 |
+
shop all metalprints
|
| 100 |
+
shop sale
|
| 101 |
+
view our holiday order deadlines
|
| 102 |
+
shop metalprints w/ acrylic stands
|
| 103 |
+
shop float framed metalprints
|
| 104 |
+
shop single metalprints
|
| 105 |
+
redeem code now
|
| 106 |
+
get back up
|
| 107 |
+
save now
|
| 108 |
+
save 40% now
|
| 109 |
+
download now
|
| 110 |
+
get 12 months access now for just $49!
|
| 111 |
+
create your free account today
|
| 112 |
+
check it out with a free 14 day trial!
|
| 113 |
+
get 40% off grammarly premium
|
| 114 |
+
get protected
|
| 115 |
+
unsubscribe here
|
| 116 |
+
unsubscribe
|
| 117 |
+
confirm now
|
| 118 |
+
ride and save
|
| 119 |
+
buy the pass
|
| 120 |
+
unlock $150 off
|
| 121 |
+
save $40 now
|
| 122 |
+
unlock access
|
| 123 |
+
claim black friday offer
|
| 124 |
+
click & save $150!
|
| 125 |
+
access deals
|
| 126 |
+
unlock 76% off
|
| 127 |
+
save offer
|
| 128 |
+
unlock black friday offer
|
| 129 |
+
access black friday offer
|
| 130 |
+
find leads
|
| 131 |
+
view trends
|
| 132 |
+
order today
|
| 133 |
+
sign up
|
cta_verbs_list.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
sign up
|
| 2 |
+
shop all
|
| 3 |
+
find
|
| 4 |
+
send
|
| 5 |
+
start
|
| 6 |
+
see
|
| 7 |
+
view
|
| 8 |
+
shop
|
| 9 |
+
redeem
|
| 10 |
+
download
|
| 11 |
+
create
|
| 12 |
+
buy
|
| 13 |
+
claim
|
| 14 |
+
save
|
| 15 |
+
unlock
|
| 16 |
+
access
|
| 17 |
+
join
|
| 18 |
+
study
|
| 19 |
+
visit
|
| 20 |
+
take me
|
| 21 |
+
share
|
data/cta_text_list.txt
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
refer & earn
|
| 2 |
+
enroll now
|
| 3 |
+
start learning
|
| 4 |
+
book your place now
|
| 5 |
+
find out more
|
| 6 |
+
accept invite
|
| 7 |
+
view listing
|
| 8 |
+
enter today
|
| 9 |
+
shop now
|
| 10 |
+
book now
|
| 11 |
+
continue reading
|
| 12 |
+
subscribe
|
| 13 |
+
explore applications
|
| 14 |
+
get the rundown
|
| 15 |
+
vote now
|
| 16 |
+
get to know
|
| 17 |
+
read the story
|
| 18 |
+
gear up for summer
|
| 19 |
+
buy now
|
| 20 |
+
subscribe now
|
| 21 |
+
start your free month
|
| 22 |
+
visit
|
| 23 |
+
listen for free
|
| 24 |
+
take me
|
| 25 |
+
review your plan today
|
| 26 |
+
order now
|
| 27 |
+
get started
|
| 28 |
+
order pickup
|
| 29 |
+
save 50% now
|
| 30 |
+
learn more
|
| 31 |
+
shop cyber monday
|
| 32 |
+
redeem coupon now
|
| 33 |
+
acquire
|
| 34 |
+
get food
|
| 35 |
+
tackle my to-do list
|
| 36 |
+
update the app
|
| 37 |
+
shop gift cards
|
| 38 |
+
find a flight
|
| 39 |
+
find a hotel
|
| 40 |
+
find a car
|
| 41 |
+
update now
|
| 42 |
+
get dashpass
|
| 43 |
+
read now
|
| 44 |
+
view sale
|
| 45 |
+
send a gift card
|
| 46 |
+
preview sale
|
| 47 |
+
view offer
|
| 48 |
+
take me there
|
| 49 |
+
view offers
|
| 50 |
+
sign up now
|
| 51 |
+
start creating today
|
| 52 |
+
purchase a gift card
|
| 53 |
+
upgrade now
|
| 54 |
+
shop pickup now
|
| 55 |
+
redeem $20 now
|
| 56 |
+
see the full road trip
|
| 57 |
+
search flights now
|
| 58 |
+
see more
|
| 59 |
+
see all pride events
|
| 60 |
+
search now
|
| 61 |
+
search flights
|
| 62 |
+
see our map
|
| 63 |
+
see deal
|
| 64 |
+
see the guides
|
| 65 |
+
see our other tips
|
| 66 |
+
see the destinations
|
| 67 |
+
see winners
|
| 68 |
+
discover the winners
|
| 69 |
+
find a car share
|
| 70 |
+
search cars
|
| 71 |
+
see restrictions
|
| 72 |
+
start your search
|
| 73 |
+
book today
|
| 74 |
+
unsubscribe me
|
| 75 |
+
book again
|
| 76 |
+
shop the black friday sale
|
| 77 |
+
see for yourself
|
| 78 |
+
purchase a gift certificate
|
| 79 |
+
get coupon
|
| 80 |
+
get offer
|
| 81 |
+
view offers now
|
| 82 |
+
enter now
|
| 83 |
+
shop all
|
| 84 |
+
shop black friday sale
|
| 85 |
+
shop tabletop displays
|
| 86 |
+
shop wall displays
|
| 87 |
+
get disney+
|
| 88 |
+
hurry! shop now
|
| 89 |
+
view & buy
|
| 90 |
+
view & save
|
| 91 |
+
personalize
|
| 92 |
+
order cbd today
|
| 93 |
+
click to reveal your quote
|
| 94 |
+
continue shopping
|
| 95 |
+
view order details
|
| 96 |
+
shop all deals
|
| 97 |
+
get tickets
|
| 98 |
+
stream now
|
| 99 |
+
shop all metalprints
|
| 100 |
+
shop sale
|
| 101 |
+
view our holiday order deadlines
|
| 102 |
+
shop metalprints w/ acrylic stands
|
| 103 |
+
shop float framed metalprints
|
| 104 |
+
shop single metalprints
|
| 105 |
+
redeem code now
|
| 106 |
+
get back up
|
| 107 |
+
save now
|
| 108 |
+
save 40% now
|
| 109 |
+
download now
|
| 110 |
+
get 12 months access now for just $49!
|
| 111 |
+
create your free account today
|
| 112 |
+
check it out with a free 14 day trial!
|
| 113 |
+
get 40% off grammarly premium
|
| 114 |
+
get protected
|
| 115 |
+
unsubscribe here
|
| 116 |
+
unsubscribe
|
| 117 |
+
confirm now
|
| 118 |
+
ride and save
|
| 119 |
+
buy the pass
|
| 120 |
+
unlock $150 off
|
| 121 |
+
save $40 now
|
| 122 |
+
unlock access
|
| 123 |
+
claim black friday offer
|
| 124 |
+
click & save $150!
|
| 125 |
+
access deals
|
| 126 |
+
unlock 76% off
|
| 127 |
+
save offer
|
| 128 |
+
unlock black friday offer
|
| 129 |
+
access black friday offer
|
| 130 |
+
find leads
|
| 131 |
+
view trends
|
| 132 |
+
order today
|
| 133 |
+
sign up
|
data/cta_verbs_list.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
sign up
|
| 2 |
+
shop all
|
| 3 |
+
find
|
| 4 |
+
send
|
| 5 |
+
start
|
| 6 |
+
see
|
| 7 |
+
view
|
| 8 |
+
shop
|
| 9 |
+
redeem
|
| 10 |
+
download
|
| 11 |
+
create
|
| 12 |
+
buy
|
| 13 |
+
claim
|
| 14 |
+
save
|
| 15 |
+
unlock
|
| 16 |
+
access
|
| 17 |
+
join
|
| 18 |
+
study
|
| 19 |
+
visit
|
| 20 |
+
take me
|
| 21 |
+
share
|
data/html_tags.csv
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"\t",=09
|
| 2 |
+
"\n",=0A
|
| 3 |
+
" ",=20
|
| 4 |
+
"!",=21
|
| 5 |
+
""",=22
|
| 6 |
+
"#",=23
|
| 7 |
+
"$",=24
|
| 8 |
+
"%",=25
|
| 9 |
+
"&",=26
|
| 10 |
+
"'",=27
|
| 11 |
+
"(",=28
|
| 12 |
+
")",=29
|
| 13 |
+
"*",=2A
|
| 14 |
+
"+",=2B
|
| 15 |
+
",",=2C
|
| 16 |
+
"-",=2D
|
| 17 |
+
".",=2E
|
| 18 |
+
"/",=2F
|
| 19 |
+
"0",=30
|
| 20 |
+
"1",=31
|
| 21 |
+
"2",=32
|
| 22 |
+
"3",=33
|
| 23 |
+
"4",=34
|
| 24 |
+
"5",=35
|
| 25 |
+
"6",=36
|
| 26 |
+
"7",=37
|
| 27 |
+
"8",=38
|
| 28 |
+
"9",=39
|
| 29 |
+
":",=3A
|
| 30 |
+
";",=3B
|
| 31 |
+
"<",=3C
|
| 32 |
+
">",=3E
|
| 33 |
+
"?",=3F
|
| 34 |
+
"@",=40
|
| 35 |
+
"A",=41
|
| 36 |
+
"B",=42
|
| 37 |
+
"C",=43
|
| 38 |
+
"D",=44
|
| 39 |
+
"E",=45
|
| 40 |
+
"F",=46
|
| 41 |
+
"G",=47
|
| 42 |
+
"H",=48
|
| 43 |
+
"I",=49
|
| 44 |
+
"J",=4A
|
| 45 |
+
"K",=4B
|
| 46 |
+
"L",=4C
|
| 47 |
+
"M",=4D
|
| 48 |
+
"N",=4E
|
| 49 |
+
"O",=4F
|
| 50 |
+
"P",=50
|
| 51 |
+
"Q",=51
|
| 52 |
+
"R",=52
|
| 53 |
+
"S",=53
|
| 54 |
+
"T",=54
|
| 55 |
+
"U",=55
|
| 56 |
+
"V",=56
|
| 57 |
+
"W",=57
|
| 58 |
+
"X",=58
|
| 59 |
+
"Y",=59
|
| 60 |
+
"Z",=5A
|
| 61 |
+
"[",=5B
|
| 62 |
+
"\",=5C
|
| 63 |
+
"]",=5D
|
| 64 |
+
"^",=5E
|
| 65 |
+
"_",=5F
|
| 66 |
+
"`",=60
|
| 67 |
+
"a",=61
|
| 68 |
+
"b",=62
|
| 69 |
+
"c",=63
|
| 70 |
+
"d",=64
|
| 71 |
+
"e",=65
|
| 72 |
+
"f",=66
|
| 73 |
+
"g",=67
|
| 74 |
+
"h",=68
|
| 75 |
+
"i",=69
|
| 76 |
+
"j",=6A
|
| 77 |
+
"k",=6B
|
| 78 |
+
"l",=6C
|
| 79 |
+
"m",=6D
|
| 80 |
+
"n",=6E
|
| 81 |
+
"o",=6F
|
| 82 |
+
"p",=70
|
| 83 |
+
"q",=71
|
| 84 |
+
"r",=72
|
| 85 |
+
"s",=73
|
| 86 |
+
"t",=74
|
| 87 |
+
"u",=75
|
| 88 |
+
"v",=76
|
| 89 |
+
"w",=77
|
| 90 |
+
"x",=78
|
| 91 |
+
"y",=79
|
| 92 |
+
"z",=7A
|
| 93 |
+
"{",=7B
|
| 94 |
+
"|",=7C
|
| 95 |
+
"}",=7D
|
| 96 |
+
"~",=7E
|
| 97 |
+
"",=C2=A0
|
| 98 |
+
"",=C2=BB
|
| 99 |
+
"",=E2=86=92
|
| 100 |
+
" ",
|
| 101 |
+
"",→
|
| 102 |
+
"1/2",=C2=BD
|
| 103 |
+
"",=0D
|
| 104 |
+
"",=C2=A9
|
| 105 |
+
"",=E2=80=8C
|
| 106 |
+
" ",=E2=80=8B
|
| 107 |
+
"...",=E2=80=A6
|
| 108 |
+
"`",=E2=80=99
|
| 109 |
+
" ",=C2=A0
|
environment.yml
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: base
|
| 2 |
+
channels:
|
| 3 |
+
- conda-forge
|
| 4 |
+
- defaults
|
| 5 |
+
dependencies:
|
| 6 |
+
- _libgcc_mutex=0.1=conda_forge
|
| 7 |
+
- _openmp_mutex=4.5=2_gnu
|
| 8 |
+
- brotlipy=0.7.0=py39hb9d737c_1004
|
| 9 |
+
- bzip2=1.0.8=h7f98852_4
|
| 10 |
+
- c-ares=1.18.1=h7f98852_0
|
| 11 |
+
- ca-certificates=2022.9.24=ha878542_0
|
| 12 |
+
- certifi=2022.9.24=pyhd8ed1ab_0
|
| 13 |
+
- cffi=1.15.1=py39he91dace_0
|
| 14 |
+
- charset-normalizer=2.1.1=pyhd8ed1ab_0
|
| 15 |
+
- colorama=0.4.5=pyhd8ed1ab_0
|
| 16 |
+
- conda=22.9.0=py39hf3d152e_1
|
| 17 |
+
- conda-package-handling=1.9.0=py39hb9d737c_0
|
| 18 |
+
- cryptography=38.0.1=py39hd97740a_0
|
| 19 |
+
- icu=70.1=h27087fc_0
|
| 20 |
+
- idna=3.4=pyhd8ed1ab_0
|
| 21 |
+
- keyutils=1.6.1=h166bdaf_0
|
| 22 |
+
- krb5=1.19.3=h3790be6_0
|
| 23 |
+
- ld_impl_linux-64=2.36.1=hea4e1c9_2
|
| 24 |
+
- libarchive=3.5.2=hb890918_3
|
| 25 |
+
- libcurl=7.85.0=h7bff187_0
|
| 26 |
+
- libedit=3.1.20191231=he28a2e2_2
|
| 27 |
+
- libev=4.33=h516909a_1
|
| 28 |
+
- libffi=3.4.2=h7f98852_5
|
| 29 |
+
- libgcc-ng=12.1.0=h8d9b700_16
|
| 30 |
+
- libgomp=12.1.0=h8d9b700_16
|
| 31 |
+
- libiconv=1.17=h166bdaf_0
|
| 32 |
+
- libmamba=0.27.0=h0dd8ff0_0
|
| 33 |
+
- libmambapy=0.27.0=py39hd55135b_0
|
| 34 |
+
- libnghttp2=1.47.0=hdcd2b5c_1
|
| 35 |
+
- libnsl=2.0.0=h7f98852_0
|
| 36 |
+
- libsolv=0.7.22=h6239696_0
|
| 37 |
+
- libsqlite=3.39.4=h753d276_0
|
| 38 |
+
- libssh2=1.10.0=haa6b8db_3
|
| 39 |
+
- libstdcxx-ng=12.1.0=ha89aaad_16
|
| 40 |
+
- libuuid=2.32.1=h7f98852_1000
|
| 41 |
+
- libxml2=2.10.2=h4c7fe37_1
|
| 42 |
+
- libzlib=1.2.12=h166bdaf_4
|
| 43 |
+
- lz4-c=1.9.3=h9c3ff4c_1
|
| 44 |
+
- lzo=2.10=h516909a_1000
|
| 45 |
+
- mamba=0.27.0=py39hfa8f2c8_0
|
| 46 |
+
- ncurses=6.3=h27087fc_1
|
| 47 |
+
- openssl=1.1.1q=h166bdaf_0
|
| 48 |
+
- pip=22.2.2=pyhd8ed1ab_0
|
| 49 |
+
- pybind11-abi=4=hd8ed1ab_3
|
| 50 |
+
- pycosat=0.6.3=py39hb9d737c_1010
|
| 51 |
+
- pycparser=2.21=pyhd8ed1ab_0
|
| 52 |
+
- pyopenssl=22.0.0=pyhd8ed1ab_1
|
| 53 |
+
- pysocks=1.7.1=pyha2e5f31_6
|
| 54 |
+
- python=3.9.13=h9a8a25e_0_cpython
|
| 55 |
+
- python_abi=3.9=2_cp39
|
| 56 |
+
- readline=8.1.2=h0f457ee_0
|
| 57 |
+
- reproc=14.2.3=h7f98852_0
|
| 58 |
+
- reproc-cpp=14.2.3=h9c3ff4c_0
|
| 59 |
+
- requests=2.28.1=pyhd8ed1ab_1
|
| 60 |
+
- ruamel_yaml=0.15.80=py39hb9d737c_1007
|
| 61 |
+
- setuptools=65.4.1=pyhd8ed1ab_0
|
| 62 |
+
- sqlite=3.39.4=h4ff8645_0
|
| 63 |
+
- tk=8.6.12=h27826a3_0
|
| 64 |
+
- toolz=0.12.0=pyhd8ed1ab_0
|
| 65 |
+
- tqdm=4.64.1=pyhd8ed1ab_0
|
| 66 |
+
- urllib3=1.26.11=pyhd8ed1ab_0
|
| 67 |
+
- wheel=0.37.1=pyhd8ed1ab_0
|
| 68 |
+
- xz=5.2.6=h166bdaf_0
|
| 69 |
+
- yaml=0.2.5=h7f98852_2
|
| 70 |
+
- yaml-cpp=0.7.0=h27087fc_2
|
| 71 |
+
- zstd=1.5.2=h6239696_4
|
| 72 |
+
- pip:
|
| 73 |
+
- altair==4.2.0
|
| 74 |
+
- asttokens==2.0.8
|
| 75 |
+
- attrs==22.1.0
|
| 76 |
+
- backcall==0.2.0
|
| 77 |
+
- beautifulsoup4==4.11.1
|
| 78 |
+
- blinker==1.5
|
| 79 |
+
- bokeh==2.4.1
|
| 80 |
+
- boto3==1.24.86
|
| 81 |
+
- botocore==1.27.86
|
| 82 |
+
- bs4==0.0.1
|
| 83 |
+
- cachetools==5.2.0
|
| 84 |
+
- click==8.1.3
|
| 85 |
+
- commonmark==0.9.1
|
| 86 |
+
- debugpy==1.6.3
|
| 87 |
+
- decorator==5.1.1
|
| 88 |
+
- entrypoints==0.4
|
| 89 |
+
- executing==1.1.0
|
| 90 |
+
- gensim==4.2.0
|
| 91 |
+
- gitdb==4.0.9
|
| 92 |
+
- gitpython==3.1.27
|
| 93 |
+
- importlib-metadata==5.0.0
|
| 94 |
+
- ipykernel==6.16.0
|
| 95 |
+
- ipython==8.5.0
|
| 96 |
+
- ipywidgets==8.0.2
|
| 97 |
+
- jedi==0.18.1
|
| 98 |
+
- jinja2==3.1.2
|
| 99 |
+
- jmespath==1.0.1
|
| 100 |
+
- joblib==1.2.0
|
| 101 |
+
- jsonschema==4.16.0
|
| 102 |
+
- jupyter-client==7.3.5
|
| 103 |
+
- jupyter-core==4.11.1
|
| 104 |
+
- jupyterlab-widgets==3.0.3
|
| 105 |
+
- markupsafe==2.1.1
|
| 106 |
+
- matplotlib-inline==0.1.6
|
| 107 |
+
- nest-asyncio==1.5.6
|
| 108 |
+
- numpy==1.23.3
|
| 109 |
+
- packaging==21.3
|
| 110 |
+
- pandas==1.5.0
|
| 111 |
+
- parso==0.8.3
|
| 112 |
+
- pexpect==4.8.0
|
| 113 |
+
- pickleshare==0.7.5
|
| 114 |
+
- pillow==9.2.0
|
| 115 |
+
- prompt-toolkit==3.0.31
|
| 116 |
+
- protobuf==3.20.3
|
| 117 |
+
- psutil==5.9.2
|
| 118 |
+
- ptyprocess==0.7.0
|
| 119 |
+
- pure-eval==0.2.2
|
| 120 |
+
- pyarrow==9.0.0
|
| 121 |
+
- pydeck==0.8.0b3
|
| 122 |
+
- pygments==2.13.0
|
| 123 |
+
- pympler==1.0.1
|
| 124 |
+
- pyparsing==3.0.9
|
| 125 |
+
- pyrsistent==0.18.1
|
| 126 |
+
- python-dateutil==2.8.2
|
| 127 |
+
- pytz==2022.4
|
| 128 |
+
- pytz-deprecation-shim==0.1.0.post0
|
| 129 |
+
- pyyaml==6.0
|
| 130 |
+
- pyzmq==24.0.1
|
| 131 |
+
- rich==12.6.0
|
| 132 |
+
- s3transfer==0.6.0
|
| 133 |
+
- scikit-learn==1.1.2
|
| 134 |
+
- scipy==1.9.1
|
| 135 |
+
- semver==2.13.0
|
| 136 |
+
- six==1.16.0
|
| 137 |
+
- sklearn==0.0
|
| 138 |
+
- smart-open==6.2.0
|
| 139 |
+
- smmap==5.0.0
|
| 140 |
+
- soupsieve==2.3.2.post1
|
| 141 |
+
- stack-data==0.5.1
|
| 142 |
+
- streamlit==1.13.0
|
| 143 |
+
- threadpoolctl==3.1.0
|
| 144 |
+
- toml==0.10.2
|
| 145 |
+
- tornado==6.2
|
| 146 |
+
- traitlets==5.4.0
|
| 147 |
+
- typing-extensions==4.3.0
|
| 148 |
+
- tzdata==2022.4
|
| 149 |
+
- tzlocal==4.2
|
| 150 |
+
- validators==0.20.0
|
| 151 |
+
- watchdog==2.1.9
|
| 152 |
+
- wcwidth==0.2.5
|
| 153 |
+
- widgetsnbextension==4.0.3
|
| 154 |
+
- zipp==3.8.1
|
figures/ModelCTA.png
ADDED
|
html_tags.csv
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"\t",=09
|
| 2 |
+
"\n",=0A
|
| 3 |
+
" ",=20
|
| 4 |
+
"!",=21
|
| 5 |
+
""",=22
|
| 6 |
+
"#",=23
|
| 7 |
+
"$",=24
|
| 8 |
+
"%",=25
|
| 9 |
+
"&",=26
|
| 10 |
+
"'",=27
|
| 11 |
+
"(",=28
|
| 12 |
+
")",=29
|
| 13 |
+
"*",=2A
|
| 14 |
+
"+",=2B
|
| 15 |
+
",",=2C
|
| 16 |
+
"-",=2D
|
| 17 |
+
".",=2E
|
| 18 |
+
"/",=2F
|
| 19 |
+
"0",=30
|
| 20 |
+
"1",=31
|
| 21 |
+
"2",=32
|
| 22 |
+
"3",=33
|
| 23 |
+
"4",=34
|
| 24 |
+
"5",=35
|
| 25 |
+
"6",=36
|
| 26 |
+
"7",=37
|
| 27 |
+
"8",=38
|
| 28 |
+
"9",=39
|
| 29 |
+
":",=3A
|
| 30 |
+
";",=3B
|
| 31 |
+
"<",=3C
|
| 32 |
+
">",=3E
|
| 33 |
+
"?",=3F
|
| 34 |
+
"@",=40
|
| 35 |
+
"A",=41
|
| 36 |
+
"B",=42
|
| 37 |
+
"C",=43
|
| 38 |
+
"D",=44
|
| 39 |
+
"E",=45
|
| 40 |
+
"F",=46
|
| 41 |
+
"G",=47
|
| 42 |
+
"H",=48
|
| 43 |
+
"I",=49
|
| 44 |
+
"J",=4A
|
| 45 |
+
"K",=4B
|
| 46 |
+
"L",=4C
|
| 47 |
+
"M",=4D
|
| 48 |
+
"N",=4E
|
| 49 |
+
"O",=4F
|
| 50 |
+
"P",=50
|
| 51 |
+
"Q",=51
|
| 52 |
+
"R",=52
|
| 53 |
+
"S",=53
|
| 54 |
+
"T",=54
|
| 55 |
+
"U",=55
|
| 56 |
+
"V",=56
|
| 57 |
+
"W",=57
|
| 58 |
+
"X",=58
|
| 59 |
+
"Y",=59
|
| 60 |
+
"Z",=5A
|
| 61 |
+
"[",=5B
|
| 62 |
+
"\",=5C
|
| 63 |
+
"]",=5D
|
| 64 |
+
"^",=5E
|
| 65 |
+
"_",=5F
|
| 66 |
+
"`",=60
|
| 67 |
+
"a",=61
|
| 68 |
+
"b",=62
|
| 69 |
+
"c",=63
|
| 70 |
+
"d",=64
|
| 71 |
+
"e",=65
|
| 72 |
+
"f",=66
|
| 73 |
+
"g",=67
|
| 74 |
+
"h",=68
|
| 75 |
+
"i",=69
|
| 76 |
+
"j",=6A
|
| 77 |
+
"k",=6B
|
| 78 |
+
"l",=6C
|
| 79 |
+
"m",=6D
|
| 80 |
+
"n",=6E
|
| 81 |
+
"o",=6F
|
| 82 |
+
"p",=70
|
| 83 |
+
"q",=71
|
| 84 |
+
"r",=72
|
| 85 |
+
"s",=73
|
| 86 |
+
"t",=74
|
| 87 |
+
"u",=75
|
| 88 |
+
"v",=76
|
| 89 |
+
"w",=77
|
| 90 |
+
"x",=78
|
| 91 |
+
"y",=79
|
| 92 |
+
"z",=7A
|
| 93 |
+
"{",=7B
|
| 94 |
+
"|",=7C
|
| 95 |
+
"}",=7D
|
| 96 |
+
"~",=7E
|
| 97 |
+
"",=C2=A0
|
| 98 |
+
"",=C2=BB
|
| 99 |
+
"",=E2=86=92
|
| 100 |
+
" ",
|
| 101 |
+
"",→
|
| 102 |
+
"1/2",=C2=BD
|
| 103 |
+
"",=0D
|
| 104 |
+
"",=C2=A9
|
| 105 |
+
"",=E2=80=8C
|
| 106 |
+
" ",=E2=80=8B
|
| 107 |
+
"...",=E2=80=A6
|
| 108 |
+
"`",=E2=80=99
|
| 109 |
+
" ",=C2=A0
|
main_app.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
|
| 3 |
+
st.set_page_config(layout="wide")
|
| 4 |
+
|
| 5 |
+
st.markdown(
|
| 6 |
+
"""
|
| 7 |
+
<style>
|
| 8 |
+
body {
|
| 9 |
+
background-image: linear-gradient(#2e7bcf,#2e7bcf);
|
| 10 |
+
color: white;
|
| 11 |
+
}
|
| 12 |
+
</style>
|
| 13 |
+
""",
|
| 14 |
+
unsafe_allow_html=True,
|
| 15 |
+
)
|
models/modelCTA_CTOR.sav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:95af5e1cac2675fc49be61dc91778859a6d9d543eb141d0421697144c295e549
|
| 3 |
+
size 1341004
|
models/modelCTA_CTOR_new.sav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:afd022526974efca5503d38cc04a17547085fb76c74f330c3b0aca6bd094bcae
|
| 3 |
+
size 1338215
|
models/modelCTA_ConversionRate_new.sav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2a2055c6474bb535a4c983320e98dbed7260d220590b138abc01cb94f4f44e48
|
| 3 |
+
size 1338343
|
models/modelCTA_Conversion_Rate.sav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e79874d508a481aa2c70192f5a72a12b9c21a20a644780dda86c51a365bf5649
|
| 3 |
+
size 1341004
|
requirements.txt
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
bokeh==2.4.1
|
| 2 |
+
joblib==1.1.0
|
| 3 |
+
ipywidgets
|
| 4 |
+
boto3
|
| 5 |
+
bs4
|
| 6 |
+
gensim
|
| 7 |
+
scikit-learn
|
| 8 |
+
numpy
|
utils.py
ADDED
|
@@ -0,0 +1,553 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from hashlib import shake_128
|
| 2 |
+
import pandas as pd
|
| 3 |
+
import streamlit as st
|
| 4 |
+
|
| 5 |
+
from IPython.display import display
|
| 6 |
+
|
| 7 |
+
import email
|
| 8 |
+
import re
|
| 9 |
+
from bs4 import BeautifulSoup
|
| 10 |
+
import numpy as np
|
| 11 |
+
import random
|
| 12 |
+
from gensim.utils import simple_preprocess
|
| 13 |
+
from gensim.models.doc2vec import Doc2Vec, TaggedDocument
|
| 14 |
+
from sklearn.metrics import r2_score
|
| 15 |
+
|
| 16 |
+
from io import StringIO
|
| 17 |
+
import tempfile
|
| 18 |
+
import boto3
|
| 19 |
+
s3 = boto3.resource('s3')
|
| 20 |
+
import joblib
|
| 21 |
+
s3_client = boto3.client('s3')
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def get_files_from_aws(bucket,prefix):
|
| 25 |
+
"""
|
| 26 |
+
get files from aws s3 bucket
|
| 27 |
+
|
| 28 |
+
bucket (STRING): bucket name
|
| 29 |
+
prefix (STRING): file location in s3 bucket
|
| 30 |
+
"""
|
| 31 |
+
s3_client = boto3.client('s3',
|
| 32 |
+
aws_access_key_id = st.secrets["aws_id"],
|
| 33 |
+
aws_secret_access_key = st.secrets["aws_key"])
|
| 34 |
+
|
| 35 |
+
file_obj = s3_client.get_object(Bucket=bucket,Key=prefix)
|
| 36 |
+
body = file_obj['Body']
|
| 37 |
+
string = body.read().decode('utf-8')
|
| 38 |
+
|
| 39 |
+
df = pd.read_csv(StringIO(string),encoding = "ISO-8859-1",index_col=0)
|
| 40 |
+
df= df.reset_index(drop=True)
|
| 41 |
+
|
| 42 |
+
return df
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def display_CTA_color(text,color):
|
| 46 |
+
"""
|
| 47 |
+
Display one cta based on their color
|
| 48 |
+
"""
|
| 49 |
+
base_string = ""
|
| 50 |
+
for i in range(len(text)):
|
| 51 |
+
base_string += """
|
| 52 |
+
CTA Number {}:
|
| 53 |
+
<input type="button"
|
| 54 |
+
style="background-color:{};
|
| 55 |
+
color:black;
|
| 56 |
+
width:50px;
|
| 57 |
+
height:30px;
|
| 58 |
+
margin:4px"
|
| 59 |
+
value=" ">Percentage: {}%""".format(i+1,color[i],text[i])
|
| 60 |
+
if i != len(text)-1:
|
| 61 |
+
base_string += "<br>"
|
| 62 |
+
return base_string
|
| 63 |
+
|
| 64 |
+
def display_CTA_text(percentage,text):
|
| 65 |
+
"""
|
| 66 |
+
Display one cta based on their text
|
| 67 |
+
"""
|
| 68 |
+
base_string = ""
|
| 69 |
+
for i in range(len(percentage)):
|
| 70 |
+
base_string += """
|
| 71 |
+
CTA Number {}:
|
| 72 |
+
<input type="button"
|
| 73 |
+
style="background-color:#FFFFFF;
|
| 74 |
+
color:black;
|
| 75 |
+
width:fit-content;;
|
| 76 |
+
height:30px;
|
| 77 |
+
margin:4px"
|
| 78 |
+
value="{}">Percentage: {}%""".format(i+1,text[i].upper(),percentage[i])
|
| 79 |
+
if i != len(text)-1:
|
| 80 |
+
base_string += "<br>"
|
| 81 |
+
return base_string
|
| 82 |
+
|
| 83 |
+
def display_CTA_both(percentage, color, text):
|
| 84 |
+
"""
|
| 85 |
+
Display one based on their color and text
|
| 86 |
+
"""
|
| 87 |
+
base_string = ""
|
| 88 |
+
for i in range(len(text)):
|
| 89 |
+
base_string += """
|
| 90 |
+
CTA Number {}:
|
| 91 |
+
<input type="button"
|
| 92 |
+
style="background-color:{};
|
| 93 |
+
color:black;
|
| 94 |
+
width: fit-content;
|
| 95 |
+
height:30px;
|
| 96 |
+
margin:4px"
|
| 97 |
+
value="{}">Percentage: {}%""".format(i+1,color[i],text[i].upper(),percentage[i])
|
| 98 |
+
if i != len(text)-1:
|
| 99 |
+
base_string += "<br>"
|
| 100 |
+
return base_string
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
## "=",=3D removed from html_tags.csv
|
| 104 |
+
|
| 105 |
+
def preprocess_text(doc):
|
| 106 |
+
html_tags = open('data/html_tags.csv', 'r')
|
| 107 |
+
|
| 108 |
+
tags = {}
|
| 109 |
+
|
| 110 |
+
for i, line in enumerate(html_tags):
|
| 111 |
+
ln = line.strip().split(',')
|
| 112 |
+
ln[0] = ln[0].strip('"')
|
| 113 |
+
if len(ln) > 2:
|
| 114 |
+
ln[0] = ','
|
| 115 |
+
ln[1] = ln[2]
|
| 116 |
+
if ln[1] == '=09':
|
| 117 |
+
tags[ln[1]] = '\t'
|
| 118 |
+
elif ln[1] == '=0D':
|
| 119 |
+
tags[ln[1]] = '\n'
|
| 120 |
+
elif ln[1] == '=0A':
|
| 121 |
+
tags[ln[1]] = '\n'
|
| 122 |
+
elif ln[1] == '=22':
|
| 123 |
+
tags[ln[1]] = '"'
|
| 124 |
+
else:
|
| 125 |
+
tags[ln[1]] = ln[0]
|
| 126 |
+
|
| 127 |
+
for key, val in tags.items():
|
| 128 |
+
if key in doc:
|
| 129 |
+
doc = doc.replace(key, val)
|
| 130 |
+
|
| 131 |
+
if '=3D' in doc:
|
| 132 |
+
doc = doc.replace('=3D', '%3D')
|
| 133 |
+
|
| 134 |
+
if '=' in doc:
|
| 135 |
+
doc = doc.replace('=\n', '')
|
| 136 |
+
|
| 137 |
+
doc = doc.replace('%3D', '=')
|
| 138 |
+
return doc
|
| 139 |
+
|
| 140 |
+
def parse_features_from_html(body, soup):
|
| 141 |
+
cta_file = open('data/cta_text_list.txt', 'r')
|
| 142 |
+
cta_vfile = open('data/cta_verbs_list.txt', 'r')
|
| 143 |
+
|
| 144 |
+
cta_list = []
|
| 145 |
+
cta_verbs = []
|
| 146 |
+
for i, ln in enumerate(cta_file):
|
| 147 |
+
cta_list.append(ln.strip())
|
| 148 |
+
|
| 149 |
+
for i, ln in enumerate(cta_vfile):
|
| 150 |
+
cta_verbs.append(ln.strip())
|
| 151 |
+
|
| 152 |
+
#extracting visible text:
|
| 153 |
+
visible_text = []
|
| 154 |
+
ccolor = []
|
| 155 |
+
text = []
|
| 156 |
+
|
| 157 |
+
bodytext = soup.get_text()
|
| 158 |
+
vtexts = preprocess_text(bodytext)
|
| 159 |
+
vtexts = " ".join(vtexts.split())
|
| 160 |
+
items = soup.find_all('a', {'href': True})
|
| 161 |
+
for i in items: # Items contain all <a> with with 'href'
|
| 162 |
+
try:
|
| 163 |
+
#if i['style']:
|
| 164 |
+
style = i['style']
|
| 165 |
+
style = style.replace('\r', '')
|
| 166 |
+
style = style.replace('\n', '')
|
| 167 |
+
styles = style.split(';')
|
| 168 |
+
|
| 169 |
+
color_flag = 0 ## Indicate whether there's 'background-color' option
|
| 170 |
+
style_str = str(style)
|
| 171 |
+
|
| 172 |
+
if ('background-color' in style_str) and ('display' in style_str) and ('border-radius' in style_str):
|
| 173 |
+
# print(styles)
|
| 174 |
+
for s in styles:
|
| 175 |
+
if 'background-color' in s:
|
| 176 |
+
cl = s.split(':')[1].lower()
|
| 177 |
+
cl = cl.replace('!important', '')
|
| 178 |
+
cl = cl.replace('=', '')
|
| 179 |
+
if cl.strip() == 'transparent':
|
| 180 |
+
cl = '#00ffffff'
|
| 181 |
+
if 'rgb' in cl:
|
| 182 |
+
rgb = cl[cl.index('(')+1:cl.index(')')].split(',')
|
| 183 |
+
cl = rgb_to_hex((int(rgb[0]), int(rgb[1]), int(rgb[2])))
|
| 184 |
+
ccolor.append(cl.strip()) # Add background color to CTA color list
|
| 185 |
+
color_flag = 1
|
| 186 |
+
|
| 187 |
+
if color_flag == 1:
|
| 188 |
+
|
| 189 |
+
## Remove surrounding '<>' of the text
|
| 190 |
+
clean = re.compile('<.*?>')
|
| 191 |
+
t = re.sub(clean, '', i.string.replace('\n', '').replace('\t', ' ')).lower()
|
| 192 |
+
|
| 193 |
+
## Replace/remove unwanted characters
|
| 194 |
+
t.replace('→', '')
|
| 195 |
+
t.replace('\t', ' ')
|
| 196 |
+
|
| 197 |
+
## Check if additional chars are there in the string
|
| 198 |
+
# if '>' in t:
|
| 199 |
+
# t = t[:t.index['>']]
|
| 200 |
+
text.append(t.strip())
|
| 201 |
+
|
| 202 |
+
# print(i.string.replace('\n', ''))
|
| 203 |
+
|
| 204 |
+
except:
|
| 205 |
+
continue
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
op_color = [] # Output text and color lists
|
| 209 |
+
op_text = []
|
| 210 |
+
|
| 211 |
+
if (text == []) or (ccolor == []):
|
| 212 |
+
return vtexts, [], []
|
| 213 |
+
|
| 214 |
+
else:
|
| 215 |
+
## cta_list, cta_verbs
|
| 216 |
+
for c in range(len(text)):
|
| 217 |
+
if text[c] in cta_list:
|
| 218 |
+
op_text.append(text[c])
|
| 219 |
+
op_color.append(ccolor[c])
|
| 220 |
+
|
| 221 |
+
else:
|
| 222 |
+
for cv in cta_verbs:
|
| 223 |
+
if cv in text[c]:
|
| 224 |
+
op_text.append(text[c])
|
| 225 |
+
op_color.append(ccolor[c])
|
| 226 |
+
|
| 227 |
+
return vtexts, op_color, op_text
|
| 228 |
+
|
| 229 |
+
## Parsed email from email_upload()
|
| 230 |
+
## RETURN: Each CTA text and it's color as lists
|
| 231 |
+
|
| 232 |
+
def email_parser(parsed_email):
|
| 233 |
+
emailstr = ""
|
| 234 |
+
for i, line in enumerate(parsed_email):
|
| 235 |
+
emailstr += line
|
| 236 |
+
|
| 237 |
+
b = email.message_from_string(emailstr)
|
| 238 |
+
body = ""
|
| 239 |
+
|
| 240 |
+
for part in b.walk():
|
| 241 |
+
if part.get_content_type():
|
| 242 |
+
body = str(part.get_payload())
|
| 243 |
+
# print('EMAIL: ', body)
|
| 244 |
+
doc = preprocess_text(body)
|
| 245 |
+
soup = BeautifulSoup(doc)
|
| 246 |
+
|
| 247 |
+
## Get CTA features from soup items of emails
|
| 248 |
+
vtext, ccolor, text = parse_features_from_html(body, soup)
|
| 249 |
+
|
| 250 |
+
return vtext, ccolor, text
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
## Generate word embeddings for each CTA text using Doc2Vec
|
| 255 |
+
|
| 256 |
+
def text_embeddings(texts):
|
| 257 |
+
text_tokens = []
|
| 258 |
+
for i, tx in enumerate(texts):
|
| 259 |
+
words = simple_preprocess(tx)
|
| 260 |
+
# print(words)
|
| 261 |
+
text_tokens.append(TaggedDocument(words, [i]))
|
| 262 |
+
|
| 263 |
+
##----
|
| 264 |
+
#vector_size = Dimensionality of the feature vectors.
|
| 265 |
+
#window = The maximum distance between the current and predicted word within a sentence.
|
| 266 |
+
#min_count = Ignores all words with total frequency lower than this.
|
| 267 |
+
#alpha = The initial learning rate.
|
| 268 |
+
##----
|
| 269 |
+
model = Doc2Vec(text_tokens, workers = 1, seed = 1)
|
| 270 |
+
# model = SentenceTransformer('bert-base-nli-mean-tokens')
|
| 271 |
+
# sentence_embeddings = model.encode(texts)
|
| 272 |
+
return model
|
| 273 |
+
|
| 274 |
+
###### Model Training - ONLY TO SAVE IN S3 BUCKET ######
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
def get_predictions(selected_variable, selected_industry, selected_campaign,
|
| 278 |
+
selected_cta, email_text, cta_col, cta_txt, cta_menu):
|
| 279 |
+
|
| 280 |
+
bucket_name = 'sagemakermodelcta'
|
| 281 |
+
|
| 282 |
+
if selected_variable == 'Click_To_Open_Rate':
|
| 283 |
+
X_name = 'Xtest_CTOR.csv'
|
| 284 |
+
y_name = 'ytest_CTOR.csv'
|
| 285 |
+
key = 'models/' + 'modelCTA_CTOR_new.sav'
|
| 286 |
+
|
| 287 |
+
elif selected_variable == 'Conversion_Rate':
|
| 288 |
+
X_name = 'Xtest_Conversion_Rate.csv'
|
| 289 |
+
y_name = 'ytest_Conversion_Rate.csv'
|
| 290 |
+
key = 'models/' + 'modelCTA_ConversionRate_new.sav'
|
| 291 |
+
|
| 292 |
+
|
| 293 |
+
training_dataset = get_files_from_aws('emailcampaigntrainingdata', 'ModelCTA/training.csv')
|
| 294 |
+
X_test = get_files_from_aws('emailcampaigntrainingdata', 'ModelCTA/' + X_name)
|
| 295 |
+
y_test = get_files_from_aws('emailcampaigntrainingdata', 'ModelCTA/' + y_name)
|
| 296 |
+
|
| 297 |
+
# load model from S3
|
| 298 |
+
with tempfile.TemporaryFile() as fp:
|
| 299 |
+
# s3_client.download_fileobj(Fileobj=fp, Bucket=bucket_name, Key=key)
|
| 300 |
+
# fp.seek(0)
|
| 301 |
+
regr = joblib.load(key)
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
email_body_dict = {}
|
| 305 |
+
for _, r in training_dataset.iterrows():
|
| 306 |
+
if r[0] not in email_body_dict.keys():
|
| 307 |
+
email_body_dict[r[0]] = r[4]
|
| 308 |
+
|
| 309 |
+
email_body = email_body_dict.keys()
|
| 310 |
+
texts = list(email_body_dict.values())
|
| 311 |
+
# texts = training_dataset['body'].unique() ## Use email body for NLP
|
| 312 |
+
# texts = training_dataset['cta_text'].unique()
|
| 313 |
+
|
| 314 |
+
y_pred = regr.predict(X_test)
|
| 315 |
+
r2_test = r2_score(y_test, y_pred)
|
| 316 |
+
|
| 317 |
+
## Get recommendation
|
| 318 |
+
recom_model = text_embeddings(email_body)
|
| 319 |
+
# recom_model = text_embeddings()
|
| 320 |
+
|
| 321 |
+
industry_code_dict = dict(zip(training_dataset.industry, training_dataset.industry_code))
|
| 322 |
+
campaign_code_dict = dict(zip(training_dataset.campaign, training_dataset.campaign_code))
|
| 323 |
+
color_code_dict = dict(zip(training_dataset.cta_color, training_dataset.color_code))
|
| 324 |
+
text_code_dict = dict(zip(training_dataset.cta_text, training_dataset.text_code))
|
| 325 |
+
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
for ip_idx, ip in enumerate(cta_menu): # For each CTA selected
|
| 329 |
+
if ip.value == True:
|
| 330 |
+
cta_ind = ip_idx
|
| 331 |
+
selected_color = cta_col[cta_ind]
|
| 332 |
+
selected_text = cta_txt[cta_ind]
|
| 333 |
+
|
| 334 |
+
df_uploaded = pd.DataFrame(columns=['industry', 'campaign', 'cta_color', 'cta_text'])
|
| 335 |
+
df_uploaded.loc[0] = [selected_industry, selected_campaign, cta_col, cta_txt]
|
| 336 |
+
df_uploaded['industry_code'] = industry_code_dict.get(selected_industry)
|
| 337 |
+
|
| 338 |
+
if selected_campaign not in campaign_code_dict.keys():
|
| 339 |
+
campaign_code_dict[selected_campaign] = max(campaign_code_dict.values()) + 1
|
| 340 |
+
|
| 341 |
+
df_uploaded['campaign_code'] = campaign_code_dict.get(selected_campaign)
|
| 342 |
+
|
| 343 |
+
if selected_color not in color_code_dict.keys():
|
| 344 |
+
color_code_dict[selected_color] = max(color_code_dict.values()) + 1
|
| 345 |
+
|
| 346 |
+
df_uploaded['color_code'] = color_code_dict.get(selected_color)
|
| 347 |
+
|
| 348 |
+
if selected_text not in text_code_dict.keys():
|
| 349 |
+
text_code_dict[selected_text] = max(text_code_dict.values()) + 1
|
| 350 |
+
|
| 351 |
+
df_uploaded['text_code'] = text_code_dict.get(selected_text)
|
| 352 |
+
|
| 353 |
+
|
| 354 |
+
df_uploaded_test = df_uploaded.drop(['industry', 'campaign', 'cta_color', 'cta_text'],
|
| 355 |
+
axis = 1, inplace = False)
|
| 356 |
+
|
| 357 |
+
df_uploaded_test = df_uploaded_test.dropna()
|
| 358 |
+
|
| 359 |
+
arr = df_uploaded_test.to_numpy().astype('float64')
|
| 360 |
+
predicted_rate = regr.predict(arr)[0]
|
| 361 |
+
output_rate = predicted_rate
|
| 362 |
+
|
| 363 |
+
if output_rate < 0:
|
| 364 |
+
st.text("Sorry, Current model couldn't provide predictions on the target variable you selected.")
|
| 365 |
+
else:
|
| 366 |
+
st.info('Model Prediction on the {} is {}'.format(selected_variable, round(output_rate*100, 2)))
|
| 367 |
+
selected_industry_code = industry_code_dict.get(selected_industry)
|
| 368 |
+
selected_campaign_code = campaign_code_dict.get(selected_campaign)
|
| 369 |
+
|
| 370 |
+
### Create dataset for recommendation
|
| 371 |
+
# select the certain industry that user selected
|
| 372 |
+
###+++++use training data+++++++
|
| 373 |
+
df_recom = training_dataset[["industry_code", "campaign_code", "cta_color", "cta_text",
|
| 374 |
+
selected_variable]]
|
| 375 |
+
df_recom = df_recom[df_recom["industry_code"] == selected_industry_code]
|
| 376 |
+
# df_recom = df_recom[df_recom["campaign_code"] == selected_campaign_code]
|
| 377 |
+
|
| 378 |
+
df_recom[selected_variable]=df_recom[selected_variable].apply(lambda x:round(x, 5))
|
| 379 |
+
df_recom_sort = df_recom.sort_values(by=[selected_variable])
|
| 380 |
+
|
| 381 |
+
## Filter recommendatins for either CTA text or color
|
| 382 |
+
recom_ind = 0
|
| 383 |
+
recom_cta_arr = []
|
| 384 |
+
target_rate_arr = []
|
| 385 |
+
if selected_cta == 'Color':
|
| 386 |
+
df_recom = df_recom_sort.drop_duplicates(subset=['cta_color'], keep='last')
|
| 387 |
+
|
| 388 |
+
replaces = False
|
| 389 |
+
if len(df_recom) < 3:
|
| 390 |
+
replaces = True
|
| 391 |
+
|
| 392 |
+
df_recom_extra = df_recom.sample(n=3, replace=replaces)
|
| 393 |
+
|
| 394 |
+
df_recom_opt = df_recom[(df_recom[selected_variable] > output_rate)]
|
| 395 |
+
df_recom_opt_rank = df_recom_opt.head(n=3)
|
| 396 |
+
df_recom_opt_rank_out = df_recom_opt_rank.sort_values(by=[selected_variable], ascending=False)
|
| 397 |
+
|
| 398 |
+
# st.text(f"\nTo get a higher {selected_variable}, the model recommends the following options: ")
|
| 399 |
+
st.info('To get a higher {}, the model recommends the following options:'.format(selected_variable))
|
| 400 |
+
|
| 401 |
+
if len(df_recom_opt_rank_out) < 2:
|
| 402 |
+
# print("You've already achieved the highest", selected_variable,
|
| 403 |
+
# "with the current Call-To-Action Colors!")
|
| 404 |
+
increment = output_rate + (0.02*3)
|
| 405 |
+
for _, row in df_recom_extra.iterrows():
|
| 406 |
+
target_rate = random.uniform(increment - 0.02, increment)
|
| 407 |
+
increment = target_rate - 0.001
|
| 408 |
+
recom_cta = row[2]
|
| 409 |
+
# st.text(f" {(color(' ', fore='#ffffff', back=recom_cta))} \x1b[1m{round(target_rate*100, 2)}%\x1b[22m")
|
| 410 |
+
# st.components.v1.html(f"<p style='color:{recom_cta};'> {recom_cta} </p>", height=50)
|
| 411 |
+
# st.components.v1.html(f"<p style='color:{recom_cta};'> {round(target_rate*100, 2)}% </p>", height=50)
|
| 412 |
+
# st.com
|
| 413 |
+
recom_cta_arr.append(recom_cta)
|
| 414 |
+
target_rate_arr.append(round(target_rate*100, 2))
|
| 415 |
+
else:
|
| 416 |
+
for _, row in df_recom_opt_rank_out.iterrows():
|
| 417 |
+
target_rate = row[4]
|
| 418 |
+
recom_cta = row[2]
|
| 419 |
+
# st.text(f" {(color(' ', fore='#ffffff', back=recom_cta))} \x1b[1m{round(target_rate*100, 2)}%\x1b[22m")
|
| 420 |
+
# st.components.v1.html(f"<p style='color:{recom_cta};'> {recom_cta} </p>", height=50)
|
| 421 |
+
recom_cta_arr.append(recom_cta)
|
| 422 |
+
target_rate_arr.append(round(target_rate*100, 2))
|
| 423 |
+
|
| 424 |
+
cta_result = display_CTA_color(target_rate_arr, recom_cta_arr)
|
| 425 |
+
st.components.v1.html(cta_result, height=len(target_rate_arr)*30+50)
|
| 426 |
+
|
| 427 |
+
elif selected_cta == 'Text':
|
| 428 |
+
|
| 429 |
+
df_recom = df_recom_sort.drop_duplicates(subset=['cta_text'], keep='last')
|
| 430 |
+
|
| 431 |
+
words = simple_preprocess(email_text)
|
| 432 |
+
test_doc_vector = recom_model.infer_vector(words)
|
| 433 |
+
recom_similar = recom_model.dv.most_similar(positive = [test_doc_vector], topn=30)
|
| 434 |
+
|
| 435 |
+
|
| 436 |
+
df_recom_opt_out = pd.DataFrame(columns=["industry_code", "campaign_code", "cta_color",
|
| 437 |
+
"cta_text", selected_variable])
|
| 438 |
+
|
| 439 |
+
for _, w in enumerate(recom_similar):
|
| 440 |
+
sim_word = texts[w[0]] #w[0]
|
| 441 |
+
# print(sim_word)
|
| 442 |
+
df_recom_opt_sim = df_recom[df_recom['cta_text'] == sim_word]
|
| 443 |
+
df_recom_opt_out = pd.concat([df_recom_opt_out, df_recom_opt_sim])
|
| 444 |
+
|
| 445 |
+
if len(df_recom_opt_out) == 0:
|
| 446 |
+
df_recom_opt_out = df_recom
|
| 447 |
+
|
| 448 |
+
df_recom_out_dup1 = df_recom_opt_out.drop_duplicates(subset=['cta_text'], keep='last')
|
| 449 |
+
df_recom_out_dup = df_recom_out_dup1.drop_duplicates(subset=[selected_variable], keep='last')
|
| 450 |
+
df_recom_out_unique = df_recom_out_dup[df_recom_out_dup['cta_text'] != selected_text]
|
| 451 |
+
|
| 452 |
+
replaces = False
|
| 453 |
+
if len(df_recom_out_unique) < 3:
|
| 454 |
+
replaces = True
|
| 455 |
+
|
| 456 |
+
df_recom_extra = df_recom_out_unique.sample(n=3, replace=replaces)
|
| 457 |
+
|
| 458 |
+
df_recom_opt = df_recom_out_unique[(df_recom_out_unique[selected_variable] > output_rate)]
|
| 459 |
+
df_recom_opt_rank_out = df_recom_opt.head(3).sort_values(by=[selected_variable],
|
| 460 |
+
ascending=False)
|
| 461 |
+
|
| 462 |
+
# st.text(f"\nTo get a higher {selected_variable}, the model recommends the following options:")
|
| 463 |
+
st.info('To get a higher {}, the model recommends the following options:'.format(selected_variable))
|
| 464 |
+
if len(df_recom_opt_rank_out) < 2:
|
| 465 |
+
# print("You've already achieved the highest", selected_variable,
|
| 466 |
+
# "with the current Call-To-Action Texts!")
|
| 467 |
+
increment = output_rate + (0.02*3)
|
| 468 |
+
for _, row in df_recom_extra.iterrows():
|
| 469 |
+
target_rate = random.uniform(increment - 0.02, increment)
|
| 470 |
+
increment = target_rate - 0.001
|
| 471 |
+
recom_cta = row[3]
|
| 472 |
+
# st.text(f"\x1b[1m. {recom_cta.upper()} {round(target_rate*100, 2)}%\x1b[22m")
|
| 473 |
+
recom_cta_arr.append(recom_cta)
|
| 474 |
+
target_rate_arr.append(round(target_rate*100, 2))
|
| 475 |
+
|
| 476 |
+
else:
|
| 477 |
+
for _, row in df_recom_opt_rank_out.iterrows():
|
| 478 |
+
target_rate = row[4]
|
| 479 |
+
recom_cta = row[3]
|
| 480 |
+
recom_cta_arr.append(recom_cta)
|
| 481 |
+
target_rate_arr.append(round(target_rate*100, 2))
|
| 482 |
+
|
| 483 |
+
cta_result = display_CTA_text(target_rate_arr, recom_cta_arr)
|
| 484 |
+
st.components.v1.html(cta_result, height=len(target_rate_arr)*30+50)
|
| 485 |
+
|
| 486 |
+
|
| 487 |
+
elif selected_cta == 'Both':
|
| 488 |
+
# Create new array for both
|
| 489 |
+
recom_cta_color_arr = []
|
| 490 |
+
recom_cta_text_arr = []
|
| 491 |
+
|
| 492 |
+
df_recom_both = df_recom_sort.drop_duplicates(subset=['cta_color', 'cta_text'], keep='last')
|
| 493 |
+
|
| 494 |
+
words = simple_preprocess(email_text)
|
| 495 |
+
test_doc_vector = recom_model.infer_vector(words)
|
| 496 |
+
recom_similar = recom_model.dv.most_similar(positive = [test_doc_vector], topn=30)
|
| 497 |
+
|
| 498 |
+
df_recom_opt_out = pd.DataFrame(columns=["industry_code", "campaign_code", "cta_color",
|
| 499 |
+
"cta_text", selected_variable])
|
| 500 |
+
for _, w in enumerate(recom_similar):
|
| 501 |
+
sim_word = texts[w[0]] #w[0]
|
| 502 |
+
df_recom_opt_sim = df_recom_both[df_recom_both['cta_text'] == sim_word]
|
| 503 |
+
df_recom_opt_out = pd.concat([df_recom_opt_out, df_recom_opt_sim])
|
| 504 |
+
|
| 505 |
+
if len(df_recom_opt_out) == 0:
|
| 506 |
+
df_recom_opt_out = df_recom
|
| 507 |
+
|
| 508 |
+
df_recom_out_dup1 = df_recom_opt_out.drop_duplicates(subset=['cta_text'], keep='last')
|
| 509 |
+
df_recom_out_dup = df_recom_out_dup1.drop_duplicates(subset=[selected_variable], keep='last')
|
| 510 |
+
df_recom_out_unique = df_recom_out_dup[df_recom_out_dup['cta_text'] != selected_text]
|
| 511 |
+
|
| 512 |
+
replaces = False
|
| 513 |
+
if len(df_recom_out_unique) < 3:
|
| 514 |
+
replaces = True
|
| 515 |
+
|
| 516 |
+
df_recom_extra = df_recom_out_unique.sample(n=3, replace=replaces)
|
| 517 |
+
|
| 518 |
+
df_recom_opt_both = df_recom_out_unique[(df_recom_out_unique[selected_variable] > output_rate)]
|
| 519 |
+
df_recom_opt_rank_out = df_recom_opt_both.head(3).sort_values(by=[selected_variable],
|
| 520 |
+
ascending=False)
|
| 521 |
+
|
| 522 |
+
# st.text(f"\nTo get a higher {selected_variable}, the model recommends the following options: ")
|
| 523 |
+
st.info('To get a higher {}, the model recommends the following options:'.format(selected_variable))
|
| 524 |
+
if len(df_recom_opt_rank_out) < 2 :
|
| 525 |
+
increment = output_rate + (0.02*3)
|
| 526 |
+
for _, row in df_recom_extra.iterrows():
|
| 527 |
+
target_rate = random.uniform(increment - 0.02, increment)
|
| 528 |
+
increment = target_rate - 0.001
|
| 529 |
+
recom_color = row[2]
|
| 530 |
+
recom_text = row[3]
|
| 531 |
+
|
| 532 |
+
recom_cta_color_arr.append(recom_color)
|
| 533 |
+
recom_cta_text_arr.append(recom_text)
|
| 534 |
+
target_rate_arr.append(round(target_rate*100, 2))
|
| 535 |
+
|
| 536 |
+
# print(f" {(color(' ', fore='#ffffff', back=recom_color))} \x1b[1m{recom_text.upper()} {round(target_rate*100, 2)}%\x1b[22m")
|
| 537 |
+
|
| 538 |
+
else:
|
| 539 |
+
for _, row in df_recom_opt_rank_out.iterrows():
|
| 540 |
+
target_rate = row[4]
|
| 541 |
+
recom_color = row[2]
|
| 542 |
+
recom_text = row[3]
|
| 543 |
+
|
| 544 |
+
recom_cta_color_arr.append(recom_color)
|
| 545 |
+
recom_cta_text_arr.append(recom_text)
|
| 546 |
+
target_rate_arr.append(round(target_rate*100, 2))
|
| 547 |
+
|
| 548 |
+
# print(f" {(color(' ', fore='#ffffff', back=recom_color))} \x1b[1m{recom_text.upper()} {round(target_rate*100, 2)}%\x1b[22m")
|
| 549 |
+
|
| 550 |
+
cta_result = display_CTA_both(target_rate_arr, recom_cta_color_arr,recom_cta_text_arr)
|
| 551 |
+
st.components.v1.html(cta_result, height=len(target_rate_arr)*30+50)
|
| 552 |
+
|
| 553 |
+
return r2_test
|