~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
=============================
People and the answer tracker
=============================

Sometimes you want to find out what questions a person is involved with.


Searching
=========

IQuestionsPerson defines a searchQuestions() method which is used to select
all, or a subset of, the questions in which a person is involved.  This
includes questions which the person created, is assigned to, is subscribed to,
commented on, or answered.  Various subsets can be selected by using the
various search criteria.

    >>> from lp.answers.interfaces.questionsperson import IQuestionsPerson
    >>> from lp.registry.interfaces.person import IPersonSet
    >>> personset = getUtility(IPersonSet)
    >>> foo_bar_raw = personset.getByEmail('foo.bar@canonical.com')
    >>> foo_bar = IQuestionsPerson(foo_bar_raw)


Search text
-----------

The search_text parameter will limit the questions to those matching
the query using the regular full text algorithm.

    >>> for question in foo_bar.searchQuestions(search_text='firefox'):
    ...     print question.title, question.status.title
    Firefox loses focus and gets stuck              Open
    mailto: problem in webpage                      Solved
    Newly installed plug-in doesn't seem to be used Answered


Sorting
-------

When using the search_text criteria, the default is to sort the results by
relevancy.  One can use the sort parameter to change that.  It takes one of
the constant defined in the QuestionSort enumeration.

    >>> from lp.answers.enums import QuestionSort
    >>> for question in foo_bar.searchQuestions(
    ...         search_text='firefox', sort=QuestionSort.OLDEST_FIRST):
    ...     print question.id, question.title, question.status.title
    4 Firefox loses focus and gets stuck              Open
    6 Newly installed plug-in doesn't seem to be used Answered
    9 mailto: problem in webpage                      Solved

When no text search is done, the default sort order is newest first.

    >>> for question in foo_bar.searchQuestions():
    ...     print question.id, question.title, question.status.title
    11 Continue playing after shutdown                      Open
    10 Play DVDs in Totem                                   Answered
     9 mailto: problem in webpage                           Solved
     8 Installation of Java Runtime Environment for Mozilla Answered
     7 Slow system                                          Needs information
     6 Newly installed plug-in doesn't seem to be used      Answered
     4 Firefox loses focus and gets stuck                   Open


Status
------

As shown above, expired and invalid questions are not returned.  The status
parameter can be used to control the list of statuses to select.

    >>> from lp.answers.enums import QuestionStatus
    >>> for question in foo_bar.searchQuestions(
    ...         status=QuestionStatus.INVALID):
    ...     print question.title, question.status.title
    Firefox is slow and consumes too much RAM   Invalid

The status parameter can also take a list of statuses.

    >>> for question in foo_bar.searchQuestions(
    ...         status=(QuestionStatus.SOLVED, QuestionStatus.INVALID)):
    ...     print question.title, question.status.title
    mailto: problem in webpage                  Solved
    Firefox is slow and consumes too much RAM   Invalid


Participation
-------------

By default, any relationship between a person and a question is considered by
searchQuestions.  This can customized through the participation parameter.  It
takes one or a list of constants from the QuestionParticipation enumeration.

To select only questions on which the person commented, the
QuestionParticipation.COMMENTER is used.

    >>> from lp.answers.enums import QuestionParticipation
    >>> for question in foo_bar.searchQuestions(
    ...         participation=QuestionParticipation.COMMENTER, status=None):
    ...     print question.title
    Continue playing after shutdown
    Play DVDs in Totem
    mailto: problem in webpage
    Installation of Java Runtime Environment for Mozilla
    Newly installed plug-in doesn't seem to be used

QuestionParticipation.SUBSCRIBER will only select the questions to which the
person is subscribed.

    >>> for question in foo_bar.searchQuestions(
    ...         participation=QuestionParticipation.SUBSCRIBER, status=None):
    ...     print question.title
    Slow system
    Firefox is slow and consumes too much RAM

QuestionParticipation.OWNER selects the questions that the person created.

    >>> for question in foo_bar.searchQuestions(
    ...         participation=QuestionParticipation.OWNER, status=None):
    ...     print question.title
    Slow system
    Firefox loses focus and gets stuck
    Firefox is slow and consumes too much RAM

