~launchpad-pqm/launchpad/devel

7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
1
=======================
2
Answer tracker workflow
3
=======================
4
5
The state of a question is tracked through its status, which model a
6
question's lifecycle.  These are defined in the QuestionStatus enumeration.
7
12915.5.1 by Curtis Hovey
Rename questionenums.py to the new standard or enums.py
8
    >>> from lp.answers.enums import QuestionStatus
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
9
    >>> for status in QuestionStatus.items:
10
    ...     print status.name
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
11
    OPEN
12
    NEEDSINFO
13
    ANSWERED
3691.197.22 by Francis J. Lacoste
Rename ANSWERED_CONFIRMED to SOLVED
14
    SOLVED
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
15
    EXPIRED
16
    INVALID
17
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
18
Status change occurs as a consequence of a user's action.  The possible
3691.398.17 by Francis J. Lacoste
Rename dbschemas.
19
actions are defined in the QuestionAction enumeration.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
20
12915.5.1 by Curtis Hovey
Rename questionenums.py to the new standard or enums.py
21
    >>> from lp.answers.enums import QuestionAction
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
22
    >>> for status in QuestionAction.items:
23
    ...     print status.name
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
24
    REQUESTINFO
25
    GIVEINFO
26
    COMMENT
27
    ANSWER
28
    CONFIRM
29
    REJECT
30
    EXPIRE
31
    REOPEN
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
32
    SETSTATUS
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
33
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
34
Each defined action can be executed.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
35
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
36
No Privileges Person is the submitter of questions.  Sample Person is an
37
answer contact for the Ubuntu distribution.  Marilize Coetze is another user
38
providing support.  Stub is a Launchpad administrator that isn't also in the
39
Ubuntu Team owning the distribution.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
40
41
    >>> login('no-priv@canonical.com')
42
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
43
    >>> from lp.registry.interfaces.distribution import IDistributionSet
44
    >>> from lp.registry.interfaces.person import IPersonSet
45
    >>> from lp.services.worlddata.interfaces.language import ILanguageSet
3691.197.61 by Francis J. Lacoste
Add sanity check for admin not being an ubuntu Team member
46
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
47
    >>> personset = getUtility(IPersonSet)
48
    >>> sample_person = personset.getByEmail('test@canonical.com')
49
    >>> no_priv = personset.getByEmail('no-priv@canonical.com')
50
    >>> marilize = personset.getByEmail('marilize@hbd.com')
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
51
    >>> stub = personset.getByName('stub')
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
52
53
    >>> ubuntu = getUtility(IDistributionSet)['ubuntu']
4215.2.15 by Curtis Hovey
Added English to Lp. Rvised tests; removing old rules and added new rule. Translation tests probably fail. This branch needs some cleaning too.
54
    >>> english = getUtility(ILanguageSet)['en']
55
    >>> sample_person.addLanguage(english)
12959.4.21 by Curtis Hovey
Alway pass the subscribed_by argument to addAnswerContact.
56
    >>> ubuntu.addAnswerContact(sample_person, sample_person)
3691.197.11 by Francis J. Lacoste
Move workflow tests to support-tracker-workflow.txt
57
    True
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
58
3691.259.2 by Francis J. Lacoste
Convert to Moin-style headers, convert () comments to regular python comments.
59
    # Sanity check: the admin isn't in the team owning the distribution.
3691.197.61 by Francis J. Lacoste
Add sanity check for admin not being an ubuntu Team member
60
    >>> stub.inTeam(ubuntu.owner)
61
    False
62
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
63
A question starts its lifecycle in the Open state.
3691.197.11 by Francis J. Lacoste
Move workflow tests to support-tracker-workflow.txt
64
65
    >>> from datetime import datetime, timedelta
66
    >>> from pytz import UTC
67
    >>> now = datetime.now(UTC)
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
68
    >>> new_question_args = dict(
69
    ...     owner=no_priv,
70
    ...     title='Unable to boot installer',
71
    ...     description="I've tried installing Ubuntu on a Mac. "
72
    ...                 "But the installer never boots.",
73
    ...     datecreated=now,
74
    ...     )
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
75
    >>> question = ubuntu.newQuestion(**new_question_args)
76
    >>> print question.status.title
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
77
    Open
78
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
79
The following scenarios are now possible.
80
81
82
1) Another user helps the submitter with his question
83
=====================================================
84
85
The most common scenario is where another user comes to help the submitter and
86
answers his question.  This may involve exchanging information with the
87
submitter to clarify the question.
88
89
The requestInfo() method is used to ask the user for more information.  This
90
method takes two mandatory parameters: the user asking the question and his
91
question.  It can also takes a 'datecreated' parameter specifying the creation
92
date of the question (which defaults to 'now').
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
93
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
94
    >>> question = ubuntu.newQuestion(**new_question_args)
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
95
    >>> now_plus_one_hour = now + timedelta(hours=1)
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
96
    >>> request_message = question.requestInfo(
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
97
    ...     sample_person, 'What is your Mac model?',
98
    ...     datecreated=now_plus_one_hour)
99
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
100
We now have the IQuestionMessage that was added to the question messages
101
history.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
102
4682.1.6 by Christian Reis
Use webapp.testing's verifyObject in all doctests, which copes with security-proxied objects, and remove the crack I added to the bug and bugtask uses of it.
103
    >>> from canonical.launchpad.webapp.testing import verifyObject
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
104
    >>> from lp.answers.interfaces.questionmessage import IQuestionMessage
