Summary
In 0.6, recurrence became a reality in Chandler. Most of the recurrence functionality available in Apple's iCal is exposed in Chandler. OSAF developers have now had a chance to experience some of the challenges recurrence presents.
There's more to be implemented in the recurrence realm, and there's lots of room for improvement in Chandler's existing recurrence code.
Issues now
Wasteful and buggy to duplicate attribute values
Background
Currently, every occurrence and modification copies all attribute values from masters. This leads to potential consistency problems that have to be managed, and it's wasteful.
Path forward
Use the repository's inheritFrom mechanism to add only changed attributes to occurrences and modifications.
Unfortunately, inheritFrom isn’t really quite the right tool for the job. It has to be specified on a per-attribute basis, so the only
way it could be used to implement this would be to subclass
osaf.pim.Note, and then override almost all the attributes
(including ones inherited from
ContentItem,
Annotation and
Stamp attributes) to
inheritFrom
the event’s
occurrenceFor. This mechanism would also break if new
Stamp or
Annotation subclasses were
added to
Note.
Instead, it’s possible to subclass
osaf.pim.Note, and override attribute access in Python to fetch unset attributes from
the event’s master.
Pros:
- Fewer attributes are copied when creating occurrences; this should cut down on repository usage and indexing activities.
- It’s straightforward to tell exactly what attributes have been changed on a given modification. This could help us produce less verbose ICalendar output.
- A given Event object could be either an occurrence or a master; this makes things more straightforward for client code (e.g. the calendar or detail views).
Cons:
- Currently, you have to override
__getattribute__ in Python to be able to intercept and override the repository’s attribute descriptors. In principle, this could be avoided by adding some kind of hook to allow code to be called before any given item’s defaultValue is called, and/or before raising some kind of AttributeError
- Attributes that rely on
schema.observer methods, like displayDate, may function weirdly in this scheme. For example, Occurrences aren’t notified when their master’s triageStatusChanged changed, although this could change their displayDate.
Wasteful to commit transient occurrences
Background
Occurrences are completely calculated from recurrence rules and master events, it seems wasteful to persist and garbage collect them.
Path forward
There's been discussion within the apps group about possibly using transient Python objects masquerading as items to represent occurrences, but there isn't consensus that persisting occurrences is all that bad, and many feel that forcing UI code and the repository to interact with non-items would be unpleasant to awful.
Some of the crufty code relating to recurrence is in the getNextOccurrence, this could be cleaned up (probably with speed enhancements) by making occurrences into linked lists. Once that's done, and once
#DuplicatingDataOccurrences is fixed, the remaining major problem that's been named (so far) with occurrences-as-items will be that a fair amount of juggling takes place to avoid including generated occurrences in calculated collections. Perhaps that can't be helped.
Wasteful to generate all events in the past when only recent occurrences are needed
Background
Currently, API like
Calendar.recurringEventsInRange() and
EventStamp.getOccurrencesBetween() generate all
occurrences from the first to those in the requested range. So, if you have a weekly, recurring event whose master started in 2003, you
will create over 150 occurrences just to find the occurrence in the current week.
Path forward
Invert the dependency of
EventStamp._generateRule() and
EventStamp.getNextOccurrence(). Have
_generateRule
generate its events based on its
dateutil rruleset directly.
Sharing relies on all of a recurring event being in a collection
Background
Putting only masters in collections makes sharing work with recurrence easily.
Unfortunately,
#OccurrencesInCollections is an 0.7 requirement. Sharing will likely need some reworking.
Path forward
Redesign the way sharing interacts with recurrence. How?
[Morgen: This needs more thought, but it might be enough to have the ICalendarFormat reject any non-master events during export]
Having generated occurrences confuses share syncing
Background
In the world where shares are synchronized by using repository view merging, having persistent occurrences that are generated on the fly can confuse the issue. For example:
- You sync to a collection with a recurring event. At the end of the merge, you have an event (with an rruleset) whose occurrences contains just the event itself.
- In the interim, you view that calendar, so a bunch of occurrences get added to the list.
- On a subsequent sync, the rruleset changes (e.g. sharer changes one of the occurrences). We currently make a new rruleset, but the view merging code will replay the above changes to the event, so you end up with the occurrences from step 2 in the list, and all of those have the old rruleset.
Path forward
We are getting by with a hack in the view merging code. Another approach that might work is to make occurrences belong to the
RecurrenceRuleSet? object (so that there are two separate 'occurrences' attributes in step 3 above).
Summary view doesn't show the right items
Background
Currently, the summary view shows exactly the items that live in its collection, and currently modifications don't live in collections, see
#SharingCollections, so they don't show up in the summary view.
Path forward
Where is the summary view going in 0.7? Is it possible we'll want the summary view to do things like display the first five occurrences of a recurring rule, plus any modifications? Once we decide that, we'll need to figure out how to fix the summary view.
Master UUIDs change, creating problems in sharing (Fixed in 0.7α4)
6607 Master events shouldn't be occurrences
Background
Currently, when a master event is modified, a dance takes place behind the scenes, creating a new event to be the master for the event. The acrobatics allow the Detail View's selected item to remain the same.
Unfortunately, this strategy causes major problems for sharing. The sharing layer pays attention to one UUID for recurring events, the master's. When this UUID shifts (but the icalUID stays the same), sync machinery fails, ultimately deleting the event.
Path forward
Stop having master events act as instances. Whenever a recurrence rule is created, create a generated occurrence mirroring the master, switch the Detail View to display the new occurrence.
THISANDFUTURE changes create entirely new events
Background
Originally in 0.6, recurring events were allowed to have THIS or THISANDFUTURE modifications. Getting this to work smoothly with a late-in-the-release shift in Jeffrey's understanding of Mimi's goals was risky, so as a compromise, recurrence code fell back to iCal's method of working with THISANDFUTURE changes, create a separate event (with a different icalUID) when a THISANDFUTURE change is made.
This is a reasonable strategy for iCal, but Chandler's sharing machinery will behave in odd, undesirable ways in this paradigm. To wit, imagine that Shaniqua has a weekly Monday Ultimate Frisbee game in her Frisbee share. Alfred subscribes to this collection, then drags the Monday meeting to his Exercise collection. Finally, Shaniqua's Monday meeting moves to Tuesdays starting in February.
When Alfred syncs, his version of the Frisbee share will continue to have the old Monday game, ending in January, plus a new Tuesday game starting in February. Unfortunately, this Tuesday game won't appear in his Exercise collection, and Alfred will stop getting exercise and he'll die of a stroke.
Path forward
Re-implement THISANDFUTURE changes staying connected to their original master. The changes above for #DuplicatingDataOccurrences should make things easier.
Proxies are hard to work with
Background
When a user makes a change to a recurring item, different code paths should be followed depending on whether the change is to one event, future events, or all events. But it's unattractive to force all attribute change code in Chandler to know about recurrence. A dialog needs to be popped up prompting the user to explain what they meant, and their response needs to be processed.
Proxies help with this in a couple of ways. First, they prevent schema.observer methods from firing, which would cause a THIS change which may not be intended. Second, they expose the new value of whatever attribute is being changed to other UI elements using proxies, so the Detail View and Calendar View can accurately reflect the change even though the change hasn't been propagated. For example, if I move an event two hours later in the Calendar View, the change is reflected in the Detail View while the THIS/THISANDFUTURE dialog is popped up.
Unfortunately, the current proxy implementation doesn't work well with Python code like:
foo.random_attribute = proxy_of_bar #the proxy isn't actually an item, this fails at commit time
proxy_of_bar.random_method(foo) # the proxy code passes through getattr calls to the real item (bar),
# so random_method doesn't ever trigger attribute changes in the proxy
It also falls down in cases like:
MailStamp(proxy_of_bar).add() # This changes proxy_of_bar.stamp_types, and then does some internal
# book-keeping, so the proxy dialog can’t be run asynchronously,
# because cancelling would leave bar in an inconsistent state.
Because proxies don't currently work transparently, they can't just be created for all events or items. This, in turn, means that all code that might change events needs to know about and use proxies. This is bad. Parcel authors really shouldn't need to know about recurrence to work with items that happen to be stamped as events.
Path forward
Possibilities for improving or avoiding proxies:
- At the repository level, implement an API to request a real item if a non-item is received, allowing non-items to be used in places items are expected. This could make proxies easier to work with, but stamping and method calls would still be tricky
- Implement a beforeValueChange callback, allowing items to intercept changes before they happen. Andi has indicated willingness to implement such a hook, but it may slow down the application. If such a hook existed, proxies would no longer be needed except for the issue of UI consistency, perhaps other solutions could be found for this problem
Occurrence ↔ reminder interactions are iffy
Background
There are lots of hacks in place relating to reminders, pretending each event has one or zero reminders (the content model, and the iCalendar standard, allows many). Also, occurrences of recurring events with reminders have their reminders preset to expired if the occurrence is in the past.
Path forward
[Grant]
- Multiple reminders: In general, this might not be of the highest priority: For example, in two years of use, I had no idea iCal supported multiple reminders per event. However, now that I know, I realized that I have a case where it would be handy: For our weekly status meetings, everyone posts their status to a WIKI page before the meeting. I'd use one reminder to remember to write my status first thing in the morning, one to remind me of the meeting itself (and to read everyone else's status). Here are some steps:
- Update the detail view to display all reminders for an event. It's unclear to me how hard this would be, since it involves having a variable number of blocks in the detail view.
- Updating the ReminderDialog to handle multiple reminders per event shouldn't be too difficult. However, it should only happen after step 1, or else the user would be confronted by mysterious, invisible reminders.
- Allowing the user to add/remove reminders in the detail view would need some design help. FWIW, iCal does this by allowing you to click on the "alarm" text in the detail view to get a drop down with "Add Alarm" and "Remove Alarm". We could probably get away with a "+" or "-" buttons below the last reminder.
- Recurring reminders: I'd like to revisit this after [[#DuplicatingDataOccurrences], which will hopefully allow for simplifications.
Unstamping event-ness of a subset of a recurring event
Background
What does it mean to unstamp event-ness for one occurrence? Presumably, exclude that date from the rule, and unstamp the occurrence.
What does it mean to unstamp event for more than one occurrence of a recurring event (all, or all future)? It would seem that this would create an undefined number of identical non-events. Should unstamping event-ness always apply to just one occurrence?
Path forward
Get design answers.
icalUIDMap is potentially unreliable (Fixed in 0.7α3)
Background
When an event from an iCalendar file is processed via import or sharing, it needs to be quickly checked against existing items with matching icalUID. Currently, there is a mapping between icalUIDs and repository items which handles this, but it isn't clear whether this mechanism is reliable for recurring events.
Path forward
Use an indexed collection instead of icalUIDMap.
Exceptions on a per-attribute basis
Background
Currently, if any attribute is modified on a recurring event, all its attributes are considered detached. For instance, if title for event Foo is modified, then all events times are modified to be two hours earlier, Foo's time doesn't change, because it's detached. Ideally, we'd track the fact that title changed, not time, and the time change would propagate to Foo.
Path forward
Happily, solving this should be a simple matter of implementing
#DuplicatingDataOccurrences
Collection membership for individual occurrences
Background
The current recurring event collection logic only puts master events into collections, all subsequent occurrences are assumed to be part of the collection.
For 0.7, it should be possible to add an individual occurrence from a recurring series to a different collection.
Sharing and collection rendering logic for this change will likely be thorny.
[Grant]
For example, imagine we fix Chandler so that individual modifications can have different
appearsIn from their masters, and that
the domain model maintains consistency. Then, imagine we have a master event
master that occurs in collection
A, and a
modification
mod that only lives in collection
B. It’s hard to imagine what we should do if sharing both collections as ICalendar (e.g. via CalDAV): On the one hand, we’re supposed to identify events uniquely via UID, so we want their ICalendar (.ics) representation
to be the same. On the other, we want to be able to say in the case of
B: event
mod is the only occurrence that actually appears in this collection. The only way I can think of doing this is to include the collections in the ICalendar (i.e. as
X-COLLECTION-UID properties).
Path forward
Change sharing logic to handle recurrence differently (how? Not clear), change display logic to allow modifications to be in different collections from masters.
Should occurrences live in collections, if masters and modifications do?
Nice features to add
Additional recurrence frequency options
Background
There are a host of recurrence options in iCalendar. Most iCalendar compatible calendars don't expose everything. Some UI that might be nice to allow users to create within Chandler:
- Every nth frequency events (every 6 weeks, every 6 months, etc.)
- MWF events
- 1st and 12th of the month events
- Last Friday of the month events
- Custom recurrence dates: Jan 12, Feb 4, April 30, 2006
Path forward
Discuss, design, implement.
Modifications that override existing modifications
Background
Once
#PerAttributeExceptions is implemented, it might still be nice to allow users to specify that modifications should apply to ALL occurrences, including detached occurrences.
Path forward
Discuss, design, implement.
Rendering of recurrence (exceptions, for instance)
Background
Currently, when viewing a recurring event, there's no way to know what the first occurrence of the rule is, or whether this is an occurrence, the master, or a modification. It might be nice to give users feedback about this.
Path forward
Discuss, design, implement.
Confirmation when changing rule (from weekly to monthly, for instance)
Background
Currently, if a user changes rule frequency, no confirmation dialog is popped up, it just deletes all future events, and changes the frequency. This is probably not what we want, probably there should be confirmation if any modifications have been made.
Path forward
Discuss, design, implement.
Avoiding dialogs with keystrokes
Background
Modal dialogs are annoying. If you're a power user, it would be nice to be able to avoid them. One way to avoid them would be to implement keystrokes that modified how a drag modification was applied, for instance, shift + drag means change just this instance, ctrl + drag means change this and future. This may not be feasible in wx, and it doesn't help for changes in the Detail View, but if it's possible, it would be a nice feature to add.
Path forward
Discuss, design, implement.