From 25e0dd950ef119f1bf6b7ab0a54c730f4f9f5922 Mon Sep 17 00:00:00 2001 From: Scott Torborg Date: Wed, 4 Nov 2009 20:53:47 -1000 Subject: [PATCH] added email notifications. requires schema change to add email column to users table. --- bluechips/config/environment.py | 6 ++++ bluechips/controllers/spend.py | 23 +++++++++++---- bluechips/controllers/transfer.py | 20 +++++++++---- bluechips/lib/app_globals.py | 34 ++++++++++++++++++++++ bluechips/model/__init__.py | 3 +- bluechips/templates/emails/expenditure.txt | 14 +++++++++ bluechips/templates/emails/transfer.txt | 9 ++++++ 7 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 bluechips/templates/emails/expenditure.txt create mode 100644 bluechips/templates/emails/transfer.txt diff --git a/bluechips/config/environment.py b/bluechips/config/environment.py index 72a9200..c6ba9f0 100644 --- a/bluechips/config/environment.py +++ b/bluechips/config/environment.py @@ -4,6 +4,7 @@ import os from mako.lookup import TemplateLookup from pylons import config from sqlalchemy import engine_from_config +from mailer import Mailer import bluechips.lib.app_globals as app_globals import bluechips.lib.helpers @@ -42,3 +43,8 @@ def load_environment(global_conf, app_conf): # CONFIGURATION OPTIONS HERE (note: all config options will override # any Pylons config options) + config['pylons.app_globals'].mailer = Mailer(config.get('mailer.host', + '127.0.0.1')) + if 'mailer.user' in config: + config['pylons.app_globals'].mailer.login(config['mailer.user'], + config['mailer.password']) diff --git a/bluechips/controllers/spend.py b/bluechips/controllers/spend.py index 749d730..e309b00 100644 --- a/bluechips/controllers/spend.py +++ b/bluechips/controllers/spend.py @@ -8,7 +8,7 @@ from decimal import Decimal, InvalidOperation from bluechips.lib.base import * -from pylons import request +from pylons import request, app_globals as g from pylons.decorators.rest import dispatch_on from pylons.decorators import validate @@ -16,6 +16,8 @@ from formencode import validators, Schema from formencode.foreach import ForEach from formencode.variabledecode import NestedVariables +from mailer import Message + log = logging.getLogger(__name__) @@ -71,8 +73,10 @@ class SpendController(BaseController): if id is None: e = model.Expenditure() meta.Session.add(e) + op = 'created' else: e = meta.Session.query(model.Expenditure).get(id) + op = 'updated' # Set the fields that were submitted shares = self.form_result.pop('shares') @@ -88,10 +92,17 @@ class SpendController(BaseController): e.split(split_dict) meta.Session.commit() - - if id is None: - h.flash("Expenditure created.") - else: - h.flash('Expenditure updated.') + show = ("Expenditure of %s paid for by %s %s." % + (e.amount, e.spender, op)) + h.flash(show) + + # Send email notification to involved users if they have an email set. + involved_users = set(sp.user for sp in e.splits if sp.share != 0) + involved_users.add(e.spender) + body = render('/emails/expenditure.txt', + extra_vars={'expenditure': e, + 'op': op}) + g.handle_notification(involved_users, show, body) + return h.redirect_to('/') diff --git a/bluechips/controllers/transfer.py b/bluechips/controllers/transfer.py index 0fb9a55..016f508 100644 --- a/bluechips/controllers/transfer.py +++ b/bluechips/controllers/transfer.py @@ -8,11 +8,13 @@ from datetime import date from bluechips.lib.base import * -from pylons import request +from pylons import request, app_globals as g from pylons.decorators import validate from formencode import Schema, validators +from mailer import Message + log = logging.getLogger(__name__) @@ -47,15 +49,21 @@ class TransferController(BaseController): if id is None: t = model.Transfer() meta.Session.add(t) + op = 'created' else: t = meta.Session.query(model.Transfer).get(id) + op = 'updated' update_sar(t, self.form_result) meta.Session.commit() - if id is None: - h.flash('Transfer created.') - else: - h.flash('Transfer updated.') - + show = ('Transfer of %s from %s to %s %s.' % + (t.amount, t.debtor, t.creditor, op)) + h.flash(show) + + # Send email notification to involved users if they have an email set. + body = render('/emails/transfer.txt', extra_vars={'transfer': t, + 'op': op}) + g.handle_notification((t.debtor, t.creditor), show, body) + return h.redirect_to('/') diff --git a/bluechips/lib/app_globals.py b/bluechips/lib/app_globals.py index 342c8cb..016a9e1 100644 --- a/bluechips/lib/app_globals.py +++ b/bluechips/lib/app_globals.py @@ -1,5 +1,13 @@ """The application's Globals object""" +import logging + +from pylons import config, request +from paste.deploy.converters import asbool +from mailer import Message + +log = logging.getLogger(__name__) + class Globals(object): """Globals acts as a container for objects available throughout the life of the application @@ -11,3 +19,29 @@ class Globals(object): variable """ pass + + def send_message(self, msg): + """ + Wrap the call to mailer.send() so that we can do stuff like defer mail + sending, wrap in a test fixture, selectively disable mailing in certain + environments, etc. + """ + if asbool(config.get('testing')) or asbool(config.get('network_free')): + if 'mailer.messages' not in request.environ: + request.environ['mailer.messages'] = [] + request.environ['mailer.messages'].append(msg) + log.info("From: %s\nTo: %s\nSubject: %s\n\n%s", + msg.From, msg.To, msg.Subject, msg.Body) + else: + self.mailer.send(msg) + + def handle_notification(self, users, subject, body): + "Send a notification email." + recipients = [u.email for u in users if u.email is not None] + if len(recipients) > 0: + msg = Message(From=config.get('mailer.from', + 'root@localhost'), + To=recipients) + msg.Subject = "BlueChips: %s" % subject + msg.Body = body + self.send_message(msg) diff --git a/bluechips/model/__init__.py b/bluechips/model/__init__.py index ba5404f..8efaec6 100644 --- a/bluechips/model/__init__.py +++ b/bluechips/model/__init__.py @@ -27,7 +27,8 @@ users = sa.Table('users', meta.metadata, sa.Column('id', sa.types.Integer, primary_key=True), sa.Column('username', sa.types.Unicode(32), nullable=False), sa.Column('name', sa.types.Unicode(64)), - sa.Column('resident', sa.types.Boolean, default=True) + sa.Column('resident', sa.types.Boolean, default=True), + sa.Column('email', sa.types.Unicode(64)) ) expenditures = sa.Table('expenditures', meta.metadata, diff --git a/bluechips/templates/emails/expenditure.txt b/bluechips/templates/emails/expenditure.txt new file mode 100644 index 0000000..bca63ff --- /dev/null +++ b/bluechips/templates/emails/expenditure.txt @@ -0,0 +1,14 @@ +The following expenditure was ${op}: + +${expenditure.amount} paid for by ${expenditure.spender} + +Description: +${expenditure.description} + +The shares of this expenditure are: +% for split in expenditure.splits: +${split.user}: ${split.share} +% endfor + +To view or edit this expenditure, visit: +${h.url_for(controller='spend', action='edit', id=expenditure.id, qualified=True)} diff --git a/bluechips/templates/emails/transfer.txt b/bluechips/templates/emails/transfer.txt new file mode 100644 index 0000000..6fffa93 --- /dev/null +++ b/bluechips/templates/emails/transfer.txt @@ -0,0 +1,9 @@ +The following transfer was ${op}: + +${transfer.amount} from ${transfer.debtor} to ${transfer.creditor} + +Description: +${transfer.description} + +To view or edit this transfer, visit: +${h.url_for(controller='transfer', action='edit', id=transfer.id, qualified=True)} -- 2.45.2