3691.398.18 by Francis J. Lacoste
Rename interfaces.
105
    >>> verifyObject(IQuestionMessage, request_message)
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
106
    True
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
107
    >>> request_message == question.messages[-1]
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
108
    True
109
    >>> request_message.datecreated == now_plus_one_hour
110
    True
111
    >>> print request_message.owner.displayname
112
    Sample Person
113
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
114
The question message contains the action that was executed and the status of
115
the question after the action was executed.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
116
117
    >>> print request_message.action.name
118
    REQUESTINFO
3691.197.55 by Francis J. Lacoste
Rename newstatus to new_status as per new naming convention. Renamed foreign key to ticket__answer__fk as requested by stub.
119
    >>> print request_message.new_status.name
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
120
    NEEDSINFO
121
122
    >>> print request_message.text_contents
123
    What is your Mac model?
124
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
125
The subject of the message was generated automatically.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
126
127
    >>> print request_message.subject
128
    Re: Unable to boot installer
129
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
130
The question is moved to the NEEDSINFO state and the last response date is
131
updated to the message's timestamp.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
132
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
133
    >>> print question.status.name
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
134
    NEEDSINFO
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
135
    >>> question.datelastresponse == now_plus_one_hour
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
136
    True
137
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
138
The question owner can reply to this information by using the giveInfo()
3691.398.18 by Francis J. Lacoste
Rename interfaces.
139
method which adds an IQuestionMessage with action GIVEINFO.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
140
3691.197.109 by Francis J. Lacoste
- Add launchpad.Owner permission.
141
    >>> login('no-priv@canonical.com')
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
142
    >>> now_plus_two_hours = now + timedelta(hours=2)
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
143
    >>> reply_message = question.giveInfo(
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
144
    ...     "I have a PowerMac 7200.", datecreated=now_plus_two_hours)
145
146
    >>> print reply_message.action.name
147
    GIVEINFO
3691.197.55 by Francis J. Lacoste
Rename newstatus to new_status as per new naming convention. Renamed foreign key to ticket__answer__fk as requested by stub.
148
    >>> print reply_message.new_status.name
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
149
    OPEN
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
150
    >>> reply_message == question.messages[-1]
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
151
    True
152
    >>> print reply_message.owner.displayname
153
    No Privileges Person
154
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
155
The question is moved back to the OPEN state and the last query date is
156
updated to the message's creation date.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
157
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
158
    >>> print question.status.name
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
159
    OPEN
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
160
    >>> question.datelastquery == now_plus_two_hours
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
161
    True
162
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
163
Now, the other user has enough information to give an answer to the question.
164
The giveAnswer() method is used for that purpose.  Like the requestInfo()
165
method, it takes two mandatory parameters: the user providing the answer and
166
the answer itself.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
167
3691.197.109 by Francis J. Lacoste
- Add launchpad.Owner permission.
168
    >>> login('test@canonical.com')
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
169
    >>> now_plus_three_hours = now + timedelta(hours=3)
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
170
    >>> answer_message = question.giveAnswer(
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
171
    ...     sample_person,
172
    ...     "You need some configuration on the Mac side "
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
173
    ...     "to boot the installer on that model. Consult "
174
    ...     "https://help.ubuntu.com/community/Installation/OldWorldMacs "
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
175
    ...     "for all the details.",
176
    ...     datecreated=now_plus_three_hours)
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
177
    >>> print answer_message.action.name
178
    ANSWER
3691.197.55 by Francis J. Lacoste
Rename newstatus to new_status as per new naming convention. Renamed foreign key to ticket__answer__fk as requested by stub.
179
    >>> print answer_message.new_status.name
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
180
    ANSWERED
181
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
182
The question's status is changed to ANSWERED and the last response date is
183
updated to contain the date of the message.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
184
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
185
    >>> print question.status.name
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
186
    ANSWERED
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
187
    >>> question.datelastresponse == now_plus_three_hours
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
188
    True
189
3881.1.1 by Francis J. Lacoste
Renamed reference to support tracker in doctests.
190
At that point, the question is considered answered, but we don't have
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
191
feedback from the user on whether it solved his problem or not.  If it
192
doesn't, the user can reopen the question.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
193
3691.197.109 by Francis J. Lacoste
- Add launchpad.Owner permission.
194
    >>> login('no-priv@canonical.com')
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
195
    >>> tomorrow = now + timedelta(days=1)
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
196
    >>> reopen_message = question.reopen(
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
197
    ...     "I installed BootX and I've progressed somewhat. I now get the "
198
    ...     "boot screen. But soon after the Ubuntu progress bar appears, I "
199
    ...     "get a OOM Killer message appearing on the screen.",
200
    ...      datecreated=tomorrow)
201
    >>> print reopen_message.action.name
202
    REOPEN
3691.197.55 by Francis J. Lacoste
Rename newstatus to new_status as per new naming convention. Renamed foreign key to ticket__answer__fk as requested by stub.
203
    >>> print reopen_message.new_status.name
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
204
    OPEN
205
    >>> print reopen_message.owner.displayname
206
    No Privileges Person
207
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
208
This moves back the question to the OPEN state and the last query date is
209
updated to the message's creation date.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
210
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
211
    >>> print question.status.name
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
212
    OPEN
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
213
    >>> question.datelastquery == tomorrow
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
214
    True
