Commit fdf531ae by Diana Huang

Migrations for LinkedIn.

Clean up common.

Add the ability to dry-run the command without sending e-mail.

Don't save courses sent during a dry run

Switch to EmailMessage for LinkedIn so we can send HTML emails

Update subject copy.

Use correct name for CertificationName

Fix up certificate url information.
parent 469fab58
...@@ -7,7 +7,7 @@ import json ...@@ -7,7 +7,7 @@ import json
import urllib import urllib
from django.conf import settings from django.conf import settings
from django.core.mail import send_mail from django.core.mail import EmailMessage
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.template import Context from django.template import Context
from django.template.loader import get_template from django.template.loader import get_template
...@@ -43,6 +43,13 @@ class Command(BaseCommand): ...@@ -43,6 +43,13 @@ class Command(BaseCommand):
"all users that have earned certificates to date to add their " "all users that have earned certificates to date to add their "
"certificates. Afterwards the default, one email per " "certificates. Afterwards the default, one email per "
"certificate mail form will be used."),) "certificate mail form will be used."),)
option_list = option_list + (
make_option(
'--mock',
action='store_true',
dest='mock_run',
default=False,
help="Run without sending the final e-mails."),)
def __init__(self): def __init__(self):
super(Command, self).__init__() super(Command, self).__init__()
...@@ -50,6 +57,7 @@ class Command(BaseCommand): ...@@ -50,6 +57,7 @@ class Command(BaseCommand):
def handle(self, *args, **options): def handle(self, *args, **options):
whitelist = settings.LINKEDIN_API['EMAIL_WHITELIST'] whitelist = settings.LINKEDIN_API['EMAIL_WHITELIST']
grandfather = options.get('grandfather', False) grandfather = options.get('grandfather', False)
mock_run = options.get('mock_run', False)
accounts = LinkedIn.objects.filter(has_linkedin_account=True) accounts = LinkedIn.objects.filter(has_linkedin_account=True)
for account in accounts: for account in accounts:
user = account.user user = account.user
...@@ -65,8 +73,9 @@ class Command(BaseCommand): ...@@ -65,8 +73,9 @@ class Command(BaseCommand):
if not certificates: if not certificates:
continue continue
if grandfather: if grandfather:
self.send_grandfather_email(user, certificates) self.send_grandfather_email(user, certificates, mock_run)
emailed.extend([cert.course_id for cert in certificates]) if not mock_run:
emailed.extend([cert.course_id for cert in certificates])
else: else:
for certificate in certificates: for certificate in certificates:
self.send_triggered_email(user, certificate) self.send_triggered_email(user, certificate)
...@@ -83,11 +92,11 @@ class Command(BaseCommand): ...@@ -83,11 +92,11 @@ class Command(BaseCommand):
tracking_code = '-'.join([ tracking_code = '-'.join([
'eml', 'eml',
'prof', # the 'product'--no idea what that's supposed to mean 'prof', # the 'product'--no idea what that's supposed to mean
course.org, # Partner's name 'edX', # Partner's name
course.number, # Certificate's name course.number, # Certificate's name
'gf' if grandfather else 'T']) 'gf' if grandfather else 'T'])
query = [ query = [
('pfCertificationName', certificate.name), ('pfCertificationName', course.display_name_with_default),
('pfAuthorityName', settings.PLATFORM_NAME), ('pfAuthorityName', settings.PLATFORM_NAME),
('pfAuthorityId', settings.LINKEDIN_API['COMPANY_ID']), ('pfAuthorityId', settings.LINKEDIN_API['COMPANY_ID']),
('pfCertificationUrl', certificate.download_url), ('pfCertificationUrl', certificate.download_url),
...@@ -99,7 +108,7 @@ class Command(BaseCommand): ...@@ -99,7 +108,7 @@ class Command(BaseCommand):
('force', 'true')] ('force', 'true')]
return 'http://www.linkedin.com/profile/guided?' + urllib.urlencode(query) return 'http://www.linkedin.com/profile/guided?' + urllib.urlencode(query)
def send_grandfather_email(self, user, certificates): def send_grandfather_email(self, user, certificates, mock_run=False):
""" """
Send the 'grandfathered' email informing historical students that they Send the 'grandfathered' email informing historical students that they
may now post their certificates on their LinkedIn profiles. may now post their certificates on their LinkedIn profiles.
...@@ -124,13 +133,14 @@ class Command(BaseCommand): ...@@ -124,13 +133,14 @@ class Command(BaseCommand):
'course_title': course_title, 'course_title': course_title,
'course_image_url': course_img_url, 'course_image_url': course_img_url,
'course_end_date': course_end_date, 'course_end_date': course_end_date,
'linkedin_add_url': self.certificate_url(cert), 'linkedin_add_url': self.certificate_url(cert, True),
}) })
context = {'courses_list': courses_list, 'num_courses': len(courses_list)} context = {'courses_list': courses_list, 'num_courses': len(courses_list)}
body = render_to_string('linkedin/linkedin_email.html', context) body = render_to_string('linkedin/linkedin_email.html', context)
subject = 'Congratulations! Put your certificates on LinkedIn' subject = '{}, Add your Achievements to your LinkedIn Profile'.format(user.profile.name)
self.send_email(user, subject, body) if not mock_run:
self.send_email(user, subject, body)
def send_triggered_email(self, user, certificate): def send_triggered_email(self, user, certificate):
""" """
...@@ -153,4 +163,6 @@ class Command(BaseCommand): ...@@ -153,4 +163,6 @@ class Command(BaseCommand):
""" """
fromaddr = settings.DEFAULT_FROM_EMAIL fromaddr = settings.DEFAULT_FROM_EMAIL
toaddr = '%s <%s>' % (user.profile.name, user.email) toaddr = '%s <%s>' % (user.profile.name, user.email)
send_mail(subject, body, fromaddr, (toaddr,)) msg = EmailMessage(subject, body, fromaddr, (toaddr,))
msg.content_subtype = "html"
msg.send()
...@@ -116,7 +116,23 @@ class MailusersTests(TestCase): ...@@ -116,7 +116,23 @@ class MailusersTests(TestCase):
self.assertEqual( self.assertEqual(
mail.outbox[0].to, ['Fred Flintstone <fred@bedrock.gov>']) mail.outbox[0].to, ['Fred Flintstone <fred@bedrock.gov>'])
self.assertEqual( self.assertEqual(
mail.outbox[0].subject, 'Fred Flintstone, Add your Achievements to your LinkedIn Profile')
self.assertEqual(
mail.outbox[1].to, ['Barney Rubble <barney@bedrock.gov>']) mail.outbox[1].to, ['Barney Rubble <barney@bedrock.gov>'])
self.assertEqual(
mail.outbox[1].subject, 'Barney Rubble, Add your Achievements to your LinkedIn Profile')
def test_mail_users_grandfather_mock(self):
"""
test that we aren't sending anything when in mock_run mode
"""
fut = mailusers.Command().handle
fut(grandfather=True, mock_run=True)
self.assertEqual(
json.loads(self.fred.linkedin.emailed_courses), [])
self.assertEqual(
json.loads(self.barney.linkedin.emailed_courses), [])
self.assertEqual(len(mail.outbox), 0)
def test_mail_users_only_new_courses(self): def test_mail_users_only_new_courses(self):
""" """
......
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'LinkedIn'
db.create_table('linkedin_linkedin', (
('user', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True, primary_key=True)),
('has_linkedin_account', self.gf('django.db.models.fields.NullBooleanField')(default=None, null=True, blank=True)),
('emailed_courses', self.gf('django.db.models.fields.TextField')(default='[]')),
))
db.send_create_signal('linkedin', ['LinkedIn'])
def backwards(self, orm):
# Deleting model 'LinkedIn'
db.delete_table('linkedin_linkedin')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'linkedin.linkedin': {
'Meta': {'object_name': 'LinkedIn'},
'emailed_courses': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
'has_linkedin_account': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
}
}
complete_apps = ['linkedin']
\ No newline at end of file
...@@ -1147,10 +1147,6 @@ GRADES_DOWNLOAD = { ...@@ -1147,10 +1147,6 @@ GRADES_DOWNLOAD = {
##################### LinkedIn ##################### ##################### LinkedIn #####################
INSTALLED_APPS += ('django_openid_auth',) INSTALLED_APPS += ('django_openid_auth',)
LINKEDIN_API = {
'COMPANY_NAME': 'edX',
}
############################ LinkedIn Integration ############################# ############################ LinkedIn Integration #############################
INSTALLED_APPS += ('linkedin',) INSTALLED_APPS += ('linkedin',)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment