How to send mass emails in Open edX?

Hello, everyone. I want to email everyone. How do I set it?

@Dexter do you mean that the bulk email feature isn’t visible in your instructor dashboard? If that’s the case, you need to add a flag in the Django admin panel as shown here.

If the bulk email functionality is already available in your instructor dashboard, you can follow the instructions from the doc.

I hope this helps!

2 Likes

@gabrieldamours Thanks for your reply, I’ve set it up in the admin page, but in my instructor dashboard, I can’t find the email function. I don’t understand what’s going on.

@Dexter I just found how to do it. AFAIK this isn’t documented. You have to set an authorization to send emails per course:

Go to the Django admin page and scroll down to the Bulk Email Administration, then click on “Add” next to “Course authorizations”:

Paste your course ID (it’s at the end of the URL when you’re on the course outline view in the LMS) and save. A moment later the Email tab will appear under the instructor dashboard:

VoilĂ !

2 Likes

If you want to enable bulk email globally, you can disable the “REQUIRE COURSE EMAIL AUTH” flag in the admin view.

image

1 Like

@gabrieldamours Thanks for your reply. Under your guidance, I can see the bulk email functionality is already available in my instructor dashboard. I tried to send emails, but all failed. I don’t know why?

@felipe.espinoza.r Thanks for your reply. I can’t find the “REQUIRE COURSE EMAIL AUTH” flag in the admin view. Where can I find the flag.

@Dexter

It works for me, so I can’t really help you with that one unfortunately. Did you self-install or do you work with a provider? We at OpenCraft can provide technical support if needed, let me know.

1 Like

@gabrieldamours Thank you for your help. We have our own developers, but they’re not very professional.

@Dexter, can you check from email id in your log files when you send email?
AFAIK, it will create from email id using your course id and that email will not be registered in you email client. Maybe this could be the reason of the failure.

Yes, this prepended course identifier is a pain:

# Suffix used to construct 'from' email address for bulk emails.
# A course-specific identifier is prepended.
BULK_EMAIL_DEFAULT_FROM_EMAIL = 'no-reply@example.com'

So actual email from ‘test’ course - comes from
test-no-reply@example.com
And it’s the reason that emails are not sent. Crazy.
The error is:
smtplib.SMTPSenderRefused: (553, b’5.7.1 Sender address rejected: not owned by auth user.’ …)

We use the setting django setting EMAIL_USE_DEFAULT_FROM_FOR_BULK to override that prepended course identifier.

There’s a little more documentation at Feature Toggles — edx-platform Technical Reference documentation

2 Likes

Thank you @pdpinch This is very interesting. Is it a new feature? I cannot see this file in Koa3.
So my workaround was to go to:
/edx/app/edxapp/edx-platform/lms/djangoapps/bulk_email/tasks.py
and change the line in function:
_get_source_address
from
email=u'<{course_name}-{from_email}>',
to
email=u'<{from_email}>'
after that
sudo /edx/bin/supervisorctl restart all

Hi all,

I have followed the instructions from @gabrieldamours to enable bulk emails and I can access the “Email” tab. However, when I try to send an email (for instance to myself), the process fails with the following error: Object of type LazyStaticAbsoluteUrl is not JSON serializable (full Trace below).

Any idea what my cause this problem ?

Thanks!

