Skip to content

Support quiz submission via plain-email reply for non-AMP clients #727

Description

@payamnj

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions