Error
DocAbout
This macro creates a tool that allows mail to be sent with authentication. It uses Python (tool) to connect to the mail server.
Doc1
Data or text is input from the macro input or UI, respectively. This data goes through a gate based on wether data or manual entry is selected and the chosen data continues through to be processed.
Doc2
SMTP, username and password are input from the UI. These go on to create the connection to the mail server.
Doc3
SMTP, username and password are appended to the data. This isn't entirely necessary but it allows for multiple sender modifications to be built in more easily.
Doc4
Main python file is executed to create a connection to the mail server are prepare the messages. Tests are also executed.
Doc5
Successful emails outputted.
Failed emails outputted with test results.
Test1
To and CC email addresses are validated for their format x@x.x and are also checked for valid delimiters (is there more than 1 @ in each record after "," split)
Test2
Any attached files are checked to make sure they exist.
Test3
Username (email address) are validated for their format x@x.x and are also checked for valid delimiters (is there more than 1 @ in each record after "," split)
Output117
Main and Validation Test Node
=2.5.0 in c:\\program files\\alteryx\\bin\\miniconda3\\pythontool_venv\\lib\\site-packages (from pandas)\nRequirement already satisfied: pytz>=2011k in c:\\program files\\alteryx\\bin\\miniconda3\\pythontool_venv\\lib\\site-packages (from pandas)\nRequirement already satisfied: six>=1.5 in c:\\program files\\alteryx\\bin\\miniconda3\\pythontool_venv\\lib\\site-packages (from python-dateutil>=2.5.0->pandas)\n\n"},{"data":{"text/plain":"[(, 0, 0, '', ('::1', 8080, 0, 0)),\n (, 0, 0, '', ('127.0.0.1', 8080))]"},"execution_count":8,"metadata":{},"output_type":"execute_result"}],"source":"# List all non-standard packages to be imported by your \n# script here (only missing packages will be installed)\nfrom ayx import Package\nPackage.installPackages(['numpy', 'pandas'])\nimport pandas as pd\nimport socket\nsocket.getaddrinfo('localhost', 8080)"},{"cell_type":"code","execution_count":9,"metadata":{"trusted":true},"outputs":[{"name":"stdout","output_type":"stream","text":"SUCCESS: reading input data \"#1\"\n"}],"source":"from ayx import Alteryx\nimport smtplib\nfrom os.path import basename\nfrom email.mime.application import MIMEApplication\nfrom email.mime.multipart import MIMEMultipart\nfrom email.mime.text import MIMEText\nfrom email.utils import COMMASPACE, formatdate\nfrom email.header import Header\n\nmail = pd.DataFrame(Alteryx.read(\"#1\"))\n\n"},{"cell_type":"code","execution_count":10,"metadata":{"trusted":true},"outputs":[],"source":"def sendemail(from_addr, to_addr_list, subject, message,\n srv_inst, cc_addr_list = None, files = None):\n\n to_addr_list = [x.replace(' ','') for x in to_addr_list]\n assert isinstance(to_addr_list, list)\n \n if cc_addr_list != None:\n cc_addr_list = [x.replace(' ','') for x in cc_addr_list]\n assert isinstance(cc_addr_list, list)\n \n if files != None:\n files = [x.strip(' ') for x in files]\n assert isinstance(files, list)\n \n \n msg = MIMEMultipart()\n msg['From'] = from_addr\n msg['To'] = COMMASPACE.join(to_addr_list)\n if cc_addr_list != None:\n msg['Cc'] = COMMASPACE.join(cc_addr_list)\n msg['Date'] = formatdate(localtime=True)\n msg['Subject'] = Header(subject, 'UTF-8')\n \n msg.attach(MIMEText(message, 'html', 'utf-8'))\n \n if files != None:\n #try:\n for f in files or []:\n with open(f, \"rb\") as fil:\n part = MIMEApplication(\n fil.read(),\n Name=basename(f)\n )\n # After the file is closed\n part['Content-Disposition'] = 'attachment; filename=\"%s\"' % basename(f)\n msg.attach(part)\n #except:\n # return(to_addr_list)\n \n if cc_addr_list != None: \n to_addr_list += cc_addr_list\n try: \n refused = srv_inst.sendmail(from_addr, to_addr_list, msg.as_string())\n if len(refused) != 0 and refused is not None:\n return([x for x in refused.keys()])\n except:\n return(to_addr_list)\n "},{"cell_type":"code","execution_count":11,"metadata":{"trusted":true},"outputs":[],"source":"def main(mail):\n frm = mail['From'][0]\n SMTP = mail['SMTP'][0]\n pw = mail['Password'][0]\n problems = []\n \n server = smtplib.SMTP(SMTP)\n server.starttls()\n server.login(frm,pw)\n \n for i, e in mail.iterrows():\n #print(e)\n to = e['To'].split(',')\n #print(to)\n subject = e['Subject']\n body = e['Body']\n #print(to)\n try:\n if len(e['CC']) == 0:\n cc = None\n else:\n cc = e['CC'].split(',')\n except:\n cc = None\n try:\n if len(e['Attachment Path']) == 0:\n files = None\n else:\n files = e['Attachment Path'].split(',')\n except:\n files = None\n problems += [[str(i+1), str(sendemail(frm, to, subject, body, server, cc, files))]]\n print(problems)\n server.quit()\n problems = pd.DataFrame(problems)\n problems.columns = ['Index', 'Failed']\n Alteryx.write(problems,1)"},{"cell_type":"code","execution_count":12,"metadata":{"trusted":true},"outputs":[{"name":"stdout","output_type":"stream","text":"[['1', 'None']]\nSUCCESS: writing outgoing connection data 1\n"}],"source":"main(mail)"},{"cell_type":"code","execution_count":null,"metadata":{"trusted":true},"outputs":[],"source":""}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.0"}},"nbformat":4,"nbformat_minor":2}]]>
56314
\\cjefferies-dt\dev\alteryx\macros\sendmail.yxmc
[{"label":"info","contents_keyword":"Alteryx.help()","cell_css":"border: 3px solid #357; margin: 4px; background: #fbffff","first_line":"Run `Alteryx.help()` for info about useful functions.","cell_type":"markdown","cell_class":"text_cell"},{"label":"deps","contents_keyword":"installPackages","cell_css":"border: 1px solid #58a; margin: 2px;","first_line":"# List all non-standard packages to be imported by your","cell_type":"code","cell_class":"code_cell"}]
True
True
true
Sender's email has valid format
#1
ExpFirst
[From Match]
To emails have valid format
#2
ExpAll
[To Match] or isnull([To])
CC emails have valid format
#3
ExpAll
[CC Match] or isnull([CC]) or [CC] = 0
To is not delimited with comma or semicolon
#4
ExpAll
[To is not delimited]
CC is not delimited with comma or semicolon
#5
ExpAll
[CC is not delimited]
Attachment Exists
#6
ExpAll
[Attachment Exists]
Validation
Sender's email has valid format
To emails have valid format
CC emails have valid format
To is not delimited with comma or semicolon
CC is not delimited with comma or semicolon
Attachment Exists
Copy of python input
Email to send with index
Failed Emails
Result with Tests and Failure List
R
Validation Union
Warn
Data/Manual Format Validation
Record
1
Int32
6
0
To
CC
Validate Email Login Format
CC Match = REGEX_Match(tostring([CC]), "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-...
Validate Email Login Format
To Match = REGEX_Match([To], "[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-...
Validate Delimiter
Validate Delimiter
Count@ = REGEX_CountMatches(tostring([CC]),'@')
CC is not delimited = [Count@] <...
Attachment Path
Warning
All
ByName
Auth Validation
Validate Email Login Format
From Match = REGEX_Match([From], "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](...
Data/Manual Input
Input
I
aaaaaaaaaaaaa
aaaaaaaaaaaaaa
aaaaaaaaaaaa
aaaaaaaaaaaaa
bork@smork.com
flirr@durr.wer, aldjlakj
aa
bb
Warning
All
ByName
Manual Entry
[Source] = "M"
Simple
=
Source
True
fixed
2018-12-19 16:19:06
0
M
2018-12-19 16:19:06
2018-12-19 16:19:06
[Source] = "M"
[Source] = "D"
Simple
=
Source
True
fixed
2018-12-19 16:19:06
0
D
2018-12-19 16:19:06
2018-12-19 16:19:06
[Source] = "D"
To = replace(ToString([To]), ';', ',')
CC = if (isnull(ToString([CC])) or ToStri...
Warning
All
ByName
"CC","Attachment Path"
True
True
True
False
False
False
False
False
False
upper
SMTP and Auth Input
pw9387401837408174081
smtp.office365.com
email@email.net
Horizontal
Question
Check Box (27)
Question
Text Box (5)
Question
Text Box (4)
Question
Text Box (6)
Question
Radio Button (43)
Question
Text Box (46)
Question
Text Box (47)
Question
Text Box (48)
Question
Text Box (49)
Question
Text Box (83)
Question
Radio Button (65)
Email (Auth)
Send multiple emails with authentication from any smtp server. V1.3.
SendMail
1.4
Reporting
email, authentication, smtp, python, mass email
William Bowman
Solarwinds
2019
NoCondition
(Always Run)
Dynamic
{{INPUT}}
8/Data/r[1]/c[3]
Update Cell
Expression
row 1
column 3
Dynamic
{{INPUT}}
8/Data/r[1]/c[2]
Update Cell
Expression
row 1
column 2
Dynamic
{{INPUT}}
8/Data/r[1]/c[1]
Update Cell
Expression
row 1
column 1
Dynamic
{{INPUT}}
26/Disabled/@value
Enable/Disable Container from Question
Expression
Dynamic
{{INPUT}}
45/DetourRight/@value
Update Detour Direction from Question
Expression
Dynamic
{{INPUT}}
52/Data/r[1]/c[1]
Update Cell
Expression
row 1
column 1
Dynamic
{{INPUT}}
52/Data/r[1]/c[2]
Update Cell
Expression
row 1
column 2
Dynamic
{{INPUT}}
52/Data/r[1]/c[3]
Update Cell
Expression
row 1
column 3
Dynamic
{{INPUT}}
52/Data/r[1]/c[4]
Update Cell
Expression
row 1
column 4
Dynamic
{{INPUT}}
52/Data/r[1]/c[5]
Update Cell
Expression
row 1
column 5
Error
'Please enter sender address'
[#1]=''
Error
'Please enter outgoing SMTP'
[#1]=''
Error
'Please enter password'
[#1]=''
Tab
Questions
Tab (1)
BooleanGroup
Diasble
Check Box (27)
Label
SendMail supports authentication, HTML in email body, and dynamic attachments
Label (94)
LabelGroup
Setup
LabelGroup (95)
TextBox
Outgoing SMTP
Text Box (5)
smtp.office365.com
TextBox
Sender Address
Text Box (4)
TextBox
Password
Text Box (6)
LabelGroup
Email Content
LabelGroup (41)
Label
Label (24)
RadioGroup
Data Informed Content
Radio Button (43)
Label
Each row will result in an email
Label (50)
MacroInput
Macro Input (2)
Macro Input (2)
RadioGroup
Manual Entry
Radio Button (65)
Label
Data input will be ignored
Label (70)
TextBox
To
Text Box (46)
TextBox
CC
Text Box (47)
TextBox
Subject
Text Box (48)
TextBox
Body
Text Box (49)
TextBox
Attachment Path
Text Box (83)
MacroOutput
Macro Output (18)
Macro Output (99)
MacroOutput
Macro Output (117)
Macro Output (117)
Macro
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