Example #1
0
def _get_student_email(basedir, course, person, student=None):
    if student is None:
        student = person
        targets = None
        _LOG.debug('construct student grade email about {} for {}'.format(
            student, student))
    else:
        targets = [person]
        _LOG.debug('construct student grade email about {} for {}'.format(
            student, person))
    emails = list(
        _student_email(basedir=basedir,
                       author=course.robot,
                       course=course,
                       student=student,
                       targets=targets,
                       old=True))
    if len(emails) == 0:
        if targets is None:
            text = (
                "We don't have any of your grades on file for this course.")
        else:
            text = ("We don't have any grades for {} on file for this course."
                    ).format(student.name)
        message = _pgp_mime.encodedMIMEText(text)
        message['Subject'] = 'No grades for {}'.format(student.alias())
        raise _Response(message=message, complete=True)
    elif len(emails) > 1:
        raise NotImplementedError(emails)
    email, callback = emails[0]
    # callback records notification, but don't bother here
    return email
Example #2
0
def load_attachment(filename, encoding='us-ascii'):
    mimetype,content_encoding = _mimetypes.guess_type(filename)
    if mimetype is None or content_encoding is not None:
        mimetype = 'application/octet-stream'
    maintype,subtype = mimetype.split('/', 1)
    _pgp_mime.LOG.info('loading attachment {} as {} ({})'.format(
            filename, mimetype, content_encoding))
    if maintype == 'text':
        text = read_file(filename=filename, encoding=encoding)
        attachment = _pgp_mime.encodedMIMEText(text)
        del attachment['content-disposition']
    else:
        data = open(filename, 'rb').read()
        if maintype == 'application':
            attachment = _MIMEApplication(data, subtype)
        elif maintype == 'audio':
            attachment = _MIMEAudio(data)
        elif maintype == 'image':
            attachment = _MIMEImage(data)
        else:
            attachment = _MIMENonMultipary(maintype, subtype)
            attachment.set_payload(data, _encode_base64)
    attachment.add_header(
        'Content-Disposition', 'attachment', filename=filename)
    return attachment
Example #3
0
def load_attachment(filename, encoding='us-ascii'):
    mimetype, content_encoding = _mimetypes.guess_type(filename)
    if mimetype is None or content_encoding is not None:
        mimetype = 'application/octet-stream'
    maintype, subtype = mimetype.split('/', 1)
    _pgp_mime.LOG.info('loading attachment {} as {} ({})'.format(
        filename, mimetype, content_encoding))
    if maintype == 'text':
        text = read_file(filename=filename, encoding=encoding)
        attachment = _pgp_mime.encodedMIMEText(text)
        del attachment['content-disposition']
    else:
        data = open(filename, 'rb').read()
        if maintype == 'application':
            attachment = _MIMEApplication(data, subtype)
        elif maintype == 'audio':
            attachment = _MIMEAudio(data)
        elif maintype == 'image':
            attachment = _MIMEImage(data)
        else:
            attachment = _MIMENonMultipary(maintype, subtype)
            attachment.set_payload(data, _encode_base64)
    attachment.add_header('Content-Disposition',
                          'attachment',
                          filename=filename)
    return attachment
Example #4
0
def _get_student_email(basedir, course, person, student=None):
    if student is None:
        student = person
        targets = None
        _LOG.debug('construct student grade email about {} for {}'.format(
                student, student))
    else:
        targets = [person]
        _LOG.debug('construct student grade email about {} for {}'.format(
                student, person))
    emails = list(_student_email(
        basedir=basedir, author=course.robot, course=course,
        student=student, targets=targets, old=True))
    if len(emails) == 0:
        if targets is None:
            text = (
                "We don't have any of your grades on file for this course."
                )
        else:
            text = (
                "We don't have any grades for {} on file for this course."
                ).format(student.name)
        message = _pgp_mime.encodedMIMEText(text)
        message['Subject'] = 'No grades for {}'.format(student.alias())
        raise _Response(message=message, complete=True)
    elif len(emails) > 1:
        raise NotImplementedError(emails)
    email,callback = emails[0]
    # callback records notification, but don't bother here
    return email
