Merge lp:~andrea.corbellini/launchpad/blueprint-comments-part3 into lp:launchpad

Proposed by Andrea Corbellini
Status: Work in progress
Proposed branch: lp:~andrea.corbellini/launchpad/blueprint-comments-part3
Merge into: lp:launchpad
Diff against target: 351 lines (+237/-8)
5 files modified
lib/lp/blueprints/configure.zcml (+88/-1)
lib/lp/blueprints/doc/specification.txt (+49/-0)
lib/lp/blueprints/interfaces/specification.py (+17/-5)
lib/lp/blueprints/interfaces/specificationmessage.py (+27/-2)
lib/lp/blueprints/model/specification.py (+56/-0)
To merge this branch: bzr merge lp:~andrea.corbellini/launchpad/blueprint-comments-part3
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Needs Resubmitting
Eleanor Berger (community) Approve
Review via email: mp+16291@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Andrea Corbellini (andrea.corbellini) wrote :

This branch contains the model work needed for blueprint comments.

* I created two new interfaces: ISpecificationComment and ISpecificationMessageAddForm. Currently they are not used but will be needed for displaying in the web UI.
* I also added some new attributes to ISpecification: messages, newMessage, linkMessage, getMessageChunks.
* Finally I added tests at the end of lib/lp/blueprints/doc/specification.txt.

To test the changes: bin/test -t specification.txt

Revision history for this message
Eleanor Berger (intellectronica) wrote :

Andrea, thanks again for taking on this work. And well done, the branch looks in great shape. We can't land it just yet, since the tree is closed, but I'm marking it approved and you can continue with the UI branch building on top of this one.

review: Approve
10065. By Andrea Corbellini

Merge with devel.

10066. By Andrea Corbellini

Allow missing attributes.

10067. By Andrea Corbellini

Merge with devel.

Revision history for this message
Martin Pool (mbp) wrote :

Can we land this now?

Revision history for this message
Andrea Corbellini (andrea.corbellini) wrote :

Unfortunately no, sorry. The PQM said some tests failed. I'm almost sure the problem is caused by the fact that some attributes are missing from the <allow> directives in the configure.zcml file.

I'd fix the errors, but unfortunately in this period I don't have the time to run the full test suite.

Revision history for this message
Brad Crittenden (bac) wrote :

I am changing the status of this branch to Work in progress as it will now need significant work before landing and will need a re-review. Andrea I hope you can pick this work up again when you have time.

review: Needs Resubmitting (code)
Revision history for this message
Robert Collins (lifeless) wrote :

It may not need significant work.

Running it through ec2 once will return a subunit file listing all of
the failing tests, allowing it to be worked on much more simply than
by running the full test suite.

Unmerged revisions

10067. By Andrea Corbellini

Merge with devel.

10066. By Andrea Corbellini

Allow missing attributes.

10065. By Andrea Corbellini

Merge with devel.

10064. By Andrea Corbellini

Lint code.

10063. By Andrea Corbellini

Adjust permissions.

10062. By Andrea Corbellini

Add tests.

10061. By Andrea Corbellini

Add the missing imports.

10060. By Andrea Corbellini

Add the code for the new attributes and methods.

10059. By Andrea Corbellini

Set permissions for the new methods.

10058. By Andrea Corbellini

Add `linkMessage()` to ISpecification.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/blueprints/configure.zcml'
2--- lib/lp/blueprints/configure.zcml 2009-09-23 03:36:55 +0000
3+++ lib/lp/blueprints/configure.zcml 2010-02-16 12:43:20 +0000
4@@ -157,7 +157,84 @@
5 <class
6 class="lp.blueprints.model.specification.Specification">
7 <allow
8- interface="lp.blueprints.interfaces.specification.ISpecification"/>
9+ attributes="
10+ linkBranch
11+ unlinkBranch
12+ unqueue
13+ getBranchLink
14+ messages
15+ all_blocked
16+ goal_proposer
17+ getSubscriptionByName
18+ owner
19+ linked_branches
20+ productseries
21+ getFeedbackRequests
22+ id
23+ createDependency
24+ sprints
25+ queue
26+ man_days
27+ removeDependency
28+ goal
29+ linkMessage
30+ date_goal_proposed
31+ priority
32+ date_goal_decided
33+ declineBy
34+ date_started
35+ has_accepted_goal
36+ goalstatus
37+ is_started
38+ getSprintSpecification
39+ product
40+ getDelta
41+ subscriptions
42+ sprint_links
43+ feedbackrequests
44+ retarget
45+ unlinkSprint
46+ dependencies
47+ unsubscribe
48+ subscribers
49+ milestone
50+ linkSprint
51+ updateLifecycleStatus
52+ is_blocked
53+ completer
54+ is_incomplete
55+ starter
56+ subscription
57+ notificationRecipientAddresses
58+ is_complete
59+ date_completed
60+ direction_approved
61+ subscribe
62+ isSubscribed
63+ superseded_by
64+ distroseries
65+ whiteboard
66+ proposeGoal
67+ datecreated
68+ blocked_specs
69+ all_deps
70+ distribution
71+ getMessageChunks
72+ informational
73+ goal_decider
74+ implementation_status
75+ acceptBy
76+ name
77+ title
78+ approver
79+ definition_status
80+ summary
81+ assignee
82+ specurl
83+ drafter
84+ target
85+ specification_branch
86+ mentoring_offers"/>
87
88 <!-- We allow any authenticated person to update the whiteboard -->
89
90@@ -185,6 +262,14 @@
91 attributes="
92 linkBug
93 unlinkBug"/>
94+ <require
95+ permission="launchpad.Edit"
96+ attributes="
97+ newMessage"/>
98+ <require
99+ permission="launchpad.Admin"
100+ attributes="
101+ setCommentVisibility"/>
102 </class>
103 <class
104 class="lp.blueprints.model.specificationbug.SpecificationBug">
105@@ -232,6 +317,8 @@
106 class="lp.blueprints.model.specificationmessage.SpecificationMessage">
107 <allow
108 interface="lp.blueprints.interfaces.specificationmessage.ISpecificationMessage"/>
109+ <require permission="launchpad.Admin"
110+ set_attributes="visible"/>
111 </class>
112
113 <!-- SpecificationMessageSet -->
114
115=== modified file 'lib/lp/blueprints/doc/specification.txt'
116--- lib/lp/blueprints/doc/specification.txt 2009-08-13 19:03:36 +0000
117+++ lib/lp/blueprints/doc/specification.txt 2010-02-16 12:43:20 +0000
118@@ -531,3 +531,52 @@
119 False
120 >>> canvas.date_completed is not None
121 False
122+
123+
124+== Specification comments ==
125+
126+A comment can be added to a specification using the `newMessage()` method:
127+
128+ >>> spec = specset.new(
129+ ... 'spec-for-comments', 'Spec for comments',
130+ ... 'http://www.example.com/SpecForComments',
131+ ... 'Specification used to test comments',
132+ ... SpecificationDefinitionStatus.APPROVED, mark,
133+ ... distribution=ubuntu)
134+ >>> message = spec.newMessage(mark, 'Subject', 'First comment.')
135+
136+All messages are stored in `messages`.
137+
138+ >>> message in spec.messages
139+ True
140+
141+The message chunks associated to a blueprint can be obtained with the method
142+`getMessageChunks()`.
143+
144+ >>> chunks = spec.getMessageChunks()
145+ >>> len(chunks)
146+ 1
147+ >>> chunk = chunks[0]
148+ >>> chunk.message.subject
149+ u'Subject'
150+ >>> chunk.content
151+ u'First comment.'
152+
153+All comments are visible by default, but they can be hidden by an
154+administrator using `setCommentVisibility()`.
155+
156+ >>> from zope.component import getUtility
157+ >>> from lp.blueprints.interfaces.specificationmessage import (
158+ ... ISpecificationMessageSet)
159+ >>> message_set = getUtility(ISpecificationMessageSet)
160+ >>> comment = message_set.getBySpecificationAndMessage(
161+ ... spec, spec.messages[0])
162+
163+ >>> comment.visible
164+ True
165+ >>> spec.setCommentVisibility(mark, 0, False)
166+ >>> comment.visible
167+ False
168+
169+ >>> check_permission('launchpad.Admin', spec.setCommentVisibility)
170+ True
171
172=== modified file 'lib/lp/blueprints/interfaces/specification.py'
173--- lib/lp/blueprints/interfaces/specification.py 2009-07-17 00:26:05 +0000
174+++ lib/lp/blueprints/interfaces/specification.py 2010-02-16 12:43:20 +0000
175@@ -26,10 +26,7 @@
176 ]
177
178
179-from lazr.restful.declarations import (
180- REQUEST_USER, call_with, export_as_webservice_entry,
181- export_write_operation, operation_parameters, operation_returns_entry)
182-from lazr.restful.fields import Reference
183+from lazr.restful.declarations import export_as_webservice_entry
184 from zope.interface import Interface, Attribute
185 from zope.component import getUtility
186
187@@ -40,7 +37,6 @@
188 ContentNameField, PublicPersonChoice, Summary, Title)
189 from canonical.launchpad.validators import LaunchpadValidationError
190 from lp.registry.interfaces.role import IHasOwner
191-from lp.code.interfaces.branch import IBranch
192 from lp.code.interfaces.branchlink import IHasLinkedBranches
193 from lp.registry.interfaces.mentoringoffer import ICanBeMentored
194 from canonical.launchpad.interfaces.validation import valid_webref
195@@ -879,6 +875,22 @@
196 def getBranchLink(branch):
197 """Return the SpecificationBranch link for the branch, or None."""
198
199+ # comments
200+ messages = Attribute("The specification messages related to this object.")
201+
202+ def newMessage(owner, subject, content):
203+ """Create a new message and link it to this specification."""
204+
205+ def linkMessage(message, user):
206+ """Link a message to this specification."""
207+
208+ def getMessageChunks():
209+ """Return MessageChunks linked to comments made on this specification
210+ """
211+
212+ def setCommentVisibility(user, comment_number, visible):
213+ """Set the `visible` attribute on a specification comment."""
214+
215
216 # Interfaces for containers
217 class ISpecificationSet(IHasSpecifications):
218
219=== modified file 'lib/lp/blueprints/interfaces/specificationmessage.py'
220--- lib/lp/blueprints/interfaces/specificationmessage.py 2009-08-10 12:34:20 +0000
221+++ lib/lp/blueprints/interfaces/specificationmessage.py 2010-02-16 12:43:20 +0000
222@@ -12,8 +12,9 @@
223 ]
224
225 from lazr.restful.fields import Reference
226-from zope.interface import Interface
227-from zope.schema import Bool, Int
228+from zope.interface import Attribute, Interface
229+from zope.schema import Bool, Int, Text
230+from canonical.launchpad.fields import Title
231
232 from lp.blueprints.interfaces.specification import ISpecification
233 from canonical.launchpad.interfaces.message import IMessage
234@@ -54,3 +55,27 @@
235
236 Return None if no such ISpecificationMesssage exists.
237 """
238+
239+
240+class ISpecificationComment(IMessage):
241+ """A specification comment for displaying in the web UI."""
242+
243+ specification = Attribute(u"The specification the comments belongs to.")
244+ show_for_admin = Bool(
245+ title=u"A hidden comment still displayed for admins.",
246+ readonly=True)
247+ index = Int(title=u"The comment number.", required=True, readonly=True)
248+ was_truncated = Bool(
249+ title=u"Whether the displayed text was truncated for display.",
250+ readonly=True)
251+ text_for_display = Text(
252+ title=u"The comment text to be displayed in the UI.", readonly=True)
253+ display_title = Attribute(u"Whether or not to show the title.")
254+
255+
256+class ISpecificationMessageAddForm(Interface):
257+ """Schema used to build the add form for specification comment.
258+ """
259+
260+ subject = Title(title=u"Subject", required=True)
261+ comment = Text(title=u"Comment", required=False)
262
263=== modified file 'lib/lp/blueprints/model/specification.py'
264--- lib/lp/blueprints/model/specification.py 2009-09-21 14:56:07 +0000
265+++ lib/lp/blueprints/model/specification.py 2010-02-16 12:43:20 +0000
266@@ -10,8 +10,11 @@
267 'SpecificationSet',
268 ]
269
270+from email.Utils import make_msgid
271+
272 from storm.store import Store
273
274+from zope.component import getUtility
275 from zope.interface import implements
276 from zope.event import notify
277
278@@ -33,6 +36,7 @@
279 from canonical.database.constants import DEFAULT, UTC_NOW
280 from canonical.database.datetimecol import UtcDateTimeCol
281 from canonical.database.enumcol import EnumCol
282+from canonical.launchpad.database.message import Message, MessageChunk
283
284 from canonical.launchpad.helpers import (
285 get_contact_email_addresses, shortlist)
286@@ -55,6 +59,10 @@
287 SprintSpecification)
288 from lp.blueprints.model.sprint import Sprint
289
290+from lp.blueprints.model.specificationmessage import SpecificationMessage
291+from lp.blueprints.interfaces.specificationmessage import (
292+ ISpecificationMessageSet)
293+
294 from lazr.lifecycle.objectdelta import ObjectDelta
295 from lp.blueprints.adapters import SpecificationDelta
296
297@@ -652,6 +660,54 @@
298 spec_branch = self.getBranchLink(branch)
299 spec_branch.destroySelf()
300
301+ # comments
302+ messages = SQLRelatedJoin(
303+ 'Message', joinColumn='specification', otherColumn='message',
304+ intermediateTable='SpecificationMessage', prejoins=['owner'],
305+ orderBy=['datecreated', 'id'])
306+
307+ def newMessage(self, owner, subject=None, content=None):
308+ """See `ISpecification`."""
309+ if subject is None:
310+ subject = self.followup_subject()
311+ msg = Message(
312+ owner=owner, subject=subject,
313+ rfc822msgid=make_msgid('blueprints'))
314+ MessageChunk(message=msg, content=content, sequence=1)
315+ specmsg = self.linkMessage(msg)
316+ if not specmsg:
317+ return
318+ notify(ObjectCreatedEvent(specmsg, user=owner))
319+ return specmsg.message
320+
321+ def linkMessage(self, message, user=None):
322+ """See `ISpecification`."""
323+ if message not in self.messages:
324+ if user is None:
325+ user = message.owner
326+ return SpecificationMessage(specification=self, message=message)
327+
328+ def getMessageChunks(self):
329+ """See `ISpecification`."""
330+ query = """
331+ SpecificationMessage.specification = %s AND
332+ Message.id = MessageChunk.message AND
333+ SpecificationMessage.message = Message.id
334+ """ % sqlvalues(self)
335+ chunks = MessageChunk.select(
336+ query, clauseTables=['SpecificationMessage', 'Message'],
337+ prejoinClauseTables=['Message'],
338+ orderBy=['Message.datecreated', 'Message.id',
339+ 'MessageChunk.sequence'])
340+ return list(chunks)
341+
342+ def setCommentVisibility(self, user, comment_number, visible):
343+ """See `ISpecification`."""
344+ message_set = getUtility(ISpecificationMessageSet)
345+ message = message_set.getBySpecificationAndMessage(
346+ self, self.messages[comment_number])
347+ message.visible = visible
348+
349
350 class HasSpecificationsMixin:
351 """A mixin class that implements many of the common shortcut properties