215
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
216
Once again, an answer is given.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
217
3691.197.109 by Francis J. Lacoste
- Add launchpad.Owner permission.
218
    >>> login('test@canonical.com')
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
219
    >>> tomorrow_plus_one_hour = tomorrow + timedelta(hours=1)
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
220
    >>> answer2_message = question.giveAnswer(
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
221
    ...     marilize,
222
    ...     "You probably do not have enough RAM to use the "
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
223
    ...     "graphical installer. You can try the alternate CD with the "
224
    ...     "text installer.")
225
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
226
The question is moved back to the ANSWERED state.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
227
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
228
    >>> print question.status.name
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
229
    ANSWERED
230
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
231
The question owner will hopefully come back to confirm that his problem is
232
solved.  He can specify which answer message helped him solved his problem.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
233
3691.197.109 by Francis J. Lacoste
- Add launchpad.Owner permission.
234
    >>> login('no-priv@canonical.com')
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
235
    >>> two_weeks_from_now = now + timedelta(days=14)
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
236
    >>> confirm_message = question.confirmAnswer(
11235.5.4 by Curtis Hovey
hush lint.
237
    ...     "I upgraded to 512M of RAM (found on eBay) and I've successfully "
238
    ...     "managed to install Ubuntu. Thanks for all the help.",
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
239
    ...     datecreated=two_weeks_from_now, answer=answer_message)
240
    >>> print confirm_message.action.name
241
    CONFIRM
3691.197.55 by Francis J. Lacoste
Rename newstatus to new_status as per new naming convention. Renamed foreign key to ticket__answer__fk as requested by stub.
242
    >>> print confirm_message.new_status.name
3691.197.22 by Francis J. Lacoste
Rename ANSWERED_CONFIRMED to SOLVED
243
    SOLVED
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
244
    >>> print confirm_message.owner.displayname
245
    No Privileges Person
246
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
247
The question is moved to the SOLVED state, and the message that solved the
248
question is saved.  The date the question was solved and answerer are also
249
updated.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
250
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
251
    >>> print question.status.name
3691.197.22 by Francis J. Lacoste
Rename ANSWERED_CONFIRMED to SOLVED
252
    SOLVED
4664.2.4 by Curtis Hovey
Renamed attribute from datesolved to date_solved. Doh. This was
253
    >>> question.date_solved == two_weeks_from_now
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
254
    True
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
255
    >>> print question.answerer.displayname
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
256
    Sample Person
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
257
    >>> question.answer == answer_message
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
258
    True
259
4471.5.8 by Curtis Hovey
Revision per review. More work is needed in test_question_workflow.py.
260
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
261
2) Self-answering
262
=================
263
264
In this scenario the user comes back to give the solution to the question
265
himself.  The question owner can choose a best answer message later on.  The
266
workflow permits the question owner to choose an answer before or after the
267
question status is set to SOLVED.
268
269
A new question is posed.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
270
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
271
    >>> question = ubuntu.newQuestion(**new_question_args)
4471.5.1 by Curtis Hovey
Revised workflow to allow the question own to set the question status to Solved without selecting an answser first. An Answer can be confirmed while in the solved state.
272
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
273
The answer provides some useful information to the questioner.
4471.5.8 by Curtis Hovey
Revision per review. More work is needed in test_question_workflow.py.
274
4471.5.1 by Curtis Hovey
Revised workflow to allow the question own to set the question status to Solved without selecting an answser first. An Answer can be confirmed while in the solved state.
275
    >>> login('test@canonical.com')
276
    >>> tomorrow_plus_one_hour = tomorrow + timedelta(hours=1)
277
    >>> alt_answer_message = question.giveAnswer(
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
278
    ...     marilize,
279
    ...     "Are you using a pre-G3 Mac? They are very difficult "
4471.5.1 by Curtis Hovey
Revised workflow to allow the question own to set the question status to Solved without selecting an answser first. An Answer can be confirmed while in the solved state.
280
    ...     "to install to. You must mess with the hardware to trick "
281
    ...     "the core chips to let it install. You may not want to do this.")
282
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
283
The question has researched the problem, and has comes to a solution himself.
4471.5.8 by Curtis Hovey
Revision per review. More work is needed in test_question_workflow.py.
284
4471.5.1 by Curtis Hovey
Revised workflow to allow the question own to set the question status to Solved without selecting an answser first. An Answer can be confirmed while in the solved state.
285
    >>> login('no-priv@canonical.com')
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
286
    >>> self_answer_message = question.giveAnswer(
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
287
    ...     no_priv,
288
    ...     "I found some instructions on the Wiki on how to "
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
289
    ...     "install BootX to boot the installation CD on OldWorld Mac: "
290
    ...     "https://help.ubuntu.com/community/Installation/OldWorldMacs "
291
    ...     "This is complicated and since it's a very old machine, not "
292
    ...     "worth the trouble.",
293
    ...     datecreated=now_plus_one_hour)
294
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
295
The question owner is considered to have given information that the problem is
296
solved and the question is moved to the SOLVED state.  The 'answerer'
297
will be the question owner.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
298
299
    >>> print self_answer_message.action.name
300
    CONFIRM
3691.197.55 by Francis J. Lacoste
Rename newstatus to new_status as per new naming convention. Renamed foreign key to ticket__answer__fk as requested by stub.
301
    >>> print self_answer_message.new_status.name
3691.197.22 by Francis J. Lacoste
Rename ANSWERED_CONFIRMED to SOLVED
302
    SOLVED
4471.5.8 by Curtis Hovey
Revision per review. More work is needed in test_question_workflow.py.
303
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
304
    >>> print question.status.name
3691.197.22 by Francis J. Lacoste
Rename ANSWERED_CONFIRMED to SOLVED
305
    SOLVED
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
306
    >>> print question.answerer.displayname
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
307
    No Privileges Person
4664.2.4 by Curtis Hovey
Renamed attribute from datesolved to date_solved. Doh. This was
308
    >>> question.date_solved == now_plus_one_hour
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
309
    True
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
310
    >>> print question.answer
311
    None
4471.5.1 by Curtis Hovey
Revised workflow to allow the question own to set the question status to Solved without selecting an answser first. An Answer can be confirmed while in the solved state.
312
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
313
The question owner can still specify which message helped him solved his
314
problem.  The confirmAnswer() method is used when the question owner chooses
315
another user's answer as a best answer.  The status will remain SOLVED.  The
316
'answerer' will be the message owner, and the 'answer' will be the message.
317
The question's solution date will be the date of the answer message.
4471.5.1 by Curtis Hovey
Revised workflow to allow the question own to set the question status to Solved without selecting an answser first. An Answer can be confirmed while in the solved state.
318
319
    >>> confirm_message = question.confirmAnswer(
320
    ...     "Thanks Marilize for your help. I don't think I'll put Ubuntu "
321
    ...     "Ubuntu on my Mac.",
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
322
    ...     datecreated=now_plus_one_hour,
323
    ...     answer=alt_answer_message)
4471.5.1 by Curtis Hovey
Revised workflow to allow the question own to set the question status to Solved without selecting an answser first. An Answer can be confirmed while in the solved state.
324
    >>> print confirm_message.action.name
325
    CONFIRM
326
    >>> print confirm_message.new_status.name
327
    SOLVED
328
    >>> print confirm_message.owner.displayname
329
    No Privileges Person
330
4471.5.8 by Curtis Hovey
Revision per review. More work is needed in test_question_workflow.py.
331
    >>> print question.status.name
332
    SOLVED
333
    >>> print question.answerer.displayname
334
    Marilize Coetzee
335
    >>> question.answer == alt_answer_message
336
    True
4664.2.4 by Curtis Hovey
Renamed attribute from datesolved to date_solved. Doh. This was
337
    >>> question.date_solved == now_plus_one_hour
4471.5.8 by Curtis Hovey
Revision per review. More work is needed in test_question_workflow.py.
338
    True
339
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
340
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
341
3) The question expires
342
=======================
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
343
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
344
It is also possible that nobody will answer the question, either because the
345
question is too complex or too vague.  These questions are expired by using
346
the expireQuestion() method.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
347
3691.197.109 by Francis J. Lacoste
- Add launchpad.Owner permission.
348
    >>> login('no-priv@canonical.com')
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
349
    >>> question = ubuntu.newQuestion(**new_question_args)
350
    >>> expire_message = question.expireQuestion(
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
351
    ...     sample_person,
352
    ...     "There was no activity on this question for two "
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
353
    ...     "weeks and this question was expired. If you are still having "
3881.1.1 by Francis J. Lacoste
Renamed reference to support tracker in doctests.
354
    ...     "this problem you should reopen the question and provide more "
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
355
    ...     "information about your problem.",
356
    ...     datecreated=two_weeks_from_now)
357
    >>> print expire_message.action.name
358
    EXPIRE
3691.197.55 by Francis J. Lacoste
Rename newstatus to new_status as per new naming convention. Renamed foreign key to ticket__answer__fk as requested by stub.
359
    >>> print expire_message.new_status.name
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
360
    EXPIRED
361
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
362
The question is moved to the EXPIRED state and the last response date is
363
updated to the message creation date.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
364
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
365
    >>> print question.status.name
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
366
    EXPIRED
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
367
    >>> question.datelastresponse == two_weeks_from_now
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
368
    True
369
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
370
If the user comes back and provide more information, the question will be
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
371
reopened.
372
373
    >>> much_later = now + timedelta(days=30)
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
374
    >>> reopen_message = question.reopen(
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
375
    ...     "I'm installing on PowerMac 7200/120 with 32 Megs of RAM. After "
376
    ...     "I insert the CD and restart the computer, it boots straight "
377
    ...     "into Mac OS/9 instead of booting the installer.",
378
    ...     datecreated=much_later)
379
    >>> print reopen_message.action.name
380
    REOPEN
381
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
382
The question status is changed back to OPEN and the last query date is
383
updated.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
384
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
385
    >>> print question.status.name
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
386
    OPEN
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
387
    >>> question.datelastquery == much_later
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
388
    True
389
390
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
391
4) The question is invalid
392
==========================
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
393
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
394
In this scenario the user posts an inappropriate message, such as a spam
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
395
message or a request for Ubuntu CDs.
396
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
397
    >>> spam_question = ubuntu.newQuestion(
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
398
    ...     no_priv, 'CDs', 'Please send 10 Ubuntu Dapper CDs.',
399
    ...     datecreated=now)
400
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
401
Such questions can be rejected by an answer contact, a product or distribution
402
owner, or a Launchpad administrator.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
403
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
404
The canReject() method can be used to test if a user is allowed to reject the
405
question.  While neither No Privileges Person nor Marilize are able to reject
406
questions, Sample Person and the Ubuntu owner can.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
407
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
408
    >>> spam_question.canReject(no_priv)
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
409
    False
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
410
    >>> spam_question.canReject(marilize)
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
411
    False