QuestionParticipation.ANSWERER selects the questions for which the person gave
an answer.

    >>> for question in foo_bar.searchQuestions(
    ...         participation=QuestionParticipation.ANSWERER, status=None):
    ...     print question.title
    mailto: problem in webpage
    Firefox is slow and consumes too much RAM

QuestionParticipation.ASSIGNEE selects that questions which are assigned to
the person.

    >>> list(foo_bar.searchQuestions(
    ...         participation=QuestionParticipation.ASSIGNEE, status=None))
    []

If a list of these constants is used, all of these participation types
will be selected.

    >>> for question in foo_bar.searchQuestions(
    ...         participation=(QuestionParticipation.OWNER,
    ...                        QuestionParticipation.ANSWERER),
    ...         status=None):
    ...     print question.title
    mailto: problem in webpage
    Slow system
    Firefox loses focus and gets stuck
    Firefox is slow and consumes too much RAM


Language
--------

By default, questions in all languages are included in the results.  It is
possible to filter questions by the language they were written in.  One or a
sequence of ILanguage object can be passed in to specify the language filter.

    >>> from lp.services.worlddata.interfaces.language import ILanguageSet
    >>> spanish = getUtility(ILanguageSet)['es']
    >>> english = getUtility(ILanguageSet)['en']

Foo bar doesn't have any questions written in Spanish.

    >>> list(foo_bar.searchQuestions(language=spanish))
    []

But Carlos has one.

    # Because not everyone uses a real editor <wink>
    >>> from lp.services.encoding import ascii_smash
    >>> carlos_raw = personset.getByName('carlos')
    >>> carlos = IQuestionsPerson(carlos_raw)
    >>> for question in carlos.searchQuestions(
    ...         language=(english, spanish)):
    ...     print ascii_smash(question.title), question.language.code
    Problema al recompilar kernel con soporte smp (doble-nucleo)    es


Questions needing attention
---------------------------

You can select only the questions that needs attention from a person.  This
includes questions owned by the person in the ANSWERED or NEEDSINFO state.  It
also includes questions on which the person requested more information or gave
an answer and are back in the OPEN state.

    >>> for question in foo_bar.searchQuestions(needs_attention=True):
    ...     print question.status.title, question.owner.displayname, (
    ...         question.title)
    Open              Sample Person Continue playing after shutdown
    Needs information Foo Bar       Slow system


Search combinations
-------------------

The results are the intersection of the sets delimited by each criteria.

    >>> for question in foo_bar.searchQuestions(
    ...         search_text='firefox OR Java',
    ...         status=QuestionStatus.ANSWERED,
    ...         participation=QuestionParticipation.COMMENTER):
    ...     print question.title, question.status.title
    Installation of Java Runtime Environment for Mozilla    Answered
    Newly installed plug-in doesn't seem to be used         Answered


Question languages
==================

IQuestionsPerson also defines a getQuestionLanguages() attribute which
contains the set of languages used by all of the questions in which this
person is involved.

    >>> print ', '.join(
    ...     sorted(language.code
    ...            for language in foo_bar.getQuestionLanguages()))
    en

This includes questions which the person owns, and questions that the user is
subscribed to...

    >>> from lp.answers.interfaces.questioncollection import IQuestionSet
    >>> pt_BR_question = getUtility(IQuestionSet).get(13)
    >>> login('foo.bar@canonical.com')
    >>> pt_BR_question.subscribe(foo_bar_raw)
    <QuestionSubscription...>

    >>> print ', '.join(
    ...     sorted(language.code
    ...            for language in foo_bar.getQuestionLanguages()))
    en, pt_BR

...and questions for which he's the answerer...

    >>> es_question = getUtility(IQuestionSet).get(12)
    >>> es_question.reject(foo_bar_raw, 'Reject question.')
    <QuestionMessage...>

    >>> print ', '.join(
    ...     sorted(language.code
    ...            for language in foo_bar.getQuestionLanguages()))
    en, es, pt_BR

...as well as questions which are assigned to the user...

    >>> pt_BR_question.assignee = carlos_raw
    >>> print ', '.join(
    ...     sorted(language.code
    ...            for language in carlos.getQuestionLanguages()))
    es, pt_BR

