Merge lp:~thekorn/launchpad/make_iperson_ihasbugs into lp:launchpad
- make_iperson_ihasbugs
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Gavin Panella |
Approved revision: | not available |
Merge reported by: | Gavin Panella |
Merged at revision: | not available |
Proposed branch: | lp:~thekorn/launchpad/make_iperson_ihasbugs |
Merge into: | lp:launchpad |
Diff against target: |
975 lines (+372/-88) 22 files modified
lib/canonical/launchpad/interfaces/_schema_circular_imports.py (+55/-2) lib/canonical/launchpad/interfaces/message.py (+4/-6) lib/lp/bugs/doc/bugtask-search.txt (+12/-1) lib/lp/bugs/interfaces/bug.py (+5/-6) lib/lp/bugs/interfaces/bugtarget.py (+20/-18) lib/lp/bugs/interfaces/bugtask.py (+9/-2) lib/lp/bugs/interfaces/bugtracker.py (+1/-2) lib/lp/bugs/interfaces/bugwatch.py (+1/-2) lib/lp/bugs/model/bugtask.py (+46/-2) lib/lp/bugs/stories/webservice/xx-bug.txt (+74/-4) lib/lp/registry/configure.zcml (+2/-0) lib/lp/registry/interfaces/distribution.py (+0/-7) lib/lp/registry/interfaces/distributionsourcepackage.py (+4/-3) lib/lp/registry/interfaces/distroseries.py (+5/-3) lib/lp/registry/interfaces/milestone.py (+3/-2) lib/lp/registry/interfaces/person.py (+2/-13) lib/lp/registry/interfaces/product.py (+0/-7) lib/lp/registry/interfaces/productseries.py (+3/-2) lib/lp/registry/interfaces/project.py (+3/-2) lib/lp/registry/interfaces/sourcepackage.py (+3/-2) lib/lp/registry/model/person.py (+18/-2) lib/lp/registry/tests/test_person.py (+102/-0) |
To merge this branch: | bzr merge lp:~thekorn/launchpad/make_iperson_ihasbugs |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gavin Panella (community) | Approve | ||
Markus Korn (community) | Needs Resubmitting | ||
Eleanor Berger (community) | code | Approve | |
Review via email:
|
Commit message
Person.
Description of the change
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Markus Korn (thekorn) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Gavin Panella (allenap) wrote : | # |
Hi Markus,
I've added some comments to the diff below. It looks pretty good,
thank you so much for doing this.
Because I worked closely on you on this branch, I'm going to ask
someone else to review it too.
Gavin.
> === modified file 'lib/canonical/
> --- lib/canonical/
> +++ lib/canonical/
> @@ -24,11 +24,11 @@
>
> from lp.registry.
> IStructuralSubs
> -from lp.bugs.
> +from lp.bugs.
> from lp.bugs.
> from lp.bugs.
> from lp.bugs.
> -from lp.bugs.
> +from lp.bugs.
> from lp.soyuz.
> BuildStatus, IBuild)
> from lp.soyuz.
> @@ -69,6 +69,11 @@
> from lp.soyuz.
> IPackageUpload, PackageUploadCu
> from lp.registry.
> +from canonical.
> + IIndexedMessage, IMessage, IUserToUserEmail)
> +
> +from lp.bugs.
> +from lp.bugs.
>
>
> IBranch[
> @@ -312,5 +317,53 @@
> patch_reference
> IStructuralSubs
> IStructuralSubs
> -
> +
> IBuildBase[
> +
> +# IHasBugs
> +patch_
> + IHasBugs, 'searchTasks', 'assignee', IPerson)
> +patch_
> + IHasBugs, 'searchTasks', 'bug_reporter', IPerson)
> +patch_
> + IHasBugs, 'searchTasks', 'bug_supervisor', IPerson)
> +patch_
> + IHasBugs, 'searchTasks', 'bug_commenter', IPerson)
> +patch_
> + IHasBugs, 'searchTasks', 'bug_subscriber', IPerson)
> +patch_
> + IHasBugs, 'searchTasks', 'owner', IPerson)
> +patch_
> + IHasBugs, 'searchTasks', 'affected_user', IPerson)
> +
> +# IBugTask
> +patch_
> +
> +# IBugWatch
> +patch_
> +
> +# IIndexedMessage
> +patch_
> +
> +# IMessage
> +patch_
> +
> +# IUserToUserEmail
> +patch_
> +patch_
> +
> +# IBug
> +patch_
> + IBug, 'addNomination', 'target', IBugTarget)
> +patch_
> + IBug, 'canBeNominated
> +patch_
> +...
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Eleanor Berger (intellectronica) wrote : | # |
Markus (and Gavin), thanks a lot for picking this up. Excellent branch! I have no comments beyond what Gavin already mentioned in his review. I'm leaving the branch in NEEDS FIXING, since you'll need to fix them and then ask one of us to merge it on your behalf, but this shouldn't be much work.
- 10114. By Markus Korn
-
fixed typo
- 10115. By Markus Korn
-
started to address some comments of the review
- 10116. By Markus Korn
-
merged devel
- 10117. By Markus Korn
-
* merged devel
* fixed conflicting base classes of IPersonPublic - 10118. By Markus Korn
-
* Added IllegalRelatedB
ugTasksParams Exception which translates to a 400
HTTP error in the webservice API and will be raised in cases of invalid
queries for related tasks - 10119. By Markus Korn
-
* moved creation of BugTaskSearchParams for Person.
searchTasks( ) to an
external function.
* added more comments to document the behaviour of this function
* raise IllegalRelatedBugTasksParams exception in case it is impossible to
execute Person.searchTasks( ) because of invalid number of parameters - 10120. By Markus Korn
-
* moved doctest for Person.
searchTasks( ) to xx-bug.txt - 10121. By Markus Korn
-
* added unittests for getRelatedBugTa
sksParams
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Markus Korn (thekorn) wrote : | # |
Thanks for your review Gavin, I tried to address as many points as possible in the recent commits, most importantly I moved the logic to create search parameters into a separate function, added a few more descriptive comments/
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Eleanor Berger (intellectronica) wrote : | # |
Very nice. r=me.
Set the commit message and I'll land this on your behalf.
Thanks a lot for working on this!
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Gavin Panella (allenap) wrote : | # |
Hi Markus,
I've gone through each revision in turn since the last review; I
couldn't figure out how to get a cumulative diff without pulling in
all the merges from devel.
There are still a few things left to do to get this ready for landing,
but they're not huge, and I've done some of them (a few more unit
tests). It's really close though.
Gavin.
Revision 10114:
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -65,7 +65,7 @@
> == Person bugs ==
>
> To get all related tasks to a person call searchTasks() on the person
> -pbject:
> +object:
>
> >>> from canonical.
> >>> user = getUtility(
>
Revision 10115:
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -31,7 +31,6 @@
> import random
> import re
> import weakref
> -import copy
>
> from zope.lifecycleevent import ObjectCreatedEvent
> from zope.interface import alsoProvides, implementer, implements
> @@ -838,16 +837,20 @@
>
> def searchTasks(self, search_params, *args, **kwargs):
> """See `IHasBugs`."""
> - if search_params is None and not args:
> + if search_params is None and len(args) == 0:
> # this method is called via webapi directly
> - args = list()
> + args = []
> for key in ('assignee', 'bug_subscriber', 'owner', 'bug_commenter'):
> - if kwargs.get(key, None) is None:
> - arguments = copy.copy(kwargs)
> + # all these parameter default to None
> +
> + if kwargs.get(key) is None:
> +
> + arguments = kwargs.copy()
> arguments[key] = self
> if key == 'owner':
> - # Specify both owner and bug_reporter to try to prevent the same
> - # bug (but different tasks) being displayed.
> + # Specify both owner and bug_reporter to try to
> + # prevent the same bug (but different tasks)
> + # being displayed.
> # see `PersonRelatedB
> arguments[
> args.append(
>
Revision 10118:
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -25,6 +25,7 @@
> 'ITeamContactAd
> 'ITeamCreation',
> 'ITeamReassignm
> + 'IllegalRelated
> 'ImmutableVisib
> 'InvalidName',
> 'JoinNotAllowed',
> @@ -2057,6 +2058,12 @@
> webservice_
>
>
> +class IllegalRelatedB
> + """Exception raised when trying to ov...
- 10122. By Markus Korn
-
fixed typo in docstring of IllegalRelatedB
ugTasksParams - 10123. By Markus Korn
-
* renamed `getRelatedBugT
asksParams` into
`get_related_ bugtasks_ search_ params` and moved this function to
lp.bugs.model.bugtask
* checking type of context argument of get_related_bugtasks_ search_ params( )
* - 10124. By Markus Korn
-
* moved IllegalRelatedB
ugTasksParams to lp.bugs. interfaces. bugtask - 10125. By Markus Korn
-
* added webservice API test for IllegalRelatedB
ugTasksParams - 10126. By Markus Korn
-
* Merged Gavin Panella's changes to the unittests for the related bug task
search. - 10127. By Markus Korn
-
added more detailed doctests to Person.
searchTasks( )
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Markus Korn (thekorn) wrote : | # |
With r10127 I *think* I've addressed all of Gavin's comments
- 10128. By Markus Korn
-
removed trailing whitespaces from lib/canonical/
launchpad/ interfaces/ _schema_ circular_ imports. py - 10129. By Markus Korn
-
removed trailing whitespaces from lib/lp/
bugs/doc/ bugtask- search. txt - 10130. By Markus Korn
-
removed trailing whitespaces from lib/lp/
bugs/interfaces /bugtask. py - 10131. By Markus Korn
-
removed trailing whitespaces from lib/lp/
bugs/stories/ webservice/ xx-bug. txt - 10132. By Markus Korn
-
merged with devel
- 10133. By Markus Korn
-
removed trailing whitespaces from lib/lp/
registry/ interfaces/ distributionsou rcepackage. py - 10134. By Markus Korn
-
removed trailing whitespaces from lib/lp/
registry/ interfaces/ distroseries. py
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Markus Korn (thekorn) wrote : | # |
fixed bunch of trailing whitespaces.
Now I get messages from `make lint` for code I did not even touch directly
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Gavin Panella (allenap) wrote : | # |
As discussed on IRC earlier, you accidentally merged db-devel instead of devel at revision 10132. My branch lp:~allenap/launchpad/make_iperson_ihasbugs fixes this and also removes the remaining trailing white-space from the branch.
Right, I'll get it landed now :)
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Gavin Panella (allenap) wrote : | # |
Merged via lp:~allenap/launchpad/make_iperson_ihasbugs.
Preview Diff
1 | === modified file 'lib/canonical/launchpad/interfaces/_schema_circular_imports.py' |
2 | --- lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2010-02-11 00:54:32 +0000 |
3 | +++ lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2010-02-12 10:32:22 +0000 |
4 | @@ -24,11 +24,11 @@ |
5 | |
6 | from lp.registry.interfaces.structuralsubscription import ( |
7 | IStructuralSubscription, IStructuralSubscriptionTarget) |
8 | -from lp.bugs.interfaces.bug import IBug |
9 | +from lp.bugs.interfaces.bug import IBug, IFrontPageBugAddForm |
10 | from lp.bugs.interfaces.bugbranch import IBugBranch |
11 | from lp.bugs.interfaces.bugnomination import IBugNomination |
12 | from lp.bugs.interfaces.bugtask import IBugTask |
13 | -from lp.bugs.interfaces.bugtarget import IHasBugs |
14 | +from lp.bugs.interfaces.bugtarget import IHasBugs, IBugTarget |
15 | from lp.soyuz.interfaces.build import ( |
16 | BuildStatus, IBuild) |
17 | from lp.soyuz.interfaces.buildrecords import IHasBuildRecords |
18 | @@ -70,6 +70,11 @@ |
19 | from lp.soyuz.interfaces.queue import ( |
20 | IPackageUpload, PackageUploadCustomFormat, PackageUploadStatus) |
21 | from lp.registry.interfaces.sourcepackage import ISourcePackage |
22 | +from canonical.launchpad.interfaces.message import ( |
23 | + IIndexedMessage, IMessage, IUserToUserEmail) |
24 | + |
25 | +from lp.bugs.interfaces.bugtracker import IBugTracker |
26 | +from lp.bugs.interfaces.bugwatch import IBugWatch |
27 | |
28 | |
29 | IBranch['bug_branches'].value_type.schema = IBugBranch |
30 | @@ -315,3 +320,51 @@ |
31 | IStructuralSubscriptionTarget) |
32 | |
33 | IBuildBase['buildstate'].vocabulary = BuildStatus |
34 | + |
35 | +# IHasBugs |
36 | +patch_plain_parameter_type( |
37 | + IHasBugs, 'searchTasks', 'assignee', IPerson) |
38 | +patch_plain_parameter_type( |
39 | + IHasBugs, 'searchTasks', 'bug_reporter', IPerson) |
40 | +patch_plain_parameter_type( |
41 | + IHasBugs, 'searchTasks', 'bug_supervisor', IPerson) |
42 | +patch_plain_parameter_type( |
43 | + IHasBugs, 'searchTasks', 'bug_commenter', IPerson) |
44 | +patch_plain_parameter_type( |
45 | + IHasBugs, 'searchTasks', 'bug_subscriber', IPerson) |
46 | +patch_plain_parameter_type( |
47 | + IHasBugs, 'searchTasks', 'owner', IPerson) |
48 | +patch_plain_parameter_type( |
49 | + IHasBugs, 'searchTasks', 'affected_user', IPerson) |
50 | + |
51 | +# IBugTask |
52 | +patch_reference_property(IBugTask, 'owner', IPerson) |
53 | + |
54 | +# IBugWatch |
55 | +patch_reference_property(IBugWatch, 'owner', IPerson) |
56 | + |
57 | +# IIndexedMessage |
58 | +patch_reference_property(IIndexedMessage, 'inside', IBugTask) |
59 | + |
60 | +# IMessage |
61 | +patch_reference_property(IMessage, 'owner', IPerson) |
62 | + |
63 | +# IUserToUserEmail |
64 | +patch_reference_property(IUserToUserEmail, 'sender', IPerson) |
65 | +patch_reference_property(IUserToUserEmail, 'recipient', IPerson) |
66 | + |
67 | +# IBug |
68 | +patch_plain_parameter_type( |
69 | + IBug, 'addNomination', 'target', IBugTarget) |
70 | +patch_plain_parameter_type( |
71 | + IBug, 'canBeNominatedFor', 'target', IBugTarget) |
72 | +patch_plain_parameter_type( |
73 | + IBug, 'getNominationFor', 'target', IBugTarget) |
74 | +patch_plain_parameter_type( |
75 | + IBug, 'getNominations', 'target', IBugTarget) |
76 | + |
77 | +# IFrontPageBugAddForm |
78 | +patch_reference_property(IFrontPageBugAddForm, 'bugtarget', IBugTarget) |
79 | + |
80 | +# IBugTracker |
81 | +patch_reference_property(IBugTracker, 'owner', IPerson) |
82 | |
83 | === modified file 'lib/canonical/launchpad/interfaces/message.py' |
84 | --- lib/canonical/launchpad/interfaces/message.py 2009-12-01 11:53:59 +0000 |
85 | +++ lib/canonical/launchpad/interfaces/message.py 2010-02-12 10:32:22 +0000 |
86 | @@ -27,10 +27,8 @@ |
87 | |
88 | from canonical.launchpad import _ |
89 | from canonical.launchpad.interfaces import NotFoundError |
90 | -from lp.bugs.interfaces.bugtask import IBugTask |
91 | from lp.services.job.interfaces.job import IJob |
92 | from canonical.launchpad.interfaces.librarian import ILibraryFileAlias |
93 | -from lp.registry.interfaces.person import IPerson |
94 | |
95 | from lazr.delegates import delegates |
96 | from lazr.restful.fields import CollectionField, Reference |
97 | @@ -57,7 +55,7 @@ |
98 | # add form used by MessageAddView. |
99 | content = Text(title=_("Message"), required=True, readonly=True) |
100 | owner = exported( |
101 | - Reference(title=_('Person'), schema=IPerson, |
102 | + Reference(title=_('Person'), schema=Interface, |
103 | required=False, readonly=True)) |
104 | |
105 | # Schema is really IMessage, but this cannot be declared here. It's |
106 | @@ -181,7 +179,7 @@ |
107 | |
108 | class IIndexedMessage(Interface): |
109 | """An `IMessage` decorated with its index and context.""" |
110 | - inside = Reference(title=_('Inside'), schema=IBugTask, |
111 | + inside = Reference(title=_('Inside'), schema=Interface, |
112 | description=_("The bug task which is " |
113 | "the context for this message."), |
114 | required=True, readonly=True) |
115 | @@ -227,12 +225,12 @@ |
116 | """User to user direct email communications.""" |
117 | |
118 | sender = Object( |
119 | - schema=IPerson, |
120 | + schema=Interface, |
121 | title=_("The message sender"), |
122 | required=True, readonly=True) |
123 | |
124 | recipient = Object( |
125 | - schema=IPerson, |
126 | + schema=Interface, |
127 | title=_("The message recipient"), |
128 | required=True, readonly=True) |
129 | |
130 | |
131 | === modified file 'lib/lp/bugs/doc/bugtask-search.txt' |
132 | --- lib/lp/bugs/doc/bugtask-search.txt 2010-02-02 17:12:29 +0000 |
133 | +++ lib/lp/bugs/doc/bugtask-search.txt 2010-02-12 10:32:22 +0000 |
134 | @@ -62,6 +62,17 @@ |
135 | >>> ubuntu_firefox_bugs.count() > 0 |
136 | True |
137 | |
138 | +== Person bugs == |
139 | + |
140 | +To get all related tasks to a person call searchTasks() on the person |
141 | +object: |
142 | + |
143 | + >>> from canonical.launchpad.interfaces import IPersonSet |
144 | + >>> user = getUtility(IPersonSet).getByName('name16') |
145 | + >>> user_bugs = user.searchTasks(None, user=None) |
146 | + >>> user_bugs.count() > 0 |
147 | + True |
148 | + |
149 | == Dupes and Conjoined tasks == |
150 | |
151 | You can set flags to omit duplicates: |
152 | @@ -962,7 +973,7 @@ |
153 | >>> print reduce( |
154 | ... lambda x, y: x and y, |
155 | ... [task.bug.isUserAffected(foo_bar) |
156 | - ... for task in firefox.searchTasks(search_params)]) |
157 | + ... for task in firefox.searchTasks(search_params)]) |
158 | True |
159 | |
160 | |
161 | |
162 | === modified file 'lib/lp/bugs/interfaces/bug.py' |
163 | --- lib/lp/bugs/interfaces/bug.py 2010-01-23 21:42:36 +0000 |
164 | +++ lib/lp/bugs/interfaces/bug.py 2010-02-12 10:32:22 +0000 |
165 | @@ -33,7 +33,6 @@ |
166 | from canonical.launchpad.fields import ( |
167 | BugField, ContentNameField, DuplicateBug, PublicPersonChoice, Tag, Title) |
168 | from lp.bugs.interfaces.bugattachment import IBugAttachment |
169 | -from lp.bugs.interfaces.bugtarget import IBugTarget |
170 | from lp.bugs.interfaces.bugtask import ( |
171 | BugTaskImportance, BugTaskStatus, IBugTask) |
172 | from lp.bugs.interfaces.bugwatch import IBugWatch |
173 | @@ -587,7 +586,7 @@ |
174 | """Create an INullBugTask and return it for the given parameters.""" |
175 | |
176 | @operation_parameters( |
177 | - target=Reference(schema=IBugTarget, title=_('Target'))) |
178 | + target=Reference(schema=Interface, title=_('Target'))) |
179 | @call_with(owner=REQUEST_USER) |
180 | @export_factory_operation(Interface, []) |
181 | def addNomination(owner, target): |
182 | @@ -601,7 +600,7 @@ |
183 | """ |
184 | |
185 | @operation_parameters( |
186 | - target=Reference(schema=IBugTarget, title=_('Target'))) |
187 | + target=Reference(schema=Interface, title=_('Target'))) |
188 | @export_read_operation() |
189 | def canBeNominatedFor(target): |
190 | """Can this bug nominated for this target? |
191 | @@ -612,7 +611,7 @@ |
192 | """ |
193 | |
194 | @operation_parameters( |
195 | - target=Reference(schema=IBugTarget, title=_('Target'))) |
196 | + target=Reference(schema=Interface, title=_('Target'))) |
197 | @operation_returns_entry(Interface) |
198 | @export_read_operation() |
199 | def getNominationFor(target): |
200 | @@ -625,7 +624,7 @@ |
201 | |
202 | @operation_parameters( |
203 | target=Reference( |
204 | - schema=IBugTarget, title=_('Target'), required=False), |
205 | + schema=Interface, title=_('Target'), required=False), |
206 | nominations=List( |
207 | title=_("Nominations to search through."), |
208 | value_type=Reference(schema=Interface), # IBugNomination |
209 | @@ -895,7 +894,7 @@ |
210 | """Create a bug for any bug target.""" |
211 | |
212 | bugtarget = Reference( |
213 | - schema=IBugTarget, title=_("Where did you find the bug?"), |
214 | + schema=Interface, title=_("Where did you find the bug?"), |
215 | required=True) |
216 | |
217 | |
218 | |
219 | === modified file 'lib/lp/bugs/interfaces/bugtarget.py' |
220 | --- lib/lp/bugs/interfaces/bugtarget.py 2009-08-18 11:12:06 +0000 |
221 | +++ lib/lp/bugs/interfaces/bugtarget.py 2010-02-12 10:32:22 +0000 |
222 | @@ -25,7 +25,6 @@ |
223 | from canonical.launchpad.fields import Tag |
224 | from lp.bugs.interfaces.bugtask import ( |
225 | BugTagsSearchCombinator, IBugTask, IBugTaskSearch) |
226 | -from lp.registry.interfaces.person import IPerson |
227 | from lazr.enum import DBEnumeratedType |
228 | from lazr.restful.fields import Reference |
229 | from lazr.restful.interface import copy_field |
230 | @@ -56,11 +55,6 @@ |
231 | "A list of unassigned BugTasks for this target.") |
232 | all_bugtasks = Attribute( |
233 | "A list of all BugTasks ever reported for this target.") |
234 | - official_bug_tags = exported(List( |
235 | - title=_("Official Bug Tags"), |
236 | - description=_("The list of bug tags defined as official."), |
237 | - value_type=Tag(), |
238 | - readonly=True)) |
239 | |
240 | @call_with(search_params=None, user=REQUEST_USER) |
241 | @operation_parameters( |
242 | @@ -71,13 +65,13 @@ |
243 | search_text=copy_field(IBugTaskSearch['searchtext']), |
244 | status=copy_field(IBugTaskSearch['status']), |
245 | importance=copy_field(IBugTaskSearch['importance']), |
246 | - assignee=Reference(schema=IPerson), |
247 | - bug_reporter=Reference(schema=IPerson), |
248 | - bug_supervisor=Reference(schema=IPerson), |
249 | - bug_commenter=Reference(schema=IPerson), |
250 | - bug_subscriber=Reference(schema=IPerson), |
251 | - owner=Reference(schema=IPerson), |
252 | - affected_user=Reference(schema=IPerson), |
253 | + assignee=Reference(schema=Interface), |
254 | + bug_reporter=Reference(schema=Interface), |
255 | + bug_supervisor=Reference(schema=Interface), |
256 | + bug_commenter=Reference(schema=Interface), |
257 | + bug_subscriber=Reference(schema=Interface), |
258 | + owner=Reference(schema=Interface), |
259 | + affected_user=Reference(schema=Interface), |
260 | has_patch=copy_field(IBugTaskSearch['has_patch']), |
261 | has_cve=copy_field(IBugTaskSearch['has_cve']), |
262 | tags=copy_field(IBugTaskSearch['tag']), |
263 | @@ -278,13 +272,21 @@ |
264 | self.status = status |
265 | |
266 | |
267 | -class IOfficialBugTagTargetPublic(Interface): |
268 | +class IHasOfficialBugTags(Interface): |
269 | + """An entity that exposes a set of official bug tags.""" |
270 | + |
271 | + official_bug_tags = exported(List( |
272 | + title=_("Official Bug Tags"), |
273 | + description=_("The list of bug tags defined as official."), |
274 | + value_type=Tag(), |
275 | + readonly=True)) |
276 | + |
277 | + |
278 | +class IOfficialBugTagTargetPublic(IHasOfficialBugTags): |
279 | """Public attributes for `IOfficialBugTagTarget`.""" |
280 | |
281 | - official_bug_tags = exported(List( |
282 | - title=_("Official Bug Tags"), |
283 | - description=_("The list of bug tags defined as official."), |
284 | - value_type=Tag())) |
285 | + official_bug_tags = copy_field( |
286 | + IHasOfficialBugTags['official_bug_tags'], readonly=False) |
287 | |
288 | |
289 | class IOfficialBugTagTargetRestricted(Interface): |
290 | |
291 | === modified file 'lib/lp/bugs/interfaces/bugtask.py' |
292 | --- lib/lp/bugs/interfaces/bugtask.py 2010-02-05 16:00:51 +0000 |
293 | +++ lib/lp/bugs/interfaces/bugtask.py 2010-02-12 10:32:22 +0000 |
294 | @@ -26,6 +26,7 @@ |
295 | 'IDistroBugTask', |
296 | 'IDistroSeriesBugTask', |
297 | 'IFrontPageBugTaskSearch', |
298 | + 'IllegalRelatedBugTasksParams', |
299 | 'IllegalTarget', |
300 | 'INominationsReviewTableBatchNavigator', |
301 | 'INullBugTask', |
302 | @@ -60,7 +61,6 @@ |
303 | from lp.soyuz.interfaces.component import IComponent |
304 | from canonical.launchpad.interfaces.launchpad import IHasDateCreated, IHasBug |
305 | from lp.registry.interfaces.mentoringoffer import ICanBeMentored |
306 | -from lp.registry.interfaces.person import IPerson |
307 | from canonical.launchpad.searchbuilder import all, any, NULL |
308 | from canonical.launchpad.validators import LaunchpadValidationError |
309 | from canonical.launchpad.validators.name import name_validator |
310 | @@ -340,6 +340,13 @@ |
311 | """Exception raised when trying to set an illegal bug task target.""" |
312 | webservice_error(400) #Bad request. |
313 | |
314 | + |
315 | +class IllegalRelatedBugTasksParams(Exception): |
316 | + """Exception raised when trying to overwrite all relevant parameters |
317 | + in a search for related bug tasks""" |
318 | + webservice_error(400) #Bad request. |
319 | + |
320 | + |
321 | class IBugTask(IHasDateCreated, IHasBug, ICanBeMentored): |
322 | """A bug needing fixing in a particular product or package.""" |
323 | export_as_webservice_entry() |
324 | @@ -474,7 +481,7 @@ |
325 | description=_("The age of this task in seconds, a delta between " |
326 | "now and the date the bug task was created.")) |
327 | owner = exported( |
328 | - Reference(title=_("The owner"), schema=IPerson, readonly=True)) |
329 | + Reference(title=_("The owner"), schema=Interface, readonly=True)) |
330 | target = exported(Reference( |
331 | title=_('Target'), required=True, schema=Interface, # IBugTarget |
332 | readonly=True, |
333 | |
334 | === modified file 'lib/lp/bugs/interfaces/bugtracker.py' |
335 | --- lib/lp/bugs/interfaces/bugtracker.py 2009-12-14 13:51:00 +0000 |
336 | +++ lib/lp/bugs/interfaces/bugtracker.py 2010-02-12 10:32:22 +0000 |
337 | @@ -27,7 +27,6 @@ |
338 | from canonical.launchpad import _ |
339 | from canonical.launchpad.fields import ( |
340 | ContentNameField, StrippedTextLine, URIField) |
341 | -from lp.registry.interfaces.person import IPerson |
342 | from canonical.launchpad.validators import LaunchpadValidationError |
343 | from canonical.launchpad.validators.name import name_validator |
344 | |
345 | @@ -216,7 +215,7 @@ |
346 | required=False), |
347 | exported_as='base_url_aliases') |
348 | owner = exported( |
349 | - Reference(title=_('Owner'), schema=IPerson), |
350 | + Reference(title=_('Owner'), schema=Interface), |
351 | exported_as='registrant') |
352 | contactdetails = exported( |
353 | Text( |
354 | |
355 | === modified file 'lib/lp/bugs/interfaces/bugwatch.py' |
356 | --- lib/lp/bugs/interfaces/bugwatch.py 2009-12-22 16:32:42 +0000 |
357 | +++ lib/lp/bugs/interfaces/bugwatch.py 2010-02-12 10:32:22 +0000 |
358 | @@ -22,7 +22,6 @@ |
359 | from canonical.launchpad import _ |
360 | from canonical.launchpad.fields import StrippedTextLine |
361 | from canonical.launchpad.interfaces.launchpad import IHasBug |
362 | -from lp.registry.interfaces.person import IPerson |
363 | from lp.bugs.interfaces.bugtracker import IBugTracker |
364 | |
365 | from lazr.restful.declarations import ( |
366 | @@ -134,7 +133,7 @@ |
367 | exported_as='date_created') |
368 | owner = exported( |
369 | Reference(title=_('Owner'), required=True, |
370 | - readonly=True, schema=IPerson)) |
371 | + readonly=True, schema=Interface)) |
372 | |
373 | # Useful joins. |
374 | bugtasks = exported( |
375 | |
376 | === modified file 'lib/lp/bugs/model/bugtask.py' |
377 | --- lib/lp/bugs/model/bugtask.py 2010-02-02 17:12:29 +0000 |
378 | +++ lib/lp/bugs/model/bugtask.py 2010-02-12 10:32:22 +0000 |
379 | @@ -16,6 +16,7 @@ |
380 | 'NullBugTask', |
381 | 'bugtask_sort_key', |
382 | 'get_bug_privacy_filter', |
383 | + 'get_related_bugtasks_search_params', |
384 | 'search_value_to_where_condition'] |
385 | |
386 | |
387 | @@ -61,7 +62,7 @@ |
388 | INullBugTask, IProductSeriesBugTask, IUpstreamBugTask, IllegalTarget, |
389 | RESOLVED_BUGTASK_STATUSES, UNRESOLVED_BUGTASK_STATUSES, |
390 | UserCannotEditBugTaskImportance, UserCannotEditBugTaskMilestone, |
391 | - UserCannotEditBugTaskStatus) |
392 | + UserCannotEditBugTaskStatus, IllegalRelatedBugTasksParams) |
393 | from lp.bugs.model.bugsubscription import BugSubscription |
394 | from lp.registry.interfaces.distribution import ( |
395 | IDistribution, IDistributionSet) |
396 | @@ -82,7 +83,8 @@ |
397 | from canonical.launchpad.searchbuilder import ( |
398 | all, any, greater_than, NULL, not_equals) |
399 | from lp.registry.interfaces.person import ( |
400 | - validate_person_not_private_membership, validate_public_person) |
401 | + IPerson, validate_person_not_private_membership, |
402 | + validate_public_person) |
403 | from canonical.launchpad.webapp.interfaces import ( |
404 | IStoreSelector, DEFAULT_FLAVOR, MAIN_STORE, NotFoundError) |
405 | |
406 | @@ -137,6 +139,48 @@ |
407 | return ( |
408 | bugtask.bug.id, distribution_name, product_name, productseries_name, |
409 | distroseries_name, sourcepackage_name) |
410 | + |
411 | +def get_related_bugtasks_search_params(user, context, **kwargs): |
412 | + """Returns a list of `BugTaskSearchParams` which can be used to |
413 | + search for all tasks related to a user given by `context`. |
414 | + |
415 | + Which tasks are related to a user? |
416 | + * the user has to be either assignee or owner of this tasks |
417 | + OR |
418 | + * the user has to be subscriber or commenter to the underlying bug |
419 | + OR |
420 | + * the user is reporter of the underlying bug, but this condition |
421 | + is automatically fulfilled by the first one as each new bug |
422 | + always get one task owned by the bug reporter |
423 | + """ |
424 | + assert IPerson.providedBy(context), "Context argument needs to be IPerson" |
425 | + relevant_fields = ('assignee', 'bug_subscriber', 'owner', 'bug_commenter') |
426 | + search_params = [] |
427 | + for key in relevant_fields: |
428 | + # all these parameter default to None |
429 | + user_param = kwargs.get(key) |
430 | + if user_param is None or user_param == context: |
431 | + # we are only creating a `BugTaskSearchParams` object if |
432 | + # the field is None or equal to the context |
433 | + arguments = kwargs.copy() |
434 | + arguments[key] = context |
435 | + if key == 'owner': |
436 | + # Specify both owner and bug_reporter to try to |
437 | + # prevent the same bug (but different tasks) |
438 | + # being displayed. |
439 | + # see `PersonRelatedBugTaskSearchListingView.searchUnbatched` |
440 | + arguments['bug_reporter'] = context |
441 | + search_params.append( |
442 | + BugTaskSearchParams.fromSearchForm(user, **arguments)) |
443 | + if len(search_params) == 0: |
444 | + # unable to search for related tasks to user_context because user |
445 | + # modified the query in an invalid way by overwriting all user |
446 | + # related parameters |
447 | + raise IllegalRelatedBugTasksParams( |
448 | + ('Cannot search for related tasks to \'%s\', at least one ' |
449 | + 'of these parameter has to be empty: %s' |
450 | + %(context.name, ", ".join(relevant_fields)))) |
451 | + return search_params |
452 | |
453 | |
454 | class BugTaskDelta: |
455 | |
456 | === modified file 'lib/lp/bugs/stories/webservice/xx-bug.txt' |
457 | --- lib/lp/bugs/stories/webservice/xx-bug.txt 2010-02-04 21:18:35 +0000 |
458 | +++ lib/lp/bugs/stories/webservice/xx-bug.txt 2010-02-12 10:32:22 +0000 |
459 | @@ -676,7 +676,7 @@ |
460 | ... |
461 | Location: http://.../bugs/.../nominations/... |
462 | ... |
463 | - |
464 | + |
465 | >>> nominations = webservice.named_get( |
466 | ... '/bugs/%d' % bug.id, 'getNominations').jsonBody() |
467 | >>> pprint_collection(nominations) |
468 | @@ -699,7 +699,7 @@ |
469 | John cannot approve or decline the nomination. |
470 | |
471 | >>> nom_url = nominations['entries'][0]['self_link'] |
472 | - |
473 | + |
474 | >>> print john_webservice.named_get(nom_url, 'canApprove').jsonBody() |
475 | False |
476 | |
477 | @@ -715,9 +715,9 @@ |
478 | >>> logout() |
479 | |
480 | Eric, however, can and does decline the nomination. |
481 | - |
482 | + |
483 | >>> eric_webservice = webservice_for_person( |
484 | - ... eric, permission=OAuthPermission.WRITE_PRIVATE) |
485 | + ... eric, permission=OAuthPermission.WRITE_PRIVATE) |
486 | >>> print eric_webservice.named_post(nom_url, 'decline') |
487 | HTTP/1.1 200 Ok... |
488 | |
489 | @@ -1645,6 +1645,76 @@ |
490 | http://api.launchpad.dev/beta/ubuntu/+source/linux-source-2.6.15/+bug/10 |
491 | |
492 | |
493 | +User related bug tasks |
494 | +~~~~~~~~~~~~~~~~~~~~~~ |
495 | + |
496 | +Calling searchTasks() on a Person object returns a collection of tasks |
497 | +related to this person. |
498 | + |
499 | +First create some sample data |
500 | + |
501 | + >>> login('foo.bar@canonical.com') |
502 | + >>> testuser1 = factory.makePerson(name='testuser1') |
503 | + >>> testuser2 = factory.makePerson(name='testuser2') |
504 | + >>> testuser3 = factory.makePerson(name='testuser3') |
505 | + >>> testbug1 = factory.makeBug(owner=testuser1) |
506 | + >>> testbug2 = factory.makeBug(owner=testuser1) |
507 | + >>> subscription = testbug2.subscribe(testuser2, testuser2) |
508 | + >>> logout() |
509 | + |
510 | +There are two tasks related to `testuser1`, the initial tasks of both |
511 | +bugs: |
512 | + |
513 | + >>> related = webservice.named_get( |
514 | + ... '/~testuser1', 'searchTasks' |
515 | + ... ).jsonBody() |
516 | + >>> pprint_collection(related) |
517 | + start: 0 |
518 | + total_size: 2 |
519 | + --- |
520 | + ... |
521 | + owner_link: u'http://api.launchpad.dev/beta/~testuser1' |
522 | + ... |
523 | + --- |
524 | + ... |
525 | + owner_link: u'http://api.launchpad.dev/beta/~testuser1' |
526 | + ... |
527 | + |
528 | +`testuser2` is subscribed to `testbugs2`, so this bug is related to this |
529 | +user: |
530 | + |
531 | + >>> related = webservice.named_get( |
532 | + ... '/~testuser2', 'searchTasks' |
533 | + ... ).jsonBody() |
534 | + >>> len(related['entries']) == 1 |
535 | + True |
536 | + >>> int(related['entries'][0]['bug_link'].split('/')[-1]) == testbug2.id |
537 | + True |
538 | + |
539 | +`testuser3` is not active, so the collection of related tasks to him is |
540 | +empty: |
541 | + |
542 | + >>> related = webservice.named_get( |
543 | + ... '/~testuser3', 'searchTasks' |
544 | + ... ).jsonBody() |
545 | + >>> pprint_collection(related) |
546 | + start: None |
547 | + total_size: 0 |
548 | + --- |
549 | + |
550 | +You are not allowed to overwrite all user related parameter in the same |
551 | +query, because in this case this bug will no be related to the person |
552 | +anymore. In this case a `400 Bad Request`-Error will be returned |
553 | + |
554 | + >>> name12 = webservice.get("/~name12").jsonBody() |
555 | + >>> print webservice.named_get( |
556 | + ... '/~name16', 'searchTasks', assignee=name12['self_link'], |
557 | + ... owner=name12['self_link'], bug_subscriber=name12['self_link'], |
558 | + ... bug_commenter=name12['self_link'] |
559 | + ... ) |
560 | + HTTP/1.1 400 Bad Request... |
561 | + |
562 | + |
563 | Affected users |
564 | -------------- |
565 | |
566 | |
567 | === modified file 'lib/lp/registry/configure.zcml' |
568 | --- lib/lp/registry/configure.zcml 2010-02-10 10:37:16 +0000 |
569 | +++ lib/lp/registry/configure.zcml 2010-02-12 10:32:22 +0000 |
570 | @@ -905,6 +905,8 @@ |
571 | <allow |
572 | interface="lp.bugs.interfaces.bugtarget.IHasBugs"/> |
573 | <allow |
574 | + interface="lp.bugs.interfaces.bugtarget.IHasOfficialBugTags"/> |
575 | + <allow |
576 | interface="canonical.launchpad.webapp.interfaces.IAuthorization"/> |
577 | <require |
578 | permission="launchpad.Edit" |
579 | |
580 | === modified file 'lib/lp/registry/interfaces/distribution.py' |
581 | --- lib/lp/registry/interfaces/distribution.py 2010-02-04 21:18:32 +0000 |
582 | +++ lib/lp/registry/interfaces/distribution.py 2010-02-12 10:32:22 +0000 |
583 | @@ -532,13 +532,6 @@ |
584 | """An operating system distribution.""" |
585 | export_as_webservice_entry() |
586 | |
587 | -# Patch the official_bug_tags field to make sure that it's |
588 | -# writable from the API, and not readonly like its definition |
589 | -# in IHasBugs. |
590 | -writable_obt_field = copy_field(IDistribution['official_bug_tags']) |
591 | -writable_obt_field.readonly = False |
592 | -IDistribution._v_attrs['official_bug_tags'] = writable_obt_field |
593 | - |
594 | |
595 | class IBaseDistribution(IDistribution): |
596 | """A Distribution that is the base for other Distributions.""" |
597 | |
598 | === modified file 'lib/lp/registry/interfaces/distributionsourcepackage.py' |
599 | --- lib/lp/registry/interfaces/distributionsourcepackage.py 2009-12-05 18:37:28 +0000 |
600 | +++ lib/lp/registry/interfaces/distributionsourcepackage.py 2010-02-12 10:32:22 +0000 |
601 | @@ -21,7 +21,7 @@ |
602 | rename_parameters_as) |
603 | |
604 | from canonical.launchpad import _ |
605 | -from lp.bugs.interfaces.bugtarget import IBugTarget |
606 | +from lp.bugs.interfaces.bugtarget import IBugTarget, IHasOfficialBugTags |
607 | from lp.bugs.interfaces.bugtask import IBugTask |
608 | from lp.code.interfaces.hasbranches import IHasBranches, IHasMergeProposals |
609 | from lp.registry.interfaces.distribution import IDistribution |
610 | @@ -31,7 +31,8 @@ |
611 | |
612 | |
613 | class IDistributionSourcePackage(IBugTarget, IHasBranches, IHasMergeProposals, |
614 | - IStructuralSubscriptionTarget): |
615 | + IStructuralSubscriptionTarget, |
616 | + IHasOfficialBugTags): |
617 | """Represents a source package in a distribution. |
618 | |
619 | Create IDistributionSourcePackages by invoking |
620 | @@ -101,7 +102,7 @@ |
621 | """ |
622 | |
623 | def get_distroseries_packages(active_only=True): |
624 | - """Return a list of DistroSeriesSourcePackage objects, each |
625 | + """Return a list of DistroSeriesSourcePackage objects, each |
626 | representing this same source package in the series of this |
627 | distribution. |
628 | |
629 | |
630 | === modified file 'lib/lp/registry/interfaces/distroseries.py' |
631 | --- lib/lp/registry/interfaces/distroseries.py 2010-01-21 17:49:38 +0000 |
632 | +++ lib/lp/registry/interfaces/distroseries.py 2010-02-12 10:32:22 +0000 |
633 | @@ -28,7 +28,8 @@ |
634 | from lp.registry.interfaces.series import SeriesStatus |
635 | from lp.registry.interfaces.structuralsubscription import ( |
636 | IStructuralSubscriptionTarget) |
637 | -from lp.bugs.interfaces.bugtarget import IBugTarget, IHasBugs |
638 | +from lp.bugs.interfaces.bugtarget import ( |
639 | + IBugTarget, IHasBugs, IHasOfficialBugTags) |
640 | from lp.soyuz.interfaces.buildrecords import IHasBuildRecords |
641 | from lp.translations.interfaces.languagepack import ILanguagePack |
642 | from canonical.launchpad.interfaces.launchpad import ( |
643 | @@ -154,7 +155,8 @@ |
644 | |
645 | class IDistroSeriesPublic(IHasAppointedDriver, IHasDrivers, IHasOwner, |
646 | IBugTarget, ISpecificationGoal, IHasMilestones, |
647 | - IHasBuildRecords, ISeriesMixin): |
648 | + IHasBuildRecords, ISeriesMixin, |
649 | + IHasOfficialBugTags): |
650 | """Public IDistroSeries properties.""" |
651 | |
652 | id = Attribute("The distroseries's unique number.") |
653 | @@ -389,7 +391,7 @@ |
654 | def getDistroArchSeriesByProcessor(processor): |
655 | """Return the distroarchseries for this distroseries with the |
656 | given architecturetag from a `IProcessor`. |
657 | - |
658 | + |
659 | :param processor: An `IProcessor` |
660 | :return: An `IDistroArchSeries` or None when none was found. |
661 | """ |
662 | |
663 | === modified file 'lib/lp/registry/interfaces/milestone.py' |
664 | --- lib/lp/registry/interfaces/milestone.py 2010-01-27 19:12:10 +0000 |
665 | +++ lib/lp/registry/interfaces/milestone.py 2010-02-12 10:32:22 +0000 |
666 | @@ -21,7 +21,7 @@ |
667 | from lp.registry.interfaces.structuralsubscription import ( |
668 | IStructuralSubscriptionTarget) |
669 | from lp.registry.interfaces.productrelease import IProductRelease |
670 | -from lp.bugs.interfaces.bugtarget import IHasBugs |
671 | +from lp.bugs.interfaces.bugtarget import IHasBugs, IHasOfficialBugTags |
672 | from lp.bugs.interfaces.bugtask import IBugTask |
673 | from canonical.launchpad import _ |
674 | from canonical.launchpad.fields import ( |
675 | @@ -70,7 +70,8 @@ |
676 | return milestone |
677 | |
678 | |
679 | -class IMilestone(IHasBugs, IStructuralSubscriptionTarget): |
680 | +class IMilestone(IHasBugs, IStructuralSubscriptionTarget, |
681 | + IHasOfficialBugTags): |
682 | """A milestone, or a targeting point for bugs and other |
683 | release-management items that need coordination. |
684 | """ |
685 | |
686 | === modified file 'lib/lp/registry/interfaces/person.py' |
687 | --- lib/lp/registry/interfaces/person.py 2010-02-08 14:37:50 +0000 |
688 | +++ lib/lp/registry/interfaces/person.py 2010-02-12 10:32:22 +0000 |
689 | @@ -97,6 +97,7 @@ |
690 | from canonical.launchpad.webapp.interfaces import NameLookupFailed |
691 | from canonical.launchpad.webapp.authorization import check_permission |
692 | |
693 | +from lp.bugs.interfaces.bugtarget import IHasBugs |
694 | |
695 | PRIVATE_TEAM_PREFIX = 'private-' |
696 | |
697 | @@ -478,7 +479,7 @@ |
698 | class IPersonPublic(IHasBranches, IHasSpecifications, IHasMentoringOffers, |
699 | IHasMergeProposals, IHasLogo, IHasMugshot, IHasIcon, |
700 | IHasLocation, IHasRequestedReviews, IObjectWithLocation, |
701 | - IPrivacy): |
702 | + IPrivacy, IHasBugs): |
703 | """Public attributes for a Person.""" |
704 | |
705 | id = Int(title=_('ID'), required=True, readonly=True) |
706 | @@ -1017,18 +1018,6 @@ |
707 | used between TeamMembership and Person objects. |
708 | """ |
709 | |
710 | - def searchTasks(search_params, *args): |
711 | - """Search IBugTasks with the given search parameters. |
712 | - |
713 | - :search_params: a BugTaskSearchParams object |
714 | - :args: any number of BugTaskSearchParams objects |
715 | - |
716 | - If more than one BugTaskSearchParams is given, return the union of |
717 | - IBugTasks which match any of them. |
718 | - |
719 | - Return an iterable of matching results. |
720 | - """ |
721 | - |
722 | def getLatestMaintainedPackages(): |
723 | """Return `SourcePackageRelease`s maintained by this person. |
724 | |
725 | |
726 | === modified file 'lib/lp/registry/interfaces/product.py' |
727 | --- lib/lp/registry/interfaces/product.py 2010-01-20 13:58:45 +0000 |
728 | +++ lib/lp/registry/interfaces/product.py 2010-02-12 10:32:22 +0000 |
729 | @@ -724,13 +724,6 @@ |
730 | IProject['products'].value_type = Reference(IProduct) |
731 | IProductRelease['product'].schema = IProduct |
732 | |
733 | -# Patch the official_bug_tags field to make sure that it's |
734 | -# writable from the API, and not readonly like its definition |
735 | -# in IHasBugs. |
736 | -writable_obt_field = copy_field(IProduct['official_bug_tags']) |
737 | -writable_obt_field.readonly = False |
738 | -IProduct._v_attrs['official_bug_tags'] = writable_obt_field |
739 | - |
740 | |
741 | class IProductSet(Interface): |
742 | export_as_webservice_collection(IProduct) |
743 | |
744 | === modified file 'lib/lp/registry/interfaces/productseries.py' |
745 | --- lib/lp/registry/interfaces/productseries.py 2009-12-13 11:55:40 +0000 |
746 | +++ lib/lp/registry/interfaces/productseries.py 2010-02-12 10:32:22 +0000 |
747 | @@ -24,7 +24,7 @@ |
748 | from lp.registry.interfaces.structuralsubscription import ( |
749 | IStructuralSubscriptionTarget) |
750 | from lp.code.interfaces.branch import IBranch |
751 | -from lp.bugs.interfaces.bugtarget import IBugTarget |
752 | +from lp.bugs.interfaces.bugtarget import IBugTarget, IHasOfficialBugTags |
753 | from lp.registry.interfaces.series import SeriesStatus |
754 | from canonical.launchpad.interfaces.launchpad import ( |
755 | IHasAppointedDriver, IHasDrivers) |
756 | @@ -93,7 +93,8 @@ |
757 | |
758 | |
759 | class IProductSeriesPublic(IHasAppointedDriver, IHasDrivers, IHasOwner, |
760 | - IBugTarget, ISpecificationGoal, IHasMilestones): |
761 | + IBugTarget, ISpecificationGoal, IHasMilestones, |
762 | + IHasOfficialBugTags): |
763 | """Public IProductSeries properties.""" |
764 | # XXX Mark Shuttleworth 2004-10-14: Would like to get rid of id in |
765 | # interfaces, as soon as SQLobject allows using the object directly |
766 | |
767 | === modified file 'lib/lp/registry/interfaces/project.py' |
768 | --- lib/lp/registry/interfaces/project.py 2009-12-05 18:37:28 +0000 |
769 | +++ lib/lp/registry/interfaces/project.py 2010-02-12 10:32:22 +0000 |
770 | @@ -24,7 +24,7 @@ |
771 | from lp.code.interfaces.branchvisibilitypolicy import ( |
772 | IHasBranchVisibilityPolicy) |
773 | from lp.code.interfaces.hasbranches import IHasBranches, IHasMergeProposals |
774 | -from lp.bugs.interfaces.bugtarget import IHasBugs |
775 | +from lp.bugs.interfaces.bugtarget import IHasBugs, IHasOfficialBugTags |
776 | from lp.registry.interfaces.karma import IKarmaContext |
777 | from canonical.launchpad.interfaces.launchpad import ( |
778 | IHasAppointedDriver, IHasDrivers, IHasIcon, IHasLogo, IHasMugshot) |
779 | @@ -64,7 +64,8 @@ |
780 | IHasDrivers, IHasBranchVisibilityPolicy, IHasIcon, IHasLogo, |
781 | IHasMentoringOffers, IHasMergeProposals, IHasMilestones, IHasMugshot, |
782 | IHasOwner, IHasSpecifications, IHasSprints, IHasTranslationGroup, |
783 | - IMakesAnnouncements, IKarmaContext, IPillar, IRootContext): |
784 | + IMakesAnnouncements, IKarmaContext, IPillar, IRootContext, |
785 | + IHasOfficialBugTags): |
786 | """Public IProject properties.""" |
787 | |
788 | id = Int(title=_('ID'), readonly=True) |
789 | |
790 | === modified file 'lib/lp/registry/interfaces/sourcepackage.py' |
791 | --- lib/lp/registry/interfaces/sourcepackage.py 2010-02-04 21:18:32 +0000 |
792 | +++ lib/lp/registry/interfaces/sourcepackage.py 2010-02-12 10:32:22 +0000 |
793 | @@ -21,7 +21,7 @@ |
794 | from lazr.enum import DBEnumeratedType, DBItem |
795 | |
796 | from canonical.launchpad import _ |
797 | -from lp.bugs.interfaces.bugtarget import IBugTarget |
798 | +from lp.bugs.interfaces.bugtarget import IBugTarget, IHasOfficialBugTags |
799 | from lp.code.interfaces.hasbranches import IHasBranches, IHasMergeProposals |
800 | from lp.soyuz.interfaces.component import IComponent |
801 | from lazr.restful.fields import Reference, ReferenceChoice |
802 | @@ -31,7 +31,8 @@ |
803 | operation_returns_entry, REQUEST_USER) |
804 | |
805 | |
806 | -class ISourcePackage(IBugTarget, IHasBranches, IHasMergeProposals): |
807 | +class ISourcePackage(IBugTarget, IHasBranches, IHasMergeProposals, |
808 | + IHasOfficialBugTags): |
809 | """A SourcePackage. See the MagicSourcePackage specification. This |
810 | interface preserves as much as possible of the old SourcePackage |
811 | interface from the SourcePackage table, with the new table-less |
812 | |
813 | === modified file 'lib/lp/registry/model/person.py' |
814 | --- lib/lp/registry/model/person.py 2010-02-10 23:14:56 +0000 |
815 | +++ lib/lp/registry/model/person.py 2010-02-12 10:32:22 +0000 |
816 | @@ -84,7 +84,7 @@ |
817 | from lp.code.model.hasbranches import ( |
818 | HasBranchesMixin, HasMergeProposalsMixin, HasRequestedReviewsMixin) |
819 | from lp.bugs.interfaces.bugtask import ( |
820 | - BugTaskSearchParams, IBugTaskSet) |
821 | + BugTaskSearchParams, IBugTaskSet, IllegalRelatedBugTasksParams) |
822 | from lp.bugs.interfaces.bugtarget import IBugTarget |
823 | from lp.registry.interfaces.codeofconduct import ( |
824 | ISignedCodeOfConductSet) |
825 | @@ -128,7 +128,8 @@ |
826 | |
827 | from lp.soyuz.model.archive import Archive |
828 | from lp.registry.model.codeofconduct import SignedCodeOfConduct |
829 | -from lp.bugs.model.bugtask import BugTask |
830 | +from lp.bugs.model.bugtask import ( |
831 | + BugTask, get_related_bugtasks_search_params) |
832 | from canonical.launchpad.database.emailaddress import ( |
833 | EmailAddress, HasOwnerMixin) |
834 | from lp.registry.model.karma import KarmaCache, KarmaTotalCache |
835 | @@ -838,6 +839,21 @@ |
836 | |
837 | def searchTasks(self, search_params, *args, **kwargs): |
838 | """See `IHasBugs`.""" |
839 | + if search_params is None and len(args) == 0: |
840 | + # this method is called via webapi directly |
841 | + # calling this method on a Person object directly via the |
842 | + # webservice API means searching for user related tasks |
843 | + user = kwargs.pop('user') |
844 | + try: |
845 | + search_params = get_related_bugtasks_search_params( |
846 | + user, self, **kwargs) |
847 | + except IllegalRelatedBugTasksParams, e: |
848 | + # dirty hack, marking an exception with a HTTP error |
849 | + # only works if the exception is raised in the exported |
850 | + # method, see docstring of |
851 | + # `lazr.restful.declarations.webservice_error()` |
852 | + raise e |
853 | + return getUtility(IBugTaskSet).search(*search_params) |
854 | if len(kwargs) > 0: |
855 | # if keyword arguments are supplied, use the deault |
856 | # implementation in HasBugsBase. |
857 | |
858 | === modified file 'lib/lp/registry/tests/test_person.py' |
859 | --- lib/lp/registry/tests/test_person.py 2009-12-08 17:53:37 +0000 |
860 | +++ lib/lp/registry/tests/test_person.py 2010-02-12 10:32:22 +0000 |
861 | @@ -29,6 +29,8 @@ |
862 | from lp.registry.model.structuralsubscription import ( |
863 | StructuralSubscription) |
864 | from lp.registry.model.person import Person |
865 | +from lp.bugs.model.bugtask import get_related_bugtasks_search_params |
866 | +from lp.bugs.interfaces.bugtask import IllegalRelatedBugTasksParams |
867 | from lp.answers.model.answercontact import AnswerContact |
868 | from lp.blueprints.model.specification import Specification |
869 | from lp.testing import TestCaseWithFactory |
870 | @@ -495,5 +497,105 @@ |
871 | name='/john') |
872 | |
873 | |
874 | +class TestPersonRelatedBugTaskSearch(TestCaseWithFactory): |
875 | + |
876 | + layer = LaunchpadFunctionalLayer |
877 | + |
878 | + def setUp(self): |
879 | + super(TestPersonRelatedBugTaskSearch, self).setUp() |
880 | + self.user = self.factory.makePerson(displayname="User") |
881 | + self.context = self.factory.makePerson(displayname="Context") |
882 | + |
883 | + def checkUserFields( |
884 | + self, params, assignee=None, bug_subscriber=None, |
885 | + owner=None, bug_commenter=None, bug_reporter=None): |
886 | + self.failUnlessEqual(assignee, params.assignee) |
887 | + # fromSearchForm() takes a bug_subscriber parameter, but saves |
888 | + # it as subscriber on the parameter object. |
889 | + self.failUnlessEqual(bug_subscriber, params.subscriber) |
890 | + self.failUnlessEqual(owner, params.owner) |
891 | + self.failUnlessEqual(bug_commenter, params.bug_commenter) |
892 | + self.failUnlessEqual(bug_reporter, params.bug_reporter) |
893 | + |
894 | + def test_get_related_bugtasks_search_params(self): |
895 | + # With no specified options, get_related_bugtasks_search_params() |
896 | + # returns 4 BugTaskSearchParams objects, each with a different |
897 | + # user field set. |
898 | + search_params = get_related_bugtasks_search_params(self.user, self.context) |
899 | + self.assertEqual(len(search_params), 4) |
900 | + self.checkUserFields( |
901 | + search_params[0], assignee=self.context) |
902 | + self.checkUserFields( |
903 | + search_params[1], bug_subscriber=self.context) |
904 | + self.checkUserFields( |
905 | + search_params[2], owner=self.context, bug_reporter=self.context) |
906 | + self.checkUserFields( |
907 | + search_params[3], bug_commenter=self.context) |
908 | + |
909 | + def test_get_related_bugtasks_search_params_with_assignee(self): |
910 | + # With assignee specified, get_related_bugtasks_search_params() returns |
911 | + # 3 BugTaskSearchParams objects. |
912 | + search_params = get_related_bugtasks_search_params( |
913 | + self.user, self.context, assignee=self.user) |
914 | + self.assertEqual(len(search_params), 3) |
915 | + self.checkUserFields( |
916 | + search_params[0], assignee=self.user, bug_subscriber=self.context) |
917 | + self.checkUserFields( |
918 | + search_params[1], assignee=self.user, owner=self.context, |
919 | + bug_reporter=self.context) |
920 | + self.checkUserFields( |
921 | + search_params[2], assignee=self.user, bug_commenter=self.context) |
922 | + |
923 | + def test_get_related_bugtasks_search_params_with_owner(self): |
924 | + # With owner specified, get_related_bugtasks_search_params() returns |
925 | + # 3 BugTaskSearchParams objects. |
926 | + search_params = get_related_bugtasks_search_params( |
927 | + self.user, self.context, owner=self.user) |
928 | + self.assertEqual(len(search_params), 3) |
929 | + self.checkUserFields( |
930 | + search_params[0], owner=self.user, assignee=self.context) |
931 | + self.checkUserFields( |
932 | + search_params[1], owner=self.user, bug_subscriber=self.context) |
933 | + self.checkUserFields( |
934 | + search_params[2], owner=self.user, bug_commenter=self.context) |
935 | + |
936 | + def test_get_related_bugtasks_search_params_with_bug_reporter(self): |
937 | + # With bug reporter specified, get_related_bugtasks_search_params() |
938 | + # returns 4 BugTaskSearchParams objects, but the bug reporter |
939 | + # is overwritten in one instance. |
940 | + search_params = get_related_bugtasks_search_params( |
941 | + self.user, self.context, bug_reporter=self.user) |
942 | + self.assertEqual(len(search_params), 4) |
943 | + self.checkUserFields( |
944 | + search_params[0], bug_reporter=self.user, |
945 | + assignee=self.context) |
946 | + self.checkUserFields( |
947 | + search_params[1], bug_reporter=self.user, |
948 | + bug_subscriber=self.context) |
949 | + # When a BugTaskSearchParams is prepared with the owner filled |
950 | + # in, the bug reporter is overwritten to match. |
951 | + self.checkUserFields( |
952 | + search_params[2], bug_reporter=self.context, |
953 | + owner=self.context) |
954 | + self.checkUserFields( |
955 | + search_params[3], bug_reporter=self.user, |
956 | + bug_commenter=self.context) |
957 | + |
958 | + def test_get_related_bugtasks_search_params_illegal(self): |
959 | + self.assertRaises( |
960 | + IllegalRelatedBugTasksParams, |
961 | + get_related_bugtasks_search_params, self.user, self.context, |
962 | + assignee=self.user, owner=self.user, bug_commenter=self.user, |
963 | + bug_subscriber=self.user) |
964 | + |
965 | + def test_get_related_bugtasks_search_params_illegal_context(self): |
966 | + # in case the `context` argument is not of type IPerson an |
967 | + # AssertionError is raised |
968 | + self.assertRaises( |
969 | + AssertionError, |
970 | + get_related_bugtasks_search_params, self.user, "Username", |
971 | + assignee=self.user) |
972 | + |
973 | + |
974 | def test_suite(): |
975 | return unittest.TestLoader().loadTestsFromName(__name__) |
This is the first step to fix bug 282178 (making Person.searchTasks available over the webservice API)
This branch includes three different types of changes: searchTasks( ) in a way that it can be called without BugTaskSearchParams and only by giving keyword arguments. Person. searchTasks( ) returns the same list of tasks which is shown as "related Bugs" in bugs.launchpad. net/~username.
* implemented Person.
* as a person cannot have official_bug_tags this field is moved away from IHasBugs - thanks to Gavin Panella for helping me with the related changes.
* added testcases for the internal API and the webservice API
The seconds step to fix the bug mentioned above will be to add (alias like) methods to IPerson (searchCommente dTasks( ), searchSubscribe dTasks( ) ...)