[Chandler-dev] Edit/update support in the domain model

Grant Baillie grant at osafoundation.org
Wed Dec 20 09:17:36 PST 2006


Howdy, chandler-dev@:

So, I'm getting ready to commit the first round of changes for edit/ 
update in the next day or two. This should cover the domain model  
changes for supporting "lastModified", and I’ve hooked up enough of  
the UI (e.g., by adding a field or two to the detail view) that you  
can tell edit state is being tracked.

Here's a high-level overview. It's perhaps a little long and heavy on  
using the code as illustration, but comments or suggestions are  
welcome. If you're a managerial type, there are some TODOs at the  
end, too.

1. In osaf.pim.items, there's now a Modification enumerated type that  
is used to say what the last change to a given ContentItem was:

class Modification(schema.Enumeration):
     """
     Enumeration of the types of modification that are part of
     the edit/update workflows (a.k.a. "Stamping Storyboards")
     """
     values = { "edited":100, "queued":200, "sent":300, "updated":400 }

2. The design requires some state to be associated with each item;  
for example, once an invitation has been sent, future sendings are  
actually called "updates". So, besides just storing the what the last  
modification, there's a set of flags that is used to maintain that  
history:

(added to ContentItem):

     lastModification = schema.One(
         Modification,
         doc="What the last modification was"
     )

     modifiedFlags = schema.Many(
         Modification,
         initialValue=set([]),
         description='Used to track the modification state of the item'
     )

3. I added the following method to ContentItem:

     def changeEditState(self, modType=Modification.edited, who=None,
                         when=None):

This does the job of updating the lastModified-related attributes,  
including modifiedFlags, as appropriate. (See #7 for how the UI ends  
up calling this).

4. There's a "byline" Calculated attribute on ContentItem; this is  
used to show a string like "Edited by me on 12/21/2006" in the detail  
view.

5. I added an "error" attribute (a schema.Text) that stores a string  
containing the last error associated with a given item. At the  
moment, it’s only used during mail delivery failures, but in theory  
it could be used for sharing problems, too.

6. I changed the mail delivery code to use ContentItem.changeEditState 
(); in particular, we set the edited state to queued when we first  
hand off your message to the twisted thread for delivery, and set the  
sent/updated/error attributes as appropriate when done.

7. Making sure the UI calls changeEditState() when the user makes  
changes was an interesting strategy. One option I tried, but seemed a  
little haphazard, was to find all the setattr() calls in the various  
AttributeEditor subclasses, and add calls to changeEditState(). I  
realized this would also lead to weirdness if, for example, you  
edited a recurring event, because there the proxy would delay the  
actual attribute change till the user had confirmed it, but the  
change to lastModified would have happened anyway.

So, for now I went via a different route, which was to retrofit the  
occurrence proxy to call changeEditState(), even for items that  
aren’t recurring events. There are probably a few cases where we are  
explicitly not using the proxy in the UI; it's on my list to seek out  
and find those, and deal with the case where we want to call  
changeEditState() but not have the recurring event dialog show up.

8. I reworked some of Bryan Stearns' code for the communication  
column icons a little. I left the code that figures out what icon to  
use in the osaf.views.main.summaryblocks, but I moved the calculation  
of the various bits that contribute to an item's "communication  
state" over to an Annotation in osaf.pim.mail:

class CommunicationStatus(schema.Annotation):
     schema.kindInfo(annotates=items.ContentItem)

     # These flag bits govern the sort position of each  
communications state:
     # UPDATE      =         1
     # IN          =        1
     # OUT         =       1
     # DRAFT       =      1
     # QUEUED      =     1
     # SENT        =    1
     # NEEDS_REPLY =   1
     # READ        =  1
     # ERROR       = 1
     UPDATE, IN, OUT, DRAFT, QUEUED, SENT, NEEDS_REPLY, READ, ERROR = (
         1<<n for n in xrange(9)
     )

     @staticmethod
     def getItemCommState(itemOrUUID, view=None):
         """Given an item or a UUID, determine its communications  
status"""
         ...

     attributes = ... # attributes that contribute toward  
Communication status

     status = schema.Calculated(
         schema.Integer,
         basedOn=tuple(t[0] for t in attributes),
         fget=lambda self: self.getItemCommState(self.itsItem),
         doc="A 'bitfield' of (UPDATE, IN, OUT, DRAFT, ...) |'ed  
together"

9. The index used by the communication column now uses the above  
CommunicationStatus.status attribute (and knows the dependency on  
CommunicationStatus.attributes). Howvever, as Bryan originally  
designed it, this could be done via a method index and the  
getItemCommState method.

10. In the detail view, I added fields to show the byline and error  
string (if any), and changed the "from" field to be a "send as" for  
outgoing messages. As I'm a domain-model troglodyte lacking in high- 
level UI sensibilities, these probably need some tweaking w.r.t  
positioning, fonts and all that fun stuff.

TODO:
=====

- Add support for lastModifiedBy. (Currently, this value is always  
None, which means it's treated as being by the user). Mainly, I/we  
need to decide on using Contact vs EmailAddress here.

- Review the various CommunicationStatus states, to make sure they  
match the design spec.

- Look into how all this works (or doesn't!) with recurring events.

- Currently, there's an SMTPDeliveryStatus class in the mail code  
that tracks DRAFT/QUEUED/SENT/ERROR state. Look into unifying this  
with the enumerated type above, or for moving the communication- 
related Modification values into the mail module somehow. If I knew  
how to do this cleanly, I probably would have done it already, so  
suggestions are welcome :).

- Add support for a 'created' Modification type. Currently, if you  
create a new item, it shows up as "Created by me ...". Then, as soon  
as you change something, it says "Edited by me ...". As a nice to  
have (IIRC), the design called for it remaining "Created" in this  
case, until you switch focus to a different item in the detail view.  
I had a first cut at doing this, but it turns out it depends on some  
changes I’m making in the recurrence branch.

- UI polish/review detail view changes with Reid/Bryan.



More information about the chandler-dev mailing list