Thanks again for working on this. Your branch looks very good and is almost ready to land. There are a few things I'd like you to fix, and I also have a couple of questions, so I'm marking this Needs Fixing for now, but it should be very easy to fix.
It's a bit odd to do this assignment at the end of a test (though I understand
why you did it). I think it's better to merge the code part of this section
with the next one.
> +
Please add an extra blank line between sections (so there are two blank lines).
Hi Andrea,
Thanks again for working on this. Your branch looks very good and is almost ready to land. There are a few things I'd like you to fix, and I also have a couple of questions, so I'm marking this Needs Fixing for now, but it should be very easy to fix.
> === modified file 'lib/lp/ blueprints/ configure. zcml' blueprints/ configure. zcml 2009-07-13 18:15:02 +0000 blueprints/ configure. zcml 2009-08-10 09:34:05 +0000 "lp.blueprints. interfaces. specification. ISpecificationD elta"/> ssage --> lp.blueprints. model.specifica tionmessage. SpecificationMe ssage"> "lp.blueprints. interfaces. specificationme ssage.ISpe ge"/> ssageSet --> lp.blueprints. model.specifica tionmessage. SpecificationMe ssageSet" > "lp.blueprints. interfaces. specificationme ssage.ISpe geSet"/ > lp.blueprints. model.specifica tionmessage. SpecificationMe ssageSet" "lp.blueprints. interfaces. specificationme ssage.ISpecific "lp.blueprints. interfaces. specificationme ssage.ISpe geSet"/ > blueprints. interfaces. specification. ISpecification interfaces. IObjectModified Event" blueprints/ doc/specificati onmessage. txt' blueprints/ doc/specificati onmessage. txt 1970-01-01 00:00:00 blueprints/ doc/specificati onmessage. txt 2009-08-10 09:34:05 essage interface.
> --- lib/lp/
> +++ lib/lp/
> @@ -219,6 +219,28 @@
> <allow
> interface=
> </class>
> +
> + <!-- SpecificationMe
> +
> + <class
> +
> class="
> + <allow
> + interface=
> cificationMessa
> + </class>
> +
> + <!-- SpecificationMe
> +
> + <class
> +
> class="
> + <allow
> + interface=
> cificationMessa
> + </class>
> + <securedutility
> +
> class="
> + provides=
> ationMessageSet">
> + <allow
> + interface=
> cificationMessa
> + </securedutility>
> </facet>
> <subscriber
> for="lp.
> lazr.lifecycle.
>
> === added file 'lib/lp/
> --- lib/lp/
> +0000
> +++ lib/lp/
> +0000
> @@ -0,0 +1,38 @@
> += Specification Messages =
> +
> +Specification messages are messages associated with blueprints. A specifiction
> +message is described by the ISpecificationM
Narrative in doctests should wrap around 72 characters.
> + Message is always associated with exactly one specification.
> +One IMessage can be associated with many ISpecification, but one
> +ISpecification
That's techincally true, but I don't think very relevant. You will never want
to do that, so I think this bit can be removed.
> + interfaces. specification import ISpecificationSet interfaces. specificationme ssage import ( essageSet) ISpecificationS et) ISpecificationM essageSet)
> + >>> from lp.blueprints.
> + >>> from lp.blueprints.
> + ... ISpecificationM
> + >>> specset = getUtility(
> + >>> specmessageset = getUtility(
It's a bit odd to do this assignment at the end of a test (though I understand
why you did it). I think it's better to merge the code part of this section
with the next one.
> +
Please add an extra blank line between sections (so there are two blank lines).
> +== Creating specification messages == essageSet. createMessage: launchpad. interfaces import IPersonSet IPersonSet) .get(12) developer. mozilla. org/en/ docs/SVG') createMessage( person, message. subject
> +
> +To create a specification message, use
> ISpecificationM
> +
> + >>> from canonical.
> + >>> sample_person = getUtility(
> + >>> spec = specset.getByURL('http://
> + >>> test_message = specmessageset.
> + ... subject="test message subject",
> + ... content="text message content",
> + ... owner=sample_
> + ... spec=spec)
> + >>> test_message.
> + u'test message subject'
> +
Extra blank line.
> +== Retrieving specification messages == MessageSet represents the set of all ISpecificationM essages in essage can be retrieved with MessageSet. get: get(1) one.message. subject blueprints/ interfaces/ specificationme ssage.py' blueprints/ interfaces/ specificationme ssage.py 1970-01-01 blueprints/ interfaces/ specificationme ssage.py 2009-08-10 msg=E0211, E0213
> +
> +ISpecification
> +the system. An individual ISpecificationM
> +ISpecification
> +
> + >>> specmessage_one = specmessageset.
> + >>> specmessage_
> + u'test message subject'
>
> === added file 'lib/lp/
> --- lib/lp/
> 00:00:00 +0000
> +++ lib/lp/
> 09:34:05 +0000
> @@ -0,0 +1,58 @@
> +# Copyright 2009 Canonical Ltd. This software is licensed under the
> +# GNU Affero General Public License version 3 (see the file LICENSE).
> +
> +# pylint: disable-
Why are you disabling these lint messages?
> + Message' , MessageSet' ,
> +"""Specification message interfaces."""
> +
> +__metaclass__ = type
> +__all__ = [
> + 'ISpecification
> + 'ISpecification
> + ]
> +
> +from zope.interface import Attribute, Interface
> +from zope.schema import Bool, Bytes, Int, Object, Text, TextLine
You are importing things you don't need. You should always run `make lint` to
get help finding things like this.
> + launchpad. fields import Title interfaces. specification import ISpecification launchpad. interfaces. message import IMessage launchpad. validators. attachment import ( size_constraint ) essage( Interface) : schema= ISpecification, title=u"The specification.")
> +from canonical.
> +from lp.blueprints.
> +from canonical.
> +from canonical.
> + attachment_
> +
> +
> +class ISpecificationM
> + """A link between a specification and a message."""
> +
> + specification = Object(
It would be better to use a Reference field (which you can, later on, expose
via the web service API.
> + messageID = Int(title=u"The message id.", readonly=True)
Why is this attribute needed? We only very rarely need to work with IDs. Normally
Storm should take care of references for us.
> + message = Object( schema= IMessage, title=u"The message.")
Likewise, a Reference field will be a better choice.
> + visible = Bool(title=u"This message is visible or not.", required=False,
> + default=True)
I think "Is this message visible?" would be a better title.
> + essageSet( Interface) : essages. """ subject, specification, owner, content=None): essage. essage. onmessageid) : essage by its ID.""" ionAndMessage( specification, message): esssage. esssage exists. blueprints/ model/specifica tionmessage. py' blueprints/ model/specifica tionmessage. py 1970-01-01 00:00:00 blueprints/ model/specifica tionmessage. py 2009-08-10 09:34:05 msg=E0611, W0212 essage' , essageSet' database. sqlbase import SQLBase, sqlvalues interfaces. specificationme ssage import ( essage, ISpecificationM essageSet) launchpad. database. message import Message, MessageChunk ssage(SQLBase) : ISpecificationM essage) essage' 'specification' , foreignKey= 'Specification' , notNull=True) dbName= 'message' , foreignKey= 'Message' , notNull=True) notNull= True, default=True) ssageSet: essageSet. """ ISpecificationM essageSet) essageSet. """ make_msgid( 'blueprint' ), subject=subject) message= msg, content=content, sequence=1) ssage(specifica tion=spec, message=msg) specmsg) .flush( )
> +
> +class ISpecificationM
> + """The set of all ISpecificationM
> +
> + def createMessage(
> + """Create an ISpecificationM
> +
> + title -- a string
> + specification -- an ISpecification
> + owner -- an IPerson
> + content -- a string
> +
> + The created message will have the specification's initial message as
> + its parent.
> +
> + Returns the created ISpecificationM
> + """
> +
> + def get(specificati
> + """Retrieve an ISpecificationM
> +
> + def getBySpecificat
> + """Return the corresponding ISpecificationM
> +
> + Return None if no such ISpecificationM
> + """
>
> === added file 'lib/lp/
> --- lib/lp/
> +0000
> +++ lib/lp/
> +0000
> @@ -0,0 +1,60 @@
> +# Copyright 2009 Canonical Ltd. This software is licensed under the
> +# GNU Affero General Public License version 3 (see the file LICENSE).
> +
> +# pylint: disable-
> +
> +__metaclass__ = type
> +__all__ = [
> + 'SpecificationM
> + 'SpecificationM
> + ]
> +
> +from email.Utils import make_msgid
> +
> +from zope.interface import implements
> +
> +from sqlobject import BoolCol, ForeignKey, StringCol
> +from storm.store import Store
> +
> +from canonical.
> +from lp.blueprints.
> + ISpecificationM
> +from canonical.
> +
> +
> +class SpecificationMe
> + """A table linking specifictions and messages."""
> +
> + implements(
> +
> + _table = 'SpecificationM
> +
> + specification = ForeignKey(
> + dbName=
> + message = ForeignKey(
> + visible = BoolCol(
> +
> +
> +class SpecificationMe
> + """See ISpecificationM
> +
> + implements(
> +
> + def createMessage(self, subject, spec, owner, content=None):
> + """See ISpecificationM
> + msg = Message(
> + owner=owner, rfc822msgid=
> + chunk = MessageChunk(
> + specmsg = SpecificationMe
> +
> + Store.of(
Why is it necessary to flush here?
> + return specmsg essageSet. """ ssage.get( specmessageid) ionAndMessage( self, spec, message): essageSet. """ ssage.selectOne By(
> +
> + def get(self, specmessageid):
> + """See ISpecificationM
> + return SpecificationMe
> +
> + def getBySpecificat
> + """See ISpecificationM
> + return SpecificationMe
> + specification=spec, message=message)