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
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
|
= Searching BugTasks =
BugTasks are usually searched through an IBugTarget's searchTasks()
method, but they all delegate the search to IBugTaskSet.search(). That
method accepts a single parameter; an BugTaskSearchParams instance.
>>> from lp.bugs.interfaces.bugtask import (
... BugTaskSearchParams,
... IBugTaskSet,
... )
>>> bugtask_set = getUtility(IBugTaskSet)
>>> all_public = BugTaskSearchParams(user=None)
>>> found_bugtasks = bugtask_set.search(all_public)
>>> from lp.bugs.model.bugtask import BugTask
>>> all_public_bugtasks = BugTask.select(
... "BugTask.bug = Bug.id AND Bug.private = false",
... clauseTables=['Bug'])
>>> found_bugtasks.count() == all_public_bugtasks.count()
True
== Searching by bug supervisor ==
The 'bug_supervisor' parameter allows you to search bugtasks that a certain
person is responsible for. A person can be a bug supervisor for a product,
a distribution, or a distribution source package. No Privileges Person
isn't a bug supervisor, so no bugs are found for him:
>>> from lp.registry.interfaces.person import IPersonSet
>>> no_priv = getUtility(IPersonSet).getByName('no-priv')
>>> no_priv_bug_supervisor = BugTaskSearchParams(
... user=None, bug_supervisor=no_priv)
>>> found_bugtasks = bugtask_set.search(no_priv_bug_supervisor)
>>> found_bugtasks.count()
0
== Product bugs ==
Firefox has a few bugs:
>>> from lp.registry.interfaces.product import IProductSet
>>> firefox = getUtility(IProductSet).getByName('firefox')
>>> firefox_bugs = firefox.searchTasks(all_public)
>>> firefox_public_bugs = firefox_bugs.count()
>>> firefox_public_bugs > 0
True
== Distribution and package bugs ==
Ubuntu does too:
>>> from lp.registry.interfaces.distribution import IDistributionSet
>>> ubuntu = getUtility(IDistributionSet).getByName("ubuntu")
>>> all_public = BugTaskSearchParams(user=None)
>>> ubuntu_bugs = ubuntu.searchTasks(all_public)
>>> ubuntu_bugs.count() > 0
True
and in particular, mozilla-firefox in Ubuntu has 'em:
>>> ubuntu_firefox = ubuntu.getSourcePackage("mozilla-firefox")
>>> ubuntu_firefox_bugs = ubuntu_firefox.searchTasks(all_public)
>>> ubuntu_firefox_bugs.count() > 0
True
== Person bugs ==
To get all related tasks to a person call searchTasks() on the person
object:
>>> from lp.registry.interfaces.person import IPersonSet
>>> user = getUtility(IPersonSet).getByName('name16')
>>> user_bugs = user.searchTasks(None, user=None)
>>> user_bugs.count() > 0
True
== Dupes and Conjoined tasks ==
You can set flags to omit duplicates:
>>> no_dupes = BugTaskSearchParams(user=None, omit_dupes=True)
>>> firefox = getUtility(IProductSet).getByName('firefox')
>>> sans_dupes = firefox.searchTasks(no_dupes)
>>> sans_dupes.count() < firefox_public_bugs
True
and also series-targeted bugs:
>>> no_targeted = BugTaskSearchParams(user=None, omit_targeted=True)
>>> sans_targeted = ubuntu.searchTasks(no_targeted)
>>> sans_targeted.count() < ubuntu_bugs
True
=== Product bug supervisor ===
If No Privileges is specified as Firefox's bug supervisor, searching for his
bugs return all of Firefox's bugs.
>>> login('foo.bar@canonical.com')
>>> firefox.setBugSupervisor(no_priv, no_priv)
>>> found_bugtasks = bugtask_set.search(no_priv_bug_supervisor)
>>> found_bugtasks.count() == firefox_bugs.count()
True
>>> found_targets = set(
... bugtask.target.bugtargetdisplayname for bugtask in found_bugtasks)
>>> for target_name in sorted(found_targets):
... print target_name
Mozilla Firefox
=== Distribution bug supervisor ===
If someone is bug supervisor for Firefox, Firefox in Ubuntu, and Ubuntu,
all bugs in Firefox and Ubuntu are returned. Bugs in the Ubuntu Firefox
package are included in the Ubuntu bugs, so they won't be returned
twice.
>>> all_public = BugTaskSearchParams(user=None)
>>> ubuntu_bugs = ubuntu.searchTasks(all_public)
>>> ubuntu_bugs.count() > 0
True
>>> ubuntu.setBugSupervisor(no_priv, no_priv)
>>> found_bugtasks = bugtask_set.search(no_priv_bug_supervisor)
>>> found_bugtasks.count() == firefox_bugs.count() + ubuntu_bugs.count()
True
== Searching in comments ==
Comments can be searched when specifying a search text in the bugtask
search. Whether comments are searched is controlled by the
search_comments config option.
For example, no Firefox bugs are found when searching for
'wordincomment'.
>>> comment_search = BugTaskSearchParams(
... user=None, searchtext='wordincomment')
>>> found_bugtasks = firefox.searchTasks(comment_search)
>>> found_bugtasks.count()
0
If we add a comment containing the search string to bug one, that bug
will be returned by the search only if search_comments is True.
>>> from lp.bugs.interfaces.bug import IBugSet
>>> bug_one = getUtility(IBugSet).get(1)
>>> bug_one.newMessage(no_priv, 'No subject', 'some wordincomment')
<Message at ...>
# Add another comment to make sure that it won't cause the same
# bugtask to be returned twice.
>>> bug_one.newMessage(no_priv, 'No subject', 'another wordincomment')
<Message at ...>
When the search_comments config option is False, the bug is not found.
>>> from lp.services.config import config
>>> search_comments = """
... [malone]
... search_comments: %s
... """
>>> config.push('search_comments', search_comments % False)
>>> found_bugtasks = firefox.searchTasks(comment_search)
>>> [bugtask.bug.id for bugtask in found_bugtasks]
[]
>>> config_data = config.pop('search_comments')
If the search_comments config option is True, the bug is found.
>>> config.push('search_comments', search_comments % True)
>>> found_bugtasks = firefox.searchTasks(comment_search)
>>> for bugtask in found_bugtasks:
... print "#%s" % bugtask.bug.id
#1
# Of course, searching in a project not containing bug #1 doesn't
# return any matches.
>>> evolution = getUtility(IProductSet).getByName('evolution')
>>> found_bugtasks = evolution.searchTasks(comment_search)
>>> found_bugtasks.count()
0
>>> config_data = config.pop('search_comments')
== Searching using bug full-text index ==
The searchtext parameter does an extensive and expensive search (it
looks through the bug's full text index, bug comments, bugtask
target name, etc.) For some use cases, it is often easier and cheaper
to simply search on the bug's full text index and omit the more
expensive search on other related information.
For example, there are no bugs with the word 'Fnord' in Firefox.
>>> text_search = BugTaskSearchParams(user=None, fast_searchtext='Fnord')
>>> found_bugtasks = firefox.searchTasks(text_search)
>>> found_bugtasks.count()
0
But if we put that word in the bug #4 description, it will be found.
>>> bug_four = getUtility(IBugSet).get(4)
>>> bug_four.description += (
... '\nThat happens pretty often with the Fnord Highlighter '
... 'extension installed.')
>>> found_bugtasks = firefox.searchTasks(text_search)
>>> for bugtask in found_bugtasks:
... print "#%s" % bugtask.bug.id
#4
== Searching by bug reporter ==
The 'bug_reporter' parameter allows you to search for bugs reported by a
certain person.
>>> foo_bar = getUtility(IPersonSet).getByEmail('foo.bar@canonical.com')
>>> reported_by_foo_bar = BugTaskSearchParams(
... user=None, bug_reporter=foo_bar)
>>> reported_by_foo_bar.setDistribution(ubuntu)
>>> found_bugtasks = bugtask_set.search(reported_by_foo_bar)
>>> for bugtask in found_bugtasks:
... print "#%s in %s reported by %s" % (
... bugtask.bug.id, bugtask.bugtargetname,
... bugtask.bug.owner.displayname)
#9 in thunderbird (Ubuntu) reported by Foo Bar
#10 in linux-source-2.6.15 (Ubuntu) reported by Foo Bar
== Searching for nominated bugs ==
We can search for bugs nominated to a distribution series by using the
nominated_for parameter.
>>> ubuntu = getUtility(IDistributionSet).getByName('ubuntu')
>>> warty = ubuntu.getSeries('warty')
>>> from lp.bugs.model.bugnomination import BugNomination
>>> print list(BugNomination.selectBy(distroseries=warty))
[]
>>> from lp.bugs.interfaces.bug import CreateBugParams
>>> nominated_for_warty = BugTaskSearchParams(
... user=None, nominated_for=warty)
>>> list(ubuntu.searchTasks(nominated_for_warty))
[]
>>> nominated_bug = ubuntu.createBug(
... CreateBugParams(owner=no_priv, title='Test nominated bug',
... comment='Something'))
>>> BugNomination(
... owner=no_priv, distroseries=warty, bug=nominated_bug)
<BugNomination at ...>
>>> for bugtask in ubuntu.searchTasks(nominated_for_warty):
... print bugtask.bug.title
Test nominated bug
The same parameter is used to search for bugs nominated to a product
series.
>>> firefox = getUtility(IProductSet).getByName('firefox')
>>> firefox_trunk = firefox.getSeries('trunk')
>>> print list(BugNomination.selectBy(productseries=firefox_trunk))
[]
>>> nominated_for_trunk = BugTaskSearchParams(
... user=None, nominated_for=firefox_trunk)
>>> list(firefox.searchTasks(nominated_for_trunk))
[]
>>> nominated_bug = firefox.createBug(
... CreateBugParams(owner=no_priv, title='Bug to be fixed in trunk',
... comment='Something'))
>>> BugNomination(
... owner=no_priv, productseries=firefox_trunk, bug=nominated_bug)
<BugNomination at ...>
>>> for bugtask in firefox.searchTasks(nominated_for_trunk):
... print bugtask.bug.title
Bug to be fixed in trunk
== Filter by Upstream Status ==
Add an Ubuntu bugtask for a bug that is confirmed upstream.
>>> from lp.bugs.interfaces.bugtask import (
... BugTaskImportance,
... BugTaskStatus,
... )
>>> from lp.bugs.model.tests.test_bugtask import (
... BugTaskSearchBugsElsewhereTest)
>>> def bugTaskInfo(bugtask):
... return '%i %i %s %s' % (
... bugtask.id, bugtask.bug.id, bugtask.bugtargetdisplayname,
... bugtask.bug.title)
>>> test_helper = BugTaskSearchBugsElsewhereTest(helper_only=True)
>>> bug_twelve = getUtility(IBugSet).get(12)
>>> task_open_upstream = bugtask_set.createTask(
... bug_twelve, foo_bar, ubuntu,
... status=BugTaskStatus.NEW, importance=BugTaskImportance.MEDIUM)
>>> test_helper.assertBugTaskIsOpenUpstream(task_open_upstream)
Pass the resolved_upstream flag to include only bugtasks linked to
watches that are rejected, fixed committed or fix released, or bugtasks
related to upstream bugtasks (i.e. filed on the same bug) that are fix
committed or fix released.
>>> test_helper.setUpBugsResolvedUpstreamTests()
>>> params = BugTaskSearchParams(
... resolved_upstream=True, orderby='id', user=None)
>>> closed_elsewhere_tasks = ubuntu.searchTasks(params)
>>> for bugtask in closed_elsewhere_tasks:
... test_helper.assertBugTaskIsResolvedUpstream(bugtask)
... print bugTaskInfo(bugtask)
17 1 mozilla-firefox (Ubuntu) Firefox does not support SVG
26 2 Ubuntu Blackhole Trash folder
23 9 thunderbird (Ubuntu) Thunderbird crashes
Pass the open_upstream flag to include only bugtasks linked to those
watches or those upstream bugtasks that have the status "unconfirmed",
"needs info", "confirmed", "in progress" or "unknown". Note that a bug
may be associated with three or more bugtasks. If one upstream task
has a state associated with "open upstream", and another upstream task
has a state associated with "resolved upstream", the bug is included
in the results of the "open upstream" filter as well as the "resolved
upstream" filter.
(In the examples below, the last bugtask is ellipsized because its ID
is generated here and therefore sampledata-dependent.)
>>> params = BugTaskSearchParams(
... open_upstream=True, orderby='id', user=None)
>>> open_elsewhere_tasks = ubuntu.searchTasks(params)
>>> for bugtask in open_elsewhere_tasks:
... test_helper.assertBugTaskIsOpenUpstream(bugtask)
... print bugTaskInfo(bugtask)
17 1 mozilla-firefox (Ubuntu) Firefox does not support SVG
26 2 Ubuntu Blackhole Trash folder
... ... Ubuntu Copy, Cut and Delete operations should work on selections
We can also filter our search to include only bugs that are not known to
affect upstream, i.e., bugs that don't have an IUpstreamBugTask.
>>> params = BugTaskSearchParams(
... has_no_upstream_bugtask=True, orderby='id', user=None)
>>> tasks_with_no_upstreams = ubuntu.searchTasks(params)
>>> for bugtask in tasks_with_no_upstreams:
... test_helper.assertShouldBeShownOnNoUpstreamTaskSearch(bugtask)
... print bugTaskInfo(bugtask)
25 10 linux-source-2.6.15 (Ubuntu) another test bug
... ... Ubuntu Test nominated bug
If we combine upstream-related filters, we get the union of the results
of the single filters.
>>> params = BugTaskSearchParams(
... has_no_upstream_bugtask=True, resolved_upstream=True,
... orderby='id', user=None)
>>> tasks_with_no_upstreams = ubuntu.searchTasks(params)
>>> for bugtask in tasks_with_no_upstreams:
... print bugTaskInfo(bugtask)
17 1 mozilla-firefox (Ubuntu) Firefox does not support SVG
26 2 Ubuntu Blackhole Trash folder
23 9 thunderbird (Ubuntu) Thunderbird crashes
25 10 linux-source-2.6.15 (Ubuntu) another test bug
... ... Ubuntu Test nominated bug
>>> test_helper.tearDownBugsElsewhereTests()
The search filter can also return bugs that are related to CVE reports:
>>> from lp.bugs.interfaces.cve import ICveSet
>>> def getCves(bugtask):
... bugcve = getUtility(ICveSet).getBugCvesForBugTasks([bugtask])[0]
... return bugcve.cve.sequence
>>> params = BugTaskSearchParams(
... has_cve=True, orderby='id', user=None)
>>> tasks_with_cves = ubuntu.searchTasks(params)
>>> for bugtask in tasks_with_cves:
... print bugTaskInfo(bugtask), getCves(bugtask)
17 1 mozilla-firefox (Ubuntu) Firefox does not support SVG 1999-8979
26 2 Ubuntu Blackhole Trash folder 1999-2345
== Searching by bug commenter ==
The 'bug_commenter' parameter allows you to search bugtasks on which a certain
person has commented. No Privileges Person hasn't commented on any bugs, so no
bugs are found for him:
>>> from lp.registry.interfaces.person import IPersonSet
>>> import transaction
>>> transaction.abort()
>>> no_priv = getUtility(IPersonSet).getByName('no-priv')
>>> no_priv_bug_commenter = BugTaskSearchParams(
... user=None, bug_commenter=no_priv)
>>> found_bugtasks = bugtask_set.search(no_priv_bug_commenter)
>>> found_bugtasks.count()
0
If No Privileges Person comments on some bugs, those bugs can then be found by
a bug commenter search. There will be one bug task instance returned for each
bug target that the task is registered against, so in the test below three
comments will produce eight found bug tasks (three for bug 1, five for bug 2).
>>> bug_one = getUtility(IBugSet).get(1)
>>> bug_one.newMessage(no_priv, 'No subject', 'some comment')
<Message at ...>
>>> bug_two = getUtility(IBugSet).get(2)
>>> bug_two.newMessage(no_priv, 'No subject', 'another comment')
<Message at ...>
>>> bug_two.newMessage(no_priv, 'No subject', 'yet another comment')
<Message at ...>
>>> for (bug_id, target) in sorted((bugtask.bug.id, bugtask.bugtargetname)
... for bugtask in found_bugtasks):
... print bug_id, target
1 firefox
1 mozilla-firefox (Debian)
1 mozilla-firefox (Ubuntu)
2 Ubuntu Hoary
2 mozilla-firefox (Debian Woody)
2 mozilla-firefox (Debian)
2 tomcat
2 ubuntu
If No Privileges Person reports a bug and does not comment on it, that bug
will not be included in the results returned by the bug commenter search.
>>> from lp.bugs.interfaces.bug import CreateBugParams
>>> from lp.registry.interfaces.product import IProductSet
>>> firefox = getUtility(IProductSet).getByName('firefox')
>>> firefox.createBug(
... CreateBugParams(no_priv, "Some bug", "Some comment"))
<Bug at ...>
>>> for (bug_id, target) in sorted((bugtask.bug.id, bugtask.bugtargetname)
... for bugtask in found_bugtasks):
... print bug_id, target
1 firefox
1 mozilla-firefox (Debian)
1 mozilla-firefox (Ubuntu)
2 Ubuntu Hoary
2 mozilla-firefox (Debian Woody)
2 mozilla-firefox (Debian)
2 tomcat
2 ubuntu
== Search for BugTasks assigned to milestones ==
BugTaskSet.search() can return bugtasks associated with milestones.
No BugTask is associated yet with firefox milestone 1.0.
>>> product_milestone = firefox.getMilestone('1.0')
>>> params = BugTaskSearchParams(milestone=product_milestone, user=None)
>>> milestone_tasks = bugtask_set.search(params)
>>> print milestone_tasks.count()
0
Similary, no BugTasks are associated with the project firexfox belongs to.
>>> mozilla = firefox.project
>>> project_milestone = mozilla.getMilestone('1.0')
>>> params = BugTaskSearchParams(milestone=project_milestone, user=None)
>>> milestone_tasks = bugtask_set.search(params)
>>> print milestone_tasks.count()
0
When a BugTask is associated with a milestone, it is returned in a search
for bugs of this milestone.
>>> bugtask = firefox.searchTasks(BugTaskSearchParams(user=None))[0]
>>> print bugTaskInfo(bugtask)
2 1 Mozilla Firefox Firefox does not support SVG
>>> bugtask.milestone = product_milestone
>>> params = BugTaskSearchParams(milestone=product_milestone, user=None)
>>> milestone_tasks = bugtask_set.search(params)
>>> for bugtask in milestone_tasks:
... print bugTaskInfo(bugtask)
2 1 Mozilla Firefox Firefox does not support SVG
This BugTask is also a BugTask of the milestone of the mozilla project.
>>> params = BugTaskSearchParams(milestone=project_milestone, user=None)
>>> milestone_tasks = bugtask_set.search(params)
>>> for bugtask in milestone_tasks:
... print bugTaskInfo(bugtask)
2 1 Mozilla Firefox Firefox does not support SVG
If a bug has one bugtask associated with a product and another bugtask
associated with a product series, and if both tasks are assigned to the
same milestone...
>>> firefox_1_0 = firefox.getSeries("1.0")
>>> productseries_task = bugtask_set.createTask(
... bug_one, no_priv, firefox_1_0)
>>> productseries_task.milestone = product_milestone
>>> print bugTaskInfo(productseries_task)
40 1 Mozilla Firefox 1.0 Firefox does not support SVG
...both of them are returned, by a search for bugs associated with the
product milestone...
>>> params = BugTaskSearchParams(milestone=product_milestone, user=None)
>>> milestone_tasks = bugtask_set.search(params)
>>> for bugtask in milestone_tasks:
... print bugTaskInfo(bugtask)
2 1 Mozilla Firefox Firefox does not support SVG
40 1 Mozilla Firefox 1.0 Firefox does not support SVG
...as well as by a search for bugs associated with the project milestone.
>>> params = BugTaskSearchParams(milestone=project_milestone, user=None)
>>> milestone_tasks = bugtask_set.search(params)
>>> for bugtask in milestone_tasks:
... print bugTaskInfo(bugtask)
2 1 Mozilla Firefox Firefox does not support SVG
40 1 Mozilla Firefox 1.0 Firefox does not support SVG
== Bugs with partner packages ==
Bugs may also be targeted to partner packages. First turn "cdrkit" into
a partner package:
>>> from zope.security.proxy import removeSecurityProxy
>>> from lp.soyuz.interfaces.component import IComponentSet
>>> proxied_cdrkit = ubuntu.getSourcePackage("cdrkit")
>>> cdrkit = removeSecurityProxy(proxied_cdrkit)
>>> cdrkit.component = getUtility(IComponentSet)['partner']
>>> cdrkit.archive = ubuntu.getArchiveByComponent('partner')
>>> transaction.commit()
It starts off with no bugs:
>>> cdrkit_bugs = cdrkit.searchTasks(all_public)
>>> cdrkit_bugs.count()
0
We can file a bug against it and see that show up in a search:
>>> from lp.bugs.interfaces.bug import CreateBugParams
>>> bug = cdrkit.createBug(
... CreateBugParams(owner=no_priv, title='Bug to be fixed in trunk',
... comment='Something'))
>>> cdrkit_bugs = cdrkit.searchTasks(all_public)
>>> cdrkit_bugs.count()
1
== Searching by tags ==
It is possible to search for bugs by their tags. Tags in the search
parameters can be combined using either ''any'' or ''all''.
First, we create some test bugs.
>>> firefox = getUtility(IProductSet).get(4)
>>> foobar = getUtility(IPersonSet).get(16)
The first bug is tagged with both 'test-tag-1' and 'test-tag-2'.
>>> params = CreateBugParams(
... title="test bug a", comment="test bug a", owner=foobar)
>>> test_bug_a = firefox.createBug(params)
>>> test_bug_a.tags = ['test-tag-1', 'test-tag-2']
The second bug is tagged with only 'test-tag-1'.
>>> params = CreateBugParams(
... title="test bug b", comment="test bug b", owner=foobar)
>>> test_bug_b = firefox.createBug(params)
>>> test_bug_b.tags = ['test-tag-1']
Searching for bugs with any of the tags returns both of them.
>>> from operator import attrgetter
>>> from lp.services.searchbuilder import all, any
>>> def search_tasks_and_print_bugs(user=None, **args):
... params = BugTaskSearchParams(user=user, **args)
... tasks = firefox.searchTasks(params)
... bugs = (task.bug for task in tasks)
... bugs = sorted(bugs, key=attrgetter('id'))
... for bug in bugs:
... print "%s [%s]" % (bug.title, ", ".join(bug.tags))
>>> search_tasks_and_print_bugs(
... tag=any('test-tag-1', 'test-tag-2'))
test bug a [test-tag-1, test-tag-2]
test bug b [test-tag-1]
Searching for bugs with all of the tags returns only test bug a.
>>> search_tasks_and_print_bugs(
... tag=all('test-tag-1', 'test-tag-2'))
test bug a [test-tag-1, test-tag-2]
Search for the absence of a tag is possible by prefixing the tag name
with a minus.
>>> search_tasks_and_print_bugs(tag=any('-test-tag-2'))
Firefox does not support SVG []
Reflow problems with complex page layouts [layout-test]
Firefox install instructions should be complete [doc]
Firefox crashes when Save As dialog for a nonexistent window is closed []
Some bug []
test bug b [test-tag-1]
The any() and all() search combinators are taken into consideration
when searching for the absence of tags too. The following search says
"give me bugs that don't have the test-tag-2 tag set *OR* that don't
have the layout-test tag set". Only test-bug-1 is elimininated because
it has no tags other than those requested in the search.
>>> search_tasks_and_print_bugs(
... tag=any('-test-tag-1', '-test-tag-2'))
Firefox does not support SVG []
Reflow problems with complex page layouts [layout-test]
Firefox install instructions should be complete [doc]
Firefox crashes when Save As dialog for a nonexistent window is closed []
Some bug []
test bug b [test-tag-1]
Whereas the following search says "give me bugs that don't have the
test-tag-2 tag set *AND* that don't have the layout-test tag set".
>>> search_tasks_and_print_bugs(
... tag=all('-test-tag-2', '-layout-test'))
Firefox does not support SVG []
Firefox install instructions should be complete [doc]
Firefox crashes when Save As dialog for a nonexistent window is closed []
Some bug []
test bug b [test-tag-1]
Searching for the presence of any tags at all is also possible using a
wildcard. If prefixed with a minus it searches for the absence of
tags.
>>> search_tasks_and_print_bugs(tag=all('*'))
Reflow problems with complex page layouts [layout-test]
Firefox install instructions should be complete [doc]
test bug a [test-tag-1, test-tag-2]
test bug b [test-tag-1]
>>> search_tasks_and_print_bugs(tag=all('-*'))
Firefox does not support SVG []
Firefox crashes when Save As dialog for a nonexistent window is closed []
Some bug []
Searching for the presence and absence of tags finds no
matches. Unsurprisingly.
>>> search_tasks_and_print_bugs(tag=all('*', '-*'))
Wildcards can be combined with non-wildcard tags. The following finds
all bugs with tags, but without test-tag-1:
>>> search_tasks_and_print_bugs(tag=all('*', '-test-tag-1'))
Reflow problems with complex page layouts [layout-test]
Firefox install instructions should be complete [doc]
The following is very similar; it finds all bugs with tags, *or*
without test-tag-1:
>>> search_tasks_and_print_bugs(tag=any('*', '-test-tag-1'))
Firefox does not support SVG []
Reflow problems with complex page layouts [layout-test]
Firefox install instructions should be complete [doc]
Firefox crashes when Save As dialog for a nonexistent window is closed []
Some bug []
test bug a [test-tag-1, test-tag-2]
test bug b [test-tag-1]
The following finds all untagged bugs and bugs with the doc tag.
>>> search_tasks_and_print_bugs(tag=any('-*', 'doc'))
Firefox does not support SVG []
Firefox install instructions should be complete [doc]
Firefox crashes when Save As dialog for a nonexistent window is closed []
Some bug []
== Searching by date_closed ==
It's possible to limit the search by date_closed, to get only bugs
closed after a certain date. greater_than is used to search for bugs
closed after a certain date.
>>> import pytz
>>> from datetime import datetime, timedelta
>>> from lp.services.searchbuilder import greater_than
>>> product = factory.makeProduct()
>>> utc_now = datetime(2008, 9, 4, 12, 0, 0, tzinfo=pytz.timezone('UTC'))
>>> not_closed_bug = factory.makeBug(product=product, title="Not closed")
>>> bug_closed_a_day_ago = factory.makeBug(
... product=product, date_closed=utc_now-timedelta(days=1),
... title="Closed a day ago")
>>> bug_closed_a_week_ago = factory.makeBug(
... product=product, date_closed=utc_now-timedelta(days=7),
... title="Closed a week ago")
>>> search_params = BugTaskSearchParams(
... user=None, orderby="-date_closed",
... date_closed=greater_than(utc_now))
>>> list(product.searchTasks(search_params))
[]
>>> search_params.date_closed = greater_than(utc_now - timedelta(days=2))
>>> for bug_task in product.searchTasks(search_params):
... print bug_task.bug.title
Closed a day ago
== Searching for bug with attachments ==
It's possible to search for bugs with an attachment of a certain type.
>>> from StringIO import StringIO
>>> from lp.services.librarian.interfaces import ILibraryFileAliasSet
>>> from lp.services.messages.interfaces.message import IMessageSet
>>> from lp.bugs.interfaces.bugattachment import (
... BugAttachmentType,
... IBugAttachmentSet,
... )
>>> product = factory.makeProduct()
>>> patch_bug = factory.makeBug(
... product=product)
>>> filecontent = 'Some diff data'
>>> filealias = getUtility(ILibraryFileAliasSet).create(
... name='patch.diff', size=len(filecontent),
... file=StringIO(filecontent), contentType='text/plain')
>>> message = getUtility(IMessageSet).fromText(
... subject="title", content="added a patch.")
>>> attachmentset = getUtility(IBugAttachmentSet)
>>> attachment = attachmentset.create(
... bug=patch_bug, filealias=filealias, title='Patch',
... message=message, attach_type=BugAttachmentType.PATCH)
>>> patch_bug.attachments.count()
1
We've added an attachment to our new bug with an attachment type
PATCH. Searching for bugs with that attachment type we get one result.
>>> search_params = BugTaskSearchParams(
... user=None, attachmenttype=BugAttachmentType.PATCH)
>>> product.searchTasks(search_params).count()
1
>>> filecontent = 'Some more diff data'
>>> filealias = getUtility(ILibraryFileAliasSet).create(
... name='patch.diff', size=len(filecontent),
... file=StringIO(filecontent), contentType='text/plain')
>>> message = getUtility(IMessageSet).fromText(
... subject="title", content="added another patch.")
>>> attachmentset = getUtility(IBugAttachmentSet)
>>> attachment = attachmentset.create(
... bug=patch_bug, filealias=filealias, title='Patch 2',
... message=message, attach_type=BugAttachmentType.PATCH)
>>> patch_bug.attachments.count()
2
We've added another patch to the bug. Searching for bugs with patch
attachments still returns a single result, since even though a new
attachment was added, there is still only one bug with attachments.
>>> product.searchTasks(search_params).count()
1
== Searching for bugs affecting a user ==
We can search for bugs which a user marked as affecting them.
>>> affecting_bug = factory.makeBug(title='A bug affecting a user')
>>> affected_user = factory.makePerson(name='affected-user')
>>> affecting_bug.markUserAffected(affected_user)
>>> target = affecting_bug.bugtasks[0].target
>>> affecting_tasks = target.searchTasks(
... None, user=None, affected_user=affected_user)
>>> for task in affecting_tasks:
... print task.bug.title
A bug affecting a user
== Searching for bugs related to hardware ==
We can search for bugs which are related to a given hardware device or
a given driver.
We can search for bugs whose reporters own a given device. A device
must be specified by a bus as enumerated by HWBus, a vendor ID and a
product ID. If we search for bugs related to the PCI device (0x10de,
0x0455), which appears in a HWDB submission from Sample Person, bugs
reported by him will be returned.
>>> from lp.hardwaredb.interfaces.hwdb import HWBus
>>> search_params = BugTaskSearchParams(
... user=None, hardware_bus=HWBus.PCI, hardware_vendor_id='0x10de',
... hardware_product_id='0x0455', hardware_owner_is_bug_reporter=True)
>>> for bugtask in ubuntu.searchTasks(search_params):
... print bugtask.bug.id, bugtask.bug.owner.displayname
1 Sample Person
2 Sample Person
If one of the parameters bus, vendor ID or prodct ID is missing,
the query is not limited to any devices. In other words, we get
the same result as if we would not have specified any hardware
related parameters.
>>> search_params = BugTaskSearchParams(
... user=None, hardware_bus=HWBus.PCI, hardware_vendor_id='0x10de',
... hardware_owner_is_bug_reporter=True)
>>> for bugtask in ubuntu.searchTasks(search_params):
... print bugtask.bug.id, bugtask.bug.owner.displayname
1 Sample Person
9 Foo Bar
10 Foo Bar
2 Sample Person
19 No Privileges Person
Similary, we can search for device drivers appearing in HWDB submissions
of a bug reporter.
>>> search_params = BugTaskSearchParams(
... user=None, hardware_driver_name='ehci_hcd',
... hardware_owner_is_bug_reporter=True)
>>> for bugtask in ubuntu.searchTasks(search_params):
... print bugtask.bug.id, bugtask.bug.owner.displayname
1 Sample Person
2 Sample Person
We can additionally specify a packge name.
>>> search_params = BugTaskSearchParams(
... user=None, hardware_driver_name='ehci_hcd',
... hardware_driver_package_name='linux-image-2.6.24-19-generic',
... hardware_owner_is_bug_reporter=True)
>>> for bugtask in ubuntu.searchTasks(search_params):
... print bugtask.bug.id, bugtask.bug.owner.displayname
1 Sample Person
2 Sample Person
>>> search_params = BugTaskSearchParams(
... user=None, hardware_driver_name='ehci_hcd',
... hardware_driver_package_name='linux-image',
... hardware_owner_is_bug_reporter=True)
>>> ubuntu.searchTasks(search_params).count()
0
If we specify a driver and a device, we'll get those bugs whose owners
use the given device together with the given driver.
>>> search_params = BugTaskSearchParams(
... user=None, hardware_bus=HWBus.PCI, hardware_vendor_id='0x10de',
... hardware_product_id='0x0455', hardware_driver_name='ehci_hcd',
... hardware_owner_is_bug_reporter=True)
>>> for bugtask in ubuntu.searchTasks(search_params):
... print bugtask.bug.id, bugtask.bug.owner.displayname
1 Sample Person
2 Sample Person
The PCI device (0x10de, 0x0455) is not controlled in any HWDB submission
by the sd driver, so we'll get an empty result set for this query.
>>> search_params = BugTaskSearchParams(
... user=None, hardware_bus=HWBus.PCI, hardware_vendor_id='0x10de',
... hardware_product_id='0x0455', hardware_driver_name='sd',
... hardware_owner_is_bug_reporter=True)
>>> firefox.searchTasks(search_params).count()
0
We can also search for device owners which are subscribed to a bug.
>>> sample_person = getUtility(IPersonSet).getByEmail('test@canonical.com')
>>> search_params = BugTaskSearchParams(
... user=None, hardware_bus=HWBus.PCI, hardware_vendor_id='0x10de',
... hardware_product_id='0x0455',
... hardware_owner_is_subscribed_to_bug=True)
>>> for bugtask in ubuntu.searchTasks(search_params):
... print bugtask.bug.id, bugtask.bug.isSubscribed(sample_person)
1 True
9 True
And we can search for device owners who are affected by a bug.
>>> bug_ten = getUtility(IBugSet).get(10)
>>> bug_ten.markUserAffected(sample_person, affected=True)
>>> search_params = BugTaskSearchParams(
... user=None, hardware_bus=HWBus.PCI, hardware_vendor_id='0x10de',
... hardware_product_id='0x0455',
... hardware_owner_is_affected_by_bug=True)
>>> for bugtask in ubuntu.searchTasks(search_params):
... print bugtask.bug.id, bugtask.bug.isUserAffected(sample_person)
10 True
Finally, we can search for who bugs which are directly linked to
a HWDB submission, where the submission contains the given device or
driver.
>>> from lp.hardwaredb.interfaces.hwdb import IHWSubmissionSet
>>> hw_submission = getUtility(IHWSubmissionSet).getBySubmissionKey(
... 'sample-submission')
>>> bug_19 = getUtility(IBugSet).get(19)
>>> bug_19.linkHWSubmission(hw_submission)
>>> search_params = BugTaskSearchParams(
... user=None, hardware_bus=HWBus.PCI, hardware_vendor_id='0x10de',
... hardware_product_id='0x0455', hardware_is_linked_to_bug=True)
>>> for bugtask in ubuntu.searchTasks(search_params):
... print bugtask.bug.id
19
If a device appears in a private submission, related bugs are shown
only if the user running the request is the owner of the submission
or an admin.
>>> naked_hw_submission = removeSecurityProxy(hw_submission)
>>> naked_hw_submission.private = True
>>> search_params = BugTaskSearchParams(
... user=sample_person, hardware_bus=HWBus.PCI,
... hardware_vendor_id='0x10de', hardware_product_id='0x0455',
... hardware_is_linked_to_bug=True)
>>> for bugtask in ubuntu.searchTasks(search_params):
... print bugtask.bug.id
19
>>> search_params = BugTaskSearchParams(
... user=foo_bar, hardware_bus=HWBus.PCI, hardware_vendor_id='0x10de',
... hardware_product_id='0x0455', hardware_is_linked_to_bug=True)
>>> for bugtask in ubuntu.searchTasks(search_params):
... print bugtask.bug.id
19
Other users cannot see that a bug is related to a device from a
private submission.
>>> search_params = BugTaskSearchParams(
... user=no_priv, hardware_bus=HWBus.PCI, hardware_vendor_id='0x10de',
... hardware_product_id='0x0455', hardware_is_linked_to_bug=True)
>>> ubuntu.searchTasks(search_params).count()
0
>>> search_params = BugTaskSearchParams(
... user=None, hardware_bus=HWBus.PCI, hardware_vendor_id='0x10de',
... hardware_product_id='0x0455', hardware_is_linked_to_bug=True)
>>> ubuntu.searchTasks(search_params).count()
0
== Searching for bugs affecting me ==
The user searching for bugs can search for bugs affecting him.
We search for bugs affecting foo_bar, then check that all the results
return True for isUserAffected(foo_bar).
>>> search_params = BugTaskSearchParams(
... user=foo_bar, affects_me=True)
>>> print reduce(
... lambda x, y: x and y,
... [task.bug.isUserAffected(foo_bar)
... for task in firefox.searchTasks(search_params)])
True
== Searching for bugs linked to branches ==
We can search for bugs having branches linked to them.
>>> from lp.bugs.interfaces.bugtask import BugBranchSearch
>>> search_params = BugTaskSearchParams(
... user=None, linked_branches=BugBranchSearch.BUGS_WITH_BRANCHES)
>>> for task in firefox.searchTasks(search_params):
... print task.bug.id, task.bug.linked_branches.count()
4 2
5 1
Similarly, we can search for bugs that do not have any linked branches.
>>> from lp.bugs.interfaces.bugtask import BugBranchSearch
>>> search_params = BugTaskSearchParams(
... user=None, linked_branches=BugBranchSearch.BUGS_WITHOUT_BRANCHES)
>>> for task in firefox.searchTasks(search_params):
... print task.bug.id, task.bug.linked_branches.count()
1 0
6 0
18 0
20 0
21 0
And we can search for bugs linked to a specific branch.
>>> search_params = BugTaskSearchParams(
... user=None, linked_branches=1)
>>> for task in firefox.searchTasks(search_params):
... print task.bug.id
4
5
== Ordering search results ==
The result returned by bugtask searches can come sorted by a specified order
=== Ordering by number of duplicates ===
It is possible to sort the results by the number of duplicates each bag has.
Here is the list of bugs for Ubuntu.
>>> params = BugTaskSearchParams(
... orderby='-number_of_duplicates', user=None)
>>> ubuntu_tasks = ubuntu.searchTasks(params)
>>> for bugtask in ubuntu_tasks:
... print bugTaskInfo(bugtask)
17 1 mozilla-firefox (Ubuntu) Firefox does not support SVG
23 9 thunderbird (Ubuntu) Thunderbird crashes
25 10 linux-source-2.6.15 (Ubuntu) another test bug
26 2 Ubuntu Blackhole Trash folder
41 19 cdrkit (Ubuntu) Bug to be fixed in trunk
None of these bugs have any duplicates.
>>> [bugtask.bug.id for bugtask in ubuntu_tasks
... if bugtask.bug.duplicateof is not None]
[]
>>> from canonical.database.sqlbase import flush_database_updates
We mark bug #10 as a duplicate of bug #9.
>>> bug_nine = getUtility(IBugSet).get(9)
>>> bug_ten = getUtility(IBugSet).get(10)
>>> bug_ten.markAsDuplicate(bug_nine)
>>> flush_database_updates()
Searching again reveals bug #9 at the top of the list, since it now has a duplicate.
>>> ubuntu_tasks = ubuntu.searchTasks(params)
>>> for bugtask in ubuntu_tasks:
... print bugTaskInfo(bugtask)
23 9 thunderbird (Ubuntu) Thunderbird crashes
17 1 mozilla-firefox (Ubuntu) Firefox does not support SVG
25 10 linux-source-2.6.15 (Ubuntu) another test bug
26 2 Ubuntu Blackhole Trash folder
41 19 cdrkit (Ubuntu) Bug to be fixed in trunk
=== Ordering by number of comments ===
It is also possible to sort the results by the number of comments on a bug.
Here is the list of bugs for Ubuntu, sorted by their number of comments.
>>> params = BugTaskSearchParams(
... orderby='-message_count', user=None)
>>> ubuntu_tasks = ubuntu.searchTasks(params)
>>> for bugtask in ubuntu_tasks:
... bug = bugtask.bug
... print '%s %s [%s comments]' % (
... bug.id, bug.title, bug.message_count)
2 Blackhole Trash folder [5 comments]
1 Firefox does not support SVG [3 comments]
10 another test bug [2 comments]
9 Thunderbird crashes [1 comments]
19 Bug to be fixed in trunk [1 comments]
=== Ordering by bug heat ===
Another way of sorting searches is by bug heat.
>>> params = BugTaskSearchParams(
... orderby='id', user=None)
>>> ubuntu_tasks = ubuntu.searchTasks(params)
>>> for task in ubuntu_tasks:
... task.bug.setHeat(task.bug.id)
>>> transaction.commit()
>>> params = BugTaskSearchParams(
... orderby='-heat', user=None)
>>> ubuntu_tasks = ubuntu.searchTasks(params)
>>> for bugtask in ubuntu_tasks:
... bug = bugtask.bug
... print '%s %s [heat: %s]' % (
... bug.id, bug.title, bug.heat)
19 Bug to be fixed in trunk [heat: 19]
10 another test bug [heat: 10]
9 Thunderbird crashes [heat: 9]
2 Blackhole Trash folder [heat: 2]
1 Firefox does not support SVG [heat: 1]
=== Ordering by patch age ===
We can also sort search results by the creation time of the youngest
patch attached to a bug.
Since we have at present no bugs with patches, we use effectively
the default sort order, by bug task ID (which is implicitly added as
a "second level" sort order to ensure reliable sorting).
>>> params = BugTaskSearchParams(
... orderby='latest_patch_uploaded', user=None)
>>> ubuntu_tasks = ubuntu.searchTasks(params)
>>> for bugtask in ubuntu_tasks:
... print bugTaskInfo(bugtask)
17 1 mozilla-firefox (Ubuntu) Firefox does not support SVG
23 9 thunderbird (Ubuntu) Thunderbird crashes
25 10 linux-source-2.6.15 (Ubuntu) another test bug
26 2 Ubuntu Blackhole Trash folder
41 19 cdrkit (Ubuntu) Bug to be fixed in trunk
If we add a patch attachment to bug 2 and bug 10, they are listed first.
>>> patch_attachment_bug_2 = factory.makeBugAttachment(
... bug=bug_two, is_patch=True)
>>> transaction.commit()
>>> patch_attachment_bug_10 = factory.makeBugAttachment(
... bug=bug_ten, is_patch=True)
>>> params = BugTaskSearchParams(
... orderby='latest_patch_uploaded', user=None)
>>> ubuntu_tasks = ubuntu.searchTasks(params)
>>> for bugtask in ubuntu_tasks:
... print bugTaskInfo(bugtask)
26 2 Ubuntu Blackhole Trash folder
25 10 linux-source-2.6.15 (Ubuntu) another test bug
17 1 mozilla-firefox (Ubuntu) Firefox does not support SVG
23 9 thunderbird (Ubuntu) Thunderbird crashes
41 19 cdrkit (Ubuntu) Bug to be fixed in trunk
== Searching using a flat interface ==
An alternative, simplified interface for searching bug tasks is available
by passing all search parameters as function arguments to searchTasks.
This interface corresponds to the search options available from the web
search form and the public API.
>>> def print_bugtasks(bugtasks):
... for bugtask in bugtasks:
... print '%s %s %s %s %s' % (
... bugtask.bug.id, bugtask.bugtargetdisplayname,
... bugtask.bug.title, bugtask.status.title.upper(),
... bugtask.importance.title.upper())
When we call the method on Ubuntu, without any parameters, the result is
identical to calling searchTasks.
>>> print_bugtasks(ubuntu.searchTasks(None, user=None))
1 mozilla-firefox (Ubuntu) Firefox does not support SVG NEW MEDIUM
9 thunderbird (Ubuntu) Thunderbird crashes CONFIRMED MEDIUM
2 Ubuntu Blackhole Trash folder NEW MEDIUM
19 cdrkit (Ubuntu) Bug to be fixed in trunk NEW UNDECIDED
If we want to restrict the search using certain parameters pass them to
the function directly. Here we search Ubuntu again, but only bugs for
the firefox package.
>>> from lp.registry.interfaces.sourcepackagename import (
... ISourcePackageNameSet)
>>> source_package_name_set = getUtility(ISourcePackageNameSet)
>>> firefox_source_package = source_package_name_set['mozilla-firefox']
>>> print_bugtasks(ubuntu.searchTasks(
... None, user=None, sourcepackagename=firefox_source_package))
1 mozilla-firefox (Ubuntu) Firefox does not support SVG NEW MEDIUM
Or search for bugs on a distribution source package directly.
>>> print_bugtasks(ubuntu_firefox.searchTasks(None, user=None))
1 mozilla-firefox (Ubuntu) Firefox does not support SVG NEW MEDIUM
Or we can search a certain milestone (only getting bugs targeted to it).
>>> firefox_milestone_1 = firefox.getMilestone('1.0')
>>> print_bugtasks(firefox_milestone_1.searchTasks(None, user=None))
1 Mozilla Firefox Firefox does not support SVG NEW LOW
1 Mozilla Firefox 1.0 Firefox does not support SVG NEW UNDECIDED
We can restrict our search for firefox bugs with a text search.
>>> print_bugtasks(firefox.searchTasks(
... None, user=None, search_text='instructions'))
5 Mozilla Firefox Firefox install instructions should be complete NEW CRITICAL
Or restrict our search (over the mozilla project this time) to a list of
relevant importance values.
>>> print_bugtasks(mozilla.searchTasks(
... None, user=None,
... importance=[BugTaskImportance.LOW, BugTaskImportance.MEDIUM]))
4 Mozilla Firefox Reflow problems with complex page layouts NEW MEDIUM
1 Mozilla Firefox Firefox does not support SVG NEW LOW
== Searching by structural subscriber ==
The 'structural_subscriber' search parameter allows one to search all the bug
tasks to which a person is structurally subscribed. A person can be a
structural subscriber for a product, a product series, a project, a milestone,
a distribution, a distribution series and a distribution source package. No
Privileges Person isn't a structural subscriber, so no bug tasks are found:
>>> from lp.registry.interfaces.person import IPersonSet
>>> no_priv = getUtility(IPersonSet).getByName('no-priv')
>>> no_priv_struct_sub = BugTaskSearchParams(
... user=None, structural_subscriber=no_priv)
>>> found_bugtasks = bugtask_set.search(no_priv_struct_sub)
>>> found_bugtasks.count()
0
Create a new person and make them a subscriber to all Firefox (product) bug
reports. Subsequently, we confirm that they are subscribed to all of the
Firefox bug tasks.
>>> product_struct_subber = factory.makePerson(
... name='product-struct-subber')
>>> firefox.addBugSubscription(product_struct_subber,
... product_struct_subber)
<...StructuralSubscription object at ...>
>>> product_struct_sub_search = BugTaskSearchParams(
... user=None, structural_subscriber=product_struct_subber)
>>> found_bugtasks = bugtask_set.search(product_struct_sub_search)
>>> found_bugtasks.count()
7
Create a new person and subscribe them to all of the bug tasks for a product
series. We then test to see that they are subscribed to all of the bug tasks
for the product series in which they are interested.
>>> product_series = firefox.getSeries('1.0')
>>> all_targeted = BugTaskSearchParams(user=None, omit_targeted=False)
>>> series_tasks = product_series.searchTasks(all_targeted)
>>> series_struct_subber = factory.makePerson(
... name='series-struct-subber')
>>> product_series.addBugSubscription(series_struct_subber,
... series_struct_subber)
<...StructuralSubscription object at ...>
>>> series_struct_sub_search = BugTaskSearchParams(
... user=None, structural_subscriber=series_struct_subber)
>>> found_bugtasks = bugtask_set.search(series_struct_sub_search)
>>> found_bugtasks.count()
2
Create a new product which will be a part of a project group. A bug is
created for the product which should then show up for structural subscribers
of the project group. Then a new person is created who is subscribed to all
of the bug reports about the project. Search for bug tasks that this new
person is subscribed.
>>> product = factory.makeProduct()
>>> bug = factory.makeBug(product=product)
>>> project = factory.makeProject()
>>> product.project = project
>>> project_struct_subber = factory.makePerson(
... name='project-struct-subber')
>>> project.addBugSubscription(project_struct_subber,
... project_struct_subber)
<...StructuralSubscription object at ...>
>>> project_struct_sub_search = BugTaskSearchParams(
... user=None, structural_subscriber=project_struct_subber)
>>> found_bugtasks = bugtask_set.search(project_struct_sub_search)
>>> found_bugtasks.count()
1
We will also subscribe this project subscriber to a product that is a part of
the project and ensure that duplicate bug tasks do not appear in the search
results.
>>> product2 = factory.makeProduct()
>>> bug = factory.makeBug(product=product2)
>>> product2.project = project
>>> product2.addBugSubscription(project_struct_subber,
... project_struct_subber)
<...StructuralSubscription object at ...>
>>> project_struct_sub_search = BugTaskSearchParams(
... user=None, structural_subscriber=project_struct_subber)
>>> found_bugtasks = bugtask_set.search(project_struct_sub_search)
>>> found_bugtasks.count()
2
Create a new person and subscribe them to all of bug tasks targeted to a
milestone. We then test to see that they are subscribed to all of the
bug tasks for the milestone in which they are interested.
>>> milestone_struct_subber = factory.makePerson(
... name='milestone-struct-subber')
>>> product_milestone.addBugSubscription(milestone_struct_subber,
... milestone_struct_subber)
<...StructuralSubscription object at ...>
>>> milestone_struct_sub_search = BugTaskSearchParams(
... user=None, structural_subscriber=milestone_struct_subber)
>>> found_bugtasks = bugtask_set.search(milestone_struct_sub_search)
>>> found_bugtasks.count()
2
Create another new person and subscribe them to all the Ubuntu bug reports -
crazy I know. Then test to see that this poor person is subscribed to all
bugs with an Ubuntu bug task.
>>> distro_struct_subber = factory.makePerson(
... name='distro-struct-subber')
>>> ubuntu.addBugSubscription(distro_struct_subber,
... distro_struct_subber)
<...StructuralSubscription object at ...>
>>> distro_struct_sub_search = BugTaskSearchParams(
... user=None, structural_subscriber=distro_struct_subber)
>>> found_bugtasks = bugtask_set.search(distro_struct_sub_search)
>>> found_bugtasks.count()
5
Create a new person who will only be subscribed to an Ubuntu series, which is
something more reasonable than all of Ubuntu. Test to ensure that this person
is subscribed to all of the bug tasks about that distro series.
>>> ubuntu = getUtility(IDistributionSet).getByName('ubuntu')
>>> hoary = ubuntu.getSeries('hoary')
>>> distro_series_struct_subber = factory.makePerson(
... name='distro-series-struct-subber')
>>> hoary.addBugSubscription(distro_series_struct_subber,
... distro_series_struct_subber)
<...StructuralSubscription object at ...>
>>> distro_series_struct_sub_search = BugTaskSearchParams(
... user=None, structural_subscriber=distro_series_struct_subber)
>>> found_bugtasks = bugtask_set.search(distro_series_struct_sub_search)
>>> all_targeted = BugTaskSearchParams(user=None, omit_targeted=False)
>>> hoary_bugtasks = hoary.searchTasks(all_targeted)
>>> found_bugtasks.count() == hoary_bugtasks.count()
True
Create a new person and make them a subscriber to all Ubuntu Firefox (a
distribution source package) bug reports. Test to see that the new person is
subscribed to all of the Ubuntu Firefox bug tasks.
>>> package_struct_subber = factory.makePerson(
... name='package-struct-subber')
>>> ubuntu_firefox.addBugSubscription(package_struct_subber,
... package_struct_subber)
<...StructuralSubscription object at ...>
>>> package_struct_sub_search = BugTaskSearchParams(
... user=None, structural_subscriber=package_struct_subber)
>>> found_bugtasks = bugtask_set.search(package_struct_sub_search)
>>> found_bugtasks.count()
1
We'll also subscribe the person who is currently subscribed to a package's bug
reports, package_struct_subber, to the bug reports of a product series to
ensure that the structural_subscriber search is returning the set of both bug
tasks.
>>> product_series.addBugSubscription(package_struct_subber,
... package_struct_subber)
<...StructuralSubscription object at ...>
>>> package_struct_sub_search = BugTaskSearchParams(
... user=None, structural_subscriber=package_struct_subber)
>>> found_bugtasks = bugtask_set.search(package_struct_sub_search)
>>> combined_bugtasks_count = (ubuntu_firefox_bugs.count () +
... series_tasks.count())
>>> found_bugtasks.count() == combined_bugtasks_count
True
|