-# Copyright (C) 2012 W. Trevor King <wking@drexel.edu>
+# Copyright (C) 2012 W. Trevor King <wking@tremily.us>
#
# This file is part of pygrader.
#
from .extract_mime import message_time as _message_time
from .model.person import Person as _Person
+from .handler import InsecureMessage as _InsecureMessage
+from .handler import InvalidAssignmentSubject as _InvalidAssignmentSubject
from .handler import InvalidMessage as _InvalidMessage
+from .handler import InvalidStudentSubject as _InvalidStudentSubject
from .handler import InvalidSubjectMessage as _InvalidSubjectMessage
+from .handler import PermissionViolationMessage as _PermissionViolationMessage
from .handler import Response as _Response
from .handler import UnsignedMessage as _UnsignedMessage
-from .handler import InsecureMessage as _InsecureMessage
-from .handler.get import InvalidStudent as _InvalidStudent
from .handler.get import run as _handle_get
-from .handler.submission import InvalidAssignment as _InvalidAssignment
+from .handler.grade import run as _handle_grade
+from .handler.grade import MissingGradeMessage as _MissingGradeMessage
from .handler.submission import run as _handle_submission
+from .handler.submission import InvalidSubmission as _InvalidSubmission
_TAG_REGEXP = _re.compile('^.*\[([^]]*)\].*$')
trust_email_infrastructure=False,
handlers={
'get': _handle_get,
+ 'grade': _handle_grade,
'submit': _handle_submission,
}, respond=None, dry_run=False, **kwargs):
"""Run from procmail to sort incomming submissions
>>> message['Return-Path'] = '<bb@greyhavens.net>'
>>> process(message) # doctest: +REPORT_UDIFF, +ELLIPSIS
respond with:
+ Content-Type: multipart/signed; protocol="application/pgp-signature"; micalg="pgp-sha1"; boundary="===============...=="
+ MIME-Version: 1.0
+ Content-Disposition: inline
+ Date: ...
+ From: Robot101 <phys101@tower.edu>
+ Reply-to: Robot101 <phys101@tower.edu>
+ To: Bilbo Baggins <bb@shire.org>
+ Subject: Received Assignment 1 submission
+ <BLANKLINE>
+ --===============...==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Disposition: inline
Yours,
phys-101 robot
<BLANKLINE>
+ --===============...==
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: 7bit
+ Content-Description: OpenPGP digital signature
+ Content-Type: application/pgp-signature; name="signature.asc"; charset="us-ascii"
+ <BLANKLINE>
+ -----BEGIN PGP SIGNATURE-----
+ Version: GnuPG v2.0.19 (GNU/Linux)
+ <BLANKLINE>
+ ...
+ -----END PGP SIGNATURE-----
+ <BLANKLINE>
+ --===============...==--
>>> course.print_tree() # doctest: +REPORT_UDIFF, +ELLIPSIS
Bilbo_Baggins
... 'for <wking@tremily.us>; Mon, 09 Oct 2011 11:50:46 -0400 (EDT)')
>>> process(message) # doctest: +REPORT_UDIFF, +ELLIPSIS
respond with:
+ Content-Type: multipart/signed; protocol="application/pgp-signature"; micalg="pgp-sha1"; boundary="===============...=="
+ MIME-Version: 1.0
+ Content-Disposition: inline
+ Date: ...
+ From: Robot101 <phys101@tower.edu>
+ Reply-to: Robot101 <phys101@tower.edu>
+ To: Bilbo Baggins <bb@shire.org>
+ Subject: Received Assignment 1 submission
+ <BLANKLINE>
+ --===============...==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Disposition: inline
Yours,
phys-101 robot
<BLANKLINE>
+ --===============...==
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: 7bit
+ Content-Description: OpenPGP digital signature
+ Content-Type: application/pgp-signature; name="signature.asc"; charset="us-ascii"
+ <BLANKLINE>
+ -----BEGIN PGP SIGNATURE-----
+ Version: GnuPG v2.0.19 (GNU/Linux)
+ <BLANKLINE>
+ ...
+ -----END PGP SIGNATURE-----
+ <BLANKLINE>
+ --===============...==--
+
>>> course.print_tree() # doctest: +REPORT_UDIFF, +ELLIPSIS
Bilbo_Baggins
Bilbo_Baggins/Assignment_1
>>> message['Message-ID'] = '<hgi.jlk@home.net>'
>>> process(message) # doctest: +REPORT_UDIFF, +ELLIPSIS
respond with:
+ Content-Type: multipart/signed; protocol="application/pgp-signature"; micalg="pgp-sha1"; boundary="===============...=="
+ MIME-Version: 1.0
+ Content-Disposition: inline
+ Date: ...
+ From: Robot101 <phys101@tower.edu>
+ Reply-to: Robot101 <phys101@tower.edu>
+ To: Bilbo Baggins <bb@shire.org>
+ Subject: Received Assignment 1 submission
+ <BLANKLINE>
+ --===============...==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Disposition: inline
Yours,
phys-101 robot
<BLANKLINE>
+ --===============...==
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: 7bit
+ Content-Description: OpenPGP digital signature
+ Content-Type: application/pgp-signature; name="signature.asc"; charset="us-ascii"
+ <BLANKLINE>
+ -----BEGIN PGP SIGNATURE-----
+ Version: GnuPG v2.0.19 (GNU/Linux)
+ <BLANKLINE>
+ ...
+ -----END PGP SIGNATURE-----
+ <BLANKLINE>
+ --===============...==--
Response to a submission on an unsubmittable assignment:
except _InvalidMessage as error:
error.course = course
error.message = original
- if person is not None and not hasattr(error, 'person'):
- error.person = person
- if subject is not None and not hasattr(error, 'subject'):
- error.subject = subject
- if target is not None and not hasattr(error, 'target'):
- error.target = target
+ for attribute,value in [('person', person),
+ ('subject', subject),
+ ('target', target)]:
+ if (value is not None and
+ getattr(error, attribute, None) is None):
+ setattr(error, attribute, value)
_LOG.warn('invalid message {}'.format(error.message_id()))
if not continue_after_invalid_message:
raise
except _InvalidMessage as error:
error.course = course
error.message = original
- if person is not None and not hasattr(error, 'person'):
- error.person = person
- if subject is not None and not hasattr(error, 'subject'):
- error.subject = subject
- if target is not None and not hasattr(error, 'target'):
- error.target = target
+ for attribute,value in [('person', person),
+ ('subject', subject),
+ ('target', target)]:
+ if (value is not None and
+ getattr(error, attribute, None) is None):
+ setattr(error, attribute, value)
raise
return (original, message, person, subject, target)
author = error.course.robot
target = getattr(error, 'person', None)
subject = str(error)
- if isinstance(error, InvalidHandlerMessage):
+ if isinstance(error, _InvalidSubmission):
+ subject = 'Received invalid {} submission'.format(
+ error.assignment.name)
+ text = (
+ 'We received your submission for {}, but you are not\n'
+ 'allowed to submit that assignment via email.'
+ ).format(error.assignment.name)
+ elif isinstance(error, _MissingGradeMessage):
+ subject = 'No grade in {!r}'.format(error.subject)
+ text = (
+ 'Your grade submission did not include a text/plain\n'
+ 'part containing the new grade and comment.'
+ )
+ elif isinstance(error, InvalidHandlerMessage):
targets = sorted(error.handlers.keys())
if not targets:
hint = (
' {!r}\n'
'which does not match any submittable handler name for\n'
'{}.\n'
- '{}').format(repr(error.subject), error.course.name, hint)
+ '{}').format(error.subject, error.course.name, hint)
elif isinstance(error, SubjectlessMessage):
subject = 'no subject in {}'.format(error.message['Message-ID'])
text = 'We received an email message from you without a subject.'
'or TA.').format(error.course.name)
elif isinstance(error, NoReturnPath):
return
+ elif isinstance(error, _InvalidAssignmentSubject):
+ if error.assignments:
+ hint = (
+ 'but it matches several assignments:\n'
+ ' * {}').format('\n * '.join(
+ a.name for a in error.assignments))
+ else:
+ # prefer a submittable example assignment
+ assignments = [
+ a for a in error.course.assignments if a.submittable]
+ assignments += course.assignments # but fall back to any one
+ hint = (
+ 'Remember to use the full name for the assignment in the\n'
+ 'subject. For example:\n'
+ ' {} submission').format(assignments[0].name)
+ text = (
+ 'We got an email from you with the following subject:\n'
+ ' {!r}\n{}').format(error.subject, hint)
+ elif isinstance(error, _InvalidStudentSubject):
+ text = (
+ 'We got an email from you with the following subject:\n'
+ ' {!r}\n'
+ 'but it matches several students:\n'
+ ' * {}').format(
+ error.subject, '\n * '.join(s.name for s in error.students))
elif isinstance(error, _InvalidSubjectMessage):
text = (
'We received an email message from you with an invalid\n'
subject = 'unsigned message {}'.format(error.message['Message-ID'])
text = (
'We received an email message from you without a valid\n'
- 'PGP signature.')
- elif isinstance(error, _InvalidAssignment):
- text = (
- 'We received your submission for {}, but you are not\n'
- 'allowed to submit that assignment via email.'
- ).format(error.assignment.name)
- elif isinstance(error, _InvalidStudent):
+ 'PGP signature.'
+ )
+ elif isinstance(error, _PermissionViolationMessage):
text = (
'We got an email from you with the following subject:\n'
' {!r}\n'
- 'but it matches several students:\n'
+ "but you can't do that unless you belong to one of the\n"
+ 'following groups:\n'
' * {}').format(
- error.subject, '\n * '.join(s.name for s in error.students))
+ error.subject, '\n * '.join(error.allowed_groups))
elif isinstance(error, _InvalidMessage):
text = subject
else: