Files
stacosys/app/services/processor.py
T

243 lines
7.4 KiB
Python
Raw Normal View History

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
import re
from datetime import datetime
from threading import Thread
from queue import Queue
from jinja2 import Environment, FileSystemLoader
from app.models.site import Site
from app.models.comment import Comment
2015-05-24 19:40:46 +02:00
from app.models.reader import Reader
2015-05-16 18:12:18 +02:00
import requests
import json
import config
logger = logging.getLogger(__name__)
queue = Queue()
proc = None
env = None
class Processor(Thread):
def stop(self):
logger.info("stop requested")
self.is_running = False
def run(self):
self.is_running = True
while self.is_running:
2015-05-16 18:12:18 +02:00
try:
msg = queue.get()
if msg['request'] == 'new_comment':
new_comment(msg['data'])
2015-05-16 20:55:55 +02:00
elif msg['request'] == 'new_mail':
reply_comment_email(msg['data'])
2015-05-24 19:40:46 +02:00
elif msg['request'] == 'unsubscribe':
unsubscribe_reader(msg['data'])
2015-05-16 18:12:18 +02:00
else:
logger.info("throw unknown request " + str(msg))
except:
logger.exception("processing failure")
def new_comment(data):
2015-05-16 18:12:18 +02:00
logger.info('new comment received: %s' % data)
token = data.get('token', '')
url = data.get('url', '')
author_name = data.get('author', '')
author_email = data.get('email', '')
author_site = data.get('site', '')
message = data.get('message', '')
subscribe = data.get('subscribe', '')
# create a new comment row
site = Site.select().where(Site.token == token).get()
if author_site and author_site[:4] != 'http':
author_site = 'http://' + author_site
created = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# add a row to Comment table
comment = Comment(site=site, url=url, author_name=author_name,
2015-05-18 13:23:24 +02:00
author_site=author_site, author_email=author_email,
content=message, created=created, published=None)
2015-05-16 18:12:18 +02:00
comment.save()
# render email body template
comment_list = (
'author: %s' % author_name,
'email: %s' % author_email,
'site: %s' % author_site,
'date: %s' % created,
'url: %s' % url,
'',
'%s' % message,
''
)
comment_text = '\n'.join(comment_list)
2015-05-18 13:23:24 +02:00
email_body = get_template('new_comment').render(
url=url, comment=comment_text)
2015-05-16 18:12:18 +02:00
# send email
2015-05-17 19:26:19 +02:00
subject = '%s: [%d:%s]' % (site.name, comment.id, token)
2015-05-16 18:12:18 +02:00
mail(site.admin_email, subject, email_body)
# Reader subscribes to further comments
2015-05-18 13:23:24 +02:00
if subscribe and author_email:
2015-05-24 19:40:46 +02:00
subscribe_reader(author_email, token, url)
2015-05-16 18:12:18 +02:00
logger.debug("new comment processed ")
2015-05-16 20:55:55 +02:00
def reply_comment_email(data):
2015-05-24 19:40:46 +02:00
from_email = data['from']
2015-05-16 20:55:55 +02:00
subject = data['subject']
message = ''
for part in data['parts']:
if part['content-type'] == 'text/plain':
message = part['content']
break
2015-05-17 19:26:19 +02:00
m = re.search('\[(\d+)\:(\w+)\]', subject)
comment_id = int(m.group(1))
token = m.group(2)
2015-05-16 20:55:55 +02:00
# retrieve site and comment rows
comment = Comment.select().where(Comment.id == comment_id).get()
if comment.site.token != token:
logger.warn('ignore corrupted email')
return
if not message:
logger.warn('ignore empty email')
return
# safe logic: no answer or unknown answer is a go for publishing
if message[:2].upper() == 'NO':
logger.info('discard comment: %d' % comment_id)
comment.delete_instance()
email_body = get_template('drop_comment').render(original=message)
2015-05-24 19:40:46 +02:00
mail(from_email, 'Re: ' + subject, email_body)
2015-05-16 20:55:55 +02:00
else:
2015-05-18 13:23:24 +02:00
# update Comment row
2015-05-16 20:55:55 +02:00
comment.published = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
comment.save()
logger.info('commit comment: %d' % comment_id)
# send approval confirmation email to admin
email_body = get_template('approve_comment').render(original=message)
2015-05-24 19:40:46 +02:00
mail(from_email, 'Re: ' + subject, email_body)
2015-05-16 20:55:55 +02:00
# notify reader once comment is published
2015-05-24 19:40:46 +02:00
reader_email = get_email_metadata(message)
if reader_email:
notify_reader(from_email, reader_email, comment.site.token,
comment.url)
2015-05-16 20:55:55 +02:00
# notify subscribers every time a new comment is published
2015-05-24 19:40:46 +02:00
notify_subscribed_readers(comment.site.token, comment.url)
def get_email_metadata(message):
2015-05-24 19:40:46 +02:00
# retrieve metadata reader email from email body sent by admin
email = ""
m = re.search('email:\s(.+@.+\..+)', message)
if m:
email = m.group(1)
2015-05-24 19:40:46 +02:00
return email
2015-05-24 19:40:46 +02:00
def subscribe_reader(email, token, url):
logger.info('subscribe reader %s to %s [%s]' % (email, url, token))
recorded = Reader.select().join(Site).where(Site.token == token,
Reader.email == email,
Reader.url == url).count()
if recorded:
logger.debug('reader %s is already recorded' % email)
else:
site = Site.select().where(Site.token == token).get()
reader = Reader(site=site, email=email, url=url)
reader.save()
2015-05-24 19:40:46 +02:00
def unsubscribe_reader(data):
token = data.get('token', '')
url = data.get('url', '')
email = data.get('email', '')
logger.info('unsubscribe reader %s from %s (%s)' % (email, url, token))
for reader in Reader.select().join(Site).where(Site.token == token,
Reader.email == email,
Reader.url == url):
reader.delete_instance()
def notify_subscribed_readers(token, url):
logger.info('notify subscribers for %s (%s)' % (url, token))
for reader in Reader.select().join(Site).where(Site.token == token,
Reader.url == url):
to_email = reader.email
logger.info('notify reader %s' % to_email)
unsubscribe_url = '%s?email=%s&token=%s&url=%s' % (
config.UNSUBSCRIBE_URL, to_email, token, reader.url)
email_body = get_template(
'notify_subscriber').render(article_url=reader.url,
unsubscribe_url=unsubscribe_url)
subject = get_template('notify_message').render()
2015-05-24 19:40:46 +02:00
mail(to_email, subject, email_body)
2015-05-24 19:40:46 +02:00
def notify_reader(from_email, to_email, token, url):
logger.info('notify reader: email %s about URL %s' % (to_email, url))
email_body = get_template('notify_reader').render(article_url=url)
subject = get_template('notify_message').render()
2015-05-24 19:40:46 +02:00
mail(to_email, subject, email_body)
2015-05-16 18:12:18 +02:00
def mail(to_email, subject, message):
2015-05-16 18:12:18 +02:00
headers = {'Content-Type': 'application/json; charset=utf-8'}
msg = {
'to': to_email,
'subject': subject,
'content': message
}
r = requests.post(config.MAIL_URL, data=json.dumps(msg), headers=headers)
if r.status_code in (200, 201):
logger.debug('Email for %s posted' % to_email)
else:
logger.warn('Cannot post email for %s' % to_email)
def get_template(name):
2015-05-16 18:12:18 +02:00
return env.get_template(config.LANG + '/' + name + '.tpl')
def enqueue(something):
queue.put(something)
def get_processor():
return proc
def start(template_dir):
global proc, env
# initialize Jinja 2 templating
logger.info("load templates from directory %s" % template_dir)
env = Environment(loader=FileSystemLoader(template_dir))
# start processor thread
proc = Processor()
proc.start()