Commit b3bc4023 by Sarina Canelake

Refactor html->plaintext conversion (for bulk email) into separate library

parent f98d6764
"""Provides a function to convert html to plaintext."""
from subprocess import Popen, PIPE
def html_to_text(html_message):
"""
Converts an html message to plaintext.
Currently uses lynx in a subprocess; should be refactored to
use something more pythonic.
"""
process = Popen(
['lynx', '-stdin', '-display_charset=UTF-8', '-assume_charset=UTF-8', '-dump'],
stdin=PIPE,
stdout=PIPE
)
# use lynx to get plaintext
(plaintext, err_from_stderr) = process.communicate(
input=html_message.encode('utf-8')
)
if err_from_stderr:
log.info(err_from_stderr)
return plaintext
......@@ -14,6 +14,7 @@ class Email(models.Model):
hash = models.CharField(max_length=128, db_index=True)
subject = models.CharField(max_length=128, blank=True)
html_message = models.TextField(null=True, blank=True)
text_message = models.TextField(null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
......
......@@ -8,7 +8,6 @@ import re
import time
from smtplib import SMTPServerDisconnected, SMTPDataError, SMTPConnectError
from subprocess import Popen, PIPE
from django.conf import settings
from django.contrib.auth.models import User, Group
......@@ -98,15 +97,9 @@ def course_email(hash_for_msg, to_list, course_title, course_url, throttle=False
subject = "[" + course_title + "] " + msg.subject
process = Popen(['lynx', '-stdin', '-display_charset=UTF-8', '-assume_charset=UTF-8', '-dump'], stdin=PIPE, stdout=PIPE)
(plaintext, err_from_stderr) = process.communicate(input=msg.html_message.encode('utf-8')) # use lynx to get plaintext
course_title_no_quotes = re.sub(r'"', '', course_title)
from_addr = '"{0}" Course Staff <{1}>'.format(course_title_no_quotes, settings.DEFAULT_BULK_FROM_EMAIL)
if err_from_stderr:
log.info(err_from_stderr)
try:
connection = get_connection()
connection.open()
......@@ -136,14 +129,15 @@ def course_email(hash_for_msg, to_list, course_title, course_url, throttle=False
email_msg = EmailMultiAlternatives(
subject,
plaintext + plain_footer.encode('utf-8'),
msg.text_message + plain_footer.encode('utf-8'),
from_addr,
[email],
connection=connection
)
email_msg.attach_alternative(msg.html_message + html_footer.encode('utf-8'), 'text/html')
if throttle or current_task.request.retries > 0: # throttle if we tried a few times and got the rate limiter
# Throttle if we tried a few times and got the rate limiter
if throttle or current_task.request.retries > 0:
time.sleep(0.2)
try:
......
......@@ -31,6 +31,12 @@ class TestOptoutCourseEmails(ModuleStoreTestCase):
self.client.login(username=self.student.username, password="test")
def tearDown(self):
"""
Undo all patches.
"""
patch.stopall()
def navigate_to_email_view(self):
"""Navigate to the instructor dash's email view"""
# Pull up email view on instructor dashboard
......@@ -54,6 +60,8 @@ class TestOptoutCourseEmails(ModuleStoreTestCase):
Make sure student does not receive course email after opting out.
"""
url = reverse('change_email_settings')
# This is a checkbox, so on the post of opting out (that is, an Un-check of the box),
# the Post that is sent will not contain 'receive_emails'
response = self.client.post(url, {'course_id': self.course.id})
self.assertEquals(json.loads(response.content), {'success': True})
......
......@@ -64,6 +64,12 @@ class TestEmailSendFromDashboard(ModuleStoreTestCase):
selected_email_link = '<a href="#" onclick="goto(\'Email\')" class="selectedmode">Email</a>'
self.assertTrue(selected_email_link in response.content)
def tearDown(self):
"""
Undo all patches.
"""
patch.stopall()
def test_send_to_self(self):
"""
Make sure email send to myself goes to myself.
......@@ -185,7 +191,7 @@ class TestEmailSendFromDashboard(ModuleStoreTestCase):
self.assertIn(
u'ẗëṡẗ ṁëṡṡäġë ḟöṛ äḷḷ イ乇丂イ ᄊ乇丂丂ムg乇 キo尺 ムレレ тэѕт мэѕѕаБэ fоѓ аll',
mail.outbox[0].body.decode('utf-8')
mail.outbox[0].body
)
def test_unicode_students_send_to_all(self):
......
......@@ -40,8 +40,10 @@ class TestEmailErrors(ModuleStoreTestCase):
self.url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id})
def tearDown(self):
self.smtp_server_thread.stop()
patch.stopall()
@patch('bulk_email.tasks.course_email.retry')
def test_data_err_retry(self, retry):
......
......@@ -32,6 +32,12 @@ class TestInstructorDashboardEmailView(ModuleStoreTestCase):
# URL for email view
self.email_link = '<a href="#" onclick="goto(\'Email\')" class="None">Email</a>'
def tearDown(self):
"""
Undo all patches.
"""
patch.stopall()
@patch.dict(settings.MITX_FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True})
def test_email_flag_true(self):
# Assert that the URL for the email view is in the response
......
......@@ -55,6 +55,7 @@ from mitxmako.shortcuts import render_to_string
from bulk_email.models import CourseEmail
from html_to_text import html_to_text
import datetime
from hashlib import md5
from bulk_email import tasks
......@@ -706,12 +707,14 @@ def instructor_dashboard(request, course_id):
to_option = request.POST.get("to_option")
subject = request.POST.get("subject")
html_message = request.POST.get("message")
text_message = html_to_text(html_message)
email = CourseEmail(course_id=course_id,
sender=request.user,
to=to_option,
subject=subject,
html_message=html_message,
text_message=text_message,
hash=md5((html_message + subject + datetime.datetime.isoformat(datetime.datetime.now())).encode('utf-8')).hexdigest())
email.save()
......
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