Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

create news app, basic HTTP API for subscribing

  • Loading branch information...
commit 9c729a5e88d0f2c27a7043cc3d40cfd5e5a9eacf 1 parent d77bb65
James Long jlongster authored
0  apps/news/__init__.py
No changes.
3  apps/news/models.py
... ... @@ -0,0 +1,3 @@
  1 +from django.db import models
  2 +
  3 +# Create your models here.
13 apps/news/news.html
... ... @@ -0,0 +1,13 @@
  1 +<!DOCTYPE html>
  2 +<html>
  3 + <head>
  4 + </head>
  5 + <body>
  6 + <form action="http://localhost:8000/news/subscribe/" method="post">
  7 + <div>
  8 + Email: <input type="text" name="email" />
  9 + </div>
  10 + <input type="submit" />
  11 + </form>
  12 + </body>
  13 +</html>
102 apps/news/responsys.py
... ... @@ -0,0 +1,102 @@
  1 +from functools import wraps
  2 +
  3 +from suds import WebFault
  4 +from suds.client import Client
  5 +
  6 +
  7 +class UnathorizedException(Exception):
  8 + pass
  9 +
  10 +
  11 +def logged_in(f):
  12 + """ Decorator to ensure an authenticated session with Responsys
  13 + before calling a function """
  14 +
  15 + @wraps(f)
  16 + def wrapper(inst, *args, **kwargs):
  17 + if not inst.session:
  18 + raise UnauthorizedException("Not logged in to Responsys, "
  19 + "must call login()")
  20 + f(inst, *args, **kwargs)
  21 + return wrapper
  22 +
  23 +
  24 +class Responsys(object):
  25 + WSDL_URL = 'https://ws2.responsys.net/webservices/wsdl/ResponsysWS_Level1.wsdl'
  26 +
  27 + def __init__(self):
  28 + self.client = None
  29 + self.session = None
  30 +
  31 + def login(self, user, pass_):
  32 + """ Login and create a Responsys session, returns False on
  33 + failure """
  34 +
  35 + if not self.client:
  36 + self.client = Client(self.__class__.WSDL_URL)
  37 + elif self.session:
  38 + self.logout()
  39 +
  40 + try:
  41 + res = self.client.service.login(user, pass_)
  42 + except WebFault, e:
  43 + return False
  44 +
  45 + self.session = res['sessionId']
  46 +
  47 + # Set auth token for all requests
  48 + header = self.client.factory.create('SessionHeader')
  49 + header.sessionId = self.session
  50 + self.client.set_options(soapheaders=header)
  51 +
  52 + @logged_in
  53 + def logout(self):
  54 + """ Logout and expire the current Responsys session """
  55 +
  56 + self.client.service.logout()
  57 + self.session = None
  58 +
  59 + @logged_in
  60 + def merge_list_members(self, folder, list_, fields, records):
  61 + """
  62 + Add data to the list located at <folder>/<list_> in
  63 + Responsys.
  64 +
  65 + <fields> is an array of field names
  66 + <records> is a single record or an array of records to insert
  67 + (record = array). If the email already exists, its data will
  68 + be updated
  69 + """
  70 +
  71 + client = self.client
  72 + records = [records] if isinstance(records[0], basestring) else records
  73 +
  74 + def make_record(record):
  75 + data = client.factory.create('Record')
  76 + data.fieldValues = record
  77 + return data
  78 +
  79 + target = client.factory.create('InteractObject')
  80 + target.folderName = folder
  81 + target.objectName = list_
  82 +
  83 + data = client.factory.create('RecordData')
  84 + data.fieldNames = fields
  85 + data.records = [make_record(r) for r in records]
  86 +
  87 + # Configure the action to update the data when it matches on
  88 + # the email address field, otherwise insert a new entry, and
  89 + # default opt in
  90 + rule = client.factory.create('ListMergeRule')
  91 + rule.insertOnNoMatch = True
  92 + rule.updateOnMatch = 'REPLACE_ALL'
  93 + rule.matchColumnName1 = 'EMAIL_ADDRESS_'
  94 + rule.matchOperator = 'NONE'
  95 + rule.optinValue = 'I'
  96 + rule.optoutValue = 'O'
  97 + rule.htmlValue = 'H'
  98 + rule.textValue = 'T'
  99 + rule.rejectRecordIfChannelEmpty = 'E'
  100 + rule.defaultPermissionStatus = 'OPTIN'
  101 +
  102 + self.client.service.mergeListMembers(target, data, rule)
23 apps/news/tests.py
... ... @@ -0,0 +1,23 @@
  1 +"""
  2 +This file demonstrates two different styles of tests (one doctest and one
  3 +unittest). These will both pass when you run "manage.py test".
  4 +
  5 +Replace these with more appropriate tests for your application.
  6 +"""
  7 +
  8 +from django.test import TestCase
  9 +
  10 +class SimpleTest(TestCase):
  11 + def test_basic_addition(self):
  12 + """
  13 + Tests that 1 + 1 always equals 2.
  14 + """
  15 + self.failUnlessEqual(1 + 1, 2)
  16 +
  17 +__test__ = {"doctest": """
  18 +Another way to test that 1 + 1 is equal to 2.
  19 +
  20 +>>> 1 + 1 == 2
  21 +True
  22 +"""}
  23 +
6 apps/news/urls.py
... ... @@ -0,0 +1,6 @@
  1 +from django.conf.urls.defaults import *
  2 +from views import subscribe
  3 +
  4 +urlpatterns = patterns('',
  5 + url('^subscribe/$', subscribe),
  6 +)
53 apps/news/views.py
... ... @@ -0,0 +1,53 @@
  1 +from datetime import date
  2 +import urlparse
  3 +
  4 +from django.http import (HttpResponse, HttpResponseRedirect,
  5 + HttpResponseBadRequest, HttpResponseForbidden)
  6 +from django.views.decorators.csrf import csrf_exempt
  7 +from django.conf import settings
  8 +
  9 +from responsys import Responsys
  10 +
  11 +
  12 +@csrf_exempt
  13 +def subscribe(request):
  14 + if request.method == 'POST':
  15 + data = request.POST
  16 +
  17 + # validate parameters
  18 + for name in ['email']:
  19 + if name not in data:
  20 + return HttpResponseBadRequest('%s is missing' % name)
  21 +
  22 + record = {'EMAIL_ADDRESS_': data['email'],
  23 + 'EMAIL_FORMAT_': data.get('format', 'H'),
  24 + 'COUNTRY_': data.get('country', 'en'),
  25 + 'MOZILLA_AND_YOU_FLG': 'Y',
  26 + 'MOZILLA_AND_YOU_DATE': date.today().strftime('%Y-%m-%d')}
  27 +
  28 + # do the subscription
  29 + rs = Responsys()
  30 + rs.login(settings.RESPONSYS_USER, settings.RESPONSYS_PASS)
  31 + rs.merge_list_members(settings.RESPONSYS_FOLDER,
  32 + settings.RESPONSYS_LIST,
  33 + record.keys(),
  34 + record.values())
  35 + rs.logout()
  36 +
  37 + # redirect back to the page, if any
  38 + next = data.get('next', request.META.get('HTTP_REFERER', None))
  39 + if next:
  40 + parts = urlparse.urlsplit(next)
  41 + query = '%s%s%s' % (parts.query,
  42 + '&' if parts.query else '',
  43 + 'subscribed')
  44 + next = urlparse.urlunsplit((parts.scheme,
  45 + parts.netloc,
  46 + parts.path,
  47 + query,
  48 + parts.fragment))
  49 + return HttpResponseRedirect(next)
  50 + return HttpResponse('Success! You have been subscribed.')
  51 +
  52 + return HttpResponseBadRequest('GET is not supported')
  53 +
