email.py
import os
import argparse
import yaml
import smtplib
import csv
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from urllib import unquote
import logging
import logging.handlers logger = logging.getLogger()
error_list = []
all_error_file_list=[] def extension(filename):
"""
get file extension
:param filename:
:return:
"""
ext = os.path.splitext(filename)[1]
if ext.startswith('.'):
# os.path.splitext retains . separator
ext = ext[1:]
return ext def list_file(file_name, list):
"""
get all failed file list ,contains sub folder
:param file_name: file name or folder name
:param list:
:return:
"""
if os.path.isdir(file_name):
for s in os.listdir(file_name):
list_file(os.path.join(file_name, s), list)
else:
ext = extension(file_name)
if ext == "failed":
list.append(file_name) def read_email_record(faild_path):
"""
read last failed file record
:param faild_path:
:return:
"""
record = os.path.join(faild_path, "email_record.txt")
if not os.path.isfile(record):
return {}
try:
return yaml.load(open(record))
except Exception as err:
return {} def rewrite_record(args):
"""
save record file
:param args:
:return:
"""
if not error_list:
return
record = os.path.join(args.failed_path, "email_record.txt")
map = {}
for s in all_error_file_list:
map[s] = "emailed"
f = open(record, "w")
yaml.dump(map, f, default_flow_style=False)
f.close() def process(args):
"""
process :
* list all failed files
* read record list
* remove duplicate data
:param args:
:return:
"""
faild_path = args.failed_path
if not os.path.isdir(faild_path):
logger.error("failed path:%s is not exist, exist", faild_path)
return
list_file(faild_path, all_error_file_list)
record_map = read_email_record(faild_path)
if not record_map:
error_list.extend(all_error_file_list)
else:
for file in all_error_file_list:
if not record_map.has_key(file):
error_list.append(file) def generate_body(s3path):
"""
generate email body
:return:
"""
body = """<h4>The following billing csv files are format failed,please check</h4>
"""
logger.info("failed file list as below:")
# list all failed list which need send email
for s in error_list:
# define line number from 0
line_num = 0
# to get error file name and path , just support {orgid}/{failed_file} format
ps = os.path.split(s)
# to regenerate new web path, such as s3://{bucket}/xxx/{orgid}/{fail_file}
s3 = os.path.join(s3path, os.path.split(ps[0])[1], ps[1])
# generate html body
body += "<hr>"
# add file name with link
body += """<div>File: <a href="%s">%s<a></div>""" % (s3, ps[1])
# add URL which can copy
body += "<div>URL: %s</div>" % (s3)
# add logger info
logger.info("-"*50)
logger.info("file:[%s]",s)
# lines which read each time
total_rows_each_read=10000000
# open failed file
with open(s, 'r') as f:
# read file in loop
while 1:
# get lines array list
lines = f.readlines(total_rows_each_read)
if not lines:
break
# check each line with csv , when failed ,
# generate new array from failed index and re-check until finished
while len(lines)>0:
# check csv format with strict = True
reader=csv.reader(lines, strict=True)
# define array index
i = 0
try:
# get rows of each line
for row in reader:
# when row is csv format , array index will ++
i += 1
line_num += 1
for item in row:
# check each column whether with \n , if yes ,will add line number
if str(item).find('\n')>0:
i += 1
line_num += 1
# when all lines is correct , array will set to empty to break loop
lines=[]
# get Exceptions
except Exception as err:
# when error occur , line number will increase 1
line_num += 1
#line_num += reader.line_num
line = lines[i]
# generate each error line information
body += "<div></div><br><br>"
body += "<li><div>Error line number: [#%s] </div></li>" \
"<div>#Exceptions: [%s]</div>" % (line_num, err)
body += """<div>#Error row data as below:</div>
<div>%s</div>""" % line
if (i+1) == len(lines):
lines=[]
else:
tmp=lines[i+1:]
lines = tmp
body += "<hr>"
return body def email_sender(args):
"""
send email ,iterator each receiver then send email
:param args:
:return:
"""
subject = "Error Pipeline CSV File List"
#if not error_list:
# return False
#body = generate_body(args.s3url)
body = "test"
emails = args.email
smtp_host = args.smtpHost
smtp_port = args.smtpPort
username = args.username
password = args.password
sent_from = args.sentFrom if emails:
mail_server = smtplib.SMTP()
try:
msg = MIMEMultipart()
msg['From'] = sent_from
msg['Subject'] = subject
msg.attach(MIMEText(body, 'html'))
mail_server.connect(smtp_host, smtp_port)
mail_server.ehlo()
mail_server.starttls()
mail_server.ehlo()
if password and smtp_host != 'eng-smtp.calix.local':
mail_server.login(username, unquote(password))
for recipient in emails:
logger.info("send email to %s", recipient)
msg['To'] = recipient
mail_server.sendmail(sent_from, recipient, msg.as_string())
except Exception as err:
logger.error("send email failed:%s", err)
return False
finally:
if mail_server:
mail_server.close()
return True
return False def init_log(log_file):
"""
init logger
:param log_file:
:return:
"""
log_path = os.path.split(log_file)[0]
if not os.path.isdir(log_path):
os.makedirs(log_path)
logger.setLevel(logging.DEBUG)
handler = logging.handlers.RotatingFileHandler(log_file,
maxBytes=20 * 1024 * 1024,
backupCount=5,
)
fmt = '%(asctime)s-[%(filename)s:%(lineno)s]-[%(levelname)s]- %(message)s'
formatter = logging.Formatter(fmt)
handler.setFormatter(formatter)
logger.addHandler(handler) def main():
parser = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('--failed_path', default='/var/tmp/billing/', help='store failed files')
parser.add_argument('-e', '--email', action='append',
help="To send multiple emails --email <email-address-1> --email <email-address-2> ...")
parser.add_argument('--smtpHost', type=str, help="Host of SMTP server", required=False,
default="outlook.office365.com")
parser.add_argument('--smtpPort', type=int, help="Port of SMTP server", required=False, default=587)
parser.add_argument('--username', type=str, help="outlook username", required=False)
parser.add_argument('--password', type=str, help="outlook password", required=False)
parser.add_argument('--sentFrom', type=str, help="outlook email", required=False,
default="noreply-compass-fa@calix.com")
parser.add_argument('--logfile', help='logger file name', required=False,
default='/var/log/sxadp-api-server/scan_failed_email.log')
parser.add_argument('--s3url',type=str,help="billing s3 URL ",required=False)
args = parser.parse_args()
init_log(args.logfile)
#process(args)
if email_sender(args):
print "done"
#rewrite_record(args) if __name__ == '__main__':
main()
email.py的更多相关文章
- Python遇到ModuleNotFoundError: No module named 'email.mime'; 'email' is not a package问题的处理办法
写Python的时候我们会遇到如下的错误: Traceback (most recent call last): File "F:/exploitation/codes/python/Jet ...
- 新建py文件时取名千万要小心 不要和已有模块重名
这是因为我新建了一个email.py的文件 后来我将文件名rename成了myemail.py没有看改名提示,结果导致所有的对email的import和调用全部改成了对myemail的import和调 ...
- Python 程序员经常犯的 10 个错误
关于PythonPython是一种解释性.面向对象并具有动态语义的高级程序语言.它内建了高级的数据结构,结合了动态类型和动态绑定的优点,这使得... 关于Python Python是一种解释性.面向对 ...
- Python开发最常犯错误总结10种
不管是在学习还是工作过程中,人都会犯错.虽然Python的语法简单.灵活,但也一样存在一些不小的坑,一不小心,初学者和资深Python程序员都有可能会栽跟头.本文是Toptal网站的程序员梳理的10大 ...
- Python程序的常见错误(收集篇)
关于Python Python是一门解释性的,面向对象的,并具有动态语义的高级编程语言.它高级的内置数据结构,结合其动态类型和动态绑定的特性,使得它在快速应用程序开发(Rapid Applicatio ...
- flask 程序结构概括
以此结构为例,这个小项目是<Flask Web开发:基于python的web应用开发实战>第一部分结束后的代码框架 第一层 有app.tests.migrations三个文件夹和confi ...
- Python开发者最常犯的10个错误
Python是一门简单易学的编程语言,语法简洁而清晰,并且拥有丰富和强大的类库.与其它大多数程序设计语言使用大括号不一样 ,它使用缩进来定义语句块. 在平时的工作中,Python开发者很容易犯一些小错 ...
- python web -- flask
Flask是一个简洁的 Python_web 框架. 零. virtualenv 虚拟环境配置. $ easy_install pip $ pip install virtualenv $ virtu ...
- 【Flask】 项目结构说明
项目结构 Flask的一大优势就是其极其轻量化.但是也需要注意到,如果我们要用Flask做一个大项目的话,把所有代码写在一个文件里肯定是不合适的.非常难以维护.但是和Django这种框架又不一样,Fl ...
随机推荐
- 洛谷 题解 P1908 【逆序对】
一开始竟然妄想用\(n^2\)的算法过这题,然而这是不可能的 所以只好写归并排序来求逆序対惹 比如将下面两个区间排序 3 4 7 9 1 5 8 10 首先将右区间的\(1\)取出,放到\(r_k\) ...
- ESP32 - 乐鑫官方Flash烧录工具使用
第一步:打开软件flash_download_tools_v3.6.6.exe 第二步:点击ESP32 DownloadTool,启动我们板子的烧录工具 第三步:按照下图顺序,加载bin_prog目录 ...
- Python--读取数据库
原文地址:https://www.wukong.com/answer/6591280609824342286/?iid=40708017633&app=news_article&sha ...
- xv6解析-- 多处理器操作
xv6可以运行多cpu的计算机上,这个os使用mycpu函数来标识初当前的cpu,使用struct cpu结构体来记录当前的CPU状态.使用cpus这些状态存放于cpus数组中,使用ncpu来标志cp ...
- go struct 的方法1
方法 Go 没有类.然而,仍然可以在结构体类型上定义方法. 方法接收者 出现在 func 关键字和方法名之间的参数中. package main import ( "fmt" ...
- Windows计划任务无法写Log的问题
参照:https://www.cnblogs.com/jonezzz/p/10364153.html 使用WIndows计划任务去执行Exe文件时无法写Log,而Exe双击执行就能写Log,这是由于计 ...
- QCache<key,T> 就定义了一个缓存,其类似于map,好处是QCache自动获得被插入对象的所有权,控制所有对象的costs总和(自动管理对象的生存时间。正经数据是不会用到它的,辅助控制才有可能用到它)
在软件开发中,我们经常需要在内存中存储一些临时数据用于后续相关计算.我们一般把这些数据存储到某个数组里,或者STL中的某个合适的容器中.其实,在Qt中直接为我们提供了一个QCache类专用于这种需求. ...
- 解决django的后台管理界面添加中文内容乱码问题
在使用django migrate功能时,默认数据库的字符集不是utf8. 是latin 1,然后在后台管理model时,不允许有中文字符插入 解决方案: 在使用migrate建库之前先把数据库建立起 ...
- jacascript Math (算数)对象
前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! 实际应用中用的比较多的有:round(); random(); floor(); ceil(); 其次还有 ...
- .Net Core Swagger:Actions require an explicit HttpMethod binding for Swagger 2.0
添加完Swagger包引用后运行报错:Actions require an explicit HttpMethod binding for Swagger 2.0 第一时间想到了父类控制器 没有添加 ...