Motivation
Quiz emails are currently sent as a multipart AMP + HTML + plain-text message (SendQuizCommand, django_email_learning/services/command_models/send_quiz_command.py). Email clients that support AMP let learners answer and submit a quiz directly inline in the email. Clients that don't support AMP — which is a large share of providers — fall back to the HTML/plain-text version, which only offers a "click here" link to a web page. For a platform whose whole premise is staying inside the inbox, that's a meaningful gap for non-AMP users.
Reported as feedback by Jamie (@freshpixel on HelpBank).
Proposed solution
For courses connected to an IMAP connection, also include a mailto: reply link in the quiz email (HTML and plain-text versions). The link prefills:
-
Subject: quiz <token> — reusing the existing IMAP subject-line command convention already used for enroll <slug> / verify <code> / drop <slug> (django_email_learning/services/defaults/imap_interface.py). <token> is the same JWT already embedded in the quiz's web link (delivery id/hash + selected question ids).
-
Body: the quiz questions with their answer choices rendered as [ ] checkboxes, e.g.:
1. What does AMP stand for?
[ ] Accelerated Mobile Pages
[ ] Advanced Mail Protocol
[ ] Automated Message Pipeline
Put an x inside the brackets next to your answer(s) and hit reply/send.
A question's is_multiple_choice() (Question.is_multiple_choice(), django_email_learning/models/course_contents.py) is determined per-question, not per-quiz, so the instructional copy needs "select one" vs. "select all that apply" phrasing per question — matching what the AMP version already implies generically ("some questions may have multiple correct answers").
The learner replies (ideally without touching the prefilled subject) and a new IMAP inbound handler parses the reply, extracts which boxes were marked per question, and records the submission through the same scoring path already used for AMP/web submissions (QuizSubmissionView.process_quiz_submission, django_email_learning/personalised/api/views.py).
Scope / sub-issues
Design decisions
- Routing: subject-line
quiz <token> command, following the existing IMAP command convention — no new routing/threading mechanism needed.
- Parse failures (ambiguous/missing marks, mangled quoting, etc.): silently drop and log via the metric recorder, matching the existing
imap_command_handling_failed pattern — no auto-reply on failure.
- On successful parsing: send the learner a result email (score/pass-fail). The IMAP job runs on a schedule rather than in real time, so a learner replying to a quiz shouldn't be left wondering whether their answer was received or how they did.
Known risks / open considerations for implementers
mailto: body length is inconsistently capped across mail clients and OS-level mailto handlers (a few thousand characters is a common practical ceiling). Long quizzes with many questions could get silently truncated in some clients — worth testing against real clients and possibly documenting a soft limit or a fallback note ("if your reply looks cut off, use the web link instead").
- Reply-body parsing robustness: mail clients vary widely in how they quote/format a reply (some strip all original formatting, some prefix every line with
>, some convert plain text to HTML on reply, mobile clients may reflow whitespace). The [x] parsing needs to tolerate whitespace variations ([x], [ x ], [X]) and quoted-line prefixes.
- Identify the learner via the
From header matching the enrollment's email, the same way enroll/verify/drop already do — no new identity mechanism needed.
Motivation
Quiz emails are currently sent as a multipart AMP + HTML + plain-text message (
SendQuizCommand,django_email_learning/services/command_models/send_quiz_command.py). Email clients that support AMP let learners answer and submit a quiz directly inline in the email. Clients that don't support AMP — which is a large share of providers — fall back to the HTML/plain-text version, which only offers a "click here" link to a web page. For a platform whose whole premise is staying inside the inbox, that's a meaningful gap for non-AMP users.Reported as feedback by Jamie (
@freshpixelon HelpBank).Proposed solution
For courses connected to an IMAP connection, also include a
mailto:reply link in the quiz email (HTML and plain-text versions). The link prefills:Subject:
quiz <token>— reusing the existing IMAP subject-line command convention already used forenroll <slug>/verify <code>/drop <slug>(django_email_learning/services/defaults/imap_interface.py).<token>is the same JWT already embedded in the quiz's web link (delivery id/hash + selected question ids).Body: the quiz questions with their answer choices rendered as
[ ]checkboxes, e.g.:A question's
is_multiple_choice()(Question.is_multiple_choice(),django_email_learning/models/course_contents.py) is determined per-question, not per-quiz, so the instructional copy needs "select one" vs. "select all that apply" phrasing per question — matching what the AMP version already implies generically ("some questions may have multiple correct answers").The learner replies (ideally without touching the prefilled subject) and a new IMAP inbound handler parses the reply, extracts which boxes were marked per question, and records the submission through the same scoring path already used for AMP/web submissions (
QuizSubmissionView.process_quiz_submission,django_email_learning/personalised/api/views.py).Scope / sub-issues
quiz <token>reply, extract marked answers, submit via the existing scoring/grading path, and email the learner their result.Design decisions
quiz <token>command, following the existing IMAP command convention — no new routing/threading mechanism needed.imap_command_handling_failedpattern — no auto-reply on failure.Known risks / open considerations for implementers
mailto:body length is inconsistently capped across mail clients and OS-level mailto handlers (a few thousand characters is a common practical ceiling). Long quizzes with many questions could get silently truncated in some clients — worth testing against real clients and possibly documenting a soft limit or a fallback note ("if your reply looks cut off, use the web link instead").>, some convert plain text to HTML on reply, mobile clients may reflow whitespace). The[x]parsing needs to tolerate whitespace variations ([x],[ x ],[X]) and quoted-line prefixes.Fromheader matching the enrollment's email, the same wayenroll/verify/dropalready do — no new identity mechanism needed.