412
3881.1.1 by Francis J. Lacoste
Renamed reference to support tracker in doctests.
413
    # Answer contact
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
414
    >>> spam_question.canReject(sample_person)
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
415
    True
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
416
    >>> spam_question.canReject(ubuntu.owner)
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
417
    True
418
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
419
As a Launchpad administrator, so can Stub.
420
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
421
    >>> spam_question.canReject(stub)
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
422
    True
423
3691.197.108 by Francis J. Lacoste
Remove Unauthorized from database code.
424
    >>> login(marilize.preferredemail.email)
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
425
    >>> spam_question.reject(
12756.3.17 by William Grant
Destroy more references.
426
    ...     marilize, "We don't send free CDs any more.")
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
427
    Traceback (most recent call last):
428
      ...
3691.197.63 by Francis J. Lacoste
Raise Unauthorized instead of AssertionError when user cannot reject a request.
429
    Unauthorized: ...
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
430
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
431
When rejecting a question, a comment explaining the reason is given.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
432
3691.197.108 by Francis J. Lacoste
Remove Unauthorized from database code.
433
    >>> login('test@canonical.com')
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
434
    >>> reject_message = spam_question.reject(
12756.3.17 by William Grant
Destroy more references.
435
    ...     sample_person, "We don't send free CDs any more.",
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
436
    ...     datecreated=now_plus_one_hour)
437
    >>> print reject_message.action.name
438
    REJECT
3691.197.55 by Francis J. Lacoste
Rename newstatus to new_status as per new naming convention. Renamed foreign key to ticket__answer__fk as requested by stub.
439
    >>> print reject_message.new_status.name
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
440
    INVALID
441
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
442
After rejection, the question is marked as invalid and the last response date
443
is updated.
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
444
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
445
    >>> print spam_question.status.name
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
446
    INVALID
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
447
    >>> spam_question.datelastresponse == now_plus_one_hour
3691.197.12 by Francis J. Lacoste
- Removed can_be_rejected attribute from ITicket.
448
    True
449
3691.197.33 by Francis J. Lacoste
Consider a rejection message as answering the ticket.
450
The rejection message is also considered as answering the message, so the
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
451
solution date, answerer, and answer are also updated.
3691.197.33 by Francis J. Lacoste
Consider a rejection message as answering the ticket.
452
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
453
    >>> spam_question.answer == reject_message
3691.197.33 by Francis J. Lacoste
Consider a rejection message as answering the ticket.
454
    True
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
455
    >>> print spam_question.answerer.displayname
3691.197.33 by Francis J. Lacoste
Consider a rejection message as answering the ticket.
456
    Sample Person
4664.2.4 by Curtis Hovey
Renamed attribute from datesolved to date_solved. Doh. This was
457
    >>> spam_question.date_solved == now_plus_one_hour
3691.197.33 by Francis J. Lacoste
Consider a rejection message as answering the ticket.
458
    True
459
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
460
461
Other scenarios
462
===============
463
464
Many other scenarios are possible and some are likely more common than others.
465
For example, it is likely that a user will directly answer a question without
466
asking for other information first.  Sometimes, the original questioner won't
467
come back to confirm that an answer solved his problem.
468
469
Another likely scenario is where the question will expire in the NEEDSINFO
470
state because the question owner doesn't reply to the request for more
471
information.  All of these scenarios are covered by this API, though it is not
472
necessary to cover all these various possibilities here.  (The
473
../tests/test_question_workflow.py functional test exercises all the various
474
possible transitions.)
475
476
477
Changing the question status
478
============================
479
480
It is not possible to change the status attribute directly.
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
481
3691.197.62 by Francis J. Lacoste
Explicitely login as admin user to clarify that nobody can set the status directly
482
    >>> login('foo.bar@canonical.com')
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
483
    >>> question = ubuntu.newQuestion(**new_question_args)
484
    >>> question.status = QuestionStatus.INVALID
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
485
    Traceback (most recent call last):
486
      ...
487
    ForbiddenAttribute...
488
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
489
A user having launchpad.Admin permission on the question can set the question
490
status to an arbitrary value, by giving the new status and a comment
491
explaining the status change.
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
492
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
493
    >>> old_datelastquery = question.datelastquery
3691.197.104 by Francis J. Lacoste
- Protect setStatus by launchpad.Admin.
494
    >>> login(stub.preferredemail.email)
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
495
    >>> status_change_message = question.setStatus(
3691.398.17 by Francis J. Lacoste
Rename dbschemas.
496
    ...      stub, QuestionStatus.INVALID, 'Changed status to INVALID',
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
497
    ...     datecreated=now_plus_one_hour)
498
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
499
The method returns the IQuestionMessage recording the change.
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
500
501
    >>> print status_change_message.action.name
502
    SETSTATUS
3691.197.55 by Francis J. Lacoste
Rename newstatus to new_status as per new naming convention. Renamed foreign key to ticket__answer__fk as requested by stub.
503
    >>> print status_change_message.new_status.name
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
504
    INVALID
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
505
    >>> print question.status.name
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
506
    INVALID
507
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
508
The status change updates the last response date.
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
509
4213.1.2 by Francis J. Lacoste
Update datelastresponse on setStatus.
510
    >>> question.datelastresponse == now_plus_one_hour
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
511
    True
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
512
    >>> question.datelastquery == old_datelastquery
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
513
    True
