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