Exemple #1
0
def ics_from_list(events, tzs, random_uid=False):
    """convert an iterable of icalendar.Events to an icalendar.Calendar

    :params events: list of events all with the same uid
    :type events: list(icalendar.cal.Event)
    :param random_uid: assign random uids to all events
    :type random_uid: bool
    :param tzs: collection of timezones
    :type tzs: dict(icalendar.cal.Vtimzone
    """
    calendar = icalendar.Calendar()
    calendar.add('version', '2.0')
    calendar.add('prodid', '-//CALENDARSERVER.ORG//NONSGML Version 1//EN')

    if random_uid:
        new_uid = generate_random_uid()

    needed_tz, missing_tz = set(), set()
    for sub_event in events:
        if random_uid:
            sub_event['UID'] = new_uid
        # icalendar round-trip converts `TZID=a b` to `TZID="a b"` investigate, file bug XXX
        for prop in ['DTSTART', 'DTEND', 'DUE', 'EXDATE', 'RDATE', 'RECURRENCE-ID', 'DUE']:
            if isinstance(sub_event.get(prop), list):
                items = sub_event.get(prop)
            else:
                items = [sub_event.get(prop)]

            for item in items:
                if not (hasattr(item, 'dt') or hasattr(item, 'dts')):
                    continue
                # if prop is a list, all items have the same parameters
                datetime_ = item.dts[0].dt if hasattr(item, 'dts') else item.dt

                if not hasattr(datetime_, 'tzinfo'):
                    continue

                # check for datetimes' timezones which are not understood by
                # icalendar
                if datetime_.tzinfo is None and 'TZID' in item.params and \
                        item.params['TZID'] not in missing_tz:
                    logger.warning(
                        'Cannot find timezone `{}` in .ics file, using default timezone. '
                        'This can lead to erroneous time shifts'.format(item.params['TZID'])
                    )
                    missing_tz.add(item.params['TZID'])
                elif datetime_.tzinfo != pytz.UTC:
                    needed_tz.add(datetime_.tzinfo)

    for tzid in needed_tz:
        if str(tzid) in tzs:
            calendar.add_component(tzs[str(tzid)])
        else:
            logger.warning(
                'Cannot find timezone `{}` in .ics file, this could be a bug, '
                'please report this issue at http://github.com/pimutils/khal/.'.format(tzid))
    for sub_event in events:
        calendar.add_component(sub_event)
    return calendar.to_ical().decode('utf-8')
Exemple #2
0
def ics_from_list(events, tzs, random_uid=False):
    """convert an iterable of icalendar.Events to an icalendar.Calendar

    :params events: list of events all with the same uid
    :type events: list(icalendar.cal.Event)
    :param random_uid: assign random uids to all events
    :type random_uid: bool
    :param tzs: collection of timezones
    :type tzs: dict(icalendar.cal.Vtimzone
    """
    calendar = icalendar.Calendar()
    calendar.add('version', '2.0')
    calendar.add('prodid', '-//CALENDARSERVER.ORG//NONSGML Version 1//EN')

    if random_uid:
        new_uid = generate_random_uid()

    needed_tz, missing_tz = set(), set()
    for sub_event in events:
        if random_uid:
            sub_event['UID'] = new_uid
        # icalendar round-trip converts `TZID=a b` to `TZID="a b"` investigate, file bug XXX
        for prop in ['DTSTART', 'DTEND', 'DUE', 'EXDATE', 'RDATE', 'RECURRENCE-ID', 'DUE']:
            if isinstance(sub_event.get(prop), list):
                items = sub_event.get(prop)
            else:
                items = [sub_event.get(prop)]

            for item in items:
                if not (hasattr(item, 'dt') or hasattr(item, 'dts')):
                    continue
                # if prop is a list, all items have the same parameters
                datetime_ = item.dts[0].dt if hasattr(item, 'dts') else item.dt

                if not hasattr(datetime_, 'tzinfo'):
                    continue

                # check for datetimes' timezones which are not understood by
                # icalendar
                if datetime_.tzinfo is None and 'TZID' in item.params and \
                        item.params['TZID'] not in missing_tz:
                    logger.warning(
                        'Cannot find timezone `{}` in .ics file, using default timezone. '
                        'This can lead to erroneous time shifts'.format(item.params['TZID'])
                    )
                    missing_tz.add(item.params['TZID'])
                elif datetime_.tzinfo != pytz.UTC:
                    needed_tz.add(datetime_.tzinfo)

    for tzid in needed_tz:
        if str(tzid) in tzs:
            calendar.add_component(tzs[str(tzid)])
        else:
            logger.warning(
                'Cannot find timezone `{}` in .ics file, this could be a bug, '
                'please report this issue at http://github.com/pimutils/khal/.'.format(tzid))
    for sub_event in events:
        calendar.add_component(sub_event)
    return calendar.to_ical().decode('utf-8')
