~launchpad-pqm/launchpad/devel

13668.1.21 by Curtis Hovey
Updated copyrights.
1
# Copyright 2010-2011 Canonical Ltd.  This software is licensed under the
12043.4.2 by Gavin Panella
Move AnswerTrackerHandler to lp.answers.mail.handler.
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
4
"""Handle incoming Answers email."""
5
6
__metaclass__ = type
12043.4.3 by Gavin Panella
Reenable emailinterface.txt test for Answers.
7
__all__ = [
8
    "AnswerTrackerHandler",
9
    ]
12043.4.2 by Gavin Panella
Move AnswerTrackerHandler to lp.answers.mail.handler.
10
11
import re
12
13
from zope.component import getUtility
14
from zope.interface import implements
15
13668.1.22 by Curtis Hovey
Sorted imports.
16
from lp.answers.enums import QuestionStatus
17
from lp.answers.interfaces.questioncollection import IQuestionSet
13668.1.4 by Curtis Hovey
Moved c.l.interfaces.mail to lp.services.mail.interfaces.
18
from lp.services.mail.interfaces import IMailHandler
12929.9.2 by j.c.sackett
Moved messages from canonical to lp.services
19
from lp.services.messages.interfaces.message import IMessageSet
14612.2.1 by William Grant
format-imports on lib/. So many imports.
20
from lp.services.webapp.interfaces import ILaunchBag
12043.4.2 by Gavin Panella
Move AnswerTrackerHandler to lp.answers.mail.handler.
21
22
23
class AnswerTrackerHandler:
24
    """Handles emails sent to the Answer Tracker."""
25
26
    implements(IMailHandler)
27
28
    allow_unknown_users = False
29
30
    # XXX flacoste 2007-04-23: The 'ticket' part is there for backward
31
    # compatibility with the old notification address. We probably want to
32
    # remove it in the future.
33
    _question_address = re.compile(r'^(ticket|question)(?P<id>\d+)@.*')
34
35
    def process(self, signed_msg, to_addr, filealias=None, log=None):
36
        """See IMailHandler."""
37
        match = self._question_address.match(to_addr)
38
        if not match:
39
            return False
40
41
        question_id = int(match.group('id'))
42
        question = getUtility(IQuestionSet).get(question_id)
43
        if question is None:
44
            # No such question, don't process the email.
45
            return False
46
47
        messageset = getUtility(IMessageSet)
48
        message = messageset.fromEmail(
49
            signed_msg.parsed_string,
50
            owner=getUtility(ILaunchBag).user,
51
            filealias=filealias,
52
            parsed_message=signed_msg)
53
54
        if message.owner == question.owner:
55
            self.processOwnerMessage(question, message)
56
        else:
57
            self.processUserMessage(question, message)
58
        return True
59
60
    def processOwnerMessage(self, question, message):
61
        """Choose the right workflow action for a message coming from
62
        the question owner.
63
64
        When the question status is OPEN or NEEDINFO,
65
        the message is a GIVEINFO action; when the status is ANSWERED
66
        or EXPIRED, we interpret the message as a reopenening request;
67
        otherwise it's a comment.
68
        """
69
        if question.status in [
70
            QuestionStatus.OPEN, QuestionStatus.NEEDSINFO]:
71
            question.giveInfo(message)
72
        elif question.status in [
73
            QuestionStatus.ANSWERED, QuestionStatus.EXPIRED]:
74
            question.reopen(message)
75
        else:
76
            question.addComment(message.owner, message)
77
78
    def processUserMessage(self, question, message):
79
        """Choose the right workflow action for a message coming from a user
80
        that is not the question owner.
81
82
        When the question status is OPEN, NEEDSINFO, or ANSWERED, we interpret
83
        the message as containing an answer. (If it was really a request for
84
        more information, the owner will still be able to answer it while
85
        reopening the request.)
86
87
        In the other status, the message is a comment without status change.
88
        """
89
        if question.status in [
90
            QuestionStatus.OPEN, QuestionStatus.NEEDSINFO,
91
        QuestionStatus.ANSWERED]:
92
            question.giveAnswer(message.owner, message)
93
        else:
94
            # In the other states, only a comment can be added.
95
            question.addComment(message.owner, message)