...and questions on which the user commented.

    >>> en_question = getUtility(IQuestionSet).get(1)
    >>> login('carlos@canonical.com')
    >>> en_question.addComment(carlos_raw, 'A simple comment.')
    <QuestionMessage...>

    >>> print ', '.join(
    ...     sorted(language.code
    ...            for language in carlos.getQuestionLanguages()))
    en, es, pt_BR


Direct subscriptions
====================

IQuestionsPerson defines getDirectAnswerQuestionTargets that can be used to
retrieve a list of IQuestionTargets that a person subscribed himself to as an
answer contact.

    >>> no_priv_raw = personset.getByName('no-priv')
    >>> no_priv = IQuestionsPerson(no_priv_raw)
    >>> no_priv.getDirectAnswerQuestionTargets()
    []

    >>> from lp.registry.interfaces.product import IProductSet
    >>> firefox = getUtility(IProductSet).getByName("firefox")

    # Answer contacts must speak a language
    >>> no_priv_raw.addLanguage(english)
    >>> firefox.addAnswerContact(no_priv_raw, no_priv_raw)
    True

    >>> for target in no_priv.getDirectAnswerQuestionTargets():
    ...    print target.name
    firefox


Indirect subscriptions
======================

IQuestionsPerson defines getTeamAnswerQuestionTargets that retrieves a list of
IQuestionTargets that the person is subscribed to indirectly as an answer
contact through his team membership.

    >>> landscape_team = personset.getByName("landscape-developers")
    >>> ignored = landscape_team.addMember(no_priv_raw, foo_bar_raw)
    >>> no_priv_raw.inTeam(landscape_team)
    True

    >>> from lp.registry.interfaces.distribution import IDistributionSet
    >>> ubuntu = getUtility(IDistributionSet).getByName("ubuntu")
    >>> landscape_team.addLanguage(english)
    >>> ubuntu.addAnswerContact(landscape_team, landscape_team.teamowner)
    True

    >>> print ', '.join(
    ...     sorted(target.name
    ...            for target in no_priv.getTeamAnswerQuestionTargets()))
    ubuntu

Indirect team membership is also taken in consideration.  For example, when
the Landscape Team joins the Translator Team, targets for which the Translator
team is an answer contact will be included in No Privileges Person's supported
IQuestionTargets.

    >>> translator_team = personset.getByName('ubuntu-translators')
    >>> no_priv_raw.inTeam(translator_team)
    False
    >>> ignored = translator_team.addMember(landscape_team, carlos_raw)

    # We need to accept the invitation sent by the addMember() call in
    # order to make landscape_team an actual member of translator_team.
    >>> login(landscape_team.teamowner.preferredemail.email)
    >>> landscape_team.acceptInvitationToBeMemberOf(
    ...     translator_team, comment='something')

    >>> no_priv_raw.hasParticipationEntryFor(translator_team)
    True
    >>> evolution_package = ubuntu.getSourcePackage('evolution')
    >>> translator_team.addLanguage(english)
    >>> evolution_package.addAnswerContact(
    ...     translator_team, translator_team.teamowner)
    True
    >>> print ', '.join(
    ...     sorted(target.name
    ...            for target in no_priv.getTeamAnswerQuestionTargets()))
    evolution, ubuntu


Deactivated pillars
===================

Only valid IQuestionTargets are returned, ensuring that no deactivated pillars
are in the results.

If the Firefox project is deactivated, it is removed from the list of
supported projects.

    >>> login('foo.bar@canonical.com')

    # Unlink the source packages so the project can be deactivated.
    >>> from lp.testing import unlink_source_packages
    >>> unlink_source_packages(firefox)
    >>> firefox.active = False
    >>> sorted(target.name
    ...        for target in no_priv.getDirectAnswerQuestionTargets())
    []

When the Firefox project is reactivated, the answer contact relationship is
visible.  These relationships are persistent for cases where we only want is
deactivated for a short period.

    >>> firefox.active = True
    >>> print ', '.join(
    ...     sorted(target.name
    ...            for target in no_priv.getDirectAnswerQuestionTargets()))
    firefox