1
# Copyright 2011 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4
"""Job classes related to QuestionJob."""
12
from storm.expr import (
15
from storm.locals import (
20
from zope.component import getUtility
21
from zope.interface import (
26
from lazr.delegates import delegates
28
from canonical.database.enumcol import EnumCol
29
from canonical.launchpad.interfaces.lpstorm import (
32
from canonical.launchpad.scripts import log
33
from lp.answers.enums import QuestionJobType
34
from lp.answers.interfaces.questionjob import (
37
IQuestionEmailJobSource,
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
47
class QuestionJob(StormBase):
48
"""A Job for queued question emails."""
50
implements(IQuestionJob)
52
__storm_table__ = 'QuestionJob'
54
id = Int(primary=True)
56
job_id = Int(name='job')
57
job = Reference(job_id, Job.id)
59
job_type = EnumCol(enum=QuestionJobType, notNull=True)
61
question_id = Int(name='question')
62
question = Reference(question_id, Question.id)
64
_json_data = Unicode('json_data')
66
def __init__(self, question, job_type, metadata):
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
74
super(QuestionJob, self).__init__()
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')
83
"<{self.__class__.__name__} for question {self.question.id}; "
84
"status={self.job.status}>").format(self=self)
88
"""See `IQuestionJob`."""
89
return simplejson.loads(self._json_data)
92
class QuestionEmailJob(BaseRunnableJob):
93
"""Intermediate class for deriving from QuestionJob."""
94
delegates(IQuestionJob)
95
implements(IQuestionEmailJob)
96
classProvides(IQuestionEmailJobSource)
98
def __init__(self, job):
101
class_job_type = QuestionJobType.EMAIL
104
def create(cls, question, user, subject, body, headers):
105
"""See `IQuestionJob`."""
113
question=question, job_type=cls.class_job_type, metadata=metadata)
118
"""See `IJobSource`."""
119
store = IMasterStore(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)
128
"""See `IQuestionEmailJob`."""
129
return getUtility(IPersonSet).get(self.metadata['user'])
133
"""See `IQuestionEmailJob`."""
134
return self.metadata['subject']
138
"""See `IQuestionEmailJob`."""
139
return self.metadata['body']
143
"""See `IQuestionEmailJob`."""
144
return self.metadata['headers']
148
"""See `IRunnableJob`."""
149
return self.__class__.__name__
151
def getOopsVars(self):
152
"""See `IRunnableJob`."""
153
vars = BaseRunnableJob.getOopsVars(self)
155
('question', self.question.id),
156
('user', self.user.name),
160
def getErrorRecipients(self):
161
"""See `IRunnableJob`."""
167
"%s will send email for question %s.",
168
self.log_name, self.question.id)
169
# Extract and adapt QuestionNotification.send().
171
"%s has sent email for question %s.",
172
self.log_name, self.question.id)