514
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
515
If an answer was present on the question, the status change also clears
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
516
the answer and solution date.
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
517
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
518
    >>> msg = question.setStatus(stub, QuestionStatus.OPEN, 'Status change.')
519
    >>> answer_message = question.giveAnswer(sample_person, 'Install BootX.')
3691.197.109 by Francis J. Lacoste
- Add launchpad.Owner permission.
520
521
    >>> login('no-priv@canonical.com')
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
522
    >>> msg = question.confirmAnswer('This worked.', answer=answer_message)
4664.2.4 by Curtis Hovey
Renamed attribute from datesolved to date_solved. Doh. This was
523
    >>> question.date_solved is not None
3691.197.92 by Francis J. Lacoste
Remove write access to all internally-managed attributes
524
    True
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
525
    >>> question.answer == answer_message
3691.197.92 by Francis J. Lacoste
Remove write access to all internally-managed attributes
526
    True
3691.197.109 by Francis J. Lacoste
- Add launchpad.Owner permission.
527
528
    >>> login(stub.preferredemail.email)
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
529
    >>> status_change_message = question.setStatus(
3881.1.1 by Francis J. Lacoste
Renamed reference to support tracker in doctests.
530
    ...     stub, QuestionStatus.OPEN, 'Reopen the question',
3691.197.92 by Francis J. Lacoste
Remove write access to all internally-managed attributes
531
    ...     datecreated=now_plus_one_hour)
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
532
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
533
    >>> print question.date_solved
534
    None
535
    >>> print question.answer
536
    None
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
537
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
538
When the status is changed by a user who doesn't have the launchpad.Admin
539
permission, an Unauthorized exception is thrown.
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
540
3691.197.108 by Francis J. Lacoste
Remove Unauthorized from database code.
541
    >>> login('test@canonical.com')
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
542
    >>> question.setStatus(sample_person, QuestionStatus.EXPIRED, 'Expire.')
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
543
    Traceback (most recent call last):
544
      ...
545
    Unauthorized...
546
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
547
548
Adding Comments Without Changing the Status
549
===========================================
550
551
Comments can be added to questions without changing the question's status.
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
552
3691.197.104 by Francis J. Lacoste
- Protect setStatus by launchpad.Admin.
553
    >>> login('no-priv@canonical.com')
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
554
    >>> old_status = question.status
555
    >>> old_datelastresponse = question.datelastresponse
556
    >>> old_datelastquery = question.datelastquery
557
    >>> comment = question.addComment(
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
558
    ...     no_priv, 'This is a comment.',
559
    ...     datecreated=now_plus_two_hours)
560
561
    >>> print comment.action.name
562
    COMMENT
3691.197.55 by Francis J. Lacoste
Rename newstatus to new_status as per new naming convention. Renamed foreign key to ticket__answer__fk as requested by stub.
563
    >>> comment.new_status == old_status
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
564
    True
565
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
566
This method does not update the last response date or last query date.
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
567
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
568
    >>> question.datelastresponse == old_datelastresponse
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
569
    True
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
570
    >>> question.datelastquery == old_datelastquery
3691.197.16 by Francis J. Lacoste
- Add setStatus() and addComment() methods.
571
    True
3691.197.31 by Francis J. Lacoste
All workflow methods now triggers an ISQLObjectModifiedEvent.
572
8848.2.1 by Curtis Hovey
Allow answer contacts to assign questions.
573
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
574
Setting the question assignee
575
=============================
8848.2.1 by Curtis Hovey
Allow answer contacts to assign questions.
576
577
Users with launchpad.Moderator privileges, which are answer contacts,
578
question target owners, and admins, can assign someone to answer a question.
579
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
580
Sample Person is an answer contact for ubuntu, so he can set the assignee.
8848.2.1 by Curtis Hovey
Allow answer contacts to assign questions.
581
582
    >>> login('test@canonical.com')
583
    >>> question.assignee = stub
584
    >>> print question.assignee.displayname
585
    Stuart Bishop
586
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
587
Users without launchpad.Moderator privileges cannot set the assignee.
8848.2.1 by Curtis Hovey
Allow answer contacts to assign questions.
588
589
    >>> login('no-priv@canonical.com')
590
    >>> question.assignee = sample_person
591
    Traceback (most recent call last):
592
      ...
11326.2.11 by Brad Crittenden
Updated permissions for distro, FAQ, and questions.
593
    Unauthorized: (<Question ...>, 'assignee', 'launchpad.Append')
8848.2.1 by Curtis Hovey
Allow answer contacts to assign questions.
594
595
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
596
Events
597
======
3691.197.31 by Francis J. Lacoste
All workflow methods now triggers an ISQLObjectModifiedEvent.
598
7876.3.6 by Francis J. Lacoste
Used ISQLObject from lazr.lifecycle
599
Each of the workflow methods will trigger a ObjectCreatedEvent for
600
the message they create and a ObjectModifiedEvent for the question.
3691.197.31 by Francis J. Lacoste
All workflow methods now triggers an ISQLObjectModifiedEvent.
601
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
602
    # Register an event listener that will print events it receives.
7876.3.6 by Francis J. Lacoste
Used ISQLObject from lazr.lifecycle
603
    >>> from lazr.lifecycle.interfaces import (
604
    ...     IObjectCreatedEvent, IObjectModifiedEvent)
