def __init__(self, *args, **kwargs): """Set keys to upper for initial dict. """ CaselessDict.__init__(self, *args, **kwargs) # set parameters here for properties that use non-default values self.subcomponents = [] # Components can be nested. self.is_broken = False # True if we ignored an exception while
def __init__(self, *args, **kwargs): """Set keys to upper for initial dict. """ CaselessDict.__init__(self, *args, **kwargs) # set parameters here for properties that use non-default values self.subcomponents = [] # Components can be nested. self.is_broken = False # True if we ignored an exception while
def __init__(self, *args, **kwargs): "Set keys to upper for initial dict" CaselessDict.__init__(self, *args, **kwargs) self['VEVENT'] = Event self['VTODO'] = Todo self['VJOURNAL'] = Journal self['VFREEBUSY'] = FreeBusy self['VTIMEZONE'] = Timezone self['VALARM'] = Alarm self['VCALENDAR'] = Calendar
def __init__(self, *args, **kwargs): "Set keys to upper for initial dict" CaselessDict.__init__(self, *args, **kwargs) self['VEVENT'] = Event self['VTODO'] = Todo self['VJOURNAL'] = Journal self['VFREEBUSY'] = FreeBusy self['VTIMEZONE'] = Timezone self['VALARM'] = Alarm self['VCALENDAR'] = Calendar
def __init__(self, *args, **kwargs): "Set keys to upper for initial dict" CaselessDict.__init__(self, *args, **kwargs) self["VEVENT"] = Event self["VTODO"] = Todo self["VJOURNAL"] = Journal self["VFREEBUSY"] = FreeBusy self["VTIMEZONE"] = Timezone self["VALARM"] = Alarm self["VCALENDAR"] = Calendar
class vWeekday(compat.unicode_type): """This returns an unquoted weekday abbrevation. """ week_days = CaselessDict({ "SU": 0, "MO": 1, "TU": 2, "WE": 3, "TH": 4, "FR": 5, "SA": 6, }) def __new__(cls, value, encoding=DEFAULT_ENCODING): value = to_unicode(value, encoding=encoding) self = super(vWeekday, cls).__new__(cls, value) match = WEEKDAY_RULE.match(self) if match is None: raise ValueError('Expected weekday abbrevation, got: %s' % self) match = match.groupdict() sign = match['signal'] weekday = match['weekday'] relative = match['relative'] if weekday not in vWeekday.week_days or sign not in '+-': raise ValueError('Expected weekday abbrevation, got: %s' % self) self.relative = relative and int(relative) or None self.params = Parameters() return self def to_ical(self): return self.encode(DEFAULT_ENCODING).upper() @classmethod def from_ical(cls, ical): try: return cls(ical.upper()) except: raise ValueError('Expected weekday abbrevation, got: %s' % ical)
class vFrequency(compat.unicode_type): """A simple class that catches illegal values. """ frequencies = CaselessDict({ "SECONDLY": "SECONDLY", "MINUTELY": "MINUTELY", "HOURLY": "HOURLY", "DAILY": "DAILY", "WEEKLY": "WEEKLY", "MONTHLY": "MONTHLY", "YEARLY": "YEARLY", }) def __new__(cls, value, encoding=DEFAULT_ENCODING): value = to_unicode(value, encoding=encoding) self = super(vFrequency, cls).__new__(cls, value) if not self in vFrequency.frequencies: raise ValueError('Expected frequency, got: %s' % self) self.params = Parameters() return self def to_ical(self): return self.encode(DEFAULT_ENCODING).upper() @classmethod def from_ical(cls, ical): try: return cls(ical.upper()) except: raise ValueError('Expected frequency, got: %s' % ical)
def __init__(self, *args, **kwargs): "Set keys to upper for initial dict" CaselessDict.__init__(self, *args, **kwargs) self['binary'] = vBinary self['boolean'] = vBoolean self['cal-address'] = vCalAddress self['date'] = vDDDTypes self['date-time'] = vDDDTypes self['duration'] = vDDDTypes self['float'] = vFloat self['integer'] = vInt self['period'] = vPeriod self['recur'] = vRecur self['text'] = vText self['time'] = vTime self['uri'] = vUri self['utc-offset'] = vUTCOffset self['geo'] = vGeo self['inline'] = vInline
def __init__(self, *args, **kwargs): "Set keys to upper for initial dict" CaselessDict.__init__(self, *args, **kwargs) self['binary'] = vBinary self['boolean'] = vBoolean self['cal-address'] = vCalAddress self['date'] = vDDDTypes self['date-time'] = vDDDTypes self['duration'] = vDDDTypes self['float'] = vFloat self['integer'] = vInt self['period'] = vPeriod self['recur'] = vRecur self['text'] = vText self['time'] = vTime self['uri'] = vUri self['utc-offset'] = vUTCOffset self['geo'] = vGeo self['inline'] = vInline
def __init__(self, *args, **kwargs): "Set keys to upper for initial dict" CaselessDict.__init__(self, *args, **kwargs) self.all_types = ( vBinary, vBoolean, vCalAddress, vDDDLists, vDDDTypes, vDate, vDatetime, vDuration, vFloat, vFrequency, vGeo, vInline, vInt, vPeriod, vRecur, vText, vTime, vUTCOffset, vUri, vWeekday ) self['binary'] = vBinary self['boolean'] = vBoolean self['cal-address'] = vCalAddress self['date'] = vDDDTypes self['date-time'] = vDDDTypes self['duration'] = vDDDTypes self['float'] = vFloat self['integer'] = vInt self['period'] = vPeriod self['recur'] = vRecur self['text'] = vText self['time'] = vTime self['uri'] = vUri self['utc-offset'] = vUTCOffset self['geo'] = vGeo self['inline'] = vInline self['date-time-list'] = vDDDLists
def __init__(self, *args, **kwargs): "Set keys to upper for initial dict" CaselessDict.__init__(self, *args, **kwargs) self["binary"] = vBinary self["boolean"] = vBoolean self["cal-address"] = vCalAddress self["date"] = vDDDTypes self["date-time"] = vDDDTypes self["duration"] = vDDDTypes self["float"] = vFloat self["integer"] = vInt self["period"] = vPeriod self["recur"] = vRecur self["text"] = vText self["time"] = vTime self["uri"] = vUri self["utc-offset"] = vUTCOffset self["geo"] = vGeo self["inline"] = vInline self["date-time-list"] = vDDDLists
def __init__(self, *args, **kwargs): "Set keys to upper for initial dict" CaselessDict.__init__(self, *args, **kwargs) self.all_types = (vBinary, vBoolean, vCalAddress, vDDDLists, vDDDTypes, vDate, vDatetime, vDuration, vFloat, vFrequency, vGeo, vInline, vInt, vPeriod, vRecur, vText, vTime, vUTCOffset, vUri, vWeekday) self['binary'] = vBinary self['boolean'] = vBoolean self['cal-address'] = vCalAddress self['date'] = vDDDTypes self['date-time'] = vDDDTypes self['duration'] = vDDDTypes self['float'] = vFloat self['integer'] = vInt self['period'] = vPeriod self['recur'] = vRecur self['text'] = vText self['time'] = vTime self['uri'] = vUri self['utc-offset'] = vUTCOffset self['geo'] = vGeo self['inline'] = vInline self['date-time-list'] = vDDDLists
class vFrequency(str): """ A simple class that catches illegal values. >>> f = vFrequency('bad test') Traceback (most recent call last): ... ValueError: Expected frequency, got: BAD TEST >>> vFrequency('daily').ical() 'DAILY' >>> vFrequency('daily').from_ical('MONTHLY') 'MONTHLY' """ frequencies = CaselessDict({ "SECONDLY": "SECONDLY", "MINUTELY": "MINUTELY", "HOURLY": "HOURLY", "DAILY": "DAILY", "WEEKLY": "WEEKLY", "MONTHLY": "MONTHLY", "YEARLY": "YEARLY", }) def __new__(cls, *args, **kwargs): self = super(vFrequency, cls).__new__(cls, *args, **kwargs) if not self in vFrequency.frequencies: raise ValueError, 'Expected frequency, got: %s' % self self.params = Parameters() return self def ical(self): return self.upper() def from_ical(ical): "Parses the data format from ical text format" try: return vFrequency(ical.upper()) except: raise ValueError, 'Expected weekday abbrevation, got: %s' % ical from_ical = staticmethod(from_ical) def __str__(self): return self.ical()
class vBoolean(int): """Returns specific string according to state. """ BOOL_MAP = CaselessDict(true=True, false=False) def __new__(cls, *args, **kwargs): self = super(vBoolean, cls).__new__(cls, *args, **kwargs) self.params = Parameters() return self def to_ical(self): if self: return b'TRUE' return b'FALSE' @classmethod def from_ical(cls, ical): try: return cls.BOOL_MAP[ical] except: raise ValueError("Expected 'TRUE' or 'FALSE'. Got %s" % ical)
class vBoolean(int): """ Returns specific string according to state >>> bin = vBoolean(True) >>> bin.ical() 'TRUE' >>> bin = vBoolean(0) >>> bin.ical() 'FALSE' The roundtrip test >>> x = True >>> x == vBoolean.from_ical(vBoolean(x).ical()) True >>> vBoolean.from_ical('true') True """ def __new__(cls, *args, **kwargs): self = super(vBoolean, cls).__new__(cls, *args, **kwargs) self.params = Parameters() return self def ical(self): if self: return 'TRUE' return 'FALSE' bool_map = CaselessDict(true=True, false=False) def from_ical(ical): "Parses the data format from ical text format" try: return vBoolean.bool_map[ical] except: raise ValueError, "Expected 'TRUE' or 'FALSE'. Got %s" % ical from_ical = staticmethod(from_ical) def __str__(self): return self.ical()
class vRecur(CaselessDict): """ Let's see how close we can get to one from the rfc: FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;BYMINUTE=30 >>> r = dict(freq='yearly', interval=2) >>> r['bymonth'] = 1 >>> r['byday'] = 'su' >>> r['byhour'] = [8,9] >>> r['byminute'] = 30 >>> r = vRecur(r) >>> r.ical() 'BYHOUR=8,9;BYDAY=SU;BYMINUTE=30;BYMONTH=1;FREQ=YEARLY;INTERVAL=2' >>> r = vRecur(FREQ='yearly', INTERVAL=2) >>> r['BYMONTH'] = 1 >>> r['BYDAY'] = 'su' >>> r['BYHOUR'] = [8,9] >>> r['BYMINUTE'] = 30 >>> r.ical() 'BYDAY=SU;BYMINUTE=30;BYMONTH=1;INTERVAL=2;FREQ=YEARLY;BYHOUR=8,9' >>> r = vRecur(freq='DAILY', count=10) >>> r['bysecond'] = [0, 15, 30, 45] >>> r.ical() 'COUNT=10;FREQ=DAILY;BYSECOND=0,15,30,45' >>> r = vRecur(freq='DAILY', until=datetime(2005,1,1,12,0,0)) >>> r.ical() 'FREQ=DAILY;UNTIL=20050101T120000' How do we fare with regards to parsing? >>> r = vRecur.from_ical('FREQ=DAILY;INTERVAL=2;COUNT=10') >>> r {'COUNT': [10], 'FREQ': ['DAILY'], 'INTERVAL': [2]} >>> vRecur(r).ical() 'COUNT=10;FREQ=DAILY;INTERVAL=2' >>> r = vRecur.from_ical('FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=-SU;BYHOUR=8,9;BYMINUTE=30') >>> r {'BYHOUR': [8, 9], 'BYDAY': ['-SU'], 'BYMINUTE': [30], 'BYMONTH': [1], 'FREQ': ['YEARLY'], 'INTERVAL': [2]} >>> vRecur(r).ical() 'BYDAY=-SU;BYMINUTE=30;INTERVAL=2;BYMONTH=1;FREQ=YEARLY;BYHOUR=8,9' Some examples from the spec >>> r = vRecur.from_ical('FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1') >>> vRecur(r).ical() 'BYSETPOS=-1;FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR' >>> r = vRecur.from_ical('FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;BYMINUTE=30') >>> vRecur(r).ical() 'BYDAY=SU;BYMINUTE=30;INTERVAL=2;BYMONTH=1;FREQ=YEARLY;BYHOUR=8,9' and some errors >>> r = vRecur.from_ical('BYDAY=12') Traceback (most recent call last): ... ValueError: Error in recurrence rule: BYDAY=12 """ frequencies = [ "SECONDLY", "MINUTELY", "HOURLY", "DAILY", "WEEKLY", "MONTHLY", "YEARLY" ] types = CaselessDict({ 'COUNT': vInt, 'INTERVAL': vInt, 'BYSECOND': vInt, 'BYMINUTE': vInt, 'BYHOUR': vInt, 'BYMONTHDAY': vInt, 'BYYEARDAY': vInt, 'BYMONTH': vInt, 'UNTIL': vDDDTypes, 'BYSETPOS': vInt, 'WKST': vWeekday, 'BYDAY': vWeekday, 'FREQ': vFrequency }) def __init__(self, *args, **kwargs): CaselessDict.__init__(self, *args, **kwargs) self.params = Parameters() def ical(self): # SequenceTypes result = [] for key, vals in self.items(): typ = self.types[key] if not type(vals) in SequenceTypes: vals = [vals] vals = ','.join([typ(val).ical() for val in vals]) result.append('%s=%s' % (key, vals)) return ';'.join(result) def parse_type(key, values): # integers parser = vRecur.types.get(key, vText) return [parser.from_ical(v) for v in values.split(',')] parse_type = staticmethod(parse_type) def from_ical(ical): "Parses the data format from ical text format" try: recur = vRecur() for pairs in ical.split(';'): key, vals = pairs.split('=') recur[key] = vRecur.parse_type(key, vals) return dict(recur) except: raise ValueError, 'Error in recurrence rule: %s' % ical from_ical = staticmethod(from_ical) def __str__(self): return self.ical()
class TypesFactory(CaselessDict): """All Value types defined in rfc 2445 are registered in this factory class. The value and parameter names don't overlap. So one factory is enough for both kinds. """ def __init__(self, *args, **kwargs): "Set keys to upper for initial dict" CaselessDict.__init__(self, *args, **kwargs) self.all_types = (vBinary, vBoolean, vCalAddress, vDDDLists, vDDDTypes, vDate, vDatetime, vDuration, vFloat, vFrequency, vGeo, vInline, vInt, vPeriod, vRecur, vText, vTime, vUTCOffset, vUri, vWeekday) self['binary'] = vBinary self['boolean'] = vBoolean self['cal-address'] = vCalAddress self['date'] = vDDDTypes self['date-time'] = vDDDTypes self['duration'] = vDDDTypes self['float'] = vFloat self['integer'] = vInt self['period'] = vPeriod self['recur'] = vRecur self['text'] = vText self['time'] = vTime self['uri'] = vUri self['utc-offset'] = vUTCOffset self['geo'] = vGeo self['inline'] = vInline self['date-time-list'] = vDDDLists ################################################# # Property types # These are the default types types_map = CaselessDict({ #################################### # Property value types # Calendar Properties 'calscale': 'text', 'method': 'text', 'prodid': 'text', 'version': 'text', # Descriptive Component Properties 'attach': 'uri', 'categories': 'text', 'class': 'text', 'comment': 'text', 'description': 'text', 'geo': 'geo', 'location': 'text', 'percent-complete': 'integer', 'priority': 'integer', 'resources': 'text', 'status': 'text', 'summary': 'text', # Date and Time Component Properties 'completed': 'date-time', 'dtend': 'date-time', 'due': 'date-time', 'dtstart': 'date-time', 'duration': 'duration', 'freebusy': 'period', 'transp': 'text', # Time Zone Component Properties 'tzid': 'text', 'tzname': 'text', 'tzoffsetfrom': 'utc-offset', 'tzoffsetto': 'utc-offset', 'tzurl': 'uri', # Relationship Component Properties 'attendee': 'cal-address', 'contact': 'text', 'organizer': 'cal-address', 'recurrence-id': 'date-time', 'related-to': 'text', 'url': 'uri', 'uid': 'text', # Recurrence Component Properties 'exdate': 'date-time-list', 'exrule': 'recur', 'rdate': 'date-time-list', 'rrule': 'recur', # Alarm Component Properties 'action': 'text', 'repeat': 'integer', 'trigger': 'duration', # Change Management Component Properties 'created': 'date-time', 'dtstamp': 'date-time', 'last-modified': 'date-time', 'sequence': 'integer', # Miscellaneous Component Properties 'request-status': 'text', #################################### # parameter types (luckily there is no name overlap) 'altrep': 'uri', 'cn': 'text', 'cutype': 'text', 'delegated-from': 'cal-address', 'delegated-to': 'cal-address', 'dir': 'uri', 'encoding': 'text', 'fmttype': 'text', 'fbtype': 'text', 'language': 'text', 'member': 'cal-address', 'partstat': 'text', 'range': 'text', 'related': 'text', 'reltype': 'text', 'role': 'text', 'rsvp': 'boolean', 'sent-by': 'cal-address', 'tzid': 'text', 'value': 'text', }) def for_property(self, name): """Returns a the default type for a property or parameter """ return self[self.types_map.get(name, 'text')] def to_ical(self, name, value): """Encodes a named value from a primitive python type to an icalendar encoded string. """ type_class = self.for_property(name) return type_class(value).to_ical() def from_ical(self, name, value): """Decodes a named property or parameter value from an icalendar encoded string to a primitive python type. """ type_class = self.for_property(name) decoded = type_class.from_ical(value) return decoded
def __init__(self, *args, **kwargs): CaselessDict.__init__(self, *args, **kwargs) self.params = Parameters()
class vRecur(CaselessDict): """Recurrence definition. """ frequencies = [ "SECONDLY", "MINUTELY", "HOURLY", "DAILY", "WEEKLY", "MONTHLY", "YEARLY" ] # Mac iCal ignores RRULEs where FREQ is not the first rule part. # Sorts parts according to the order listed in RFC 5545, section 3.3.10. canonical_order = ("FREQ", "UNTIL", "COUNT", "INTERVAL", "BYSECOND", "BYMINUTE", "BYHOUR", "BYDAY", "BYMONTHDAY", "BYYEARDAY", "BYWEEKNO", "BYMONTH", "BYSETPOS", "WKST") types = CaselessDict({ 'COUNT': vInt, 'INTERVAL': vInt, 'BYSECOND': vInt, 'BYMINUTE': vInt, 'BYHOUR': vInt, 'BYMONTHDAY': vInt, 'BYYEARDAY': vInt, 'BYMONTH': vInt, 'UNTIL': vDDDTypes, 'BYSETPOS': vInt, 'WKST': vWeekday, 'BYDAY': vWeekday, 'FREQ': vFrequency, }) def __init__(self, *args, **kwargs): CaselessDict.__init__(self, *args, **kwargs) self.params = Parameters() def to_ical(self): result = [] for key, vals in self.sorted_items(): typ = self.types[key] if not isinstance(vals, SEQUENCE_TYPES): vals = [vals] vals = b','.join(typ(val).to_ical() for val in vals) # CaselessDict keys are always unicode key = key.encode(DEFAULT_ENCODING) result.append(key + b'=' + vals) return b';'.join(result) @classmethod def parse_type(cls, key, values): # integers parser = cls.types.get(key, vText) return [parser.from_ical(v) for v in values.split(',')] @classmethod def from_ical(cls, ical): if isinstance(ical, cls): return ical try: recur = cls() for pairs in ical.split(';'): key, vals = pairs.split('=') recur[key] = cls.parse_type(key, vals) return dict(recur) except: raise ValueError('Error in recurrence rule: %s' % ical)
def item_to_ics(item): """ Convert an etm item to an ical object and return a tuple (Success, object) """ doc_id = item.doc_id if not doc_id: return False, None itemtype = item.get('itemtype') if not itemtype: return False, None element = item_types.get(itemtype) if not element: return False, None summary = item['summary'] element.add('uid', doc_id) if 'z' in item: # pytz is required to get the proper tzid into datetimes tz = pytz.timezone(item['z']) else: tz = None if 's' in item: dt = item['s'] dz = dt.replace(tzinfo=tz) tzinfo = dz.tzinfo dt = dz dd = dz.date() else: dt = None tzinfo = None # tzname = None if 'r' in item: # repeating rlst = item['r'] for r in rlst: ritem = {} for k in ical_rrule_keys: if k in r: if k == 'f': ritem[ical_item[k]] = freq_item[r[k]] elif k == 'w': if type(r[k]) == list: ritem[ical_item[k]] = [x.upper() for x in r[k]] else: ritem[ical_item[k]] = r[k].upper() elif k == 'u': uz = parse_str(r[k], item['z']).replace(tzinfo=tzinfo) ritem[ical_item[k]] = uz else: ritem[ical_item[k]] = r[k] citem = CaselessDict(ritem) element.add('rrule', citem) if '+' in item: for pd in item['+']: element.add('rdate', pd) if '-' in item: for md in item['-']: element.add('exdate', md) element.add('summary', summary) if 'p' in item: element.add('priority', item['p']) if 'l' in item: element.add('location', item['l']) if 't' in item: element.add('categories', item['t']) if 'd' in item: element.add('description', item['d']) if 'm' in item: element.add('comment', item['m']) if 'w' in item: element.add('organizer', item['w']) if 'i' in item: for x in item['i']: element.add('attendee', "MAILTO:{0}".format(x)) if item['itemtype'] in ['-', '+', '%']: done, due, following = getDoneAndTwo(item) if 's' in item: element.add('dtstart', dt) if done: finz = done.replace(tzinfo=tzinfo) fint = vDatetime(finz) element.add('completed', fint) if due: duez = due.replace(tzinfo=tzinfo) dued = vDate(duez) element.add('due', dued) elif item['itemtype'] == '^': element.add('dtstart', dd) elif dt: try: element.add('dtstart', dt) except: logger.exception('exception adding dtstart: {0}'.format(dt)) if item['itemtype'] == '*': if 'e' in item and item['e']: ez = dz + item['e'] else: ez = dz try: element.add('dtend', ez) except: logger.exception('exception adding dtend: {0}, {1}'.format(ez, tz)) elif item['itemtype'] == '~': if 'e' in item and item['e']: element.add('comment', timedelta2Str(item['e'])) return True, element
""" def __init__(self, *args, **kwargs): "Set keys to upper for initial dict" CaselessDict.__init__(self, *args, **kwargs) self['VEVENT'] = Event self['VTODO'] = Todo self['VJOURNAL'] = Journal self['VFREEBUSY'] = FreeBusy self['VTIMEZONE'] = Timezone self['VALARM'] = Alarm self['VCALENDAR'] = Calendar # These Properties have multiple property values inlined in one propertyline # seperated by comma. Use CaselessDict as simple caseless set. INLINE = CaselessDict([(cat, 1) for cat in ('CATEGORIES', 'RESOURCES', 'FREEBUSY')]) _marker = [] class Component(CaselessDict): """ Component is the base object for calendar, Event and the other components defined in RFC 2445. normally you will not use this class directy, but rather one of the subclasses. A component is like a dictionary with extra methods and attributes. >>> c = Component() >>> c.name = 'VCALENDAR' Every key defines a property. A property can consist of either a single
def __init__(self, *args, **kwargs): "Set keys to upper for initial dict" CaselessDict.__init__(self, *args, **kwargs) # set parameters here for properties that use non-default values self.subcomponents = [] # Components can be nested.
def __init__(self, *args, **kwargs): "Set keys to upper for initial dict" CaselessDict.__init__(self, *args, **kwargs) # set parameters here for properties that use non-default values self.subcomponents = [] # Components can be nested.
def __init__(self, *args, **kwargs): CaselessDict.__init__(self, *args, **kwargs) self.params = Parameters()
class TypesFactory(CaselessDict): """ All Value types defined in rfc 2445 are registered in this factory class. To get a type you can use it like this. >>> factory = TypesFactory() >>> datetime_parser = factory['date-time'] >>> dt = datetime_parser(datetime(2001, 1, 1)) >>> dt.ical() '20010101T000000' A typical use is when the parser tries to find a content type and use text as the default >>> value = '20050101T123000' >>> value_type = 'date-time' >>> typ = factory.get(value_type, 'text') >>> typ.from_ical(value) datetime.datetime(2005, 1, 1, 12, 30) It can also be used to directly encode property and parameter values >>> comment = factory.ical('comment', u'by Rasmussen, Max Møller') >>> str(comment) 'by Rasmussen\\\\, Max M\\xc3\\xb8ller' >>> factory.ical('priority', 1) '1' >>> factory.ical('cn', u'Rasmussen, Max Møller') 'Rasmussen\\\\, Max M\\xc3\\xb8ller' >>> factory.from_ical('cn', 'Rasmussen\\\\, Max M\\xc3\\xb8ller') u'Rasmussen, Max M\\xf8ller' The value and parameter names don't overlap. So one factory is enough for both kinds. """ def __init__(self, *args, **kwargs): "Set keys to upper for initial dict" CaselessDict.__init__(self, *args, **kwargs) self['binary'] = vBinary self['boolean'] = vBoolean self['cal-address'] = vCalAddress self['date'] = vDDDTypes self['date-time'] = vDDDTypes self['duration'] = vDDDTypes self['float'] = vFloat self['integer'] = vInt self['period'] = vPeriod self['recur'] = vRecur self['text'] = vText self['time'] = vTime self['uri'] = vUri self['utc-offset'] = vUTCOffset self['geo'] = vGeo self['inline'] = vInline self['date-time-list'] = vDDDLists ################################################# # Property types # These are the default types types_map = CaselessDict({ #################################### # Property valye types # Calendar Properties 'calscale': 'text', 'method': 'text', 'prodid': 'text', 'version': 'text', # Descriptive Component Properties 'attach': 'uri', 'categories': 'text', 'class': 'text', 'comment': 'text', 'description': 'text', 'geo': 'geo', 'location': 'text', 'percent-complete': 'integer', 'priority': 'integer', 'resources': 'text', 'status': 'text', 'summary': 'text', # Date and Time Component Properties 'completed': 'date-time', 'dtend': 'date-time', 'due': 'date-time', 'dtstart': 'date-time', 'duration': 'duration', 'freebusy': 'period', 'transp': 'text', # Time Zone Component Properties 'tzid': 'text', 'tzname': 'text', 'tzoffsetfrom': 'utc-offset', 'tzoffsetto': 'utc-offset', 'tzurl': 'uri', # Relationship Component Properties 'attendee': 'cal-address', 'contact': 'text', 'organizer': 'cal-address', 'recurrence-id': 'date-time', 'related-to': 'text', 'url': 'uri', 'uid': 'text', # Recurrence Component Properties 'exdate': 'date-time-list', 'exrule': 'recur', 'rdate': 'date-time-list', 'rrule': 'recur', # Alarm Component Properties 'action': 'text', 'repeat': 'integer', 'trigger': 'duration', # Change Management Component Properties 'created': 'date-time', 'dtstamp': 'date-time', 'last-modified': 'date-time', 'sequence': 'integer', # Miscellaneous Component Properties 'request-status': 'text', #################################### # parameter types (luckilly there is no name overlap) 'altrep': 'uri', 'cn': 'text', 'cutype': 'text', 'delegated-from': 'cal-address', 'delegated-to': 'cal-address', 'dir': 'uri', 'encoding': 'text', 'fmttype': 'text', 'fbtype': 'text', 'language': 'text', 'member': 'cal-address', 'partstat': 'text', 'range': 'text', 'related': 'text', 'reltype': 'text', 'role': 'text', 'rsvp': 'boolean', 'sent-by': 'cal-address', 'tzid': 'text', 'value': 'text', }) def for_property(self, name): "Returns a the default type for a property or parameter" return self[self.types_map.get(name, 'text')] def ical(self, name, value): """ Encodes a named value from a primitive python type to an icalendar encoded string. """ type_class = self.for_property(name) return type_class(value).ical() def from_ical(self, name, value): """ Decodes a named property or parameter value from an icalendar encoded string to a primitive python type. """ type_class = self.for_property(name) decoded = type_class.from_ical(str(value)) return decoded
class vWeekday(str): """ This returns an unquoted weekday abbrevation >>> a = vWeekday('mo') >>> a.ical() 'MO' >>> a = vWeekday('erwer') Traceback (most recent call last): ... ValueError: Expected weekday abbrevation, got: ERWER >>> vWeekday.from_ical('mo') 'MO' >>> vWeekday.from_ical('+3mo') '+3MO' >>> vWeekday.from_ical('Saturday') Traceback (most recent call last): ... ValueError: Expected weekday abbrevation, got: Saturday >>> a = vWeekday('+mo') >>> a.ical() '+MO' >>> a = vWeekday('+3mo') >>> a.ical() '+3MO' >>> a = vWeekday('-tu') >>> a.ical() '-TU' """ week_days = CaselessDict({ "SU": 0, "MO": 1, "TU": 2, "WE": 3, "TH": 4, "FR": 5, "SA": 6 }) def __init__(self, *args, **kwargs): match = WEEKDAY_RULE.match(self) if match is None: raise ValueError, 'Expected weekday abbrevation, got: %s' % self match = match.groupdict() sign = match['signal'] weekday = match['weekday'] relative = match['relative'] if not weekday in vWeekday.week_days or sign not in '+-': raise ValueError, 'Expected weekday abbrevation, got: %s' % self self.relative = relative and int(relative) or None self.params = Parameters() def ical(self): return self.upper() def from_ical(ical): "Parses the data format from ical text format" try: return vWeekday(ical.upper()) except: raise ValueError, 'Expected weekday abbrevation, got: %s' % ical from_ical = staticmethod(from_ical) def __str__(self): return self.ical()
self['VEVENT'] = Event self['VTODO'] = Todo self['VJOURNAL'] = Journal self['VFREEBUSY'] = FreeBusy self['VTIMEZONE'] = Timezone self['STANDARD'] = TimezoneStandard self['DAYLIGHT'] = TimezoneDaylight self['VALARM'] = Alarm self['VCALENDAR'] = Calendar # These Properties have multiple property values inlined in one propertyline # seperated by comma. Use CaselessDict as simple caseless set. INLINE = CaselessDict({ 'CATEGORIES': 1, 'RESOURCES': 1, 'FREEBUSY': 1, }) _marker = [] class Component(CaselessDict): """Component is the base object for calendar, Event and the other components defined in RFC 2445. normally you will not use this class directy, but rather one of the subclasses. """ name = None # should be defined in each component required = () # These properties are required singletons = () # These properties must only appear once