Example #5
0
def run(basedir, course, message, person, subject, max_late=0, dry_run=None,
        **kwargs):
    """
    >>> from pgp_mime.email import encodedMIMEText
    >>> from ..test.course import StubCourse
    >>> from . import Response
    >>> course = StubCourse()
    >>> person = list(
    ...     course.course.find_people(email='*****@*****.**'))[0]
    >>> message = encodedMIMEText('The answer is 42.')
    >>> message['Message-ID'] = '<*****@*****.**>'
    >>> message['Received'] = (
    ...     'from smtp.home.net (smtp.home.net [123.456.123.456]) '
    ...     'by smtp.mail.uu.edu (Postfix) with ESMTP id 5BA225C83EF '
    ...     'for <*****@*****.**>; Sun, 09 Oct 2011 11:50:46 -0400 (EDT)')
    >>> subject = '[submit] assignment 1'
    >>> try:
    ...     run(basedir=course.basedir, course=course.course, message=message,
    ...         person=person, subject=subject, max_late=0)
    ... except Response as e:
    ...     print('respond with:')
    ...     print(e.message.as_string())
    ... # doctest: +ELLIPSIS, +REPORT_UDIFF
    respond with:
    Content-Type: text/plain; charset="us-ascii"
    MIME-Version: 1.0
    Content-Transfer-Encoding: 7bit
    Content-Disposition: inline
    Subject: Received Assignment 1 submission
    <BLANKLINE>
    We received your submission for Assignment 1 on ....

    >>> course.cleanup()
    """
    time = _message_time(message=message)
    assignment = _get_assignment(course=course, subject=subject)
    assignment_path = _assignment_path(basedir, assignment, person)
    _save_local_message_copy(
        msg=message, person=person, assignment_path=assignment_path,
        dry_run=dry_run)
    _extract_mime(message=message, output=assignment_path, dry_run=dry_run)
    _check_late(
        basedir=basedir, assignment=assignment, person=person, time=time,
        max_late=max_late, dry_run=dry_run)
    if time:
        time_str = 'on {}'.format(_formatdate(time))
    else:
        time_str = 'at an unknown time'
    message = _pgp_mime.encodedMIMEText((
            'We received your submission for {} {}.'
            ).format(
            assignment.name, time_str))
    message['Subject'] = 'Received {} submission'.format(assignment.name)
    raise _Response(message=message)
Example #6
0
def test_smtp(smtp, author, targets, msg=None):
    """Test the SMTP connection by sending a message to `target`
    """
    if msg is None:
        msg = _pgp_mime.encodedMIMEText('Success!')
        msg['Date'] = _email_utils.formatdate()
        msg['From'] = author
        msg['Reply-to'] = msg['From']
        msg['To'] = ', '.join(targets)
        msg['Subject'] = 'Testing pygrader SMTP connection'
    _LOG.info('send test message to SMTP server')
    smtp.send_message(msg=msg)
Example #7
0
def construct_response(author, targets, subject, text, original, cc=None):
    r"""Build a multipart/mixed response email using `Person` instances

    >>> from pygrader.model.person import Person as Person
    >>> student = Person(name='Джон Доу', emails=['*****@*****.**'])
    >>> assistant = Person(name='Jill', emails=['*****@*****.**'])
    >>> cc = [assistant]
    >>> msg = construct_text_email(author=student, targets=[assistant],
    ...     subject='Assignment 1 submission', text='Bla bla bla...')
    >>> rsp = construct_response(author=assistant, targets=[student],
    ...     subject='Received assignment 1 submission', text='3 hours late',
    ...     original=msg)
    >>> print(rsp.as_string())  # doctest: +REPORT_UDIFF, +ELLIPSIS
    Content-Type: multipart/mixed; boundary="===============...=="
    MIME-Version: 1.0
    Date: ...
    From: Jill <*****@*****.**>
    Reply-to: Jill <*****@*****.**>
    To: =?utf-8?b?0JTQttC+0L0g0JTQvtGD?= <*****@*****.**>
    Subject: Received assignment 1 submission
    <BLANKLINE>
    --===============...==
    Content-Type: text/plain; charset="us-ascii"
    MIME-Version: 1.0
    Content-Transfer-Encoding: 7bit
    Content-Disposition: inline
    <BLANKLINE>
    3 hours late
    --===============...==
    Content-Type: message/rfc822
    MIME-Version: 1.0
    <BLANKLINE>
    Content-Type: text/plain; charset="us-ascii"
    MIME-Version: 1.0
    Content-Transfer-Encoding: 7bit
    Content-Disposition: inline
    Date: ...
    From: =?utf-8?b?0JTQttC+0L0g0JTQvtGD?= <*****@*****.**>
    Reply-to: =?utf-8?b?0JTQttC+0L0g0JTQvtGD?= <*****@*****.**>
    To: Jill <*****@*****.**>
    Subject: Assignment 1 submission
    <BLANKLINE>
    Bla bla bla...
    --===============...==--
    """
    message = _MIMEMultipart('mixed')
    message.attach(_pgp_mime.encodedMIMEText(text))
    message.attach(_MIMEMessage(original))
    return construct_email(
        author=author, targets=targets, subject=subject, message=message,
        cc=cc)
