The Chandler Dashboard

This document describes the classes that support the Chandler Dashboard, the “list view” of a user’s items that supports triage, sorting, and has various custom columns.

AppDashboardEntry

Chandler’s Application code expands on basic dashboard entries by defining a Chandler Application Dashboard Entry Add-on, AppDashboardEntry.

>>> from chandler.dashboard import *
>>> import chandler.core as core
>>> from chandler.event import Event
>>> from chandler.reminder import ReminderList
>>> from chandler.time_services import TimeZone, getNow, setNow, nowTimestamp
>>> from datetime import datetime, timedelta
>>> november_first = datetime(2008, 11, 1, 16, tzinfo=TimeZone.eastern)
>>> setNow(november_first)
>>> item = core.Item()
>>> entry = list(item.dashboard_entries)[0]
>>> app_entry = AppDashboardEntry(entry)

AppDashboardEntry defines is_event, is_starred, triage_status, triage_position and reminder_scheduled cells.

>>> app_entry.is_event
False
>>> event = Event(item).add()
>>> app_entry.is_event
True
>>> app_entry.triage_status
100.0
>>> app_entry.triage_position == nowTimestamp()
True
>>> app_entry.reminder_scheduled
False

reminder_scheduled will be True if the entry has a reminder scheduled in the future.

>>> reminder = ReminderList(item).add_reminder()
>>> app_entry.reminder_scheduled
False
>>> reminder.fixed_trigger = november_first + timedelta(days=1)
>>> app_entry.reminder_scheduled
True
>>> reminder.fixed_trigger = november_first - timedelta(days=1)
>>> app_entry.reminder_scheduled
False

TODO

  • when should probably be moved here
  • add created/edited/new/updated
  • add status (unread, mainly)

AppEntryAggregate: Mapping sets of Items to sets of DashboardEntries

The dashboard module also provides the class AppEntryAggregate. This is a chandler.core.ComputedSet subclass that accepts a Set of Items as its input, producing all associated DashboardEntry instances.

Let’s see how this works in practice. First, we’ll start off with a Set:

>>> items = trellis.Set()
>>> entries = AppEntryAggregate(input=items)
>>> entries
AppEntryAggregate([])

We’ll track changes to entries via a Performer:

>>> def observe_entries():
...     print "%d item(s) (%d added, %d removed)" % (len(entries),
...                                                  len(entries.added),
...                                                  len(entries.removed))
...     if entries.added or entries.removed:
...         for appEntry in sorted(entries, key=lambda appEntry:(appEntry._item.title, appEntry.triage_status)):
...             print "<%s (title=%s triage=%s)>" % (type(appEntry).__name__, appEntry._item.title, appEntry.triage_status)
>>> performer = trellis.Performer(observe_entries)
0 item(s) (0 added, 0 removed)

Let’s now add a few interesting Items to our items Set:

>>> plain_item = core.Item(title=u'This is my first Item!')
>>> event_item = core.Item(title=u'This is another Item, an Event in fact')
>>> event = Event(event_item).add(base_start=getNow() - timedelta(hours=1),
...                               base_duration=timedelta(hours=3))
>>> items.update((plain_item, event_item))
2 item(s) (2 added, 0 removed)
<AppDashboardEntry (title=This is another Item, an Event in fact triage=100.0)>
<AppDashboardEntry (title=This is my first Item! triage=100.0)>
2 item(s) (0 added, 0 removed)
>>> items.remove(plain_item)
1 item(s) (0 added, 1 removed)
<AppDashboardEntry (title=This is another Item, an Event in fact triage=100.0)>
1 item(s) (0 added, 0 removed)

Let’s now make our event have daily recurrence:

>>> from chandler.recurrence import Recurrence
>>> recur = Recurrence(event_item).add(frequency='daily')
2 item(s) (2 added, 1 removed)
<AppDashboardEntry (title=This is another Item, an Event in fact triage=100.0)>
<AppDashboardEntry (title=This is another Item, an Event in fact triage=200.0)>
2 item(s) (0 added, 0 removed)

As (hopefully) expected, we have two new Occurrence objects, one with triage_status of NOW, and the other LATER. (The DashboardEntry corresponding to the “master” Event has been removed).

The Dashboard Interaction Component

The Dashboard class is an interaction component designed to model Chandler’s “Dashboard”, also known as “Triage” or “List” view. This is actually a chandler.core.Table subclass specifically configured to display the correct columns for a set of AppDashboardEntry objects.

The model of a Dashboard is expected to be a Set of AppDashboardEntry objects. In practice, this would usually be a AppEntryAggregate. For example, we might use our entries from above:

>>> db = Dashboard(model=entries)

XXX: The items attribute name is a little confusing here, because you might think these are Item instances. Maybe we should change the name to rows or objects.

>>> type(db.items)
<class 'peak.events.collections.SortedSet'>
>>> db.items.data is db.model
True

For the moment, the Dashboard only comes with a single column, which displays whether or not the entry in question is starred:

>>> db.star_column
<chandler.core.TableColumn object at 0x...>
>>> list(db.columns) == [db.star_column]
True

TODO

  • add more columns
  • demonstrate sorting
  • extending the Dashboard
  • (LATER) sections