def get_events(self): """Generator that yields all events in the calendar. """ ics_filenames = glob(os.path.join(self.path, 'Events', '*.ics')) for ics_filename in ics_filenames: with open(ics_filename, 'rt') as ics_file: for component in vobject.readComponents(ics_file): for event in component.vevent_list: #event.prettyPrint() event.dtstart.value = tz.normalize_to_utc(event.dtstart.value) event.dtend.value = tz.normalize_to_utc(event.dtend.value) #event.prettyPrint() yield event return
def main(args=sys.argv[1:]): option_parser = optparse.OptionParser( usage='usage: ical2org [options] [calendar titles]', conflict_handler='resolve', description='Convert iCal calendar entries to org-mode data for use with emacs', ) option_parser.add_option('-c', '--config', action='store', dest='config_filename', help='Configuration file name. Defaults to ~/.ical2org', default=os.path.expanduser('~/.ical2org'), ) option_parser.add_option('--begin', '-b', '--days-ago', action='store', dest='days_ago', help='Number of days back in time to search. Defaults to 14.', default=14, type=int, ) option_parser.add_option('--end', '-e', '--days-ahead', action='store', dest='days_ahead', help='Number of days forward in time to search. Defaults to 30.', default=30, type=int, ) option_parser.add_option('-v', '--verbose', action='count', dest='verbose_level', default=1, help='Increase verbose level', ) option_parser.add_option('-q', '--quiet', action='store_const', const=0, dest='verbose_level', help='Turn off verbose output', ) option_parser.add_option('--output-file', '-o', action='store', dest='output_file_name', help='Write the output to the named file instead of stdout', default=None, ) option_parser.add_option('--all', action='store_false', dest='active_only', default=True, help='Include all calendars, not just active.', ) option_parser.add_option('--input-directory', action='store', dest='input_directory', default=os.path.expanduser('~/Library/Calendars'), help='Directory containing calendars. Defaults to ~/Library/Calendars.', ) option_parser.add_option('--format', '-f', action='store', dest='format', type='choice', choices=FORMATTER_FACTORIES.keys(), default='org', help='Output format. One of %s. Defaults to "diary".' % FORMATTER_FACTORIES.keys(), ) option_parser.add_option('--opt', '--formatter-option', action='callback', type='string', callback=remember_formatter_option, help='Formatter-specific option name=value', ) option_parser.add_option('--help', action='callback', callback=show_verbose_help, help='Verbose help', ) options, calendar_titles = option_parser.parse_args(args) log_level = VERBOSE_LEVELS.get(options.verbose_level, logging.DEBUG) logging.basicConfig(level=log_level, format='%(message)s') config = ConfigParser() config.read([options.config_filename]) # Compute the date range for items to be included in our output. start_date = tz.normalize_to_utc(datetime.datetime.combine( datetime.date.today() - datetime.timedelta(options.days_ago), datetime.time.min, )) end_date = tz.normalize_to_utc(datetime.datetime.combine( datetime.date.today() + datetime.timedelta(options.days_ahead + 1), datetime.time.min, )) logging.info('Starting %d days ago at %s', options.days_ago, start_date.astimezone(tz.local)) logging.info('Ending %d days from now at %s', options.days_ahead, end_date.astimezone(tz.local)) if options.output_file_name: logging.info('Writing to %s', options.output_file_name) # Load the calendars if calendar_titles: calendar_generator = calendars.get_by_titles(path=options.input_directory, titles=calendar_titles) else: calendar_generator = calendars.discover(path=options.input_directory, active_only=options.active_only) # Process the calendar data output = sys.stdout if options.output_file_name: output = codecs.open(options.output_file_name, 'wt', 'UTF-8') try: formatter = FORMATTER_FACTORIES[options.format](output, config, options) for calendar in calendar_generator: logging.info('Processing: %s', calendar.title) formatter.start_calendar(calendar) for event in filter.unique(filter.by_date_range(calendar.get_events(), start_date, end_date, )): logging.info(' %s', event.summary.value) formatter.add_event(event) formatter.end_calendar(calendar) finally: formatter.close() if output != sys.stdout: output.close() return
def by_date_range(events, start, end): """Iterate over the incoming events and yield those that fall within the date range. """ log.debug('filtering between %s and %s', start, end) for event in events: # Fix time zones in date objects event_start = event.dtstart.value event_end = event.dtend.value if not isinstance(event_start, datetime.datetime): event_start = datetime.datetime.combine(event.dtstart.value, datetime.time.min, ) if event_start == event_end: event_end = datetime.datetime.combine(event.dtend.value, datetime.time.max, ) else: event_end = datetime.datetime.combine(event.dtend.value, datetime.time.min, ) event_start = tz.normalize_to_utc(event_start) event_end = tz.normalize_to_utc(event_end) # Replace the dates in case we updated the timezone event.dtstart.value = event_start event.dtend.value = event_end # event.prettyPrint() # sys.stdout.flush() event_rrule = getattr(event, 'rrule', None) log.debug('checking %s - %s == %s', event.dtstart.value, event.dtend.value, event.summary.value, ) if event_rrule is not None: duration = event.dtend.value - event.dtstart.value rruleset = event.getrruleset(False) # Clean up timezone values in rrules. # Based on ShootQ calendarparser module. for rrule in rruleset._rrule: # normalize start and stop dates for each recurrance if rrule._dtstart: rrule._dtstart = tz.normalize_to_utc(rrule._dtstart) if hasattr(rrule, '_dtend') and rrule._dtend: rrule._dtend = tz.normalize_to_utc(rrule._dtend) if rrule._until: rrule._until = tz.normalize_to_utc(rrule._until) if rruleset._exdate: # normalize any exclusion dates exdates = [] for exdate in rruleset._exdate: exdate = tz.normalize_to_utc(exdate) rruleset._exdate = exdates if hasattr(rruleset, '_tzinfo') and rruleset._tzinfo is None: # if the ruleset doesn't have a time zone, give it # the local zone rruleset._tzinfo = tz.local # Explode the event into repeats for recurrance in rruleset.between(start, end, inc=True): log.debug(' recurrance %s', recurrance) dupe = event.__class__.duplicate(event) dupe.dtstart.value = tz.normalize_to_utc(recurrance) dupe.dtend.value = tz.normalize_to_utc(recurrance + duration) yield dupe elif event_start >= start and event_end <= end: yield event
def main(args=sys.argv[1:]): option_parser = optparse.OptionParser( usage='usage: ical2org [options] [calendar titles]', conflict_handler='resolve', description= 'Convert iCal calendar entries to org-mode data for use with emacs', ) option_parser.add_option( '-c', '--config', action='store', dest='config_filename', help='Configuration file name. Defaults to ~/.ical2org', default=os.path.expanduser('~/.ical2org'), ) option_parser.add_option( '--begin', '-b', '--days-ago', action='store', dest='days_ago', help='Number of days back in time to search. Defaults to 14.', default=14, type=int, ) option_parser.add_option( '--end', '-e', '--days-ahead', action='store', dest='days_ahead', help='Number of days forward in time to search. Defaults to 30.', default=30, type=int, ) option_parser.add_option( '-v', '--verbose', action='count', dest='verbose_level', default=1, help='Increase verbose level', ) option_parser.add_option( '-q', '--quiet', action='store_const', const=0, dest='verbose_level', help='Turn off verbose output', ) option_parser.add_option( '--output-file', '-o', action='store', dest='output_file_name', help='Write the output to the named file instead of stdout', default=None, ) option_parser.add_option( '--all', action='store_false', dest='active_only', default=True, help='Include all calendars, not just active.', ) option_parser.add_option( '--input-directory', action='store', dest='input_directory', default=os.path.expanduser('~/Library/Calendars'), help='Directory containing calendars. Defaults to ~/Library/Calendars.', ) option_parser.add_option( '--format', '-f', action='store', dest='format', type='choice', choices=FORMATTER_FACTORIES.keys(), default='org', help='Output format. One of %s. Defaults to "diary".' % FORMATTER_FACTORIES.keys(), ) option_parser.add_option( '--opt', '--formatter-option', action='callback', type='string', callback=remember_formatter_option, help='Formatter-specific option name=value', ) option_parser.add_option( '--help', action='callback', callback=show_verbose_help, help='Verbose help', ) options, calendar_titles = option_parser.parse_args(args) log_level = VERBOSE_LEVELS.get(options.verbose_level, logging.DEBUG) logging.basicConfig(level=log_level, format='%(message)s') config = ConfigParser() config.read([options.config_filename]) # Compute the date range for items to be included in our output. start_date = tz.normalize_to_utc( datetime.datetime.combine( datetime.date.today() - datetime.timedelta(options.days_ago), datetime.time.min, )) end_date = tz.normalize_to_utc( datetime.datetime.combine( datetime.date.today() + datetime.timedelta(options.days_ahead + 1), datetime.time.min, )) logging.info('Starting %d days ago at %s', options.days_ago, start_date.astimezone(tz.local)) logging.info('Ending %d days from now at %s', options.days_ahead, end_date.astimezone(tz.local)) if options.output_file_name: logging.info('Writing to %s', options.output_file_name) # Load the calendars if calendar_titles: calendar_generator = calendars.get_by_titles( path=options.input_directory, titles=calendar_titles) else: calendar_generator = calendars.discover( path=options.input_directory, active_only=options.active_only) # Process the calendar data output = codecs.getwriter('utf-8')(sys.stdout) if options.output_file_name: output = codecs.open(options.output_file_name, 'wt', 'UTF-8') try: formatter = FORMATTER_FACTORIES[options.format](output, config, options) for calendar in calendar_generator: logging.info('Processing: %s', calendar.title) formatter.start_calendar(calendar) for event in filter.unique( filter.by_date_range( calendar.get_events(), start_date, end_date, )): logging.info(' %s', event.summary.value) formatter.add_event(event) formatter.end_calendar(calendar) finally: formatter.close() if output != sys.stdout: output.close() return