Example #8
0
def _get_student_submission_email(basedir, course, person, assignments,
                                  student):
    _LOG.debug('construct student submission email about {} {} for {}'.format(
        student, assignments, person))
    subject = '{} assignment submissions for {}'.format(
        course.name, student.name)
    text = '{}:\n  * {}\n'.format(subject,
                                  '\n  * '.join(a.name for a in assignments))
    message = _MIMEMultipart('mixed')
    message.attach(_pgp_mime.encodedMIMEText(text))
    for assignment in assignments:
        grade = course.grade(student=student, assignment=assignment)
        if grade is not None:
            text = '{} grade: {}\n'.format(assignment.name, grade.points)
            if grade.comment:
                text += '\n{}\n'.format(grade.comment)
            message.attach(_pgp_mime.encodedMIMEText(text))
        assignment_path = _assignment_path(basedir, assignment, student)
        mpath = _os_path.join(assignment_path, 'mail')
        try:
            mbox = _mailbox.Maildir(mpath, factory=None, create=False)
        except _mailbox.NoSuchMailboxError as e:
            pass
        else:
            messages = []
            for key, msg in mbox.items():
                subpath = mbox._lookup(key)
                if subpath.endswith('.gitignore'):
                    _LOG.debug('skipping non-message {}'.format(subpath))
                    continue
                messages.append(msg)
            messages.sort(key=_message_time)
            for msg in messages:
                message.attach(_MIMEMessage(msg))
    return _construct_email(author=course.robot,
                            targets=[person],
                            subject=subject,
                            message=message)
Example #9
0
def _get_student_submission_email(
    basedir, course, person, assignments, student):
    _LOG.debug('construct student submission email about {} {} for {}'.format(
            student, assignments, person))
    subject = '{} assignment submissions for {}'.format(
        course.name, student.name)
    text = '{}:\n  * {}\n'.format(
        subject, '\n  * '.join(a.name for a in assignments))
    message = _MIMEMultipart('mixed')
    message.attach(_pgp_mime.encodedMIMEText(text))
    for assignment in assignments:
        grade = course.grade(student=student, assignment=assignment)
        if grade is not None:
            text = '{} grade: {}\n'.format(assignment.name, grade.points)
            if grade.comment:
                text += '\n{}\n'.format(grade.comment)
            message.attach(_pgp_mime.encodedMIMEText(text))
        assignment_path = _assignment_path(basedir, assignment, student)
        mpath = _os_path.join(assignment_path, 'mail')
        try:
            mbox = _mailbox.Maildir(mpath, factory=None, create=False)
        except _mailbox.NoSuchMailboxError as e:
            pass
        else:
            messages = []
            for key,msg in mbox.items():
                subpath = mbox._lookup(key)
                if subpath.endswith('.gitignore'):
                    _LOG.debug('skipping non-message {}'.format(subpath))
                    continue
                messages.append(msg)
            messages.sort(key=_message_time)
            for msg in messages:
                message.attach(_MIMEMessage(msg))
    return _construct_email(
        author=course.robot, targets=[person], subject=subject,
        message=message)
Example #10
0
def construct_text_email(author, targets, subject, text, cc=None):
    r"""Build a text/plain email using `Person` instances

    >>> from pygrader.model.person import Person as Person
    >>> author = Person(name='Джон Доу', emails=['*****@*****.**'])
    >>> targets = [Person(name='Jill', emails=['*****@*****.**'])]
    >>> cc = [Person(name='H.D.', emails=['*****@*****.**'])]
    >>> msg = construct_text_email(author, targets, cc=cc,
    ...     subject='Once upon a time', text='Bla bla bla...')
    >>> print(msg.as_string())  # doctest: +REPORT_UDIFF, +ELLIPSIS
    Content-Type: text/plain; charset="us-ascii"
    MIME-Version: 1.0
    Content-Transfer-Encoding: 7bit
    Content-Disposition: inline
    Date: ...
    From: =?utf-8?b?0JTQttC+0L0g0JTQvtGD?= <*****@*****.**>
    Reply-to: =?utf-8?b?0JTQttC+0L0g0JTQvtGD?= <*****@*****.**>
    To: Jill <*****@*****.**>
    Cc: "H.D." <*****@*****.**>
    Subject: Once upon a time
    <BLANKLINE>
    Bla bla bla...

    With unicode text:

    >>> msg = construct_text_email(author, targets, cc=cc,
    ...     subject='Once upon a time', text='Funky ✉.')
    >>> print(msg.as_string())  # doctest: +REPORT_UDIFF, +ELLIPSIS
    Content-Type: text/plain; charset="utf-8"
    MIME-Version: 1.0
    Content-Transfer-Encoding: base64
    Content-Disposition: inline
    Date: ...
    From: =?utf-8?b?0JTQttC+0L0g0JTQvtGD?= <*****@*****.**>
    Reply-to: =?utf-8?b?0JTQttC+0L0g0JTQvtGD?= <*****@*****.**>
    To: Jill <*****@*****.**>
    Cc: "H.D." <*****@*****.**>
    Subject: Once upon a time
    <BLANKLINE>
    RnVua3kg4pyJLg==
    <BLANKLINE>
    """
    message = _pgp_mime.encodedMIMEText(text)
    return construct_email(
        author=author, targets=targets, subject=subject, message=message,
        cc=cc)
