Package modules :: Module calendar_ics
[hide private]
[frames] | no frames]

Source Code for Module modules.calendar_ics

  1  """ 
  2  Syncronize with an iCalendar file 
  3   
  4  This file is part of Pisi. 
  5   
  6  ICalendar support is based on the VOBJECT library by skyhouseconsulting (U{http://vobject.skyhouseconsulting.com/}). 
  7  Consequently, the vobject site-package has to be installed for utilizing ICS support in PISI. 
  8   
  9  Pisi is free software: you can redistribute it and/or modify 
 10  it under the terms of the GNU General Public License as published by 
 11  the Free Software Foundation, either version 3 of the License, or 
 12  (at your option) any later version. 
 13   
 14  Pisi is distributed in the hope that it will be useful, 
 15  but WITHOUT ANY WARRANTY; without even the implied warranty of 
 16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 17  GNU General Public License for more details. 
 18   
 19  You should have received a copy of the GNU General Public License 
 20  along with Pisi.  If not, see <http://www.gnu.org/licenses/> 
 21  """ 
 22   
 23  from events import events 
 24  import datetime,time 
 25  import pisiprogress 
 26  import datetime 
 27  import os.path 
 28  import os 
 29  from pisiconstants import * 
 30  #import vobject 
 31  from vobjecttools import * 
 32   
 33  FIELD_PISI = "X-PISI-ID" 
 34  """Name of additional field in ICS file for PISI ID (must start with X-)""" 
 35   
36 -class SynchronizationModule(events.AbstractCalendarSynchronizationModule):
37 """ 38 The implementation of the interface L{events.AbstractCalendarSynchronizationModule} for the ICalendar file backend 39 """
40 - def __init__( self, modulesString, config, configsection, folder, verbose=False, soft=False):
41 """ 42 Constructor 43 44 Super class constructor (L{events.AbstractCalendarSynchronizationModule.__init__}) is called. 45 Local variables are initialized. 46 The settings from the configuration file are loaded. 47 """ 48 events.AbstractCalendarSynchronizationModule.__init__(self, verbose, soft, modulesString, config, configsection, "ICalendar file") 49 self._folder = folder 50 self._path = config.get(configsection,'path') 51 pisiprogress.getCallback().verbose('ics-module using file %s' % (self._path)) 52 self._rawData = {}
53 54
55 - def _guessAmount(self):
56 """ 57 Try to determine roughly how many entries are in this file (we can't know prior to parsing; but we have a guess using the file size) 58 """ 59 info = os.stat(self._path) 60 return info.st_size / ICS_BYTES_PER_ENTRY
61
62 - def load(self):
63 """ 64 Load all data from local ICS file 65 66 File is opened and the entries are parsed. For each entry an L{events.events.Event} instance is created and stored in the instance dictionary. 67 """ 68 pisiprogress.getCallback().verbose("ICalendar: Loading") 69 file = None 70 try: 71 file = open(self._path, "r") 72 except IOError, e: 73 pisiprogress.getCallback().error("--> Error: Could not load ICS file - we still carry on with processing:\n\t%s" %(e)) 74 return 75 pisiprogress.getCallback().progress.push(0, 100) 76 vcal = vobject.readOne(file) 77 pisiprogress.getCallback().progress.setProgress(20) 78 pisiprogress.getCallback().update("Loading") 79 amount = self._guessAmount() 80 i = 0 81 if vcal.contents.has_key('vevent'): # maybe it's an empty file - you new know ;) 82 for x in vcal.contents['vevent']: 83 atts, globalId, updated = extractICSEntry(x) 84 if not globalId or globalId == "": 85 globalId = events.assembleID() 86 tmpEvent = events.Event( globalId, updated, atts) 87 tmpEvent.attributes['globalid'] = globalId 88 self.replaceEvent(globalId, tmpEvent) 89 else: 90 tmpEvent = events.Event(globalId, updated, atts) 91 tmpEvent.attributes['globalid'] = globalId 92 93 self._allEvents[globalId] = tmpEvent 94 self._rawData[globalId] = x 95 96 if i < amount: 97 i += 1 98 pisiprogress.getCallback().progress.setProgress(20 + ((i*80) / amount)) 99 pisiprogress.getCallback().update('Loading') 100 101 file.close() 102 pisiprogress.getCallback().progress.drop()
103 104
105 - def saveModifications(self):
106 """ 107 Save whatever changes have come by 108 109 Iterates the history of actions and replaces the corresponding items with new vobject instances. 110 In the end, the vobject representation is written to the ICS file. 111 """ 112 pisiprogress.getCallback().verbose("ICalendar module: I apply %d changes now" %(len(self._history))) 113 if len(self._history) == 0: 114 return # don't touch anything if there haven't been any changes 115 116 # rename old file 117 try: 118 bakFilename = os.path.join(self._folder, os.path.basename(self._path)) 119 os.rename(self._path, bakFilename) 120 except OSError, e: 121 pisiprogress.getCallback().verbose("\tError when backing up old ICS file - we still carry on with processing:\n\t%s" %(e)) 122 123 file = open(self._path, "w") 124 125 i = 0 126 for actionItem in self._history: 127 action = actionItem[0] 128 globalId = actionItem[1] 129 if action == ACTIONID_DELETE: 130 pisiprogress.getCallback().verbose("\t\t<ics> deleting %s" %(globalId)) 131 del self._rawData[globalId] 132 elif action == ACTIONID_ADD or action == ACTIONID_MODIFY: 133 pisiprogress.getCallback().verbose("\t\t<ics> adding or replacing %s" %(globalId)) 134 e = self.getEvent(globalId) 135 self._rawData[globalId] = createRawEventEntry(e) 136 i+=1 137 pisiprogress.getCallback().progress.setProgress(i * 70 / len(self._history)) 138 pisiprogress.getCallback().update('Storing') 139 140 pisiprogress.getCallback().progress.setProgress(70) 141 pisiprogress.getCallback().update('Storing') 142 143 pisiprogress.getCallback().verbose("\tICS: Writing through file") 144 calendar = vobject.iCalendar() 145 calendar.add('vevent') 146 del calendar.contents['vevent'][0] 147 for x in self._rawData.values(): 148 calendar.contents['vevent'].append(x) 149 file.write(calendar.serialize()) 150 pisiprogress.getCallback().verbose("\tNew ICS file saved to %s; \n\tbackup file is located in %s." %(self._path, bakFilename)) 151 file.close()
152