Exemple #3
0
def import_event(vevent, collection, locale, batch, format=None, env=None):
    """import one event into collection, let user choose the collection

    :type vevent: list of vevents, which can be more than one VEVENT, i.e., the
        same UID, i.e., one "master" event and (optionally) 1+ RECURRENCE-ID events
    :type vevent: list(str)
    """
    # print all sub-events
    if not batch:
        for item in icalendar.Calendar.from_ical(vevent).walk():
            if item.name == 'VEVENT':
                event = Event.fromVEvents(
                    [item],
                    calendar=collection.default_calendar_name,
                    locale=locale)
                echo(event.format(format, datetime.now(), env=env))

    # get the calendar to insert into
    if batch or len(collection.writable_names) == 1:
        calendar_name = collection.writable_names[0]
    else:
        calendar_names = sorted(collection.writable_names)
        choices = ', '.join([
            '{}({})'.format(name, num)
            for num, name in enumerate(calendar_names)
        ])
        while True:
            value = prompt(
                "Which calendar do you want to import to? (unique prefixes are fine)\n"
                "{}".format(choices),
                default=collection.default_calendar_name,
            )
            try:
                calendar_name = calendar_names[int(value)]
                break
            except (ValueError, IndexError):
                matches = [
                    x for x in collection.writable_names if x.startswith(value)
                ]
                if len(matches) == 1:
                    calendar_name = matches[0]
                    break
            echo('invalid choice')

    if batch or confirm("Do you want to import this event into `{}`?".format(
            calendar_name)):
        try:
            collection.new(Item(vevent), collection=calendar_name)
        except DuplicateUid:
            if batch or confirm(
                    "An event with the same UID already exists. Do you want to update it?"
            ):
                collection.force_update(Item(vevent), collection=calendar_name)
            else:
                logger.warning("Not importing event with UID `{}`".format(
                    event.uid))
Exemple #4
0
def import_event(vevent, collection, locale, batch, format=None, env=None):
    """import one event into collection, let user choose the collection

    :type vevent: list of vevents, which can be more than one VEVENT, i.e., the
        same UID, i.e., one "master" event and (optionally) 1+ RECURRENCE-ID events
    :type vevent: list(str)
    """
    # print all sub-events
    if not batch:
        for item in icalendar.Calendar.from_ical(vevent).walk():
            if item.name == 'VEVENT':
                event = Event.fromVEvents(
                    [item], calendar=collection.default_calendar_name, locale=locale)
                echo(event.format(format, datetime.now(), env=env))

    # get the calendar to insert into
    if not collection.writable_names:
        raise ConfigurationError('No writable calendars found, aborting import.')
    if len(collection.writable_names) == 1:
        calendar_name = collection.writable_names[0]
    elif batch:
        calendar_name = collection.default_calendar_name
    else:
        calendar_names = sorted(collection.writable_names)
        choices = ', '.join(
            ['{}({})'.format(name, num) for num, name in enumerate(calendar_names)])
        while True:
            value = prompt(
                "Which calendar do you want to import to? (unique prefixes are fine)\n"
                "{}".format(choices),
                default=collection.default_calendar_name,
            )
            try:
                calendar_name = calendar_names[int(value)]
                break
            except (ValueError, IndexError):
                matches = [x for x in collection.writable_names if x.startswith(value)]
                if len(matches) == 1:
                    calendar_name = matches[0]
                    break
            echo('invalid choice')
    assert calendar_name in collection.writable_names

    if batch or confirm("Do you want to import this event into `{}`?".format(calendar_name)):
        try:
            collection.new(Item(vevent), collection=calendar_name)
        except DuplicateUid:
            if batch or confirm(
                    "An event with the same UID already exists. Do you want to update it?"):
                collection.force_update(Item(vevent), collection=calendar_name)
            else:
                logger.warning("Not importing event with UID `{}`".format(event.uid))
Exemple #5
0
    def parse_config(self, cfile):
        self._conf_parser = RawConfigParser()
        try:
            if not self._conf_parser.read(cfile):
                logger.error("Cannot read config file' {}'".format(cfile))
                return None
        except ConfigParserError as error:
            logger.error("Could not parse config file "
                         "'{}': {}".format(cfile, error))
            return None
        items = dict()
        failed = False
        for section in self._conf_parser.sections():
            parser = self._get_section_parser(section)
            if parser is None:
                logger.warning(
                    "Found unknown section '{}' in config file".format(section)
                )
                continue

            values = parser.parse(section)
            if values is None:
                failed = True
                continue
            if parser.is_collection():
                if parser.group not in items:
                    items[parser.group] = []
                items[parser.group].append(values)
            else:
                items[parser.group] = values

        failed = self.check_required(items) or failed
        self.warn_leftovers()
        self.dump(items)

        if failed:
            return None
        else:
            return Namespace(items)
Exemple #6
0
    def parse_config(self, cfile):
        self._conf_parser = RawConfigParser()
        try:
            if not self._conf_parser.read(cfile):
                logger.error("Cannot read config file' {}'".format(cfile))
                return None
        except ConfigParserError as error:
            logger.error("Could not parse config file "
                         "'{}': {}".format(cfile, error))
            return None
        items = dict()
        failed = False
        for section in self._conf_parser.sections():
            parser = self._get_section_parser(section)
            if parser is None:
                logger.warning(
                    "Found unknown section '{}' in config file".format(
                        section))
                continue

            values = parser.parse(section)
            if values is None:
                failed = True
                continue
            if parser.is_collection():
                if parser.group not in items:
                    items[parser.group] = []
                items[parser.group].append(values)
            else:
                items[parser.group] = values

        failed = self.check_required(items) or failed
        self.warn_leftovers()
        self.dump(items)

        if failed:
            return None
        else:
            return Namespace(items)