Refactor html->plaintext conversion (for bulk email) into separate library
This commit is contained in:
23
common/lib/html_to_text.py
Normal file
23
common/lib/html_to_text.py
Normal file
@@ -0,0 +1,23 @@
|
||||
"""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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user