35 settings.py
@@ -29,39 +29,17 @@
29 29 }
30 30 }
31 31
32   -# Local time zone for this installation. Choices can be found here:
33   -# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
34   -# although not all choices may be available on all operating systems.
35   -# If running in a Windows environment this must be set to the same as your
36   -# system time zone.
37 32 TIME_ZONE = 'America/Chicago'
38   -
39   -# Language code for this installation. All choices can be found here:
40   -# http://www.i18nguy.com/unicode/language-identifiers.html
41 33 LANGUAGE_CODE = 'en-us'
42   -
43 34 SITE_ID = 1
44   -
45   -# If you set this to False, Django will make some optimizations so as not
46   -# to load the internationalization machinery.
47 35 USE_I18N = True
48 36
49   -# Absolute path to the directory that holds media.
50   -# Example: "/home/media/media.lawrence.com/"
51 37 MEDIA_ROOT = path('media')
52   -
53   -# URL that handles the media served from MEDIA_ROOT. Make sure to use a
54   -# trailing slash if there is a path component (optional in other cases).
55   -# Examples: "http://media.lawrence.com", "http://example.com/media/"
56 38 MEDIA_URL = '/media/'
57   -
58   -# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
59   -# trailing slash.
60   -# Examples: "http://foo.com/media/", "/media/".
61 39 ADMIN_MEDIA_PREFIX = '/admin-media/'
62 40
63 41 # Make this unique, and don't share it with anybody.
64   -SECRET_KEY = ''
  42 +SECRET_KEY = '0D8AE44F-5714-40EF-9AC8-4AC6EB556161'
65 43
66 44 # List of callables that know how to import templates from various sources.
67 45 TEMPLATE_LOADERS = (
@@ -95,6 +73,7 @@
95 73 'nagios',
96 74 'subscriptions',
97 75 'vars',
  76 + 'news',
98 77
99 78 'fixture_magic',
100 79 'piston',
@@ -129,7 +108,7 @@
129 108 DEFAULT_FROM_NAME = 'Mozilla'
130 109
131 110 # Logging
132   -LOG_LEVEL = logging.DEBUG
  111 +LOG_LEVEL = logging.INFO
133 112 HAS_SYSLOG = True # syslog is used if HAS_SYSLOG and NOT DEBUG.
134 113 SYSLOG_TAG = "http_app_basket"
135 114 # See PEP 391 and log_settings.py for formatting help. Each section of LOGGING
@@ -150,11 +129,8 @@
150 129 }
151 130
152 131 EMAIL_BACKEND = 'mysmtp.EmailBackend'
153   -
154 132 EMAIL_BACKLOG_TOLERANCE = 200
155   -
156 133 SYNC_UNSUBSCRIBE_LIMIT = 1000
157   -
158 134 LDAP_TIMEOUT = 2
159 135
160 136 def JINJA_CONFIG():
@@ -164,3 +140,8 @@ def JINJA_CONFIG():
164 140 'jinja2.ext.with_', 'jinja2.ext.loopcontrols'],
165 141 'finalize': lambda x: x if x is not None else ''}
166 142 return config
  143 +
  144 +RESPONSYS_USER = 'MOZILLA_API'
  145 +RESPONSYS_PASS = ''
  146 +RESPONSYS_FOLDER = '!MasterData'
  147 +RESPONSYS_LIST = 'TEST_CONTACTS_LIST'
1  urls.py
@@ -9,6 +9,7 @@
9 9 (r'^admin/doc/', include('django.contrib.admindocs.urls')),
10 10 (r'^admin/', include(admin.site.urls)),
11 11 ('^nagios/', include('nagios.urls')),
  12 + (r'^news/', include('news.urls'))
12 13 )
13 14
14 15 if settings.DEBUG:

0 comments on commit 9c729a5

Please sign in to comment.
Something went wrong with that request. Please try again.