11716.1.12 by Curtis Hovey
Sorted imports in doctests.
605
    >>> from canonical.lazr.testing.event import TestEventListener
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
606
    >>> from lp.answers.interfaces.question import IQuestion
607
3691.197.31 by Francis J. Lacoste
All workflow methods now triggers an ISQLObjectModifiedEvent.
608
    >>> def print_event(object, event):
609
    ...     print "Received %s on %s" % (
610
    ...         event.__class__.__name__.split('.')[-1],
611
    ...         object.__class__.__name__.split('.')[-1])
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
612
    >>> questionmessage_event_listener = TestEventListener(
7876.3.6 by Francis J. Lacoste
Used ISQLObject from lazr.lifecycle
613
    ...     IQuestionMessage, IObjectCreatedEvent, print_event)
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
614
    >>> question_event_listener = TestEventListener(
7876.3.6 by Francis J. Lacoste
Used ISQLObject from lazr.lifecycle
615
    ...     IQuestion, IObjectModifiedEvent, print_event)
3691.197.31 by Francis J. Lacoste
All workflow methods now triggers an ISQLObjectModifiedEvent.
616
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
617
Changing the status triggers the event.
3691.197.31 by Francis J. Lacoste
All workflow methods now triggers an ISQLObjectModifiedEvent.
618
3691.197.104 by Francis J. Lacoste
- Protect setStatus by launchpad.Admin.
619
    >>> login(stub.preferredemail.email)
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
620
    >>> msg = question.setStatus(
621
    ...     stub, QuestionStatus.EXPIRED, 'Status change.')
7876.3.6 by Francis J. Lacoste
Used ISQLObject from lazr.lifecycle
622
    Received ObjectCreatedEvent on QuestionMessage
623
    Received ObjectModifiedEvent on Question
3691.197.31 by Francis J. Lacoste
All workflow methods now triggers an ISQLObjectModifiedEvent.
624
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
625
Rejecting the question triggers the events.
3691.197.31 by Francis J. Lacoste
All workflow methods now triggers an ISQLObjectModifiedEvent.
626
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
627
    >>> msg = question.reject(stub, 'Close this question.')
7876.3.6 by Francis J. Lacoste
Used ISQLObject from lazr.lifecycle
628
    Received ObjectCreatedEvent on QuestionMessage
629
    Received ObjectModifiedEvent on Question
3691.197.31 by Francis J. Lacoste
All workflow methods now triggers an ISQLObjectModifiedEvent.
630
631
Even only adding a comment without changing the status will send
632
these events.
633
3691.197.104 by Francis J. Lacoste
- Protect setStatus by launchpad.Admin.
634
    >>> login('test@canonical.com')
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
635
    >>> msg = question.addComment(sample_person, 'A comment')
7876.3.6 by Francis J. Lacoste
Used ISQLObject from lazr.lifecycle
636
    Received ObjectCreatedEvent on QuestionMessage
637
    Received ObjectModifiedEvent on Question
3691.197.31 by Francis J. Lacoste
All workflow methods now triggers an ISQLObjectModifiedEvent.
638
3691.259.2 by Francis J. Lacoste
Convert to Moin-style headers, convert () comments to regular python comments.
639
    # Cleanup
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
640
    >>> questionmessage_event_listener.unregister()
641
    >>> question_event_listener.unregister()
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
642
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
643
644
Reopening the question
645
======================
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
646
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
647
Whenever a question considered answered (in the SOLVED or INVALID state)
3691.398.19 by Francis J. Lacoste
Rename most remaining classes (views, database, authorizations, notifications, scripts.)
648
is reopened, a QuestionReopening is created.
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
649
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
650
    # Register an event listener to notify us whenever a QuestionReopening is
651
    # created.
652
    >>> from lp.answers.interfaces.questionreopening import IQuestionReopening
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
653
    >>> reopening_event_listener = TestEventListener(
7876.3.6 by Francis J. Lacoste
Used ISQLObject from lazr.lifecycle
654
    ...     IQuestionReopening, IObjectCreatedEvent, print_event)
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
655
656
The most common use case is when a user confirms a solution, and then
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
657
comes back to say that it doesn't, in fact, work.
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
658
3691.197.109 by Francis J. Lacoste
- Add launchpad.Owner permission.
659
    >>> login('no-priv@canonical.com')
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
660
    >>> question = ubuntu.newQuestion(**new_question_args)
661
    >>> answer_message = question.giveAnswer(
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
662
    ...     sample_person,
663
    ...     "You need some setup on the Mac side. "
664
    ...     "Follow the instructions at "
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
665
    ...     "https://help.ubuntu.com/community/Installation/OldWorldMacs",
666
    ...     datecreated=now_plus_one_hour)
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
667
    >>> confirm_message = question.confirmAnswer(
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
668
    ...     "I've installed BootX and the installer now boot properly.",
669
    ...     answer=answer_message, datecreated=now_plus_two_hours)
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
670
    >>> reopen_message = question.reopen(
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
671
    ...     "Actually, altough the installer boots properly. I'm not able "
672
    ...     "to pass beyond the partitioning.",
673
    ...     datecreated=now_plus_three_hours)
7876.3.6 by Francis J. Lacoste
Used ISQLObject from lazr.lifecycle
674
    Received ObjectCreatedEvent on QuestionReopening
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
675
676
The reopening record is available through the reopenings attribute.
677
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
678
    >>> reopenings = list(question.reopenings)
679
    >>> len(reopenings)
680
    1