lms-worker_1     | Traceback (most recent call last):
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/serialization.py", line 50, in _reraise_errors
lms-worker_1     |     yield
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/serialization.py", line 221, in dumps
lms-worker_1     |     payload = encoder(data)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/utils/json.py", line 69, in dumps
lms-worker_1     |     return _dumps(s, cls=cls or _default_encoder,
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/simplejson/__init__.py", line 398, in dumps
lms-worker_1     |     return cls(
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/simplejson/encoder.py", line 296, in encode
lms-worker_1     |     chunks = self.iterencode(o, _one_shot=True)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/simplejson/encoder.py", line 378, in iterencode
lms-worker_1     |     return _iterencode(o, 0)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/utils/json.py", line 59, in default
lms-worker_1     |     return super(JSONEncoder, self).default(o)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/simplejson/encoder.py", line 272, in default
lms-worker_1     |     raise TypeError('Object of type %s is not JSON serializable' %
lms-worker_1     | TypeError: Object of type LazyStaticAbsoluteUrl is not JSON serializable
lms-worker_1     | 
lms-worker_1     | During handling of the above exception, another exception occurred:
lms-worker_1     | 
lms-worker_1     | Traceback (most recent call last):
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/celery/app/trace.py", line 412, in trace_task
lms-worker_1     |     R = retval = fun(*args, **kwargs)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/celery/app/trace.py", line 704, in __protected_call__
lms-worker_1     |     return self.run(*args, **kwargs)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/edx_django_utils/monitoring/internal/code_owner/utils.py", line 179, in new_function
lms-worker_1     |     return wrapped_function(*args, **kwargs)
lms-worker_1     |   File "/openedx/edx-platform/lms/djangoapps/instructor_task/tasks.py", line 164, in send_bulk_course_email
lms-worker_1     |     return run_main_task(entry_id, visit_fcn, action_name)
lms-worker_1     |   File "/openedx/edx-platform/lms/djangoapps/instructor_task/tasks_helper/runner.py", line 120, in run_main_task
lms-worker_1     |     task_progress = task_fcn(entry_id, course_id, task_input, action_name)
lms-worker_1     |   File "/openedx/edx-platform/lms/djangoapps/bulk_email/tasks.py", line 221, in perform_delegate_email_batches
lms-worker_1     |     progress = queue_subtasks_for_query(
lms-worker_1     |   File "/openedx/edx-platform/lms/djangoapps/instructor_task/subtasks.py", line 348, in queue_subtasks_for_query
lms-worker_1     |     new_subtask.apply_async()
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/celery/canvas.py", line 235, in apply_async
lms-worker_1     |     return _apply(args, kwargs, **options)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/celery/app/task.py", line 566, in apply_async
lms-worker_1     |     return app.send_task(
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/celery/app/base.py", line 741, in send_task
lms-worker_1     |     amqp.send_task_message(P, name, message, **options)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/celery/app/amqp.py", line 552, in send_task_message
lms-worker_1     |     ret = producer.publish(
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/messaging.py", line 167, in publish
lms-worker_1     |     body, content_type, content_encoding = self._prepare(
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/messaging.py", line 252, in _prepare
lms-worker_1     |     body) = dumps(body, serializer=serializer)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/serialization.py", line 221, in dumps
lms-worker_1     |     payload = encoder(data)
lms-worker_1     |   File "/opt/pyenv/versions/3.8.6/lib/python3.8/contextlib.py", line 131, in __exit__
lms-worker_1     |     self.gen.throw(type, value, traceback)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/serialization.py", line 54, in _reraise_errors
lms-worker_1     |     reraise(wrapper, wrapper(exc), sys.exc_info()[2])
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/vine/five.py", line 194, in reraise
lms-worker_1     |     raise value.with_traceback(tb)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/serialization.py", line 50, in _reraise_errors
lms-worker_1     |     yield
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/serialization.py", line 221, in dumps
lms-worker_1     |     payload = encoder(data)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/utils/json.py", line 69, in dumps
lms-worker_1     |     return _dumps(s, cls=cls or _default_encoder,
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/simplejson/__init__.py", line 398, in dumps
lms-worker_1     |     return cls(
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/simplejson/encoder.py", line 296, in encode
lms-worker_1     |     chunks = self.iterencode(o, _one_shot=True)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/simplejson/encoder.py", line 378, in iterencode
lms-worker_1     |     return _iterencode(o, 0)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/utils/json.py", line 59, in default
lms-worker_1     |     return super(JSONEncoder, self).default(o)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/simplejson/encoder.py", line 272, in default
lms-worker_1     |     raise TypeError('Object of type %s is not JSON serializable' %
lms-worker_1     | kombu.exceptions.EncodeError: Object of type LazyStaticAbsoluteUrl is not JSON serializable
lms-worker_1     | 2021-11-26 21:51:36,460 ERROR 153 [celery.app.trace] [user None] [ip None] trace.py:255 - Task lms.djangoapps.instructor_task.tasks.send_bulk_course_email[5f935607-b787-4922-ad45-7d924b2527b2] raised unexpected: EncodeError(TypeError('Object of type LazyStaticAbsoluteUrl is not JSON serializable'))
lms-worker_1     | Traceback (most recent call last):
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/serialization.py", line 50, in _reraise_errors
lms-worker_1     |     yield
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/serialization.py", line 221, in dumps
lms-worker_1     |     payload = encoder(data)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/utils/json.py", line 69, in dumps
lms-worker_1     |     return _dumps(s, cls=cls or _default_encoder,
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/simplejson/__init__.py", line 398, in dumps
lms-worker_1     |     return cls(
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/simplejson/encoder.py", line 296, in encode
lms-worker_1     |     chunks = self.iterencode(o, _one_shot=True)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/simplejson/encoder.py", line 378, in iterencode
lms-worker_1     |     return _iterencode(o, 0)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/utils/json.py", line 59, in default
lms-worker_1     |     return super(JSONEncoder, self).default(o)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/simplejson/encoder.py", line 272, in default
lms-worker_1     |     raise TypeError('Object of type %s is not JSON serializable' %
lms-worker_1     | TypeError: Object of type LazyStaticAbsoluteUrl is not JSON serializable
lms-worker_1     | 
lms-worker_1     | During handling of the above exception, another exception occurred:
lms-worker_1     | 
lms-worker_1     | Traceback (most recent call last):
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/celery/app/trace.py", line 412, in trace_task
lms-worker_1     |     R = retval = fun(*args, **kwargs)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/celery/app/trace.py", line 704, in __protected_call__
lms-worker_1     |     return self.run(*args, **kwargs)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/edx_django_utils/monitoring/internal/code_owner/utils.py", line 179, in new_function
lms-worker_1     |     return wrapped_function(*args, **kwargs)
lms-worker_1     |   File "/openedx/edx-platform/lms/djangoapps/instructor_task/tasks.py", line 164, in send_bulk_course_email
lms-worker_1     |     return run_main_task(entry_id, visit_fcn, action_name)
lms-worker_1     |   File "/openedx/edx-platform/lms/djangoapps/instructor_task/tasks_helper/runner.py", line 120, in run_main_task
lms-worker_1     |     task_progress = task_fcn(entry_id, course_id, task_input, action_name)
lms-worker_1     |   File "/openedx/edx-platform/lms/djangoapps/bulk_email/tasks.py", line 221, in perform_delegate_email_batches
lms-worker_1     |     progress = queue_subtasks_for_query(
lms-worker_1     |   File "/openedx/edx-platform/lms/djangoapps/instructor_task/subtasks.py", line 348, in queue_subtasks_for_query
lms-worker_1     |     new_subtask.apply_async()
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/celery/canvas.py", line 235, in apply_async
lms-worker_1     |     return _apply(args, kwargs, **options)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/celery/app/task.py", line 566, in apply_async
lms-worker_1     |     return app.send_task(
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/celery/app/base.py", line 741, in send_task
lms-worker_1     |     amqp.send_task_message(P, name, message, **options)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/celery/app/amqp.py", line 552, in send_task_message
lms-worker_1     |     ret = producer.publish(
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/messaging.py", line 167, in publish
lms-worker_1     |     body, content_type, content_encoding = self._prepare(
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/messaging.py", line 252, in _prepare
lms-worker_1     |     body) = dumps(body, serializer=serializer)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/serialization.py", line 221, in dumps
lms-worker_1     |     payload = encoder(data)
lms-worker_1     |   File "/opt/pyenv/versions/3.8.6/lib/python3.8/contextlib.py", line 131, in __exit__
lms-worker_1     |     self.gen.throw(type, value, traceback)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/serialization.py", line 54, in _reraise_errors
lms-worker_1     |     reraise(wrapper, wrapper(exc), sys.exc_info()[2])
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/vine/five.py", line 194, in reraise
lms-worker_1     |     raise value.with_traceback(tb)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/serialization.py", line 50, in _reraise_errors
lms-worker_1     |     yield
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/serialization.py", line 221, in dumps
lms-worker_1     |     payload = encoder(data)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/utils/json.py", line 69, in dumps
lms-worker_1     |     return _dumps(s, cls=cls or _default_encoder,
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/simplejson/__init__.py", line 398, in dumps
lms-worker_1     |     return cls(
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/simplejson/encoder.py", line 296, in encode
lms-worker_1     |     chunks = self.iterencode(o, _one_shot=True)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/simplejson/encoder.py", line 378, in iterencode
lms-worker_1     |     return _iterencode(o, 0)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/kombu/utils/json.py", line 59, in default
lms-worker_1     |     return super(JSONEncoder, self).default(o)
lms-worker_1     |   File "/openedx/venv/lib/python3.8/site-packages/simplejson/encoder.py", line 272, in default
lms-worker_1     |     raise TypeError('Object of type %s is not JSON serializable' %
lms-worker_1     | kombu.exceptions.EncodeError: Object of type LazyStaticAbsoluteUrl is not JSON serializable

@RonanFR This bug is realted to tutor and have been fixed from tutor >= v13.1.1 release notes, also there is a related disucssion in tutor discourse.

1 Like