From 0d90c71a6a7960e6220958883885b8486eb2d43a Mon Sep 17 00:00:00 2001 From: JP Rosevear Date: Tue, 17 Apr 2001 17:23:25 +0000 Subject: Initial revision svn path=/trunk/; revision=9413 --- libical/src/python/Property.py | 839 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 839 insertions(+) create mode 100644 libical/src/python/Property.py (limited to 'libical/src/python/Property.py') diff --git a/libical/src/python/Property.py b/libical/src/python/Property.py new file mode 100644 index 0000000000..d63b265525 --- /dev/null +++ b/libical/src/python/Property.py @@ -0,0 +1,839 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +#====================================================================== +# FILE: Property.py +# CREATOR: eric +# +# DESCRIPTION: +# +# +# $Id$ +# $Locker$ +# +# (C) COPYRIGHT 2001, Eric Busboom +# (C) COPYRIGHT 2001, Patrick Lewis +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of either: +# +# The LGPL as published by the Free Software Foundation, version +# 2.1, available at: http://www.fsf.org/copyleft/lesser.html +# +# Or: +# +# The Mozilla Public License Version 1.0. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +#====================================================================== + +from LibicalWrap import * +from types import * +import regsub +import base64 +from string import index, upper + +#def icalerror_supress(arg): +# pass + +#def icalerror_restore(a,b): +# pass + +def error_type(): + error = icalerror_perror() + return error[:index(error,':')] + +class Property: + """ Represent any iCalendar Property. + + Usage: + Property(dict) + + Where: + dict is a dictionary with keys of 'name', 'value_type', and 'value'. + In addition, parameter:parameter value entries may be included. + """ + + class ConstructorFailedError(Exception): + "Failed to construct a property" + + class UpdateFailedError(Exception): + "Failed to update the value of a property" + + + def __init__(self, type = None, ref = None): + + + assert(ref == None or isinstance(ref,StringType)) + assert(type == None or isinstance(type,StringType)) + + self._ref = None + + if ref != None: + self._ref = ref + elif type != None: + kind = icalproperty_string_to_kind(type) + self._ref = icalproperty_new(kind) + + + if self._ref == None or self._ref == 'NULL': + raise Property.ConstructorFailedError("Failed to construct Property") + + self._deleted = 0; + + + # Initialize all of the required keys + + + def __del__(self): + + self._deleted = 1; + + if not self._deleted and \ + self.ref() and \ + icalproperty_get_parent(self.ref()) == 'NULL': + + icalproperty_free(self.ref()) + + def name(self,v=None): + """ Return the name of the property """ + str = icalproperty_as_ical_string(self._ref) + + idx = index(str, '\n') + + return str[:idx] + + def ref(self,v=None): + """ Return the internal reference to the libical icalproperty """ + if(v != None): + + if not self._deleted and self._ref and \ + icalproperty_get_parent(self._ref) == 'NULL': + + icalproperty_free(self._ref) + + self._ref = v + + return self._ref + + + def value(self,v=None, kind = None): + """ Return the RFC2445 representation of the value """ + + if(v != None): + + if kind != None: + # Get the default kind of value for this property + default_kind = icalvalue_kind_to_string(icalproperty_kind_to_value_kind(icalproperty_string_to_kind(self.name()))) + + if(kind != default_kind): + self.__setitem__('VALUE',kind) + vt = kind + elif self.__getitem__('VALUE'): + vt = self.__getitem__('VALUE') + else: + vt = 'NO' # Use the kind of the existing value + + + icalerror_clear_errno() + + #e1=icalerror_supress("MALFORMEDDATA") + icalproperty_set_value_from_string(self._ref,v,vt) + #icalerror_restore("MALFORMEDDATA",e1) + + if error_type() != "NO": + raise Property.UpdateFailedError(error_type()) + + s = icalproperty_get_value_as_string(self._ref) + assert(s == v) + + return icalproperty_get_value_as_string(self._ref) + + def parameters(self): + + d_string = icallangbind_property_eval_string(self._ref,":") + dict = eval(d_string) + + desc_keys = ('name', 'value', 'value_type', 'pid', 'ref', 'deleted' ) + + def foo(k,d=dict): + if d.has_key(k): del d[k] + + map( foo, desc_keys) + + return filter(lambda p, s=self: s[p] != None, dict.keys()) + + + def as_ical_string(self): + + return icalproperty_as_ical_string(self._ref) + + def __getitem__(self,key): + """ Return property values by name """ + key = upper(key) + + str = icalproperty_get_parameter_as_string(self._ref,key) + + if(str == 'NULL'): return None + + return str + + def __setitem__(self,key,value): + """ Set Property Values by Name """ + key = upper(key) + + icalproperty_set_parameter_from_string(self._ref,key,value) + + return self.__getitem__(key) + + def __str__(self): + + str = self.as_ical_string() + return regsub.gsub('\r?\n ?','',str) + + def __cmp__(self, other): + s_str = str(self) + o_str = str(other) + + return cmp(s_str,o_str) + + +class Time(Property): + """ Represent iCalendar DATE, TIME and DATE-TIME """ + def __init__(self, arg, name="DTSTART"): + """ + Create a new Time from a string or number of seconds past the + POSIX epoch + + Time("19970325T123000Z") Construct from an iCalendar string + Time(8349873494) Construct from seconds past POSIX epoch + + """ + e1=icalerror_supress("MALFORMEDDATA") + e2=icalerror_supress("BADARG") + + if isinstance(arg, DictType): + # Dictionary -- used for creating from Component + self.tt = icaltime_from_string(arg['value']) + Property.__init__(self, ref=arg['ref']) + else: + if isinstance(arg, StringType): + # Create from an iCal string + self.tt = icaltime_from_string(arg) + elif isinstance(arg, IntType) or \ + isinstance(arg, FloatType): + # Create from seconds past the POSIX epoch + self.tt = icaltime_from_timet(int(arg),0) + elif isinstance(arg, Time): + # Copy an instance + self.tt = arg.tt + else: + self.tt = icaltime_null_time() + + Property.__init__(self,type=name) + + icalerror_restore("MALFORMEDDATA",e1) + icalerror_restore("BADARG",e2) + + if icaltime_is_null_time(self.tt): + raise Property.ConstructorFailedError("Failed to construct a Time") + + try: + self._update_value() + except Property.UpdateFailedError: + raise Property.ConstructorFailedError("Failed to construct a Time") + + def _update_value(self): + self.tt = icaltime_normalize(self.tt) + self.value(icaltime_as_ical_string(self.tt),"DATE-TIME") + + def valid(self): + " Return true if this is a valid time " + return not icaltime_is_null_time(self.tt) + + def utc_seconds(self,v=None): + """ Return or set time in seconds past POSIX epoch""" + if (v!=None): + self.tt = icaltime_from_timet(v,0) + self._update_value() + + return icaltime_as_timet(self.tt) + + def is_utc(self,v=None): + """ Return or set boolean indicating if time is in UTC """ + if(v != None): + icaltimetype_is_utc_set(self.tt,v) + self._update_value() + return icaltimetype_is_utc_get(self.tt) + + def is_date(self,v=None): + """ Return or set boolean indicating if time is actually a date """ + if(v != None): + icaltimetype_is_date_set(self.tt,v) + self._update_value() + return icaltimetype_is_date_get(self.tt) + + def timezone(self,v=None): + """ Return or set the timezone string for this time """ + + if (v != None): + assert(isinstance(v,StringType) ) + self['TZID'] = v + return self['TZID'] + + def second(self,v=None): + """ Get or set the seconds component of this time """ + if(v != None): + icaltimetype_second_set(self.tt,v) + self._update_value() + return icaltimetype_second_get(self.tt) + + def minute(self,v=None): + """ Get or set the minute component of this time """ + if(v != None): + icaltimetype_minute_set(self.tt,v) + self._update_value() + return icaltimetype_minute_get(self.tt) + + def hour(self,v=None): + """ Get or set the hour component of this time """ + if(v != None): + icaltimetype_hour_set(self.tt,v) + self._update_value() + return icaltimetype_hour_get(self.tt) + + def day(self,v=None): + """ Get or set the month day component of this time """ + if(v != None): + icaltimetype_day_set(self.tt,v) + self._update_value() + return icaltimetype_day_get(self.tt) + + def month(self,v=None): + """ Get or set the month component of this time. January is month 1 """ + if(v != None): + icaltimetype_month_set(self.tt,v) + self._update_value() + return icaltimetype_month_get(self.tt) + + def year(self,v=None): + """ Get or set the year component of this time """ + if(v != None): + icaltimetype_year_set(self.tt,v) + self._update_value() + + return icaltimetype_year_get(self.tt) + + + + def __add__(self,o): + + other = Duration(o,"DURATION") + + if not other.valid(): + return Duration(0,"DURATION") + + seconds = self.utc_seconds() + other.seconds() + + new = Time(seconds,self.name()) + new.timezone(self.timezone()) + new.is_utc(self.is_utc()) + + return new + + def __radd_(self,o): + return self.__add__(o) + + + def __sub__(self,o): + + + if isinstance(o,Time): + # Subtract a time from this time and return a duration + seconds = self.utc_seconds() - other.utc_seconds() + return Duration(seconds) + elif isinstance(o,Duration): + # Subtract a duration from this time and return a time + other = Duration(o) + if(not other.valid()): + return Time() + + seconds = self.utc_seconds() - other.seconds() + return Time(seconds) + else: + raise TypeError, "subtraction with Time reqires Time or Duration" + +class Duration(Property): + """ + Represent a length of time, like 3 minutes, or 6 days, 20 seconds. + + + """ + + def __init__(self, arg, name="DURATION"): + """ + Create a new duration from an RFC2445 string or number of seconds. + Construct the duration from an iCalendar string or a number of seconds. + + Duration("P3DT2H34M45S") Construct from an iCalendar string + Duration(3660) Construct from seconds + """ + + self.dur = None + + e=icalerror_supress("MALFORMEDDATA") + + if isinstance(arg, DictType): + + self.dur = icaldurationtype_from_string(arg['value']) + Property.__init__(self,ref=arg['ref']) + else: + if isinstance(arg, StringType): + self.dur = icaldurationtype_from_string(arg) + elif isinstance(arg, IntType): + self.dur = icaldurationtype_from_int(arg) + elif isinstance(arg,Duration): + self.dur = arg.dur + else: + self.dur = icaldurationtype_null_duration() + + Property.__init__(self,type=name) + + icalerror_restore("MALFORMEDDATA",e) + + if self.dur == None or icaldurationtype_is_null_duration(self.dur): + raise Property.ConstructorFailedError("Failed to construct Duration from " +str(arg)) + + try: + self._update_value() + except Property.UpdateFailedError: + raise Property.ConstructorFailedError("Failed to construct Duration from " + str(arg)) + + def _update_value(self): + + self.value(icaldurationtype_as_ical_string(self.dur),"DURATION") + + def valid(self): + "Return true if this is a valid duration" + + return not icaldurationtype_is_null_duration(self.dur) + + def seconds(self,v=None): + """Return or set duration in seconds""" + if(v != None): + self.dur = icaldurationtype_from_int(v); + self.dict['value'] = icaltimedurationtype_as_ical_string(self.dur) + return icaldurationtype_as_int(self.dur) + + +class Period(Property): + """Represent a span of time""" + def __init__(self,arg,name='FREEBUSY'): + """ """ + + Property.__init__(self, type = name) + + self.pt=None + + #icalerror_clear_errno() + e1=icalerror_supress("MALFORMEDDATA") + e2=icalerror_supress("BADARG") + + if isinstance(arg, DictType): + + + es=icalerror_supress("MALFORMEDDATA") + self.pt = icalperiodtype_from_string(arg['value']) + icalerror_restore("MALFORMEDDATA",es) + + Property.__init__(self, ref=arg['ref']) + else: + if isinstance(arg, StringType): + + self.pt = icalperiodtype_from_string(arg) + + else: + self.pt = icalperiodtype_null_period() + + Property.__init__(self,type=name) + + icalerror_restore("MALFORMEDDATA",e1) + icalerror_restore("BADARG",e2) + + + if self.pt == None or icalperiodtype_is_null_period(self.pt): + raise Property.ConstructorFailedError("Failed to construct Period") + + + try: + self._update_value() + except Property.UpdateFailedError: + raise Property.ConstructorFailedError("Failed to construct Period") + + def _end_is_duration(self): + dur = icalperiodtype_duration_get(self.pt) + if not icaldurationtype_is_null_duration(dur): + return 1 + return 0 + + def _end_is_time(self): + end = icalperiodtype_end_get(self.pt) + if not icaltime_is_null_time(end): + return 1 + return 0 + + def _update_value(self): + + self.value(icalperiodtype_as_ical_string(self.pt),"PERIOD") + + + def valid(self): + "Return true if this is a valid period" + + return not icalperiodtype_is_null_period(self.dur) + + def start(self,v=None): + """ + Return or set start time of the period. The start time may be + expressed as an RFC2445 format string or an instance of Time. + The return value is an instance of Time + """ + + if(v != None): + if isinstance(t,Time): + t = v + elif isinstance(t,StringType) or isinstance(t,IntType): + t = Time(v,"DTSTART") + else: + raise TypeError + + icalperiodtype_start_set(self.pt,t.tt) + + self._update_value() + + + return Time(icaltime_as_timet(icalperiodtype_start_get(self.pt)), + "DTSTART") + + def end(self,v=None): + """ + Return or set end time of the period. The end time may be + expressed as an RFC2445 format string or an instance of Time. + The return value is an instance of Time. + + If the Period has a duration set, but not an end time, this + method will caluculate the end time from the duration. """ + + if(v != None): + + if isinstance(t,Time): + t = v + elif isinstance(t,StringType) or isinstance(t,IntType): + t = Time(v) + else: + raise TypeError + + if(self._end_is_duration()): + start = icaltime_as_timet(icalperiodtype_start_get(self.pt)) + dur = t.utc_seconds()-start; + icalperiodtype_duration_set(self.pt, + icaldurationtype_from_int(dur)) + else: + icalperiodtype_end_set(self.pt,t.tt) + + self._update_value() + + if(self._end_is_time()): + rt = Time(icaltime_as_timet(icalperiodtype_end_get(self.pt)), + 'DTEND') + rt.timezone(self.timezone()) + return rt + elif(self._end_is_duration()): + start = icaltime_as_timet(icalperiodtype_start_get(self.pt)) + dur = icaldurationtype_as_int(icalperiodtype_duration_get(self.pt)) + rt = Time(start+dur,'DTEND') + rt.timezone(self.timezone()) + return rt + else: + return Time({},'DTEND') + + + + def duration(self,v=None): + """ + Return or set the duration of the period. The duration may be + expressed as an RFC2445 format string or an instance of Duration. + The return value is an instance of Duration. + + If the period has an end time set, but not a duration, this + method will calculate the duration from the end time. """ + + if(v != None): + + if isinstance(t,Duration): + d = v + elif isinstance(t,StringType) or isinstance(t,IntType): + d = Duration(v) + else: + raise TypeError + + if(self._end_is_time()): + start = icaltime_as_timet(icalperiodtype_start_get(self.pt)) + end = start + d.seconds() + + icalperiodtype_end_set(self.pt,icaltime_from_timet(end,0)) + else: + icalperiodtype_duration_set(self.pt,d.dur) + + if(self._end_is_time()): + start =icaltime_as_timet(icalperiodtype_start_get(self.pt)) + end = icaltime_as_timet(icalperiodtype_end_get(self.pt)) + + print "End is time " + str(end-start) + + return Duration(end-start,"DURATION") + + elif(self._end_is_duration()): + dur = icaldurationtype_as_int( + icalperiodtype_duration_get(self.pt)) + + return Duration(dur,"DURATION") + else: + + + return Duration(0,"DURATION") + + + def timezone(self,v=None): + """ Return or set the timezone string for this time """ + if (v != None): + self['TZID'] = v + return self['TZID'] + +class Attendee(Property): + """Class for Attendee properties. + + Usage: + Attendee([dict]) + Attendee([address]) + + Where: + dict is an optional dictionary with keys of + 'value': CAL-ADDRESS string and any parameter: parameter_value entries. + 'name' and 'value_type' entries in dict are ignored and automatically set + with the appropriate values. + address is the CAL-ADDRESS (string) of the Attendee + """ + + def __init__(self, arg={}): + + assert(isinstance(arg,DictType)) + + ref = None + + if arg!={}: + ref = arg['ref'] + + Property.__init__(self,type='ATTENDEE',ref=ref) + + def _doParam(self, parameter, v): + if v!=None: + self[parameter]=v + return self[parameter] + + # Methods for accessing enumerated parameters + def cn(self, v=None): self._doParam('CN', v) + def cutype(self, v=None): self._doParam('CUTYPE', v) + def dir(self, v=None): self._doParam('DIR', v) + def delegated_from(self, v=None): self._doParam('DELEGATED-FROM', v) + def delegated_to(self, v=None): self._doParam('DELEGATED-TO', v) + def language(self, v=None): self._doParam('LANGUAGE', v) + def member(self, v=None): self._doParam('MEMBER', v) + def partstat(self, v=None): self._doParam('PARTSTAT', v) + def role(self, v=None): self._doParam('ROLE', v) + def rsvp(self, v=None): self._doParam('RSVP', v) + def sent_by(self, v=None): self._doParam('SENT-BY', v) + + +class Organizer(Property): + """Class for Organizer property. + """ + + def __init__(self, arg={}): + + assert(isinstance(arg, DictType)) + + ref = None + if arg != {}: + ref = arg['ref'] + Property.__init__(self, type='ORGANIZER', ref=ref) + +## param_t = ( 'CN', 'DIR', 'SENT-BY', 'LANGUAGE' ) +## for param in param_t: +## self[param] = None +## if value != None: +## self.value(value) + + + def _doParam(self, parameter, v): + if v!=None: + self[parameter]=v + return self[parameter] + + def name(self): + "Return the name of the property." + return Property.name(self) + + def value_type(self): + "Return the value type of the property." + return self._desc['value_type'] + + # Methods for accessing enumerated parameters + def cn(self, v=None): self._doParam('CN', v) + def dir(self, v=None): self._doParam('DIR', v) + def language(self, v=None): self._doParam('LANGUAGE', v) + def sent_by(self, v=None): self._doParam('SENT-BY', v) + +class Recurrence_Id(Time): + """Class for RECURRENCE-ID property. + + Usage: + Reccurence_Id(dict) # A normal property dictionary + Reccurence_Id("19960401") # An iCalendar string + Reccurence_Id(8349873494) # Seconds from epoch + + If the 'dict' constructor is used, 'name' and 'value_type' + entries in dict are ignored and automatically set with the appropriate + values. + """ + + def __init__(self, dict={}): + Time.__init__(self, dict) + Property.name(self, 'RECURRENCE-ID') + + def name(self): + return Property.name(self) + + def _doParam(self, parameter, v): + if v!=None: + self[parameter]=v + return self[parameter] + + # Enumerated parameters + def value_parameter(self, v=None): + """Sets or gets the VALUE parameter value. + + The value passed should be either "DATE-TIME" or "DATE". Setting this + parameter has no impact on the property's value_type. Doing something + like: + + rid=Recurrence_Id("19960401") # Sets value & makes value_type="DATE" + rid.value_parameter("DATE-TIME") # Sets the parameter VALUE=DATE-TIME + + Would be allowed (even though it is wrong), so pay attention. + Verifying the component will reveal the error. + """ + if v!=None and v!="DATE" and v!="DATE-TIME": + raise ValueError, "%s is an invalid VALUE parameter value" % str(v) + self._doParam("VALUE", v) + + def tzid(self, v=None): + "Sets or gets the TZID parameter value." + self._doParam("TZID", v) + + def range_parameter(self, v=None): # 'range' is a builtin function + "Sets or gets the RANGE parameter value." + if v!=None and v!="THISANDPRIOR" and v!= "THISANDFUTURE": + raise ValueError, "%s is an invalid RANGE parameter value" % str(v) + self._doParam("RANGE", v) + +class Attach(Property): + """A class representing an ATTACH property. + + Usage: + Attach(uriString [, parameter_dict]) + Attach(fileObj [, parameter_dict]) + """ + + def __init__(self, value=None, parameter_dict={}): + Property.__init__(self, parameter_dict) + Property.name(self, 'ATTACH') + self.value(value) + + def value(self, v=None): + "Returns or sets the value of the property." + if v != None: + if isinstance(v, StringType): # Is a URI + self._desc['value']=v + Property.value_type(self, 'URI') + else: + try: + tempStr = v.read() + except: + raise TypeError,"%s must be a URL string or file-ish type"\ + % str(v) + self._desc['value'] = base64.encodestring(tempStr) + Property.value_type(self, 'BINARY') + else: + return self._desc['value'] + + def name(self): + "Returns the name of the property." + return Property.name(self) + + def value_type(self): + return Property.value_type(self) + + def fmttype(self, v=None): + "Gets or sets the FMTYPE parameter." + if v!= None: + self['FMTTYPE']=v + else: + return self['FMTTYPE'] + +class RecurrenceSet: + """ + Represents a set of event occurrences. This + class controls a component's RRULE, EXRULE, RDATE and EXDATE + properties and can produce from them a set of occurrences. + """ + + def __init__(self): + pass + + def include(self, **params): + """ + Include a date or rule to the set. + + Use date= or pass in a + Time instance to include a date. Included dates will add an + RDATE property or will remove an EXDATE property of the same + date. + + Use rule= or pass in a string to include a rule. Included + rules with either add a RRULE property or remove an EXRULE + property. + + """ + pass + + def exclude(self, **params): + """ + Exclude date or rule to the set. + + Use date= or pass in a Time instance to exclude a + date. Excluded dates will add an EXDATE property or will remove + an RDATE property of the same date. + + Use rule= or pass in a string to exclude a rule. Excluded + rules with either add an EXRULE property or remove an RRULE + property. + + """ + pass + + def occurrences(self, count=None): + """ + Return 'count' occurrences as a tuple of Time instances. + """ + pass + + -- cgit v1.2.3