~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/answers/model/questionjob.py

  • Committer: Launchpad Patch Queue Manager
  • Date: 2011-04-27 21:08:24 UTC
  • mfrom: (12919.3.20 question-email-0)
  • Revision ID: launchpad@pqm.canonical.com-20110427210824-n35ei6vy3eftypa8
[r=jcsackett][no-qa] Create a QuestionJob that can queue emails.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2011 Canonical Ltd.  This software is licensed under the
 
2
# GNU Affero General Public License version 3 (see the file LICENSE).
 
3
 
 
4
"""Job classes related to QuestionJob."""
 
5
 
 
6
__metaclass__ = type
 
7
__all__ = [
 
8
    'QuestionJob',
 
9
    ]
 
10
 
 
11
import simplejson
 
12
from storm.expr import (
 
13
    And,
 
14
    )
 
15
from storm.locals import (
 
16
    Int,
 
17
    Reference,
 
18
    Unicode,
 
19
    )
 
20
from zope.component import getUtility
 
21
from zope.interface import (
 
22
    classProvides,
 
23
    implements,
 
24
    )
 
25
 
 
26
from lazr.delegates import delegates
 
27
 
 
28
from canonical.database.enumcol import EnumCol
 
29
from canonical.launchpad.interfaces.lpstorm import (
 
30
    IMasterStore,
 
31
    )
 
32
from canonical.launchpad.scripts import log
 
33
from lp.answers.enums import QuestionJobType
 
34
from lp.answers.interfaces.questionjob import (
 
35
    IQuestionJob,
 
36
    IQuestionEmailJob,
 
37
    IQuestionEmailJobSource,
 
38
    )
 
39
from lp.answers.model.question import Question
 
40
from lp.registry.interfaces.person import IPersonSet
 
41
from lp.services.database.stormbase import StormBase
 
42
from lp.services.job.model.job import Job
 
43
from lp.services.job.runner import BaseRunnableJob
 
44
from lp.services.propertycache import cachedproperty
 
45
 
 
46
 
 
47
class QuestionJob(StormBase):
 
48
    """A Job for queued question emails."""
 
49
 
 
50
    implements(IQuestionJob)
 
51
 
 
52
    __storm_table__ = 'QuestionJob'
 
53
 
 
54
    id = Int(primary=True)
 
55
 
 
56
    job_id = Int(name='job')
 
57
    job = Reference(job_id, Job.id)
 
58
 
 
59
    job_type = EnumCol(enum=QuestionJobType, notNull=True)
 
60
 
 
61
    question_id = Int(name='question')
 
62
    question = Reference(question_id, Question.id)
 
63
 
 
64
    _json_data = Unicode('json_data')
 
65
 
 
66
    def __init__(self, question, job_type, metadata):
 
67
        """Constructor.
 
68
 
 
69
        :param question: The question related to this job.
 
70
        :param job_type: The specific job being performed for the question.
 
71
        :param metadata: The type-specific variables, as a JSON-compatible
 
72
            dict.
 
73
        """
 
74
        super(QuestionJob, self).__init__()
 
75
        self.job = Job()
 
76
        self.job_type = job_type
 
77
        self.question = question
 
78
        json_data = simplejson.dumps(metadata)
 
79
        self._json_data = json_data.decode('utf-8')
 
80
 
 
81
    def __repr__(self):
 
82
        return (
 
83
            "<{self.__class__.__name__} for question {self.question.id}; "
 
84
            "status={self.job.status}>").format(self=self)
 
85
 
 
86
    @property
 
87
    def metadata(self):
 
88
        """See `IQuestionJob`."""
 
89
        return simplejson.loads(self._json_data)
 
90
 
 
91
 
 
92
class QuestionEmailJob(BaseRunnableJob):
 
93
    """Intermediate class for deriving from QuestionJob."""
 
94
    delegates(IQuestionJob)
 
95
    implements(IQuestionEmailJob)
 
96
    classProvides(IQuestionEmailJobSource)
 
97
 
 
98
    def __init__(self, job):
 
99
        self.context = job
 
100
 
 
101
    class_job_type = QuestionJobType.EMAIL
 
102
 
 
103
    @classmethod
 
104
    def create(cls, question, user, subject, body, headers):
 
105
        """See `IQuestionJob`."""
 
106
        metadata = {
 
107
            'user': user.id,
 
108
            'subject': subject,
 
109
            'body': body,
 
110
            'headers': headers,
 
111
            }
 
112
        job = QuestionJob(
 
113
            question=question, job_type=cls.class_job_type, metadata=metadata)
 
114
        return cls(job)
 
115
 
 
116
    @classmethod
 
117
    def iterReady(cls):
 
118
        """See `IJobSource`."""
 
119
        store = IMasterStore(QuestionJob)
 
120
        jobs = store.find(
 
121
            QuestionJob,
 
122
            And(QuestionJob.job_type == cls.class_job_type,
 
123
                QuestionJob.job_id.is_in(Job.ready_jobs)))
 
124
        return (cls(job) for job in jobs)
 
125
 
 
126
    @cachedproperty
 
127
    def user(self):
 
128
        """See `IQuestionEmailJob`."""
 
129
        return getUtility(IPersonSet).get(self.metadata['user'])
 
130
 
 
131
    @property
 
132
    def subject(self):
 
133
        """See `IQuestionEmailJob`."""
 
134
        return self.metadata['subject']
 
135
 
 
136
    @property
 
137
    def body(self):
 
138
        """See `IQuestionEmailJob`."""
 
139
        return self.metadata['body']
 
140
 
 
141
    @property
 
142
    def headers(self):
 
143
        """See `IQuestionEmailJob`."""
 
144
        return self.metadata['headers']
 
145
 
 
146
    @property
 
147
    def log_name(self):
 
148
        """See `IRunnableJob`."""
 
149
        return self.__class__.__name__
 
150
 
 
151
    def getOopsVars(self):
 
152
        """See `IRunnableJob`."""
 
153
        vars = BaseRunnableJob.getOopsVars(self)
 
154
        vars.extend([
 
155
            ('question', self.question.id),
 
156
            ('user', self.user.name),
 
157
            ])
 
158
        return vars
 
159
 
 
160
    def getErrorRecipients(self):
 
161
        """See `IRunnableJob`."""
 
162
        return self.user
 
163
 
 
164
    def run(self):
 
165
        """Send emails."""
 
166
        log.debug(
 
167
            "%s will send email for question %s.",
 
168
            self.log_name, self.question.id)
 
169
        # Extract and adapt QuestionNotification.send().
 
170
        log.debug(
 
171
            "%s has sent email for question %s.",
 
172
            self.log_name, self.question.id)