class Calendar(object): def __init__(self, name, dbpath, path, readonly=False, color='', unicode_symbols=True, default_tz=None, local_tz=None): """ :param name: the name of the calendar :type name: str :param dbpath: path where the local chaching db should be saved :type dbpath: str :param readonly: if True, this Calendar cannot be modified :type readonly: bool :param color: the color which this calendar's events should be printed in :type color: str :param unicode_symbols: if True, unicode symbols will be used for representing this calendars's events properties :type unicode_symbols: bool :param default_tz: timezone used by default :tpye default_tz: pytz.timezone :param local_tz: the timezone this calendar's event's times should be shown in :type local_tz: pytz.timezone """ self._default_tz = default_tz self._local_tz = default_tz if local_tz is None else local_tz self.name = name self.color = color self.path = os.path.expanduser(path) self._dbtool = backend.SQLiteDb( self.name, dbpath, default_tz=self._default_tz, local_tz=self._local_tz, ) self._storage = FilesystemStorage(path, '.ics') self._readonly = readonly self._unicode_symbols = unicode_symbols if self._db_needs_update(): self.db_update() def local_ctag(self): return os.path.getmtime(self.path) def get_by_time_range(self, start, end, show_deleted=False): return self._dbtool.get_time_range(start, end, show_deleted) def get_allday_by_time_range(self, start, end=None, show_deleted=False): return self._dbtool.get_allday_range(start, end, show_deleted) def get_datetime_by_time_range(self, start, end, show_deleted=False): return self._dbtool.get_time_range(start, end, show_deleted) def get_event(self, href): event = self._dbtool.get(href) event.color = self.color event.readonly = self.readonly event.unicode_symbols = self.unicode_symbols return event def update(self, event): """update an event in the database param event: the event that should be updated type event: event.Event """ if self._readonly: raise ReadOnlyCalendarError() if event.etag is None: self.new(event) else: self._storage.update(event.href, event, event.etag) self._dbtool.update(event.vevent.to_ical(), event.href, etag=event.etag) def new(self, event): """save a new event to the database param event: the event that should be updated type event: event.Event """ if self._readonly: raise ReadOnlyCalendarError() event.href, event.etag = self._storage.upload(event) self._dbtool.update(event.to_ical(), event.href, event.etag) self._dbtool.set_ctag(self.local_ctag()) def delete(self, event): """delete event from this collection """ if self._readonly: raise ReadOnlyCalendarError() self._storage.delete(event.href, event.etag) self._dbtool.delete(event.href) def _db_needs_update(self): if self.local_ctag() == self._dbtool.get_ctag(): return False else: return True def db_update(self): """update the db from the vdir, should be called after every change to the vdir """ storage_hrefs = list() for href, etag in self._storage.list(): storage_hrefs.append(href) if etag != self._dbtool.get_etag(href): self._update_vevent(href) db_hrefs = [href for href, etag in self._dbtool.list()] for href in set(db_hrefs) - set(storage_hrefs): self._dbtool.delete(href) self._dbtool.set_ctag(self.local_ctag()) def _update_vevent(self, href): """should only be called during db_update, does not check for readonly""" event, etag = self._storage.get(href) try: self._dbtool.update(event.raw, href=href, etag=etag) return True except Exception as error: if not isinstance(error, UnsupportedFeatureError): logger.exception('') logger.error( "During `update_vevent` we failed to parse vcard {}/{} with " "error\n\"{}\",\nthis means, that event is not available in " "khal.".format(self.name, href, error)) return False def new_event(self, ical, local_tz, default_tz): """creates and returns (but does not insert) new event from ical string""" return Event(ical=ical, calendar=self.name, local_tz=local_tz, default_tz=default_tz)
class Calendar(object): def __init__(self, name, dbpath, path, readonly=False, color='', unicode_symbols=True, default_tz=None, local_tz=None, debug=True): """ :param name: the name of the calendar :type name: str :param dbpath: path where the local chaching db should be saved :type dbpath: str :param readonly: if True, this Calendar cannot be modified :type readonly: bool :param color: the color which this calendar's events should be printed in :type color: str :param unicode_symbols: if True, unicode symbols will be used for representing this calendars's events properties :type unicode_symbols: bool :param default_tz: timezone used by default :tpye default_tz: pytz.timezone :param local_tz: the timezone this calendar's event's times should be shown in :type local_tz: pytz.timezone :param debug: if True, some debugging information will be printed :type debug: bool """ self._default_tz = default_tz self._local_tz = default_tz if local_tz is None else local_tz self.name = name self.color = color self.path = os.path.expanduser(path) self._debug = debug self._dbtool = backend.SQLiteDb( self.name, dbpath, default_tz=self._default_tz, local_tz=self._local_tz, color=self.color, readonly=readonly, debug=self._debug) self._storage = FilesystemStorage(path, '.ics') self._readonly = readonly self._unicode_symbols = unicode_symbols if self._db_needs_update(): self.db_update() def local_ctag(self): return os.path.getmtime(self.path) def get_by_time_range(self, start, end, show_deleted=False): return self._dbtool.get_time_range(start, end, show_deleted) def get_allday_by_time_range(self, start, end=None, show_deleted=False): return self._dbtool.get_allday_range(start, end, show_deleted) def get_datetime_by_time_range(self, start, end, show_deleted=False): return self._dbtool.get_time_range(start, end, show_deleted) def get_event(self, href): return self._dbtool.get(href) def update(self, event): """update an event in the database param event: the event that should be updated type event: event.Event """ if self._readonly: raise ReadOnlyCalendarError() if event.etag is None: self.new(event) else: try: self._storage.update(event.href, event, event.etag) self._dbtool.update(event.vevent.to_ical(), self.name, event.href, etag=event.etag) except Exception as error: logger.error('Failed to parse vcard {} from collection {} ' 'during update'.format(event.href, self.name)) logger.debug(traceback.format_exc(error)) def new(self, event): """save a new event to the database param event: the event that should be updated type event: event.Event """ if self._readonly: raise ReadOnlyCalendarError() href, etag = self._storage.upload(event) event.href = href event.etag = etag try: self._dbtool.update(event.to_ical(), href=href, etag=etag) self._dbtool.set_ctag(self.local_ctag()) except Exception as error: logger.error( 'Failed to parse vcard {} during new in collection ' '{}'.format(event.href, self.name)) logger.debug(traceback.format_exc(error)) def delete(self, event): """delete event from this collection """ if self._readonly: raise ReadOnlyCalendarError() self._storage.delete(event.href, event.etag) self._dbtool.delete(event.href) def _db_needs_update(self): if self.local_ctag() == self._dbtool.get_ctag(): return False else: return True def db_update(self): """update the db from the vdir, should be called after every change to the vdir """ storage_hrefs = list() for href, etag in self._storage.list(): storage_hrefs.append(href) if etag != self._dbtool.get_etag(href): self._update_vevent(href) db_hrefs = [href for href, etag in self._dbtool.list()] for href in set(db_hrefs) - set(storage_hrefs): self._dbtool.delete(href) self._dbtool.set_ctag(self.local_ctag()) def _update_vevent(self, href): """should only be called during db_update, does not check for readonly""" event, etag = self._storage.get(href) try: self._dbtool.update(event.raw, href=href, etag=etag, ignore_invalid_items=True) return True except Exception as error: if not isinstance(error, UnsupportedFeatureError): logger.exception('') logger.error( "During `update_vevent` we failed to parse vcard {}/{} with " "error\n\"{}\",\nthis means, that event is not available in " "khal." .format(self.name, href, error)) return False def new_event(self, ical, local_tz, default_tz): """creates and returns (but does not insert) new event from ical string""" return Event(ical=ical, calendar=self.name, local_tz=local_tz, default_tz=default_tz)
class Calendar(object): def __init__(self, name, dbpath, path, readonly=False, color='', unicode_symbols=True, locale=None): """ :param name: the name of the calendar :type name: str :param dbpath: path where the local chaching db should be saved :type dbpath: str :param readonly: if True, this Calendar cannot be modified :type readonly: bool :param color: the color which this calendar's events should be printed in :type color: str :param unicode_symbols: if True, unicode symbols will be used for representing this calendars's events properties :type unicode_symbols: bool :param locale: the locale settings :type locale: dict() """ self._locale = locale self.name = name self.color = color self.path = os.path.expanduser(path) self._dbtool = backend.SQLiteDb(self.name, dbpath, locale=self._locale) create_directory(path) self._storage = FilesystemStorage(path, '.ics') self._readonly = readonly self._unicode_symbols = unicode_symbols if self._db_needs_update(): self.db_update() @property def readonly(self): return self._readonly def _cover_event(self, event): event.color = self.color event.readonly = self._readonly event.unicode_symbols = self._unicode_symbols return event def local_ctag(self): return os.path.getmtime(self.path) def get_allday_by_time_range(self, start, end=None): return [ self._cover_event(event) for event in self._dbtool.get_allday_range(start, end) ] def get_datetime_by_time_range(self, start, end): return [ self._cover_event(event) for event in self._dbtool.get_time_range(start, end) ] def get_event(self, href): return self._cover_event(self._dbtool.get(href)) def update(self, event): """update an event in the database param event: the event that should be updated type event: event.Event """ with self._dbtool.at_once(): if self._readonly: raise ReadOnlyCalendarError() if event.etag is None: self.new(event) else: etag = self._storage.update(event.href, event, event.etag) self._dbtool.update(event.vevent.to_ical(), event.href, etag=etag) def new(self, event): """save a new event to the database param event: the event that should be updated type event: event.Event """ with self._dbtool.at_once(): if self._readonly: raise ReadOnlyCalendarError() event.href, event.etag = self._storage.upload(event) self._dbtool.update(event.to_ical(), event.href, event.etag) self._dbtool.set_ctag(self.local_ctag()) def delete(self, href, etag): """delete event from this collection """ if self._readonly: raise ReadOnlyCalendarError() self._storage.delete(href, etag) self._dbtool.delete(href) def _db_needs_update(self): if self.local_ctag() == self._dbtool.get_ctag(): return False else: return True def db_update(self): """update the db from the vdir, should be called after every change to the vdir """ db_hrefs = set(href for href, etag in self._dbtool.list()) storage_hrefs = set() with self._dbtool.at_once(): for href, etag in self._storage.list(): storage_hrefs.add(href) dbetag = self._dbtool.get_etag(href) if etag != dbetag: logger.debug('Updating {} because {} != {}'.format( href, etag, dbetag)) self._update_vevent(href) for href in db_hrefs - storage_hrefs: self._dbtool.delete(href) self._dbtool.set_ctag(self.local_ctag()) def _update_vevent(self, href): """should only be called during db_update, does not check for readonly""" event, etag = self._storage.get(href) try: self._dbtool.update(event.raw, href=href, etag=etag) return True except Exception as e: if not isinstance(e, (UpdateFailed, UnsupportedFeatureError)): logger.exception('Unknown exception happened.') logger.warning('Skipping {}/{}: {}\n' 'This event will not be available in khal.'.format( self.name, href, str(e))) return False def new_event(self, ical): """creates and returns (but does not insert) new event from ical string""" return Event(ical=ical, calendar=self.name, locale=self._locale) def search(self, search_string): return [ self._cover_event(event) for event in self._dbtool.search(search_string) ]
class Calendar(object): def __init__(self, name, dbpath, path, readonly=False, color='', unicode_symbols=True, default_tz=None, local_tz=None, debug=True): self._default_tz = default_tz self._local_tz = default_tz if local_tz is None else local_tz self.name = name self.color = color self.path = path self._debug = debug self._dbtool = backend.SQLiteDb( dbpath, default_tz=self._default_tz, local_tz=self._local_tz, debug=self._debug) self._storage = FilesystemStorage(path, '.ics') self._readonly = readonly self._unicode_symbols = unicode_symbols if self._db_needs_update(): self.db_update() def local_ctag(self): return os.path.getmtime(self.path) def get_by_time_range(self, start, end, show_deleted=False): return self._dbtool.get_time_range(start, end, self.name, self.color, self._readonly, self._unicode_symbols, show_deleted) def get_allday_by_time_range(self, start, end=None, show_deleted=False): return self._dbtool.get_allday_range( start, end, self.name, self.color, self._readonly, self._unicode_symbols, show_deleted) def get_datetime_by_time_range(self, start, end, show_deleted=False): return self._dbtool.get_time_range( start, end, self.name, self.color, self._readonly, self._unicode_symbols, show_deleted) def get_event(self, href): return self._dbtool.get_vevent_from_db( href, self.name, color=self.color, readonly=self._readonly, unicode_symbols=self._unicode_symbols) def update(self, event): """update an event in the database param event: the event that should be updated type event: event.Event """ if event.etag is None: self.new(event) else: try: self._storage.update(event.href, event, event.etag) self._dbtool.update(event.vevent.to_ical(), self.name, event.href, etag=event.etag) except Exception as error: logging.error('Failed to parse vcard {} from collection {} ' 'during update'.format(event.href, self.name)) logging.debug(traceback.format_exc(error)) def new(self, event): """save a new event to the database param event: the event that should be updated type event: event.Event """ href, etag = self._storage.upload(event) event.href = href event.etag = etag try: self._dbtool.update(event.to_ical(), self.name, href=href, etag=etag) self._dbtool.set_ctag(self.name, self.local_ctag()) except Exception as error: logging.error( 'Failed to parse vcard {} during new in collection ' '{}'.format(event.href, self.name)) logging.debug(traceback.format_exc(error)) def delete(self, event): """delete event from this collection """ self._storage.delete(event.href, event.etag) self._dbtool.delete(event.href, event.account) def _db_needs_update(self): if self.local_ctag() == self._dbtool.get_ctag(self.name): return False else: return True def db_update(self): """update the db from the vdir, should be called after every change to the vdir """ # This compares folder-content against db-content status = True fs_hrefs = [] for href, etag in self._storage.list(): fs_hrefs.append(href) if etag != self._dbtool.get_etag(href, self.name): status = status and self.update_vevent(href) if status: self._dbtool.set_ctag(self.name, self.local_ctag()) # this compares db-content against folder-content # if an item is removed from the vdir, we also delete it in the db # in this case we asume, that the vdir content is the place to trust for href,account in self._dbtool.get_all_href_from_db([self.name]): if href not in fs_hrefs: self._dbtool.delete(href,self.name) def update_vevent(self, href): event, etag = self._storage.get(href) try: self._dbtool.update(event.raw, self.name, href=href, etag=etag, ignore_invalid_items=True) return True except Exception as error: logging.error( 'Failed to parse vcard {} during ' 'update_vevent in collection ''{}'.format(href, self.name)) logging.debug(traceback.format_exc(error)) return False def new_event(self, ical): """creates new event form ical string""" return Event(ical=ical, account=self.name)
class Calendar(object): def __init__(self, name, dbpath, path, readonly=False, color='', unicode_symbols=True, default_tz=None, local_tz=None, debug=True): self._default_tz = default_tz self._local_tz = default_tz if local_tz is None else local_tz self.name = name self.color = color self.path = path self._debug = debug self._dbtool = backend.SQLiteDb( dbpath, default_tz=self._default_tz, local_tz=self._local_tz, debug=self._debug) self._storage = FilesystemStorage(path, '.ics') self._readonly = readonly self._unicode_symbols = unicode_symbols if self._db_needs_update(): self.db_update() def local_ctag(self): return os.path.getmtime(self.path) def get_by_time_range(self, start, end, show_deleted=False): return self._dbtool.get_time_range(start, end, self.name, self.color, self._readonly, self._unicode_symbols, show_deleted) def get_allday_by_time_range(self, start, end=None, show_deleted=False): return self._dbtool.get_allday_range( start, end, self.name, self.color, self._readonly, self._unicode_symbols, show_deleted) def get_datetime_by_time_range(self, start, end, show_deleted=False): return self._dbtool.get_time_range( start, end, self.name, self.color, self._readonly, self._unicode_symbols, show_deleted) def get_event(self, href): return self._dbtool.get_vevent_from_db( href, self.name, color=self.color, readonly=self._readonly, unicode_symbols=self._unicode_symbols) def update(self, event): """update an event in the database param event: the event that should be updated type event: event.Event """ if event.etag is None: self.new(event) else: try: self._storage.update(event.href, event, event.etag) self._dbtool.update(event.vevent.to_ical(), self.name, event.href, etag=event.etag) except Exception as error: logger.error('Failed to parse vcard {} from collection {} ' 'during update'.format(event.href, self.name)) logger.debug(traceback.format_exc(error)) def new(self, event): """save a new event to the database param event: the event that should be updated type event: event.Event """ href, etag = self._storage.upload(event) event.href = href event.etag = etag try: self._dbtool.update(event.to_ical(), self.name, href=href, etag=etag) self._dbtool.set_ctag(self.name, self.local_ctag()) except Exception as error: logger.error( 'Failed to parse vcard {} during new in collection ' '{}'.format(event.href, self.name)) logger.debug(traceback.format_exc(error)) def delete(self, event): """delete event from this collection """ self._storage.delete(event.href, event.etag) self._dbtool.delete(event.href, event.account) def _db_needs_update(self): if self.local_ctag() == self._dbtool.get_ctag(self.name): return False else: return True def db_update(self): """update the db from the vdir, should be called after every change to the vdir """ status = True for href, etag in self._storage.list(): if etag != self._dbtool.get_etag(href, self.name): status = status and self.update_vevent(href) if status: self._dbtool.set_ctag(self.name, self.local_ctag()) def update_vevent(self, href): event, etag = self._storage.get(href) try: self._dbtool.update(event.raw, self.name, href=href, etag=etag, ignore_invalid_items=True) return True except Exception as error: logger.error( 'Failed to parse vcard {} during ' 'update_vevent in collection ''{}'.format(href, self.name)) logger.debug(traceback.format_exc(error)) return False def new_event(self, ical, local_tz, default_tz): """creates new event form ical string""" return Event(ical=ical, account=self.name, local_tz=local_tz, default_tz=default_tz)