681
    >>> reopening = reopenings[0]
3691.398.18 by Francis J. Lacoste
Rename interfaces.
682
    >>> verifyObject(IQuestionReopening, reopening)
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
683
    True
684
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
685
The reopening contain the date of the reopening, and the person who cause the
686
reopening to happen.
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
687
688
    >>> reopening.datecreated == now_plus_three_hours
689
    True
690
    >>> print reopening.reopener.displayname
691
    No Privileges Person
692
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
693
It also contains the question's prior answerer, the date created, and the
694
prior status of the question.
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
695
696
    >>> print reopening.answerer.displayname
697
    Sample Person
4664.2.4 by Curtis Hovey
Renamed attribute from datesolved to date_solved. Doh. This was
698
    >>> reopening.date_solved == now_plus_two_hours
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
699
    True
700
    >>> print reopening.priorstate.name
701
    SOLVED
702
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
703
A reopening also occurs when the question status is set back to OPEN after
704
having been rejected.
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
705
3691.197.104 by Francis J. Lacoste
- Protect setStatus by launchpad.Admin.
706
    >>> login('test@canonical.com')
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
707
    >>> question = ubuntu.newQuestion(**new_question_args)
708
    >>> reject_message = question.reject(
3881.1.1 by Francis J. Lacoste
Renamed reference to support tracker in doctests.
709
    ...     sample_person, 'This is a frivoulous question.',
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
710
    ...     datecreated=now_plus_one_hour)
3691.197.104 by Francis J. Lacoste
- Protect setStatus by launchpad.Admin.
711
712
    >>> login(stub.preferredemail.email)
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
713
    >>> status_change_message = question.setStatus(
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
714
    ...     stub, QuestionStatus.OPEN,
715
    ...     'Disregard previous rejection. '
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
716
    ...     'Sample Person was having a bad day.',
717
    ...     datecreated=now_plus_two_hours)
7876.3.6 by Francis J. Lacoste
Used ISQLObject from lazr.lifecycle
718
    Received ObjectCreatedEvent on QuestionReopening
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
719
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
720
    >>> reopening = question.reopenings[0]
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
721
    >>> print reopening.reopener.name
722
    stub
723
    >>> reopening.datecreated == now_plus_two_hours
724
    True
725
    >>> print reopening.answerer.displayname
726
    Sample Person
4664.2.4 by Curtis Hovey
Renamed attribute from datesolved to date_solved. Doh. This was
727
    >>> reopening.date_solved == now_plus_one_hour
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
728
    True
729
    >>> print reopening.priorstate.name
730
    INVALID
731
3691.259.2 by Francis J. Lacoste
Convert to Moin-style headers, convert () comments to regular python comments.
732
    # Cleanup
3691.197.34 by Francis J. Lacoste
Create TicketReopening using an event subscriber
733
    >>> reopening_event_listener.unregister()
3691.197.41 by Francis J. Lacoste
Added the possibility to use an IMessage as content in the workflow methods
734
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
735
736
Using an IMessage as an explanation
737
===================================
3691.197.41 by Francis J. Lacoste
Added the possibility to use an IMessage as content in the workflow methods
738
739
In all the workflow methods, it is possible to pass an IMessage instead of
740
a string.
741
12929.9.7 by j.c.sackett
Caught more imports.
742
    >>> from lp.services.messages.interfaces.message import IMessageSet
3691.197.104 by Francis J. Lacoste
- Protect setStatus by launchpad.Admin.
743
    >>> login('test@canonical.com')
3691.197.41 by Francis J. Lacoste
Added the possibility to use an IMessage as content in the workflow methods
744
    >>> messageset = getUtility(IMessageSet)
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
745
    >>> question = ubuntu.newQuestion(**new_question_args)
3691.197.41 by Francis J. Lacoste
Added the possibility to use an IMessage as content in the workflow methods
746
    >>> reject_message = messageset.fromText(
747
    ...     'Reject', 'Because I feel like it.', sample_person)
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
748
    >>> question_message = question.reject(sample_person, reject_message)
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
749
    >>> print question_message.subject
3691.197.41 by Francis J. Lacoste
Added the possibility to use an IMessage as content in the workflow methods
750
    Reject
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
751
    >>> print question_message.text_contents
3691.197.41 by Francis J. Lacoste
Added the possibility to use an IMessage as content in the workflow methods
752
    Because I feel like it.
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
753
    >>> question_message.rfc822msgid == reject_message.rfc822msgid
3691.197.41 by Francis J. Lacoste
Added the possibility to use an IMessage as content in the workflow methods
754
    True
755
7675.518.3 by Barry Warsaw
Reformat workflow.txt to today's doctest standards.
756
The IMessage owner must be the same as the person passed to the workflow
3691.197.41 by Francis J. Lacoste
Added the possibility to use an IMessage as content in the workflow methods
757
method.
758
3691.197.104 by Francis J. Lacoste
- Protect setStatus by launchpad.Admin.
759
    >>> login(stub.preferredemail.email)
3691.398.21 by Francis J. Lacoste
Rename all attributes and variables.
760
    >>> question.setStatus(stub, QuestionStatus.OPEN, reject_message)
3691.197.41 by Francis J. Lacoste
Added the possibility to use an IMessage as content in the workflow methods
761
    Traceback (most recent call last):
762
      ...
13099.1.13 by Curtis Hovey
Updated tests to verify the specific errors.
763
    NotMessageOwnerError...