Merge lp:~stevenk/launchpad/filter-spec-lists into lp:launchpad

Proposed by Steve Kowalik
Status: Merged
Merged at revision: 16505
Proposed branch: lp:~stevenk/launchpad/filter-spec-lists
Merge into: lp:launchpad
Diff against target: 368 lines (+103/-41)
11 files modified
lib/lp/bugs/browser/bug.py (+5/-1)
lib/lp/bugs/interfaces/bug.py (+4/-2)
lib/lp/bugs/model/bug.py (+14/-5)
lib/lp/bugs/model/tests/test_bugtask.py (+33/-10)
lib/lp/bugs/templates/bug-portlet-specs.pt (+2/-2)
lib/lp/code/browser/branch.py (+6/-2)
lib/lp/code/browser/branchmergeproposal.py (+6/-3)
lib/lp/code/interfaces/branch.py (+4/-1)
lib/lp/code/model/branch.py (+22/-8)
lib/lp/code/model/tests/test_branch.py (+5/-5)
lib/lp/code/templates/branch-macros.pt (+2/-2)
To merge this branch: bzr merge lp:~stevenk/launchpad/filter-spec-lists
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+149748@code.launchpad.net

Description of the change

Change IBug.specifications so that it returns a resultset of the linked specifications that the user can view.

Add a new method, IBranch.getSpecificationLinks() that will return a resultset of the specification links themselves (since that's what IBranch.spec_links does) that the user can view. IBranch.spec_links continues to live since it's exported via the API.

Clean up some whitespace that caught my eye.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) wrote :

I wouldn't use an explicit join in getSpecifications (particularly since you have the join condition and where clause around the wrong way), but otherwise fine.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/bugs/browser/bug.py'
--- lib/lp/bugs/browser/bug.py 2012-09-28 01:25:36 +0000
+++ lib/lp/bugs/browser/bug.py 2013-02-21 07:22:22 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2012 Canonical Ltd. This software is licensed under the1# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""IBug related view classes."""4"""IBug related view classes."""
@@ -536,6 +536,10 @@
536 """536 """
537 return getUtility(ILaunchBag).bugtask537 return getUtility(ILaunchBag).bugtask
538538
539 @property
540 def specifications(self):
541 return self.context.getSpecifications(self.user)
542
539543
540class BugInformationTypePortletView(InformationTypePortletMixin,544class BugInformationTypePortletView(InformationTypePortletMixin,
541 LaunchpadView):545 LaunchpadView):
542546
=== modified file 'lib/lp/bugs/interfaces/bug.py'
--- lib/lp/bugs/interfaces/bug.py 2013-02-06 04:22:43 +0000
+++ lib/lp/bugs/interfaces/bug.py 2013-02-21 07:22:22 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2012 Canonical Ltd. This software is licensed under the1# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Interfaces related to bugs."""4"""Interfaces related to bugs."""
@@ -313,7 +313,6 @@
313 initial_message = Attribute(313 initial_message = Attribute(
314 "The message that was specified when creating the bug")314 "The message that was specified when creating the bug")
315 questions = Attribute("List of questions related to this bug.")315 questions = Attribute("List of questions related to this bug.")
316 specifications = Attribute("List of related specifications.")
317 tags = exported(List(316 tags = exported(List(
318 title=_("Tags"),317 title=_("Tags"),
319 description=_("Space-separated keywords for classifying "318 description=_("Space-separated keywords for classifying "
@@ -383,6 +382,9 @@
383 value_type=Reference(schema=IMessage)),382 value_type=Reference(schema=IMessage)),
384 exported_as='messages'))383 exported_as='messages'))
385384
385 def getSpecifications(user):
386 """List of related specifications that the user can view."""
387
386 def _indexed_messages(include_content=False, include_parents=False):388 def _indexed_messages(include_content=False, include_parents=False):
387 """Low level query for getting bug messages.389 """Low level query for getting bug messages.
388390
389391
=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py 2013-02-06 04:56:33 +0000
+++ lib/lp/bugs/model/bug.py 2013-02-21 07:22:22 +0000
@@ -98,6 +98,11 @@
98from lp.app.interfaces.services import IService98from lp.app.interfaces.services import IService
99from lp.app.model.launchpad import InformationTypeMixin99from lp.app.model.launchpad import InformationTypeMixin
100from lp.app.validators import LaunchpadValidationError100from lp.app.validators import LaunchpadValidationError
101from lp.blueprints.model.specification import Specification
102from lp.blueprints.model.specificationbug import SpecificationBug
103from lp.blueprints.model.specificationsearch import (
104 get_specification_privacy_filter,
105 )
101from lp.bugs.adapters.bug import convert_to_information_type106from lp.bugs.adapters.bug import convert_to_information_type
102from lp.bugs.adapters.bugchange import (107from lp.bugs.adapters.bugchange import (
103 BranchLinkedToBug,108 BranchLinkedToBug,
@@ -360,11 +365,7 @@
360 cves = SQLRelatedJoin('Cve', intermediateTable='BugCve',365 cves = SQLRelatedJoin('Cve', intermediateTable='BugCve',
361 orderBy='sequence', joinColumn='bug', otherColumn='cve')366 orderBy='sequence', joinColumn='bug', otherColumn='cve')
362 cve_links = SQLMultipleJoin('BugCve', joinColumn='bug', orderBy='id')367 cve_links = SQLMultipleJoin('BugCve', joinColumn='bug', orderBy='id')
363 duplicates = SQLMultipleJoin(368 duplicates = SQLMultipleJoin('Bug', joinColumn='duplicateof', orderBy='id')
364 'Bug', joinColumn='duplicateof', orderBy='id')
365 specifications = SQLRelatedJoin('Specification', joinColumn='bug',
366 otherColumn='specification', intermediateTable='SpecificationBug',
367 orderBy='-datecreated')
368 questions = SQLRelatedJoin('Question', joinColumn='bug',369 questions = SQLRelatedJoin('Question', joinColumn='bug',
369 otherColumn='question', intermediateTable='QuestionBug',370 otherColumn='question', intermediateTable='QuestionBug',
370 orderBy='-datecreated')371 orderBy='-datecreated')
@@ -379,6 +380,14 @@
379 heat_last_updated = UtcDateTimeCol(default=None)380 heat_last_updated = UtcDateTimeCol(default=None)
380 latest_patch_uploaded = UtcDateTimeCol(default=None)381 latest_patch_uploaded = UtcDateTimeCol(default=None)
381382
383 def getSpecifications(self, user):
384 """See `IBug`."""
385 return IStore(SpecificationBug).find(
386 Specification,
387 SpecificationBug.bugID == self.id,
388 SpecificationBug.specificationID == Specification.id,
389 *get_specification_privacy_filter(user))
390
382 @property391 @property
383 def security_related(self):392 def security_related(self):
384 return self.information_type in SECURITY_INFORMATION_TYPES393 return self.information_type in SECURITY_INFORMATION_TYPES
385394
=== modified file 'lib/lp/bugs/model/tests/test_bugtask.py'
--- lib/lp/bugs/model/tests/test_bugtask.py 2012-10-18 14:43:24 +0000
+++ lib/lp/bugs/model/tests/test_bugtask.py 2013-02-21 07:22:22 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2012 Canonical Ltd. This software is licensed under the1# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4__metaclass__ = type4__metaclass__ = type
@@ -528,7 +528,7 @@
528 ])528 ])
529529
530530
531class TestBugTaskPrivacy(TestCase):531class TestBugTaskPrivacy(TestCaseWithFactory):
532 """Verify that the bug is either private or public.532 """Verify that the bug is either private or public.
533533
534 XXX: rharding 2012-05-14 bug=999298: These tests are ported from doctests534 XXX: rharding 2012-05-14 bug=999298: These tests are ported from doctests
@@ -553,14 +553,15 @@
553 ubuntu_team = getUtility(IPersonSet).getByEmail('support@ubuntu.com')553 ubuntu_team = getUtility(IPersonSet).getByEmail('support@ubuntu.com')
554 bug_upstream_firefox_crashes.bug.subscribe(ubuntu_team, ubuntu_team)554 bug_upstream_firefox_crashes.bug.subscribe(ubuntu_team, ubuntu_team)
555555
556 old_state = Snapshot(bug_upstream_firefox_crashes.bug,556 old_state = Snapshot(
557 providing=IBug)557 bug_upstream_firefox_crashes.bug, providing=IBug)
558 self.assertTrue(bug_upstream_firefox_crashes.bug.setPrivate(True,558 self.assertTrue(
559 foobar))559 bug_upstream_firefox_crashes.bug.setPrivate(True, foobar))
560560
561 bug_set_private = ObjectModifiedEvent(bug_upstream_firefox_crashes.bug,561 bug_set_private = ObjectModifiedEvent(
562 old_state,562 bug_upstream_firefox_crashes.bug, old_state,
563 ["id", "title", "private"])563 ["id", "title", "private"])
564
564 notify(bug_set_private)565 notify(bug_set_private)
565 flush_database_updates()566 flush_database_updates()
566567
@@ -671,6 +672,28 @@
671 bug_upstream_firefox_crashes.transitionToStatus(672 bug_upstream_firefox_crashes.transitionToStatus(
672 BugTaskStatus.NEW, getUtility(ILaunchBag).user)673 BugTaskStatus.NEW, getUtility(ILaunchBag).user)
673674
675 def _createBugAndSpecification(self):
676 bug = self.factory.makeBug()
677 spec = self.factory.makeSpecification(
678 information_type=InformationType.PROPRIETARY)
679 with person_logged_in(spec.product.owner):
680 spec.linkBug(bug)
681 return spec, bug
682
683 def test_bug_specifications_is_filtered_for_anonymous(self):
684 spec, bug = self._createBugAndSpecification()
685 self.assertContentEqual([], bug.getSpecifications(None))
686
687 def test_bug_specifications_is_filtered_for_unknown_user(self):
688 spec, bug = self._createBugAndSpecification()
689 self.assertContentEqual(
690 [], bug.getSpecifications(self.factory.makePerson()))
691
692 def test_bug_specifications_for_authorised_user(self):
693 spec, bug = self._createBugAndSpecification()
694 self.assertContentEqual(
695 [spec], bug.getSpecifications(spec.product.owner))
696
674697
675class TestBugTaskDelta(TestCaseWithFactory):698class TestBugTaskDelta(TestCaseWithFactory):
676699
677700
=== modified file 'lib/lp/bugs/templates/bug-portlet-specs.pt'
--- lib/lp/bugs/templates/bug-portlet-specs.pt 2009-10-29 21:39:12 +0000
+++ lib/lp/bugs/templates/bug-portlet-specs.pt 2013-02-21 07:22:22 +0000
@@ -3,10 +3,10 @@
3 xmlns:metal="http://xml.zope.org/namespaces/metal"3 xmlns:metal="http://xml.zope.org/namespaces/metal"
4 xmlns:i18n="http://xml.zope.org/namespaces/i18n"4 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
5 class="portlet vertical" id="portlet-blueprints"5 class="portlet vertical" id="portlet-blueprints"
6 tal:condition="context/specifications">6 tal:condition="view/specifications">
7 <h2>Related blueprints</h2>7 <h2>Related blueprints</h2>
8 <ul>8 <ul>
9 <li tal:repeat="spec context/specifications"9 <li tal:repeat="spec view/specifications"
10 tal:content="structure spec/fmt:link" />10 tal:content="structure spec/fmt:link" />
11 </ul>11 </ul>
12</div>12</div>
1313
=== modified file 'lib/lp/code/browser/branch.py'
--- lib/lp/code/browser/branch.py 2012-12-12 04:59:52 +0000
+++ lib/lp/code/browser/branch.py 2013-02-21 07:22:22 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2012 Canonical Ltd. This software is licensed under the1# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Branch views."""4"""Branch views."""
@@ -354,7 +354,7 @@
354 return Link('+linkbug', text, icon='add')354 return Link('+linkbug', text, icon='add')
355355
356 def link_blueprint(self):356 def link_blueprint(self):
357 if self.context.spec_links:357 if list(self.context.getSpecificationLinks(self.user)):
358 text = 'Link to another blueprint'358 text = 'Link to another blueprint'
359 else:359 else:
360 text = 'Link to a blueprint'360 text = 'Link to a blueprint'
@@ -689,6 +689,10 @@
689 self.context.branch, IBranch['lifecycle_status'],689 self.context.branch, IBranch['lifecycle_status'],
690 header='Change status to', css_class_prefix='branchstatus')690 header='Change status to', css_class_prefix='branchstatus')
691691
692 @property
693 def spec_links(self):
694 return self.context.getSpecificationLinks(self.user)
695
692696
693class BranchInProductView(BranchView):697class BranchInProductView(BranchView):
694698
695699
=== modified file 'lib/lp/code/browser/branchmergeproposal.py'
--- lib/lp/code/browser/branchmergeproposal.py 2013-01-07 02:40:55 +0000
+++ lib/lp/code/browser/branchmergeproposal.py 2013-02-21 07:22:22 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2012 Canonical Ltd. This software is licensed under the1# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Views, navigation and actions for BranchMergeProposals."""4"""Views, navigation and actions for BranchMergeProposals."""
@@ -719,8 +719,11 @@
719 def has_bug_or_spec(self):719 def has_bug_or_spec(self):
720 """Return whether or not the merge proposal has a linked bug or spec.720 """Return whether or not the merge proposal has a linked bug or spec.
721 """721 """
722 branch = self.context.source_branch722 return self.linked_bugtasks or self.spec_links
723 return self.linked_bugtasks or branch.spec_links723
724 @property
725 def spec_links(self):
726 return self.context.source_branch.getSpecificationLinks(self.user)
724727
725 @cachedproperty728 @cachedproperty
726 def linked_bugtasks(self):729 def linked_bugtasks(self):
727730
=== modified file 'lib/lp/code/interfaces/branch.py'
--- lib/lp/code/interfaces/branch.py 2013-01-16 06:41:43 +0000
+++ lib/lp/code/interfaces/branch.py 2013-02-21 07:22:22 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2012 Canonical Ltd. This software is licensed under the1# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Branch interfaces."""4"""Branch interfaces."""
@@ -482,6 +482,9 @@
482 value_type=Reference(Interface)), # Really ISpecificationBranch482 value_type=Reference(Interface)), # Really ISpecificationBranch
483 as_of="beta")483 as_of="beta")
484484
485 def getSpecificationLinks(user):
486 """Fetch the `ISpecificationBranch`'s that the user can view."""
487
485 @call_with(registrant=REQUEST_USER)488 @call_with(registrant=REQUEST_USER)
486 @operation_parameters(489 @operation_parameters(
487 spec=Reference(schema=Interface)) # Really ISpecification490 spec=Reference(schema=Interface)) # Really ISpecification
488491
=== modified file 'lib/lp/code/model/branch.py'
--- lib/lp/code/model/branch.py 2013-02-14 05:36:37 +0000
+++ lib/lp/code/model/branch.py 2013-02-21 07:22:22 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2012 Canonical Ltd. This software is licensed under the1# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4__metaclass__ = type4__metaclass__ = type
@@ -66,6 +66,11 @@
66 IPrivacy,66 IPrivacy,
67 )67 )
68from lp.app.interfaces.services import IService68from lp.app.interfaces.services import IService
69from lp.blueprints.model.specification import Specification
70from lp.blueprints.model.specificationbranch import SpecificationBranch
71from lp.blueprints.model.specificationsearch import (
72 get_specification_privacy_filter,
73 )
69from lp.bugs.interfaces.bugtask import IBugTaskSet74from lp.bugs.interfaces.bugtask import IBugTaskSet
70from lp.bugs.interfaces.bugtaskfilter import filter_bugtasks_by_context75from lp.bugs.interfaces.bugtaskfilter import filter_bugtasks_by_context
71from lp.bugs.interfaces.bugtasksearch import BugTaskSearchParams76from lp.bugs.interfaces.bugtasksearch import BugTaskSearchParams
@@ -438,9 +443,20 @@
438 """See `IBranch`."""443 """See `IBranch`."""
439 return bug.unlinkBranch(self, user)444 return bug.unlinkBranch(self, user)
440445
441 spec_links = SQLMultipleJoin('SpecificationBranch',446 spec_links = SQLMultipleJoin(
442 joinColumn='branch',447 'SpecificationBranch', joinColumn='branch', orderBy='id')
443 orderBy='id')448
449 def getSpecificationLinks(self, user):
450 """See `IBranch`."""
451 tables = [
452 SpecificationBranch,
453 Join(
454 Specification,
455 SpecificationBranch.specificationID == Specification.id)]
456 return Store.of(self).using(*tables).find(
457 SpecificationBranch,
458 SpecificationBranch.branchID == self.id,
459 *get_specification_privacy_filter(user))
444460
445 def linkSpecification(self, spec, registrant):461 def linkSpecification(self, spec, registrant):
446 """See `IBranch`."""462 """See `IBranch`."""
@@ -459,8 +475,7 @@
459 @property475 @property
460 def active_landing_targets(self):476 def active_landing_targets(self):
461 """Merge proposals not in final states where this branch is source."""477 """Merge proposals not in final states where this branch is source."""
462 store = Store.of(self)478 return Store.of(self).find(
463 return store.find(
464 BranchMergeProposal, BranchMergeProposal.source_branch == self,479 BranchMergeProposal, BranchMergeProposal.source_branch == self,
465 Not(BranchMergeProposal.queue_status.is_in(480 Not(BranchMergeProposal.queue_status.is_in(
466 BRANCH_MERGE_PROPOSAL_FINAL_STATES)))481 BRANCH_MERGE_PROPOSAL_FINAL_STATES)))
@@ -615,8 +630,7 @@
615630
616 def getStackedBranches(self):631 def getStackedBranches(self):
617 """See `IBranch`."""632 """See `IBranch`."""
618 store = Store.of(self)633 return Store.of(self).find(Branch, Branch.stacked_on == self)
619 return store.find(Branch, Branch.stacked_on == self)
620634
621 def getStackedOnBranches(self):635 def getStackedOnBranches(self):
622 """See `IBranch`."""636 """See `IBranch`."""
623637
=== modified file 'lib/lp/code/model/tests/test_branch.py'
--- lib/lp/code/model/tests/test_branch.py 2013-01-07 02:40:55 +0000
+++ lib/lp/code/model/tests/test_branch.py 2013-02-21 07:22:22 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2012 Canonical Ltd. This software is licensed under the1# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Tests for Branches."""4"""Tests for Branches."""
@@ -1573,10 +1573,10 @@
1573 spec2.linkBranch(self.branch, self.branch.owner)1573 spec2.linkBranch(self.branch, self.branch.owner)
1574 spec2_branch_id = self.branch.spec_links[1].id1574 spec2_branch_id = self.branch.spec_links[1].id
1575 self.branch.destroySelf(break_references=True)1575 self.branch.destroySelf(break_references=True)
1576 self.assertRaises(SQLObjectNotFound, SpecificationBranch.get,1576 self.assertRaises(
1577 spec1_branch_id)1577 SQLObjectNotFound, SpecificationBranch.get, spec1_branch_id)
1578 self.assertRaises(SQLObjectNotFound, SpecificationBranch.get,1578 self.assertRaises(
1579 spec2_branch_id)1579 SQLObjectNotFound, SpecificationBranch.get, spec2_branch_id)
15801580
1581 def test_branchWithSeriesRequirements(self):1581 def test_branchWithSeriesRequirements(self):
1582 """Deletion requirements for a series' branch are right."""1582 """Deletion requirements for a series' branch are right."""
15831583
=== modified file 'lib/lp/code/templates/branch-macros.pt'
--- lib/lp/code/templates/branch-macros.pt 2012-08-07 07:07:02 +0000
+++ lib/lp/code/templates/branch-macros.pt 2013-02-21 07:22:22 +0000
@@ -76,7 +76,7 @@
76 <metal:branch-link use-macro="branch/@@+macros/bug-branch-links"/>76 <metal:branch-link use-macro="branch/@@+macros/bug-branch-links"/>
77 </tal:branch-link>77 </tal:branch-link>
7878
79 <div tal:repeat="spec_link mergeproposal/source_branch/spec_links">79 <div tal:repeat="spec_link view/spec_links">
80 <img src="/@@/blueprint"80 <img src="/@@/blueprint"
81 tal:replace="structure spec_link/specification/image:icon" />81 tal:replace="structure spec_link/specification/image:icon" />
82 <a tal:attributes="href spec_link/specification/fmt:url:blueprints"82 <a tal:attributes="href spec_link/specification/fmt:url:blueprints"
@@ -140,7 +140,7 @@
140 show_edit - show the edit form140 show_edit - show the edit form
141 </tal:comment>141 </tal:comment>
142142
143 <tal:spec-tasks repeat="spec_branch branch/spec_links">143 <tal:spec-tasks repeat="spec_branch view/spec_links">
144 <div tal:define="has_edit_permission spec_branch/required:launchpad.AnyPerson;144 <div tal:define="has_edit_permission spec_branch/required:launchpad.AnyPerson;
145 show_edit show_edit|nothing;145 show_edit show_edit|nothing;
146 show_edit python: show_edit and has_edit_permission;146 show_edit python: show_edit and has_edit_permission;