Module imp_cal_preset
[hide private]
[frames] | no frames]

Source Code for Module imp_cal_preset

  1  #!/usr/bin/env python 
  2   
  3  ### BITPIM 
  4  ### 
  5  ### Copyright (C) 2006 Joe Pham <djpham@bitpim.org> 
  6  ### 
  7  ### This program is free software; you can redistribute it and/or modify 
  8  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
  9  ### 
 10  ### $Id: imp_cal_preset.py 4380 2007-08-29 00:17:07Z djpham $ 
 11   
 12  """ Handle Import Calendar Preset feature 
 13  """ 
 14   
 15  # System 
 16  from __future__ import with_statement 
 17  import calendar 
 18  import copy 
 19  import datetime 
 20  import random 
 21  import sha 
 22   
 23  # wx 
 24  import wx 
 25  import wx.wizard as wiz 
 26   
 27  # BitPim 
 28  import common_calendar 
 29  import database 
 30  import guihelper 
 31  import imp_cal_wizard 
 32  import importexport 
 33  import setphone_wizard 
 34   
 35  # modules constants 
 36  IMP_OPTION_REPLACEALL=0 
 37  IMP_OPTION_ADD=1 
 38  IMP_OPTION_PREVIEW=2 
 39  IMP_OPTION_MERGE=3 
40 41 #------------------------------------------------------------------------------- 42 -class ImportCalendarDataObject(common_calendar.FilterDataObject):
43 _knownproperties=common_calendar.FilterDataObject._knownproperties+\ 44 ['name', 'type', 'source_id', 'option' ] 45 _knownlistproperties=common_calendar.FilterDataObject._knownlistproperties 46 allproperties=_knownproperties+\ 47 [x for x in _knownlistproperties]+\ 48 [x for x in common_calendar.FilterDataObject._knowndictproperties]
49 importcalendarobjectfactory=database.dataobjectfactory(ImportCalendarDataObject)
50 51 #------------------------------------------------------------------------------- 52 -class ImportCalendarEntry(dict):
53 # a dict class that automatically generates an ID for use with 54 # BitPim database. 55
56 - def __init__(self, data=None):
57 super(ImportCalendarEntry, self).__init__() 58 if data: 59 self.update(data)
60 61 _persistrandom=random.Random()
62 - def _create_id(self):
63 "Create a BitPim serial for this entry" 64 rand2=random.Random() # this random is seeded when this function is called 65 num=sha.new() 66 num.update(`self._persistrandom.random()`) 67 num.update(`rand2.random()`) 68 return num.hexdigest()
69 - def _get_id(self):
70 s=self.get('serials', []) 71 _id=None 72 for n in s: 73 if n.get('sourcetype', None)=='bitpim': 74 _id=n.get('id', None) 75 break 76 if not _id: 77 _id=self._create_id() 78 self._set_id(_id) 79 return _id
80 - def _set_id(self, id):
81 s=self.get('serials', []) 82 for n in s: 83 if n.get('sourcetype', None)=='bitpim': 84 n['id']=id 85 return 86 self.setdefault('serials', []).append({'sourcetype': 'bitpim', 'id': id } )
87 id=property(fget=_get_id, fset=_set_id) 88
89 - def validate_properties(self):
90 # validate and remove non-persistent properties as defined in 91 # ImportCalendarDataObject class 92 _del_keys=[x for x in self \ 93 if not x in ImportCalendarDataObject.allproperties] 94 for _key in _del_keys: 95 del self[_key]
96
97 #------------------------------------------------------------------------------- 98 -class FilterDialog(common_calendar.FilterDialog):
99 - def __init__(self, parent, id, caption, data):
100 super(FilterDialog, self).__init__(parent, id, caption, []) 101 self.set(data)
102
103 - def _get_from_fs(self):
104 pass
105 - def _save_to_fs(self, data):
106 pass
107
108 #------------------------------------------------------------------------------- 109 -class PresetNamePage(setphone_wizard.MyPage):
110 - def __init__(self, parent):
111 super(PresetNamePage, self).__init__(parent, 112 'Calendar Import Preset Name')
113
114 - def GetMyControls(self):
115 vbs=wx.BoxSizer(wx.VERTICAL) 116 vbs.Add(wx.StaticText(self, -1, 'Preset Name:'), 0, 117 wx.ALL|wx.EXPAND, 5) 118 self._name=wx.TextCtrl(self, -1, '') 119 vbs.Add(self._name, 0, wx.ALL|wx.EXPAND, 5) 120 return vbs
121
122 - def ok(self):
123 return bool(self._name.GetValue())
124 - def get(self, data):
125 data['name']=self._name.GetValue()
126 - def set(self, data):
127 self._name.SetValue(data.get('name', ''))
128
129 #------------------------------------------------------------------------------- 130 -class PresetFilterPage(setphone_wizard.MyPage):
131 - def __init__(self, parent):
132 self._data={} 133 super(PresetFilterPage, self).__init__(parent, 134 'Calendar Preset Filter')
135 _col_names=({ 'label': 'Start Date:', 'attr': '_start' }, 136 { 'label': 'End Date:', 'attr': '_end' }, 137 { 'label': 'Preset Duration:', 'attr': '_preset' }, 138 { 'label': 'Repeat Events:', 'attr': '_repeat' }, 139 { 'label': 'Alarm Setting:', 'attr': '_alarm' }, 140 { 'label': 'Alarm Vibrate:', 'attr': '_vibrate' }, 141 { 'label': 'Alarm Ringtone:', 'attr': '_ringtone' }, 142 { 'label': 'Alarm Value:', 'attr': '_alarm_value' }, 143 )
144 - def GetMyControls(self):
145 gs=wx.GridBagSizer(5, 10) 146 gs.AddGrowableCol(1) 147 for _row, _col in enumerate(PresetFilterPage._col_names): 148 gs.Add(wx.StaticText(self, -1, _col['label']), 149 pos=(_row, 0), flag=wx.ALIGN_CENTER_VERTICAL) 150 _w=wx.StaticText(self, -1, _col['attr']) 151 setattr(self, _col['attr'], _w) 152 gs.Add(_w, pos=(_row, 1), flag=wx.ALIGN_CENTER_VERTICAL) 153 _btn=wx.Button(self, -1, 'Modify') 154 wx.EVT_BUTTON(self, _btn.GetId(), self._OnFilter) 155 gs.Add(_btn, pos=(_row+1, 0)) 156 return gs
157
158 - def _OnFilter(self, _):
159 with guihelper.WXDialogWrapper(FilterDialog(self, -1, 'Filtering Parameters', self._data), 160 True) as (dlg, retcode): 161 if retcode==wx.ID_OK: 162 self._data.update(dlg.get()) 163 self._populate()
164
165 - def _display(self, key, attr, fmt):
166 # display the value field of this key 167 _v=self._data.get(key, None) 168 getattr(self, attr).SetLabel(_v is not None and eval(fmt) or '')
169 - def _populate(self):
170 # populate the display with the filter parameters 171 if self._data.get('preset_date', None) is None: 172 _fmt="'%04d/%02d/%02d'%(_v[0], _v[1], _v[2])" 173 else: 174 _fmt="'<Preset>'" 175 self._display('start', '_start', _fmt) 176 self._display('end', '_end', _fmt) 177 self._display('preset_date', '_preset', 178 "['This Week', 'This Month', 'This Year', 'Next 7 Days'][_v]") 179 self._display('rpt_events', '_repeat', 180 "{ True: 'Import as mutil-single events', False: ''}[_v]") 181 if self._data.get('no_alarm', None): 182 _s='Disable All Alarms' 183 elif self._data.get('alarm_override', None): 184 _s='Set Alarm on All Events' 185 else: 186 _s='Use Alarm Settings from Import Source' 187 self._alarm.SetLabel(_s) 188 self._display('vibrate', '_vibrate', 189 "{ True: 'Enable Vibrate for Alarms', False: ''}[_v]") 190 self._display('ringtone', '_ringtone', "'%s'%((_v != 'Select:') and _v or '',)") 191 self._display('alarm_value', '_alarm_value', "'%d'%_v")
192 - def get(self, data):
193 data.update(self._data)
194 - def set(self, data):
195 self._data=data 196 self._populate()
197
198 #------------------------------------------------------------------------------- 199 -class ImportOptionPage(imp_cal_wizard.ImportOptionPage):
200 _choices=('Replace All', 'Add', 'Preview', 'Merge')
201
202 #------------------------------------------------------------------------------- 203 -class ImportCalendarPresetWizard(wiz.Wizard):
204 ID_ADD=wx.NewId()
205 - def __init__(self, parent, entry, 206 id=-1, title='Calendar Import Preset Wizard'):
207 super(ImportCalendarPresetWizard, self).__init__(parent, id, title) 208 self._data=entry 209 _import_name_page=PresetNamePage(self) 210 _import_type_page=imp_cal_wizard.ImportTypePage(self) 211 _import_source_page=imp_cal_wizard.ImportSourcePage(self) 212 _import_filter_page=PresetFilterPage(self) 213 _import_option=ImportOptionPage(self) 214 215 wiz.WizardPageSimple_Chain(_import_name_page, _import_type_page) 216 wiz.WizardPageSimple_Chain(_import_type_page, _import_source_page) 217 wiz.WizardPageSimple_Chain(_import_source_page, _import_filter_page) 218 wiz.WizardPageSimple_Chain(_import_filter_page, _import_option) 219 self.first_page=_import_name_page 220 self.GetPageAreaSizer().Add(self.first_page, 1, wx.EXPAND|wx.ALL, 5) 221 wiz.EVT_WIZARD_PAGE_CHANGING(self, self.GetId(), self.OnPageChanging) 222 wiz.EVT_WIZARD_PAGE_CHANGED(self, self.GetId(), self.OnPageChanged)
223
224 - def RunWizard(self, firstPage=None):
225 return super(ImportCalendarPresetWizard, self).RunWizard(firstPage or self.first_page)
226
227 - def OnPageChanging(self, evt):
228 pg=evt.GetPage() 229 if not evt.GetDirection() or pg.ok(): 230 pg.get(self._data) 231 else: 232 evt.Veto()
233
234 - def OnPageChanged(self, evt):
235 evt.GetPage().set(self._data)
236
237 - def get(self):
238 return self._data
239
240 - def GetActiveDatabase(self):
241 return self.GetParent().GetActiveDatabase()
242
243 - def get_categories(self):
244 if self._data.get('data_obj', None): 245 return self._data['data_obj'].get_category_list() 246 return []
247
248 #------------------------------------------------------------------------------- 249 -class CalendarPreviewDialog(wx.Dialog):
250 ID_ADD=wx.NewId() 251 ID_REPLACE=wx.NewId() 252 ID_MERGE=wx.NewId()
253 - def __init__(self, parent, data):
254 super(CalendarPreviewDialog, self).__init__(parent, -1, 255 'Calendar Import Preview') 256 _vbs=wx.BoxSizer(wx.VERTICAL) 257 _lb=wx.ListBox(self, -1, style=wx.LB_SINGLE|wx.LB_HSCROLL|wx.LB_NEEDED_SB, 258 choices=['%04d/%02d/%02d %02d:%02d - '%x.start+x.description\ 259 for _,x in data.items()]) 260 _vbs.Add(_lb, 0, wx.EXPAND|wx.ALL, 5) 261 _vbs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5) 262 _hbs=wx.BoxSizer(wx.HORIZONTAL) 263 _btn=wx.Button(self, self.ID_REPLACE, 'Replace All') 264 wx.EVT_BUTTON(self, self.ID_REPLACE, self._OnButton) 265 _hbs.Add(_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 266 _btn=wx.Button(self, self.ID_ADD, 'Add') 267 wx.EVT_BUTTON(self, self.ID_ADD, self._OnButton) 268 _hbs.Add(_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 269 _btn=wx.Button(self, self.ID_MERGE, 'Merge') 270 wx.EVT_BUTTON(self, self.ID_MERGE, self._OnButton) 271 _hbs.Add(_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 272 _hbs.Add(wx.Button(self, wx.ID_CANCEL, 'Cancel'), 0, 273 wx.ALIGN_CENTRE|wx.ALL, 5) 274 _vbs.Add(_hbs, 0, wx.EXPAND|wx.ALL, 5) 275 self.SetSizer(_vbs) 276 self.SetAutoLayout(True) 277 _vbs.Fit(self)
278
279 - def _OnButton(self, evt):
280 self.EndModal(evt.GetId())
281
282 #------------------------------------------------------------------------------- 283 -class ImportCalendarPresetDialog(wx.Dialog):
284 ID_ADD=wx.NewId() 285 ID_MERGE=wx.NewId() 286
287 - def __init__(self, parent, id, title):
288 self._parent=parent 289 self._data={} 290 self._import_data=None 291 self._buttons=[] 292 super(ImportCalendarPresetDialog, self).__init__(parent, id, 293 title, 294 size=(500, 500)) 295 _vbs=wx.BoxSizer(wx.VERTICAL) 296 _static_bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, 297 'Available Presets:'), 298 wx.VERTICAL) 299 self._name_lb=wx.ListBox(self, -1, style=wx.LB_SINGLE|wx.LB_NEEDED_SB) 300 wx.EVT_LISTBOX(self, self._name_lb.GetId(), self._set_button_states) 301 _static_bs.Add(self._name_lb, 0, wx.EXPAND|wx.ALL, 5) 302 _vbs.Add(_static_bs, 0, wx.EXPAND|wx.ALL, 5) 303 _vbs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5) 304 _hbs=wx.BoxSizer(wx.HORIZONTAL) 305 _btn=wx.Button(self, -1, 'Import') 306 wx.EVT_BUTTON(self, _btn.GetId(), self._OnRun) 307 self._buttons.append(_btn) 308 _hbs.Add(_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 309 _btn=wx.Button(self, wx.ID_NEW) 310 wx.EVT_BUTTON(self, _btn.GetId(), self._OnNew) 311 _hbs.Add(_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 312 _btn=wx.Button(self, -1, 'Edit') 313 wx.EVT_BUTTON(self, _btn.GetId(), self._OnEdit) 314 self._buttons.append(_btn) 315 _hbs.Add(_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 316 _btn=wx.Button(self, wx.ID_DELETE) 317 self._buttons.append(_btn) 318 wx.EVT_BUTTON(self, _btn.GetId(), self._OnDel) 319 _hbs.Add(_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 320 _hbs.Add(wx.Button(self, wx.ID_CANCEL), 321 0, wx.ALIGN_CENTRE|wx.ALL, 5) 322 _vbs.Add(_hbs, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 323 self.SetSizerAndFit(_vbs) 324 if guihelper.IsGtk(): 325 # On Linux, without this, the dialog clips the buttons 326 self.SetSizeHints(-1, 175) 327 self._get_from_fs() 328 self._populate()
329
330 - def _set_button_states(self, _=None):
331 # set the appropriate states of the buttons depending on whether a 332 # listbox item is selected or not 333 _flg=self._name_lb.GetSelection()!=wx.NOT_FOUND 334 [x.Enable(_flg) for x in self._buttons]
335
336 - def _preview_data(self):
337 # Display a preview of data just imported 338 with guihelper.WXDialogWrapper(CalendarPreviewDialog(self, self._import_data.get()), 339 True) as (_dlg, _ret_code): 340 if _ret_code==CalendarPreviewDialog.ID_REPLACE: 341 return wx.ID_OK 342 elif _ret_code==CalendarPreviewDialog.ID_ADD: 343 return self.ID_ADD 344 elif _ret_code==CalendarPreviewDialog.ID_MERGE: 345 return self.ID_MERGE 346 return wx.ID_CANCEL
347
348 - def _get_preset_thisweek(self):
349 # return the dates of (today, Sat) 350 _today=datetime.date.today() 351 _dow=_today.isoweekday()%7 #Sun=0, Sat=6 352 _end=_today+datetime.timedelta(6-_dow) 353 return ((_today.year, _today.month, _today.day), 354 (_end.year, _end.month, _end.day))
355
356 - def _get_preset_thismonth(self):
357 # return the dates of (today, end-of-month) 358 _today=datetime.date.today() 359 _end=_today.replace(day=calendar.monthrange(_today.year,_today.month)[1]) 360 return ((_today.year, _today.month, _today.day), 361 (_end.year, _end.month, _end.day))
362
363 - def _get_preset_thisyear(self):
364 # return the dates of (today, end-of-year) 365 _today=datetime.date.today() 366 _end=_today.replace(month=12, day=31) 367 return ((_today.year, _today.month, _today.day), 368 (_end.year, _end.month, _end.day))
369
370 - def _get_preset_next7(self):
371 # return the dates of (today, today+6) 372 _today=datetime.date.today() 373 _end=_today+datetime.timedelta(days=6) 374 return ((_today.year, _today.month, _today.day), 375 (_end.year, _end.month, _end.day))
376
377 - def _adjust_filter_dates(self, entry):
378 # Adjust the start/end dates of the filter 379 _preset_date=entry.get('preset_date', None) 380 if _preset_date is None: 381 # No Preset date, bail 382 return 383 entry['start'], entry['end']=getattr(self, 384 ['_get_preset_thisweek', 385 '_get_preset_thismonth', 386 '_get_preset_thisyear', 387 '_get_preset_next7'][_preset_date])()
388 @guihelper.BusyWrapper
389 - def _OnRun(self, _):
390 _idx=self._name_lb.GetSelection() 391 if _idx==wx.NOT_FOUND: 392 return 393 _entry=self._data[self._name_lb.GetClientData(_idx)] 394 _my_type=_entry['type'] 395 _info=[x for x in importexport.GetCalendarImports() \ 396 if x['type']==_my_type] 397 if not _info: 398 return 399 _info=_info[0] 400 self._import_data=_info['data']() 401 _source=_info['source']() 402 _source.id=_entry['source_id'] 403 with guihelper.WXDialogWrapper(wx.ProgressDialog('Calendar Data Import', 404 'Importing data, please wait ...', 405 parent=self)) as _dlg: 406 self._import_data.read(_source.get(), _dlg) 407 self._adjust_filter_dates(_entry) 408 self._import_data.set_filter(_entry) 409 global IMP_OPTION_PREVIEW, IMP_OPTION_REPLACEALL, IMP_OPTION_ADD, IMP_OPTION_MERGE 410 _option=_entry.get('option', IMP_OPTION_PREVIEW) 411 if _option==IMP_OPTION_PREVIEW: 412 _ret_code=self._preview_data() 413 elif _option==IMP_OPTION_ADD: 414 _ret_code=self.ID_ADD 415 elif _option==IMP_OPTION_MERGE: 416 _ret_code=self.ID_MERGE 417 else: 418 _ret_code=wx.ID_OK 419 self.EndModal(_ret_code)
420
421 - def _OnNew(self, _):
422 _entry=ImportCalendarEntry() 423 with guihelper.WXDialogWrapper(ImportCalendarPresetWizard(self, _entry)) \ 424 as _wiz: 425 if _wiz.RunWizard(): 426 _entry=_wiz.get() 427 self._data[_entry.id]=_entry 428 self._save_to_fs() 429 self._populate()
430 - def _OnEdit(self, _):
431 _idx=self._name_lb.GetSelection() 432 if _idx==wx.NOT_FOUND: 433 return 434 _key=self._name_lb.GetClientData(_idx) 435 _entry=self._data[_key].copy() 436 with guihelper.WXDialogWrapper(ImportCalendarPresetWizard(self, _entry)) \ 437 as _wiz: 438 if _wiz.RunWizard(): 439 _entry=ImportCalendarEntry(_wiz.get()) 440 del self._data[_key] 441 self._data[_entry.id]=_entry 442 self._save_to_fs() 443 self._populate()
444 - def _OnDel(self, _):
445 _idx=self._name_lb.GetSelection() 446 if _idx==wx.NOT_FOUND: 447 return 448 _key=self._name_lb.GetClientData(_idx) 449 del self._data[_key] 450 self._save_to_fs() 451 self._populate()
452
453 - def _populate(self):
454 # populate the listbox with the name of the presets 455 self._name_lb.Clear() 456 for _key, _entry in self._data.items(): 457 self._name_lb.Append(_entry['name'], _key) 458 self._set_button_states()
459
460 - def _expand_item(self, entry):
461 item={} 462 item.update(entry) 463 if item.has_key('categories'): 464 del item['categories'] 465 if item.has_key('start'): 466 del item['start'] 467 if entry['start']: 468 item['start']=[{'year': entry['start'][0], 469 'month': entry['start'][1], 470 'day': entry['start'][2] }] 471 if item.has_key('end'): 472 del item['end'] 473 if entry['end']: 474 item['end']=[{'year': entry['end'][0], 475 'month': entry['end'][1], 476 'day': entry['end'][2] }] 477 return item
478 - def _collapse_item(self, entry):
479 item={} 480 item.update(entry) 481 item['categories']=None 482 if item.has_key('start') and item['start']: 483 _d0=item['start'][0] 484 item['start']=(_d0['year'], _d0['month'], _d0['day']) 485 else: 486 item['start']=None 487 if item.has_key('end') and item['end']: 488 _d0=entry['end'][0] 489 item['end']=(_d0['year'], _d0['month'], _d0['day']) 490 else: 491 item['end']=None 492 return item
493
494 - def _get_from_fs(self):
495 # read the presets data from DB 496 _data=self._parent.GetActiveDatabase().getmajordictvalues('imp_cal_preset', 497 importcalendarobjectfactory) 498 self._data={} 499 for _, _val in _data.items(): 500 _entry=ImportCalendarEntry(self._collapse_item(_val)) 501 self._data[_entry.id]=_entry 502 self._populate()
503
504 - def _save_to_fs(self):
505 _data={} 506 for _key, _entry in self._data.items(): 507 _entry.validate_properties() 508 _data[_key]=self._expand_item(_entry) 509 database.ensurerecordtype(_data, importcalendarobjectfactory) 510 self._parent.GetActiveDatabase().savemajordict('imp_cal_preset', 511 _data)
512
513 - def get(self):
514 if self._import_data: 515 return self._import_data.get() 516 return {}
517
518 - def get_categories(self):
519 if self._import_data: 520 return self._import_data.get_category_list() 521 return []
522
523 - def GetActiveDatabase(self):
524 return self._parent.GetActiveDatabase()
525 526 #------------------------------------------------------------------------------- 527 # Testing 528 if __name__=="__main__": 529 app=wx.PySimpleApp() 530 f=wx.Frame(None, title='imp_cal_preset') 531 _data=ImportCalendarEntry() 532 _data.id 533 with guihelper.WXDialogWrapper(ImportCalendarPresetWizard(f, _data)) \ 534 as w: 535 print 'RunWizard:',w.RunWizard() 536 print 'Data:',w.get() 537