Added feature to send emails in a background-task
parent
4d6299f0d5
commit
b0cbd0a37a
@ -0,0 +1,236 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import smtplib
|
||||
import threading
|
||||
from datetime import datetime
|
||||
import logging
|
||||
import time
|
||||
import socket
|
||||
import sys
|
||||
from email.generator import Generator
|
||||
import web
|
||||
from flask_babel import gettext as _
|
||||
# from babel.dates import format_datetime
|
||||
import re
|
||||
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except ImportError as e:
|
||||
from io import StringIO
|
||||
|
||||
chunksize = 8192
|
||||
|
||||
STAT_WAITING = 0
|
||||
STAT_FAIL = 1
|
||||
STAT_STARTED = 2
|
||||
STAT_FINISH_SUCCESS = 3
|
||||
|
||||
|
||||
class email(smtplib.SMTP):
|
||||
|
||||
transferSize = 0
|
||||
progress = 0
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
smtplib.SMTP.__init__(self, *args, **kwargs)
|
||||
|
||||
def data(self, msg):
|
||||
self.transferSize = len(msg)
|
||||
(code, resp) = smtplib.SMTP.data(self, msg)
|
||||
self.progress = 0
|
||||
return (code, resp)
|
||||
|
||||
def send(self, str):
|
||||
"""Send `str' to the server."""
|
||||
if self.debuglevel > 0:
|
||||
print>> sys.stderr, 'send:', repr(str)
|
||||
if hasattr(self, 'sock') and self.sock:
|
||||
try:
|
||||
if self.transferSize:
|
||||
lock=threading.Lock()
|
||||
lock.acquire()
|
||||
self.transferSize = len(str)
|
||||
lock.release()
|
||||
for i in range(0, self.transferSize, chunksize):
|
||||
self.sock.send(str[i:i+chunksize])
|
||||
lock.acquire()
|
||||
self.progress = i
|
||||
lock.release()
|
||||
else:
|
||||
self.sock.sendall(str)
|
||||
except socket.error:
|
||||
self.close()
|
||||
raise smtplib.SMTPServerDisconnected('Server not connected')
|
||||
else:
|
||||
raise smtplib.SMTPServerDisconnected('please run connect() first')
|
||||
|
||||
def getTransferStatus(self):
|
||||
if self.transferSize:
|
||||
lock2 = threading.Lock()
|
||||
lock2.acquire()
|
||||
value = round(float(self.progress) / float(self.transferSize),2)*100
|
||||
lock2.release()
|
||||
return str(value) + ' %'
|
||||
else:
|
||||
return "100 %"
|
||||
|
||||
class email_SSL(email):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
smtplib.SMTP_SSL.__init__(self, *args, **kwargs)
|
||||
|
||||
|
||||
class EMailThread(threading.Thread):
|
||||
|
||||
def __init__(self):
|
||||
threading.Thread.__init__(self)
|
||||
self.status = 0
|
||||
self.current = 0
|
||||
self.last = 0
|
||||
self.queue=list()
|
||||
self.UIqueue = list()
|
||||
self.asyncSMTP=None
|
||||
|
||||
def run(self):
|
||||
while 1:
|
||||
doLock = threading.Lock()
|
||||
doLock.acquire()
|
||||
if self.current != self.last:
|
||||
doLock.release()
|
||||
self.send_raw_email()
|
||||
self.current += 1
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
def get_send_status(self):
|
||||
if self.asyncSMTP:
|
||||
return self.asyncSMTP.getTransferStatus()
|
||||
else:
|
||||
return "0 %"
|
||||
|
||||
def delete_completed_tasks(self):
|
||||
# muss gelockt werden
|
||||
for index, task in reversed(list(enumerate(self.UIqueue))):
|
||||
if task['progress'] == "100 %":
|
||||
# delete tasks
|
||||
self.queue.pop(index)
|
||||
self.UIqueue.pop(index)
|
||||
# if we are deleting entries before the current index, adjust the index
|
||||
# if self.current >= index:
|
||||
self.current -= 1
|
||||
self.last = len(self.queue)
|
||||
|
||||
def get_taskstatus(self):
|
||||
if self.current < len(self.queue):
|
||||
if self.queue[self.current]['status'] == STAT_STARTED:
|
||||
self.UIqueue[self.current]['progress'] = self.get_send_status()
|
||||
self.UIqueue[self.current]['runtime'] = self._formatRuntime(
|
||||
datetime.now() - self.queue[self.current]['starttime'])
|
||||
|
||||
return self.UIqueue
|
||||
|
||||
def add_email(self, data, settings, recipient, user_name):
|
||||
# if more than 50 entries in the list, clean the list
|
||||
addLock = threading.Lock()
|
||||
addLock.acquire()
|
||||
if self.last >= 3:
|
||||
self.delete_completed_tasks()
|
||||
# progress, runtime, and status = 0
|
||||
self.queue.append({'data':data, 'settings':settings, 'recipent':recipient, 'starttime': 0,
|
||||
'status': STAT_WAITING})
|
||||
self.UIqueue.append({'user': user_name, 'formStarttime': '', 'progress': " 0 %", 'type': 'E-Mail',
|
||||
'runtime': '0 s', 'status': _('Waiting') })
|
||||
# access issue
|
||||
self.last=len(self.queue)
|
||||
addLock.release()
|
||||
|
||||
def send_raw_email(self):
|
||||
obj=self.queue[self.current]
|
||||
# settings = ub.get_mail_settings()
|
||||
|
||||
obj['data']['From'] = obj['settings']["mail_from"]
|
||||
obj['data']['To'] = obj['recipent']
|
||||
|
||||
use_ssl = int(obj['settings'].get('mail_use_ssl', 0))
|
||||
|
||||
# convert MIME message to string
|
||||
fp = StringIO()
|
||||
gen = Generator(fp, mangle_from_=False)
|
||||
gen.flatten(obj['data'])
|
||||
obj['data'] = fp.getvalue()
|
||||
|
||||
# send email
|
||||
try:
|
||||
timeout = 600 # set timeout to 5mins
|
||||
|
||||
org_stderr = sys.stderr
|
||||
#org_stderr2 = smtplib.stderr
|
||||
sys.stderr = StderrLogger()
|
||||
#smtplib.stderr = StderrLogger()
|
||||
|
||||
self.queue[self.current]['status'] = STAT_STARTED
|
||||
self.UIqueue[self.current]['status'] = _('Started')
|
||||
self.queue[self.current]['starttime'] = datetime.now()
|
||||
self.UIqueue[self.current]['formStarttime'] = self.queue[self.current]['starttime']
|
||||
|
||||
|
||||
if use_ssl == 2:
|
||||
self.asyncSMTP = email_SSL(obj['settings']["mail_server"], obj['settings']["mail_port"], timeout)
|
||||
else:
|
||||
self.asyncSMTP = email(obj['settings']["mail_server"], obj['settings']["mail_port"], timeout)
|
||||
|
||||
# link to logginglevel
|
||||
if web.ub.config.config_log_level != logging.DEBUG:
|
||||
self.asyncSMTP.set_debuglevel(0)
|
||||
else:
|
||||
self.asyncSMTP.set_debuglevel(1)
|
||||
if use_ssl == 1:
|
||||
self.asyncSMTP.starttls()
|
||||
if obj['settings']["mail_password"]:
|
||||
self.asyncSMTP.login(str(obj['settings']["mail_login"]), str(obj['settings']["mail_password"]))
|
||||
self.asyncSMTP.sendmail(obj['settings']["mail_from"], obj['recipent'], obj['data'])
|
||||
self.asyncSMTP.quit()
|
||||
self.queue[self.current]['status'] = STAT_FINISH_SUCCESS
|
||||
self.UIqueue[self.current]['status'] = _('Finished')
|
||||
self.UIqueue[self.current]['progress'] = "100 %"
|
||||
self.UIqueue[self.current]['runtime'] = self._formatRuntime(
|
||||
datetime.now() - self.queue[self.current]['starttime'])
|
||||
|
||||
sys.stderr = org_stderr
|
||||
#smtplib.stderr = org_stderr2
|
||||
|
||||
except (socket.error, smtplib.SMTPRecipientsRefused, smtplib.SMTPException) as e:
|
||||
self.queue[self.current]['status'] = STAT_FAIL
|
||||
self.UIqueue[self.current]['status'] = _('Failed')
|
||||
self.UIqueue[self.current]['progress'] = "100 %"
|
||||
self.UIqueue[self.current]['runtime'] = self._formatRuntime(
|
||||
datetime.now() - self.queue[self.current]['starttime'])
|
||||
web.app.logger.error(e)
|
||||
return None
|
||||
|
||||
def _formatRuntime(self, runtime):
|
||||
val = re.split('\:|\.', str(runtime))[0:3]
|
||||
erg = list()
|
||||
for v in val:
|
||||
if int(v) > 0:
|
||||
erg.append(v)
|
||||
retVal = (':'.join(erg)).lstrip('0') + ' s'
|
||||
if retVal == ' s':
|
||||
retVal = '0 s'
|
||||
return retVal
|
||||
|
||||
class StderrLogger(object):
|
||||
|
||||
buffer = ''
|
||||
|
||||
def __init__(self):
|
||||
self.logger = web.app.logger
|
||||
|
||||
def write(self, message):
|
||||
if message == '\n':
|
||||
self.logger.debug(self.buffer)
|
||||
print self.buffer
|
||||
self.buffer = ''
|
||||
else:
|
||||
self.buffer += message
|
@ -0,0 +1,48 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block header %}
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.8.1/bootstrap-table.css">
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
<div class="discover">
|
||||
<h2>{{_('Tasks list')}}</h2>
|
||||
<table class="table table-no-bordered" id="table" data-url="{{'/ajax/emailstat'}}">
|
||||
<thead>
|
||||
<tr>
|
||||
{% if g.user.role_admin() %}
|
||||
<th data-field="user">{{_('User')}}</th>
|
||||
{% endif %}
|
||||
<th data-field="type">{{_('Task')}}</th>
|
||||
<th data-field="status">{{_('Status')}}</th>
|
||||
<th data-field="progress">{{_('Progress')}}</th>
|
||||
<th data-field="runtime">{{_('Runtime')}}</th>
|
||||
<th data-field="starttime">{{_('Starttime')}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<!--div class="btn btn-default" id="tasks_delete">{{_('Delete finished tasks')}}</div>
|
||||
<div class="btn btn-default" id="tasks_hide">{{_('Hide all tasks')}}</div-->
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block js %}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.8.1/bootstrap-table.js"></script>
|
||||
<script>
|
||||
$('#table').bootstrapTable({
|
||||
formatNoMatches: function () {
|
||||
return '';
|
||||
},
|
||||
striped: true
|
||||
});
|
||||
setInterval(function() {
|
||||
$.ajax({
|
||||
method:"get",
|
||||
url: "{{'/ajax/emailstat'}}",
|
||||
async: true,
|
||||
timeout: 900,
|
||||
success:function(data){
|
||||
$('#table').bootstrapTable("load", data);
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
Loading…
Reference in New Issue