|
18114.1.2
by Colin Watson
Implement the basics of bug linking for Git-based merge proposals. |
1 |
# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
|
|
8687.15.15
by Karl Fogel
Add the copyright header block to files under lib/lp/bugs/. |
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
3 |
||
|
1564
by Canonical.com Patch Queue Manager
refactor BugFactory to have an API that communicates what it does (fixes https://launchpad.ubuntu.com/malone/bugs/133). rip out a big chunk of search code that was behind the anorak search screen. this screen now just redirects to the Malone homepage, of course, but if the redirect doesn't happen in time, code was being hit in the search() method of its view that was raising errors. the view class itself will be entirely ripped out in another round of refactoring (noted in XXX's). |
4 |
"""Launchpad bug-related database table classes."""
|
5 |
||
6 |
__metaclass__ = type |
|
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
7 |
|
|
4755.1.43
by Curtis Hovey
Revisions pre review. |
8 |
__all__ = [ |
|
7705.1.1
by Graham Binns
Added IBug.addTask() as a nominal wrapper around IBugTaskSet.createTask(). |
9 |
'Bug', |
|
8451.2.1
by Abel Deuring
Method added to retrieve a list of device owners who are affected by bugs |
10 |
'BugAffectsPerson', |
|
7705.1.1
by Graham Binns
Added IBug.addTask() as a nominal wrapper around IBugTaskSet.createTask(). |
11 |
'BugBecameQuestionEvent', |
|
7675.1138.5
by Danilo Segan
Move basic mute functionality to BugMute table as tested by TestBugSubscriptionMethods. |
12 |
'BugMute', |
|
7705.1.1
by Graham Binns
Added IBug.addTask() as a nominal wrapper around IBugTaskSet.createTask(). |
13 |
'BugSet', |
|
8451.2.1
by Abel Deuring
Method added to retrieve a list of device owners who are affected by bugs |
14 |
'BugTag', |
|
7675.553.35
by Deryck Hodge
Fix some import warnings. |
15 |
'FileBugData', |
|
16122.2.1
by Steve Kowalik
Rewrite the heavy-lifting queries in IBug.getSubscriptionForPerson() and IPersonSubscriptionInfo._getDirectAndDuplicateSubscriptions() to be performant using two CTEs, and bulk load all related people, bugtasks and pillars in the second function. |
16 |
'generate_subscription_with', |
|
12541.2.5
by Gary Poster
refactor getAlsoNotifiedSubscribers to make it reusable, to use the structuralsubscriber function directly, and to better handle direct subscribers; eliminate duplicated code. |
17 |
'get_also_notified_subscribers', |
|
7705.1.1
by Graham Binns
Added IBug.addTask() as a nominal wrapper around IBugTaskSet.createTask(). |
18 |
'get_bug_tags_open_count', |
19 |
]
|
|
|
1102
by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs |
20 |
|
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
21 |
|
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
22 |
from cStringIO import StringIO |
|
17390.2.2
by Colin Watson
Use the modern names for email subpackages, introduced in Python 2.5. |
23 |
from email.utils import make_msgid |
|
11869.17.2
by Gavin Panella
New class BugSubscriptionInfo. |
24 |
from functools import wraps |
|
11869.18.11
by Gavin Panella
Merged subscribe-to-tag-bug-151129-5 into subscribe-to-tag-bug-151129-6, resolving 1 conflict. |
25 |
from itertools import chain |
|
3691.151.5
by kiko
Order sets, and remove obsolete use of sets.Set. Clean up some checks in dbschema. |
26 |
import operator |
27 |
import re |
|
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
28 |
|
29 |
from lazr.lifecycle.event import ( |
|
30 |
ObjectCreatedEvent, |
|
31 |
ObjectModifiedEvent, |
|
32 |
)
|
|
33 |
from lazr.lifecycle.snapshot import Snapshot |
|
|
14027.3.2
by Jeroen Vermeulen
Merge devel, resolve conflicts. |
34 |
import pytz |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
35 |
from sqlobject import ( |
36 |
BoolCol, |
|
37 |
ForeignKey, |
|
38 |
IntCol, |
|
39 |
SQLMultipleJoin, |
|
40 |
SQLObjectNotFound, |
|
41 |
SQLRelatedJoin, |
|
42 |
StringCol, |
|
43 |
)
|
|
44 |
from storm.expr import ( |
|
45 |
And, |
|
|
17241.1.8
by William Grant
Bug.default_bugtask avoids inactive products where possible, fixing the /bugs/1234 redirect for normal users. |
46 |
Coalesce, |
|
12775.3.1
by William Grant
Fix get_bug_tags_open_count to not retrieve EVERYTHING. Now returns a less unpleasant resultset instead. |
47 |
Desc, |
|
13023.7.2
by Danilo Segan
Split Gary's server-side changes. |
48 |
In, |
|
14175.1.1
by William Grant
Use nested joins rather than subselects for preloading message parents. Fixes timeouts. Also removes nasty literal SQL strings. Because ew. |
49 |
Join, |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
50 |
LeftJoin, |
51 |
Max, |
|
52 |
Not, |
|
53 |
Or, |
|
54 |
Select, |
|
55 |
SQL, |
|
|
13165.2.1
by Robert Collins
Change getUsedBugTagsWithOpenCounts to fit our usage better and teach it to use BugSummary. |
56 |
Sum, |
|
13445.1.11
by Gary Poster
revert SQL change |
57 |
Union, |
|
16122.2.1
by Steve Kowalik
Rewrite the heavy-lifting queries in IBug.getSubscriptionForPerson() and IPersonSubscriptionInfo._getDirectAndDuplicateSubscriptions() to be performant using two CTEs, and bulk load all related people, bugtasks and pillars in the second function. |
58 |
With, |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
59 |
)
|
|
11544.1.4
by Robert Collins
Remove listification from bugs/messages API call, so slicing actually can do less work. |
60 |
from storm.info import ClassAlias |
|
7675.1138.5
by Danilo Segan
Move basic mute functionality to BugMute table as tested by TestBugSubscriptionMethods. |
61 |
from storm.locals import ( |
62 |
DateTime, |
|
63 |
Int, |
|
64 |
Reference, |
|
65 |
)
|
|
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
66 |
from storm.store import ( |
67 |
EmptyResultSet, |
|
68 |
Store, |
|
69 |
)
|
|
70 |
from zope.component import getUtility |
|
|
6061.2.6
by Maris Fogels
Updated more deprecated Zope interface references. |
71 |
from zope.contenttype import guess_content_type |
|
2454
by Canonical.com Patch Queue Manager
[r=stevea]. make bug notifictions concerning the same bug be part of the same email thread. |
72 |
from zope.event import notify |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
73 |
from zope.interface import ( |
|
17610.1.1
by Colin Watson
Switch zope.interface users from class advice to class decorators. |
74 |
implementer, |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
75 |
providedBy, |
76 |
)
|
|
|
14302.4.1
by Ian Booth
Allow users in project roles and comment owners to hide comments |
77 |
from zope.security.interfaces import Unauthorized |
|
12541.2.5
by Gary Poster
refactor getAlsoNotifiedSubscribers to make it reusable, to use the structuralsubscriber function directly, and to better handle direct subscribers; eliminate duplicated code. |
78 |
from zope.security.proxy import ( |
79 |
ProxyFactory, |
|
80 |
removeSecurityProxy, |
|
81 |
)
|
|
|
7876.3.5
by Francis J. Lacoste
Snapshot moved to lazr.lifecycle. |
82 |
|
|
16048.1.1
by William Grant
format-imports |
83 |
from lp.answers.interfaces.questiontarget import IQuestionTarget |
|
15968.5.9
by Rick Harding
More file updates missed |
84 |
from lp.app.enums import ( |
|
15968.5.4
by Rick Harding
more moving information type to app instead of registry since it's gone universal |
85 |
InformationType, |
86 |
PRIVATE_INFORMATION_TYPES, |
|
87 |
PROPRIETARY_INFORMATION_TYPES, |
|
88 |
SECURITY_INFORMATION_TYPES, |
|
89 |
ServiceUsage, |
|
90 |
)
|
|
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
91 |
from lp.app.errors import ( |
92 |
NotFoundError, |
|
|
15365.1.1
by Steve Kowalik
Forbid open and delegated teams to be subscribed to private branches, and |
93 |
SubscriptionPrivacyViolation, |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
94 |
UserCannotUnsubscribePerson, |
95 |
)
|
|
|
16084.2.19
by Deryck Hodge
Bug and Branch implement IInformationType, so update the model |
96 |
from lp.app.interfaces.informationtype import IInformationType |
|
13130.1.6
by Curtis Hovey
Move ILaunchpadCelebrity to lp.app. |
97 |
from lp.app.interfaces.launchpad import ILaunchpadCelebrities |
|
15162.1.28
by Ian Booth
Add job to remove subscriptions for users who can't see the bug after it changes info_type or is retargetted |
98 |
from lp.app.interfaces.services import IService |
|
7675.1347.4
by Aaron Bentley
Use InformationTypeMixin for Bug. |
99 |
from lp.app.model.launchpad import InformationTypeMixin |
|
12442.2.9
by j.c.sackett
Ran import reformatter per review. |
100 |
from lp.app.validators import LaunchpadValidationError |
|
14986.1.1
by Steve Kowalik
Change CreateBugParams to take information_type, and drop IBug._{private,security_related}. |
101 |
from lp.bugs.adapters.bug import convert_to_information_type |
|
8523.3.1
by Gavin Panella
Bugs tree reorg after automated migration. |
102 |
from lp.bugs.adapters.bugchange import ( |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
103 |
BranchLinkedToBug, |
104 |
BranchUnlinkedFromBug, |
|
105 |
BugConvertedToQuestion, |
|
|
14027.3.2
by Jeroen Vermeulen
Merge devel, resolve conflicts. |
106 |
BugDuplicateChange, |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
107 |
BugWatchAdded, |
108 |
BugWatchRemoved, |
|
109 |
SeriesNominated, |
|
110 |
UnsubscribedFromBug, |
|
111 |
)
|
|
|
14937.1.1
by Steve Kowalik
Every other enums module contains the s, force bugs into line. |
112 |
from lp.bugs.enums import BugNotificationLevel |
|
15866.1.2
by William Grant
Bug.transitionToInformationType now uses CannotChangeInformationType, rather than the misleading BugCannotBePrivate. |
113 |
from lp.bugs.errors import InvalidDuplicateValue |
|
14174.2.2
by Ian Booth
Lint |
114 |
from lp.bugs.interfaces.bug import ( |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
115 |
IBug, |
116 |
IBugBecameQuestionEvent, |
|
|
7675.1138.5
by Danilo Segan
Move basic mute functionality to BugMute table as tested by TestBugSubscriptionMethods. |
117 |
IBugMute, |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
118 |
IBugSet, |
119 |
IFileBugData, |
|
120 |
)
|
|
|
8523.3.1
by Gavin Panella
Bugs tree reorg after automated migration. |
121 |
from lp.bugs.interfaces.bugactivity import IBugActivitySet |
122 |
from lp.bugs.interfaces.bugattachment import ( |
|
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
123 |
BugAttachmentType, |
124 |
IBugAttachmentSet, |
|
125 |
)
|
|
|
8631.1.1
by Gavin Panella
Fix up the messy imports before attemtping anything else. |
126 |
from lp.bugs.interfaces.bugmessage import IBugMessageSet |
127 |
from lp.bugs.interfaces.bugnomination import ( |
|
|
14540.3.1
by Ian Booth
Allow a declined bug nomination to be re-nominated |
128 |
BugNominationStatus, |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
129 |
NominationError, |
130 |
NominationSeriesObsoleteError, |
|
131 |
)
|
|
|
8631.1.1
by Gavin Panella
Fix up the messy imports before attemtping anything else. |
132 |
from lp.bugs.interfaces.bugnotification import IBugNotificationSet |
|
15761.2.9
by William Grant
Let CreateBugParams take an IBugTarget, rather than a product, distribution and sourcepackagename. |
133 |
from lp.bugs.interfaces.bugtarget import ISeriesBugTarget |
|
8523.3.1
by Gavin Panella
Bugs tree reorg after automated migration. |
134 |
from lp.bugs.interfaces.bugtask import ( |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
135 |
BugTaskStatus, |
|
12845.2.6
by Robert Collins
Failing test for garbo migration to INCOMPLETE_WITH/WITHOUT_RESPONSE. |
136 |
BugTaskStatusSearch, |
|
12541.2.5
by Gary Poster
refactor getAlsoNotifiedSubscribers to make it reusable, to use the structuralsubscriber function directly, and to better handle direct subscribers; eliminate duplicated code. |
137 |
IBugTask, |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
138 |
IBugTaskSet, |
|
15761.2.11
by William Grant
Use IllegalTarget instead of AssertionError when calling createBug with an ISeriesBugTarget. It'll be exposed through the API shortly. |
139 |
IllegalTarget, |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
140 |
UNRESOLVED_BUGTASK_STATUSES, |
141 |
)
|
|
|
8523.3.1
by Gavin Panella
Bugs tree reorg after automated migration. |
142 |
from lp.bugs.interfaces.bugtracker import BugTrackerType |
143 |
from lp.bugs.interfaces.bugwatch import IBugWatchSet |
|
144 |
from lp.bugs.interfaces.cve import ICveSet |
|
|
14578.2.1
by William Grant
Move librarian stuff from canonical.launchpad to lp.services.librarian. canonical.librarian remains untouched. |
145 |
from lp.bugs.interfaces.hasbug import IHasBug |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
146 |
from lp.bugs.mail.bugnotificationrecipients import BugNotificationRecipients |
|
13955.1.1
by Graham Binns
Batched activity now no longer pulls in results that have already been shown. |
147 |
from lp.bugs.model.bugactivity import BugActivity |
|
7675.489.1
by Graham Binns
Bug.has_patches now uses a straight Storm query rather than looping over Bug.attachments. |
148 |
from lp.bugs.model.bugattachment import BugAttachment |
|
8631.1.1
by Gavin Panella
Fix up the messy imports before attemtping anything else. |
149 |
from lp.bugs.model.bugbranch import BugBranch |
|
17910.1.8
by William Grant
Replace IObject(Created|Deleted)Events on IBugBranch with IObjectLinkedEvents on Bug. |
150 |
from lp.bugs.model.buglinktarget import ( |
151 |
ObjectLinkedEvent, |
|
152 |
ObjectUnlinkedEvent, |
|
153 |
)
|
|
|
8631.1.1
by Gavin Panella
Fix up the messy imports before attemtping anything else. |
154 |
from lp.bugs.model.bugmessage import BugMessage |
155 |
from lp.bugs.model.bugnomination import BugNomination |
|
156 |
from lp.bugs.model.bugnotification import BugNotification |
|
157 |
from lp.bugs.model.bugsubscription import BugSubscription |
|
|
7675.879.1
by Gavin Panella
Fix lint. |
158 |
from lp.bugs.model.bugtarget import OfficialBugTag |
|
8631.1.1
by Gavin Panella
Fix up the messy imports before attemtping anything else. |
159 |
from lp.bugs.model.bugtask import ( |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
160 |
BugTask, |
161 |
bugtask_sort_key, |
|
162 |
)
|
|
|
8631.1.1
by Gavin Panella
Fix up the messy imports before attemtping anything else. |
163 |
from lp.bugs.model.bugwatch import BugWatch |
|
7675.1054.4
by Danilo Segan
Merge Gary's branch from stable. |
164 |
from lp.bugs.model.structuralsubscription import ( |
|
14291.1.2
by Jeroen Vermeulen
Lint. |
165 |
get_structural_subscribers, |
|
14494.6.23
by Gavin Panella
Update BugSubscriptionInfo.structural_* to use new function get_structural_subscriptions(). |
166 |
get_structural_subscriptions, |
|
7675.1054.4
by Danilo Segan
Merge Gary's branch from stable. |
167 |
)
|
|
13277.4.6
by Graham Binns
Updated Bug.linked_branches to use the new linkedToBug() filter of BranchCollection. |
168 |
from lp.code.interfaces.branchcollection import IAllBranches |
|
18114.1.2
by Colin Watson
Implement the basics of bug linking for Git-based merge proposals. |
169 |
from lp.code.interfaces.gitcollection import IAllGitRepositories |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
170 |
from lp.hardwaredb.interfaces.hwdb import IHWSubmissionBugSet |
|
15866.1.2
by William Grant
Bug.transitionToInformationType now uses CannotChangeInformationType, rather than the misleading BugCannotBePrivate. |
171 |
from lp.registry.errors import CannotChangeInformationType |
|
15162.1.20
by Ian Booth
Add feature flag and functionality to mirror legacy bug access |
172 |
from lp.registry.interfaces.accesspolicy import ( |
173 |
IAccessArtifactGrantSource, |
|
174 |
IAccessArtifactSource, |
|
175 |
)
|
|
|
7675.110.3
by Curtis Hovey
Ran the migration script to move registry code to lp.registry. |
176 |
from lp.registry.interfaces.distribution import IDistribution |
|
10054.26.1
by Adi Roiban
Refactor DistroSeriesStatus to SeriesStatus; Don't prompt for setting up translations for obsolete product series. |
177 |
from lp.registry.interfaces.distroseries import IDistroSeries |
|
12482.1.1
by Robert Collins
Eager load related fields for bugs when executing bug.bugtasks. |
178 |
from lp.registry.interfaces.person import ( |
179 |
IPersonSet, |
|
|
7675.1138.5
by Danilo Segan
Move basic mute functionality to BugMute table as tested by TestBugSubscriptionMethods. |
180 |
validate_person, |
|
12482.1.1
by Robert Collins
Eager load related fields for bugs when executing bug.bugtasks. |
181 |
validate_public_person, |
182 |
)
|
|
|
7675.110.3
by Curtis Hovey
Ran the migration script to move registry code to lp.registry. |
183 |
from lp.registry.interfaces.product import IProduct |
184 |
from lp.registry.interfaces.productseries import IProductSeries |
|
|
14494.6.4
by Gavin Panella
Repair BugSubscriptionInfo so that it works more like it was originally intended, and get it to pre-load preferred email addresses for subscribers. |
185 |
from lp.registry.interfaces.role import IPersonRoles |
|
15365.1.1
by Steve Kowalik
Forbid open and delegated teams to be subscribed to private branches, and |
186 |
from lp.registry.interfaces.series import SeriesStatus |
|
15502.2.1
by Steve Kowalik
Brutally rename RemoveBugSubscriptionsJob and related gubbins to a more generic |
187 |
from lp.registry.interfaces.sharingjob import ( |
188 |
IRemoveArtifactSubscriptionsJobSource, |
|
189 |
)
|
|
|
7675.110.3
by Curtis Hovey
Ran the migration script to move registry code to lp.registry. |
190 |
from lp.registry.interfaces.sourcepackage import ISourcePackage |
|
15685.1.6
by William Grant
Drop unused imports. |
191 |
from lp.registry.model.accesspolicy import reconcile_access_for_artifact |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
192 |
from lp.registry.model.person import ( |
193 |
Person, |
|
|
11869.17.6
by Gavin Panella
Add *Set classes for subscriptions and subscribers. Disable query checks for now. |
194 |
person_sort_key, |
|
11869.17.25
by Gavin Panella
Use PersonSet._getPrecachedPersons() because it's awesome. |
195 |
PersonSet, |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
196 |
)
|
|
7675.110.3
by Curtis Hovey
Ran the migration script to move registry code to lp.registry. |
197 |
from lp.registry.model.pillar import pillar_sort_key |
|
11474.2.3
by Robert Collins
Add getSubscribersForPerson to IBug, permitting single query setup of bug index pages, which according to OOPS is about 1.2 seconds (but if we're underestimating could be more) and will make analysing performance on the page easier. |
198 |
from lp.registry.model.teammembership import TeamParticipation |
|
14606.3.1
by William Grant
Merge canonical.database into lp.services.database. |
199 |
from lp.services.config import config |
|
17746.3.1
by William Grant
Basic port of QuestionBug/BugCve to XRef. |
200 |
from lp.services.database import bulk |
|
14606.3.1
by William Grant
Merge canonical.database into lp.services.database. |
201 |
from lp.services.database.constants import UTC_NOW |
202 |
from lp.services.database.datetimecol import UtcDateTimeCol |
|
|
14550.1.1
by Steve Kowalik
Run format-imports over lib/lp and lib/canonical/launchpad |
203 |
from lp.services.database.decoratedresultset import DecoratedResultSet |
|
14937.2.1
by Steve Kowalik
Add information_type to Bug, and set it for new and changed bugs. |
204 |
from lp.services.database.enumcol import EnumCol |
|
16675.1.1
by Steve Kowalik
Destroy all the callsites I could of IStoreSelector.get(), and kill lp.services.database.lpstorm, moving all of the interfaces to interfaces. |
205 |
from lp.services.database.interfaces import IStore |
|
14606.3.1
by William Grant
Merge canonical.database into lp.services.database. |
206 |
from lp.services.database.sqlbase import ( |
207 |
SQLBase, |
|
208 |
sqlvalues, |
|
209 |
)
|
|
|
7675.1138.5
by Danilo Segan
Move basic mute functionality to BugMute table as tested by TestBugSubscriptionMethods. |
210 |
from lp.services.database.stormbase import StormBase |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
211 |
from lp.services.fields import DuplicateBug |
|
14606.3.1
by William Grant
Merge canonical.database into lp.services.database. |
212 |
from lp.services.helpers import shortlist |
|
14578.2.1
by William Grant
Move librarian stuff from canonical.launchpad to lp.services.librarian. canonical.librarian remains untouched. |
213 |
from lp.services.librarian.interfaces import ILibraryFileAliasSet |
214 |
from lp.services.librarian.model import ( |
|
215 |
LibraryFileAlias, |
|
216 |
LibraryFileContent, |
|
217 |
)
|
|
|
13130.1.12
by Curtis Hovey
Sorted imports. |
218 |
from lp.services.messages.interfaces.message import ( |
219 |
IMessage, |
|
220 |
IndexedMessage, |
|
221 |
)
|
|
222 |
from lp.services.messages.model.message import ( |
|
223 |
Message, |
|
224 |
MessageChunk, |
|
225 |
MessageSet, |
|
226 |
)
|
|
|
11382.6.34
by Gavin Panella
Reformat imports in all files touched so far. |
227 |
from lp.services.propertycache import ( |
228 |
cachedproperty, |
|
|
11789.2.3
by Gavin Panella
Remove all use of IPropertyCacheManager. |
229 |
clear_property_cache, |
|
11789.2.4
by Gavin Panella
Change all uses of IPropertyCache outside of propertycache.py to get_property_cache. |
230 |
get_property_cache, |
|
11382.6.34
by Gavin Panella
Reformat imports in all files touched so far. |
231 |
)
|
|
14606.3.1
by William Grant
Merge canonical.database into lp.services.database. |
232 |
from lp.services.webapp.authorization import check_permission |
|
15685.1.6
by William Grant
Drop unused imports. |
233 |
from lp.services.webapp.interfaces import ILaunchBag |
|
16586.2.2
by Steve Kowalik
format-imports, and make SQL() no longer use string interpolation. |
234 |
from lp.services.webapp.publisher import ( |
235 |
get_raw_form_value_from_current_request, |
|
236 |
)
|
|
|
17746.3.1
by William Grant
Basic port of QuestionBug/BugCve to XRef. |
237 |
from lp.services.xref.interfaces import IXRefSet |
|
3691.104.1
by Bjorn Tillenius
make it possible to show tags on open bugs, and to get the number of bugs using the tag, through getUsedBugTags. |
238 |
|
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
239 |
|
|
12775.3.9
by William Grant
Document tag_count_columns hack. |
240 |
def snapshot_bug_params(bug_params): |
241 |
"""Return a snapshot of a `CreateBugParams` object."""
|
|
242 |
return Snapshot( |
|
243 |
bug_params, names=[ |
|
244 |
"owner", "title", "comment", "description", "msg", |
|
|
15761.2.19
by William Grant
Add target to the CreateBugParams constructor, and remove the product/distribution/sourcepackagename attributes. |
245 |
"datecreated", "information_type", "target", "status", |
246 |
"subscribers", "tags", "subscribe_owner", "filed_by", |
|
247 |
"importance", "milestone", "assignee", "cve"]) |
|
|
12775.3.9
by William Grant
Document tag_count_columns hack. |
248 |
|
249 |
||
250 |
class BugTag(SQLBase): |
|
251 |
"""A tag belonging to a bug."""
|
|
252 |
||
253 |
bug = ForeignKey(dbName='bug', foreignKey='Bug', notNull=True) |
|
254 |
tag = StringCol(notNull=True) |
|
255 |
||
256 |
||
|
13165.2.1
by Robert Collins
Change getUsedBugTagsWithOpenCounts to fit our usage better and teach it to use BugSummary. |
257 |
def get_bug_tags_open_count(context_condition, user, tag_limit=0, |
258 |
include_tags=None): |
|
259 |
"""Worker for IBugTarget.getUsedBugTagsWithOpenCounts.
|
|
260 |
||
261 |
See `IBugTarget` for details.
|
|
262 |
||
263 |
The only change is that this function takes a SQL expression for limiting
|
|
264 |
the found tags.
|
|
|
7030.1.7
by Bjorn Tillenius
clean up. |
265 |
:param context_condition: A Storm SQL expression, limiting the
|
|
7675.1204.4
by Robert Collins
Repurpose unused _getBugTaskContectWhereClause to be a helper for linking to bugsummaries. |
266 |
used tags to a specific context. Only the BugSummary table may be
|
267 |
used to choose the context. If False then no query will be performed
|
|
268 |
(and {} returned).
|
|
|
3691.104.3
by Bjorn Tillenius
include the open bug counts in the bug tags portlet. |
269 |
"""
|
|
13165.2.1
by Robert Collins
Change getUsedBugTagsWithOpenCounts to fit our usage better and teach it to use BugSummary. |
270 |
# Circular fail.
|
|
15685.1.4
by William Grant
Factor out bugsummary privacy filtering. |
271 |
from lp.bugs.model.bugsummary import ( |
272 |
BugSummary, |
|
273 |
get_bugsummary_filter_for_user, |
|
274 |
)
|
|
|
13165.2.1
by Robert Collins
Change getUsedBugTagsWithOpenCounts to fit our usage better and teach it to use BugSummary. |
275 |
tags = {} |
276 |
if include_tags: |
|
277 |
tags = dict((tag, 0) for tag in include_tags) |
|
|
7030.1.4
by Bjorn Tillenius
rewrite query generation. |
278 |
where_conditions = [ |
|
13165.2.1
by Robert Collins
Change getUsedBugTagsWithOpenCounts to fit our usage better and teach it to use BugSummary. |
279 |
BugSummary.status.is_in(UNRESOLVED_BUGTASK_STATUSES), |
280 |
BugSummary.tag != None, |
|
|
7030.1.7
by Bjorn Tillenius
clean up. |
281 |
context_condition, |
|
7030.1.4
by Bjorn Tillenius
rewrite query generation. |
282 |
]
|
|
15685.1.4
by William Grant
Factor out bugsummary privacy filtering. |
283 |
|
284 |
# Apply the privacy filter.
|
|
285 |
store = IStore(BugSummary) |
|
286 |
user_with, user_where = get_bugsummary_filter_for_user(user) |
|
287 |
if user_with: |
|
288 |
store = store.with_(user_with) |
|
289 |
where_conditions.extend(user_where) |
|
290 |
||
|
13175.3.20
by Robert Collins
And exclude rows cancelled out by the journal. |
291 |
sum_count = Sum(BugSummary.count) |
292 |
tag_count_columns = (BugSummary.tag, sum_count) |
|
|
13155.2.15
by Francis J. Lacoste
Lint blows but hoover sucks. |
293 |
|
|
13165.2.1
by Robert Collins
Change getUsedBugTagsWithOpenCounts to fit our usage better and teach it to use BugSummary. |
294 |
# Always query for used
|
295 |
def _query(*args): |
|
296 |
return store.find(tag_count_columns, *(where_conditions + list(args)) |
|
|
13175.3.20
by Robert Collins
And exclude rows cancelled out by the journal. |
297 |
).group_by(BugSummary.tag).having(sum_count != 0).order_by( |
|
13165.2.1
by Robert Collins
Change getUsedBugTagsWithOpenCounts to fit our usage better and teach it to use BugSummary. |
298 |
Desc(Sum(BugSummary.count)), BugSummary.tag) |
299 |
used = _query() |
|
300 |
if tag_limit: |
|
301 |
used = used[:tag_limit] |
|
302 |
if include_tags: |
|
303 |
# Union in a query for just include_tags.
|
|
304 |
used = used.union(_query(BugSummary.tag.is_in(include_tags))) |
|
305 |
tags.update(dict(used)) |
|
306 |
return tags |
|
|
3691.40.10
by Bjorn Tillenius
add IBugTarget.getUsedBugTags. |
307 |
|
|
3691.40.17
by Bjorn Tillenius
apply review comments. |
308 |
|
|
17610.1.1
by Colin Watson
Switch zope.interface users from class advice to class decorators. |
309 |
@implementer(IBugBecameQuestionEvent) |
|
4755.1.43
by Curtis Hovey
Revisions pre review. |
310 |
class BugBecameQuestionEvent: |
311 |
"""See `IBugBecameQuestionEvent`."""
|
|
312 |
||
313 |
def __init__(self, bug, question, user): |
|
314 |
self.bug = bug |
|
315 |
self.question = question |
|
316 |
self.user = user |
|
317 |
||
318 |
||
|
16146.2.1
by Ian Booth
Introduce bulk update operations for bugs with duplicates affecting users and bug heat updates |
319 |
def update_bug_heat(bug_ids): |
320 |
"""Update the heat for the specified bugs."""
|
|
321 |
# We need to flush the store first to ensure that changes are
|
|
322 |
# reflected in the new bug heat total.
|
|
323 |
if not bug_ids: |
|
324 |
return
|
|
325 |
store = IStore(Bug) |
|
326 |
store.find( |
|
327 |
Bug, Bug.id.is_in(bug_ids)).set( |
|
328 |
heat=SQL('calculate_bug_heat(Bug.id)'), |
|
329 |
heat_last_updated=UTC_NOW) |
|
330 |
||
331 |
||
|
17610.1.1
by Colin Watson
Switch zope.interface users from class advice to class decorators. |
332 |
@implementer(IBug, IInformationType) |
|
7675.1347.4
by Aaron Bentley
Use InformationTypeMixin for Bug. |
333 |
class Bug(SQLBase, InformationTypeMixin): |
|
1102
by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs |
334 |
"""A bug."""
|
335 |
||
336 |
_defaultOrder = '-id' |
|
337 |
||
338 |
# db field names
|
|
|
1149
by Canonical.com Patch Queue Manager
malone debbugs integration |
339 |
name = StringCol(unique=True, default=None) |
340 |
title = StringCol(notNull=True) |
|
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
341 |
description = StringCol(notNull=False, default=None) |
|
5485.1.17
by Edwin Grubbs
Fixed indentation |
342 |
owner = ForeignKey( |
343 |
dbName='owner', foreignKey='Person', |
|
|
5821.2.40
by James Henstridge
* Move all the uses of public_person_validator over to the Storm |
344 |
storm_validator=validate_public_person, notNull=True) |
|
1670
by Canonical.com Patch Queue Manager
Big lot of database clean-up r=stub except for resolution of conflicts. |
345 |
duplicateof = ForeignKey( |
346 |
dbName='duplicateof', foreignKey='Bug', default=None) |
|
|
1650.1.2
by James Henstridge
commit the first part of the timezone awareness code |
347 |
datecreated = UtcDateTimeCol(notNull=True, default=UTC_NOW) |
|
3496.2.1
by Brad Bollenbach
checkpoint |
348 |
date_last_updated = UtcDateTimeCol(notNull=True, default=UTC_NOW) |
|
4240.1.1
by Gavin Panella
Add new columns to bug table to record who and when made the bug private, and corresponding SQLObject descriptors |
349 |
date_made_private = UtcDateTimeCol(notNull=False, default=None) |
350 |
who_made_private = ForeignKey( |
|
|
5485.1.13
by Edwin Grubbs
Sorta working |
351 |
dbName='who_made_private', foreignKey='Person', |
|
5821.2.40
by James Henstridge
* Move all the uses of public_person_validator over to the Storm |
352 |
storm_validator=validate_public_person, default=None) |
|
14937.2.1
by Steve Kowalik
Add information_type to Bug, and set it for new and changed bugs. |
353 |
information_type = EnumCol( |
|
14986.1.1
by Steve Kowalik
Change CreateBugParams to take information_type, and drop IBug._{private,security_related}. |
354 |
enum=InformationType, notNull=True, default=InformationType.PUBLIC) |
|
1478
by Canonical.com Patch Queue Manager
refactor bug subscription code to look more like bounty subscription code |
355 |
|
|
1102
by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs |
356 |
# useful Joins
|
|
3226.2.1
by Diogo Matsubara
Fix https://launchpad.net/products/launchpad/+bug/33625 (Change MultipleJoin to use the new SQLMultipleJoin.) |
357 |
activity = SQLMultipleJoin('BugActivity', joinColumn='bug', orderBy='id') |
|
3504.1.13
by kiko
Implement initial SQLRelatedJoin migration across Launchpad tree. Still needs to reconsider the Snapshot approach which will be a big performance hit. |
358 |
messages = SQLRelatedJoin('Message', joinColumn='bug', |
|
1102
by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs |
359 |
otherColumn='message', |
|
2225
by Canonical.com Patch Queue Manager
SMASH bug page fixes into rocketfuel [r=stevea] |
360 |
intermediateTable='BugMessage', |
|
3504.1.28
by kiko
Remove two XXXs in bug.py that referred to already-fixed SQLObject bugs, and prejoin owner for messages to avoid us hitting the database for each message. The latter should help with bug 42755: Optimization needed for bug comments queries -- though probably not fix it. |
361 |
prejoins=['owner'], |
|
3691.320.3
by Bjorn Tillenius
support more than one inline part, which will be added as comments. |
362 |
orderBy=['datecreated', 'id']) |
|
8137.17.24
by Barry Warsaw
thread merge |
363 |
bug_messages = SQLMultipleJoin( |
|
12346.2.2
by Robert Collins
Change all BugMessage object creation to set the index. This involved |
364 |
'BugMessage', joinColumn='bug', orderBy='index') |
|
3226.2.1
by Diogo Matsubara
Fix https://launchpad.net/products/launchpad/+bug/33625 (Change MultipleJoin to use the new SQLMultipleJoin.) |
365 |
watches = SQLMultipleJoin( |
|
3063.2.4
by Bjorn Tillenius
initial support for adding a bug watch together with an upstream task. |
366 |
'BugWatch', joinColumn='bug', orderBy=['bugtracker', 'remotebug']) |
|
16501.1.1
by Steve Kowalik
Teach IBug.specifications about visible specifications, which can be done with impunity because the API does not know about it. |
367 |
duplicates = SQLMultipleJoin('Bug', joinColumn='duplicateof', orderBy='id') |
|
17910.1.1
by William Grant
Rename Bug.linked_branches to linked_bugbranches. |
368 |
linked_bugbranches = SQLMultipleJoin( |
|
13277.4.18
by Graham Binns
Added accessor_for goodness. |
369 |
'BugBranch', joinColumn='bug', orderBy='id') |
|
4895.3.1
by Tom Berger
an optimization for the incomplete bug search - cache the last message date for each bug in the column `date_last_message`. |
370 |
date_last_message = UtcDateTimeCol(default=None) |
|
5238.2.1
by Tom Berger
allow sorting bugtask search results by the number of duplicates |
371 |
number_of_duplicates = IntCol(notNull=True, default=0) |
|
5453.4.8
by Tom Berger
merge changes from rocketfuel and number_of_comments --> message_count |
372 |
message_count = IntCol(notNull=True, default=0) |
|
7030.5.1
by Tom Berger
model for bug affects user |
373 |
users_affected_count = IntCol(notNull=True, default=0) |
|
7106.1.1
by Tom Berger
record both affected an unaffected users |
374 |
users_unaffected_count = IntCol(notNull=True, default=0) |
|
7675.465.4
by Karl Fogel
* lib/lp/bugs/model/bug.py (Bug.heat): Refer to correct column name 'heat' |
375 |
heat = IntCol(notNull=True, default=0) |
|
7675.582.4
by Graham Binns
Updated tests for heat_last_updated. |
376 |
heat_last_updated = UtcDateTimeCol(default=None) |
|
10224.18.1
by Abel Deuring
Property latest_patch_uploaded added to classes IBug and Bug; doc tests of this property |
377 |
latest_patch_uploaded = UtcDateTimeCol(default=None) |
|
3283.3.1
by Brad Bollenbach
create a new branch for bzr integration, to avoid 3 hour merge time |
378 |
|
|
17746.3.1
by William Grant
Basic port of QuestionBug/BugCve to XRef. |
379 |
@property
|
|
17910.1.1
by William Grant
Rename Bug.linked_branches to linked_bugbranches. |
380 |
def linked_branches(self): |
381 |
return [link.branch for link in self.linked_bugbranches] |
|
382 |
||
383 |
@property
|
|
|
17746.3.1
by William Grant
Basic port of QuestionBug/BugCve to XRef. |
384 |
def cves(self): |
385 |
from lp.bugs.model.cve import Cve |
|
|
17746.4.1
by William Grant
Remove non-XRef buglink model code. |
386 |
xref_cve_sequences = [ |
387 |
sequence for _, sequence in getUtility(IXRefSet).findFrom( |
|
388 |
(u'bug', unicode(self.id)), types=[u'cve'])] |
|
389 |
expr = Cve.sequence.is_in(xref_cve_sequences) |
|
|
17746.3.1
by William Grant
Basic port of QuestionBug/BugCve to XRef. |
390 |
return list(sorted( |
|
17746.3.7
by William Grant
Only read bug links from XRef when a feature flag is enabled. |
391 |
IStore(Cve).find(Cve, expr), key=operator.attrgetter('sequence'))) |
|
17746.3.1
by William Grant
Basic port of QuestionBug/BugCve to XRef. |
392 |
|
393 |
@property
|
|
394 |
def questions(self): |
|
395 |
from lp.answers.model.question import Question |
|
|
17746.4.1
by William Grant
Remove non-XRef buglink model code. |
396 |
question_ids = [ |
397 |
int(id) for _, id in getUtility(IXRefSet).findFrom( |
|
398 |
(u'bug', unicode(self.id)), types=[u'question'])] |
|
|
17746.3.1
by William Grant
Basic port of QuestionBug/BugCve to XRef. |
399 |
return list(sorted( |
|
17746.3.7
by William Grant
Only read bug links from XRef when a feature flag is enabled. |
400 |
bulk.load(Question, question_ids), key=operator.attrgetter('id'))) |
|
17746.3.1
by William Grant
Basic port of QuestionBug/BugCve to XRef. |
401 |
|
|
17746.3.2
by William Grant
SpecificationBugs create XRefs too. |
402 |
@property
|
403 |
def specifications(self): |
|
404 |
from lp.blueprints.model.specification import Specification |
|
|
17746.4.1
by William Grant
Remove non-XRef buglink model code. |
405 |
spec_ids = [ |
406 |
int(id) for _, id in getUtility(IXRefSet).findFrom( |
|
407 |
(u'bug', unicode(self.id)), types=[u'specification'])] |
|
|
17746.3.2
by William Grant
SpecificationBugs create XRefs too. |
408 |
return list(sorted( |
|
17746.3.7
by William Grant
Only read bug links from XRef when a feature flag is enabled. |
409 |
bulk.load(Specification, spec_ids), key=operator.attrgetter('id'))) |
|
17746.3.2
by William Grant
SpecificationBugs create XRefs too. |
410 |
|
|
16501.1.2
by Steve Kowalik
Rename IBug.specifications to IBug.getSpecifications(), actually teach the interface about it, add another test after refactoring. |
411 |
def getSpecifications(self, user): |
412 |
"""See `IBug`."""
|
|
|
17746.3.2
by William Grant
SpecificationBugs create XRefs too. |
413 |
from lp.blueprints.model.specification import Specification |
414 |
from lp.blueprints.model.specificationsearch import ( |
|
415 |
get_specification_privacy_filter, |
|
416 |
)
|
|
417 |
return IStore(Specification).find( |
|
|
16501.1.1
by Steve Kowalik
Teach IBug.specifications about visible specifications, which can be done with impunity because the API does not know about it. |
418 |
Specification, |
|
17746.3.2
by William Grant
SpecificationBugs create XRefs too. |
419 |
Specification.id.is_in(spec.id for spec in self.specifications), |
|
16501.1.3
by Steve Kowalik
Rewrite IBug.getSpecifications() to not use a Join. |
420 |
*get_specification_privacy_filter(user)) |
|
16501.1.1
by Steve Kowalik
Teach IBug.specifications about visible specifications, which can be done with impunity because the API does not know about it. |
421 |
|
|
15008.1.1
by Steve Kowalik
Resurrect the changes to use IBug.information_type, set information_type in |
422 |
@property
|
423 |
def security_related(self): |
|
|
14986.1.1
by Steve Kowalik
Change CreateBugParams to take information_type, and drop IBug._{private,security_related}. |
424 |
return self.information_type in SECURITY_INFORMATION_TYPES |
|
15008.1.1
by Steve Kowalik
Resurrect the changes to use IBug.information_type, set information_type in |
425 |
|
|
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
426 |
@cachedproperty
|
427 |
def _subscriber_cache(self): |
|
428 |
"""Caches known subscribers."""
|
|
429 |
return set() |
|
430 |
||
431 |
@cachedproperty
|
|
432 |
def _subscriber_dups_cache(self): |
|
433 |
"""Caches known subscribers to dupes."""
|
|
434 |
return set() |
|
435 |
||
436 |
@cachedproperty
|
|
437 |
def _unsubscribed_cache(self): |
|
438 |
"""Cache known non-subscribers."""
|
|
439 |
return set() |
|
440 |
||
|
3283.3.1
by Brad Bollenbach
create a new branch for bzr integration, to avoid 3 hour merge time |
441 |
@property
|
|
7675.517.1
by Abel Deuring
property Bug.latest_patch added; property used to display details abotu a patch on the +patches view. |
442 |
def latest_patch(self): |
443 |
"""See `IBug`."""
|
|
444 |
# We want to retrieve the most recently added bug attachment
|
|
445 |
# that is of type BugAttachmentType.PATCH. In order to find
|
|
446 |
# this attachment, we should in theory sort by
|
|
447 |
# BugAttachment.message.datecreated. Since we don't have
|
|
448 |
# an index for Message.datecreated, such a query would be
|
|
449 |
# quite slow. We search instead for the BugAttachment with
|
|
450 |
# the largest ID for a given bug. This is "nearly" equivalent
|
|
451 |
# to searching the record with the maximum value of
|
|
452 |
# message.datecreated: The only exception is the rare case when
|
|
453 |
# two BugAttachment records are simultaneuosly added to the same
|
|
454 |
# bug, where bug_attachment_1.id < bug_attachment_2.id, while
|
|
455 |
# the Message record for bug_attachment_2 is created before
|
|
456 |
# the Message record for bug_attachment_1. The difference of
|
|
|
7675.517.2
by Abel Deuring
improved tests of Bug.latest_patch; renamed one-character TAL variable; fixed a few typos |
457 |
# the datecreated values of the Message records is in this case
|
|
7675.517.1
by Abel Deuring
property Bug.latest_patch added; property used to display details abotu a patch on the +patches view. |
458 |
# probably smaller than one second and the selection of the
|
459 |
# "most recent" patch anyway somewhat arbitrary.
|
|
460 |
return Store.of(self).find( |
|
461 |
BugAttachment, BugAttachment.id == Select( |
|
462 |
Max(BugAttachment.id), |
|
463 |
And(BugAttachment.bug == self.id, |
|
464 |
BugAttachment.type == BugAttachmentType.PATCH))).one() |
|
465 |
||
466 |
@property
|
|
|
8279.3.12
by Graham Binns
Fixed comment counts so that initial comments aren't included. |
467 |
def comment_count(self): |
468 |
"""See `IBug`."""
|
|
469 |
return self.message_count - 1 |
|
470 |
||
471 |
@property
|
|
|
7881.5.1
by Tom Berger
make it possible to get the collection of users affected by a bug |
472 |
def users_affected(self): |
473 |
"""See `IBug`."""
|
|
|
10015.2.2
by Gavin Panella
Get the list of affected users with a single query. |
474 |
return Store.of(self).find( |
475 |
Person, BugAffectsPerson.person == Person.id, |
|
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
476 |
BugAffectsPerson.affected, BugAffectsPerson.bug == self) |
|
10193.3.1
by Karl Fogel
Fix bug #505845 ("Affected users should be carried through from |
477 |
|
478 |
@property
|
|
479 |
def users_unaffected(self): |
|
480 |
"""See `IBug`."""
|
|
481 |
return Store.of(self).find( |
|
482 |
Person, BugAffectsPerson.person == Person.id, |
|
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
483 |
Not(BugAffectsPerson.affected), BugAffectsPerson.bug == self) |
|
10193.3.1
by Karl Fogel
Fix bug #505845 ("Affected users should be carried through from |
484 |
|
|
10193.3.2
by Karl Fogel
Some tweaks resulting from informal review during Bugs Sprint. |
485 |
@property
|
486 |
def user_ids_affected_with_dupes(self): |
|
487 |
"""Return all IDs of Persons affected by this bug and its dupes.
|
|
|
13445.1.11
by Gary Poster
revert SQL change |
488 |
The return value is a Storm expression. Running a query with
|
489 |
this expression returns a result that may contain the same ID
|
|
490 |
multiple times, for example if that person is affected via
|
|
491 |
more than one duplicate."""
|
|
492 |
return Union( |
|
493 |
Select(Person.id, |
|
494 |
And(BugAffectsPerson.person == Person.id, |
|
495 |
BugAffectsPerson.affected, |
|
496 |
BugAffectsPerson.bug == self)), |
|
497 |
Select(Person.id, |
|
498 |
And(BugAffectsPerson.person == Person.id, |
|
499 |
BugAffectsPerson.bug == Bug.id, |
|
500 |
BugAffectsPerson.affected, |
|
501 |
Bug.duplicateof == self.id))) |
|
|
10193.3.1
by Karl Fogel
Fix bug #505845 ("Affected users should be carried through from |
502 |
|
503 |
@property
|
|
|
10193.3.2
by Karl Fogel
Some tweaks resulting from informal review during Bugs Sprint. |
504 |
def users_affected_with_dupes(self): |
|
10193.3.1
by Karl Fogel
Fix bug #505845 ("Affected users should be carried through from |
505 |
"""See `IBug`."""
|
506 |
return Store.of(self).find( |
|
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
507 |
Person, Person.id.is_in(self.user_ids_affected_with_dupes)) |
|
10193.3.1
by Karl Fogel
Fix bug #505845 ("Affected users should be carried through from |
508 |
|
509 |
@property
|
|
|
10193.3.2
by Karl Fogel
Some tweaks resulting from informal review during Bugs Sprint. |
510 |
def users_affected_count_with_dupes(self): |
|
10193.3.1
by Karl Fogel
Fix bug #505845 ("Affected users should be carried through from |
511 |
"""See `IBug`."""
|
|
10193.3.3
by Karl Fogel
With Abel, take care of an "XXX" item about scalability. |
512 |
return self.users_affected_with_dupes.count() |
|
7881.5.1
by Tom Berger
make it possible to get the collection of users affected by a bug |
513 |
|
514 |
@property
|
|
|
14189.6.9
by mbp at canonical
Add other_users_affected_count_with_dupes to get the right answers when the current user is affected by a dupe |
515 |
def other_users_affected_count_with_dupes(self): |
516 |
"""See `IBug`."""
|
|
517 |
current_user = getUtility(ILaunchBag).user |
|
518 |
if not current_user: |
|
519 |
return self.users_affected_count_with_dupes |
|
520 |
return self.users_affected_with_dupes.find( |
|
521 |
Person.id != current_user.id).count() |
|
522 |
||
523 |
@property
|
|
|
7029.4.1
by Tom Berger
provide an efficient implementation of the canonical url for messages by decorating messages with their index and context. |
524 |
def indexed_messages(self): |
525 |
"""See `IMessageTarget`."""
|
|
|
7675.1054.4
by Danilo Segan
Merge Gary's branch from stable. |
526 |
# Note that this is a decorated result set, so will cache its
|
527 |
# value (in the absence of slices)
|
|
|
11544.1.6
by Robert Collins
review feedback. |
528 |
return self._indexed_messages(include_content=True) |
|
11544.1.4
by Robert Collins
Remove listification from bugs/messages API call, so slicing actually can do less work. |
529 |
|
|
12756.1.1
by William Grant
Revert r12754. It causes parent_link to be empty in the API, apparently untested. |
530 |
def _indexed_messages(self, include_content=False, include_parents=True): |
|
11544.1.4
by Robert Collins
Remove listification from bugs/messages API call, so slicing actually can do less work. |
531 |
"""Get the bugs messages, indexed.
|
532 |
||
|
11544.1.6
by Robert Collins
review feedback. |
533 |
:param include_content: If True retrieve the content for the messages
|
534 |
too.
|
|
|
7675.166.301
by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint |
535 |
:param include_parents: If True retrieve the object for parent
|
536 |
messages too. If False the parent attribute will be *forced* to
|
|
537 |
None to reduce database lookups.
|
|
|
11544.1.4
by Robert Collins
Remove listification from bugs/messages API call, so slicing actually can do less work. |
538 |
"""
|
539 |
# Make all messages be 'in' the main bugtask.
|
|
|
7675.282.6
by Gavin Panella
Make tests pass. |
540 |
inside = self.default_bugtask |
|
11544.1.4
by Robert Collins
Remove listification from bugs/messages API call, so slicing actually can do less work. |
541 |
store = Store.of(self) |
542 |
message_by_id = {} |
|
|
12366.4.1
by Robert Collins
Remove the temporary bug message indexing code and simplify the message indexing logic as a result. |
543 |
to_messages = lambda rows: [row[0] for row in rows] |
|
7675.166.301
by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint |
544 |
|
|
11544.1.5
by Robert Collins
Skip parents for the attachments use of _indexed_messages - its not needed and saves some time. |
545 |
def eager_load_owners(messages): |
|
7675.166.301
by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint |
546 |
# Because we may have multiple owners, we spend less time
|
547 |
# in storm with very large bugs by not joining and instead
|
|
548 |
# querying a second time. If this starts to show high db
|
|
549 |
# time, we can left outer join instead.
|
|
|
11544.1.5
by Robert Collins
Skip parents for the attachments use of _indexed_messages - its not needed and saves some time. |
550 |
owner_ids = set(message.ownerID for message in messages) |
|
11544.1.4
by Robert Collins
Remove listification from bugs/messages API call, so slicing actually can do less work. |
551 |
owner_ids.discard(None) |
552 |
if not owner_ids: |
|
553 |
return
|
|
554 |
list(store.find(Person, Person.id.is_in(owner_ids))) |
|
|
7675.166.301
by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint |
555 |
|
|
11544.1.5
by Robert Collins
Skip parents for the attachments use of _indexed_messages - its not needed and saves some time. |
556 |
def eager_load_content(messages): |
|
11544.1.4
by Robert Collins
Remove listification from bugs/messages API call, so slicing actually can do less work. |
557 |
# To avoid the complexity of having multiple rows per
|
558 |
# message, or joining in the database (though perhaps in
|
|
559 |
# future we should do that), we do a single separate query
|
|
560 |
# for the message content.
|
|
|
11544.1.5
by Robert Collins
Skip parents for the attachments use of _indexed_messages - its not needed and saves some time. |
561 |
message_ids = set(message.id for message in messages) |
|
11544.1.6
by Robert Collins
review feedback. |
562 |
chunks = store.find( |
563 |
MessageChunk, MessageChunk.messageID.is_in(message_ids)) |
|
|
11544.1.4
by Robert Collins
Remove listification from bugs/messages API call, so slicing actually can do less work. |
564 |
chunks.order_by(MessageChunk.id) |
565 |
chunk_map = {} |
|
566 |
for chunk in chunks: |
|
567 |
message_chunks = chunk_map.setdefault(chunk.messageID, []) |
|
568 |
message_chunks.append(chunk) |
|
|
11544.1.5
by Robert Collins
Skip parents for the attachments use of _indexed_messages - its not needed and saves some time. |
569 |
for message in messages: |
|
11544.1.4
by Robert Collins
Remove listification from bugs/messages API call, so slicing actually can do less work. |
570 |
if message.id not in chunk_map: |
571 |
continue
|
|
|
11789.2.4
by Gavin Panella
Change all uses of IPropertyCache outside of propertycache.py to get_property_cache. |
572 |
cache = get_property_cache(message) |
|
11544.1.4
by Robert Collins
Remove listification from bugs/messages API call, so slicing actually can do less work. |
573 |
cache.text_contents = Message.chunks_text( |
574 |
chunk_map[message.id]) |
|
|
7675.166.301
by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint |
575 |
|
|
12366.4.1
by Robert Collins
Remove the temporary bug message indexing code and simplify the message indexing logic as a result. |
576 |
def eager_load(rows): |
|
11544.1.5
by Robert Collins
Skip parents for the attachments use of _indexed_messages - its not needed and saves some time. |
577 |
messages = to_messages(rows) |
578 |
eager_load_owners(messages) |
|
|
11544.1.6
by Robert Collins
review feedback. |
579 |
if include_content: |
|
11544.1.5
by Robert Collins
Skip parents for the attachments use of _indexed_messages - its not needed and saves some time. |
580 |
eager_load_content(messages) |
|
7675.166.301
by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint |
581 |
|
|
12366.4.1
by Robert Collins
Remove the temporary bug message indexing code and simplify the message indexing logic as a result. |
582 |
def index_message(row): |
|
11544.1.4
by Robert Collins
Remove listification from bugs/messages API call, so slicing actually can do less work. |
583 |
# convert row to an IndexedMessage
|
|
11544.1.6
by Robert Collins
review feedback. |
584 |
if include_parents: |
|
12366.4.1
by Robert Collins
Remove the temporary bug message indexing code and simplify the message indexing logic as a result. |
585 |
message, parent, bugmessage = row |
|
11544.1.5
by Robert Collins
Skip parents for the attachments use of _indexed_messages - its not needed and saves some time. |
586 |
if parent is not None: |
587 |
# If there is an IndexedMessage available as parent, use
|
|
588 |
# that to reduce on-demand parent lookups.
|
|
589 |
parent = message_by_id.get(parent.id, parent) |
|
590 |
else: |
|
|
12366.4.1
by Robert Collins
Remove the temporary bug message indexing code and simplify the message indexing logic as a result. |
591 |
message, bugmessage = row |
|
13023.7.12
by Danilo Segan
Lint fixes. |
592 |
parent = None # parent attribute is not going to be accessed. |
|
12366.4.1
by Robert Collins
Remove the temporary bug message indexing code and simplify the message indexing logic as a result. |
593 |
index = bugmessage.index |
|
11544.1.4
by Robert Collins
Remove listification from bugs/messages API call, so slicing actually can do less work. |
594 |
result = IndexedMessage(message, inside, index, parent) |
|
12366.4.1
by Robert Collins
Remove the temporary bug message indexing code and simplify the message indexing logic as a result. |
595 |
if include_parents: |
596 |
# This message may be the parent for another: stash it to
|
|
597 |
# permit use.
|
|
598 |
message_by_id[message.id] = result |
|
|
11544.1.4
by Robert Collins
Remove listification from bugs/messages API call, so slicing actually can do less work. |
599 |
return result |
|
11544.1.6
by Robert Collins
review feedback. |
600 |
if include_parents: |
|
14175.1.1
by William Grant
Use nested joins rather than subselects for preloading message parents. Fixes timeouts. Also removes nasty literal SQL strings. Because ew. |
601 |
ParentMessage = ClassAlias(Message) |
602 |
ParentBugMessage = ClassAlias(BugMessage) |
|
603 |
tables = [ |
|
604 |
Message, |
|
605 |
Join( |
|
606 |
BugMessage, |
|
607 |
BugMessage.messageID == Message.id), |
|
608 |
LeftJoin( |
|
609 |
Join( |
|
610 |
ParentMessage, |
|
611 |
ParentBugMessage, |
|
612 |
ParentMessage.id == ParentBugMessage.messageID), |
|
613 |
And( |
|
614 |
Message.parent == ParentMessage.id, |
|
615 |
ParentBugMessage.bugID == self.id)), |
|
616 |
]
|
|
617 |
results = store.using(*tables).find( |
|
618 |
(Message, ParentMessage, BugMessage), |
|
|
11544.1.5
by Robert Collins
Skip parents for the attachments use of _indexed_messages - its not needed and saves some time. |
619 |
BugMessage.bugID == self.id, |
620 |
)
|
|
621 |
else: |
|
|
12366.4.1
by Robert Collins
Remove the temporary bug message indexing code and simplify the message indexing logic as a result. |
622 |
lookup = Message, BugMessage |
|
12262.2.1
by Robert Collins
Add a garbo job to populate BugMessage.index, fixing bug 704446. |
623 |
results = store.find(lookup, |
|
11544.1.5
by Robert Collins
Skip parents for the attachments use of _indexed_messages - its not needed and saves some time. |
624 |
BugMessage.bugID == self.id, |
625 |
BugMessage.messageID == Message.id, |
|
626 |
)
|
|
|
12415.5.1
by Robert Collins
Use simpler sort in Bug._indexed_messages now that index is fully populated. Saves 90% on some queries. |
627 |
results.order_by(BugMessage.index) |
|
11544.1.4
by Robert Collins
Remove listification from bugs/messages API call, so slicing actually can do less work. |
628 |
return DecoratedResultSet(results, index_message, |
|
12366.4.1
by Robert Collins
Remove the temporary bug message indexing code and simplify the message indexing logic as a result. |
629 |
pre_iter_hook=eager_load) |
|
7029.4.1
by Tom Berger
provide an efficient implementation of the canonical url for messages by decorating messages with their index and context. |
630 |
|
631 |
@property
|
|
|
2450
by Canonical.com Patch Queue Manager
[r=jamesh] rework cve structure, and general polish |
632 |
def displayname(self): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
633 |
"""See `IBug`."""
|
|
2450
by Canonical.com Patch Queue Manager
[r=jamesh] rework cve structure, and general polish |
634 |
dn = 'Bug #%d' % self.id |
635 |
if self.name: |
|
|
13163.1.2
by Brad Crittenden
Fixed lint |
636 |
dn += ' (' + self.name + ')' |
|
2450
by Canonical.com Patch Queue Manager
[r=jamesh] rework cve structure, and general polish |
637 |
return dn |
|
553
by Canonical.com Patch Queue Manager
renaming phase 2 |
638 |
|
|
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
639 |
@cachedproperty
|
|
2252
by Canonical.com Patch Queue Manager
add cve report on distribution [r=stevea] |
640 |
def bugtasks(self): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
641 |
"""See `IBug`."""
|
|
12482.1.1
by Robert Collins
Eager load related fields for bugs when executing bug.bugtasks. |
642 |
# \o/ circular imports.
|
643 |
from lp.registry.model.distribution import Distribution |
|
644 |
from lp.registry.model.distroseries import DistroSeries |
|
645 |
from lp.registry.model.product import Product |
|
646 |
from lp.registry.model.productseries import ProductSeries |
|
647 |
from lp.registry.model.sourcepackagename import SourcePackageName |
|
648 |
store = Store.of(self) |
|
649 |
tasks = list(store.find(BugTask, BugTask.bugID == self.id)) |
|
|
7675.1054.4
by Danilo Segan
Merge Gary's branch from stable. |
650 |
# The bugtasks attribute is iterated in the API and web
|
651 |
# services, so it needs to preload all related data otherwise
|
|
652 |
# late evaluation is triggered in both places. Separately,
|
|
653 |
# bugtask_sort_key requires the related products, series,
|
|
654 |
# distros, distroseries and source package names to be loaded.
|
|
|
12482.1.6
by Robert Collins
IDS -> ids and drop unneeded ILaunchBag import. |
655 |
ids = set(map(operator.attrgetter('assigneeID'), tasks)) |
656 |
ids.update(map(operator.attrgetter('ownerID'), tasks)) |
|
657 |
ids.discard(None) |
|
658 |
if ids: |
|
|
12482.1.3
by Robert Collins
Eager load bugwatches and validity for assignees. |
659 |
list(getUtility(IPersonSet).getPrecachedPersonsFromIDs( |
|
12482.1.6
by Robert Collins
IDS -> ids and drop unneeded ILaunchBag import. |
660 |
ids, need_validity=True)) |
|
7675.1054.4
by Danilo Segan
Merge Gary's branch from stable. |
661 |
|
|
12482.1.1
by Robert Collins
Eager load related fields for bugs when executing bug.bugtasks. |
662 |
def load_something(attrname, klass): |
|
12482.1.6
by Robert Collins
IDS -> ids and drop unneeded ILaunchBag import. |
663 |
ids = set(map(operator.attrgetter(attrname), tasks)) |
664 |
ids.discard(None) |
|
665 |
if not ids: |
|
|
12482.1.1
by Robert Collins
Eager load related fields for bugs when executing bug.bugtasks. |
666 |
return
|
|
12482.1.6
by Robert Collins
IDS -> ids and drop unneeded ILaunchBag import. |
667 |
list(store.find(klass, klass.id.is_in(ids))) |
|
12482.1.1
by Robert Collins
Eager load related fields for bugs when executing bug.bugtasks. |
668 |
load_something('productID', Product) |
669 |
load_something('productseriesID', ProductSeries) |
|
670 |
load_something('distributionID', Distribution) |
|
671 |
load_something('distroseriesID', DistroSeries) |
|
672 |
load_something('sourcepackagenameID', SourcePackageName) |
|
|
12482.1.3
by Robert Collins
Eager load bugwatches and validity for assignees. |
673 |
list(store.find(BugWatch, BugWatch.bugID == self.id)) |
|
12482.1.1
by Robert Collins
Eager load related fields for bugs when executing bug.bugtasks. |
674 |
return sorted(tasks, key=bugtask_sort_key) |
|
1102
by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs |
675 |
|
|
2454
by Canonical.com Patch Queue Manager
[r=stevea]. make bug notifictions concerning the same bug be part of the same email thread. |
676 |
@property
|
|
6887.5.12
by Gavin Panella
Rename IBug.first_bugtask to default_bugtask. |
677 |
def default_bugtask(self): |
|
6887.5.11
by Gavin Panella
New IBug.first_bugtask attribute. |
678 |
"""See `IBug`."""
|
|
17241.1.8
by William Grant
Bug.default_bugtask avoids inactive products where possible, fixing the /bugs/1234 redirect for normal users. |
679 |
from lp.registry.model.product import Product |
680 |
return Store.of(self).using( |
|
681 |
BugTask, |
|
682 |
LeftJoin(Product, Product.id == BugTask.productID) |
|
683 |
).find( |
|
684 |
BugTask, bug=self |
|
685 |
).order_by( |
|
686 |
Desc(Coalesce(Product.active, True)), BugTask.id).first() |
|
|
6887.5.11
by Gavin Panella
New IBug.first_bugtask attribute. |
687 |
|
688 |
@property
|
|
|
3691.436.22
by Mark Shuttleworth
Clean up mentoring text and templates for 1.0 UI |
689 |
def is_complete(self): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
690 |
"""See `IBug`."""
|
|
3691.436.22
by Mark Shuttleworth
Clean up mentoring text and templates for 1.0 UI |
691 |
for task in self.bugtasks: |
|
3691.436.31
by Mark Shuttleworth
Fix implementation of bug completeness test |
692 |
if not task.is_complete: |
693 |
return False |
|
694 |
return True |
|
|
3691.436.22
by Mark Shuttleworth
Clean up mentoring text and templates for 1.0 UI |
695 |
|
|
3691.436.58
by Mark Shuttleworth
Test fixes |
696 |
@property
|
|
3847.2.30
by Mark Shuttleworth
Eliminate components/bugtask.py and polish bug listing portlets |
697 |
def affected_pillars(self): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
698 |
"""See `IBug`."""
|
|
3847.2.30
by Mark Shuttleworth
Eliminate components/bugtask.py and polish bug listing portlets |
699 |
result = set() |
700 |
for task in self.bugtasks: |
|
701 |
result.add(task.pillar) |
|
702 |
return sorted(result, key=pillar_sort_key) |
|
|
3847.2.1
by Mark Shuttleworth
Neaten up bug listing portlets |
703 |
|
704 |
@property
|
|
|
5020.3.9
by Curtis Hovey
Revisions per review. |
705 |
def permits_expiration(self): |
706 |
"""See `IBug`.
|
|
707 |
||
708 |
This property checks the general state of the bug to determine if
|
|
709 |
expiration is permitted *if* a bugtask were to qualify for expiration.
|
|
710 |
This property does not check the bugtask preconditions to identify
|
|
711 |
a specific bugtask that can expire.
|
|
712 |
||
713 |
:See: `IBug.can_expire` or `BugTaskSet.findExpirableBugTasks` to
|
|
714 |
check or get a list of bugs that can expire.
|
|
715 |
"""
|
|
716 |
# Bugs cannot be expired if any bugtask is valid.
|
|
717 |
expirable_status_list = [ |
|
718 |
BugTaskStatus.INCOMPLETE, BugTaskStatus.INVALID, |
|
719 |
BugTaskStatus.WONTFIX] |
|
|
5020.3.10
by Curtis Hovey
Changes per review. |
720 |
has_an_expirable_bugtask = False |
721 |
for bugtask in self.bugtasks: |
|
722 |
if bugtask.status not in expirable_status_list: |
|
723 |
# We found an unexpirable bugtask; the bug cannot expire.
|
|
724 |
return False |
|
725 |
if (bugtask.status == BugTaskStatus.INCOMPLETE |
|
|
5283.1.2
by Curtis Hovey
Revised the can_expire code parts to honor enable_bug_expiration. Added |
726 |
and bugtask.pillar.enable_bug_expiration): |
|
5020.3.10
by Curtis Hovey
Changes per review. |
727 |
# This bugtasks meets the basic conditions to expire.
|
728 |
has_an_expirable_bugtask = True |
|
729 |
||
730 |
return has_an_expirable_bugtask |
|
|
5020.3.9
by Curtis Hovey
Revisions per review. |
731 |
|
732 |
@property
|
|
|
5020.3.1
by Curtis Hovey
Basic can_expire property is added. bugtask-expiration needs revision to show it off. |
733 |
def can_expire(self): |
734 |
"""See `IBug`.
|
|
735 |
||
736 |
Only Incomplete bug reports that affect a single pillar with
|
|
|
5020.3.9
by Curtis Hovey
Revisions per review. |
737 |
enabled_bug_expiration set to True can be expired. To qualify for
|
738 |
expiration, the bug and its bugtasks meet the follow conditions:
|
|
739 |
||
|
11057.8.2
by Brian Murray
modify can_expire to use the days_before_expiration config option |
740 |
1. The bug is inactive; the last update of the bug is older than
|
|
5020.3.9
by Curtis Hovey
Revisions per review. |
741 |
Launchpad expiration age.
|
742 |
2. The bug is not a duplicate.
|
|
743 |
3. The bug has at least one message (a request for more information).
|
|
744 |
4. The bug does not have any other valid bugtasks.
|
|
|
5283.1.2
by Curtis Hovey
Revised the can_expire code parts to honor enable_bug_expiration. Added |
745 |
5. The bugtask belongs to a project with enable_bug_expiration set
|
746 |
to True.
|
|
|
5020.3.9
by Curtis Hovey
Revisions per review. |
747 |
6. The bugtask has the status Incomplete.
|
748 |
7. The bugtask is not assigned to anyone.
|
|
749 |
8. The bugtask does not have a milestone.
|
|
|
5020.3.1
by Curtis Hovey
Basic can_expire property is added. bugtask-expiration needs revision to show it off. |
750 |
"""
|
|
5020.3.6
by Curtis Hovey
Added pagetest and UI for expiration notices. We *really* need to replace the |
751 |
# IBugTaskSet.findExpirableBugTasks() is the authoritative determiner
|
|
5020.3.9
by Curtis Hovey
Revisions per review. |
752 |
# if a bug can expire, but it is expensive. We do a general check
|
753 |
# to verify the bug permits expiration before using IBugTaskSet to
|
|
754 |
# determine if a bugtask can cause expiration.
|
|
755 |
if not self.permits_expiration: |
|
|
5020.3.1
by Curtis Hovey
Basic can_expire property is added. bugtask-expiration needs revision to show it off. |
756 |
return False |
|
5020.3.9
by Curtis Hovey
Revisions per review. |
757 |
|
|
11057.8.2
by Brian Murray
modify can_expire to use the days_before_expiration config option |
758 |
days_old = config.malone.days_before_expiration |
|
5565.6.5
by Bjorn Tillenius
clarify comment. |
759 |
# Do the search as the Janitor, to ensure that this bug can be
|
760 |
# found, even if it's private. We don't have access to the user
|
|
761 |
# calling this property. If the user has access to view this
|
|
|
17903.1.1
by Colin Watson
Use gender-neutral pronouns where appropriate. |
762 |
# property, they have permission to see the bug, so we're not
|
|
5565.6.5
by Bjorn Tillenius
clarify comment. |
763 |
# exposing something we shouldn't. The Janitor has access to
|
764 |
# view all bugs.
|
|
|
5565.6.3
by Bjorn Tillenius
make the user parameter to findExpirableBugtasks() required. make all callsites specify it. |
765 |
bugtasks = getUtility(IBugTaskSet).findExpirableBugTasks( |
|
11057.8.2
by Brian Murray
modify can_expire to use the days_before_expiration config option |
766 |
days_old, getUtility(ILaunchpadCelebrities).janitor, bug=self) |
|
16597.1.1
by Steve Kowalik
Dump .count() > 0 for the new black of .is_empty(). |
767 |
return not bugtasks.is_empty() |
|
5020.3.1
by Curtis Hovey
Basic can_expire property is added. bugtask-expiration needs revision to show it off. |
768 |
|
|
11057.8.1
by Brian Murray
create IBug.isExpirable() which is hopefully clearer than IBug.can_expire and export it via the API |
769 |
def isExpirable(self, days_old=None): |
770 |
"""See `IBug`."""
|
|
771 |
||
772 |
# If days_old is None read it from the Launchpad configuration
|
|
773 |
# and use that value
|
|
774 |
if days_old is None: |
|
775 |
days_old = config.malone.days_before_expiration |
|
776 |
||
777 |
# IBugTaskSet.findExpirableBugTasks() is the authoritative determiner
|
|
778 |
# if a bug can expire, but it is expensive. We do a general check
|
|
779 |
# to verify the bug permits expiration before using IBugTaskSet to
|
|
780 |
# determine if a bugtask can cause expiration.
|
|
781 |
if not self.permits_expiration: |
|
782 |
return False |
|
783 |
||
784 |
# Do the search as the Janitor, to ensure that this bug can be
|
|
785 |
# found, even if it's private. We don't have access to the user
|
|
786 |
# calling this property. If the user has access to view this
|
|
|
17903.1.1
by Colin Watson
Use gender-neutral pronouns where appropriate. |
787 |
# property, they have permission to see the bug, so we're not
|
|
11057.8.1
by Brian Murray
create IBug.isExpirable() which is hopefully clearer than IBug.can_expire and export it via the API |
788 |
# exposing something we shouldn't. The Janitor has access to
|
789 |
# view all bugs.
|
|
790 |
bugtasks = getUtility(IBugTaskSet).findExpirableBugTasks( |
|
791 |
days_old, getUtility(ILaunchpadCelebrities).janitor, bug=self) |
|
|
16597.1.1
by Steve Kowalik
Dump .count() > 0 for the new black of .is_empty(). |
792 |
return not bugtasks.is_empty() |
|
11057.8.1
by Brian Murray
create IBug.isExpirable() which is hopefully clearer than IBug.can_expire and export it via the API |
793 |
|
|
12655.6.2
by Gary Poster
readd the bug and person optimizations, trying to follow the advice Robert gave; cache the initial_message because we were getting it from the SQL twice. |
794 |
@cachedproperty
|
|
2454
by Canonical.com Patch Queue Manager
[r=stevea]. make bug notifictions concerning the same bug be part of the same email thread. |
795 |
def initial_message(self): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
796 |
"""See `IBug`."""
|
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
797 |
return Store.of(self).find( |
798 |
Message, BugMessage.bug == self, |
|
799 |
BugMessage.message == Message.id).order_by('id').first() |
|
|
2454
by Canonical.com Patch Queue Manager
[r=stevea]. make bug notifictions concerning the same bug be part of the same email thread. |
800 |
|
|
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
801 |
@cachedproperty
|
802 |
def official_tags(self): |
|
803 |
"""See `IBug`."""
|
|
804 |
# Da circle of imports forces the locals.
|
|
805 |
from lp.registry.model.distribution import Distribution |
|
806 |
from lp.registry.model.product import Product |
|
807 |
table = OfficialBugTag |
|
808 |
table = LeftJoin( |
|
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
809 |
table, Distribution, |
|
13163.1.2
by Brad Crittenden
Fixed lint |
810 |
OfficialBugTag.distribution_id == Distribution.id) |
|
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
811 |
table = LeftJoin( |
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
812 |
table, Product, OfficialBugTag.product_id == Product.id) |
|
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
813 |
# When this method is typically called it already has the necessary
|
814 |
# info in memory, so rather than rejoin with Product etc, we do this
|
|
815 |
# bit in Python. If reviewing performance here feel free to change.
|
|
816 |
clauses = [] |
|
817 |
for task in self.bugtasks: |
|
|
11582.2.5
by Robert Collins
Fix up test fallout. |
818 |
clauses.append( |
819 |
# Storm cannot compile proxied objects.
|
|
820 |
removeSecurityProxy(task.target._getOfficialTagClause())) |
|
|
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
821 |
clause = Or(*clauses) |
822 |
return list(Store.of(self).using(table).find(OfficialBugTag.tag, |
|
823 |
clause).order_by(OfficialBugTag.tag).config(distinct=True)) |
|
824 |
||
|
2070
by Canonical.com Patch Queue Manager
[r=salgado] FormattingBugNotifications implementation. requires some |
825 |
def followup_subject(self): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
826 |
"""See `IBug`."""
|
|
13163.1.2
by Brad Crittenden
Fixed lint |
827 |
return 'Re: ' + self.title |
|
1228
by Canonical.com Patch Queue Manager
small bugfixes and a first go at a db schema patch for bug group |
828 |
|
|
10189.4.1
by Tom Berger
interim commit, so that i can merge in another branch |
829 |
@property
|
830 |
def has_patches(self): |
|
831 |
"""See `IBug`."""
|
|
|
10304.6.2
by Tom Berger
Use the new Bug.latest_patch_uploaded column to optimize searching for bugs with patches. |
832 |
return self.latest_patch_uploaded is not None |
|
10189.4.1
by Tom Berger
interim commit, so that i can merge in another branch |
833 |
|
|
11688.1.3
by Graham Binns
It's now possible to subscribe at a given BugNotificationLevel. |
834 |
def subscribe(self, person, subscribed_by, suppress_notify=True, |
|
11688.1.9
by Graham Binns
Minor tweak. |
835 |
level=None): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
836 |
"""See `IBug`."""
|
|
14449.6.1
by Curtis Hovey
Remove isTeam(). Replace calls with .is_team. |
837 |
if person.is_team and self.private and person.anyone_can_join(): |
|
14188.2.10
by j.c.sackett
Added method to check if team is open to person class. |
838 |
error_msg = ("Open and delegated teams cannot be subscribed " |
839 |
"to private bugs.") |
|
840 |
raise SubscriptionPrivacyViolation(error_msg) |
|
|
2396
by Canonical.com Patch Queue Manager
[r=spiv] launchpad support tracker |
841 |
# first look for an existing subscription
|
842 |
for sub in self.subscriptions: |
|
843 |
if sub.person.id == person.id: |
|
|
12556.11.1
by Gary Poster
initial cut of direct actions |
844 |
if level is not None: |
845 |
sub.bug_notification_level = level |
|
846 |
# Should subscribed_by be changed in this case? Until
|
|
847 |
# proven otherwise, we will answer with "no."
|
|
|
2396
by Canonical.com Patch Queue Manager
[r=spiv] launchpad support tracker |
848 |
return sub |
|
1478
by Canonical.com Patch Queue Manager
refactor bug subscription code to look more like bounty subscription code |
849 |
|
|
12556.11.1
by Gary Poster
initial cut of direct actions |
850 |
if level is None: |
851 |
level = BugNotificationLevel.COMMENTS |
|
852 |
||
|
6105.11.1
by Tom Berger
notify users when they are being subscribed to a bug |
853 |
sub = BugSubscription( |
|
11688.1.3
by Graham Binns
It's now possible to subscribe at a given BugNotificationLevel. |
854 |
bug=self, person=person, subscribed_by=subscribed_by, |
855 |
bug_notification_level=level) |
|
|
10606.7.4
by Deryck Hodge
Make sending notifications configurable via a parameter. |
856 |
|
|
5821.2.47
by James Henstridge
make sure bug subscribe/unsubscribe gets flushed to the DB |
857 |
# Ensure that the subscription has been flushed.
|
|
5821.11.13
by James Henstridge
Do an explicit flush in Bug.subscribe() to fix doc/security-teams.txt. |
858 |
Store.of(sub).flush() |
|
10795.5.1
by Deryck Hodge
Merging in work from production-devel branch to prevent |
859 |
|
|
15424.1.2
by Ian Booth
New findPeopleWithoutAccess service methods, ensure all subscribers are granted access to a bug when it becomes private |
860 |
# Grant the subscriber access if they can't see the bug but only if
|
861 |
# there is at least one bugtask for which access can be checked.
|
|
862 |
if self.default_bugtask: |
|
863 |
service = getUtility(IService, 'sharing') |
|
|
17332.6.19
by Colin Watson
Add sharing service support for Git repositories. |
864 |
bugs, _, _, _ = service.getVisibleArtifacts( |
|
15907.2.2
by Ian Booth
Fix permissions on exported methods |
865 |
person, bugs=[self], ignore_permissions=True) |
|
15424.1.2
by Ian Booth
New findPeopleWithoutAccess service methods, ensure all subscribers are granted access to a bug when it becomes private |
866 |
if not bugs: |
867 |
service.ensureAccessGrants( |
|
868 |
[person], subscribed_by, bugs=[self], |
|
869 |
ignore_permissions=True) |
|
|
15162.1.24
by Ian Booth
Add model code to grant access to bugs when adding a subscriber and add required sharing service methods |
870 |
|
|
10898.4.14
by Deryck Hodge
Add a comment. |
871 |
# In some cases, a subscription should be created without
|
872 |
# email notifications. suppress_notify determines if
|
|
873 |
# notifications are sent.
|
|
|
10795.5.1
by Deryck Hodge
Merging in work from production-devel branch to prevent |
874 |
if suppress_notify is False: |
875 |
notify(ObjectCreatedEvent(sub, user=subscribed_by)) |
|
876 |
||
|
16146.2.1
by Ian Booth
Introduce bulk update operations for bugs with duplicates affecting users and bug heat updates |
877 |
update_bug_heat([self.id]) |
|
6105.11.1
by Tom Berger
notify users when they are being subscribed to a bug |
878 |
return sub |
|
1478
by Canonical.com Patch Queue Manager
refactor bug subscription code to look more like bounty subscription code |
879 |
|
|
13994.2.1
by Ian Booth
Implement new subscription behaviour |
880 |
def unsubscribe(self, person, unsubscribed_by, **kwargs): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
881 |
"""See `IBug`."""
|
|
12607.6.8
by Ian Booth
Test fix |
882 |
# Drop cached subscription info.
|
883 |
clear_property_cache(self) |
|
|
12607.6.2
by Ian Booth
Rework implementation |
884 |
# Ensure the unsubscriber is in the _known_viewer cache for the bug so
|
885 |
# that the permissions are such that the operation can succeed.
|
|
886 |
get_property_cache(self)._known_viewers = set([unsubscribed_by.id]) |
|
|
8426.5.1
by Deryck Hodge
Update the API to allow IBug.unsubscribe to take a person argument. |
887 |
if person is None: |
|
8615.4.1
by Deryck Hodge
Remove use of ILaunchBag from Bug.unsubscribe. |
888 |
person = unsubscribed_by |
|
8426.5.1
by Deryck Hodge
Update the API to allow IBug.unsubscribe to take a person argument. |
889 |
|
|
13994.2.1
by Ian Booth
Implement new subscription behaviour |
890 |
ignore_permissions = kwargs.get('ignore_permissions', False) |
|
13994.2.8
by Ian Booth
Send emails when bug supervisor or security contact unsubscribed |
891 |
recipients = kwargs.get('recipients') |
|
2396
by Canonical.com Patch Queue Manager
[r=spiv] launchpad support tracker |
892 |
for sub in self.subscriptions: |
893 |
if sub.person.id == person.id: |
|
|
13994.2.1
by Ian Booth
Implement new subscription behaviour |
894 |
if (not ignore_permissions |
895 |
and not sub.canBeUnsubscribedByUser(unsubscribed_by)): |
|
|
8384.1.1
by Deryck Hodge
Add a check against canBeUnsubscribedByUser in Bug.unsubscribe. |
896 |
raise UserCannotUnsubscribePerson( |
897 |
'%s does not have permission to unsubscribe %s.' % ( |
|
898 |
unsubscribed_by.displayname, |
|
899 |
person.displayname)) |
|
|
13994.2.2
by Ian Booth
Extract out functionality for bug 672596 into a new branch |
900 |
|
901 |
self.addChange(UnsubscribedFromBug( |
|
|
13994.2.8
by Ian Booth
Send emails when bug supervisor or security contact unsubscribed |
902 |
when=UTC_NOW, person=unsubscribed_by, |
|
13994.2.9
by Ian Booth
Tweak kwargs |
903 |
unsubscribed_user=person, **kwargs), |
|
13994.2.8
by Ian Booth
Send emails when bug supervisor or security contact unsubscribed |
904 |
recipients=recipients) |
|
8384.1.5
by Deryck Hodge
Raise an error earlier to make the code easier to read. |
905 |
store = Store.of(sub) |
906 |
store.remove(sub) |
|
907 |
# Make sure that the subscription removal has been
|
|
908 |
# flushed so that code running with implicit flushes
|
|
909 |
# disabled see the change.
|
|
910 |
store.flush() |
|
|
16146.2.1
by Ian Booth
Introduce bulk update operations for bugs with duplicates affecting users and bug heat updates |
911 |
update_bug_heat([self.id]) |
|
11789.2.4
by Gavin Panella
Change all uses of IPropertyCache outside of propertycache.py to get_property_cache. |
912 |
del get_property_cache(self)._known_viewers |
|
15162.1.20
by Ian Booth
Add feature flag and functionality to mirror legacy bug access |
913 |
|
|
15587.4.3
by William Grant
Drop the disclosure.legacy_subscription_visibility.enabled feature flag -- unsubscribe now revokes unconditionally. |
914 |
# Revoke access to bug
|
915 |
artifacts_to_delete = getUtility( |
|
916 |
IAccessArtifactSource).find([self]) |
|
917 |
getUtility(IAccessArtifactGrantSource).revokeByArtifact( |
|
918 |
artifacts_to_delete, [person]) |
|
|
8384.1.5
by Deryck Hodge
Raise an error earlier to make the code easier to read. |
919 |
return
|
920 |
||
|
8656.1.1
by Deryck Hodge
Make unsubscribeFromDupes behave like unsubscribe to all |
921 |
def unsubscribeFromDupes(self, person, unsubscribed_by): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
922 |
"""See `IBug`."""
|
|
8656.1.1
by Deryck Hodge
Make unsubscribeFromDupes behave like unsubscribe to all |
923 |
if person is None: |
924 |
person = unsubscribed_by |
|
925 |
||
|
3691.163.4
by Brad Bollenbach
checkpoint |
926 |
bugs_unsubscribed = [] |
927 |
for dupe in self.duplicates: |
|
928 |
if dupe.isSubscribed(person): |
|
|
8656.1.1
by Deryck Hodge
Make unsubscribeFromDupes behave like unsubscribe to all |
929 |
dupe.unsubscribe(person, unsubscribed_by) |
|
3691.163.4
by Brad Bollenbach
checkpoint |
930 |
bugs_unsubscribed.append(dupe) |
931 |
||
932 |
return bugs_unsubscribed |
|
933 |
||
|
1478
by Canonical.com Patch Queue Manager
refactor bug subscription code to look more like bounty subscription code |
934 |
def isSubscribed(self, person): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
935 |
"""See `IBug`."""
|
|
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
936 |
return self.personIsDirectSubscriber(person) |
|
1478
by Canonical.com Patch Queue Manager
refactor bug subscription code to look more like bounty subscription code |
937 |
|
|
3691.163.2
by Brad Bollenbach
checkpoint |
938 |
def isSubscribedToDupes(self, person): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
939 |
"""See `IBug`."""
|
|
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
940 |
return self.personIsSubscribedToDuplicate(person) |
|
8620.4.8
by Deryck Hodge
Don't depend on the user being logged in, |
941 |
|
|
7675.1138.5
by Danilo Segan
Move basic mute functionality to BugMute table as tested by TestBugSubscriptionMethods. |
942 |
def _getMutes(self, person): |
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
943 |
return Store.of(self).find( |
944 |
BugMute, BugMute.bug == self, BugMute.person == person) |
|
|
7675.1138.5
by Danilo Segan
Move basic mute functionality to BugMute table as tested by TestBugSubscriptionMethods. |
945 |
|
|
12526.2.1
by Graham Binns
Added an isMuted() method to IBug. |
946 |
def isMuted(self, person): |
947 |
"""See `IBug`."""
|
|
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
948 |
return not self._getMutes(person).is_empty() |
|
12526.2.1
by Graham Binns
Added an isMuted() method to IBug. |
949 |
|
|
12599.1.1
by Graham Binns
Added an IBug.mute() method. |
950 |
def mute(self, person, muted_by): |
951 |
"""See `IBug`."""
|
|
|
12783.2.2
by Gary Poster
try to remove all message queries |
952 |
if person is None: |
953 |
# This may be a webservice request.
|
|
954 |
person = muted_by |
|
|
7675.1138.13
by Danilo Segan
Remove XXXes and add an assertion stopping team mutes as suggested by Graham and Stuart. |
955 |
assert not person.is_team, ( |
956 |
"Muting a subscription for entire team is not allowed.") |
|
957 |
||
|
7675.1138.5
by Danilo Segan
Move basic mute functionality to BugMute table as tested by TestBugSubscriptionMethods. |
958 |
# If it's already muted, ignore the request.
|
959 |
mutes = self._getMutes(person) |
|
960 |
if mutes.is_empty(): |
|
|
7675.1138.14
by Danilo Segan
Fix test failures. |
961 |
mute = BugMute(person, self) |
962 |
Store.of(mute).flush() |
|
|
12599.1.1
by Graham Binns
Added an IBug.mute() method. |
963 |
|
|
12599.1.2
by Graham Binns
Added a stubby unmute method. |
964 |
def unmute(self, person, unmuted_by): |
965 |
"""See `IBug`."""
|
|
|
7675.1138.5
by Danilo Segan
Move basic mute functionality to BugMute table as tested by TestBugSubscriptionMethods. |
966 |
if person is None: |
967 |
# This may be a webservice request.
|
|
968 |
person = unmuted_by |
|
969 |
mutes = self._getMutes(person) |
|
|
15809.2.1
by Steve Kowalik
IBug.unmute can now deal with being called when there is no mute. |
970 |
if not mutes.is_empty(): |
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
971 |
Store.of(self).remove(mutes.one()) |
|
13023.7.2
by Danilo Segan
Split Gary's server-side changes. |
972 |
return self.getSubscriptionForPerson(person) |
|
12599.1.2
by Graham Binns
Added a stubby unmute method. |
973 |
|
|
11536.1.4
by Gavin Panella
Ensure that the output of getSubscriptionsFromDuplicates is stable, and change the subscriptions ReferenceSet into a property because the web API can't adapt what ReferenceSet returns. |
974 |
@property
|
975 |
def subscriptions(self): |
|
|
11536.1.5
by Gavin Panella
Use a DecoratedResultSet instead of a list comprehension. |
976 |
"""The set of `BugSubscriptions` for this bug."""
|
|
11536.1.4
by Gavin Panella
Ensure that the output of getSubscriptionsFromDuplicates is stable, and change the subscriptions ReferenceSet into a property because the web API can't adapt what ReferenceSet returns. |
977 |
# XXX: kiko 2006-09-23: Why is subscriptions ordered by ID?
|
978 |
results = Store.of(self).find( |
|
979 |
(Person, BugSubscription), |
|
980 |
BugSubscription.person_id == Person.id, |
|
981 |
BugSubscription.bug_id == self.id).order_by(BugSubscription.id) |
|
|
11536.1.5
by Gavin Panella
Use a DecoratedResultSet instead of a list comprehension. |
982 |
return DecoratedResultSet(results, operator.itemgetter(1)) |
|
11536.1.4
by Gavin Panella
Ensure that the output of getSubscriptionsFromDuplicates is stable, and change the subscriptions ReferenceSet into a property because the web API can't adapt what ReferenceSet returns. |
983 |
|
|
14494.6.9
by Gavin Panella
Some clean-ups. |
984 |
def getSubscriptionInfo(self, level=None): |
|
11869.18.1
by Gavin Panella
New method IBug.getSubscriptionInfo(), and security definitions around BugSubscriptionInfo objects. |
985 |
"""See `IBug`."""
|
|
14494.6.33
by Gavin Panella
Remove confusing conditional expression. |
986 |
if level is None: |
987 |
level = BugNotificationLevel.LIFECYCLE |
|
988 |
return BugSubscriptionInfo(self, level) |
|
|
11869.18.1
by Gavin Panella
New method IBug.getSubscriptionInfo(), and security definitions around BugSubscriptionInfo objects. |
989 |
|
|
5454.1.5
by Tom Berger
record who created each bug subscription, and display the result in the title of the subscriber link |
990 |
def getDirectSubscriptions(self): |
991 |
"""See `IBug`."""
|
|
|
11869.18.3
by Gavin Panella
Use getSubscriptionInfo() in getDirectSub*. |
992 |
return self.getSubscriptionInfo().direct_subscriptions |
|
5454.1.5
by Tom Berger
record who created each bug subscription, and display the result in the title of the subscriber link |
993 |
|
|
15745.5.1
by William Grant
Resurrect private structsubs changes so they can be made performant. |
994 |
def getDirectSubscribers(self, recipients=None, level=None, |
995 |
filter_visible=False): |
|
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
996 |
"""See `IBug`.
|
|
3945.2.30
by kiko
Move recipients out of the interface description and into the docstrings of the database class' methods. |
997 |
|
998 |
The recipients argument is private and not exposed in the
|
|
|
6493.3.1
by Guilherme Salgado
Rename IPerson.timezone to IPerson.time_zone |
999 |
interface. If a BugNotificationRecipients instance is supplied,
|
|
3945.2.30
by kiko
Move recipients out of the interface description and into the docstrings of the database class' methods. |
1000 |
the relevant subscribers and rationales will be registered on
|
1001 |
it.
|
|
1002 |
"""
|
|
|
11688.1.4
by Graham Binns
getDirectSubscribers() now returns subscribers for a given BugNotificationLevel. |
1003 |
if level is None: |
|
7675.1139.1
by Danilo Segan
Get rid of all NOTHING usage. |
1004 |
level = BugNotificationLevel.LIFECYCLE |
|
13627.2.10
by Brad Crittenden
Fixed lint |
1005 |
direct_subscribers = ( |
1006 |
self.getSubscriptionInfo(level).direct_subscribers) |
|
|
15745.5.1
by William Grant
Resurrect private structsubs changes so they can be made performant. |
1007 |
if filter_visible: |
1008 |
filtered_subscribers = IStore(Person).find(Person, |
|
1009 |
Person.id.is_in([s.id for s in direct_subscribers]), |
|
1010 |
self.getSubscriptionInfo().visible_recipients_filter( |
|
1011 |
Person.id)) |
|
1012 |
direct_subscribers = BugSubscriberSet( |
|
1013 |
direct_subscribers.intersection(filtered_subscribers)) |
|
|
4231.1.16
by Francis J. Lacoste
Compare explicitely to None, since a NotificationRecipientSet evaluates to False when empty. |
1014 |
if recipients is not None: |
|
13627.2.7
by Brad Crittenden
Removed obsolete code, added TestGetDeferredNotifications, mark deferred notifications explicitly with a flag. |
1015 |
for subscriber in direct_subscribers: |
|
3945.2.27
by kiko
Replace rationale for recipients everywhere |
1016 |
recipients.addDirectSubscriber(subscriber) |
|
13627.2.7
by Brad Crittenden
Removed obsolete code, added TestGetDeferredNotifications, mark deferred notifications explicitly with a flag. |
1017 |
return direct_subscribers.sorted |
|
3485.6.7
by Brad Bollenbach
Fix bug 29752 (If a bug is marked as a duplicate, its subscribers should be notified when the duplicate bug changes) |
1018 |
|
|
13247.1.1
by Danilo Segan
Reapply bug 772754 fix with packagecopyjob changes removed. |
1019 |
def getDirectSubscribersWithDetails(self): |
1020 |
"""See `IBug`."""
|
|
|
13469.2.5
by Brad Crittenden
Precache the 'subscribed_by' person for performance. Add tests showing expected query count of 1. |
1021 |
SubscribedBy = ClassAlias(Person, name="subscribed_by") |
|
13247.1.1
by Danilo Segan
Reapply bug 772754 fix with packagecopyjob changes removed. |
1022 |
results = Store.of(self).find( |
|
13469.2.5
by Brad Crittenden
Precache the 'subscribed_by' person for performance. Add tests showing expected query count of 1. |
1023 |
(Person, SubscribedBy, BugSubscription), |
|
13247.1.1
by Danilo Segan
Reapply bug 772754 fix with packagecopyjob changes removed. |
1024 |
BugSubscription.person_id == Person.id, |
1025 |
BugSubscription.bug_id == self.id, |
|
|
13469.2.5
by Brad Crittenden
Precache the 'subscribed_by' person for performance. Add tests showing expected query count of 1. |
1026 |
BugSubscription.subscribed_by_id == SubscribedBy.id, |
|
13247.1.1
by Danilo Segan
Reapply bug 772754 fix with packagecopyjob changes removed. |
1027 |
Not(In(BugSubscription.person_id, |
1028 |
Select(BugMute.person_id, BugMute.bug_id == self.id))) |
|
|
17773.1.1
by Colin Watson
Rename Person.displayname to Person.display_name, leaving a property behind for compatibility. |
1029 |
).order_by(Person.display_name) |
|
13247.1.1
by Danilo Segan
Reapply bug 772754 fix with packagecopyjob changes removed. |
1030 |
return results |
1031 |
||
|
6676.4.1
by Abel Deuring
filtering of bug notifications for structural subscriptions for BugNotificationlevel.COMMENTS |
1032 |
def getIndirectSubscribers(self, recipients=None, level=None): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1033 |
"""See `IBug`.
|
|
3945.2.30
by kiko
Move recipients out of the interface description and into the docstrings of the database class' methods. |
1034 |
|
1035 |
See the comment in getDirectSubscribers for a description of the
|
|
1036 |
recipients argument.
|
|
1037 |
"""
|
|
|
3553.3.73
by Brad Bollenbach
code review fixes |
1038 |
# "Also notified" and duplicate subscribers are mutually
|
1039 |
# exclusive, so return both lists.
|
|
|
11869.18.7
by Gavin Panella
Migrate getSubscribersFromDuplicates() to use BugSubscriptionInfo. |
1040 |
indirect_subscribers = chain( |
1041 |
self.getAlsoNotifiedSubscribers(recipients, level), |
|
|
6676.4.1
by Abel Deuring
filtering of bug notifications for structural subscriptions for BugNotificationlevel.COMMENTS |
1042 |
self.getSubscribersFromDuplicates(recipients, level)) |
|
3691.209.6
by Bjorn Tillenius
review comments. |
1043 |
|
|
11545.5.10
by Deryck Hodge
Remove proxy on the object to provide sort key for indirect subscribers. |
1044 |
# Remove security proxy for the sort key, but return
|
1045 |
# the regular proxied object.
|
|
|
3553.3.73
by Brad Bollenbach
code review fixes |
1046 |
return sorted( |
|
11545.5.10
by Deryck Hodge
Remove proxy on the object to provide sort key for indirect subscribers. |
1047 |
indirect_subscribers, |
|
14494.6.34
by Gavin Panella
Update XXX for bug 911752. |
1048 |
# XXX: GavinPanella 2011-12-12 bug=911752: Use person_sort_key.
|
|
11545.5.10
by Deryck Hodge
Remove proxy on the object to provide sort key for indirect subscribers. |
1049 |
key=lambda x: removeSecurityProxy(x).displayname) |
|
3553.3.71
by Brad Bollenbach
Attempt to fix bug 66562 (BugSubscriberPortletView.getSubscribersFromDupes seems to cause timeouts) |
1050 |
|
|
5454.1.5
by Tom Berger
record who created each bug subscription, and display the result in the title of the subscriber link |
1051 |
def getSubscriptionsFromDuplicates(self, recipients=None): |
1052 |
"""See `IBug`."""
|
|
1053 |
if self.private: |
|
1054 |
return [] |
|
|
11536.1.8
by Gavin Panella
In getSubscriptionsFromDuplicates(), move the subscription selection logic into the database. |
1055 |
# For each subscription to each duplicate of this bug, find the
|
|
11536.1.9
by Gavin Panella
Change the sub-query to a DISTINCT ON clause, as suggested by lifeless. Uses a hack to work around bug 374777. |
1056 |
# earliest subscription for each subscriber. Eager load the
|
1057 |
# subscribers.
|
|
|
11536.1.8
by Gavin Panella
In getSubscriptionsFromDuplicates(), move the subscription selection logic into the database. |
1058 |
return DecoratedResultSet( |
|
11536.1.4
by Gavin Panella
Ensure that the output of getSubscriptionsFromDuplicates is stable, and change the subscriptions ReferenceSet into a property because the web API can't adapt what ReferenceSet returns. |
1059 |
IStore(BugSubscription).find( |
|
13052.1.2
by William Grant
Use Storm DISTINCT ON instead of the ignore hack. |
1060 |
(Person, BugSubscription), |
|
11536.1.9
by Gavin Panella
Change the sub-query to a DISTINCT ON clause, as suggested by lifeless. Uses a hack to work around bug 374777. |
1061 |
Bug.duplicateof == self, |
1062 |
BugSubscription.bug_id == Bug.id, |
|
|
11536.1.8
by Gavin Panella
In getSubscriptionsFromDuplicates(), move the subscription selection logic into the database. |
1063 |
BugSubscription.person_id == Person.id).order_by( |
|
14913.1.1
by William Grant
Fix Bug.getSubscriptionsFromDuplicates to specify a deterministic sort order. Its tests break occasionally otherwise. |
1064 |
BugSubscription.person_id, |
1065 |
BugSubscription.date_created, |
|
1066 |
BugSubscription.id |
|
1067 |
).config(distinct=(BugSubscription.person_id,)), |
|
|
13052.1.2
by William Grant
Use Storm DISTINCT ON instead of the ignore hack. |
1068 |
operator.itemgetter(1)) |
|
5454.1.5
by Tom Berger
record who created each bug subscription, and display the result in the title of the subscriber link |
1069 |
|
|
6676.4.1
by Abel Deuring
filtering of bug notifications for structural subscriptions for BugNotificationlevel.COMMENTS |
1070 |
def getSubscribersFromDuplicates(self, recipients=None, level=None): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1071 |
"""See `IBug`.
|
|
3945.2.30
by kiko
Move recipients out of the interface description and into the docstrings of the database class' methods. |
1072 |
|
1073 |
See the comment in getDirectSubscribers for a description of the
|
|
1074 |
recipients argument.
|
|
1075 |
"""
|
|
|
11869.18.17
by Gavin Panella
Make test_subscribers_from_dupes_uses_level() fail when it should. |
1076 |
if level is None: |
|
7675.1139.1
by Danilo Segan
Get rid of all NOTHING usage. |
1077 |
level = BugNotificationLevel.LIFECYCLE |
|
11869.18.17
by Gavin Panella
Make test_subscribers_from_dupes_uses_level() fail when it should. |
1078 |
info = self.getSubscriptionInfo(level) |
|
3945.2.27
by kiko
Replace rationale for recipients everywhere |
1079 |
if recipients is not None: |
|
14494.6.4
by Gavin Panella
Repair BugSubscriptionInfo so that it works more like it was originally intended, and get it to pre-load preferred email addresses for subscribers. |
1080 |
list(self.duplicates) # Pre-load duplicate bugs. |
1081 |
info.duplicate_only_subscribers # Pre-load subscribers. |
|
|
11869.18.7
by Gavin Panella
Migrate getSubscribersFromDuplicates() to use BugSubscriptionInfo. |
1082 |
for subscription in info.duplicate_only_subscriptions: |
|
12338.3.3
by Gary Poster
the test was a false alarm. remove it. |
1083 |
recipients.addDupeSubscriber( |
1084 |
subscription.person, subscription.bug) |
|
|
14494.6.4
by Gavin Panella
Repair BugSubscriptionInfo so that it works more like it was originally intended, and get it to pre-load preferred email addresses for subscribers. |
1085 |
return info.duplicate_only_subscribers.sorted |
|
3691.209.6
by Bjorn Tillenius
review comments. |
1086 |
|
|
11474.2.3
by Robert Collins
Add getSubscribersForPerson to IBug, permitting single query setup of bug index pages, which according to OOPS is about 1.2 seconds (but if we're underestimating could be more) and will make analysing performance on the page easier. |
1087 |
def getSubscribersForPerson(self, person): |
1088 |
"""See `IBug."""
|
|
|
7675.166.301
by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint |
1089 |
|
|
11474.2.3
by Robert Collins
Add getSubscribersForPerson to IBug, permitting single query setup of bug index pages, which according to OOPS is about 1.2 seconds (but if we're underestimating could be more) and will make analysing performance on the page easier. |
1090 |
assert person is not None |
|
7675.166.301
by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint |
1091 |
|
|
11582.2.5
by Robert Collins
Fix up test fallout. |
1092 |
def cache_unsubscribed(rows): |
|
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
1093 |
if not rows: |
1094 |
self._unsubscribed_cache.add(person) |
|
|
7675.166.301
by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint |
1095 |
|
|
11582.2.5
by Robert Collins
Fix up test fallout. |
1096 |
def cache_subscriber(row): |
|
13052.1.2
by William Grant
Use Storm DISTINCT ON instead of the ignore hack. |
1097 |
subscriber, subscription = row |
|
11536.1.17
by Gavin Panella
Fix regression introduced from merge. |
1098 |
if subscription.bug_id == self.id: |
|
11582.2.5
by Robert Collins
Fix up test fallout. |
1099 |
self._subscriber_cache.add(subscriber) |
1100 |
else: |
|
1101 |
self._subscriber_dups_cache.add(subscriber) |
|
1102 |
return subscriber |
|
|
16122.2.1
by Steve Kowalik
Rewrite the heavy-lifting queries in IBug.getSubscriptionForPerson() and IPersonSubscriptionInfo._getDirectAndDuplicateSubscriptions() to be performant using two CTEs, and bulk load all related people, bugtasks and pillars in the second function. |
1103 |
with_statement = generate_subscription_with(self, person) |
1104 |
store = Store.of(self).with_(with_statement) |
|
1105 |
return DecoratedResultSet(store.find( |
|
|
11789.3.10
by Gavin Panella
Fix some lint and remove some vestigial test narrative. |
1106 |
# Return people and subscriptions
|
|
13052.1.2
by William Grant
Use Storm DISTINCT ON instead of the ignore hack. |
1107 |
(Person, BugSubscription), |
|
16122.2.1
by Steve Kowalik
Rewrite the heavy-lifting queries in IBug.getSubscriptionForPerson() and IPersonSubscriptionInfo._getDirectAndDuplicateSubscriptions() to be performant using two CTEs, and bulk load all related people, bugtasks and pillars in the second function. |
1108 |
BugSubscription.id.is_in( |
1109 |
SQL('SELECT bugsubscriptions.id FROM bugsubscriptions')), |
|
1110 |
Person.id == BugSubscription.person_id, |
|
|
13052.1.2
by William Grant
Use Storm DISTINCT ON instead of the ignore hack. |
1111 |
).order_by(Person.name).config( |
1112 |
distinct=(Person.name, BugSubscription.person_id)), |
|
|
11582.2.5
by Robert Collins
Fix up test fallout. |
1113 |
cache_subscriber, pre_iter_hook=cache_unsubscribed) |
|
11474.2.3
by Robert Collins
Add getSubscribersForPerson to IBug, permitting single query setup of bug index pages, which according to OOPS is about 1.2 seconds (but if we're underestimating could be more) and will make analysing performance on the page easier. |
1114 |
|
|
11843.1.3
by Graham Binns
It's now possible to update your subscription. Hurrah. |
1115 |
def getSubscriptionForPerson(self, person): |
1116 |
"""See `IBug`."""
|
|
|
16122.2.1
by Steve Kowalik
Rewrite the heavy-lifting queries in IBug.getSubscriptionForPerson() and IPersonSubscriptionInfo._getDirectAndDuplicateSubscriptions() to be performant using two CTEs, and bulk load all related people, bugtasks and pillars in the second function. |
1117 |
return Store.of(self).find( |
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
1118 |
BugSubscription, BugSubscription.person == person, |
|
11843.1.3
by Graham Binns
It's now possible to update your subscription. Hurrah. |
1119 |
BugSubscription.bug == self).one() |
1120 |
||
|
6676.4.1
by Abel Deuring
filtering of bug notifications for structural subscriptions for BugNotificationlevel.COMMENTS |
1121 |
def getAlsoNotifiedSubscribers(self, recipients=None, level=None): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1122 |
"""See `IBug`.
|
|
3945.2.30
by kiko
Move recipients out of the interface description and into the docstrings of the database class' methods. |
1123 |
|
1124 |
See the comment in getDirectSubscribers for a description of the
|
|
1125 |
recipients argument.
|
|
1126 |
"""
|
|
|
12541.2.5
by Gary Poster
refactor getAlsoNotifiedSubscribers to make it reusable, to use the structuralsubscriber function directly, and to better handle direct subscribers; eliminate duplicated code. |
1127 |
return get_also_notified_subscribers(self, recipients, level) |
|
3554.3.6
by Brad Bollenbach
checkpoint |
1128 |
|
|
16152.3.3
by Curtis Hovey
Differ the retreival bug notification recipients to cached helpers. |
1129 |
def _getBugNotificationRecipients(self, level): |
1130 |
"""Get the recipients for the BugNotificationLevel."""
|
|
|
16147.2.2
by Curtis Hovey
Remove unused duplicateof param from Bug.getBugNotificationRecipients() |
1131 |
recipients = BugNotificationRecipients() |
|
15745.5.1
by William Grant
Resurrect private structsubs changes so they can be made performant. |
1132 |
self.getDirectSubscribers( |
1133 |
recipients, level=level, filter_visible=True) |
|
1134 |
self.getIndirectSubscribers(recipients, level=level) |
|
|
16152.3.3
by Curtis Hovey
Differ the retreival bug notification recipients to cached helpers. |
1135 |
return recipients |
1136 |
||
1137 |
@cachedproperty
|
|
1138 |
def _notification_recipients_for_lifecycle(self): |
|
1139 |
"""The cached BugNotificationRecipients for LIFECYCLE events."""
|
|
1140 |
return self._getBugNotificationRecipients( |
|
1141 |
BugNotificationLevel.LIFECYCLE) |
|
1142 |
||
1143 |
@cachedproperty
|
|
1144 |
def _notification_recipients_for_metadata(self): |
|
1145 |
"""The cached BugNotificationRecipients for METADATA events."""
|
|
1146 |
return self._getBugNotificationRecipients( |
|
1147 |
BugNotificationLevel.METADATA) |
|
1148 |
||
1149 |
@cachedproperty
|
|
1150 |
def _notification_recipients_for_comments(self): |
|
1151 |
"""The cached BugNotificationRecipients for COMMENT events."""
|
|
1152 |
return self._getBugNotificationRecipients( |
|
1153 |
BugNotificationLevel.COMMENTS) |
|
1154 |
||
1155 |
def getBugNotificationRecipients(self, |
|
1156 |
level=BugNotificationLevel.LIFECYCLE): |
|
1157 |
"""See `IBug`."""
|
|
1158 |
recipients = BugNotificationRecipients() |
|
1159 |
if level == BugNotificationLevel.LIFECYCLE: |
|
1160 |
recipients.update(self._notification_recipients_for_lifecycle) |
|
1161 |
elif level == BugNotificationLevel.METADATA: |
|
1162 |
recipients.update(self._notification_recipients_for_metadata) |
|
1163 |
else: |
|
1164 |
recipients.update(self._notification_recipients_for_comments) |
|
1165 |
return recipients |
|
|
1478
by Canonical.com Patch Queue Manager
refactor bug subscription code to look more like bounty subscription code |
1166 |
|
|
16152.3.8
by Curtis Hovey
Ues a model method to control the clearing of the _notification_recipients* |
1167 |
def clearBugNotificationRecipientsCache(self): |
1168 |
cache = get_property_cache(self) |
|
1169 |
if getattr(cache, '_notification_recipients_for_lifecycle', False): |
|
1170 |
del cache._notification_recipients_for_lifecycle |
|
1171 |
if getattr(cache, '_notification_recipients_for_metadata', False): |
|
1172 |
del cache._notification_recipients_for_metadata |
|
1173 |
if getattr(cache, '_notification_recipients_for_comments', False): |
|
1174 |
del cache._notification_recipients_for_comments |
|
1175 |
||
|
16856.2.1
by William Grant
Bug.addCommentNotification now permits overriding of the notification level. |
1176 |
def addCommentNotification(self, message, recipients=None, activity=None, |
1177 |
level=BugNotificationLevel.COMMENTS): |
|
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1178 |
"""See `IBug`."""
|
|
5937.1.1
by Tom Berger
merge patches for the original branch |
1179 |
if recipients is None: |
|
16856.2.1
by William Grant
Bug.addCommentNotification now permits overriding of the notification level. |
1180 |
recipients = self.getBugNotificationRecipients(level=level) |
|
5937.1.1
by Tom Berger
merge patches for the original branch |
1181 |
getUtility(IBugNotificationSet).addNotification( |
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
1182 |
bug=self, is_comment=True, message=message, recipients=recipients, |
1183 |
activity=activity) |
|
|
3254.1.14
by Bjorn Tillenius
checkpoint commit |
1184 |
|
|
16146.2.1
by Ian Booth
Introduce bulk update operations for bugs with duplicates affecting users and bug heat updates |
1185 |
def addChange(self, change, recipients=None, deferred=False, |
1186 |
update_heat=True): |
|
|
7947.1.1
by Graham Binns
Added the basics of the new API. |
1187 |
"""See `IBug`."""
|
|
7947.2.9
by Graham Binns
Whole wodges of stuff changed. I can't remember what, though. |
1188 |
when = change.when |
1189 |
if when is None: |
|
1190 |
when = UTC_NOW |
|
1191 |
||
|
7947.1.3
by Graham Binns
Added basic tests for BugActivity in addChange(). |
1192 |
activity_data = change.getBugActivity() |
1193 |
if activity_data is not None: |
|
|
12366.6.1
by Gary Poster
basic changes to make bugactivity an attribute of a notification as frequently as possible |
1194 |
activity = getUtility(IBugActivitySet).new( |
|
7947.2.9
by Graham Binns
Whole wodges of stuff changed. I can't remember what, though. |
1195 |
self, when, change.person, |
|
7947.1.3
by Graham Binns
Added basic tests for BugActivity in addChange(). |
1196 |
activity_data['whatchanged'], |
1197 |
activity_data.get('oldvalue'), |
|
1198 |
activity_data.get('newvalue'), |
|
1199 |
activity_data.get('message')) |
|
|
12366.6.1
by Gary Poster
basic changes to make bugactivity an attribute of a notification as frequently as possible |
1200 |
else: |
1201 |
activity = None |
|
|
7947.1.1
by Graham Binns
Added the basics of the new API. |
1202 |
|
|
7947.1.5
by Graham Binns
Added the remainder of the tests for notifications, etc. |
1203 |
notification_data = change.getBugNotification() |
1204 |
if notification_data is not None: |
|
|
7947.1.7
by Graham Binns
Removed comment-handling code from Bug.addChange(). |
1205 |
assert notification_data.get('text') is not None, ( |
1206 |
"notification_data must include a `text` value.") |
|
|
12289.11.2
by Gavin Panella
Remove addChangeNotification() entirely. |
1207 |
message = MessageSet().fromText( |
1208 |
self.followup_subject(), notification_data['text'], |
|
1209 |
owner=change.person, datecreated=when) |
|
1210 |
if recipients is None: |
|
|
12289.11.4
by Gavin Panella
Assign to recipients to make the intent clearer. |
1211 |
recipients = self.getBugNotificationRecipients( |
|
16856.2.4
by William Grant
Promote task add/delete/retarget events to LIFECYCLE. |
1212 |
level=change.change_level) |
|
12289.11.4
by Gavin Panella
Assign to recipients to make the intent clearer. |
1213 |
getUtility(IBugNotificationSet).addNotification( |
1214 |
bug=self, is_comment=False, message=message, |
|
|
13627.2.7
by Brad Crittenden
Removed obsolete code, added TestGetDeferredNotifications, mark deferred notifications explicitly with a flag. |
1215 |
recipients=recipients, activity=activity, |
1216 |
deferred=deferred) |
|
|
7947.1.5
by Graham Binns
Added the remainder of the tests for notifications, etc. |
1217 |
|
|
16146.2.1
by Ian Booth
Introduce bulk update operations for bugs with duplicates affecting users and bug heat updates |
1218 |
if update_heat: |
1219 |
update_bug_heat([self.id]) |
|
|
7675.472.31
by Graham Binns
Added tests and implementation for calculating bug heat upon bug activity. |
1220 |
|
|
3691.440.23
by James Henstridge
expire pending bug notifications for newly created bugs |
1221 |
def expireNotifications(self): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1222 |
"""See `IBug`."""
|
|
3691.440.23
by James Henstridge
expire pending bug notifications for newly created bugs |
1223 |
for notification in BugNotification.selectBy( |
1224 |
bug=self, date_emailed=None): |
|
1225 |
notification.date_emailed = UTC_NOW |
|
1226 |
notification.syncUpdate() |
|
1227 |
||
|
6293.1.1
by Tom Berger
reply to remore bug comments ui |
1228 |
def newMessage(self, owner=None, subject=None, |
|
7337.7.4
by Graham Binns
Fixed spurious test failures. |
1229 |
content=None, parent=None, bugwatch=None, |
|
16071.2.1
by Curtis Hovey
Do not send notifications about new bug messages if they are supressed. |
1230 |
remote_comment_id=None, send_notifications=True): |
|
3254.1.24
by Bjorn Tillenius
fix bug 25724, remove comment_on_change hack. |
1231 |
"""Create a new Message and link it to this bug."""
|
|
9037.1.2
by Tom Berger
instead of a None subject, use the followup subject when saving |
1232 |
if subject is None: |
1233 |
subject = self.followup_subject() |
|
|
2938.2.4
by Brad Bollenbach
test fixes |
1234 |
msg = Message( |
1235 |
parent=parent, owner=owner, subject=subject, |
|
1236 |
rfc822msgid=make_msgid('malone')) |
|
|
3691.62.21
by kiko
Clean up the use of ID/.id in select*By and constructors |
1237 |
MessageChunk(message=msg, content=content, sequence=1) |
|
2938.2.10
by Brad Bollenbach
response to code review |
1238 |
|
|
7337.7.4
by Graham Binns
Fixed spurious test failures. |
1239 |
bugmsg = self.linkMessage( |
1240 |
msg, bugwatch, remote_comment_id=remote_comment_id) |
|
|
4187.5.2
by Abel Deuring
implemented reviewer's suggestions |
1241 |
if not bugmsg: |
1242 |
return
|
|
|
4187.5.1
by Abel Deuring
fix for bug 1804 |
1243 |
|
|
16071.2.1
by Curtis Hovey
Do not send notifications about new bug messages if they are supressed. |
1244 |
if send_notifications: |
1245 |
notify(ObjectCreatedEvent(bugmsg, user=owner)) |
|
|
2938.2.1
by Brad Bollenbach
checkpoint |
1246 |
|
1247 |
return bugmsg.message |
|
|
2396
by Canonical.com Patch Queue Manager
[r=spiv] launchpad support tracker |
1248 |
|
|
6002.8.4
by Bjorn Tillenius
add BugMessage.remote_comment_id and have the comment importer set it. |
1249 |
def linkMessage(self, message, bugwatch=None, user=None, |
1250 |
remote_comment_id=None): |
|
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1251 |
"""See `IBug`."""
|
|
2048
by Canonical.com Patch Queue Manager
debbugssync, hct enabling, and ui fixes. r=jamesh |
1252 |
if message not in self.messages: |
|
5548.9.8
by Graham Binns
Salgado's review changes. |
1253 |
if user is None: |
1254 |
user = message.owner |
|
|
5292.2.6
by Graham Binns
Imported comments are not shown. |
1255 |
result = BugMessage(bug=self, message=message, |
|
12346.2.2
by Robert Collins
Change all BugMessage object creation to set the index. This involved |
1256 |
bugwatch=bugwatch, remote_comment_id=remote_comment_id, |
1257 |
index=self.bug_messages.count()) |
|
|
4187.5.2
by Abel Deuring
implemented reviewer's suggestions |
1258 |
getUtility(IBugWatchSet).fromText( |
|
5548.9.8
by Graham Binns
Salgado's review changes. |
1259 |
message.text_contents, self, user) |
1260 |
self.findCvesInText(message.text_contents, user) |
|
|
12845.2.5
by Robert Collins
Serialise INCOMPLETE status to INCOMPLETE_WITHOUT_RESPONSE. |
1261 |
for bugtask in self.bugtasks: |
1262 |
# Check the stored value so we don't write to unaltered tasks.
|
|
|
13973.2.5
by Brad Crittenden
Version with lots of debugging junk |
1263 |
if (bugtask._status in ( |
1264 |
BugTaskStatus.INCOMPLETE, |
|
1265 |
BugTaskStatusSearch.INCOMPLETE_WITHOUT_RESPONSE)): |
|
|
12845.2.5
by Robert Collins
Serialise INCOMPLETE status to INCOMPLETE_WITHOUT_RESPONSE. |
1266 |
# This is not a semantic change, so we don't update date
|
1267 |
# records or send email.
|
|
|
14039.1.8
by Brad Crittenden
Fixed lint |
1268 |
bugtask._status = ( |
1269 |
BugTaskStatusSearch.INCOMPLETE_WITH_RESPONSE) |
|
|
5821.5.20
by James Henstridge
* Add some flush calls to message/bugmessage creation, to make sure |
1270 |
# XXX 2008-05-27 jamesh:
|
1271 |
# Ensure that BugMessages get flushed in same order as
|
|
1272 |
# they are created.
|
|
1273 |
Store.of(result).flush() |
|
|
4187.5.2
by Abel Deuring
implemented reviewer's suggestions |
1274 |
return result |
|
2048
by Canonical.com Patch Queue Manager
debbugssync, hct enabling, and ui fixes. r=jamesh |
1275 |
|
|
16183.4.1
by Ian Booth
Improve bug form and model infrastructure to avoid repeated calls to various validation methods |
1276 |
def addTask(self, owner, target, validate_target=True): |
|
7705.1.1
by Graham Binns
Added IBug.addTask() as a nominal wrapper around IBugTaskSet.createTask(). |
1277 |
"""See `IBug`."""
|
|
16183.4.1
by Ian Booth
Improve bug form and model infrastructure to avoid repeated calls to various validation methods |
1278 |
return getUtility(IBugTaskSet).createTask( |
1279 |
self, owner, target, validate_target) |
|
|
7705.1.1
by Graham Binns
Added IBug.addTask() as a nominal wrapper around IBugTaskSet.createTask(). |
1280 |
|
|
2048
by Canonical.com Patch Queue Manager
debbugssync, hct enabling, and ui fixes. r=jamesh |
1281 |
def addWatch(self, bugtracker, remotebug, owner): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1282 |
"""See `IBug`."""
|
|
3691.209.3
by Bjorn Tillenius
add page test to make sure +editstatus doesn't create duplicate bug watches. |
1283 |
# We shouldn't add duplicate bug watches.
|
1284 |
bug_watch = self.getBugWatch(bugtracker, remotebug) |
|
|
5821.2.57
by James Henstridge
more changes to resolve failing tests. |
1285 |
if bug_watch is None: |
1286 |
bug_watch = BugWatch( |
|
|
3691.209.3
by Bjorn Tillenius
add page test to make sure +editstatus doesn't create duplicate bug watches. |
1287 |
bug=self, bugtracker=bugtracker, |
1288 |
remotebug=remotebug, owner=owner) |
|
|
5821.2.57
by James Henstridge
more changes to resolve failing tests. |
1289 |
Store.of(bug_watch).flush() |
|
7982.1.4
by Bjorn Tillenius
Make sure something is added to the activity log. |
1290 |
self.addChange(BugWatchAdded(UTC_NOW, owner, bug_watch)) |
|
7982.1.2
by Bjorn Tillenius
Fire off the event from inside addWatch(). |
1291 |
notify(ObjectCreatedEvent(bug_watch, user=owner)) |
|
5821.2.57
by James Henstridge
more changes to resolve failing tests. |
1292 |
return bug_watch |
|
2048
by Canonical.com Patch Queue Manager
debbugssync, hct enabling, and ui fixes. r=jamesh |
1293 |
|
|
7982.1.6
by Bjorn Tillenius
Add Bug.removeWatch(). |
1294 |
def removeWatch(self, bug_watch, user): |
1295 |
"""See `IBug`."""
|
|
|
7982.1.7
by Bjorn Tillenius
Make sure bug watch removals are recorded properly. |
1296 |
self.addChange(BugWatchRemoved(UTC_NOW, user, bug_watch)) |
|
7982.1.6
by Bjorn Tillenius
Add Bug.removeWatch(). |
1297 |
bug_watch.destroySelf() |
1298 |
||
|
6655.4.14
by Gavin Panella
Fix up the doc for IBug.addAttachment, and other related fixes. |
1299 |
def addAttachment(self, owner, data, comment, filename, is_patch=False, |
|
16473.2.2
by Steve Kowalik
Stop lp.services.webapp.publisher re-exporting get_current_browser_request and fix all import sites to use lazr.restful.utils. If IProductRelease.add_file or IBug.addAttachment are called via the API, call into the new method, get_raw_form_value_from_current_request which will get the non-encoded file content. |
1300 |
content_type=None, description=None, from_api=False): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1301 |
"""See `IBug`."""
|
|
16473.2.3
by Steve Kowalik
Sprinkle in XXXes for the call sites of get_raw_form_value_from_current_request. |
1302 |
# XXX: StevenK 2013-02-06 bug=1116954: We should not need to refetch
|
1303 |
# the file content from the request, since the passed in one has been
|
|
1304 |
# wrongly encoded.
|
|
|
16473.2.2
by Steve Kowalik
Stop lp.services.webapp.publisher re-exporting get_current_browser_request and fix all import sites to use lazr.restful.utils. If IProductRelease.add_file or IBug.addAttachment are called via the API, call into the new method, get_raw_form_value_from_current_request which will get the non-encoded file content. |
1305 |
if from_api: |
|
16951.1.1
by William Grant
Adjust usage of get_raw_form_value_from_current_request to preserve the original field if it isn't corrupted. It'll be uncorrupted if there's a filename. |
1306 |
data = get_raw_form_value_from_current_request(data, 'data') |
|
6655.4.2
by Gavin Panella
First round of annotations, with tests. |
1307 |
if isinstance(data, str): |
1308 |
filecontent = data |
|
1309 |
else: |
|
1310 |
filecontent = data.read() |
|
1311 |
||
|
3644.1.11
by Brad Bollenbach
refactor bug attachment API and allow adding an attachment while commenting |
1312 |
if is_patch: |
1313 |
content_type = 'text/plain' |
|
1314 |
else: |
|
|
3691.320.5
by Bjorn Tillenius
support adding attachments to the bug report. |
1315 |
if content_type is None: |
1316 |
content_type, encoding = guess_content_type( |
|
1317 |
name=filename, body=filecontent) |
|
|
3644.1.11
by Brad Bollenbach
refactor bug attachment API and allow adding an attachment while commenting |
1318 |
|
|
15903.1.2
by Steve Kowalik
Raise the exception in ILibraryFileAliasSet.create(), use an actual exception. |
1319 |
filealias = getUtility(ILibraryFileAliasSet).create( |
1320 |
name=filename, size=len(filecontent), |
|
1321 |
file=StringIO(filecontent), contentType=content_type, |
|
1322 |
restricted=self.private) |
|
|
3644.1.11
by Brad Bollenbach
refactor bug attachment API and allow adding an attachment while commenting |
1323 |
|
|
11235.7.4
by Abel Deuring
renamed Bug._linkAttachment() again to Bug.linkAttachment(); allowed the DB user 'bugnotification' to read the table bugattachment. |
1324 |
return self.linkAttachment( |
|
7675.534.1
by Graham Binns
Added an an IBug.linkAttachment() method. |
1325 |
owner, filealias, comment, is_patch, description) |
1326 |
||
|
11235.7.4
by Abel Deuring
renamed Bug._linkAttachment() again to Bug.linkAttachment(); allowed the DB user 'bugnotification' to read the table bugattachment. |
1327 |
def linkAttachment(self, owner, file_alias, comment, is_patch=False, |
|
11634.2.9
by Robert Collins
Another missed fixture stateless-use. |
1328 |
description=None, send_notifications=True): |
|
11235.7.4
by Abel Deuring
renamed Bug._linkAttachment() again to Bug.linkAttachment(); allowed the DB user 'bugnotification' to read the table bugattachment. |
1329 |
"""See `IBug`.
|
1330 |
||
1331 |
This method should only be called by addAttachment() and
|
|
1332 |
FileBugViewBase.submit_bug_action, otherwise
|
|
|
11235.7.1
by Abel Deuring
set the restricted flag of the Librarian record when an attachment is aded to a private bug; flip the restricted flag of Librarian files from bug attachments when the Bug.setPrivate() is called. |
1333 |
we may get inconsistent settings of bug.private and
|
1334 |
file_alias.restricted.
|
|
|
11634.2.9
by Robert Collins
Another missed fixture stateless-use. |
1335 |
|
1336 |
:param send_notifications: Control sending of notifications for this
|
|
1337 |
attachment. This is disabled when adding attachments from 'extra
|
|
1338 |
data' in the filebug form, because that triggered hundreds of DB
|
|
1339 |
inserts and thus timeouts. Defaults to sending notifications.
|
|
|
11235.7.1
by Abel Deuring
set the restricted flag of the Librarian record when an attachment is aded to a private bug; flip the restricted flag of Librarian files from bug attachments when the Bug.setPrivate() is called. |
1340 |
"""
|
|
7675.534.1
by Graham Binns
Added an an IBug.linkAttachment() method. |
1341 |
if is_patch: |
1342 |
attach_type = BugAttachmentType.PATCH |
|
1343 |
else: |
|
1344 |
attach_type = BugAttachmentType.UNSPECIFIED |
|
1345 |
||
|
3644.1.11
by Brad Bollenbach
refactor bug attachment API and allow adding an attachment while commenting |
1346 |
if description: |
1347 |
title = description |
|
1348 |
else: |
|
|
7675.534.1
by Graham Binns
Added an an IBug.linkAttachment() method. |
1349 |
title = file_alias.filename |
|
3644.1.11
by Brad Bollenbach
refactor bug attachment API and allow adding an attachment while commenting |
1350 |
|
|
3644.1.14
by Brad Bollenbach
checkpoint |
1351 |
if IMessage.providedBy(comment): |
1352 |
message = comment |
|
1353 |
else: |
|
1354 |
message = self.newMessage( |
|
1355 |
owner=owner, subject=description, content=comment) |
|
|
3644.1.11
by Brad Bollenbach
refactor bug attachment API and allow adding an attachment while commenting |
1356 |
|
|
5796.14.1
by Abel Deuring
Fix for bug 195664 |
1357 |
return getUtility(IBugAttachmentSet).create( |
|
7675.534.1
by Graham Binns
Added an an IBug.linkAttachment() method. |
1358 |
bug=self, filealias=file_alias, attach_type=attach_type, |
|
11634.2.9
by Robert Collins
Another missed fixture stateless-use. |
1359 |
title=title, message=message, |
1360 |
send_notifications=send_notifications) |
|
|
3644.1.11
by Brad Bollenbach
refactor bug attachment API and allow adding an attachment while commenting |
1361 |
|
|
8698.10.3
by Paul Hummer
Integrated IHasLinkedBranches into the interfaces |
1362 |
def linkBranch(self, branch, registrant): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1363 |
"""See `IBug`."""
|
|
17910.1.10
by William Grant
Don't leak BugBranches out of linkBranch. |
1364 |
if branch in self.linked_branches: |
1365 |
return
|
|
|
3283.3.1
by Brad Bollenbach
create a new branch for bzr integration, to avoid 3 hour merge time |
1366 |
|
|
17910.1.10
by William Grant
Don't leak BugBranches out of linkBranch. |
1367 |
BugBranch(branch=branch, bug=self, registrant=registrant) |
|
5001.2.3
by Tim Penhey
More test details. |
1368 |
branch.date_last_modified = UTC_NOW |
|
3283.3.1
by Brad Bollenbach
create a new branch for bzr integration, to avoid 3 hour merge time |
1369 |
|
|
8640.2.2
by Gavin Panella
Don't send notifications for bug-branch linking/unlinking if the bug is complete. |
1370 |
self.addChange(BranchLinkedToBug(UTC_NOW, registrant, branch, self)) |
|
17910.1.8
by William Grant
Replace IObject(Created|Deleted)Events on IBugBranch with IObjectLinkedEvents on Bug. |
1371 |
notify(ObjectLinkedEvent(branch, self, user=registrant)) |
1372 |
notify(ObjectLinkedEvent(self, branch, user=registrant)) |
|
|
3554.1.4
by Brad Bollenbach
make sure adding/editing a bug branch via the UI updates IBug.date_last_updated |
1373 |
|
|
8698.10.3
by Paul Hummer
Integrated IHasLinkedBranches into the interfaces |
1374 |
def unlinkBranch(self, branch, user): |
|
7977.2.7
by Bjorn Tillenius
Add Bug.removeBranch(). |
1375 |
"""See `IBug`."""
|
1376 |
bug_branch = BugBranch.selectOneBy(bug=self, branch=branch) |
|
1377 |
if bug_branch is not None: |
|
|
8640.2.2
by Gavin Panella
Don't send notifications for bug-branch linking/unlinking if the bug is complete. |
1378 |
self.addChange(BranchUnlinkedFromBug(UTC_NOW, user, branch, self)) |
|
17910.1.8
by William Grant
Replace IObject(Created|Deleted)Events on IBugBranch with IObjectLinkedEvents on Bug. |
1379 |
notify(ObjectUnlinkedEvent(branch, self, user=user)) |
1380 |
notify(ObjectUnlinkedEvent(self, branch, user=user)) |
|
|
17910.1.9
by William Grant
Only allow BugBranch deletion via Bug.unlinkBranch. |
1381 |
Store.of(bug_branch).remove(bug_branch) |
|
7977.2.7
by Bjorn Tillenius
Add Bug.removeBranch(). |
1382 |
|
|
13827.1.1
by Gary Poster
add optimization for bug page with many branches: this time for sure! |
1383 |
def getVisibleLinkedBranches(self, user, eager_load=False): |
|
18114.1.2
by Colin Watson
Implement the basics of bug linking for Git-based merge proposals. |
1384 |
"""See `IBug`."""
|
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
1385 |
linked_branches = list(getUtility(IAllBranches).visibleByUser( |
|
13827.1.1
by Gary Poster
add optimization for bug page with many branches: this time for sure! |
1386 |
user).linkedToBugs([self]).getBranches(eager_load=eager_load)) |
|
13277.4.11
by Graham Binns
Listify earlier to save multiple queries. |
1387 |
if len(linked_branches) == 0: |
|
13277.4.8
by Graham Binns
Might have fixed some fundamental issues. Might not have. Unsure. I have a feeling that this might fail spectacularly. |
1388 |
return EmptyResultSet() |
|
13277.4.11
by Graham Binns
Listify earlier to save multiple queries. |
1389 |
else: |
1390 |
branch_ids = [branch.id for branch in linked_branches] |
|
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
1391 |
return Store.of(self).find( |
|
13277.4.11
by Graham Binns
Listify earlier to save multiple queries. |
1392 |
BugBranch, |
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
1393 |
BugBranch.bug == self, In(BugBranch.branchID, branch_ids)) |
|
13277.4.1
by Graham Binns
Fixed the bug. May have compromised on performance. |
1394 |
|
|
18114.1.2
by Colin Watson
Implement the basics of bug linking for Git-based merge proposals. |
1395 |
def linkMergeProposal(self, merge_proposal, user, check_permissions=True): |
1396 |
"""See `IBug`."""
|
|
1397 |
merge_proposal.linkBug( |
|
1398 |
self, user=user, check_permissions=check_permissions) |
|
1399 |
||
1400 |
def unlinkMergeProposal(self, merge_proposal, user, |
|
1401 |
check_permissions=True): |
|
1402 |
"""See `IBug`."""
|
|
1403 |
merge_proposal.unlinkBug( |
|
1404 |
self, user=user, check_permissions=check_permissions) |
|
1405 |
||
1406 |
@property
|
|
1407 |
def linked_merge_proposals(self): |
|
1408 |
from lp.code.model.branchmergeproposal import BranchMergeProposal |
|
1409 |
merge_proposal_ids = [ |
|
1410 |
int(id) for _, id in getUtility(IXRefSet).findFrom( |
|
1411 |
(u'bug', unicode(self.id)), types=[u'merge_proposal'])] |
|
1412 |
return list(sorted( |
|
1413 |
bulk.load(BranchMergeProposal, merge_proposal_ids), |
|
1414 |
key=operator.attrgetter('id'))) |
|
1415 |
||
1416 |
def getVisibleLinkedMergeProposals(self, user, eager_load=False): |
|
1417 |
"""See `IBug`."""
|
|
1418 |
linked_merge_proposal_ids = set( |
|
1419 |
bmp.id for bmp in self.linked_merge_proposals) |
|
|
18114.1.3
by Colin Watson
Save a query if a bug has no linked merge proposals. |
1420 |
if not linked_merge_proposal_ids: |
1421 |
return EmptyResultSet() |
|
1422 |
else: |
|
1423 |
# XXX cjwatson 2016-06-24: This will also need to look at
|
|
1424 |
# IAllBranches in the event that we start linking bugs directly
|
|
1425 |
# to Bazaar-based merge proposals rather than to their source
|
|
1426 |
# branches.
|
|
1427 |
collection = getUtility(IAllGitRepositories).visibleByUser(user) |
|
1428 |
return collection.getMergeProposals( |
|
1429 |
merge_proposal_ids=linked_merge_proposal_ids, |
|
1430 |
eager_load=eager_load) |
|
|
18114.1.2
by Colin Watson
Implement the basics of bug linking for Git-based merge proposals. |
1431 |
|
|
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
1432 |
@cachedproperty
|
1433 |
def has_cves(self): |
|
1434 |
"""See `IBug`."""
|
|
1435 |
return bool(self.cves) |
|
1436 |
||
|
17775.1.1
by William Grant
(un)?link(Bug|Cve) can now have their permission check bypassed. Used by bugimport. |
1437 |
def linkCVE(self, cve, user, check_permissions=True): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1438 |
"""See `IBug`."""
|
|
17775.1.1
by William Grant
(un)?link(Bug|Cve) can now have their permission check bypassed. Used by bugimport. |
1439 |
cve.linkBug(self, user=user, check_permissions=check_permissions) |
|
7242.1.1
by Tom Berger
expose bug CVEs via the API |
1440 |
|
|
17775.1.1
by William Grant
(un)?link(Bug|Cve) can now have their permission check bypassed. Used by bugimport. |
1441 |
def unlinkCVE(self, cve, user, check_permissions=True): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1442 |
"""See `IBug`."""
|
|
17775.1.1
by William Grant
(un)?link(Bug|Cve) can now have their permission check bypassed. Used by bugimport. |
1443 |
cve.unlinkBug(self, user=user, check_permissions=check_permissions) |
|
2450
by Canonical.com Patch Queue Manager
[r=jamesh] rework cve structure, and general polish |
1444 |
|
|
4476.1.3
by Bjorn Tillenius
fix test to expose problem when creating CVEs on package uploads. Fix the test failure by requiring a user attribute for linkCVE and findCvesInText. |
1445 |
def findCvesInText(self, text, user): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1446 |
"""See `IBug`."""
|
|
2450
by Canonical.com Patch Queue Manager
[r=jamesh] rework cve structure, and general polish |
1447 |
cves = getUtility(ICveSet).inText(text) |
1448 |
for cve in cves: |
|
|
17828.1.1
by William Grant
Bypass normal bug linking permission checks when linking CVES from comments. |
1449 |
self.linkCVE(cve, user, check_permissions=False) |
|
1102
by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs |
1450 |
|
|
3691.436.12
by Mark Shuttleworth
Make sure we don't see completed bugs or specs |
1451 |
# Several other classes need to generate lists of bugs, and
|
1452 |
# one thing they often have to filter for is completeness. We maintain
|
|
1453 |
# this single canonical query string here so that it does not have to be
|
|
1454 |
# cargo culted into Product, Distribution, ProductSeries etc
|
|
|
10234.3.5
by Curtis Hovey
Quiet lint. |
1455 |
completeness_clause = """ |
|
3691.436.12
by Mark Shuttleworth
Make sure we don't see completed bugs or specs |
1456 |
BugTask.bug = Bug.id AND """ + BugTask.completeness_clause |
1457 |
||
|
4755.1.15
by Curtis Hovey
Revised the rules for choosing the BugTask/QuestionTarget. |
1458 |
def canBeAQuestion(self): |
1459 |
"""See `IBug`."""
|
|
1460 |
return (self._getQuestionTargetableBugTask() is not None |
|
1461 |
and self.getQuestionCreatedFromBug() is None) |
|
1462 |
||
1463 |
def _getQuestionTargetableBugTask(self): |
|
1464 |
"""Return the only bugtask that can be a QuestionTarget, or None.
|
|
|
4755.1.16
by Curtis Hovey
Minor revisions made as preparation for the bug-question interface |
1465 |
|
|
4755.1.26
by Curtis Hovey
Text revisions. Revisions to getQuestionTargetableBugTask per |
1466 |
Bugs that are also in external bug trackers cannot be converted
|
1467 |
to questions. This is also true for bugs that are being developed.
|
|
1468 |
None is returned when either of these conditions are true.
|
|
1469 |
||
|
4755.1.19
by Curtis Hovey
Added a interface test to verify that all bugtarget types are handled |
1470 |
The bugtask is selected by these rules:
|
|
4755.1.26
by Curtis Hovey
Text revisions. Revisions to getQuestionTargetableBugTask per |
1471 |
1. It's status is not Invalid.
|
1472 |
2. It is not a conjoined slave.
|
|
1473 |
Only one bugtask must meet both conditions to be return. When
|
|
1474 |
zero or many bugtasks match, None is returned.
|
|
|
4755.1.15
by Curtis Hovey
Revised the rules for choosing the BugTask/QuestionTarget. |
1475 |
"""
|
|
4755.1.43
by Curtis Hovey
Revisions pre review. |
1476 |
# We may want to removed the bugtask.conjoined_master check
|
1477 |
# below. It is used to simplify the task of converting
|
|
1478 |
# conjoined bugtasks to question--since slaves cannot be
|
|
1479 |
# directly updated anyway.
|
|
1480 |
non_invalid_bugtasks = [ |
|
1481 |
bugtask for bugtask in self.bugtasks |
|
1482 |
if (bugtask.status != BugTaskStatus.INVALID |
|
1483 |
and bugtask.conjoined_master is None)] |
|
1484 |
if len(non_invalid_bugtasks) != 1: |
|
1485 |
return None |
|
1486 |
[valid_bugtask] = non_invalid_bugtasks |
|
|
14062.2.2
by Curtis Hovey
Do not permit bugs to be converted to question when the pillar does not |
1487 |
pillar = valid_bugtask.pillar |
1488 |
if (pillar.bug_tracking_usage == ServiceUsage.LAUNCHPAD |
|
1489 |
and pillar.answers_usage == ServiceUsage.LAUNCHPAD): |
|
|
4755.1.43
by Curtis Hovey
Revisions pre review. |
1490 |
return valid_bugtask |
1491 |
else: |
|
1492 |
return None |
|
1493 |
||
1494 |
def convertToQuestion(self, person, comment=None): |
|
|
4755.1.15
by Curtis Hovey
Revised the rules for choosing the BugTask/QuestionTarget. |
1495 |
"""See `IBug`."""
|
|
4755.1.5
by Curtis Hovey
Basic UI behaviour is present. The actual UI (button or link) |
1496 |
question = self.getQuestionCreatedFromBug() |
1497 |
assert question is None, ( |
|
1498 |
'This bug was already converted to question #%s.' % question.id) |
|
|
4755.1.15
by Curtis Hovey
Revised the rules for choosing the BugTask/QuestionTarget. |
1499 |
bugtask = self._getQuestionTargetableBugTask() |
1500 |
assert bugtask is not None, ( |
|
|
4755.1.18
by Curtis Hovey
Finally grocked how bug notification works. |
1501 |
'A question cannot be created from this bug without a '
|
1502 |
'valid bugtask.') |
|
|
4755.1.15
by Curtis Hovey
Revised the rules for choosing the BugTask/QuestionTarget. |
1503 |
|
|
4755.1.18
by Curtis Hovey
Finally grocked how bug notification works. |
1504 |
bugtask_before_modification = Snapshot( |
1505 |
bugtask, providing=providedBy(bugtask)) |
|
|
4755.1.15
by Curtis Hovey
Revised the rules for choosing the BugTask/QuestionTarget. |
1506 |
bugtask.transitionToStatus(BugTaskStatus.INVALID, person) |
|
4755.1.43
by Curtis Hovey
Revisions pre review. |
1507 |
edited_fields = ['status'] |
1508 |
if comment is not None: |
|
1509 |
self.newMessage( |
|
1510 |
owner=person, subject=self.followup_subject(), |
|
1511 |
content=comment) |
|
|
4755.1.18
by Curtis Hovey
Finally grocked how bug notification works. |
1512 |
notify( |
|
7876.3.6
by Francis J. Lacoste
Used ISQLObject from lazr.lifecycle |
1513 |
ObjectModifiedEvent( |
|
4755.1.18
by Curtis Hovey
Finally grocked how bug notification works. |
1514 |
object=bugtask, |
1515 |
object_before_modification=bugtask_before_modification, |
|
|
4755.1.43
by Curtis Hovey
Revisions pre review. |
1516 |
edited_fields=edited_fields, |
|
4755.1.18
by Curtis Hovey
Finally grocked how bug notification works. |
1517 |
user=person)) |
|
4755.1.5
by Curtis Hovey
Basic UI behaviour is present. The actual UI (button or link) |
1518 |
|
|
4755.1.19
by Curtis Hovey
Added a interface test to verify that all bugtarget types are handled |
1519 |
question_target = IQuestionTarget(bugtask.target) |
1520 |
question = question_target.createQuestionFromBug(self) |
|
|
8053.3.6
by Bjorn Tillenius
Use the new addChange() API when converting a bug to a question. |
1521 |
self.addChange(BugConvertedToQuestion(UTC_NOW, person, question)) |
|
11789.2.4
by Gavin Panella
Change all uses of IPropertyCache outside of propertycache.py to get_property_cache. |
1522 |
get_property_cache(self)._question_from_bug = question |
|
4755.1.18
by Curtis Hovey
Finally grocked how bug notification works. |
1523 |
notify(BugBecameQuestionEvent(self, question, person)) |
|
4755.1.5
by Curtis Hovey
Basic UI behaviour is present. The actual UI (button or link) |
1524 |
return question |
1525 |
||
|
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
1526 |
@cachedproperty
|
1527 |
def _question_from_bug(self): |
|
|
4755.1.2
by Curtis Hovey
Added core functionality to create a questions from a bug. More tests are needed, particularly for IQuestionTarget ftests. The UI work and pagetests are not done; some direction is needed. |
1528 |
for question in self.questions: |
|
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
1529 |
if (question.ownerID == self.ownerID |
|
4755.1.43
by Curtis Hovey
Revisions pre review. |
1530 |
and question.datecreated == self.datecreated): |
1531 |
return question |
|
|
4755.1.5
by Curtis Hovey
Basic UI behaviour is present. The actual UI (button or link) |
1532 |
return None |
|
4755.1.2
by Curtis Hovey
Added core functionality to create a questions from a bug. More tests are needed, particularly for IQuestionTarget ftests. The UI work and pagetests are not done; some direction is needed. |
1533 |
|
|
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
1534 |
def getQuestionCreatedFromBug(self): |
1535 |
"""See `IBug`."""
|
|
1536 |
return self._question_from_bug |
|
1537 |
||
|
12376.1.2
by Robert Collins
Basic implementation in place, tests not updated. |
1538 |
def getMessagesForView(self, slice_info): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1539 |
"""See `IBug`."""
|
|
7675.1054.4
by Danilo Segan
Merge Gary's branch from stable. |
1540 |
# Note that this function and indexed_messages have significant
|
1541 |
# overlap and could stand to be refactored.
|
|
|
12376.1.2
by Robert Collins
Basic implementation in place, tests not updated. |
1542 |
slices = [] |
1543 |
if slice_info is not None: |
|
1544 |
# NB: This isn't a full implementation of the slice protocol,
|
|
1545 |
# merely the bits needed by BugTask:+index.
|
|
1546 |
for slice in slice_info: |
|
1547 |
if not slice.start: |
|
1548 |
assert slice.stop > 0, slice.stop |
|
1549 |
slices.append(BugMessage.index < slice.stop) |
|
1550 |
elif not slice.stop: |
|
1551 |
if slice.start < 0: |
|
|
12376.1.6
by Robert Collins
Test (and fix) Bug.getMessagesForView. |
1552 |
# If the high index is N, a slice of -1: should
|
1553 |
# return index N - so we need to add one to the
|
|
1554 |
# range.
|
|
|
12376.1.2
by Robert Collins
Basic implementation in place, tests not updated. |
1555 |
slices.append(BugMessage.index >= SQL( |
1556 |
"(select max(index) from "
|
|
|
12376.1.6
by Robert Collins
Test (and fix) Bug.getMessagesForView. |
1557 |
"bugmessage where bug=%s) + 1 - %s" % ( |
1558 |
sqlvalues(self.id, -slice.start)))) |
|
|
12376.1.2
by Robert Collins
Basic implementation in place, tests not updated. |
1559 |
else: |
1560 |
slices.append(BugMessage.index >= slice.start) |
|
1561 |
else: |
|
|
12376.1.6
by Robert Collins
Test (and fix) Bug.getMessagesForView. |
1562 |
slices.append(And(BugMessage.index >= slice.start, |
1563 |
BugMessage.index < slice.stop)) |
|
|
12376.1.2
by Robert Collins
Basic implementation in place, tests not updated. |
1564 |
if slices: |
1565 |
ranges = [Or(*slices)] |
|
1566 |
else: |
|
1567 |
ranges = [] |
|
1568 |
# We expect:
|
|
1569 |
# 1 bugmessage -> 1 message -> small N chunks. For now, using a wide
|
|
1570 |
# query seems fine as we have to join out from bugmessage anyway.
|
|
1571 |
result = Store.of(self).find((BugMessage, Message, MessageChunk), |
|
|
13163.1.2
by Brad Crittenden
Fixed lint |
1572 |
Message.id == MessageChunk.messageID, |
1573 |
BugMessage.messageID == Message.id, |
|
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
1574 |
BugMessage.bug == self.id, *ranges) |
|
12376.1.2
by Robert Collins
Basic implementation in place, tests not updated. |
1575 |
result.order_by(BugMessage.index, MessageChunk.sequence) |
|
7675.1054.4
by Danilo Segan
Merge Gary's branch from stable. |
1576 |
|
|
12376.1.2
by Robert Collins
Basic implementation in place, tests not updated. |
1577 |
def eager_load_owners(rows): |
1578 |
owners = set() |
|
1579 |
for row in rows: |
|
1580 |
owners.add(row[1].ownerID) |
|
1581 |
owners.discard(None) |
|
1582 |
if not owners: |
|
1583 |
return
|
|
|
12443.1.1
by Robert Collins
Actually eager load message owners in Bug._indexed_messages. |
1584 |
list(PersonSet().getPrecachedPersonsFromIDs(owners, |
1585 |
need_validity=True)) |
|
|
12376.1.6
by Robert Collins
Test (and fix) Bug.getMessagesForView. |
1586 |
return DecoratedResultSet(result, pre_iter_hook=eager_load_owners) |
|
1670
by Canonical.com Patch Queue Manager
Big lot of database clean-up r=stub except for resolution of conflicts. |
1587 |
|
|
3614.1.68
by Brad Bollenbach
reapply MaloneReleaseManagement |
1588 |
def addNomination(self, owner, target): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1589 |
"""See `IBug`."""
|
|
9206.3.13
by William Grant
Refuse to nominate if canBeNominatedFor fails, and make that return false if a non-series is given. |
1590 |
if not self.canBeNominatedFor(target): |
1591 |
raise NominationError( |
|
1592 |
"This bug cannot be nominated for %s." % |
|
1593 |
target.bugtargetdisplayname) |
|
|
11587.5.1
by Brian Murray
restrict adding nominations to the bug supervisor |
1594 |
|
|
4285.2.1
by Mark Shuttleworth
Massive renaming of distrorelease to distroseries |
1595 |
distroseries = None |
|
3614.1.68
by Brad Bollenbach
reapply MaloneReleaseManagement |
1596 |
productseries = None |
|
4285.2.1
by Mark Shuttleworth
Massive renaming of distrorelease to distroseries |
1597 |
if IDistroSeries.providedBy(target): |
1598 |
distroseries = target |
|
|
10054.26.1
by Adi Roiban
Refactor DistroSeriesStatus to SeriesStatus; Don't prompt for setting up translations for obsolete product series. |
1599 |
if target.status == SeriesStatus.OBSOLETE: |
|
4285.2.1
by Mark Shuttleworth
Massive renaming of distrorelease to distroseries |
1600 |
raise NominationSeriesObsoleteError( |
|
9206.3.13
by William Grant
Refuse to nominate if canBeNominatedFor fails, and make that return false if a non-series is given. |
1601 |
"%s is an obsolete series." % target.bugtargetdisplayname) |
|
3614.1.68
by Brad Bollenbach
reapply MaloneReleaseManagement |
1602 |
else: |
1603 |
assert IProductSeries.providedBy(target) |
|
1604 |
productseries = target |
|
1605 |
||
|
11587.5.7
by Brian Murray
make it so only bug supervisors or owners or drivers can nominate a bug for a series |
1606 |
if not (check_permission("launchpad.BugSupervisor", target) or |
1607 |
check_permission("launchpad.Driver", target)): |
|
1608 |
raise NominationError( |
|
1609 |
"Only bug supervisors or owners can nominate bugs.") |
|
|
11587.5.2
by Brian Murray
move nomination permission checking to addNomination of bug |
1610 |
|
|
14540.3.1
by Ian Booth
Allow a declined bug nomination to be re-nominated |
1611 |
# There may be an existing DECLINED nomination. If so, we set the
|
1612 |
# status back to PROPOSED. We do not alter the original date_created.
|
|
1613 |
nomination = None |
|
1614 |
try: |
|
1615 |
nomination = self.getNominationFor(target) |
|
1616 |
except NotFoundError: |
|
1617 |
pass
|
|
1618 |
if nomination: |
|
1619 |
nomination.status = BugNominationStatus.PROPOSED |
|
1620 |
nomination.decider = None |
|
1621 |
nomination.date_decided = None |
|
1622 |
else: |
|
1623 |
nomination = BugNomination( |
|
1624 |
owner=owner, bug=self, distroseries=distroseries, |
|
1625 |
productseries=productseries) |
|
|
9206.3.10
by William Grant
Drop auto-approval from IBug.addNomination. The view does it explicitly itself. |
1626 |
self.addChange(SeriesNominated(UTC_NOW, owner, target)) |
|
3691.434.4
by Bjorn Tillenius
move some logic from the bug nomination view code to database code. |
1627 |
return nomination |
|
3614.1.68
by Brad Bollenbach
reapply MaloneReleaseManagement |
1628 |
|
|
9206.3.12
by William Grant
Export the rest of the IBug nomination methods, and add some more tests. |
1629 |
def canBeNominatedFor(self, target): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1630 |
"""See `IBug`."""
|
|
3614.1.68
by Brad Bollenbach
reapply MaloneReleaseManagement |
1631 |
try: |
|
14540.3.1
by Ian Booth
Allow a declined bug nomination to be re-nominated |
1632 |
nomination = self.getNominationFor(target) |
|
3614.1.68
by Brad Bollenbach
reapply MaloneReleaseManagement |
1633 |
except NotFoundError: |
|
3614.1.71
by Brad Bollenbach
convert BugNominationView to a LaunchpadFormView. add and test duplicate nomination error handling for the web UI. |
1634 |
# No nomination exists. Let's see if the bug is already
|
|
9206.3.12
by William Grant
Export the rest of the IBug nomination methods, and add some more tests. |
1635 |
# directly targeted to this nomination target.
|
1636 |
if IDistroSeries.providedBy(target): |
|
|
9206.3.14
by William Grant
Forbid nomination for a series if the series' pillar has no task. |
1637 |
series_getter = operator.attrgetter("distroseries") |
1638 |
pillar_getter = operator.attrgetter("distribution") |
|
|
9206.3.12
by William Grant
Export the rest of the IBug nomination methods, and add some more tests. |
1639 |
elif IProductSeries.providedBy(target): |
|
9206.3.14
by William Grant
Forbid nomination for a series if the series' pillar has no task. |
1640 |
series_getter = operator.attrgetter("productseries") |
1641 |
pillar_getter = operator.attrgetter("product") |
|
|
3614.1.71
by Brad Bollenbach
convert BugNominationView to a LaunchpadFormView. add and test duplicate nomination error handling for the web UI. |
1642 |
else: |
|
9206.3.13
by William Grant
Refuse to nominate if canBeNominatedFor fails, and make that return false if a non-series is given. |
1643 |
return False |
|
3614.1.71
by Brad Bollenbach
convert BugNominationView to a LaunchpadFormView. add and test duplicate nomination error handling for the web UI. |
1644 |
|
1645 |
for task in self.bugtasks: |
|
|
9206.3.14
by William Grant
Forbid nomination for a series if the series' pillar has no task. |
1646 |
if series_getter(task) == target: |
|
3614.1.71
by Brad Bollenbach
convert BugNominationView to a LaunchpadFormView. add and test duplicate nomination error handling for the web UI. |
1647 |
# The bug is already targeted at this
|
|
9206.3.12
by William Grant
Export the rest of the IBug nomination methods, and add some more tests. |
1648 |
# nomination target.
|
|
3614.1.71
by Brad Bollenbach
convert BugNominationView to a LaunchpadFormView. add and test duplicate nomination error handling for the web UI. |
1649 |
return False |
1650 |
||
1651 |
# No nomination or tasks are targeted at this
|
|
|
9206.3.14
by William Grant
Forbid nomination for a series if the series' pillar has no task. |
1652 |
# nomination target. But we also don't want to nominate for a
|
1653 |
# series of a product or distro for which we don't have a
|
|
1654 |
# plain pillar task.
|
|
1655 |
for task in self.bugtasks: |
|
1656 |
if pillar_getter(task) == pillar_getter(target): |
|
1657 |
return True |
|
1658 |
||
1659 |
# No tasks match the candidate's pillar. We must refuse.
|
|
1660 |
return False |
|
|
3614.1.71
by Brad Bollenbach
convert BugNominationView to a LaunchpadFormView. add and test duplicate nomination error handling for the web UI. |
1661 |
else: |
|
14540.3.4
by Ian Booth
Fix tests |
1662 |
# The bug may be already nominated for this nomination target.
|
|
14540.3.1
by Ian Booth
Allow a declined bug nomination to be re-nominated |
1663 |
# If the status is declined, the bug can be renominated, else
|
1664 |
# return False
|
|
|
14540.3.4
by Ian Booth
Fix tests |
1665 |
if nomination: |
1666 |
return nomination.status == BugNominationStatus.DECLINED |
|
1667 |
return False |
|
|
3614.1.68
by Brad Bollenbach
reapply MaloneReleaseManagement |
1668 |
|
|
9206.3.12
by William Grant
Export the rest of the IBug nomination methods, and add some more tests. |
1669 |
def getNominationFor(self, target): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1670 |
"""See `IBug`."""
|
|
9206.3.12
by William Grant
Export the rest of the IBug nomination methods, and add some more tests. |
1671 |
if IDistroSeries.providedBy(target): |
1672 |
filter_args = dict(distroseriesID=target.id) |
|
|
9206.3.16
by William Grant
Don't crash if a nomination for a non-series is requested. |
1673 |
elif IProductSeries.providedBy(target): |
|
9206.3.12
by William Grant
Export the rest of the IBug nomination methods, and add some more tests. |
1674 |
filter_args = dict(productseriesID=target.id) |
|
15968.3.1
by j.c.sackett
Simple fix in place. |
1675 |
elif ISourcePackage.providedBy(target): |
1676 |
filter_args = dict(distroseriesID=target.series.id) |
|
|
9206.3.16
by William Grant
Don't crash if a nomination for a non-series is requested. |
1677 |
else: |
1678 |
return None |
|
|
3614.1.68
by Brad Bollenbach
reapply MaloneReleaseManagement |
1679 |
|
1680 |
nomination = BugNomination.selectOneBy(bugID=self.id, **filter_args) |
|
1681 |
||
1682 |
if nomination is None: |
|
1683 |
raise NotFoundError( |
|
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1684 |
"Bug #%d is not nominated for %s." % ( |
|
9206.3.12
by William Grant
Export the rest of the IBug nomination methods, and add some more tests. |
1685 |
self.id, target.displayname)) |
|
3614.1.68
by Brad Bollenbach
reapply MaloneReleaseManagement |
1686 |
|
1687 |
return nomination |
|
1688 |
||
|
6291.1.2
by Bjorn Tillenius
get rid of all the repeated BugNomination queries. |
1689 |
def getNominations(self, target=None, nominations=None): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1690 |
"""See `IBug`."""
|
|
3614.1.68
by Brad Bollenbach
reapply MaloneReleaseManagement |
1691 |
# Define the function used as a sort key.
|
|
4002.7.32
by Matthew Paul Thomas
Renames bugtargetname to bugtargetdisplayname. |
1692 |
def by_bugtargetdisplayname(nomination): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1693 |
"""Return the friendly sort key verson of displayname."""
|
|
4002.7.32
by Matthew Paul Thomas
Renames bugtargetname to bugtargetdisplayname. |
1694 |
return nomination.target.bugtargetdisplayname.lower() |
|
3614.1.68
by Brad Bollenbach
reapply MaloneReleaseManagement |
1695 |
|
|
6291.1.2
by Bjorn Tillenius
get rid of all the repeated BugNomination queries. |
1696 |
if nominations is None: |
1697 |
nominations = BugNomination.selectBy(bugID=self.id) |
|
|
3614.1.68
by Brad Bollenbach
reapply MaloneReleaseManagement |
1698 |
if IProduct.providedBy(target): |
1699 |
filtered_nominations = [] |
|
1700 |
for nomination in shortlist(nominations): |
|
1701 |
if (nomination.productseries and |
|
1702 |
nomination.productseries.product == target): |
|
1703 |
filtered_nominations.append(nomination) |
|
1704 |
nominations = filtered_nominations |
|
1705 |
elif IDistribution.providedBy(target): |
|
1706 |
filtered_nominations = [] |
|
1707 |
for nomination in shortlist(nominations): |
|
|
4285.2.1
by Mark Shuttleworth
Massive renaming of distrorelease to distroseries |
1708 |
if (nomination.distroseries and |
1709 |
nomination.distroseries.distribution == target): |
|
|
3614.1.68
by Brad Bollenbach
reapply MaloneReleaseManagement |
1710 |
filtered_nominations.append(nomination) |
1711 |
nominations = filtered_nominations |
|
1712 |
||
|
4002.7.32
by Matthew Paul Thomas
Renames bugtargetname to bugtargetdisplayname. |
1713 |
return sorted(nominations, key=by_bugtargetdisplayname) |
|
3614.1.68
by Brad Bollenbach
reapply MaloneReleaseManagement |
1714 |
|
|
3691.209.1
by Bjorn Tillenius
add IBug.getBugWatch |
1715 |
def getBugWatch(self, bugtracker, remote_bug): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1716 |
"""See `IBug`."""
|
|
5613.1.5
by Graham Binns
Bug.getBugWatch() now always return None for EMAILADDRESS bug trackers. |
1717 |
# If the bug tracker is of BugTrackerType.EMAILADDRESS we can
|
1718 |
# never tell if a bug is already being watched upstream, since
|
|
1719 |
# the remotebug field for such bug watches contains either '' or
|
|
1720 |
# an RFC822 message ID. In these cases, then, we always return
|
|
1721 |
# None for the sake of sanity.
|
|
|
5613.1.11
by Graham Binns
Fixed a very. very, very stupid bug. |
1722 |
if bugtracker.bugtrackertype == BugTrackerType.EMAILADDRESS: |
1723 |
return None |
|
|
5613.1.5
by Graham Binns
Bug.getBugWatch() now always return None for EMAILADDRESS bug trackers. |
1724 |
|
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1725 |
# XXX: BjornT 2006-10-11:
|
1726 |
# This matching is a bit fragile, since bugwatch.remotebug
|
|
1727 |
# is a user editable text string. We should improve the
|
|
1728 |
# matching so that for example '#42' matches '42' and so on.
|
|
|
3691.209.6
by Bjorn Tillenius
review comments. |
1729 |
return BugWatch.selectFirstBy( |
|
5821.2.57
by James Henstridge
more changes to resolve failing tests. |
1730 |
bug=self, bugtracker=bugtracker, remotebug=str(remote_bug), |
|
3691.209.6
by Bjorn Tillenius
review comments. |
1731 |
orderBy='id') |
|
3691.209.1
by Bjorn Tillenius
add IBug.getBugWatch |
1732 |
|
|
4053.1.5
by Bjorn Tillenius
add IBug.setStatus to make it easier to modify target's bug status. |
1733 |
def setStatus(self, target, status, user): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1734 |
"""See `IBug`."""
|
|
4053.1.5
by Bjorn Tillenius
add IBug.setStatus to make it easier to modify target's bug status. |
1735 |
bugtask = self.getBugTask(target) |
1736 |
if bugtask is None: |
|
1737 |
if IProductSeries.providedBy(target): |
|
1738 |
bugtask = self.getBugTask(target.product) |
|
1739 |
elif ISourcePackage.providedBy(target): |
|
|
4285.2.1
by Mark Shuttleworth
Massive renaming of distrorelease to distroseries |
1740 |
current_distro_series = target.distribution.currentseries |
1741 |
current_package = current_distro_series.getSourcePackage( |
|
|
4053.1.5
by Bjorn Tillenius
add IBug.setStatus to make it easier to modify target's bug status. |
1742 |
target.sourcepackagename.name) |
1743 |
if self.getBugTask(current_package) is not None: |
|
|
4285.2.1
by Mark Shuttleworth
Massive renaming of distrorelease to distroseries |
1744 |
# The bug is targeted to the current series, don't
|
|
4053.1.5
by Bjorn Tillenius
add IBug.setStatus to make it easier to modify target's bug status. |
1745 |
# fall back on the general distribution task.
|
1746 |
return None |
|
1747 |
distro_package = target.distribution.getSourcePackage( |
|
1748 |
target.sourcepackagename.name) |
|
1749 |
bugtask = self.getBugTask(distro_package) |
|
1750 |
else: |
|
1751 |
return None |
|
1752 |
||
1753 |
if bugtask is None: |
|
1754 |
return None |
|
1755 |
||
1756 |
if bugtask.conjoined_master is not None: |
|
1757 |
bugtask = bugtask.conjoined_master |
|
1758 |
||
|
5343.1.1
by Bjorn Tillenius
fix Bug.setStatus() not to return the bugtask if it wasn't edited. |
1759 |
if bugtask.status == status: |
1760 |
return None |
|
1761 |
||
|
4053.1.5
by Bjorn Tillenius
add IBug.setStatus to make it easier to modify target's bug status. |
1762 |
bugtask_before_modification = Snapshot( |
1763 |
bugtask, providing=providedBy(bugtask)) |
|
|
4318.3.12
by Gavin Panella
Changing transitionToStatus to accept user argument, part 3. |
1764 |
bugtask.transitionToStatus(status, user) |
|
7876.3.6
by Francis J. Lacoste
Used ISQLObject from lazr.lifecycle |
1765 |
notify(ObjectModifiedEvent( |
|
5343.1.1
by Bjorn Tillenius
fix Bug.setStatus() not to return the bugtask if it wasn't edited. |
1766 |
bugtask, bugtask_before_modification, ['status'], user=user)) |
|
4053.1.5
by Bjorn Tillenius
add IBug.setStatus to make it easier to modify target's bug status. |
1767 |
|
1768 |
return bugtask |
|
1769 |
||
|
14986.1.6
by Steve Kowalik
Fix ident. |
1770 |
def setPrivate(self, private, who): |
|
13994.2.1
by Ian Booth
Implement new subscription behaviour |
1771 |
"""See `IBug`.
|
1772 |
||
1773 |
We also record who made the change and when the change took
|
|
1774 |
place.
|
|
1775 |
"""
|
|
|
14986.1.4
by Steve Kowalik
Delete IBug.setPrivacyAndSecurityRelated(), replace all uses. |
1776 |
return self.transitionToInformationType( |
1777 |
convert_to_information_type(private, self.security_related), who) |
|
|
13994.2.1
by Ian Booth
Implement new subscription behaviour |
1778 |
|
1779 |
def setSecurityRelated(self, security_related, who): |
|
1780 |
"""Setter for the `security_related` property."""
|
|
|
14986.1.4
by Steve Kowalik
Delete IBug.setPrivacyAndSecurityRelated(), replace all uses. |
1781 |
return self.transitionToInformationType( |
1782 |
convert_to_information_type(self.private, security_related), who) |
|
|
13994.2.1
by Ian Booth
Implement new subscription behaviour |
1783 |
|
|
15693.3.3
by William Grant
Add Bug.getAllowedInformationTypes, like Branch.getAllowedInformationTypes. |
1784 |
def getAllowedInformationTypes(self, who): |
1785 |
"""See `IBug`."""
|
|
1786 |
types = set(InformationType.items) |
|
1787 |
for pillar in self.affected_pillars: |
|
1788 |
types.intersection_update( |
|
1789 |
set(pillar.getAllowedBugInformationTypes())) |
|
|
15970.2.2
by Ian Booth
Introduce forbidden sharing policies and disallow branch creation and bug filing when policy is forbidden |
1790 |
types.add(self.information_type) |
|
15693.3.3
by William Grant
Add Bug.getAllowedInformationTypes, like Branch.getAllowedInformationTypes. |
1791 |
return types |
1792 |
||
|
15840.1.2
by Curtis Hovey
Allow bugs to be set Proprietary over the API. |
1793 |
def transitionToInformationType(self, information_type, who): |
|
14986.1.1
by Steve Kowalik
Change CreateBugParams to take information_type, and drop IBug._{private,security_related}. |
1794 |
"""See `IBug`."""
|
|
14986.1.9
by Steve Kowalik
Destroy use of is comparing against InformationType, expand some SQL commands to use the enum directly, hopefully clear up mail handling, and make use of |
1795 |
if self.information_type == information_type: |
|
14986.1.1
by Steve Kowalik
Change CreateBugParams to take information_type, and drop IBug._{private,security_related}. |
1796 |
return False |
|
15866.1.3
by William Grant
Bug.transitionToInformationType now respects bug_sharing_policy. |
1797 |
if information_type not in self.getAllowedInformationTypes(who): |
1798 |
raise CannotChangeInformationType("Forbidden by project policy.") |
|
|
15866.1.5
by William Grant
Extend the single-pillar Proprietary bug checks to Embargoed too. |
1799 |
if (information_type in PROPRIETARY_INFORMATION_TYPES |
1800 |
and len(self.affected_pillars) > 1): |
|
|
15866.1.2
by William Grant
Bug.transitionToInformationType now uses CannotChangeInformationType, rather than the misleading BugCannotBePrivate. |
1801 |
raise CannotChangeInformationType( |
1802 |
"Proprietary bugs can only affect one project.") |
|
|
14986.1.1
by Steve Kowalik
Change CreateBugParams to take information_type, and drop IBug._{private,security_related}. |
1803 |
if information_type in PRIVATE_INFORMATION_TYPES: |
1804 |
self.who_made_private = who |
|
1805 |
self.date_made_private = UTC_NOW |
|
1806 |
else: |
|
1807 |
self.who_made_private = None |
|
1808 |
self.date_made_private = None |
|
1809 |
# XXX: This should be a bulk update. RBC 20100827
|
|
1810 |
# bug=https://bugs.launchpad.net/storm/+bug/625071
|
|
1811 |
for attachment in self.attachments_unpopulated: |
|
1812 |
attachment.libraryfile.restricted = ( |
|
1813 |
information_type in PRIVATE_INFORMATION_TYPES) |
|
|
15124.2.1
by j.c.sackett
Unrevert the previous branch. |
1814 |
|
|
15721.3.2
by j.c.sackett
Moved the actual transition. |
1815 |
self.information_type = information_type |
|
15721.3.5
by j.c.sackett
Reordered operations in transition. |
1816 |
self._reconcileAccess() |
|
15721.3.3
by j.c.sackett
Some cleanup. |
1817 |
|
|
16994.2.1
by William Grant
Stop subscribing the pillar driver, bug supervisor and/or owner (except for an Ubuntu special case, because why not) when a bug transitions to USERDATA. We have +sharing now, so direct subscriptions aren't necessary to retain access. |
1818 |
# If the new type is private, some people may no longer have
|
1819 |
# access to the bug. We ensure that the bug reporter and the
|
|
1820 |
# person changing the privacy can see the bug, and then remove
|
|
1821 |
# subscriptions for anyone who can't see it any more.
|
|
|
15424.1.2
by Ian Booth
New findPeopleWithoutAccess service methods, ensure all subscribers are granted access to a bug when it becomes private |
1822 |
if information_type in PRIVATE_INFORMATION_TYPES: |
|
16994.2.1
by William Grant
Stop subscribing the pillar driver, bug supervisor and/or owner (except for an Ubuntu special case, because why not) when a bug transitions to USERDATA. We have +sharing now, so direct subscriptions aren't necessary to retain access. |
1823 |
service = getUtility(IService, 'sharing') |
1824 |
for person in (who, self.owner): |
|
|
17332.6.19
by Colin Watson
Add sharing service support for Git repositories. |
1825 |
bugs, _, _, _ = service.getVisibleArtifacts( |
|
16994.2.1
by William Grant
Stop subscribing the pillar driver, bug supervisor and/or owner (except for an Ubuntu special case, because why not) when a bug transitions to USERDATA. We have +sharing now, so direct subscriptions aren't necessary to retain access. |
1826 |
person, bugs=[self], ignore_permissions=True) |
1827 |
if not bugs: |
|
1828 |
# subscribe() isn't sufficient if a subscription
|
|
1829 |
# already exists, as it will do nothing even if
|
|
1830 |
# there is no corresponding access grant. We
|
|
1831 |
# explicitly ensureAccessGrants() to ensure that the
|
|
1832 |
# subscription is retained.
|
|
|
15627.2.1
by Ian Booth
Ensure transitionToInformationType() works for branches without subscribers |
1833 |
service.ensureAccessGrants( |
|
16994.2.1
by William Grant
Stop subscribing the pillar driver, bug supervisor and/or owner (except for an Ubuntu special case, because why not) when a bug transitions to USERDATA. We have +sharing now, so direct subscriptions aren't necessary to retain access. |
1834 |
[person], who, bugs=[self], ignore_permissions=True) |
1835 |
self.subscribe(person, who) |
|
|
15424.1.2
by Ian Booth
New findPeopleWithoutAccess service methods, ensure all subscribers are granted access to a bug when it becomes private |
1836 |
|
|
16994.2.1
by William Grant
Stop subscribing the pillar driver, bug supervisor and/or owner (except for an Ubuntu special case, because why not) when a bug transitions to USERDATA. We have +sharing now, so direct subscriptions aren't necessary to retain access. |
1837 |
# And now fire off a job to remove any subscribers that can
|
1838 |
# no longer see the bug.
|
|
1839 |
getUtility(IRemoveArtifactSubscriptionsJobSource).create( |
|
1840 |
who, [self]) |
|
|
15721.3.10
by j.c.sackett
Shut up, lint. |
1841 |
|
|
16146.2.1
by Ian Booth
Introduce bulk update operations for bugs with duplicates affecting users and bug heat updates |
1842 |
update_bug_heat([self.id]) |
|
14986.1.1
by Steve Kowalik
Change CreateBugParams to take information_type, and drop IBug._{private,security_related}. |
1843 |
return True |
1844 |
||
|
4053.1.1
by Bjorn Tillenius
add tests, and move getBugTask to IBug. |
1845 |
def getBugTask(self, target): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
1846 |
"""See `IBug`."""
|
|
4053.1.1
by Bjorn Tillenius
add tests, and move getBugTask to IBug. |
1847 |
for bugtask in self.bugtasks: |
1848 |
if bugtask.target == target: |
|
1849 |
return bugtask |
|
1850 |
||
1851 |
return None |
|
1852 |
||
|
3691.40.1
by Bjorn Tillenius
add a tags attribute to IBug. |
1853 |
def _getTags(self): |
|
3691.40.17
by Bjorn Tillenius
apply review comments. |
1854 |
"""Get the tags as a sorted list of strings."""
|
|
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
1855 |
return self._cached_tags |
1856 |
||
1857 |
@cachedproperty
|
|
1858 |
def _cached_tags(self): |
|
1859 |
return list(Store.of(self).find( |
|
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
1860 |
BugTag.tag, BugTag.bugID == self.id).order_by(BugTag.tag)) |
|
3691.40.1
by Bjorn Tillenius
add a tags attribute to IBug. |
1861 |
|
1862 |
def _setTags(self, tags): |
|
1863 |
"""Set the tags from a list of strings."""
|
|
|
14130.1.1
by Benji York
change bug status queries to fully utilize the new flavors of incomplete status |
1864 |
# Sets provide an easy way to get the difference between the old and
|
1865 |
# new tags.
|
|
|
3691.40.17
by Bjorn Tillenius
apply review comments. |
1866 |
new_tags = set([tag.lower() for tag in tags]) |
1867 |
old_tags = set(self.tags) |
|
|
14130.1.4
by Benji York
move the tag cache clearing to a point at which the cache won't be repopulated incorrectly |
1868 |
# The cache will be stale after we add/remove tags, clear it.
|
1869 |
del get_property_cache(self)._cached_tags |
|
|
14130.1.1
by Benji York
change bug status queries to fully utilize the new flavors of incomplete status |
1870 |
# Find the set of tags that are to be removed and remove them.
|
|
3691.40.17
by Bjorn Tillenius
apply review comments. |
1871 |
removed_tags = old_tags.difference(new_tags) |
1872 |
for removed_tag in removed_tags: |
|
|
3691.62.21
by kiko
Clean up the use of ID/.id in select*By and constructors |
1873 |
tag = BugTag.selectOneBy(bug=self, tag=removed_tag) |
|
3691.40.1
by Bjorn Tillenius
add a tags attribute to IBug. |
1874 |
tag.destroySelf() |
|
14130.1.1
by Benji York
change bug status queries to fully utilize the new flavors of incomplete status |
1875 |
# Find the set of tags that are to be added and add them.
|
1876 |
added_tags = new_tags.difference(old_tags) |
|
|
3691.40.17
by Bjorn Tillenius
apply review comments. |
1877 |
for added_tag in added_tags: |
1878 |
BugTag(bug=self, tag=added_tag) |
|
|
14130.1.1
by Benji York
change bug status queries to fully utilize the new flavors of incomplete status |
1879 |
# Write all pending changes to the DB, including any pending non-tag
|
1880 |
# changes.
|
|
|
5821.2.49
by James Henstridge
Add a manual flush in _setTags() so that the tags change is visible in |
1881 |
Store.of(self).flush() |
|
3691.40.1
by Bjorn Tillenius
add a tags attribute to IBug. |
1882 |
|
1883 |
tags = property(_getTags, _setTags) |
|
|
1670
by Canonical.com Patch Queue Manager
Big lot of database clean-up r=stub except for resolution of conflicts. |
1884 |
|
|
6856.2.16
by Gavin Panella
Move getBugTasksByPackageName from BugTask to Bug. Discussed with BjornT. |
1885 |
@staticmethod
|
1886 |
def getBugTasksByPackageName(bugtasks): |
|
1887 |
"""See IBugTask."""
|
|
1888 |
bugtasks_by_package = {} |
|
1889 |
for bugtask in bugtasks: |
|
1890 |
bugtasks_by_package.setdefault(bugtask.sourcepackagename, []) |
|
1891 |
bugtasks_by_package[bugtask.sourcepackagename].append(bugtask) |
|
1892 |
return bugtasks_by_package |
|
1893 |
||
|
7030.5.3
by Tom Berger
changes following a review by sinzui |
1894 |
def _getAffectedUser(self, user): |
|
6995.1.23
by Bjorn Tillenius
fix lint warnings. |
1895 |
"""Return the `IBugAffectsPerson` for a user, or None
|
|
7030.5.3
by Tom Berger
changes following a review by sinzui |
1896 |
|
|
6995.1.23
by Bjorn Tillenius
fix lint warnings. |
1897 |
:param user: An `IPerson` that may be affected by the bug.
|
1898 |
:return: An `IBugAffectsPerson` or None.
|
|
1899 |
"""
|
|
|
10015.2.3
by Gavin Panella
Bug.isUserAffected(None) should return None. |
1900 |
if user is None: |
1901 |
return None |
|
1902 |
else: |
|
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
1903 |
return Store.of(self).get(BugAffectsPerson, (self.id, user.id)) |
|
7030.5.3
by Tom Berger
changes following a review by sinzui |
1904 |
|
|
7030.5.1
by Tom Berger
model for bug affects user |
1905 |
def isUserAffected(self, user): |
1906 |
"""See `IBug`."""
|
|
|
7106.1.1
by Tom Berger
record both affected an unaffected users |
1907 |
bap = self._getAffectedUser(user) |
1908 |
if bap is not None: |
|
1909 |
return bap.affected |
|
1910 |
else: |
|
1911 |
return None |
|
|
7030.5.1
by Tom Berger
model for bug affects user |
1912 |
|
|
7030.5.7
by Tom Berger
extract common code to helper method |
1913 |
def _flushAndInvalidate(self): |
1914 |
"""Flush all changes to the store and re-read `self` from the DB."""
|
|
1915 |
store = Store.of(self) |
|
1916 |
store.flush() |
|
1917 |
store.invalidate(self) |
|
1918 |
||
|
13445.1.4
by Gary Poster
add bugtask.transitionToTarget auto-confirm behavior. |
1919 |
def shouldConfirmBugtasks(self): |
|
14918.2.3
by Steve Kowalik
Fix IHasLinkedBranches fun, drop IBugAdmin, and sort out interestingness between the model and interfaces. |
1920 |
"""See `IBug`."""
|
|
13445.1.1
by Gary Poster
bug._shouldConfirmBugtasks added |
1921 |
# == 2 would probably be sufficient once we have all legacy bug tasks
|
1922 |
# confirmed. For now, this is a compromise: we don't need a migration
|
|
1923 |
# step, but we will make some unnecessary comparisons.
|
|
1924 |
return self.users_affected_count_with_dupes > 1 |
|
1925 |
||
|
13916.1.2
by Brad Crittenden
Remove unneeded enum |
1926 |
def maybeConfirmBugtasks(self): |
|
14918.2.1
by Steve Kowalik
Blow up IBug into four classes and massively clean up the ZCML. |
1927 |
"""See `IBug`."""
|
|
13445.1.5
by Gary Poster
markUserAffected now auto confirms |
1928 |
if self.shouldConfirmBugtasks(): |
1929 |
for bugtask in self.bugtasks: |
|
|
13916.1.2
by Brad Crittenden
Remove unneeded enum |
1930 |
bugtask.maybeConfirm() |
|
13445.1.5
by Gary Poster
markUserAffected now auto confirms |
1931 |
|
|
7106.1.1
by Tom Berger
record both affected an unaffected users |
1932 |
def markUserAffected(self, user, affected=True): |
1933 |
"""See `IBug`."""
|
|
1934 |
bap = self._getAffectedUser(user) |
|
1935 |
if bap is None: |
|
1936 |
BugAffectsPerson(bug=self, person=user, affected=affected) |
|
1937 |
else: |
|
1938 |
if bap.affected != affected: |
|
1939 |
bap.affected = affected |
|
|
7675.472.37
by Graham Binns
Merged latest db-devel. |
1940 |
|
|
16146.2.1
by Ian Booth
Introduce bulk update operations for bugs with duplicates affecting users and bug heat updates |
1941 |
dupe_bug_ids = [dupe.id for dupe in self.duplicates] |
1942 |
# Where BugAffectsPerson records already exist for each duplicate,
|
|
1943 |
# update the affected status.
|
|
1944 |
if dupe_bug_ids: |
|
|
16146.2.3
by Ian Booth
Remove new bulk update method and use ResultSet.set() instead |
1945 |
Store.of(self).find( |
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
1946 |
BugAffectsPerson, BugAffectsPerson.person == user, |
|
16146.2.3
by Ian Booth
Remove new bulk update method and use ResultSet.set() instead |
1947 |
BugAffectsPerson.bugID.is_in(dupe_bug_ids), |
1948 |
).set(affected=affected) |
|
|
16155.1.2
by Ian Booth
Do flush in method, not test |
1949 |
for dupe in self.duplicates: |
1950 |
dupe._flushAndInvalidate() |
|
|
16146.2.2
by Ian Booth
Move flush call |
1951 |
self._flushAndInvalidate() |
1952 |
||
|
13445.1.7
by Gary Poster
some small cleanups |
1953 |
if affected: |
|
13916.1.2
by Brad Crittenden
Remove unneeded enum |
1954 |
self.maybeConfirmBugtasks() |
|
13445.1.1
by Gary Poster
bug._shouldConfirmBugtasks added |
1955 |
|
|
16146.2.1
by Ian Booth
Introduce bulk update operations for bugs with duplicates affecting users and bug heat updates |
1956 |
update_bug_heat(dupe_bug_ids + [self.id]) |
|
7030.5.1
by Tom Berger
model for bug affects user |
1957 |
|
|
16146.2.1
by Ian Booth
Introduce bulk update operations for bugs with duplicates affecting users and bug heat updates |
1958 |
def _markAsDuplicate(self, duplicate_of, affected_bug_ids): |
|
12938.1.1
by Abel Deuring
avoid a pointless repetition of calls of BugTarget.recalculateBugHeatCache() in Bug.MarkAsDuplicate() |
1959 |
"""Mark this bug as a duplicate of another.
|
1960 |
||
|
14753.2.2
by William Grant
Drop affected_targets leftovers. |
1961 |
Marking a bug as a duplicate requires a recalculation of the
|
1962 |
heat of this bug and of the master bug. None of this is done
|
|
1963 |
here in order to avoid unnecessary repetitions in recursive
|
|
1964 |
calls for duplicates of this bug, which also become duplicates
|
|
|
12938.1.1
by Abel Deuring
avoid a pointless repetition of calls of BugTarget.recalculateBugHeatCache() in Bug.MarkAsDuplicate() |
1965 |
of the new master bug.
|
1966 |
"""
|
|
|
8040.2.1
by Tom Berger
enforce validation of IBug.duplicateof when accessed via the API by using a mutator |
1967 |
field = DuplicateBug() |
1968 |
field.context = self |
|
|
7675.706.15
by Graham Binns
Hurrah, bug-heat.txt passes |
1969 |
current_duplicateof = self.duplicateof |
|
8040.2.1
by Tom Berger
enforce validation of IBug.duplicateof when accessed via the API by using a mutator |
1970 |
try: |
1971 |
if duplicate_of is not None: |
|
1972 |
field._validate(duplicate_of) |
|
|
11272.1.1
by Deryck Hodge
First pass at getting my dupe finder work that was |
1973 |
if self.duplicates: |
|
13627.2.5
by Brad Crittenden
Ripped out unnecessary code |
1974 |
user = getUtility(ILaunchBag).user |
|
11272.1.1
by Deryck Hodge
First pass at getting my dupe finder work that was |
1975 |
for duplicate in self.duplicates: |
|
13627.2.4
by Brad Crittenden
Do not use object modification event model for duplicate changes which are deferred. |
1976 |
old_value = duplicate.duplicateof |
|
16146.2.1
by Ian Booth
Introduce bulk update operations for bugs with duplicates affecting users and bug heat updates |
1977 |
duplicate._markAsDuplicate(duplicate_of, affected_bug_ids) |
|
13627.2.5
by Brad Crittenden
Ripped out unnecessary code |
1978 |
# Put an entry into the BugNotification table for
|
|
13627.2.4
by Brad Crittenden
Do not use object modification event model for duplicate changes which are deferred. |
1979 |
# later processing.
|
|
13627.2.14
by Brad Crittenden
Removed unneeded DeferredBugDuplicateChange class |
1980 |
change = BugDuplicateChange( |
|
13627.2.4
by Brad Crittenden
Do not use object modification event model for duplicate changes which are deferred. |
1981 |
when=None, person=user, |
1982 |
what_changed='duplicateof', |
|
1983 |
old_value=old_value, |
|
|
13627.2.5
by Brad Crittenden
Ripped out unnecessary code |
1984 |
new_value=duplicate_of) |
1985 |
empty_recipients = BugNotificationRecipients() |
|
|
13627.2.7
by Brad Crittenden
Removed obsolete code, added TestGetDeferredNotifications, mark deferred notifications explicitly with a flag. |
1986 |
duplicate.addChange( |
|
16146.2.1
by Ian Booth
Introduce bulk update operations for bugs with duplicates affecting users and bug heat updates |
1987 |
change, empty_recipients, deferred=True, |
1988 |
update_heat=False) |
|
1989 |
affected_bug_ids.add(duplicate.id) |
|
|
13627.2.4
by Brad Crittenden
Do not use object modification event model for duplicate changes which are deferred. |
1990 |
|
|
8040.2.1
by Tom Berger
enforce validation of IBug.duplicateof when accessed via the API by using a mutator |
1991 |
self.duplicateof = duplicate_of |
|
15523.4.1
by Colin Watson
Use new-style "except Exception as e" syntax rather than "except Exception, e". |
1992 |
except LaunchpadValidationError as validation_error: |
|
16339.1.15
by j.c.sackett
Merged in devel, because this branch will never die. |
1993 |
raise InvalidDuplicateValue(validation_error, already_escaped=True) |
|
7675.472.33
by Graham Binns
Added tests for marking and unmarking dupes. |
1994 |
if duplicate_of is not None: |
|
16146.2.1
by Ian Booth
Introduce bulk update operations for bugs with duplicates affecting users and bug heat updates |
1995 |
affected_bug_ids.add(duplicate_of.id) |
|
13916.1.3
by Brad Crittenden
Remove errant comments |
1996 |
# Maybe confirm bug tasks, now that more people might be affected
|
1997 |
# by this bug from the duplicates.
|
|
|
13916.1.2
by Brad Crittenden
Remove unneeded enum |
1998 |
duplicate_of.maybeConfirmBugtasks() |
|
14835.1.1
by William Grant
Stop forcing dupe heat to 0. They're hidden from searches by default, so it provides no benefit. This also lets getBugsWithOutdatedHeat use the index properly. |
1999 |
|
2000 |
# Update the former duplicateof's heat, as it will have been
|
|
2001 |
# reduced by the unduping.
|
|
2002 |
if current_duplicateof is not None: |
|
|
16146.2.1
by Ian Booth
Introduce bulk update operations for bugs with duplicates affecting users and bug heat updates |
2003 |
affected_bug_ids.add(current_duplicateof.id) |
|
12938.1.1
by Abel Deuring
avoid a pointless repetition of calls of BugTarget.recalculateBugHeatCache() in Bug.MarkAsDuplicate() |
2004 |
|
2005 |
def markAsDuplicate(self, duplicate_of): |
|
2006 |
"""See `IBug`."""
|
|
|
16146.2.1
by Ian Booth
Introduce bulk update operations for bugs with duplicates affecting users and bug heat updates |
2007 |
affected_bug_ids = set() |
2008 |
self._markAsDuplicate(duplicate_of, affected_bug_ids) |
|
2009 |
update_bug_heat(affected_bug_ids) |
|
|
7675.472.33
by Graham Binns
Added tests for marking and unmarking dupes. |
2010 |
|
|
8137.17.24
by Barry Warsaw
thread merge |
2011 |
def setCommentVisibility(self, user, comment_number, visible): |
2012 |
"""See `IBug`."""
|
|
2013 |
bug_message_set = getUtility(IBugMessageSet) |
|
2014 |
bug_message = bug_message_set.getByBugAndMessage( |
|
2015 |
self, self.messages[comment_number]) |
|
|
14302.4.3
by Ian Booth
Add feature flag |
2016 |
|
|
15587.5.1
by j.c.sackett
Removed flag code. |
2017 |
user_owns_comment = (bug_message.owner == user) |
|
14302.4.1
by Ian Booth
Allow users in project roles and comment owners to hide comments |
2018 |
if (not self.userCanSetCommentVisibility(user) |
|
14302.4.3
by Ian Booth
Add feature flag |
2019 |
and not user_owns_comment): |
|
14302.4.1
by Ian Booth
Allow users in project roles and comment owners to hide comments |
2020 |
raise Unauthorized( |
2021 |
"User %s cannot hide or show bug comments" % user.name) |
|
|
14302.4.5
by Ian Booth
Fix test failure |
2022 |
bug_message.message.setVisible(visible) |
|
8137.17.24
by Barry Warsaw
thread merge |
2023 |
|
|
11382.6.25
by Gavin Panella
Convert lp.bugs.model.bug to propertycache. |
2024 |
@cachedproperty
|
|
11307.2.24
by Robert Collins
When returning bugs viewable by a user, cache the fact that that user can see them on the bug. |
2025 |
def _known_viewers(self): |
|
12156.8.20
by Brad Crittenden
Restored pillar owners to userCanView until the proper fix for bug 702429 can be done. |
2026 |
"""A set of known persons able to view this bug.
|
2027 |
||
|
12415.7.1
by Robert Collins
Move pillar owner access rule for bugs to userCanView, removing 75 queries per bug search page. |
2028 |
This method must return an empty set or bug searches will trigger late
|
|
14902.5.27
by Guilherme Salgado
Move the code that looks up work items from getWorkItemsDueBefore() to a new private method (_getSpecificationWorkItemsDueBefore) and write tests for it |
2029 |
evaluation. Any 'should be set on load' properties must be done by the
|
|
12415.7.1
by Robert Collins
Move pillar owner access rule for bugs to userCanView, removing 75 queries per bug search page. |
2030 |
bug search.
|
2031 |
||
2032 |
If you are tempted to change this method, don't. Instead see
|
|
2033 |
userCanView which defines the just-in-time policy for bug visibility,
|
|
2034 |
and BugTask._search which honours visibility rules.
|
|
|
12156.8.20
by Brad Crittenden
Restored pillar owners to userCanView until the proper fix for bug 702429 can be done. |
2035 |
"""
|
|
12415.7.1
by Robert Collins
Move pillar owner access rule for bugs to userCanView, removing 75 queries per bug search page. |
2036 |
return set() |
|
11307.2.24
by Robert Collins
When returning bugs viewable by a user, cache the fact that that user can see them on the bug. |
2037 |
|
|
8486.16.7
by Graham Binns
Renamed isVisibleToUser() -> userCanView(). |
2038 |
def userCanView(self, user): |
|
11307.2.24
by Robert Collins
When returning bugs viewable by a user, cache the fact that that user can see them on the bug. |
2039 |
"""See `IBug`.
|
|
11403.6.5
by Curtis Hovey
merged devel. |
2040 |
|
|
13163.1.1
by Brad Crittenden
Fixed userCanView to handle anonymous users correctly. |
2041 |
This method is called by security adapters but only in the case for
|
2042 |
authenticated users. It is also called in other contexts where the
|
|
2043 |
user may be anonymous.
|
|
|
12156.8.18
by Brad Crittenden
Manage the _known_viewers cached property. Fix tests failing due to an additional db query. |
2044 |
|
|
14205.1.1
by William Grant
Bug.userCanView delegates most logic to get_bug_privacy_filter. |
2045 |
Most logic is delegated to the query provided by
|
2046 |
get_bug_privacy_filter, but some short-circuits and caching are
|
|
2047 |
reimplemented here.
|
|
2048 |
||
|
12156.8.18
by Brad Crittenden
Manage the _known_viewers cached property. Fix tests failing due to an additional db query. |
2049 |
If bug privacy rights are changed here, corresponding changes need
|
2050 |
to be made to the queries which screen for privacy. See
|
|
|
15230.2.7
by William Grant
Drop searchAsUser. |
2051 |
bugtasksearch's get_bug_privacy_filter.
|
|
11307.2.24
by Robert Collins
When returning bugs viewable by a user, cache the fact that that user can see them on the bug. |
2052 |
"""
|
|
15749.1.2
by William Grant
Fix imports. Lots of imports. Oh god, the imports. |
2053 |
from lp.bugs.interfaces.bugtasksearch import BugTaskSearchParams |
|
14758.1.1
by William Grant
Extract most search query builders from BugTaskSet to lp.bugs.model.search. |
2054 |
|
|
8486.16.3
by Graham Binns
Added an isVisibleToUser() method to IBug> |
2055 |
if not self.private: |
2056 |
# This is a public bug.
|
|
2057 |
return True |
|
|
13163.1.1
by Brad Crittenden
Fixed userCanView to handle anonymous users correctly. |
2058 |
# This method may be called for anonymous users. For private bugs
|
2059 |
# always return false for anonymous.
|
|
2060 |
if user is None: |
|
2061 |
return False |
|
2062 |
if user.id in self._known_viewers: |
|
2063 |
return True |
|
2064 |
||
|
15185.3.1
by William Grant
Bug.userCanView is now a bug search. |
2065 |
params = BugTaskSearchParams(user=user, bug=self) |
2066 |
if not getUtility(IBugTaskSet).search(params).is_empty(): |
|
|
14205.1.3
by William Grant
Populate _known_viewers again. |
2067 |
self._known_viewers.add(user.id) |
|
8486.16.3
by Graham Binns
Added an isVisibleToUser() method to IBug> |
2068 |
return True |
|
14205.1.3
by William Grant
Populate _known_viewers again. |
2069 |
return False |
|
8486.16.3
by Graham Binns
Added an isVisibleToUser() method to IBug> |
2070 |
|
|
14302.4.1
by Ian Booth
Allow users in project roles and comment owners to hide comments |
2071 |
def userCanSetCommentVisibility(self, user): |
|
14302.4.2
by Ian Booth
Move interface doc |
2072 |
"""See `IBug`"""
|
|
14302.4.1
by Ian Booth
Allow users in project roles and comment owners to hide comments |
2073 |
if user is None: |
2074 |
return False |
|
|
15608.1.2
by j.c.sackett
Comment visibility update. |
2075 |
# Admins and registry experts always have permission.
|
|
14302.4.1
by Ian Booth
Allow users in project roles and comment owners to hide comments |
2076 |
roles = IPersonRoles(user) |
2077 |
if roles.in_admin or roles.in_registry_experts: |
|
2078 |
return True |
|
|
16115.1.2
by Ian Booth
Provide bulk checkPillarAccess interface to reduce query count on bugtask page |
2079 |
return getUtility(IService, 'sharing').checkPillarAccess( |
2080 |
self.affected_pillars, InformationType.USERDATA, user) |
|
|
14302.4.1
by Ian Booth
Allow users in project roles and comment owners to hide comments |
2081 |
|
|
8486.18.1
by Abel Deuring
Methods added to class Bug to add and remove links between a bug and a HWDB submission. |
2082 |
def linkHWSubmission(self, submission): |
2083 |
"""See `IBug`."""
|
|
2084 |
getUtility(IHWSubmissionBugSet).create(submission, self) |
|
2085 |
||
2086 |
def unlinkHWSubmission(self, submission): |
|
2087 |
"""See `IBug`."""
|
|
2088 |
getUtility(IHWSubmissionBugSet).remove(submission, self) |
|
2089 |
||
2090 |
def getHWSubmissions(self, user=None): |
|
2091 |
"""See `IBug`."""
|
|
2092 |
return getUtility(IHWSubmissionBugSet).submissionsForBug(self, user) |
|
|
1716.3.3
by kiko
Fix for bug 5505: Bug nicknames no longer used. Fixes traversal by implementing an IBugSet.getByNameOrID() method, and using that in places which traverse to bugs |
2093 |
|
|
9939.1.1
by Graham Binns
Added IBug.personIsDirectSubscriber() and personIsSubscribedToDuplicate() methods. |
2094 |
def personIsDirectSubscriber(self, person): |
2095 |
"""See `IBug`."""
|
|
|
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
2096 |
if person in self._subscriber_cache: |
2097 |
return True |
|
2098 |
if person in self._unsubscribed_cache: |
|
2099 |
return False |
|
2100 |
if person is None: |
|
2101 |
return False |
|
|
9939.1.1
by Graham Binns
Added IBug.personIsDirectSubscriber() and personIsSubscribedToDuplicate() methods. |
2102 |
store = Store.of(self) |
2103 |
subscriptions = store.find( |
|
2104 |
BugSubscription, |
|
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
2105 |
BugSubscription.bug == self, BugSubscription.person == person) |
|
9939.1.6
by Graham Binns
Review changes for Gavin. |
2106 |
return not subscriptions.is_empty() |
|
9939.1.1
by Graham Binns
Added IBug.personIsDirectSubscriber() and personIsSubscribedToDuplicate() methods. |
2107 |
|
|
9939.1.2
by Graham Binns
Added implementation and btests for personIsAlsoNotifiedSubscriber(). |
2108 |
def personIsAlsoNotifiedSubscriber(self, person): |
|
9939.1.1
by Graham Binns
Added IBug.personIsDirectSubscriber() and personIsSubscribedToDuplicate() methods. |
2109 |
"""See `IBug`."""
|
|
9939.1.2
by Graham Binns
Added implementation and btests for personIsAlsoNotifiedSubscriber(). |
2110 |
# We have to use getAlsoNotifiedSubscribers() here and iterate
|
2111 |
# over what it returns because "also notified subscribers" is
|
|
|
15920.1.3
by Curtis Hovey
Do not use "bug contact" when "bug supervisor" is meant. |
2112 |
# actually a composite of bug structural subscribers and assignees.
|
2113 |
# As such, it's not possible to get them all with one query.
|
|
|
9939.1.2
by Graham Binns
Added implementation and btests for personIsAlsoNotifiedSubscriber(). |
2114 |
also_notified_subscribers = self.getAlsoNotifiedSubscribers() |
|
12915.3.1
by Brad Crittenden
Fix personIsAlsoNotifiedSubscriber to return True if one of the user's teams has a structural subscription. |
2115 |
if person in also_notified_subscribers: |
2116 |
return True |
|
2117 |
# Otherwise check to see if the person is a member of any of the
|
|
2118 |
# subscribed teams.
|
|
|
13167.1.1
by William Grant
Rollback r13154 for the third time. It breaks bugs with duplicate team subscriptions. Or something. |
2119 |
for subscriber in also_notified_subscribers: |
2120 |
if subscriber.is_team and person.inTeam(subscriber): |
|
2121 |
return True |
|
|
12915.3.1
by Brad Crittenden
Fix personIsAlsoNotifiedSubscriber to return True if one of the user's teams has a structural subscription. |
2122 |
return False |
|
9939.1.1
by Graham Binns
Added IBug.personIsDirectSubscriber() and personIsSubscribedToDuplicate() methods. |
2123 |
|
2124 |
def personIsSubscribedToDuplicate(self, person): |
|
2125 |
"""See `IBug`."""
|
|
|
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
2126 |
if person in self._subscriber_dups_cache: |
2127 |
return True |
|
2128 |
if person in self._unsubscribed_cache: |
|
2129 |
return False |
|
2130 |
if person is None: |
|
2131 |
return False |
|
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
2132 |
return not Store.of(self).find( |
2133 |
BugSubscription, Bug.duplicateof == self, |
|
|
11536.1.1
by Gavin Panella
Convert BugSubscription to Storm. |
2134 |
BugSubscription.bug_id == Bug.id, |
|
16234.1.1
by Steve Kowalik
Also check if the comment and the extra_description will be over 50001 chars. |
2135 |
BugSubscription.person == person).is_empty() |
|
9939.1.1
by Graham Binns
Added IBug.personIsDirectSubscriber() and personIsSubscribedToDuplicate() methods. |
2136 |
|
|
15354.3.13
by William Grant
reconcileAccess -> _reconcileAccess; it's private |
2137 |
def _reconcileAccess(self): |
|
15354.3.15
by William Grant
Bug._reconcileAccess now only calculates the pillars if the bug is private. A minor optimisation. |
2138 |
# reconcile_access_for_artifact will only use the pillar list if
|
2139 |
# the information type is private. But affected_pillars iterates
|
|
2140 |
# over the tasks immediately, which is needless expense for
|
|
2141 |
# public bugs.
|
|
2142 |
if self.information_type in PRIVATE_INFORMATION_TYPES: |
|
2143 |
pillars = self.affected_pillars |
|
2144 |
else: |
|
2145 |
pillars = [] |
|
|
15354.3.1
by William Grant
Extract most of Bug.updateAccessPolicyArtifacts to a generic reconcile_access_for_artifact. |
2146 |
reconcile_access_for_artifact( |
|
15354.3.15
by William Grant
Bug._reconcileAccess now only calculates the pillars if the bug is private. A minor optimisation. |
2147 |
self, self.information_type, pillars) |
|
15323.4.9
by William Grant
updateAccessPolicyArtifacts now handles APAs as it says it does. |
2148 |
|
|
11456.1.6
by Robert Collins
Make the bug attachment API query count be constant. |
2149 |
def _attachments_query(self): |
2150 |
"""Helper for the attachments* properties."""
|
|
2151 |
# bug attachments with no LibraryFileContent have been deleted - the
|
|
2152 |
# garbo_daily run will remove the LibraryFileAlias asynchronously.
|
|
2153 |
# See bug 542274 for more details.
|
|
2154 |
store = Store.of(self) |
|
2155 |
return store.find( |
|
|
12736.8.1
by William Grant
Preload LFCs alongside attachment LFAs. |
2156 |
(BugAttachment, LibraryFileAlias, LibraryFileContent), |
|
11456.1.6
by Robert Collins
Make the bug attachment API query count be constant. |
2157 |
BugAttachment.bug == self, |
|
12736.8.1
by William Grant
Preload LFCs alongside attachment LFAs. |
2158 |
BugAttachment.libraryfileID == LibraryFileAlias.id, |
2159 |
LibraryFileContent.id == LibraryFileAlias.contentID, |
|
2160 |
).order_by(BugAttachment.id) |
|
|
11456.1.6
by Robert Collins
Make the bug attachment API query count be constant. |
2161 |
|
|
10606.5.3
by Abel Deuring
implemented reviewer's comments |
2162 |
@property
|
|
10606.5.4
by Abel Deuring
implemented more reviewer comments |
2163 |
def attachments(self): |
|
11456.1.5
by Robert Collins
Make the bug/attachments API call stop performing O(N^2) work by permitting BugAttachment.message to be an IIndexedMessage, and then making it so from the property the API uses. |
2164 |
"""See `IBug`.
|
|
11382.6.42
by Gavin Panella
Move some more code to propertycache. |
2165 |
|
|
7675.166.301
by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint |
2166 |
This property does eager loading of the index_messages so that
|
2167 |
the API which wants the message_link for the attachment can
|
|
2168 |
answer that without O(N^2) overhead. As such it is moderately
|
|
2169 |
expensive to call (it currently retrieves all messages before
|
|
2170 |
any attachments, and does this when attachments is evaluated,
|
|
2171 |
not when the resultset is processed).
|
|
|
11456.1.5
by Robert Collins
Make the bug/attachments API call stop performing O(N^2) work by permitting BugAttachment.message to be an IIndexedMessage, and then making it so from the property the API uses. |
2172 |
"""
|
2173 |
message_to_indexed = {} |
|
|
11544.1.6
by Robert Collins
review feedback. |
2174 |
for message in self._indexed_messages(include_parents=False): |
|
11456.1.5
by Robert Collins
Make the bug/attachments API call stop performing O(N^2) work by permitting BugAttachment.message to be an IIndexedMessage, and then making it so from the property the API uses. |
2175 |
message_to_indexed[message.id] = message |
|
7675.166.301
by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint |
2176 |
|
|
11456.1.6
by Robert Collins
Make the bug attachment API query count be constant. |
2177 |
def set_indexed_message(row): |
2178 |
attachment = row[0] |
|
2179 |
# row[1] - the LibraryFileAlias is now in the storm cache and
|
|
2180 |
# will be found without a query when dereferenced.
|
|
|
11456.1.5
by Robert Collins
Make the bug/attachments API call stop performing O(N^2) work by permitting BugAttachment.message to be an IIndexedMessage, and then making it so from the property the API uses. |
2181 |
indexed_message = message_to_indexed.get(attachment._messageID) |
2182 |
if indexed_message is not None: |
|
|
11789.2.4
by Gavin Panella
Change all uses of IPropertyCache outside of propertycache.py to get_property_cache. |
2183 |
get_property_cache(attachment).message = indexed_message |
|
11456.1.5
by Robert Collins
Make the bug/attachments API call stop performing O(N^2) work by permitting BugAttachment.message to be an IIndexedMessage, and then making it so from the property the API uses. |
2184 |
return attachment |
|
11456.1.6
by Robert Collins
Make the bug attachment API query count be constant. |
2185 |
rawresults = self._attachments_query() |
|
11456.1.5
by Robert Collins
Make the bug/attachments API call stop performing O(N^2) work by permitting BugAttachment.message to be an IIndexedMessage, and then making it so from the property the API uses. |
2186 |
return DecoratedResultSet(rawresults, set_indexed_message) |
|
10606.5.3
by Abel Deuring
implemented reviewer's comments |
2187 |
|
|
11456.1.3
by Robert Collins
Create a dedicated property for API use for bug attachments. |
2188 |
@property
|
2189 |
def attachments_unpopulated(self): |
|
2190 |
"""See `IBug`.
|
|
|
11382.6.42
by Gavin Panella
Move some more code to propertycache. |
2191 |
|
|
11456.1.9
by Robert Collins
Mention where the explanation for attachments_unpopulated is in the interface, and fix/clarify a couple of typos. |
2192 |
This version does not pre-lookup messages and LibraryFileAliases.
|
|
11382.6.42
by Gavin Panella
Move some more code to propertycache. |
2193 |
|
|
11456.1.3
by Robert Collins
Create a dedicated property for API use for bug attachments. |
2194 |
The regular 'attachments' property does prepopulation because it is
|
2195 |
exposed in the API.
|
|
2196 |
"""
|
|
|
11456.1.9
by Robert Collins
Mention where the explanation for attachments_unpopulated is in the interface, and fix/clarify a couple of typos. |
2197 |
# Grab the attachment only; the LibraryFileAlias will be eager loaded.
|
|
11456.1.6
by Robert Collins
Make the bug attachment API query count be constant. |
2198 |
return DecoratedResultSet( |
2199 |
self._attachments_query(), |
|
2200 |
operator.itemgetter(0)) |
|
|
11456.1.3
by Robert Collins
Create a dedicated property for API use for bug attachments. |
2201 |
|
|
13955.1.1
by Graham Binns
Batched activity now no longer pulls in results that have already been shown. |
2202 |
def getActivityForDateRange(self, start_date, end_date): |
2203 |
"""See `IBug`."""
|
|
2204 |
store = Store.of(self) |
|
2205 |
activity_in_range = store.find( |
|
2206 |
BugActivity, |
|
2207 |
BugActivity.bug == self, |
|
2208 |
BugActivity.datechanged >= start_date, |
|
2209 |
BugActivity.datechanged <= end_date) |
|
2210 |
return activity_in_range |
|
|
8486.16.3
by Graham Binns
Added an isVisibleToUser() method to IBug> |
2211 |
|
|
14047.1.2
by Ian Booth
Lint |
2212 |
|
|
12541.2.5
by Gary Poster
refactor getAlsoNotifiedSubscribers to make it reusable, to use the structuralsubscriber function directly, and to better handle direct subscribers; eliminate duplicated code. |
2213 |
@ProxyFactory
|
2214 |
def get_also_notified_subscribers( |
|
2215 |
bug_or_bugtask, recipients=None, level=None): |
|
2216 |
"""Return the indirect subscribers for a bug or bug task.
|
|
2217 |
||
2218 |
Return the list of people who should get notifications about changes
|
|
2219 |
to the bug or task because of having an indirect subscription
|
|
2220 |
relationship with it (by subscribing to a target, being an assignee
|
|
2221 |
or owner, etc...)
|
|
2222 |
||
2223 |
If `recipients` is present, add the subscribers to the set of
|
|
2224 |
bug notification recipients.
|
|
2225 |
"""
|
|
2226 |
if IBug.providedBy(bug_or_bugtask): |
|
2227 |
bug = bug_or_bugtask |
|
2228 |
bugtasks = bug.bugtasks |
|
|
14494.6.17
by Gavin Panella
Part change get_also_notified_subscribers() to use BugSubscriptionInfo. |
2229 |
info = bug.getSubscriptionInfo(level) |
|
12541.2.5
by Gary Poster
refactor getAlsoNotifiedSubscribers to make it reusable, to use the structuralsubscriber function directly, and to better handle direct subscribers; eliminate duplicated code. |
2230 |
elif IBugTask.providedBy(bug_or_bugtask): |
2231 |
bug = bug_or_bugtask.bug |
|
2232 |
bugtasks = [bug_or_bugtask] |
|
|
14494.6.17
by Gavin Panella
Part change get_also_notified_subscribers() to use BugSubscriptionInfo. |
2233 |
info = bug.getSubscriptionInfo(level).forTask(bug_or_bugtask) |
|
12541.2.5
by Gary Poster
refactor getAlsoNotifiedSubscribers to make it reusable, to use the structuralsubscriber function directly, and to better handle direct subscribers; eliminate duplicated code. |
2234 |
else: |
2235 |
raise ValueError('First argument must be bug or bugtask') |
|
2236 |
||
|
14494.6.17
by Gavin Panella
Part change get_also_notified_subscribers() to use BugSubscriptionInfo. |
2237 |
# Subscribers to exclude.
|
2238 |
exclude_subscribers = frozenset().union( |
|
|
14494.6.46
by Gavin Panella
Change all_direct_subscriptions to direct_subscriptions_at_all_levels and all_direct_subscribers to direct_subscribers_at_all_levels. |
2239 |
info.direct_subscribers_at_all_levels, info.muted_subscribers) |
|
14494.6.35
by Gavin Panella
Update some comments and docstrings. |
2240 |
# Get also-notified subscribers at the given level for the given tasks.
|
|
14494.6.17
by Gavin Panella
Part change get_also_notified_subscribers() to use BugSubscriptionInfo. |
2241 |
also_notified_subscribers = info.also_notified_subscribers |
2242 |
||
2243 |
if recipients is not None: |
|
2244 |
for bugtask in bugtasks: |
|
2245 |
assignee = bugtask.assignee |
|
2246 |
if assignee in also_notified_subscribers: |
|
2247 |
# We have an assignee that is not a direct subscriber.
|
|
|
12541.2.5
by Gary Poster
refactor getAlsoNotifiedSubscribers to make it reusable, to use the structuralsubscriber function directly, and to better handle direct subscribers; eliminate duplicated code. |
2248 |
recipients.addAssignee(bugtask.assignee) |
2249 |
||
2250 |
# This structural subscribers code omits direct subscribers itself.
|
|
|
14494.6.17
by Gavin Panella
Part change get_also_notified_subscribers() to use BugSubscriptionInfo. |
2251 |
# TODO: Pass the info object into get_structural_subscribers for
|
2252 |
# efficiency... or do the recipients stuff here.
|
|
2253 |
structural_subscribers = get_structural_subscribers( |
|
2254 |
bug_or_bugtask, recipients, level, exclude_subscribers) |
|
2255 |
assert also_notified_subscribers.issuperset(structural_subscribers) |
|
|
12541.2.5
by Gary Poster
refactor getAlsoNotifiedSubscribers to make it reusable, to use the structuralsubscriber function directly, and to better handle direct subscribers; eliminate duplicated code. |
2256 |
|
|
14494.6.17
by Gavin Panella
Part change get_also_notified_subscribers() to use BugSubscriptionInfo. |
2257 |
return also_notified_subscribers.sorted |
|
12541.2.5
by Gary Poster
refactor getAlsoNotifiedSubscribers to make it reusable, to use the structuralsubscriber function directly, and to better handle direct subscribers; eliminate duplicated code. |
2258 |
|
2259 |
||
|
11869.17.4
by Gavin Panella
Move load_people() out of BugSubscriptionInfo. |
2260 |
def load_people(*where): |
2261 |
"""Get subscribers from subscriptions.
|
|
2262 |
||
|
11869.17.14
by Gavin Panella
Fix load_people to load people and teams without ValidPersonCache records. |
2263 |
Also preloads `ValidPersonCache` records if they exist.
|
|
11869.17.4
by Gavin Panella
Move load_people() out of BugSubscriptionInfo. |
2264 |
|
2265 |
:param people: An iterable sequence of `Person` IDs.
|
|
2266 |
:return: A `DecoratedResultSet` of `Person` objects. The corresponding
|
|
2267 |
`ValidPersonCache` records are loaded simultaneously.
|
|
2268 |
"""
|
|
|
11869.17.25
by Gavin Panella
Use PersonSet._getPrecachedPersons() because it's awesome. |
2269 |
return PersonSet()._getPrecachedPersons( |
|
14494.6.4
by Gavin Panella
Repair BugSubscriptionInfo so that it works more like it was originally intended, and get it to pre-load preferred email addresses for subscribers. |
2270 |
origin=[Person], conditions=where, need_validity=True, |
2271 |
need_preferred_email=True) |
|
|
11869.17.6
by Gavin Panella
Add *Set classes for subscriptions and subscribers. Disable query checks for now. |
2272 |
|
2273 |
||
|
11869.17.13
by Gavin Panella
Rename SubscriberSet to BugSubscriberSet. |
2274 |
class BugSubscriberSet(frozenset): |
|
11869.17.26
by Gavin Panella
Docstrings for the new *Set classes and their properties. |
2275 |
"""A set of bug subscribers
|
2276 |
||
2277 |
Every member should provide `IPerson`.
|
|
2278 |
"""
|
|
|
11869.17.6
by Gavin Panella
Add *Set classes for subscriptions and subscribers. Disable query checks for now. |
2279 |
|
2280 |
@cachedproperty
|
|
2281 |
def sorted(self): |
|
|
11869.17.26
by Gavin Panella
Docstrings for the new *Set classes and their properties. |
2282 |
"""A sorted tuple of this set's members.
|
2283 |
||
2284 |
Sorted with `person_sort_key`, the default sort key for `Person`.
|
|
2285 |
"""
|
|
|
11869.17.6
by Gavin Panella
Add *Set classes for subscriptions and subscribers. Disable query checks for now. |
2286 |
return tuple(sorted(self, key=person_sort_key)) |
2287 |
||
2288 |
||
2289 |
class BugSubscriptionSet(frozenset): |
|
|
11869.17.26
by Gavin Panella
Docstrings for the new *Set classes and their properties. |
2290 |
"""A set of bug subscriptions."""
|
|
11869.17.6
by Gavin Panella
Add *Set classes for subscriptions and subscribers. Disable query checks for now. |
2291 |
|
2292 |
@cachedproperty
|
|
2293 |
def sorted(self): |
|
|
11869.17.26
by Gavin Panella
Docstrings for the new *Set classes and their properties. |
2294 |
"""A sorted tuple of this set's members.
|
2295 |
||
2296 |
Sorted with `person_sort_key` of the subscription owner.
|
|
2297 |
"""
|
|
|
11869.17.6
by Gavin Panella
Add *Set classes for subscriptions and subscribers. Disable query checks for now. |
2298 |
self.subscribers # Pre-load subscribers. |
2299 |
sort_key = lambda sub: person_sort_key(sub.person) |
|
2300 |
return tuple(sorted(self, key=sort_key)) |
|
2301 |
||
2302 |
@cachedproperty
|
|
2303 |
def subscribers(self): |
|
|
11869.17.26
by Gavin Panella
Docstrings for the new *Set classes and their properties. |
2304 |
"""A `BugSubscriberSet` of the owners of this set's members."""
|
|
11869.17.11
by Gavin Panella
Demonstrate query counts for BugSubscriptionInfo properties. |
2305 |
if len(self) == 0: |
|
11869.17.13
by Gavin Panella
Rename SubscriberSet to BugSubscriberSet. |
2306 |
return BugSubscriberSet() |
|
11869.17.11
by Gavin Panella
Demonstrate query counts for BugSubscriptionInfo properties. |
2307 |
else: |
2308 |
condition = Person.id.is_in( |
|
2309 |
removeSecurityProxy(subscription).person_id |
|
2310 |
for subscription in self) |
|
|
11869.17.13
by Gavin Panella
Rename SubscriberSet to BugSubscriberSet. |
2311 |
return BugSubscriberSet(load_people(condition)) |
|
11869.17.6
by Gavin Panella
Add *Set classes for subscriptions and subscribers. Disable query checks for now. |
2312 |
|
2313 |
||
2314 |
class StructuralSubscriptionSet(frozenset): |
|
|
11869.17.26
by Gavin Panella
Docstrings for the new *Set classes and their properties. |
2315 |
"""A set of structural subscriptions."""
|
|
11869.17.6
by Gavin Panella
Add *Set classes for subscriptions and subscribers. Disable query checks for now. |
2316 |
|
2317 |
@cachedproperty
|
|
2318 |
def sorted(self): |
|
|
11869.17.26
by Gavin Panella
Docstrings for the new *Set classes and their properties. |
2319 |
"""A sorted tuple of this set's members.
|
2320 |
||
2321 |
Sorted with `person_sort_key` of the subscription owner.
|
|
2322 |
"""
|
|
|
11869.17.6
by Gavin Panella
Add *Set classes for subscriptions and subscribers. Disable query checks for now. |
2323 |
self.subscribers # Pre-load subscribers. |
2324 |
sort_key = lambda sub: person_sort_key(sub.subscriber) |
|
2325 |
return tuple(sorted(self, key=sort_key)) |
|
2326 |
||
2327 |
@cachedproperty
|
|
2328 |
def subscribers(self): |
|
|
11869.17.26
by Gavin Panella
Docstrings for the new *Set classes and their properties. |
2329 |
"""A `BugSubscriberSet` of the owners of this set's members."""
|
|
11869.17.11
by Gavin Panella
Demonstrate query counts for BugSubscriptionInfo properties. |
2330 |
if len(self) == 0: |
|
11869.17.13
by Gavin Panella
Rename SubscriberSet to BugSubscriberSet. |
2331 |
return BugSubscriberSet() |
|
11869.17.11
by Gavin Panella
Demonstrate query counts for BugSubscriptionInfo properties. |
2332 |
else: |
2333 |
condition = Person.id.is_in( |
|
2334 |
removeSecurityProxy(subscription).subscriberID |
|
2335 |
for subscription in self) |
|
|
11869.17.13
by Gavin Panella
Rename SubscriberSet to BugSubscriberSet. |
2336 |
return BugSubscriberSet(load_people(condition)) |
|
11869.17.6
by Gavin Panella
Add *Set classes for subscriptions and subscribers. Disable query checks for now. |
2337 |
|
2338 |
||
|
11869.17.34
by Gavin Panella
Add a bug number to the XXX about Zope checkers. |
2339 |
# XXX: GavinPanella 2010-12-08 bug=694057: Subclasses of frozenset don't
|
2340 |
# appear to be granted those permissions given to frozenset. This would make
|
|
2341 |
# writing ZCML tedious, so I've opted for registering custom checkers (see
|
|
2342 |
# lp_sitecustomize for some other jiggery pokery in the same vein) while I
|
|
2343 |
# seek a better solution.
|
|
|
11869.17.16
by Gavin Panella
Zope security checker hacks to get *Set classes to work. |
2344 |
from zope.security import checker |
2345 |
checker_for_frozen_set = checker.getCheckerForInstancesOf(frozenset) |
|
2346 |
checker_for_subscriber_set = checker.NamesChecker(["sorted"]) |
|
2347 |
checker_for_subscription_set = checker.NamesChecker(["sorted", "subscribers"]) |
|
2348 |
checker.BasicTypes[BugSubscriberSet] = checker.MultiChecker( |
|
2349 |
(checker_for_frozen_set.get_permissions, |
|
2350 |
checker_for_subscriber_set.get_permissions)) |
|
2351 |
checker.BasicTypes[BugSubscriptionSet] = checker.MultiChecker( |
|
2352 |
(checker_for_frozen_set.get_permissions, |
|
2353 |
checker_for_subscription_set.get_permissions)) |
|
2354 |
checker.BasicTypes[StructuralSubscriptionSet] = checker.MultiChecker( |
|
2355 |
(checker_for_frozen_set.get_permissions, |
|
2356 |
checker_for_subscription_set.get_permissions)) |
|
2357 |
||
2358 |
||
|
11869.17.6
by Gavin Panella
Add *Set classes for subscriptions and subscribers. Disable query checks for now. |
2359 |
def freeze(factory): |
2360 |
"""Return a decorator that wraps returned values with `factory`."""
|
|
2361 |
||
2362 |
def decorate(func): |
|
2363 |
"""Decorator that wraps returned values."""
|
|
2364 |
||
2365 |
@wraps(func) |
|
2366 |
def wrapper(*args, **kwargs): |
|
2367 |
return factory(func(*args, **kwargs)) |
|
2368 |
return wrapper |
|
2369 |
||
2370 |
return decorate |
|
|
11869.17.2
by Gavin Panella
New class BugSubscriptionInfo. |
2371 |
|
2372 |
||
|
17610.1.1
by Colin Watson
Switch zope.interface users from class advice to class decorators. |
2373 |
@implementer(IHasBug) |
|
11869.17.2
by Gavin Panella
New class BugSubscriptionInfo. |
2374 |
class BugSubscriptionInfo: |
|
11869.18.15
by Gavin Panella
More documentation for BugSubscriptionInfo. |
2375 |
"""Represents bug subscription sets.
|
2376 |
||
2377 |
The intention for this class is to encapsulate all calculations of
|
|
2378 |
subscriptions and subscribers for a bug. Some design considerations:
|
|
2379 |
||
2380 |
* Immutable.
|
|
2381 |
||
2382 |
* Set-based.
|
|
2383 |
||
2384 |
* Sets are cached.
|
|
2385 |
||
2386 |
* Usable with a *snapshot* of a bug. This is interesting for two reasons:
|
|
2387 |
||
2388 |
- Event subscribers commonly deal with snapshots. An instance of this
|
|
2389 |
class could be added to a custom snapshot so that multiple subscribers
|
|
2390 |
can share the information it contains.
|
|
2391 |
||
2392 |
- Use outside of the web request. A serialized snapshot could be used to
|
|
2393 |
calculate subscribers for a particular bug state. This could help us
|
|
2394 |
to move even more bug mail processing out of the web request.
|
|
2395 |
||
2396 |
"""
|
|
|
11869.17.2
by Gavin Panella
New class BugSubscriptionInfo. |
2397 |
|
2398 |
def __init__(self, bug, level): |
|
2399 |
self.bug = bug |
|
|
14494.6.12
by Gavin Panella
New methods forTask and forLevel on BugSubscriptionInfo. |
2400 |
self.bugtask = None # Implies all. |
|
11869.18.17
by Gavin Panella
Make test_subscribers_from_dupes_uses_level() fail when it should. |
2401 |
assert level is not None |
|
11869.17.2
by Gavin Panella
New class BugSubscriptionInfo. |
2402 |
self.level = level |
|
14494.6.35
by Gavin Panella
Update some comments and docstrings. |
2403 |
# This cache holds related `BugSubscriptionInfo` instances relating to
|
2404 |
# the same bug but with different levels and/or choice of bugtask.
|
|
|
14494.6.12
by Gavin Panella
New methods forTask and forLevel on BugSubscriptionInfo. |
2405 |
self.cache = {self.cache_key: self} |
|
14494.6.20
by Gavin Panella
Flush the bug's store when creating a BugSubscriptionInfo. |
2406 |
# This is often used in event handlers, many of which block implicit
|
2407 |
# flushes. However, the data needs to be in the database for the
|
|
2408 |
# queries herein to give correct answers.
|
|
2409 |
Store.of(bug).flush() |
|
|
14494.6.12
by Gavin Panella
New methods forTask and forLevel on BugSubscriptionInfo. |
2410 |
|
2411 |
@property
|
|
2412 |
def cache_key(self): |
|
|
14494.6.35
by Gavin Panella
Update some comments and docstrings. |
2413 |
"""A (bug ID, bugtask ID, level) tuple for use as a hash key.
|
2414 |
||
2415 |
This helps `forTask()` and `forLevel()` to be more efficient,
|
|
2416 |
returning previously populated instances to avoid running the same
|
|
2417 |
queries against the database again and again.
|
|
2418 |
"""
|
|
|
14494.6.12
by Gavin Panella
New methods forTask and forLevel on BugSubscriptionInfo. |
2419 |
bugtask_id = None if self.bugtask is None else self.bugtask.id |
2420 |
return self.bug.id, bugtask_id, self.level |
|
2421 |
||
2422 |
def forTask(self, bugtask): |
|
2423 |
"""Create a new `BugSubscriptionInfo` limited to `bugtask`.
|
|
2424 |
||
2425 |
The given task must refer to this object's bug. If `None` is passed a
|
|
2426 |
new `BugSubscriptionInfo` instance is returned with no limit.
|
|
2427 |
"""
|
|
2428 |
info = self.__class__(self.bug, self.level) |
|
2429 |
info.bugtask, info.cache = bugtask, self.cache |
|
2430 |
return self.cache.setdefault(info.cache_key, info) |
|
2431 |
||
2432 |
def forLevel(self, level): |
|
2433 |
"""Create a new `BugSubscriptionInfo` limited to `level`."""
|
|
2434 |
info = self.__class__(self.bug, level) |
|
2435 |
info.bugtask, info.cache = self.bugtask, self.cache |
|
2436 |
return self.cache.setdefault(info.cache_key, info) |
|
|
11869.17.2
by Gavin Panella
New class BugSubscriptionInfo. |
2437 |
|
|
11869.17.8
by Gavin Panella
Cache BugSubscriptionInfo's properties. |
2438 |
@cachedproperty
|
|
14494.6.15
by Gavin Panella
New muted_subscribers property, and muting-related stuff. |
2439 |
@freeze(BugSubscriberSet) |
2440 |
def muted_subscribers(self): |
|
2441 |
muted_people = Select(BugMute.person_id, BugMute.bug == self.bug) |
|
2442 |
return load_people(Person.id.is_in(muted_people)) |
|
2443 |
||
|
15745.5.1
by William Grant
Resurrect private structsubs changes so they can be made performant. |
2444 |
def visible_recipients_filter(self, column): |
2445 |
# Circular fail :(
|
|
|
15745.5.4
by William Grant
visible_recipients_filter uses the bulk user filter. |
2446 |
from lp.bugs.model.bugtasksearch import ( |
2447 |
get_bug_bulk_privacy_filter_terms, |
|
2448 |
)
|
|
|
15745.5.1
by William Grant
Resurrect private structsubs changes so they can be made performant. |
2449 |
|
2450 |
if self.bug.private: |
|
|
15745.5.4
by William Grant
visible_recipients_filter uses the bulk user filter. |
2451 |
return get_bug_bulk_privacy_filter_terms(column, self.bug.id) |
|
15745.5.1
by William Grant
Resurrect private structsubs changes so they can be made performant. |
2452 |
else: |
2453 |
return True |
|
2454 |
||
|
14494.6.15
by Gavin Panella
New muted_subscribers property, and muting-related stuff. |
2455 |
@cachedproperty
|
|
13627.2.7
by Brad Crittenden
Removed obsolete code, added TestGetDeferredNotifications, mark deferred notifications explicitly with a flag. |
2456 |
@freeze(BugSubscriptionSet) |
|
14494.6.4
by Gavin Panella
Repair BugSubscriptionInfo so that it works more like it was originally intended, and get it to pre-load preferred email addresses for subscribers. |
2457 |
def direct_subscriptions(self): |
|
14494.6.15
by Gavin Panella
New muted_subscribers property, and muting-related stuff. |
2458 |
"""The bug's direct subscriptions.
|
2459 |
||
2460 |
Excludes muted subscriptions.
|
|
2461 |
"""
|
|
|
13627.2.7
by Brad Crittenden
Removed obsolete code, added TestGetDeferredNotifications, mark deferred notifications explicitly with a flag. |
2462 |
return IStore(BugSubscription).find( |
2463 |
BugSubscription, |
|
2464 |
BugSubscription.bug_notification_level >= self.level, |
|
2465 |
BugSubscription.bug == self.bug, |
|
2466 |
Not(In(BugSubscription.person_id, |
|
|
15683.2.2
by Steve Kowalik
Forgive me for my indentation sins. |
2467 |
Select(BugMute.person_id, BugMute.bug_id == self.bug.id)))) |
|
13627.2.7
by Brad Crittenden
Removed obsolete code, added TestGetDeferredNotifications, mark deferred notifications explicitly with a flag. |
2468 |
|
|
14494.6.4
by Gavin Panella
Repair BugSubscriptionInfo so that it works more like it was originally intended, and get it to pre-load preferred email addresses for subscribers. |
2469 |
@property
|
|
13627.2.1
by Brad Crittenden
Defer notification to subscribers of duplicate bugs when one bug is made a duplicate of another. |
2470 |
def direct_subscribers(self): |
|
14494.6.15
by Gavin Panella
New muted_subscribers property, and muting-related stuff. |
2471 |
"""The bug's direct subscriptions.
|
2472 |
||
2473 |
Excludes muted subscribers.
|
|
2474 |
"""
|
|
|
14494.6.4
by Gavin Panella
Repair BugSubscriptionInfo so that it works more like it was originally intended, and get it to pre-load preferred email addresses for subscribers. |
2475 |
return self.direct_subscriptions.subscribers |
|
13627.2.1
by Brad Crittenden
Defer notification to subscribers of duplicate bugs when one bug is made a duplicate of another. |
2476 |
|
|
14494.6.13
by Gavin Panella
New properties for BugSubscriptionInfo: all_direct_subscriptions and all_direct_subscribers. |
2477 |
@property
|
|
14494.6.46
by Gavin Panella
Change all_direct_subscriptions to direct_subscriptions_at_all_levels and all_direct_subscribers to direct_subscribers_at_all_levels. |
2478 |
def direct_subscriptions_at_all_levels(self): |
|
14494.6.15
by Gavin Panella
New muted_subscribers property, and muting-related stuff. |
2479 |
"""The bug's direct subscriptions at all levels.
|
2480 |
||
2481 |
Excludes muted subscriptions.
|
|
2482 |
"""
|
|
|
14494.6.13
by Gavin Panella
New properties for BugSubscriptionInfo: all_direct_subscriptions and all_direct_subscribers. |
2483 |
return self.forLevel( |
2484 |
BugNotificationLevel.LIFECYCLE).direct_subscriptions |
|
2485 |
||
2486 |
@property
|
|
|
14494.6.46
by Gavin Panella
Change all_direct_subscriptions to direct_subscriptions_at_all_levels and all_direct_subscribers to direct_subscribers_at_all_levels. |
2487 |
def direct_subscribers_at_all_levels(self): |
|
14494.6.15
by Gavin Panella
New muted_subscribers property, and muting-related stuff. |
2488 |
"""The bug's direct subscribers at all levels.
|
2489 |
||
2490 |
Excludes muted subscribers.
|
|
2491 |
"""
|
|
|
14494.6.46
by Gavin Panella
Change all_direct_subscriptions to direct_subscriptions_at_all_levels and all_direct_subscribers to direct_subscribers_at_all_levels. |
2492 |
return self.direct_subscriptions_at_all_levels.subscribers |
|
14494.6.13
by Gavin Panella
New properties for BugSubscriptionInfo: all_direct_subscriptions and all_direct_subscribers. |
2493 |
|
|
13627.2.1
by Brad Crittenden
Defer notification to subscribers of duplicate bugs when one bug is made a duplicate of another. |
2494 |
@cachedproperty
|
|
14494.6.47
by Gavin Panella
Nuke duplicate_subscriptions_and_subscribers because it looks efficient but is actually a potato hole (subscribers will not have anything preloaded). |
2495 |
@freeze(BugSubscriptionSet) |
2496 |
def duplicate_subscriptions(self): |
|
2497 |
"""Subscriptions to duplicates of the bug.
|
|
2498 |
||
|
15745.5.1
by William Grant
Resurrect private structsubs changes so they can be made performant. |
2499 |
Excludes muted subscriptions, and subscribers who can not see the
|
2500 |
master bug.
|
|
|
14494.6.47
by Gavin Panella
Nuke duplicate_subscriptions_and_subscribers because it looks efficient but is actually a potato hole (subscribers will not have anything preloaded). |
2501 |
"""
|
|
15745.5.1
by William Grant
Resurrect private structsubs changes so they can be made performant. |
2502 |
return IStore(BugSubscription).find( |
2503 |
BugSubscription, |
|
2504 |
BugSubscription.bug_notification_level >= self.level, |
|
2505 |
BugSubscription.bug_id == Bug.id, |
|
2506 |
Bug.duplicateof == self.bug, |
|
2507 |
Not(In( |
|
2508 |
BugSubscription.person_id, |
|
2509 |
Select( |
|
2510 |
BugMute.person_id, BugMute.bug_id == Bug.id, |
|
2511 |
tables=[BugMute]))), |
|
2512 |
self.visible_recipients_filter(BugSubscription.person_id)) |
|
|
15683.2.4
by Steve Kowalik
Rename forbidden_recipients_filter() to the actually correct name of |
2513 |
|
|
14494.6.47
by Gavin Panella
Nuke duplicate_subscriptions_and_subscribers because it looks efficient but is actually a potato hole (subscribers will not have anything preloaded). |
2514 |
@property
|
|
13627.2.1
by Brad Crittenden
Defer notification to subscribers of duplicate bugs when one bug is made a duplicate of another. |
2515 |
def duplicate_subscribers(self): |
|
14494.6.15
by Gavin Panella
New muted_subscribers property, and muting-related stuff. |
2516 |
"""Subscribers to duplicates of the bug.
|
2517 |
||
2518 |
Excludes muted subscribers.
|
|
2519 |
"""
|
|
|
14494.6.47
by Gavin Panella
Nuke duplicate_subscriptions_and_subscribers because it looks efficient but is actually a potato hole (subscribers will not have anything preloaded). |
2520 |
return self.duplicate_subscriptions.subscribers |
|
11869.17.2
by Gavin Panella
New class BugSubscriptionInfo. |
2521 |
|
|
11869.17.8
by Gavin Panella
Cache BugSubscriptionInfo's properties. |
2522 |
@cachedproperty
|
|
11869.18.6
by Gavin Panella
New property for BugSubscriptionInfo, duplicate_only_subscriptions. |
2523 |
@freeze(BugSubscriptionSet) |
2524 |
def duplicate_only_subscriptions(self): |
|
|
14494.6.4
by Gavin Panella
Repair BugSubscriptionInfo so that it works more like it was originally intended, and get it to pre-load preferred email addresses for subscribers. |
2525 |
"""Subscriptions to duplicates of the bug only.
|
|
11869.18.6
by Gavin Panella
New property for BugSubscriptionInfo, duplicate_only_subscriptions. |
2526 |
|
|
14494.6.15
by Gavin Panella
New muted_subscribers property, and muting-related stuff. |
2527 |
Excludes muted subscriptions, subscriptions for people who have a
|
2528 |
direct subscription, or who are also notified for another reason.
|
|
|
11869.18.6
by Gavin Panella
New property for BugSubscriptionInfo, duplicate_only_subscriptions. |
2529 |
"""
|
|
13627.2.1
by Brad Crittenden
Defer notification to subscribers of duplicate bugs when one bug is made a duplicate of another. |
2530 |
self.duplicate_subscribers # Pre-load subscribers. |
|
11869.18.6
by Gavin Panella
New property for BugSubscriptionInfo, duplicate_only_subscriptions. |
2531 |
higher_precedence = ( |
|
13627.2.1
by Brad Crittenden
Defer notification to subscribers of duplicate bugs when one bug is made a duplicate of another. |
2532 |
self.direct_subscribers.union( |
|
11869.18.6
by Gavin Panella
New property for BugSubscriptionInfo, duplicate_only_subscriptions. |
2533 |
self.also_notified_subscribers)) |
2534 |
return ( |
|
2535 |
subscription for subscription in self.duplicate_subscriptions |
|
2536 |
if subscription.person not in higher_precedence) |
|
2537 |
||
|
14494.6.4
by Gavin Panella
Repair BugSubscriptionInfo so that it works more like it was originally intended, and get it to pre-load preferred email addresses for subscribers. |
2538 |
@property
|
2539 |
def duplicate_only_subscribers(self): |
|
2540 |
"""Subscribers to duplicates of the bug only.
|
|
2541 |
||
|
14494.6.15
by Gavin Panella
New muted_subscribers property, and muting-related stuff. |
2542 |
Excludes muted subscribers, subscribers who have a direct
|
2543 |
subscription, or who are also notified for another reason.
|
|
|
14494.6.4
by Gavin Panella
Repair BugSubscriptionInfo so that it works more like it was originally intended, and get it to pre-load preferred email addresses for subscribers. |
2544 |
"""
|
2545 |
return self.duplicate_only_subscriptions.subscribers |
|
2546 |
||
|
11869.18.6
by Gavin Panella
New property for BugSubscriptionInfo, duplicate_only_subscriptions. |
2547 |
@cachedproperty
|
|
11869.17.6
by Gavin Panella
Add *Set classes for subscriptions and subscribers. Disable query checks for now. |
2548 |
@freeze(StructuralSubscriptionSet) |
|
11869.17.2
by Gavin Panella
New class BugSubscriptionInfo. |
2549 |
def structural_subscriptions(self): |
|
14494.6.16
by Gavin Panella
New structural_subscribers property, and more muting-related stuff. |
2550 |
"""Structural subscriptions to the bug's targets.
|
2551 |
||
|
14494.6.23
by Gavin Panella
Update BugSubscriptionInfo.structural_* to use new function get_structural_subscriptions(). |
2552 |
Excludes direct subscriptions.
|
|
14494.6.16
by Gavin Panella
New structural_subscribers property, and more muting-related stuff. |
2553 |
"""
|
|
14494.6.23
by Gavin Panella
Update BugSubscriptionInfo.structural_* to use new function get_structural_subscriptions(). |
2554 |
subject = self.bug if self.bugtask is None else self.bugtask |
2555 |
return get_structural_subscriptions(subject, self.level) |
|
|
11869.17.2
by Gavin Panella
New class BugSubscriptionInfo. |
2556 |
|
|
14494.6.23
by Gavin Panella
Update BugSubscriptionInfo.structural_* to use new function get_structural_subscriptions(). |
2557 |
@property
|
|
14494.6.16
by Gavin Panella
New structural_subscribers property, and more muting-related stuff. |
2558 |
def structural_subscribers(self): |
|
14494.6.23
by Gavin Panella
Update BugSubscriptionInfo.structural_* to use new function get_structural_subscriptions(). |
2559 |
"""Structural subscribers to the bug's targets.
|
2560 |
||
2561 |
Excludes direct subscribers.
|
|
2562 |
"""
|
|
2563 |
return self.structural_subscriptions.subscribers |
|
|
14494.6.16
by Gavin Panella
New structural_subscribers property, and more muting-related stuff. |
2564 |
|
2565 |
@cachedproperty
|
|
2566 |
@freeze(BugSubscriberSet) |
|
|
11869.17.2
by Gavin Panella
New class BugSubscriptionInfo. |
2567 |
def all_assignees(self): |
|
14494.6.16
by Gavin Panella
New structural_subscribers property, and more muting-related stuff. |
2568 |
"""Assignees of the bug's tasks.
|
2569 |
||
2570 |
*Does not* exclude muted subscribers.
|
|
2571 |
"""
|
|
|
14494.6.14
by Gavin Panella
Update all_assignees and all_pillar_owners_without_bug_supervisors to take the bugtask into account. |
2572 |
if self.bugtask is None: |
|
15745.5.1
by William Grant
Resurrect private structsubs changes so they can be made performant. |
2573 |
assignees = load_people( |
2574 |
Person.id.is_in(Select(BugTask.assigneeID, |
|
2575 |
BugTask.bug == self.bug))) |
|
2576 |
else: |
|
2577 |
assignees = load_people(Person.id == self.bugtask.assigneeID) |
|
2578 |
if self.bug.private: |
|
2579 |
return IStore(Person).find(Person, |
|
2580 |
Person.id.is_in([a.id for a in assignees]), |
|
2581 |
self.visible_recipients_filter(Person.id)) |
|
2582 |
else: |
|
2583 |
return assignees |
|
|
11869.17.2
by Gavin Panella
New class BugSubscriptionInfo. |
2584 |
|
|
11869.17.8
by Gavin Panella
Cache BugSubscriptionInfo's properties. |
2585 |
@cachedproperty
|
|
11869.17.2
by Gavin Panella
New class BugSubscriptionInfo. |
2586 |
def also_notified_subscribers(self): |
|
14494.6.36
by Gavin Panella
Fix also_notified_subscribers' docstring. |
2587 |
"""All subscribers except direct, dupe, and muted subscribers."""
|
|
15745.5.1
by William Grant
Resurrect private structsubs changes so they can be made performant. |
2588 |
subscribers = BugSubscriberSet().union( |
2589 |
self.structural_subscribers, self.all_assignees) |
|
2590 |
return subscribers.difference( |
|
2591 |
self.direct_subscribers_at_all_levels, |
|
2592 |
self.muted_subscribers) |
|
|
11869.17.2
by Gavin Panella
New class BugSubscriptionInfo. |
2593 |
|
|
11869.17.8
by Gavin Panella
Cache BugSubscriptionInfo's properties. |
2594 |
@cachedproperty
|
|
11869.17.2
by Gavin Panella
New class BugSubscriptionInfo. |
2595 |
def indirect_subscribers(self): |
|
14494.6.16
by Gavin Panella
New structural_subscribers property, and more muting-related stuff. |
2596 |
"""All subscribers except direct subscribers.
|
2597 |
||
2598 |
Excludes muted subscribers.
|
|
2599 |
"""
|
|
|
11869.17.2
by Gavin Panella
New class BugSubscriptionInfo. |
2600 |
return self.also_notified_subscribers.union( |
|
13627.2.1
by Brad Crittenden
Defer notification to subscribers of duplicate bugs when one bug is made a duplicate of another. |
2601 |
self.duplicate_subscribers) |
|
11869.17.2
by Gavin Panella
New class BugSubscriptionInfo. |
2602 |
|
2603 |
||
|
17610.1.1
by Colin Watson
Switch zope.interface users from class advice to class decorators. |
2604 |
@implementer(IBugSet) |
|
2938.1.1
by Bjorn Tillenius
remove unused and untested methods from IBugSet. |
2605 |
class BugSet: |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
2606 |
"""See BugSet."""
|
|
1102
by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs |
2607 |
|
|
1716.3.3
by kiko
Fix for bug 5505: Bug nicknames no longer used. Fixes traversal by implementing an IBugSet.getByNameOrID() method, and using that in places which traverse to bugs |
2608 |
valid_bug_name_re = re.compile(r'''^[a-z][a-z0-9\\+\\.\\-]+$''') |
2609 |
||
|
1309
by Canonical.com Patch Queue Manager
add the rest of the hard bits of implementing bug privacy. grow the |
2610 |
def get(self, bugid): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
2611 |
"""See `IBugSet`."""
|
|
1924
by Canonical.com Patch Queue Manager
make traversing to non-existent bug IDs return a 404 instead of |
2612 |
try: |
2613 |
return Bug.get(bugid) |
|
2614 |
except SQLObjectNotFound: |
|
2615 |
raise NotFoundError( |
|
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
2616 |
"Unable to locate bug with ID %s." % str(bugid)) |
|
1670
by Canonical.com Patch Queue Manager
Big lot of database clean-up r=stub except for resolution of conflicts. |
2617 |
|
|
1716.3.3
by kiko
Fix for bug 5505: Bug nicknames no longer used. Fixes traversal by implementing an IBugSet.getByNameOrID() method, and using that in places which traverse to bugs |
2618 |
def getByNameOrID(self, bugid): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
2619 |
"""See `IBugSet`."""
|
|
1716.3.3
by kiko
Fix for bug 5505: Bug nicknames no longer used. Fixes traversal by implementing an IBugSet.getByNameOrID() method, and using that in places which traverse to bugs |
2620 |
if self.valid_bug_name_re.match(bugid): |
2621 |
bug = Bug.selectOneBy(name=bugid) |
|
2622 |
if bug is None: |
|
2623 |
raise NotFoundError( |
|
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
2624 |
"Unable to locate bug with ID %s." % bugid) |
|
1716.3.3
by kiko
Fix for bug 5505: Bug nicknames no longer used. Fixes traversal by implementing an IBugSet.getByNameOrID() method, and using that in places which traverse to bugs |
2625 |
else: |
|
3111.1.2
by Diogo Matsubara
Fixes https://launchpad.net/products/malone/+bug/31005 (ValueError on bugtask traversal) r=kiko |
2626 |
try: |
2627 |
bug = self.get(bugid) |
|
2628 |
except ValueError: |
|
2629 |
raise NotFoundError( |
|
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
2630 |
"Unable to locate bug with nickname %s." % bugid) |
|
1716.3.3
by kiko
Fix for bug 5505: Bug nicknames no longer used. Fixes traversal by implementing an IBugSet.getByNameOrID() method, and using that in places which traverse to bugs |
2631 |
return bug |
2632 |
||
|
13939.3.18
by Curtis Hovey
Reconcile the command changes with the handler using bug-emailinterface.txt |
2633 |
def createBug(self, bug_params, notify_event=True): |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
2634 |
"""See `IBugSet`."""
|
|
8342.5.18
by Gavin Panella
Move product and distribution (task) specific stuff to createBug(), from createBugWithoutTarget(). |
2635 |
# Make a copy of the parameter object, because we might modify some
|
2636 |
# of its attribute values below.
|
|
|
8342.5.27
by Gavin Panella
Factor out the snapshot of bug params. |
2637 |
params = snapshot_bug_params(bug_params) |
|
8342.5.18
by Gavin Panella
Move product and distribution (task) specific stuff to createBug(), from createBugWithoutTarget(). |
2638 |
|
|
15761.2.18
by William Grant
Rip out CreateBugParams/createBug support for exploded targets. setBugTarget(target=...) is all there is. |
2639 |
if ISeriesBugTarget.providedBy(params.target): |
|
15761.2.12
by William Grant
Make createBug's series error a little more informative. |
2640 |
raise IllegalTarget( |
2641 |
"Can't create a bug on a series. Create it with a non-series "
|
|
2642 |
"task instead, and target it to the series afterwards.") |
|
|
15761.2.9
by William Grant
Let CreateBugParams take an IBugTarget, rather than a product, distribution and sourcepackagename. |
2643 |
|
|
15761.2.8
by William Grant
Refactor createBug to use IBugTarget['pillar']. |
2644 |
if params.information_type is None: |
2645 |
params.information_type = ( |
|
|
15761.2.18
by William Grant
Rip out CreateBugParams/createBug support for exploded targets. setBugTarget(target=...) is all there is. |
2646 |
params.target.pillar.getDefaultBugInformationType()) |
|
15761.2.8
by William Grant
Refactor createBug to use IBugTarget['pillar']. |
2647 |
|
2648 |
bug, event = self._makeBug(params) |
|
2649 |
||
|
17810.2.1
by Colin Watson
Set up AccessPolicyArtifacts before subscribing the branch/repository owner, to avoid excess AccessArtifactGrants. |
2650 |
# Create the initial task on the specified target. This also
|
2651 |
# reconciles access policies for this bug based on that target.
|
|
|
15761.2.4
by William Grant
Refactor createBug to have a single createTask call. |
2652 |
getUtility(IBugTaskSet).createTask( |
|
15761.2.18
by William Grant
Rip out CreateBugParams/createBug support for exploded targets. setBugTarget(target=...) is all there is. |
2653 |
bug, params.owner, params.target, status=params.status) |
|
15424.1.2
by Ian Booth
New findPeopleWithoutAccess service methods, ensure all subscribers are granted access to a bug when it becomes private |
2654 |
|
|
15424.1.11
by Ian Booth
Remove old createBugWithoutTarget method, fix tests |
2655 |
if params.subscribe_owner: |
2656 |
bug.subscribe(params.owner, params.owner) |
|
2657 |
# Subscribe other users.
|
|
2658 |
for subscriber in params.subscribers: |
|
2659 |
bug.subscribe(subscriber, params.owner) |
|
2660 |
||
|
12926.1.6
by Graham Binns
Reverted to previous functionality. |
2661 |
bug_task = bug.default_bugtask |
2662 |
if params.assignee: |
|
2663 |
bug_task.transitionToAssignee(params.assignee) |
|
2664 |
if params.importance: |
|
2665 |
bug_task.transitionToImportance(params.importance, params.owner) |
|
2666 |
if params.milestone: |
|
2667 |
bug_task.transitionToMilestone(params.milestone, params.owner) |
|
|
12926.1.1
by Graham Binns
Hurrah. You can now Do Things in createBug() that you couldn't before. |
2668 |
|
|
8342.5.18
by Gavin Panella
Move product and distribution (task) specific stuff to createBug(), from createBugWithoutTarget(). |
2669 |
# Tell everyone.
|
|
13939.3.18
by Curtis Hovey
Reconcile the command changes with the handler using bug-emailinterface.txt |
2670 |
if notify_event: |
2671 |
notify(event) |
|
|
8342.5.18
by Gavin Panella
Move product and distribution (task) specific stuff to createBug(), from createBugWithoutTarget(). |
2672 |
|
|
7675.706.9
by Graham Binns
Fixed test failures in bug-heat.txt. |
2673 |
# Calculate the bug's initial heat.
|
|
16146.2.1
by Ian Booth
Introduce bulk update operations for bugs with duplicates affecting users and bug heat updates |
2674 |
update_bug_heat([bug.id]) |
|
7675.706.9
by Graham Binns
Fixed test failures in bug-heat.txt. |
2675 |
|
|
13939.3.18
by Curtis Hovey
Reconcile the command changes with the handler using bug-emailinterface.txt |
2676 |
if not notify_event: |
2677 |
return bug, event |
|
|
8342.5.16
by Gavin Panella
New method createBugWithoutTarget() to allow bug creation without, you guessed it, a target. |
2678 |
return bug |
2679 |
||
|
15424.1.11
by Ian Booth
Remove old createBugWithoutTarget method, fix tests |
2680 |
def _makeBug(self, bug_params): |
2681 |
"""Construct a bew bug object using the specified parameters."""
|
|
2682 |
||
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
2683 |
# Make a copy of the parameter object, because we might modify some
|
2684 |
# of its attribute values below.
|
|
|
8342.5.27
by Gavin Panella
Factor out the snapshot of bug params. |
2685 |
params = snapshot_bug_params(bug_params) |
|
3598.1.8
by Brad Bollenbach
refactor IBugSet.createBug to use a parameter object |
2686 |
|
2687 |
if not (params.comment or params.description or params.msg): |
|
|
2821.2.28
by Brad Bollenbach
add auto-subscribing of pkg bug contacts to public bug reports |
2688 |
raise AssertionError( |
|
8342.5.18
by Gavin Panella
Move product and distribution (task) specific stuff to createBug(), from createBugWithoutTarget(). |
2689 |
'Either comment, msg, or description should be specified.') |
|
2407
by Canonical.com Patch Queue Manager
[trivial] remove unused, broken, code dealing with binary packages. remove BugFactory. |
2690 |
|
|
6995.1.19
by Bjorn Tillenius
test the date format as well. |
2691 |
if not params.datecreated: |
2692 |
params.datecreated = UTC_NOW |
|
2693 |
||
|
2407
by Canonical.com Patch Queue Manager
[trivial] remove unused, broken, code dealing with binary packages. remove BugFactory. |
2694 |
# make sure we did not get TOO MUCH information
|
|
3598.1.8
by Brad Bollenbach
refactor IBugSet.createBug to use a parameter object |
2695 |
assert params.comment is None or params.msg is None, ( |
|
4656.2.1
by Curtis Hovey
Fixed spelling in raised errors, updated docstrings. |
2696 |
"Expected either a comment or a msg, but got both.") |
|
8342.5.16
by Gavin Panella
New method createBugWithoutTarget() to allow bug creation without, you guessed it, a target. |
2697 |
|
|
2821.2.18
by Brad Bollenbach
checkpoint |
2698 |
# Create the bug comment if one was given.
|
|
3598.1.8
by Brad Bollenbach
refactor IBugSet.createBug to use a parameter object |
2699 |
if params.comment: |
|
2407
by Canonical.com Patch Queue Manager
[trivial] remove unused, broken, code dealing with binary packages. remove BugFactory. |
2700 |
rfc822msgid = make_msgid('malonedeb') |
|
3598.1.8
by Brad Bollenbach
refactor IBugSet.createBug to use a parameter object |
2701 |
params.msg = Message( |
|
8342.5.29
by Gavin Panella
First blast at removing Message.distribution. |
2702 |
subject=params.title, rfc822msgid=rfc822msgid, |
2703 |
owner=params.owner, datecreated=params.datecreated) |
|
|
2489
by Canonical.com Patch Queue Manager
[r=kiko] Fix a bug on the Malone front page where 'latest bugs' wasn't |
2704 |
MessageChunk( |
|
3691.62.21
by kiko
Clean up the use of ID/.id in select*By and constructors |
2705 |
message=params.msg, sequence=1, content=params.comment, |
2706 |
blob=None) |
|
|
2407
by Canonical.com Patch Queue Manager
[trivial] remove unused, broken, code dealing with binary packages. remove BugFactory. |
2707 |
|
|
2821.2.18
by Brad Bollenbach
checkpoint |
2708 |
# Extract the details needed to create the bug and optional msg.
|
|
3598.1.8
by Brad Bollenbach
refactor IBugSet.createBug to use a parameter object |
2709 |
if not params.description: |
2710 |
params.description = params.msg.text_contents |
|
|
2407
by Canonical.com Patch Queue Manager
[trivial] remove unused, broken, code dealing with binary packages. remove BugFactory. |
2711 |
|
|
4813.12.9
by Gavin Panella
Set privacy audit info on bug creation. |
2712 |
extra_params = {} |
|
14986.1.1
by Steve Kowalik
Change CreateBugParams to take information_type, and drop IBug._{private,security_related}. |
2713 |
if params.information_type in PRIVATE_INFORMATION_TYPES: |
|
4813.12.9
by Gavin Panella
Set privacy audit info on bug creation. |
2714 |
# We add some auditing information. After bug creation
|
2715 |
# time these attributes are updated by Bug.setPrivate().
|
|
2716 |
extra_params.update( |
|
2717 |
date_made_private=params.datecreated, |
|
2718 |
who_made_private=params.owner) |
|
2719 |
||
|
2407
by Canonical.com Patch Queue Manager
[trivial] remove unused, broken, code dealing with binary packages. remove BugFactory. |
2720 |
bug = Bug( |
|
3598.1.8
by Brad Bollenbach
refactor IBugSet.createBug to use a parameter object |
2721 |
title=params.title, description=params.description, |
|
14986.1.1
by Steve Kowalik
Change CreateBugParams to take information_type, and drop IBug._{private,security_related}. |
2722 |
owner=params.owner, datecreated=params.datecreated, |
2723 |
information_type=params.information_type, |
|
|
4813.12.9
by Gavin Panella
Set privacy audit info on bug creation. |
2724 |
**extra_params) |
|
2407
by Canonical.com Patch Queue Manager
[trivial] remove unused, broken, code dealing with binary packages. remove BugFactory. |
2725 |
|
|
3691.415.2
by Bjorn Tillenius
add a Tags: field to the advanced filebug page. |
2726 |
if params.tags: |
2727 |
bug.tags = params.tags |
|
|
3598.1.28
by Brad Bollenbach
merge from rf, resolving conflicts |
2728 |
|
|
2821.2.18
by Brad Bollenbach
checkpoint |
2729 |
# Link the bug to the message.
|
|
12346.2.2
by Robert Collins
Change all BugMessage object creation to set the index. This involved |
2730 |
BugMessage(bug=bug, message=params.msg, index=0) |
|
2407
by Canonical.com Patch Queue Manager
[trivial] remove unused, broken, code dealing with binary packages. remove BugFactory. |
2731 |
|
|
8054.7.1
by Tom Berger
mark bug reporters as affected by a bug |
2732 |
# Mark the bug reporter as affected by that bug.
|
2733 |
bug.markUserAffected(bug.owner) |
|
2734 |
||
|
13939.3.10
by Curtis Hovey
Updated CreateBugParams to support CVEs. |
2735 |
if params.cve is not None: |
2736 |
bug.linkCVE(params.cve, params.owner) |
|
2737 |
||
|
8342.5.18
by Gavin Panella
Move product and distribution (task) specific stuff to createBug(), from createBugWithoutTarget(). |
2738 |
# Populate the creation event.
|
|
8342.5.14
by Gavin Panella
Fold the user parameter to IBugSet.createByg() into CreateBugParams. |
2739 |
if params.filed_by is None: |
|
8342.5.16
by Gavin Panella
New method createBugWithoutTarget() to allow bug creation without, you guessed it, a target. |
2740 |
event = ObjectCreatedEvent(bug, user=params.owner) |
|
8342.5.5
by Gavin Panella
Allow the bug filer to be specified in a call to createBug(). |
2741 |
else: |
|
8342.5.16
by Gavin Panella
New method createBugWithoutTarget() to allow bug creation without, you guessed it, a target. |
2742 |
event = ObjectCreatedEvent(bug, user=params.filed_by) |
|
8342.5.2
by Gavin Panella
Change BugSet.createBug() to send the ObjectCreatedEvent itself, with an update to test_bugchangesto test it. |
2743 |
|
|
8342.5.18
by Gavin Panella
Move product and distribution (task) specific stuff to createBug(), from createBugWithoutTarget(). |
2744 |
return (bug, event) |
|
7030.5.1
by Tom Berger
model for bug affects user |
2745 |
|
|
8486.16.9
by Graham Binns
Extracted common portion of BugTask.findSimilar() and FilebugShowSimilarBugsView.similar_bugs into IBugSet.getDistinctBugsForBugTasks(). |
2746 |
def getDistinctBugsForBugTasks(self, bug_tasks, user, limit=10): |
2747 |
"""See `IBugSet`."""
|
|
2748 |
# XXX: Graham Binns 2009-05-28 bug=75764
|
|
|
8486.16.16
by Graham Binns
Fixed a typo. |
2749 |
# We slice bug_tasks here to prevent this method from
|
2750 |
# causing timeouts, since if we try to iterate over it
|
|
|
8486.16.9
by Graham Binns
Extracted common portion of BugTask.findSimilar() and FilebugShowSimilarBugsView.similar_bugs into IBugSet.getDistinctBugsForBugTasks(). |
2751 |
# Transaction.iterSelect() will try to listify the results.
|
2752 |
# This can be fixed by selecting from Bugs directly, but
|
|
2753 |
# that's non-trivial.
|
|
|
13163.1.1
by Brad Crittenden
Fixed userCanView to handle anonymous users correctly. |
2754 |
# ---: Robert Collins 2010-08-18: if bug_tasks implements IResultSet
|
|
11307.2.30
by Robert Collins
Checkpoint for ec2test. |
2755 |
# then it should be very possible to improve on it, though
|
2756 |
# DecoratedResultSets would need careful handling (e.g. type
|
|
2757 |
# driven callbacks on columns)
|
|
|
8486.16.9
by Graham Binns
Extracted common portion of BugTask.findSimilar() and FilebugShowSimilarBugsView.similar_bugs into IBugSet.getDistinctBugsForBugTasks(). |
2758 |
# We select more than :limit: since if a bug affects more than
|
2759 |
# one source package, it will be returned more than one time. 4
|
|
2760 |
# is an arbitrary number that should be large enough.
|
|
2761 |
bugs = [] |
|
|
13155.2.15
by Francis J. Lacoste
Lint blows but hoover sucks. |
2762 |
for bug_task in bug_tasks[:4 * limit]: |
|
8486.16.9
by Graham Binns
Extracted common portion of BugTask.findSimilar() and FilebugShowSimilarBugsView.similar_bugs into IBugSet.getDistinctBugsForBugTasks(). |
2763 |
bug = bug_task.bug |
2764 |
duplicateof = bug.duplicateof |
|
2765 |
if duplicateof is not None: |
|
2766 |
bug = duplicateof |
|
2767 |
||
|
8486.16.21
by Graham Binns
BugSet.getDistinctBugsForBugTasks() will no longer return duplicate bugs which aren't visible to the user. |
2768 |
if not bug.userCanView(user): |
2769 |
continue
|
|
2770 |
||
|
8486.16.9
by Graham Binns
Extracted common portion of BugTask.findSimilar() and FilebugShowSimilarBugsView.similar_bugs into IBugSet.getDistinctBugsForBugTasks(). |
2771 |
if bug not in bugs: |
2772 |
bugs.append(bug) |
|
2773 |
if len(bugs) >= limit: |
|
2774 |
break
|
|
2775 |
||
2776 |
return bugs |
|
2777 |
||
|
9719.5.18
by Muharem Hrnjadovic
Refactored code: |
2778 |
def getByNumbers(self, bug_numbers): |
|
10124.2.6
by Graham Binns
Added updateBugHeat(). |
2779 |
"""See `IBugSet`."""
|
|
9719.5.18
by Muharem Hrnjadovic
Refactored code: |
2780 |
if bug_numbers is None or len(bug_numbers) < 1: |
2781 |
return EmptyResultSet() |
|
2782 |
store = IStore(Bug) |
|
|
7675.166.301
by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint |
2783 |
result_set = store.find(Bug, Bug.id.is_in(bug_numbers)) |
|
9772.2.1
by Muharem Hrnjadovic
Improvements to BugSet.getByNumbers() and to the related tests. |
2784 |
return result_set.order_by('id') |
|
9719.5.18
by Muharem Hrnjadovic
Refactored code: |
2785 |
|
|
14835.1.2
by William Grant
Move the heat outdated cutoff calculation outside getBugsWithOutdatedHeat. |
2786 |
def getBugsWithOutdatedHeat(self, cutoff): |
|
7675.582.6
by Graham Binns
Added IBug.getBugsWithOutdatedHeat(). |
2787 |
"""See `IBugSet`."""
|
2788 |
store = IStore(Bug) |
|
2789 |
last_updated_clause = Or( |
|
|
14835.1.2
by William Grant
Move the heat outdated cutoff calculation outside getBugsWithOutdatedHeat. |
2790 |
Bug.heat_last_updated < cutoff, |
|
7675.582.6
by Graham Binns
Added IBug.getBugsWithOutdatedHeat(). |
2791 |
Bug.heat_last_updated == None) |
2792 |
||
|
14835.1.1
by William Grant
Stop forcing dupe heat to 0. They're hidden from searches by default, so it provides no benefit. This also lets getBugsWithOutdatedHeat use the index properly. |
2793 |
return store.find(Bug, last_updated_clause).order_by( |
2794 |
Bug.heat_last_updated) |
|
|
7675.582.6
by Graham Binns
Added IBug.getBugsWithOutdatedHeat(). |
2795 |
|
|
7030.5.1
by Tom Berger
model for bug affects user |
2796 |
|
2797 |
class BugAffectsPerson(SQLBase): |
|
|
7030.5.2
by Tom Berger
missing docstring |
2798 |
"""A bug is marked as affecting a user."""
|
|
7030.5.1
by Tom Berger
model for bug affects user |
2799 |
bug = ForeignKey(dbName='bug', foreignKey='Bug', notNull=True) |
2800 |
person = ForeignKey(dbName='person', foreignKey='Person', notNull=True) |
|
|
7106.1.1
by Tom Berger
record both affected an unaffected users |
2801 |
affected = BoolCol(notNull=True, default=True) |
|
10015.2.1
by Gavin Panella
Add __storm_primary__ to BugAffectsPerson so that more queries can hit the cache. |
2802 |
__storm_primary__ = "bugID", "personID" |
|
7675.547.6
by Graham Binns
FileBugData parsed from a blob by a ProcessApportBlobJob is now used during the filebug process, rather than parsing the blob in the request. |
2803 |
|
2804 |
||
|
17610.1.1
by Colin Watson
Switch zope.interface users from class advice to class decorators. |
2805 |
@implementer(IFileBugData) |
|
7675.547.6
by Graham Binns
FileBugData parsed from a blob by a ProcessApportBlobJob is now used during the filebug process, rather than parsing the blob in the request. |
2806 |
class FileBugData: |
2807 |
"""Extra data to be added to the bug."""
|
|
2808 |
||
2809 |
def __init__(self, initial_summary=None, initial_tags=None, |
|
2810 |
private=None, subscribers=None, extra_description=None, |
|
2811 |
comments=None, attachments=None, |
|
2812 |
hwdb_submission_keys=None): |
|
2813 |
if initial_tags is None: |
|
2814 |
initial_tags = [] |
|
2815 |
if subscribers is None: |
|
2816 |
subscribers = [] |
|
2817 |
if comments is None: |
|
2818 |
comments = [] |
|
2819 |
if attachments is None: |
|
2820 |
attachments = [] |
|
2821 |
if hwdb_submission_keys is None: |
|
2822 |
hwdb_submission_keys = [] |
|
2823 |
||
2824 |
self.initial_summary = initial_summary |
|
2825 |
self.private = private |
|
2826 |
self.extra_description = extra_description |
|
2827 |
self.initial_tags = initial_tags |
|
2828 |
self.subscribers = subscribers |
|
2829 |
self.comments = comments |
|
2830 |
self.attachments = attachments |
|
2831 |
self.hwdb_submission_keys = hwdb_submission_keys |
|
2832 |
||
2833 |
def asDict(self): |
|
2834 |
"""Return the FileBugData instance as a dict."""
|
|
2835 |
return self.__dict__.copy() |
|
|
7675.1138.5
by Danilo Segan
Move basic mute functionality to BugMute table as tested by TestBugSubscriptionMethods. |
2836 |
|
2837 |
||
|
17610.1.1
by Colin Watson
Switch zope.interface users from class advice to class decorators. |
2838 |
@implementer(IBugMute) |
|
7675.1138.5
by Danilo Segan
Move basic mute functionality to BugMute table as tested by TestBugSubscriptionMethods. |
2839 |
class BugMute(StormBase): |
2840 |
"""Contains bugs a person has decided to block notifications from."""
|
|
2841 |
||
2842 |
__storm_table__ = "BugMute" |
|
2843 |
||
2844 |
def __init__(self, person=None, bug=None): |
|
2845 |
if person is not None: |
|
2846 |
self.person = person |
|
2847 |
if bug is not None: |
|
2848 |
self.bug_id = bug.id |
|
2849 |
||
2850 |
person_id = Int("person", allow_none=False, validator=validate_person) |
|
2851 |
person = Reference(person_id, "Person.id") |
|
2852 |
||
2853 |
bug_id = Int("bug", allow_none=False) |
|
2854 |
bug = Reference(bug_id, "Bug.id") |
|
2855 |
||
2856 |
__storm_primary__ = 'person_id', 'bug_id' |
|
2857 |
||
2858 |
date_created = DateTime( |
|
2859 |
"date_created", allow_none=False, default=UTC_NOW, |
|
2860 |
tzinfo=pytz.UTC) |
|
|
16122.2.1
by Steve Kowalik
Rewrite the heavy-lifting queries in IBug.getSubscriptionForPerson() and IPersonSubscriptionInfo._getDirectAndDuplicateSubscriptions() to be performant using two CTEs, and bulk load all related people, bugtasks and pillars in the second function. |
2861 |
|
|
16122.2.3
by Steve Kowalik
Resurrect preloading witchcraft in browser/bugtask, correct whitespace in generate_subscription_with, and destroy PSI._getTaskPillar. |
2862 |
|
|
16122.2.1
by Steve Kowalik
Rewrite the heavy-lifting queries in IBug.getSubscriptionForPerson() and IPersonSubscriptionInfo._getDirectAndDuplicateSubscriptions() to be performant using two CTEs, and bulk load all related people, bugtasks and pillars in the second function. |
2863 |
def generate_subscription_with(bug, person): |
2864 |
return [ |
|
2865 |
With('all_bugsubscriptions', Select( |
|
2866 |
(BugSubscription.id, BugSubscription.person_id), |
|
|
16122.2.3
by Steve Kowalik
Resurrect preloading witchcraft in browser/bugtask, correct whitespace in generate_subscription_with, and destroy PSI._getTaskPillar. |
2867 |
tables=[ |
2868 |
BugSubscription, Join(Bug, Bug.id == BugSubscription.bug_id)], |
|
|
16122.2.1
by Steve Kowalik
Rewrite the heavy-lifting queries in IBug.getSubscriptionForPerson() and IPersonSubscriptionInfo._getDirectAndDuplicateSubscriptions() to be performant using two CTEs, and bulk load all related people, bugtasks and pillars in the second function. |
2869 |
where=Or(Bug.id == bug.id, Bug.duplicateofID == bug.id))), |
2870 |
With('bugsubscriptions', Select( |
|
2871 |
SQL('all_bugsubscriptions.id'), |
|
2872 |
tables=[ |
|
|
16122.2.3
by Steve Kowalik
Resurrect preloading witchcraft in browser/bugtask, correct whitespace in generate_subscription_with, and destroy PSI._getTaskPillar. |
2873 |
SQL('all_bugsubscriptions'), |
2874 |
Join(TeamParticipation, TeamParticipation.teamID == SQL( |
|
2875 |
'all_bugsubscriptions.person'))], |
|
|
16122.2.1
by Steve Kowalik
Rewrite the heavy-lifting queries in IBug.getSubscriptionForPerson() and IPersonSubscriptionInfo._getDirectAndDuplicateSubscriptions() to be performant using two CTEs, and bulk load all related people, bugtasks and pillars in the second function. |
2876 |
where=[TeamParticipation.personID == person.id]))] |