Chandler logo

Translator

SharingTranslator implements the callbacks for converting between items and records.

>>> from datetime import datetime, timedelta
>>> from decimal import Decimal
>>> from chandler.core import Item, Collection
>>> from chandler.triage import Triage
>>> from chandler.event import Event
>>> from chandler.recurrence import Recurrence
>>> from chandler.reminder import ReminderList
>>> from chandler.time_services import TimeZone
>>> TimeZone.default = TimeZone.pacific
>>> from chandler.sharing import translator, legacy_model, eim, new_model
>>> t = translator.SharingTranslator()

ItemRecord type

Importing

>>> t.startImport()
>>> rec = legacy_model.ItemRecord(
...         uuid='f230dcd4-7c32-4c3f-908b-d92081cc9a89',
...         title='Translator test',
...         triage='200 -1167792143.00 1',
...         createdOn=Decimal("1164803131"),
...         hasBeenSent=0,
...         needsReply=0,
...         read=0,
... )
>>> rs = eim.RecordSet([rec])
>>> t.importRecords(rs)
>>> t.finishImport()
>>> item = eim.get_item_for_uuid('f230dcd4-7c32-4c3f-908b-d92081cc9a89')
>>> item is not None
True
>>> item.title
u'Translator test'
>>> Triage(item).manual
200.0
>>> Triage(item).manual_timestamp
1167792143.0
>>> item.created
1164803131.0

Exporting

>>> t.startExport()
>>> records = list(t.exportItem(item))
>>> records
[ItemRecord('f230dcd4-7c32-4c3f-908b-d92081cc9a89', u'Translator test', '200 -1167792143.00 0', Decimal("1164803131"), NoChange, NoChange, NoChange), NoteRecord('f230dcd4-7c32-4c3f-908b-d92081cc9a89', u'', Inherit, Inherit, Inherit, Inherit), DisplayAlarmRecord('f230dcd4-7c32-4c3f-908b-d92081cc9a89', None, None, None, None)]

NoteRecord type

Importing

>>> rec = legacy_model.NoteRecord(
...         uuid='f230dcd4-7c32-4c3f-908b-d92081cc9a89',
...         body='This is the body',
...         icalUid='f230dcd4-7c32-4c3f-908b-d92081cc9a89',
...         icalProperties=None,
...         icalParameters=None,
...         icalExtra=None,
... )
>>> rs = eim.RecordSet([rec])
>>> t.startImport()
>>> t.importRecords(rs)
>>> t.finishImport()
>>> item.body
u'This is the body'
>>> eim.EIM(item).ical_extra is None
True
>>> eim.EIM(item).ical_uid
u'f230dcd4-7c32-4c3f-908b-d92081cc9a89'

Exporting

>>> t.startExport()
>>> records = list(t.exportItem(item))
>>> records
[ItemRecord('f230dcd4-7c32-4c3f-908b-d92081cc9a89', u'Translator test', '200 -1167792143.00 0', Decimal("1164803131"), NoChange, NoChange, NoChange), NoteRecord('f230dcd4-7c32-4c3f-908b-d92081cc9a89', u'This is the body', u'f230dcd4-7c32-4c3f-908b-d92081cc9a89', Inherit, Inherit, Inherit), DisplayAlarmRecord('f230dcd4-7c32-4c3f-908b-d92081cc9a89', None, None, None, None)]

EventRecord type

Importing

>>> Event.installed_on(item)
False
>>> rec = legacy_model.EventRecord(
...         uuid='f230dcd4-7c32-4c3f-908b-d92081cc9a89',
...         dtstart = ';VALUE=DATE-TIME;TZID=America/Los_Angeles:20070201T140000',
...         duration = 'PT1H',
...         location = 'Nowhere',
...         rrule = None,
...         exrule = None,
...         rdate = None,
...         exdate = None,
...         status = 'CANCELLED',
...         lastPastOccurrence = None,
... )

Explain the record:

>>> for field in sorted(rec.explain()):
...     print field[:2]
('Duration', '1:00:00 (hh:mm:ss)')
('Event status', u'FYI')
('Location', u'Nowhere')
('Start time', '2007-02-01 14:00:00-08:00')
('exdate', None)
('exrule', None)
('lastPastOccurrence', None)
('rdate', None)
('rrule', None)
>>> rs = eim.RecordSet([rec])
>>> t.startImport()
>>> t.importRecords(rs)
>>> t.finishImport()
>>> Event.installed_on(item)
True
>>> event = Event(item)
>>> event.transparency
'fyi'
>>> event.location
u'Nowhere'
>>> event.start
datetime.datetime(2007, 2, 1, 14, 0, tzinfo=<ICUtzinfo: America/Los_Angeles>)
>>> event.duration
datetime.timedelta(0, 3600)

Exporting

>>> t.startExport()
>>> records = [r for r in t.exportItem(item) if isinstance(r, legacy_model.EventRecord)]
>>> records
[EventRecord('f230dcd4-7c32-4c3f-908b-d92081cc9a89', u';VALUE=DATE-TIME;TZID=America/Los_Angeles:20070201T140000', u'PT1H', u'Nowhere', None, None, None, None, u'CANCELLED', NoChange)]
>>> item2 = Item(title="Item for export that wasn't imported")
>>> dt = datetime(2008, 10, 1, 18, tzinfo = TimeZone.pacific)
>>> event2 = Event(item2).add(base_start=dt, tzinfo=TimeZone.pacific)
>>> eim.EIM.installed_on(item2)
False
>>> for r in t.exportItem(item2):
...     print r
ItemRecord(..., u"Item for export that wasn't imported", Inherit, ...)
NoteRecord(..., u'', Inherit, Inherit, Inherit, Inherit)
DisplayAlarmRecord('...', None, None, None, None)
EventRecord('...', u';VALUE=DATE-TIME;TZID=US/Pacific:20081001T180000', u'PT1H', None, None, None, None, None, u'CONFIRMED', NoChange)
>>> eim.EIM.installed_on(item2)
True
>>> eim.item_for_uuid(str(eim.EIM(item2).uuid)) is item2
True

Recurrence

Importing

>>> new_rec = legacy_model.EventRecord(
...             uuid='f230dcd4-7c32-4c3f-908b-d92081cc9a89',
...             dtstart = rec.dtstart,
...             duration = rec.duration,
...             location = rec.location,
...             rrule = 'FREQ=WEEKLY;COUNT=5;BYDAY=TU,TH',
...             exrule = None,
...             rdate = ';VALUE=DATE-TIME;TZID=America/Los_Angeles:20070225T140000,20070223T140000',
...             exdate = ';VALUE=DATE-TIME;TZID=America/Los_Angeles:20070206T140000',
...             status = rec.status,
...             lastPastOccurrence = None,
... )

The rrule and rdate fields are order independent, so they’re sorted alphabetically:

>>> new_rec.rrule
u'BYDAY=TU,TH;COUNT=5;FREQ=WEEKLY'
>>> rs = eim.RecordSet([new_rec])
>>> t.startImport()
>>> t.importRecords(rs)
>>> t.finishImport()
>>> recurrence = Recurrence(item)
>>> [i.day for i in recurrence.rruleset]
[1, 8, 13, 15, 23, 25]

Exporting

>>> t.startExport()
>>> allRecords = list(t.exportItem(item))
>>> records = [r for r in allRecords if isinstance(r, legacy_model.EventRecord)]
>>> len(records) == 1
True
>>> record = records[0]
>>> record.rrule
u'BYDAY=TU,TH;COUNT=5;FREQ=WEEKLY'
>>> record.exrule is None
True
>>> record.rdate
u';VALUE=DATE-TIME;TZID=America/Los_Angeles:20070223T140000,20070225T140000'
>>> record.exdate
u';VALUE=DATE-TIME;TZID=America/Los_Angeles:20070206T140000'

Triage status isn’t meaningful for master events, so it should be NoChange for the recurrence master.

>>> itemRecords = [r for r in allRecords if isinstance(r, legacy_model.ItemRecord)]
>>> len(itemRecords) == 1
True
>>> itemRecord = itemRecords[0]
>>> itemRecord.triage
NoChange

Modifications

>>> mod_rec = legacy_model.ItemRecord(
...         uuid='f230dcd4-7c32-4c3f-908b-d92081cc9a89:20070223T220000Z',
...         title='Changed title',
...         triage='100 -1167792143.00 1',
...         createdOn=eim.Inherit,
...         hasBeenSent=eim.Inherit,
...         needsReply=eim.Inherit,
...         read=eim.Inherit,
... )

The explain method should handle Inherit values:

>>> for field in sorted(mod_rec.explain()):
...     print field[:2]
('Created on', Inherit)
('Has been read', Inherit)
('Has been sent', Inherit)
('Needs reply', Inherit)
('Title', u'Changed title')
('Triage status', u'Now')

Importing

>>> rs = eim.Diff([mod_rec])
>>> t.startImport()
>>> t.importRecords(rs)
>>> t.finishImport()
>>> mod = recurrence.get_occurrence(event.start + timedelta(22))
>>> mod.title
u'Changed title'
>>> Triage(mod).calculated
100.0

Exporting

>>> t.startExport()
>>> records = list(t.exportItem(mod))
>>> event_records = [r for r in records if isinstance(r, legacy_model.EventRecord)]
>>> len(event_records) == 1
True
>>> event_record = event_records[0]
>>> event_record.dtstart
Inherit
>>> event_record.duration
Inherit
>>> item_records = [r for r in records if isinstance(r, legacy_model.ItemRecord)]
>>> len(item_records) == 1
True
>>> item_record = item_records[0]
>>> item_record.title
u'Changed title'
>>> item_record.triage
Inherit

Custom Reminders

Importing

>>> t.startImport()
>>> item_rec = legacy_model.ItemRecord(
...              uuid='a9b019a4-d995-11db-f269-0016cbca6aed',
...              title='Item with reminder',
...              triage='200 -1167792143.00 1',
...              createdOn=Decimal("1164803131"),
...              hasBeenSent=0,
...              needsReply=0,
...              read=0,
... )
>>> alarm_rec = legacy_model.DisplayAlarmRecord(
...               uuid='a9b019a4-d995-11db-f269-0016cbca6aed',
...               description="Don't forget!",
...               trigger=';VALUE=DATE-TIME:20060304T220000Z',
...               duration="PT1H",
...               repeat=1,
... )
>>> rs = eim.Diff([item_rec, alarm_rec])
>>> t.importRecords(rs)
>>> t.finishImport()
>>> item = eim.get_item_for_uuid('a9b019a4-d995-11db-f269-0016cbca6aed')
>>> reminders = ReminderList(item).reminders
>>> len(reminders)
1
>>> reminders[0]
<chandler.reminder.Reminder object at ...>
>>> reminders[0].fixed_trigger.astimezone(TimeZone.pacific)
datetime.datetime(2006, 3, 4, 14, 0, tzinfo=<ICUtzinfo: US/Pacific>)
>>> reminders[0].description
u"Don't forget!"

Exporting

>>> t.startExport()
>>> records = list(t.exportItem(item))
>>> len(records)
3
>>> records[0]
ItemRecord('a9b019a4-d995-11db-f269-0016cbca6aed', u'Item with reminder', '200 -1167792143.00 0', Decimal("1164803131"), ...)
>>> records[1]
NoteRecord('a9b019a4-d995-11db-f269-0016cbca6aed', u'', Inherit, Inherit, Inherit, Inherit)
>>> records[2]
DisplayAlarmRecord('a9b019a4-d995-11db-f269-0016cbca6aed', u"Don't forget!", u';VALUE=DATE-TIME:20060304T220000Z', ...)
>>> reminders[0].delta = timedelta(hours=-2)
>>> reminders[0].fixed_trigger = None
>>> t.startExport()
>>> records = list(t.exportItem(item))
>>> records[2]
DisplayAlarmRecord('a9b019a4-d995-11db-f269-0016cbca6aed', u"Don't forget!", u'-PT2H', ...)

Relative Reminders

Importing

>>> item_rec = legacy_model.ItemRecord(
...              uuid='800644b6-dd73-11db-f79a-0016cbca6aed',
...              title='Event Item with reminder',
...              triage='200 -1167792996.00 1',
...              createdOn=Decimal("1164805552"),
...              hasBeenSent=0,
...              needsReply=1,
...              read=0,
... )
>>> note_rec = legacy_model.NoteRecord(
...         uuid='800644b6-dd73-11db-f79a-0016cbca6aed',
...         body='This is the body',
...         icalUid=None,
...         icalExtra=None,
...         icalProperties=None,
...         icalParameters=None,
... )
>>> event_rec = legacy_model.EventRecord(
...         uuid='800644b6-dd73-11db-f79a-0016cbca6aed',
...         dtstart = ';VALUE=DATE-TIME;TZID=America/Los_Angeles:20070516T180000',
...         duration = 'PT1H',
...         location = 'Nowhere',
...         rrule = None,
...         exrule = None,
...         rdate = None,
...         exdate = None,
...         status = 'CONFIRMED',
...         lastPastOccurrence = None,
... )
>>> relative_alarm_rec = legacy_model.DisplayAlarmRecord(
...         uuid='800644b6-dd73-11db-f79a-0016cbca6aed',
...         description="Time to go!",
...         trigger='-PT5M',
...         duration=eim.Inherit,
...         repeat=eim.Inherit,
... )
>>> rs = eim.Diff([item_rec, note_rec, event_rec,
...                         relative_alarm_rec])
>>> t.importRecords(rs)
>>> t.finishImport()
>>> item = eim.get_item_for_uuid('800644b6-dd73-11db-f79a-0016cbca6aed')
>>> Event.installed_on(item)
True
>>> len(ReminderList(item).reminders)
1
>>> reminder = ReminderList(item).reminders[0]
>>> reminder
<chandler.reminder.Reminder object at ...>
>>> reminder.delta
datetime.timedelta(-1, 86100)
>>> reminder.description
u'Time to go!'

Exporting

>>> t.startExport()
>>> records = list(t.exportItem(item))
>>> len(records)
4
>>> records[0]
ItemRecord('800644b6-dd73-11db-f79a-0016cbca6aed', u'Event Item with reminder', '200 -1167792996.00 0', Decimal("1164805552"), ...)
>>> records[1]
NoteRecord('800644b6-dd73-11db-f79a-0016cbca6aed', ...)
>>> records[2]
DisplayAlarmRecord('800644b6-dd73-11db-f79a-0016cbca6aed', u'Time to go!', u'-PT5M', ...)
>>> records[3]
EventRecord('800644b6-dd73-11db-f79a-0016cbca6aed', u';VALUE=DATE-TIME;TZID=America/Los_Angeles:20070516T180000', u'PT1H', u'Nowhere', None, None, None, None, u'CONFIRMED', ...)

Collections

>>> d = translator.DumpTranslator()
>>> rec1 = legacy_model.ItemRecord(
...         "d3ba7961-c9b4-4e73-a860-cc8cbb40acc7",
...         "Interesting Collection Title",
...         eim.NoChange, eim.NoChange, eim.NoChange, eim.NoChange, eim.NoChange
... )
>>> rec2 = legacy_model.CollectionRecord(
...         "d3ba7961-c9b4-4e73-a860-cc8cbb40acc7",
...         0,
...         0, 0, 0, 0
... )
>>> rs = eim.Diff([rec1, rec2])
>>> d.startImport()
>>> d.importRecords(rs)
>>> d.finishImport()
>>> collection = eim.collection_for_name("d3ba7961-c9b4-4e73-a860-cc8cbb40acc7")
>>> collection
<Collection: Interesting Collection Title>
>>> d.startExport()
>>> for record in eim.sort_records(d.exportItem(collection)):
...     print record
CollectionRecord('d3ba7961-c9b4-4e73-a860-cc8cbb40acc7', 0, 0, 0, 0, None, 0)
ItemRecord('d3ba7961-c9b4-4e73-a860-cc8cbb40acc7', u'Interesting Collection Title', ...)

Keywords

Keywords are imported and exported from the sidebar using KeywordRecord.

>>> d = translator.DumpTranslator()
>>> rec1 = new_model.KeywordRecord(
...         "@keyword:Stuff",
...         127, 127, 127, 0
... )
>>> rs = eim.Diff([rec1])
>>> d.startImport()
>>> d.importRecords(rs)
>>> d.finishImport()
>>> keyword = eim.get_for_name_or_uuid("@keyword:Stuff")
>>> keyword
<Keyword: Stuff>
>>> d.startExport()
>>> for record in eim.sort_records(d.exportItem(keyword)):
...     print record
KeywordRecord(u'@keyword:Stuff', 127, 127, 127, None, 0)

Collection Membership

Collections can be specified either by well-known name or UUID.

Well-Known Name

>>> note = Item()
>>> rec = legacy_model.CollectionMembershipRecord(
...         collectionID="@dashboard",
...         itemUUID=eim.EIM(note).add().uuid,
...         index=0,
... )
>>> rs = eim.Diff([rec])
>>> d.startImport()
>>> d.importRecords(rs)
>>> d.finishImport()
>>> dashboard = eim.collection_for_name("@dashboard")
>>> note in dashboard.items
True
>>> list(d.export_collection_memberships(dashboard))
[CollectionMembershipRecord(u'@dashboard', '...', NoChange)]

By UUID

>>> collection = Collection()
>>> note = Item()
>>> rec = legacy_model.CollectionMembershipRecord(
...         collectionID=eim.EIM(collection).add().uuid,
...         itemUUID=eim.EIM(note).add().uuid,
...         index=0,
... )
>>> rs = eim.Diff([rec])
>>> d.startImport()
>>> d.importRecords(rs)
>>> d.finishImport()
>>> note in collection.items
True

Cosmo workarounds

Verify the special converters we put in place for turning None to NoChange for event status, and empty string to NoChange for triage are working

>>> rec = legacy_model.ItemRecord(
...         uuid='f230dcd4-7c32-4c3f-908b-d92081cc9a89',
...         title='triage test',
...         triage='',
...         createdOn=Decimal("1164803131"),
...         hasBeenSent=0,
...         needsReply=0,
...         read=0,
... )
>>> rec.triage
NoChange
>>> rec = legacy_model.EventRecord(
...         uuid='f230dcd4-7c32-4c3f-908b-d92081cc9a89',
...         dtstart = ';VALUE=DATE-TIME;TZID=America/Los_Angeles:20070201T140000',
...         duration = 'PT1H',
...         location = 'Nowhere',
...         rrule = None,
...         exrule = None,
...         rdate = None,
...         exdate = None,
...         status = None,
...         lastPastOccurrence = None,
... )
>>> rec.status
NoChange
>>> rec = legacy_model.EventRecord(
...         uuid='f230dcd4-7c32-4c3f-908b-d92081cc9a89',
...         dtstart = ';VALUE=DATE-TIME;TZID=America/Los_Angeles:20070201T140000',
...         duration = None,
...         location = 'Nowhere',
...         rrule = None,
...         exrule = None,
...         rdate = None,
...         exdate = None,
...         status = None,
... )
>>> rec.duration
'PT0S'