Permalink
Browse files

initial commit of code

  • Loading branch information...
0 parents commit 4a26f3891930251e3efa1522c621de79901a8a67 @brosner brosner committed Jun 29, 2011
Showing with 273 additions and 0 deletions.
  1. +8 −0 README.rst
  2. 0 mailout/__init__.py
  3. +11 −0 mailout/admin.py
  4. +32 −0 mailout/email_lists.py
  5. +9 −0 mailout/forms.py
  6. +64 −0 mailout/models.py
  7. +12 −0 mailout/urls.py
  8. +114 −0 mailout/views.py
  9. +23 −0 setup.py
@@ -0,0 +1,8 @@
+mailout README
+==============
+
+mailout was originally written for DjangoCon 2010 and PyCon 2011 then
+refactored for Gondor.io. This open-source version has been slightly
+refactored to work more generically.
+
+More README soon.
No changes.
@@ -0,0 +1,11 @@
+from django.contrib import admin
+
+from mailout.models import EmailTemplate, Campaign, CampaignLog
+
+
+admin.site.register(EmailTemplate)
+admin.site.register(Campaign)
+admin.site.register(CampaignLog,
+ list_display = ["pk", "campaign", "email", "timestamp"],
+ list_filter = ["campaign"]
+)
@@ -0,0 +1,32 @@
+from django.conf import settings
+from django.core.exceptions import ImproperlyConfigured
+from django.utils.importlib import import_module
+
+
+class EmailListDict(object):
+
+ def _load(self):
+ if hasattr(self, "_lists"):
+ return
+ if not hasattr(settings, "MAILOUT_MODULES"):
+ raise ImproperlyConfigured("You must define 'MAILOUT_MODULES' in settings")
+ self._lists = {}
+ for mp in settings.MAILOUT_MODULES:
+ i = mp.rfind(".")
+ path, module = path[:i], path[i+1:]
+ m = import_module(mp)
+ self._lists[module] = {
+ "list": m.email_list,
+ "results": getattr(m, "email_list_results", None),
+ }
+
+ def __getitem__(self, key):
+ self._load()
+ return self._lists[key]
+
+ def keys(self):
+ self._load()
+ return self._lists.keys()
+
+
+email_lists = EmailListDict()
@@ -0,0 +1,9 @@
+from django import forms
+
+from mailout.models import Campaign
+
+
+class CampaignCreateForm(forms.ModelForm):
+ class Meta:
+ model = Campaign
+ exclude = ["created", "sent"]
@@ -0,0 +1,64 @@
+import datetime
+
+from django.db import models
+from django.template import Template, Context
+
+from mailout.email_lists import email_lists
+
+
+class EmailTemplate(models.Model):
+
+ label = models.CharField(max_length=100)
+ subject = models.TextField()
+ body = models.TextField()
+
+ def __unicode__(self):
+ return self.label
+
+ def render_subject(self, ctx=None):
+ return self.render(self.subject, ctx)
+
+ def render_body(self, ctx=None):
+ return self.render(self.body, ctx)
+
+ def render(self, content, ctx=None):
+ """
+ Render the template data through the Django templating engine.
+ """
+ if ctx is None:
+ ctx = {}
+ t = Template(content)
+ return t.render(Context(ctx))
+
+
+class Campaign(models.Model):
+
+ from_address = models.CharField(max_length=150)
+ email_template = models.ForeignKey(EmailTemplate)
+ email_list = models.CharField(max_length=50)
+ created = models.DateTimeField(default=datetime.datetime.now)
+ sent = models.DateTimeField(null=True)
+
+ def __iter__(self):
+ return iter(email_lists[self.email_list]["list"]())
+
+ def results(self):
+ result_func = email_lists[self.email_list]["results"]
+ if result_func is None:
+ raise NotImplementedError("no results function found for '%s'" % self.email_list)
+ return iter(result_func())
+
+ def result_counts(self):
+ if not hasattr(self, "_result_counts"):
+ counts = len([x for x in self.results() if x[1]]), len(list(self.results()))
+ self._result_counts = counts
+ else:
+ counts = self._result_counts
+ return counts
+
+
+class CampaignLog(models.Model):
+
+ campaign = models.ForeignKey(Campaign)
+ email = models.EmailField()
+ timestamp = models.DateTimeField(default=datetime.datetime.now)
@@ -0,0 +1,12 @@
+from django.conf.urls.defaults import *
+
+
+urlpatterns = patterns("",
+ url(r"^$", "mailout.views.dashboard", name="user_mailer_dashboard"),
+ url(r"^campaign/create/$", "mailout.views.campaign_create", name="campaign_create"),
+ url(r"^campaign/(\d+)/review/$", "mailout.views.campaign_review", name="campaign_review"),
+ url(r"^campaign/(\d+)/email_preview/(.+)/$", "mailout.views.campaign_email_preview", name="campaign_email_preview"),
+ url(r"^campaign/(\d+)/submit/$", "mailout.views.campaign_submit", name="campaign_submit"),
+ url(r"^email_list/(\w+)/", "mailout.views.email_list_detail", name="email_list_detail"),
+ url(r"^campaign/(\d+)/", "mailout.views.campaign_detail", name="campaign_detail"),
+)
@@ -0,0 +1,114 @@
+import datetime
+
+from django.core.mail import send_mass_mail
+from django.conf import settings
+from django.http import Http404, HttpResponseNotAllowed
+from django.shortcuts import get_object_or_404, render_to_response, redirect, render
+from django.template import RequestContext
+
+from django.contrib.admin.views.decorators import staff_member_required
+
+from mailout.email_lists import email_lists
+from mailout.forms import CampaignCreateForm
+from mailout.models import Campaign, CampaignLog, EmailTemplate
+
+
+@staff_member_required
+def dashboard(request):
+ ctx = {
+ "email_lists": email_lists.keys(),
+ }
+ return render(request, "user_mailer/dashboard.html", ctx)
+
+
+@staff_member_required
+def campaign_create(request):
+ if request.method == "POST":
+ form = CampaignCreateForm(request.POST)
+ if form.is_valid():
+ campaign = form.save()
+ return redirect("campaign_review", campaign.pk)
+ else:
+ initial = {"from_address": settings.CONTACT_EMAIL}
+ if "email_list" in request.GET:
+ initial["email_list"] = request.GET["email_list"]
+ form = CampaignCreateForm(initial=initial)
+ ctx = {
+ "form": form,
+ }
+ ctx = RequestContext(request, ctx)
+ return render_to_response("user_mailer/campaign_create.html", ctx)
+
+
+@staff_member_required
+def campaign_review(request, pk):
+ campaign = get_object_or_404(Campaign, pk=pk)
+ ctx = {
+ "campaign": campaign,
+ }
+ ctx = RequestContext(request, ctx)
+ return render_to_response("user_mailer/campaign_review.html", ctx)
+
+
+@staff_member_required
+def campaign_submit(request, pk):
+ if request.method != "POST":
+ return HttpResponseNotAllowed(["POST"])
+ campaigns = Campaign.objects.select_related("email_template")
+ campaign = get_object_or_404(campaigns, pk=pk)
+ messages = []
+ for email, email_ctx in campaign:
+ messages.append((
+ campaign.email_template.render_subject(email_ctx),
+ campaign.email_template.render_body(email_ctx),
+ campaign.from_address,
+ [email],
+ ))
+ CampaignLog.objects.create(campaign=campaign, email=email)
+ send_mass_mail(messages)
+ campaign.sent = datetime.datetime.now()
+ campaign.save()
+ return redirect("campaign_review", campaign.pk)
+
+
+@staff_member_required
+def campaign_email_preview(request, pk, email):
+ campaigns = Campaign.objects.select_related("email_template")
+ campaign = get_object_or_404(campaigns, pk=pk)
+ email_ctx = {}
+ try:
+ email_ctx = dict(campaign)[email]
+ except KeyError:
+ raise Http404("Email not found in campaign")
+ ctx = {
+ "campaign": campaign,
+ "subject": campaign.email_template.render_subject(email_ctx),
+ "email": email,
+ "from_address": campaign.from_address,
+ "body": campaign.email_template.render_body(email_ctx),
+ }
+ ctx = RequestContext(request, ctx)
+ return render_to_response("user_mailer/_campaign_email_preview.html", ctx)
+
+
+@staff_member_required
+def email_list_detail(request, label):
+ try:
+ email_list_func = email_lists[label]["list"]
+ except KeyError:
+ raise Http404
+ ctx = {
+ "email_list_name": label,
+ "email_list": list(email_list_func()),
+ "campaigns": Campaign.objects.filter(email_list=label),
+ }
+ return render(request, "user_mailer/email_list_detail.html", ctx)
+
+
+@staff_member_required
+def campaign_detail(request, pk):
+ campaign = get_object_or_404(Campaign, pk=pk)
+ ctx = {
+ "campaign": campaign,
+ }
+ return render(request, "user_mailer/campaign_detail.html", ctx)
@@ -0,0 +1,23 @@
+from setuptools import setup, find_packages
+
+
+setup(
+ name = "mailout",
+ version = "1.0a1.dev1",
+ author = "Eldarion",
+ author_email = "development@eldarion.com",
+ description = "templated mail campaigns based on customizable queries",
+ long_description = open("README.rst").read(),
+ license = "BSD",
+ url = "http://github.com/eldarion/mailout",
+ packages = find_packages(),
+ classifiers = [
+ "Development Status :: 3 - Alpha",
+ "Environment :: Web Environment",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: BSD License",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Framework :: Django",
+ ]
+)

0 comments on commit 4a26f38

Please sign in to comment.