launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #01807
lp:~thumper/launchpad/blueprint-update-lifecycle-status-with-event into lp:launchpad/devel
Tim Penhey has proposed merging lp:~thumper/launchpad/blueprint-update-lifecycle-status-with-event into lp:launchpad/devel.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
More yak shaving for blueprints.
On this yak:
* Fixed the indentation of the primary configure.zcml file
* Moved some browser configuration into the browser/configure.zcml
* Change the updateLifecycleStatus to be called due to an object modified event
* Added a test to show that the lifecycle status is being updated
* Some lint cleanup in model/specification.py
tests:
lp.blueprints
--
https://code.launchpad.net/~thumper/launchpad/blueprint-update-lifecycle-status-with-event/+merge/40048
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~thumper/launchpad/blueprint-update-lifecycle-status-with-event into lp:launchpad/devel.
=== modified file 'lib/lp/blueprints/browser/configure.zcml'
--- lib/lp/blueprints/browser/configure.zcml 2010-10-03 15:30:06 +0000
+++ lib/lp/blueprints/browser/configure.zcml 2010-11-04 04:01:17 +0000
@@ -8,6 +8,21 @@
xmlns:i18n="http://namespaces.zope.org/i18n"
xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc"
i18n_domain="launchpad">
+
+ <adapter
+ name="blueprints"
+ provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
+ for="lp.blueprints.interfaces.specificationtarget.IHasSpecifications"
+ factory="lp.blueprints.browser.specificationtarget.BlueprintsVHostBreadcrumb"
+ permission="zope.Public"/>
+ <adapter
+ name="blueprints"
+ provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
+ for="lp.registry.interfaces.person.IPerson"
+ factory="lp.blueprints.browser.specificationtarget.BlueprintsVHostBreadcrumb"
+ permission="zope.Public"/>
+
+
<browser:navigation
module="lp.blueprints.browser.sprint"
classes="
=== modified file 'lib/lp/blueprints/browser/specification.py'
--- lib/lp/blueprints/browser/specification.py 2010-11-01 03:32:29 +0000
+++ lib/lp/blueprints/browser/specification.py 2010-11-04 04:01:17 +0000
@@ -536,13 +536,14 @@
@action(_('Change'), name='change')
def change_action(self, action, data):
+ old_status = self.context.lifecycle_status
self.updateContextFromData(data)
# We need to ensure that resolution is recorded if the spec is now
# resolved.
- newstate = self.context.updateLifecycleStatus(self.user)
- if newstate is not None:
+ new_status = self.context.lifecycle_status
+ if new_status != old_status:
self.request.response.addNotification(
- 'blueprint is now considered "%s".' % newstate.title)
+ 'Blueprint is now considered "%s".' % new_status.title)
self.next_url = canonical_url(self.context)
@@ -735,7 +736,7 @@
class SpecificationSupersedingView(LaunchpadFormView):
schema = ISpecification
field_names = ['superseded_by']
- label = _('Mark specification superseded')
+ label = _('Mark blueprint superseded')
custom_widget('superseded_by', SupersededByWidget)
@property
@@ -762,7 +763,7 @@
description=_(
"The blueprint which supersedes this one. Note "
"that selecting a blueprint here and pressing "
- "Continue will change the specification status "
+ "Continue will change the blueprint status "
"to Superseded.")),
render_context=self.render_context)
@@ -784,7 +785,7 @@
newstate = self.context.updateLifecycleStatus(self.user)
if newstate is not None:
self.request.response.addNotification(
- 'Specification is now considered "%s".' % newstate.title)
+ 'Blueprint is now considered "%s".' % newstate.title)
self.next_url = canonical_url(self.context)
@property
=== modified file 'lib/lp/blueprints/browser/tests/test_specification.py'
--- lib/lp/blueprints/browser/tests/test_specification.py 2010-08-20 20:31:18 +0000
+++ lib/lp/blueprints/browser/tests/test_specification.py 2010-11-04 04:01:17 +0000
@@ -8,10 +8,13 @@
from lazr.restful.testing.webservice import FakeRequest
from zope.publisher.interfaces import NotFound
+from canonical.launchpad.webapp.interfaces import BrowserNotificationLevel
from canonical.launchpad.webapp.servers import StepsToGo
from canonical.testing.layers import DatabaseFunctionalLayer
from lp.blueprints.browser import specification
-from lp.testing import TestCaseWithFactory
+from lp.blueprints.enums import SpecificationImplementationStatus
+from lp.testing import login_person, TestCaseWithFactory
+from lp.testing.views import create_initialized_view
class LocalFakeRequest(FakeRequest):
@@ -108,6 +111,82 @@
self.specification.getBranchLink(branch), self.traverse(segments))
+class TestSpecificationEditStatusView(TestCaseWithFactory):
+ """Test the SpecificationEditStatusView."""
+
+ layer = DatabaseFunctionalLayer
+
+ def test_records_started(self):
+ spec = self.factory.makeSpecification(
+ implementation_status=SpecificationImplementationStatus.NOTSTARTED)
+ login_person(spec.owner)
+ form = {
+ 'field.implementation_status': 'STARTED',
+ 'field.actions.change': 'Change',
+ }
+ view = create_initialized_view(spec, name='+status', form=form)
+ self.assertEqual(
+ SpecificationImplementationStatus.STARTED, spec.implementation_status)
+ self.assertEqual(spec.owner, spec.starter)
+ [notification] = view.request.notifications
+ self.assertEqual(BrowserNotificationLevel.NOTICE, notification.level)
+ self.assertEqual(
+ 'Blueprint is now considered "Started".', notification.message)
+
+ def test_unchanged_lifecycle_has_no_notification(self):
+ spec = self.factory.makeSpecification(
+ implementation_status=SpecificationImplementationStatus.STARTED)
+ login_person(spec.owner)
+ form = {
+ 'field.implementation_status': 'SLOW',
+ 'field.actions.change': 'Change',
+ }
+ view = create_initialized_view(spec, name='+status', form=form)
+ self.assertEqual(
+ SpecificationImplementationStatus.SLOW, spec.implementation_status)
+ self.assertEqual(0, len(view.request.notifications))
+
+ def test_records_unstarting(self):
+ # If a spec was started, and is changed to not started, a notice is shown.
+ # Also the spec.starter is cleared out.
+ spec = self.factory.makeSpecification(
+ implementation_status=SpecificationImplementationStatus.STARTED)
+ login_person(spec.owner)
+ form = {
+ 'field.implementation_status': 'NOTSTARTED',
+ 'field.actions.change': 'Change',
+ }
+ view = create_initialized_view(spec, name='+status', form=form)
+ self.assertEqual(
+ SpecificationImplementationStatus.NOTSTARTED,
+ spec.implementation_status)
+ self.assertIs(None, spec.starter)
+ [notification] = view.request.notifications
+ self.assertEqual(BrowserNotificationLevel.NOTICE, notification.level)
+ self.assertEqual(
+ 'Blueprint is now considered "Not started".', notification.message)
+
+ def test_records_completion(self):
+ # If a spec is marked as implemented the user is notifiec it is now
+ # complete.
+ spec = self.factory.makeSpecification(
+ implementation_status=SpecificationImplementationStatus.STARTED)
+ login_person(spec.owner)
+ form = {
+ 'field.implementation_status': 'IMPLEMENTED',
+ 'field.actions.change': 'Change',
+ }
+ view = create_initialized_view(spec, name='+status', form=form)
+ self.assertEqual(
+ SpecificationImplementationStatus.IMPLEMENTED,
+ spec.implementation_status)
+ self.assertEqual(spec.owner, spec.completer)
+ [notification] = view.request.notifications
+ self.assertEqual(BrowserNotificationLevel.NOTICE, notification.level)
+ self.assertEqual(
+ 'Blueprint is now considered "Complete".', notification.message)
+
+
class TestSecificationHelpers(unittest.TestCase):
"""Test specification helper functions."""
=== modified file 'lib/lp/blueprints/configure.zcml'
--- lib/lp/blueprints/configure.zcml 2010-08-25 04:12:59 +0000
+++ lib/lp/blueprints/configure.zcml 2010-11-04 04:01:17 +0000
@@ -22,258 +22,220 @@
name="blueprints" />
- <lp:help-folder
- folder="help" type="lp.blueprints.publisher.BlueprintsLayer" />
-
- <!-- Sprint -->
-
- <class
- class="lp.blueprints.model.sprint.Sprint">
- <allow
- interface="lp.blueprints.interfaces.sprint.ISprint"/>
- <require
- permission="launchpad.AnyPerson"
- set_schema="lp.blueprints.interfaces.sprint.ISprint"/>
- </class>
- <adapter
- provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
- for="lp.blueprints.interfaces.sprint.ISprint"
- factory="canonical.launchpad.webapp.breadcrumb.TitleBreadcrumb"
- permission="zope.Public"/>
- <adapter
- provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
- for="lp.blueprints.interfaces.sprint.ISprintSet"
- factory="lp.blueprints.browser.sprint.SprintSetBreadcrumb"
- permission="zope.Public"/>
- <adapter
- provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
- for="lp.blueprints.interfaces.specification.ISpecification"
- factory="canonical.launchpad.webapp.breadcrumb.TitleBreadcrumb"
- permission="zope.Public"/>
-
- <!-- This is a view used to export data needed by the sprint scheduler.
- As there are no API stability guarantees, the view name starts
- with "temp" to discourage people from relying on it. -->
-
-
- <!-- SprintSet -->
-
- <class
- class="lp.blueprints.model.sprint.SprintSet">
- <allow
- interface="lp.blueprints.interfaces.sprint.ISprintSet"/>
- </class>
- <securedutility
- class="lp.blueprints.model.sprint.SprintSet"
- provides="lp.blueprints.interfaces.sprint.ISprintSet">
- <allow
- interface="lp.blueprints.interfaces.sprint.ISprintSet"/>
- </securedutility>
-
- <!-- SprintSpecification -->
-
- <class
- class="lp.blueprints.model.sprintspecification.SprintSpecification">
- <allow
- interface="lp.blueprints.interfaces.sprintspecification.ISprintSpecification"/>
- <require
- permission="launchpad.Edit"
- set_attributes="whiteboard"/>
- </class>
-
- <!-- SpecificationDependency -->
-
- <class
- class="lp.blueprints.model.specificationdependency.SpecificationDependency">
- <allow
- interface="lp.blueprints.interfaces.specificationdependency.ISpecificationDependency"/>
- <require
- permission="zope.Public"
- set_schema="lp.blueprints.interfaces.specificationdependency.ISpecificationDependency"/>
- </class>
- <adapter
- for="lp.blueprints.interfaces.specificationdependency.ISpecificationDependency"
- factory="lp.blueprints.interfaces.specificationdependency.SpecDependencyIsAlsoRemoval"
- provides="lp.blueprints.interfaces.specificationdependency.ISpecificationDependencyRemoval"/>
-
- <!-- SpecificationSubscription -->
-
- <class
- class="lp.blueprints.model.specificationsubscription.SpecificationSubscription">
- <allow
- interface="lp.blueprints.interfaces.specificationsubscription.ISpecificationSubscription"/>
- <require
- permission="launchpad.Edit"
- set_attributes="essential"/>
- </class>
- <subscriber
- for="lp.blueprints.interfaces.specificationsubscription.ISpecificationSubscription lazr.lifecycle.interfaces.IObjectCreatedEvent"
- handler="canonical.launchpad.mailnotification.notify_specification_subscription_created"/>
- <subscriber
- for="lp.blueprints.interfaces.specificationsubscription.ISpecificationSubscription lazr.lifecycle.interfaces.IObjectModifiedEvent"
- handler="canonical.launchpad.mailnotification.notify_specification_subscription_modified"/>
-
- <!-- SpecificationFeedback -->
-
- <class
- class="lp.blueprints.model.specificationfeedback.SpecificationFeedback">
- <allow
- interface="lp.blueprints.interfaces.specificationfeedback.ISpecificationFeedback"/>
- <require
- permission="zope.Public"
- set_schema="lp.blueprints.interfaces.specificationfeedback.ISpecificationFeedback"/>
- </class>
-
- <!-- SprintAttendance -->
-
- <class
- class="lp.blueprints.model.sprintattendance.SprintAttendance">
- <allow
- interface="lp.blueprints.interfaces.sprintattendance.ISprintAttendance"/>
- <require
- permission="launchpad.Edit"
- set_attributes="time_starts time_ends"/>
- </class>
-
- <!-- ISpecificationBranch -->
-
- <class
- class="lp.blueprints.model.specificationbranch.SpecificationBranch">
- <allow
- interface="lp.blueprints.interfaces.specificationbranch.ISpecificationBranch"/>
- <require
- permission="launchpad.AnyPerson"
- set_attributes="summary"/>
- </class>
- <subscriber
- for="lp.blueprints.interfaces.specificationbranch.ISpecificationBranch lazr.lifecycle.interfaces.IObjectCreatedEvent"
- handler="canonical.launchpad.subscribers.karma.spec_branch_created"/>
- <facet
- facet="specifications"/>
-
- <!-- SpecificationBranchSet -->
-
- <securedutility
- class="lp.blueprints.model.specificationbranch.SpecificationBranchSet"
- provides="lp.blueprints.interfaces.specificationbranch.ISpecificationBranchSet">
- <allow
- interface="lp.blueprints.interfaces.specificationbranch.ISpecificationBranchSet"/>
- </securedutility>
- <facet
- facet="specifications">
-
- <!-- Specification -->
-
- <class
- class="lp.blueprints.model.specification.Specification">
- <allow
- interface="lp.blueprints.interfaces.specification.ISpecification"/>
-
- <!-- We allow any authenticated person to update the whiteboard -->
-
- <require
- permission="launchpad.AnyPerson"
- set_attributes="whiteboard"/>
-
- <!-- NB: goals and goalstatus are not to be set directly, it should
- only be set through the proposeGoal / acceptBy / declineBy
- methods
- -->
-
- <require
- permission="launchpad.Edit"
- set_attributes="name title summary definition_status specurl superseded_by milestone product distribution approver assignee drafter man_days implementation_status"/>
- <require
- permission="launchpad.Admin"
- set_attributes="priority direction_approved"/>
- <allow
- attributes="
- bugs
+ <lp:help-folder
+ folder="help" type="lp.blueprints.publisher.BlueprintsLayer" />
+
+ <!-- Sprint -->
+
+ <class
+ class="lp.blueprints.model.sprint.Sprint">
+ <allow
+ interface="lp.blueprints.interfaces.sprint.ISprint"/>
+ <require
+ permission="launchpad.AnyPerson"
+ set_schema="lp.blueprints.interfaces.sprint.ISprint"/>
+ </class>
+ <adapter
+ provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
+ for="lp.blueprints.interfaces.sprint.ISprint"
+ factory="canonical.launchpad.webapp.breadcrumb.TitleBreadcrumb"
+ permission="zope.Public"/>
+ <adapter
+ provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
+ for="lp.blueprints.interfaces.sprint.ISprintSet"
+ factory="lp.blueprints.browser.sprint.SprintSetBreadcrumb"
+ permission="zope.Public"/>
+ <adapter
+ provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
+ for="lp.blueprints.interfaces.specification.ISpecification"
+ factory="canonical.launchpad.webapp.breadcrumb.TitleBreadcrumb"
+ permission="zope.Public"/>
+
+ <!-- This is a view used to export data needed by the sprint scheduler.
+ As there are no API stability guarantees, the view name starts
+ with "temp" to discourage people from relying on it. -->
+
+ <!-- SprintSet -->
+
+ <class
+ class="lp.blueprints.model.sprint.SprintSet">
+ <allow interface="lp.blueprints.interfaces.sprint.ISprintSet"/>
+ </class>
+ <securedutility
+ class="lp.blueprints.model.sprint.SprintSet"
+ provides="lp.blueprints.interfaces.sprint.ISprintSet">
+ <allow interface="lp.blueprints.interfaces.sprint.ISprintSet"/>
+ </securedutility>
+
+ <!-- SprintSpecification -->
+
+ <class
+ class="lp.blueprints.model.sprintspecification.SprintSpecification">
+ <allow
+ interface="lp.blueprints.interfaces.sprintspecification.ISprintSpecification"/>
+ <require
+ permission="launchpad.Edit"
+ set_attributes="whiteboard"/>
+ </class>
+
+ <!-- SpecificationDependency -->
+
+ <class class="lp.blueprints.model.specificationdependency.SpecificationDependency">
+ <allow interface="lp.blueprints.interfaces.specificationdependency.ISpecificationDependency"/>
+ <require
+ permission="zope.Public"
+ set_schema="lp.blueprints.interfaces.specificationdependency.ISpecificationDependency"/>
+ </class>
+ <adapter
+ for="lp.blueprints.interfaces.specificationdependency.ISpecificationDependency"
+ factory="lp.blueprints.interfaces.specificationdependency.SpecDependencyIsAlsoRemoval"
+ provides="lp.blueprints.interfaces.specificationdependency.ISpecificationDependencyRemoval"/>
+
+ <!-- SpecificationSubscription -->
+
+ <class class="lp.blueprints.model.specificationsubscription.SpecificationSubscription">
+ <allow
+ interface="lp.blueprints.interfaces.specificationsubscription.ISpecificationSubscription"/>
+ <require
+ permission="launchpad.Edit"
+ set_attributes="essential"/>
+ </class>
+ <subscriber
+ for="lp.blueprints.interfaces.specificationsubscription.ISpecificationSubscription
+ lazr.lifecycle.interfaces.IObjectCreatedEvent"
+ handler="canonical.launchpad.mailnotification.notify_specification_subscription_created"/>
+ <subscriber
+ for="lp.blueprints.interfaces.specificationsubscription.ISpecificationSubscription
+ lazr.lifecycle.interfaces.IObjectModifiedEvent"
+ handler="canonical.launchpad.mailnotification.notify_specification_subscription_modified"/>
+
+ <!-- SpecificationFeedback -->
+
+ <class class="lp.blueprints.model.specificationfeedback.SpecificationFeedback">
+ <allow interface="lp.blueprints.interfaces.specificationfeedback.ISpecificationFeedback"/>
+ <require
+ permission="zope.Public"
+ set_schema="lp.blueprints.interfaces.specificationfeedback.ISpecificationFeedback"/>
+ </class>
+
+ <!-- SprintAttendance -->
+
+ <class class="lp.blueprints.model.sprintattendance.SprintAttendance">
+ <allow interface="lp.blueprints.interfaces.sprintattendance.ISprintAttendance"/>
+ <require
+ permission="launchpad.Edit"
+ set_attributes="time_starts time_ends"/>
+ </class>
+
+ <!-- ISpecificationBranch -->
+
+ <class class="lp.blueprints.model.specificationbranch.SpecificationBranch">
+ <allow interface="lp.blueprints.interfaces.specificationbranch.ISpecificationBranch"/>
+ <require
+ permission="launchpad.AnyPerson"
+ set_attributes="summary"/>
+ </class>
+ <subscriber
+ for="lp.blueprints.interfaces.specificationbranch.ISpecificationBranch
+ lazr.lifecycle.interfaces.IObjectCreatedEvent"
+ handler="canonical.launchpad.subscribers.karma.spec_branch_created"/>
+
+ <!-- SpecificationBranchSet -->
+
+ <securedutility
+ class="lp.blueprints.model.specificationbranch.SpecificationBranchSet"
+ provides="lp.blueprints.interfaces.specificationbranch.ISpecificationBranchSet">
+ <allow interface="lp.blueprints.interfaces.specificationbranch.ISpecificationBranchSet"/>
+ </securedutility>
+
+ <!-- Specification -->
+
+ <class class="lp.blueprints.model.specification.Specification">
+ <allow interface="lp.blueprints.interfaces.specification.ISpecification"/>
+ <!-- We allow any authenticated person to update the whiteboard -->
+ <require
+ permission="launchpad.AnyPerson"
+ set_attributes="whiteboard"/>
+ <!-- NB: goals and goalstatus are not to be set directly, it should
+ only be set through the proposeGoal / acceptBy / declineBy
+ methods -->
+ <require
+ permission="launchpad.Edit"
+ set_attributes="name title summary definition_status specurl
+ superseded_by milestone product distribution
+ approver assignee drafter man_days
+ implementation_status"/>
+ <require
+ permission="launchpad.Admin"
+ set_attributes="priority direction_approved"/>
+ <allow
+ attributes="bugs
bug_links"/>
- <require
- permission="launchpad.AnyPerson"
- attributes="
- linkBug
+ <require
+ permission="launchpad.AnyPerson"
+ attributes="linkBug
unlinkBug"/>
- </class>
- <class
- class="lp.blueprints.model.specificationbug.SpecificationBug">
- <allow
- interface="lp.blueprints.interfaces.specificationbug.ISpecificationBug"/>
- </class>
- <subscriber
- for="lp.blueprints.interfaces.specification.ISpecification lazr.lifecycle.interfaces.IObjectCreatedEvent"
- handler="canonical.launchpad.subscribers.karma.spec_created"/>
- <subscriber
- for="lp.blueprints.interfaces.specification.ISpecification lazr.lifecycle.interfaces.IObjectModifiedEvent"
- handler="canonical.launchpad.subscribers.karma.spec_modified"/>
-
- <!-- these pages are simple enough they don't need browser code -->
-
-
- <!-- these pages require special browser code -->
-
-
- <!-- SpecificationSet -->
-
- <class
- class="lp.blueprints.model.specification.SpecificationSet">
- <allow
- interface="lp.blueprints.interfaces.specification.ISpecificationSet"/>
- </class>
- <securedutility
- class="lp.blueprints.model.specification.SpecificationSet"
- provides="lp.blueprints.interfaces.specification.ISpecificationSet">
- <allow
- interface="lp.blueprints.interfaces.specification.ISpecificationSet"/>
- </securedutility>
-
- <!-- SpecificationDelta -->
-
- <class
- class="lp.blueprints.adapters.SpecificationDelta">
- <allow
- interface="lp.blueprints.interfaces.specification.ISpecificationDelta"/>
- </class>
-
- <!-- SpecificationMessage -->
-
- <class
- class="lp.blueprints.model.specificationmessage.SpecificationMessage">
- <allow
- interface="lp.blueprints.interfaces.specificationmessage.ISpecificationMessage"/>
- </class>
-
- <!-- SpecificationMessageSet -->
-
- <class
- class="lp.blueprints.model.specificationmessage.SpecificationMessageSet">
- <allow
- interface="lp.blueprints.interfaces.specificationmessage.ISpecificationMessageSet"/>
- </class>
- <securedutility
- class="lp.blueprints.model.specificationmessage.SpecificationMessageSet"
- provides="lp.blueprints.interfaces.specificationmessage.ISpecificationMessageSet">
- <allow
- interface="lp.blueprints.interfaces.specificationmessage.ISpecificationMessageSet"/>
- </securedutility>
- </facet>
- <subscriber
- for="lp.blueprints.interfaces.specification.ISpecification lazr.lifecycle.interfaces.IObjectModifiedEvent"
- handler="lp.blueprints.subscribers.specification_goalstatus"/>
- <subscriber
- for="lp.blueprints.interfaces.specification.ISpecification lazr.lifecycle.interfaces.IObjectModifiedEvent"
- handler="canonical.launchpad.mailnotification.notify_specification_modified"/>
- <adapter
- name="blueprints"
- provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
- for="lp.blueprints.interfaces.specificationtarget.IHasSpecifications"
- factory="lp.blueprints.browser.specificationtarget.BlueprintsVHostBreadcrumb"
- permission="zope.Public"/>
- <adapter
- name="blueprints"
- provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
- for="lp.registry.interfaces.person.IPerson"
- factory="lp.blueprints.browser.specificationtarget.BlueprintsVHostBreadcrumb"
- permission="zope.Public"/>
+ </class>
+
+ <class class="lp.blueprints.model.specificationbug.SpecificationBug">
+ <allow interface="lp.blueprints.interfaces.specificationbug.ISpecificationBug"/>
+ </class>
+
+ <subscriber
+ for="lp.blueprints.interfaces.specification.ISpecification
+ lazr.lifecycle.interfaces.IObjectCreatedEvent"
+ handler="canonical.launchpad.subscribers.karma.spec_created"/>
+ <subscriber
+ for="lp.blueprints.interfaces.specification.ISpecification
+ lazr.lifecycle.interfaces.IObjectModifiedEvent"
+ handler="canonical.launchpad.subscribers.karma.spec_modified"/>
+ <subscriber
+ for="lp.blueprints.interfaces.specification.ISpecification
+ lazr.lifecycle.interfaces.IObjectModifiedEvent"
+ handler="lp.blueprints.subscribers.specification_update_lifecycle_status"/>
+ <subscriber
+ for="lp.blueprints.interfaces.specification.ISpecification
+ lazr.lifecycle.interfaces.IObjectModifiedEvent"
+ handler="lp.blueprints.subscribers.specification_goalstatus"/>
+ <subscriber
+ for="lp.blueprints.interfaces.specification.ISpecification
+ lazr.lifecycle.interfaces.IObjectModifiedEvent"
+ handler="canonical.launchpad.mailnotification.notify_specification_modified"/>
+
+ <!-- SpecificationSet -->
+
+ <class class="lp.blueprints.model.specification.SpecificationSet">
+ <allow interface="lp.blueprints.interfaces.specification.ISpecificationSet"/>
+ </class>
+
+ <securedutility
+ class="lp.blueprints.model.specification.SpecificationSet"
+ provides="lp.blueprints.interfaces.specification.ISpecificationSet">
+ <allow interface="lp.blueprints.interfaces.specification.ISpecificationSet"/>
+ </securedutility>
+
+ <!-- SpecificationDelta -->
+
+ <class class="lp.blueprints.adapters.SpecificationDelta">
+ <allow interface="lp.blueprints.interfaces.specification.ISpecificationDelta"/>
+ </class>
+
+ <!-- SpecificationMessage -->
+
+ <class class="lp.blueprints.model.specificationmessage.SpecificationMessage">
+ <allow interface="lp.blueprints.interfaces.specificationmessage.ISpecificationMessage"/>
+ </class>
+
+ <!-- SpecificationMessageSet -->
+
+ <class class="lp.blueprints.model.specificationmessage.SpecificationMessageSet">
+ <allow interface="lp.blueprints.interfaces.specificationmessage.ISpecificationMessageSet"/>
+ </class>
+
+ <securedutility
+ class="lp.blueprints.model.specificationmessage.SpecificationMessageSet"
+ provides="lp.blueprints.interfaces.specificationmessage.ISpecificationMessageSet">
+ <allow interface="lp.blueprints.interfaces.specificationmessage.ISpecificationMessageSet"/>
+ </securedutility>
+
</configure>
=== modified file 'lib/lp/blueprints/interfaces/specification.py'
--- lib/lp/blueprints/interfaces/specification.py 2010-11-02 20:10:56 +0000
+++ lib/lp/blueprints/interfaces/specification.py 2010-11-04 04:01:17 +0000
@@ -41,6 +41,7 @@
SpecificationDefinitionStatus,
SpecificationGoalStatus,
SpecificationImplementationStatus,
+ SpecificationLifecycleStatus,
SpecificationPriority,
)
from lp.blueprints.interfaces.specificationtarget import IHasSpecifications
@@ -337,6 +338,12 @@
'attribute, and also considers informational specs to be '
'started when they are approved.')
+ lifecycle_status = Choice(
+ title=_('Lifecycle Status'),
+ vocabulary=SpecificationLifecycleStatus,
+ default=SpecificationLifecycleStatus.NOTSTARTED,
+ readonly=True)
+
def retarget(product=None, distribution=None):
"""Retarget the spec to a new product or distribution. One of
product or distribution must be None (but not both).
=== modified file 'lib/lp/blueprints/model/specification.py'
--- lib/lp/blueprints/model/specification.py 2010-11-02 20:10:56 +0000
+++ lib/lp/blueprints/model/specification.py 2010-11-04 04:01:17 +0000
@@ -12,7 +12,6 @@
from lazr.lifecycle.event import (
ObjectCreatedEvent,
- ObjectDeletedEvent,
ObjectModifiedEvent,
)
from lazr.lifecycle.objectdelta import ObjectDelta
@@ -24,11 +23,7 @@
SQLRelatedJoin,
StringCol,
)
-from storm.expr import (
- LeftJoin,
- )
from storm.locals import (
- ClassAlias,
Desc,
SQL,
)
@@ -386,6 +381,16 @@
(self.definition_status ==
SpecificationDefinitionStatus.APPROVED)))
+ @property
+ def lifecycle_status(self):
+ """Combine the is_complete and is_started emergent properties."""
+ if self.is_complete:
+ return SpecificationLifecycleStatus.COMPLETE
+ elif self.is_started:
+ return SpecificationLifecycleStatus.STARTED
+ else:
+ return SpecificationLifecycleStatus.NOTSTARTED
+
def updateLifecycleStatus(self, user):
"""See ISpecification."""
newstatus = None
=== modified file 'lib/lp/blueprints/subscribers.py'
--- lib/lp/blueprints/subscribers.py 2010-11-01 03:43:58 +0000
+++ lib/lp/blueprints/subscribers.py 2010-11-04 04:01:17 +0000
@@ -18,3 +18,13 @@
return
if delta.productseries is not None or delta.distroseries is not None:
spec.goalstatus = SpecificationGoalStatus.PROPOSED
+
+
+def specification_update_lifecycle_status(spec, event):
+ """Mark the specification as started and/or complete if appropriate.
+
+ Does nothing if there is no user associated with the event.
+ """
+ if event.user is None:
+ return
+ spec.updateLifecycleStatus(IPerson(event.user))
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2010-11-03 23:44:56 +0000
+++ lib/lp/testing/factory.py 2010-11-04 04:01:17 +0000
@@ -1623,7 +1623,8 @@
def makeSpecification(self, product=None, title=None, distribution=None,
name=None, summary=None, owner=None,
- status=SpecificationDefinitionStatus.NEW):
+ status=SpecificationDefinitionStatus.NEW,
+ implementation_status=None):
"""Create and return a new, arbitrary Blueprint.
:param product: The product to make the blueprint on. If one is
@@ -1639,7 +1640,7 @@
title = self.getUniqueString('title')
if owner is None:
owner = self.makePerson()
- return getUtility(ISpecificationSet).new(
+ spec = getUtility(ISpecificationSet).new(
name=name,
title=title,
specurl=None,
@@ -1648,6 +1649,11 @@
owner=owner,
product=product,
distribution=distribution)
+ if implementation_status is not None:
+ naked_spec = removeSecurityProxy(spec)
+ naked_spec.implementation_status = implementation_status
+ naked_spec.updateLifecycleStatus(owner)
+ return spec
def makeQuestion(self, target=None, title=None):
"""Create and return a new, arbitrary Question.
Follow ups