Example #11
0
        '--output', action='store_const', const=True,
        help="don't mail the generated message, print it to stdout instead")
    parser.add_argument(
        '-V', '--verbose', default=0, action='count',
        help='increment verbosity')

    args = parser.parse_args()

    if args.verbose:
        _pgp_mime.LOG.setLevel(max(
                _logging.DEBUG, _pgp_mime.LOG.level - 10*args.verbose))

    header_text = read_file(filename=args.header_file, encoding=args.encoding)
    header = _pgp_mime.header_from_text(header_text)
    body_text = read_file(filename=args.body_file, encoding=args.encoding)
    body = _pgp_mime.encodedMIMEText(body_text)
    if args.attachment:
        b = _MIMEMultipart()
        b.attach(body)
        body = b
        _mimetypes.init()
        for attachment in args.attachment:
            body.attach(load_attachment(
                    filename=attachment, encoding=args.encoding))

    config = _configparser.ConfigParser()
    config.read(args.config)
    client_params = _pgp_mime.get_client_params(config)

    if args.sign_as:
        signers = [args.sign_as]
Example #12
0
def run(basedir,
        course,
        message,
        person,
        subject,
        max_late=0,
        dry_run=None,
        **kwargs):
    """
    >>> from pgp_mime.email import encodedMIMEText
    >>> from ..test.course import StubCourse
    >>> from . import Response
    >>> course = StubCourse()
    >>> person = list(
    ...     course.course.find_people(email='*****@*****.**'))[0]
    >>> message = encodedMIMEText('The answer is 42.')
    >>> message['Message-ID'] = '<*****@*****.**>'
    >>> message['Received'] = (
    ...     'from smtp.home.net (smtp.home.net [123.456.123.456]) '
    ...     'by smtp.mail.uu.edu (Postfix) with ESMTP id 5BA225C83EF '
    ...     'for <*****@*****.**>; Sun, 09 Oct 2011 11:50:46 -0400 (EDT)')
    >>> subject = '[submit] assignment 1'
    >>> try:
    ...     run(basedir=course.basedir, course=course.course, message=message,
    ...         person=person, subject=subject, max_late=0)
    ... except Response as e:
    ...     print('respond with:')
    ...     print(e.message.as_string())
    ... # doctest: +ELLIPSIS, +REPORT_UDIFF
    respond with:
    Content-Type: text/plain; charset="us-ascii"
    MIME-Version: 1.0
    Content-Transfer-Encoding: 7bit
    Content-Disposition: inline
    Subject: Received Assignment 1 submission
    <BLANKLINE>
    We received your submission for Assignment 1 on ....

    >>> course.cleanup()
    """
    time = _message_time(message=message)
    assignment = _get_assignment(course=course, subject=subject)
    assignment_path = _assignment_path(basedir, assignment, person)
    _save_local_message_copy(msg=message,
                             person=person,
                             assignment_path=assignment_path,
                             dry_run=dry_run)
    _extract_mime(message=message, output=assignment_path, dry_run=dry_run)
    _check_late(basedir=basedir,
                assignment=assignment,
                person=person,
                time=time,
                max_late=max_late,
                dry_run=dry_run)
    if time:
        time_str = 'on {}'.format(_formatdate(time))
    else:
        time_str = 'at an unknown time'
    message = _pgp_mime.encodedMIMEText(
        ('We received your submission for {} {}.').format(
            assignment.name, time_str))
    message['Subject'] = 'Received {} submission'.format(assignment.name)
    raise _Response(message=message)
Example #13
0
    parser.add_argument('-V',
                        '--verbose',
                        default=0,
                        action='count',
                        help='increment verbosity')

    args = parser.parse_args()

    if args.verbose:
        _pgp_mime.LOG.setLevel(
            max(_logging.DEBUG, _pgp_mime.LOG.level - 10 * args.verbose))

    header_text = read_file(filename=args.header_file, encoding=args.encoding)
    header = _pgp_mime.header_from_text(header_text)
    body_text = read_file(filename=args.body_file, encoding=args.encoding)
    body = _pgp_mime.encodedMIMEText(body_text)
    if args.attachment:
        b = _MIMEMultipart()
        b.attach(body)
        body = b
        _mimetypes.init()
        for attachment in args.attachment:
            body.attach(
                load_attachment(filename=attachment, encoding=args.encoding))

    config = _configparser.ConfigParser()
    config.read(args.config)
    client_params = _pgp_mime.get_client_params(config)

    if args.sign_as:
        signers = [args.sign_as]