cd14 laumiulun commited on
Commit
6acac17
·
0 Parent(s):

Duplicate from loxzdigital/Model-CTA-Space

Browse files

Co-authored-by: Andy Lau <[email protected]>

.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
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
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 ;">&nbsp;</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&nbsp;START&nbsp;to&nbsp;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&nbsp;Email&nbsp;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>&nbsp;|&nbsp;</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>&nbsp;|&nbsp;</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>&nbsp;|&nbsp;</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
+ " ",&nbsp;
101
+ "",&#8594;
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
+ " ",&nbsp;
101
+ "",&#8594;
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