From 697761cc337aa77a47140c8df50ed84bc25e23f6 Mon Sep 17 00:00:00 2001 From: Hans Petter Date: Thu, 11 Sep 2003 22:04:44 +0000 Subject: Import new libical from mainline HEAD and make appropriate changes to Evolution. svn path=/trunk/; revision=22538 --- libical/src/python/.cvsignore | 9 +- libical/src/python/Attendee.py | 114 ++++++ libical/src/python/ChangeLog | 20 + libical/src/python/Component.py | 287 ++++++++++---- libical/src/python/DerivedProperties.py | 102 ++++- libical/src/python/Duration.py | 92 +++++ libical/src/python/Error.py | 10 + libical/src/python/Gauge.py | 88 +++++ libical/src/python/Libical.py | 17 +- libical/src/python/LibicalWrap.i | 231 +++++++---- libical/src/python/Makefile.am | 6 + libical/src/python/Period.py | 216 +++++++++++ libical/src/python/Property.py | 663 +++----------------------------- libical/src/python/Store.py | 55 +-- libical/src/python/Time.py | 215 +++++++++++ libical/src/python/littlefile.txt | 3 + libical/src/python/python-binding.txt | 16 +- libical/src/python/test.py | 294 ++++++++++++-- 18 files changed, 1600 insertions(+), 838 deletions(-) create mode 100644 libical/src/python/Attendee.py create mode 100644 libical/src/python/Duration.py create mode 100644 libical/src/python/Error.py create mode 100644 libical/src/python/Gauge.py create mode 100644 libical/src/python/Period.py create mode 100644 libical/src/python/Time.py create mode 100644 libical/src/python/littlefile.txt (limited to 'libical/src/python') diff --git a/libical/src/python/.cvsignore b/libical/src/python/.cvsignore index 3dda72986f..801247a12c 100644 --- a/libical/src/python/.cvsignore +++ b/libical/src/python/.cvsignore @@ -1,2 +1,9 @@ -Makefile.in Makefile +.deps +.libs +*.lo +*.la +*.pyc +LibicalWrap.c +LibicalWrap_wrap.doc +config.c diff --git a/libical/src/python/Attendee.py b/libical/src/python/Attendee.py new file mode 100644 index 0000000000..52001ba3e8 --- /dev/null +++ b/libical/src/python/Attendee.py @@ -0,0 +1,114 @@ +#!/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 Property import Property +from types import DictType, StringType, IntType + +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) diff --git a/libical/src/python/ChangeLog b/libical/src/python/ChangeLog index 7b16306ce5..ef096f4e3a 100644 --- a/libical/src/python/ChangeLog +++ b/libical/src/python/ChangeLog @@ -1,3 +1,23 @@ +2001-04-04 Eric Busboom + + * Component.py Added Calendar class. + + * Componeny.py Changed all component constructor so they cannot + take string arguments. Now, only NewComponent() can turn an iCal + string into a component. + + +2001-04-02 Eric Busboom + + * Component.py removed arguments from the Event constructor, since + I presume that the Component derived classes will always be + constructed with no arguments. + + * Property.py Split out Attendee, Organizer, Time, Duration and + Period into their own files. Moved remaining classes to Derived + Properties.pm + + 2001-03-13 Eric Busboom * Component.py Added Component.property() diff --git a/libical/src/python/Component.py b/libical/src/python/Component.py index f4399f6c20..41ef7419db 100644 --- a/libical/src/python/Component.py +++ b/libical/src/python/Component.py @@ -26,29 +26,39 @@ #====================================================================== from LibicalWrap import * -from types import * -from Property import * +from types import DictType, StringType, IntType +from Property import Property from Collection import * +from Attendee import Attendee, Organizer +from Time import Time +from Duration import Duration +from Period import Period +import string + +WrapperNULL = None class Component: - def __init__(self,str=None, component_kind="ANY", ref=None): + def __init__(self,ref=None,kind=None): - if ref != None: + if ref != None: self._ref = ref + elif kind != None: + self._ref = icalcomponent_new( + icalcomponent_string_to_kind("VCALENDAR")) + _kind = icalcomponent_string_to_kind(kind) + inner = icalcomponent_new(_kind) + + icalcomponent_add_component(self._ref,inner); + else: - self._ref = None - if str != None: - self._ref = icalparser_parse_string(str) - else: - kind = icalenum_string_to_component_kind(component_kind) - self._ref = icalcomponent_new(kind) - + raise "Could not construct component of kind" + kind + self.cached_props = {} + self.cached_comps = {} def __del__(self): - if self._ref != None and \ - icalcomponent_get_parent(self._ref) != None: + if self._ref != None and icalcomponent_get_parent(self._ref) != WrapperNULL: for k in self.cached_props.keys(): del self.cached_props[k] @@ -58,10 +68,19 @@ class Component: def _prop_from_ref(self,p): - d_string = icallangbind_property_eval_string(p,":") - d = eval(d_string) + if(p == None or p== WrapperNULL): + return None; + + d = {} + d['value'] = icalproperty_get_value_as_string(p) + d['name'] = icalproperty_get_name(p) + + propkind = icalproperty_string_to_kind(d['name']) + kind = icalproperty_kind_to_value_kind(propkind) + d['value_type'] = icalvalue_kind_to_string(kind) d['ref'] = p - + + if not self.cached_props.has_key(p): if d['value_type'] == 'DATE-TIME' or d['value_type'] == 'DATE': @@ -85,7 +104,7 @@ class Component: p = icallangbind_get_first_property(self._ref,type) - if p !='NULL': + if p !=WrapperNULL: self._prop_from_ref(p) prop = self.cached_props[p] return prop @@ -102,8 +121,9 @@ class Component: p = icallangbind_get_first_property(self._ref,type) - while p !='NULL': - self._prop_from_ref(p) + while p !=WrapperNULL and p != None: + + self._prop_from_ref(p) # Puts property in self.cached_props prop = self.cached_props[p] props.append(prop) p = icallangbind_get_next_property(self._ref,type) @@ -122,12 +142,12 @@ class Component: s = str(prop) prop_p = icalproperty_new_from_string(s) - if prop_p == 'NULL': + if prop_p == WrapperNULL: raise "Bad property string: " + s prop.ref(prop_p) - if icalproperty_get_parent(prop_p)=='NULL': + if icalproperty_get_parent(prop_p)==WrapperNULL: icalcomponent_add_property(self._ref, prop_p) elif icalproperty_get_parent(prop_p) != self._ref: raise "Property is already a child of another component" @@ -143,16 +163,48 @@ class Component: def components(self,type='ANY'): comps = [] + kind = icalcomponent_string_to_kind(type) + c = icalcomponent_get_first_component(self._ref,kind); + + while c != WrapperNULL and c != None: + + if not self.cached_comps.has_key(c): + + self.cached_comps[c] = Component(c) + + comp = self.cached_comps[c] + comps.append(comp) + c = icalcomponent_get_next_component(self._ref,kind); + return comps - def add_component(self, componentObj): - "Adds a child component." - pass + def inner_component(self): + inner = icalcomponent_get_inner(self._ref) + + if inner == WrapperNULL and inner != None: + return None - def remove_component(self, component): + return NewComponent(inner) + + def add_component(self, comp): + "Adds a child component." + + if not isinstance(comp,Component): + raise ValueError("Expected a Component") + + if icalcomponent_get_parent(comp._ref) != WrapperNULL: + raise "Failed to add child component. Child already has a parent"; + + icalcomponent_add_component(self._ref,comp._ref) + + def remove_component(self, comp): "Removes a child component" - pass + + if not isinstance(comp,Component): + raise ValueError("Expected a Component") + + icalcomponent_remove_component(self._ref,comp._ref) def as_ical_string(self): return self.__str__() @@ -161,32 +213,72 @@ class Component: return icalcomponent_as_ical_string(self._ref) + def ref(self): + """ Return the internal reference to the libical icalproperty """ + return self._ref + +def CloneComponent(c): + "Clones a string or C icalcomponent into the right component object." + + wasStr=0 # Were we passed a string or an icalcomponent? + + if isinstance(c, Component): + comp = icalparser_parse_string(c.as_ical_string()) + elif isinstance (c, StringType) and string.find(c,"icalcomponent") == -1: + comp = icalparser_parse_string(c) + else: + comp = c + + if comp == None or comp == WrapperNULL: + raise ValueError("Expected a libical reference or an iCal string") + + kind = icalcomponent_isa(comp) + kindStr = icalcomponent_kind_to_string(kind) + + if kindStr == 'VCALENDAR': + inner = icalcomponent_get_inner(comp) + kind = icalcomponent_isa(inner) + kindStr = icalcomponent_kind_to_string(kind) + + if kindStr == 'VEVENT': + newComp = Event(comp) + elif kindStr == 'VTODO': + newComp = Todo(comp) + elif kindStr == 'VJOURNAL': + newComp = Journal(comp) + else: + newComp = Component(comp) + + # I don't think I need to free the component created when passed a string, + # as it wasn't created with a _new function. + + return newComp -def NewComponent(comp): +def NewComponent(c): "Converts a string or C icalcomponent into the right component object." wasStr=0 # Were we passed a string or an icalcomponent? - if isinstance (comp, StringType): - compStr = comp - comp = icalparser_parse_string(comp) - wasStr=1 + if isinstance (c, StringType) and string.find(c,"icalcomponent") == -1: + comp = icalparser_parse_string(c) else: - compStr = icalcomponent_as_ical_string(comp) + comp = c + + if comp == None or comp == WrapperNULL: + raise ValueError("Expected a libical reference or an iCal string") kind = icalcomponent_isa(comp) - kindStr = icalenum_component_kind_to_string(kind) - # Do I need to free kind? (I think not). + kindStr = icalcomponent_kind_to_string(kind) if kindStr == 'VEVENT': - newComp = Event(compStr) + newComp = Event(comp) elif kindStr == 'VTODO': - newComp = Todo(compStr) + newComp = Todo(comp) elif kindStr == 'VJOURNAL': - newComp = Journal(compstr) + newComp = Journal(comp) else: - newComp = Component(compStr) + newComp = Component(comp) # I don't think I need to free the component created when passed a string, # as it wasn't created with a _new function. @@ -196,9 +288,16 @@ def NewComponent(comp): class GenericComponent(Component): - def __init__(self): - - # Component.__init__(self, str) # Call from subclasses + def __init__(self,ref=None,kind=None): + + if ref != None: + Component.__init__(self, ref=ref) # Call from subclasses + elif type != None: + Component.__init__(self, kind=kind) # Call from subclasses + else: + raise ValueError("Expected either a icalcomponent reference or a kind string") + + self._recurrence_set=None def _singular_property(self, name, value_type, value=None, @@ -208,14 +307,23 @@ class GenericComponent(Component): This is a constructor method for properties without a strictly defined object.""" - curr_properties = self.properties(name) + # Depending on the property name, this routine will either + # operate on the VCALENDAR container or on the inner VEVENT, + # VTODO, or VJOURNAL + + if name in ['METHOD','PRODID','CALSCALE','VERSION']: + comp = self + else: + comp = self.inner_component() + + curr_properties = comp.properties(name) # Get the value if value==None: if len(curr_properties) == 0: return None elif len(curr_properties) == 1: - return curr_properties[0].value() + return curr_properties[0] else: raise ValueError, "too many properties of type %s" % propType @@ -223,7 +331,7 @@ class GenericComponent(Component): else: # Check if value is in enumerated_values if enumerated_values: - value = upper(value) + value = string.upper(value) if value not in enumerated_values: raise ValueError, "%s is not one of %s" \ % (value, enumerated_values) @@ -235,6 +343,8 @@ class GenericComponent(Component): if property_obj == Time: p = Time(value, name) ## p.value_type(value_type) + elif property_obj == Duration: + p = Duration(value) else: p = property_obj() ## p.value_type(value_type) @@ -248,12 +358,15 @@ class GenericComponent(Component): p.value(value) if len(curr_properties) == 1: - self.remove_property(curr_properties[0]) + comp.remove_property(curr_properties[0]) elif len(curr_properties) > 1: raise ValueError, "too many properties of type %s" % propType - self.add_property(p) - + comp.add_property(p) + + # METHOD, PRODID, CALSCALE and VERSION are properties of the + # VCALENDAR, not the inner component + def method(self, v=None): "Sets or returns the value of the METHOD property." return self._singular_property("METHOD", "TEXT", v) @@ -266,10 +379,20 @@ class GenericComponent(Component): "Sets or returns the value of the CALSCALE property." return self._singular_property("CALSCALE", "TEXT", v) + def version(self, v=None): + "Sets or returns the value of the Version property." + return self._singular_property("VERSION", "TEXT", v) + + # The remaining properties are all in the inner component + + def clone(self): + "Returns a copy of the object." + return CloneComponent(self) + def class_prop(self, v=None): # Class is a reserved word "Sets or returns the value of the CLASS property." if v!=None: - v = upper(v) + v = string.upper(v) return self._singular_property('CLASS', 'TEXT', v) def created(self, v=None): @@ -313,10 +436,10 @@ class GenericComponent(Component): """Sets or returns the value of the LAST-MODIFIED property. Usage: - lastmodified(time_obj) # Set the value using a Time object - lastmodified('19970101T123000Z')# Set using an iCalendar string - lastmodified(982362522) # Set using seconds - lastmodified() # Return an iCalendar string + last_modified(time_obj) # Set the value using a Time object + last_modified('19970101T123000Z')# Set using an iCalendar string + last_modified(982362522) # Set using seconds + last_modified() # Return an iCalendar string """ return self._singular_property("LAST-MODIFIED", "DATE-TIME", v, Time) @@ -389,7 +512,7 @@ class GenericComponent(Component): """ if values!=None: for alarm in values: - self.addComponent(alarm) + self.add_component(alarm) else: return ComponentCollection(self, self.components('VALARM')) @@ -401,6 +524,8 @@ class GenericComponent(Component): property_obj=None): "Processes set/get for Properties that can have multiple instances." + comp = self.inner_component() + # Set value if values!=None: if not isinstance(values, TupleType) \ @@ -408,8 +533,8 @@ class GenericComponent(Component): raise TypeError, "%s is not a tuple or list." # Delete old properties - for p in self.properties(name): - self.remove_property(p) + for p in comp.properties(name): + comp.remove_property(p) for v in values: if property_obj: # Specialized properties @@ -419,16 +544,15 @@ class GenericComponent(Component): else: # Use existing object new_prop = v else: # Generic properties - new_prop= Property() - new_prop.name(name) + new_prop=Property(name) # new_prop.value_type(value_type) new_prop.value(v) - self.add_property(new_prop) + comp.add_property(new_prop) # Get value else: - return Collection(self, self.properties(name)) + return Collection(self, comp.properties(name)) def attachments(self, values=None): """Sets or returns a Collection of Attach properties. @@ -490,25 +614,26 @@ class GenericComponent(Component): return self._multiple_properties('CONTACT', 'TEXT', value) def related_tos(self, value=None): - "Sets or returns a Collectoin of RELATED-TO properties." + "Sets or returns a Collection of RELATED-TO properties." return self._multiple_properties('RELATED-TO', 'TEXT', value) + def x_properties(self, name, value=None): + "Sets or returns a Collection of X- properties." + return self._multiple_properties(name, 'TEXT', value) class Event(GenericComponent): "The iCalendar Event object." - def __init__(self, str=None): - Component.__init__(self, str, "VEVENT") - GenericComponent.__init__(self) + def __init__(self,ref=None): + if ref != None: + GenericComponent.__init__(self, ref=ref) + else: + GenericComponent.__init__(self, kind='VEVENT') def component_type(self): "Returns the type of component for the object." return "VEVENT" - def clone(self): - "Returns a copy of the object." - return Event(self.asIcalString()) - def dtend(self, v=None): """Sets or returns the value of the DTEND property. @@ -541,7 +666,7 @@ class Event(GenericComponent): """ if v != None: - dtend = self.properites('DTEND') + dtend = self.properties('DTEND') for d in dtend: self.remove_property(d) # Clear DTEND properties return self._singular_property("DURATION", "DURATION", v, Duration) @@ -601,14 +726,17 @@ class Event(GenericComponent): class Todo(GenericComponent): "The iCalendar TODO component." + def __init__(self,ref=None): + if ref != None: + GenericComponent.__init__(self, ref=ref) + else: + GenericComponent.__init__(self, kind='VTODO') + + def component_type(self): "Returns the type of component for the object." return "VTODO" - def clone(self): - "Returns a copy of the object." - return Todo(self.asIcalString()) - def completed(self, value=None): return self._singular_property('COMPLETED', 'DATE-TIME', value, Time) @@ -627,7 +755,7 @@ class Todo(GenericComponent): def status(self, value=None): if value!=None: - value=upper(value) + value=string.upper(value) ok_values = ('NEEDS-ACTION', 'COMPLETED', 'IN-PROCESS', 'CANCELLED') return self._singular_property('STATUS', 'TEXT', value, enumerated_values=ok_values) @@ -648,22 +776,23 @@ class Todo(GenericComponent): def resources(): pass - class Journal(GenericComponent): "The iCalendar JOURNAL component." + def __init__(self): + if ref != None: + GenericComponent.__init__(self, ref=ref) + else: + GenericComponent.__init__(self, kind='VJOURNAL') + def component_type(self): "Returns the type of component for the object." return "VJOURNAL" - def clone(self): - "Returns a copy of the object." - return Journal(self.asIcalString()) - def status(self, v=None): if v!=None: - v = upper(v) + v = string.upper(v) ok_values=('DRAFT', 'FINAL', 'CANCELLED') return self._singular_property('STATUS', 'TEXT', v, enumerated_values=ok_values) diff --git a/libical/src/python/DerivedProperties.py b/libical/src/python/DerivedProperties.py index b557a1f7d7..81248fe7b7 100644 --- a/libical/src/python/DerivedProperties.py +++ b/libical/src/python/DerivedProperties.py @@ -25,7 +25,10 @@ # the License at http://www.mozilla.org/MPL/ #====================================================================== -from Property import Time, Period, Duration +from Property import Property +from Time import Time +from Period import Period +from Duration import Duration def RDate(arg): @@ -57,3 +60,100 @@ def Trigger(arg): +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'] + diff --git a/libical/src/python/Duration.py b/libical/src/python/Duration.py new file mode 100644 index 0000000000..e6c642a8f7 --- /dev/null +++ b/libical/src/python/Duration.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +#====================================================================== +# FILE: Duration.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 Property import Property +from types import DictType, StringType, IntType + +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) diff --git a/libical/src/python/Error.py b/libical/src/python/Error.py new file mode 100644 index 0000000000..912553d14e --- /dev/null +++ b/libical/src/python/Error.py @@ -0,0 +1,10 @@ + + +class LibicalError(Exception): + "Libical Error" + + def __init__(self,str): + Exception.__init__(self,str) + + def __str__(self): + return Exception.__str__(self)+"\nLibical errno: "+icalerror_perror() diff --git a/libical/src/python/Gauge.py b/libical/src/python/Gauge.py new file mode 100644 index 0000000000..ec859055fb --- /dev/null +++ b/libical/src/python/Gauge.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +#====================================================================== +# FILE: Gauge.py +# CREATOR: mtearle +# +# 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 Error import LibicalError +from Component import Component + +class Gauge: + """ + Base class for gauge + """ + + class ConstructorFailedError(LibicalError): + "Failed to create a Guage " + + class CloneFailedError(LibicalError): + "Failed to clone a component given Gauge " + + class CompareFailedError(LibicalError): + "Failed to compare a component given Gauge " + + def __init__(self,ref=None,sql=None,expand=0): + if ref != None: + self._ref = ref + elif sql != None: + s = str(sql) + self._ref = icalgauge_new_from_sql(s,expand) + else: + Gauge.ConstructorFailedError("No SQL Specified") + + def __del__(self): + if self._ref != None: + icalgauge_free(self._ref) + self._ref = None + + def ref(self): + return self._ref + + def compare(self, comp): + if not isinstance(comp,Component): + raise Gauge.CompareFailedError("Argument is not a component") + + if comp.ref() == None: + raise Gauge.CompareFailedError("Argument is not a component") + + return icalgauge_compare(self._ref, comp.ref()) + + # Pending Implementation + #def as_sql_string(self): + # return self.__str__() + + #def __str__(self): + # return icalgauge_as_sql(self._ref) + + #def clone(self, comp): +# if not isinstance(comp,Component): +# raise Gauge.CloneFailedError("Argument is not a component") +# +# comp_ref = icalgauge_new_clone(self._ref, comp) +# +# if comp_ref == None: +# return None +# +# return Component(ref=comp_ref) diff --git a/libical/src/python/Libical.py b/libical/src/python/Libical.py index 78a0fff8ff..9cf5076520 100644 --- a/libical/src/python/Libical.py +++ b/libical/src/python/Libical.py @@ -25,15 +25,12 @@ # the License at http://www.mozilla.org/MPL/ #====================================================================== - -import LibicalWrap - from Component import Component, NewComponent, Event, Todo, Journal - -# Will eventually remove Time for real Property events -from Property import Property, Time, Duration, Period, Attendee, Organizer, \ - Recurrence_Id, Attach, RecurrenceSet - -from DerivedProperties import RDate, Trigger - +from Property import Property, RecurrenceSet, test_enum +from Time import Time +from Period import Period +from Duration import Duration +from Attendee import Attendee, Organizer +from DerivedProperties import RDate, Trigger,Recurrence_Id, Attach from Store import Store, FileStore +from Gauge import Gauge diff --git a/libical/src/python/LibicalWrap.i b/libical/src/python/LibicalWrap.i index c6b6740383..bf3e19c1d9 100644 --- a/libical/src/python/LibicalWrap.i +++ b/libical/src/python/LibicalWrap.i @@ -43,10 +43,15 @@ typedef void icalproperty; icalcomponent* icalparser_parse_string(char* str); +/* actually takes icalcomponent_kind */ icalcomponent* icalcomponent_new(int kind); icalcomponent* icalcomponent_new_clone(icalcomponent* component); icalcomponent* icalcomponent_new_from_string(char* str); +const char* icalcomponent_kind_to_string(int kind); +int icalcomponent_string_to_kind(const char* string); + + char* icalcomponent_as_ical_string(icalcomponent* component); void icalcomponent_free(icalcomponent* component); @@ -75,12 +80,19 @@ void icalcomponent_remove_property(icalcomponent* component, icalproperty* property); +void icalcomponent_add_component(icalcomponent* parent, + icalcomponent* child); + +void icalcomponent_remove_component(icalcomponent* parent, + icalcomponent* child); + +icalcomponent* icalcomponent_get_inner(icalcomponent* comp); + icalcomponent* icalcomponent_get_parent(icalcomponent* component); int icalcomponent_isa(icalcomponent* component); int icalrestriction_check(icalcomponent* comp); - /* actually takes icalproperty_kind */ icalproperty* icalproperty_new(int kind); @@ -90,44 +102,59 @@ char* icalproperty_as_ical_string(icalproperty *prop); void icalproperty_set_parameter_from_string(icalproperty* prop, const char* name, const char* value); -void icalproperty_set_value_from_string(icalproperty* prop,const char* value, const char * kind); - -const char* icalproperty_get_value_as_string(icalproperty* prop); const char* icalproperty_get_parameter_as_string(icalproperty* prop, const char* name); +void icalproperty_remove_parameter_by_name(icalproperty* prop, + const char *name); +void icalproperty_set_value_from_string(icalproperty* prop,const char* value, const char * kind); +const char* icalproperty_get_value_as_string(icalproperty* prop); icalcomponent* icalproperty_get_parent(icalproperty* property); +const char* icalproperty_kind_to_string(int kind); +int icalproperty_string_to_kind(const char* string); +int icalproperty_string_to_enum(const char* str); +int icalproperty_enum_belongs_to_property(int kind, int e); +int icalproperty_kind_to_value_kind(int kind); + +/* Deal with X properties */ + +void icalproperty_set_x_name(icalproperty* prop, const char* name); +const char* icalproperty_get_x_name(icalproperty* prop); + +/* Return the name of the property -- the type name converted to a + string, or the value of _get_x_name if the type is and X property */ +const char* icalproperty_get_name (const icalproperty* prop); + + int icalerror_supress(const char* error); void icalerror_restore(const char* error, int es); char* icalerror_perror(); void icalerror_clear_errno(void); -const char* icalproperty_kind_to_string(int kind); -int icalproperty_string_to_kind(const char* string); -int icalproperty_kind_to_value_kind(int kind); const char* icalvalue_kind_to_string(int kind); int icalvalue_string_to_kind(const char* str); +char* icalparameter_as_ical_string(icalparameter* parameter); + const char* icalparameter_kind_to_string(int kind); int icalparameter_string_to_kind(const char* string); -const char* icalenum_component_kind_to_string(int kind); -int icalenum_string_to_component_kind(const char* string); - int* icallangbind_new_array(int size); void icallangbind_free_array(int* array); int icallangbind_access_array(int* array, int index); +/* int icalrecur_expand_recurrence(char* rule, int start, + int count, int* array);*/ int icalrecur_expand_recurrence(char* rule, int start, - int count, int* array); + int count, time_t* array); -/* Iterate through properties and components using strings for the kind */ +/* Iterate through properties, components and parameters using strings for the kind */ icalproperty* icallangbind_get_first_property(icalcomponent *c, const char* prop); @@ -140,6 +167,10 @@ icalcomponent* icallangbind_get_first_component(icalcomponent *c, icalcomponent* icallangbind_get_next_component(icalcomponent *c, const char* comp); +icalparameter* icallangbind_get_first_parameter(icalproperty *prop); + +icalparameter* icallangbind_get_next_parameter(icalproperty *prop); + /* Return a string that can be evaluated in perl or python to generated a hash that holds the property's name, value and @@ -149,6 +180,8 @@ const char* icallangbind_property_eval_string(icalproperty* prop, char* sep); int icallangbind_string_to_open_flag(const char* str); +const char* icallangbind_quote_as_ical(const char* str); + /*********************************************************************** Time routines ***********************************************************************/ @@ -162,22 +195,20 @@ struct icaltimetype int hour; int minute; int second; - - int is_utc; /* 1-> time is in UTC timezone */ - - int is_date; /* 1 -> interpret this as date. */ - - const char* zone; /*Ptr to Olsen placename. Libical does not own mem*/ }; /* Convert seconds past UNIX epoch to a timetype*/ struct icaltimetype icaltime_from_timet(int v, int is_date); +/** Convert seconds past UNIX epoch to a timetype, using timezones. */ +struct icaltimetype icaltime_from_timet_with_zone(int tm, + int is_date, icaltimezone *zone); + /* Return the time as seconds past the UNIX epoch */ /* Normally, this returns a time_t, but SWIG tries to turn that type into a pointer */ -int icaltime_as_timet(struct icaltimetype); +int icaltime_as_timet(struct icaltimetype tt); /* Return a string represention of the time, in RFC2445 format. The string is owned by libical */ @@ -187,19 +218,6 @@ char* icaltime_as_ical_string(struct icaltimetype tt); struct icaltimetype icaltime_from_string(const char* str); /* Routines for handling timezones */ -/* Return the offset of the named zone as seconds. tt is a time - indicating the date for which you want the offset */ -int icaltime_utc_offset(struct icaltimetype tt, const char* tzid); - -/* convert tt, of timezone tzid, into a utc time. Does nothing if the - time is already UTC. */ -struct icaltimetype icaltime_as_utc(struct icaltimetype tt, - const char* tzid); - -/* convert tt, a time in UTC, into a time in timezone tzid */ -struct icaltimetype icaltime_as_zone(struct icaltimetype tt, - const char* tzid); - /* Return a null time, which indicates no time has been set. This time represent the beginning of the epoch */ struct icaltimetype icaltime_null_time(void); @@ -211,6 +229,22 @@ int icaltime_is_null_time(struct icaltimetype t); it, or setting one of the flags to an illegal value. */ int icaltime_is_valid_time(struct icaltimetype t); +/** @brief Return the timezone */ +const icaltimezone *icaltime_get_timezone(const struct icaltimetype t); + +/** @brief Return the tzid, or NULL for a floating time */ +char *icaltime_get_tzid(const struct icaltimetype t); + +/** @brief Set the timezone */ +struct icaltimetype icaltime_set_timezone(struct icaltimetype *t, + const icaltimezone *zone); + +/* Returns true if time is of DATE type, false if DATE-TIME */ +int icaltime_is_date(struct icaltimetype t); + +/* Returns true if time is relative to UTC zone */ +int icaltime_is_utc(struct icaltimetype t); + /* Reset all of the time components to be in their normal ranges. For instance, given a time with minutes=70, the minutes will be reduces to 10, and the hour incremented. This allows the caller to do @@ -231,9 +265,6 @@ short icaltime_day_of_week(struct icaltimetype t); given time is within. */ short icaltime_start_doy_of_week(struct icaltimetype t); -/* Return a string with the time represented in the same format as ctime(). THe string is owned by libical */ -char* icaltime_as_ctime(struct icaltimetype); - /* Return the week number for the week the given time is within */ short icaltime_week_number(struct icaltimetype t); @@ -246,6 +277,12 @@ int icaltime_compare_date_only(struct icaltimetype a, struct icaltimetype b); /* Return the number of days in the given month */ short icaltime_days_in_month(short month,short year); +/** convert tt, of timezone tzid, into a utc time. Does nothing if the + time is already UTC. */ +struct icaltimetype icaltime_convert_to_zone(struct icaltimetype tt, + icaltimezone *zone); + + /*********************************************************************** Duration Routines @@ -295,58 +332,122 @@ struct icalperiodtype icalperiodtype_null_period(); int icalperiodtype_is_null_period(struct icalperiodtype p); int icalperiodtype_is_valid_period(struct icalperiodtype p); +/*********************************************************************** + * timezone handling routines +***********************************************************************/ + +/** Returns a single builtin timezone, given its Olson city name. */ +icaltimezone* icaltimezone_get_builtin_timezone (const char *location); + +/** Returns the UTC timezone. */ +icaltimezone* icaltimezone_get_utc_timezone (void); /*********************************************************************** Storage Routines ***********************************************************************/ -icalfileset* icalfileset_new(const char* path); +/** + * @brief options for opening an icalfileset. + * + * These options should be passed to the icalset_new() function + */ + +struct icalfileset_options { + int flags; /**< flags for open() O_RDONLY, etc */ + mode_t mode; /**< file mode */ + icalcluster *cluster; /**< use this cluster to initialize data */ +}; + +icalset* icalfileset_new(const char* path); +icalset* icalfileset_new_reader(const char* path); +icalset* icalfileset_new_writer(const char* path); -/* Like _new, but takes open() flags for opening the file */ -icalfileset* icalfileset_new_open(const char* path, - int flags, int mode); +icalset* icalfileset_init(icalset *set, const char *dsn, void* options); -void icalfileset_free(icalfileset* cluster); +/* icalfileset* icalfileset_new_from_cluster(const char* path, icalcluster *cluster); */ -const char* icalfileset_path(icalfileset* cluster); +icalcluster* icalfileset_produce_icalcluster(const char *path); + +void icalfileset_free(icalset* cluster); + +const char* icalfileset_path(icalset* cluster); /* Mark the cluster as changed, so it will be written to disk when it is freed. Commit writes to disk immediately. */ -void icalfileset_mark(icalfileset* cluster); -icalerrorenum icalfileset_commit(icalfileset* cluster); +void icalfileset_mark(icalset* set); +icalerrorenum icalfileset_commit(icalset* set); -icalerrorenum icalfileset_add_component(icalfileset* cluster, +icalerrorenum icalfileset_add_component(icalset* set, icalcomponent* child); -icalerrorenum icalfileset_remove_component(icalfileset* cluster, +icalerrorenum icalfileset_remove_component(icalset* set, icalcomponent* child); -int icalfileset_count_components(icalfileset* cluster, - icalcomponent_kind kind); +int icalfileset_count_components(icalset* set, + int kind); -/* Restrict the component returned by icalfileset_first, _next to those - that pass the gauge. _clear removes the gauge */ -icalerrorenum icalfileset_select(icalfileset* store, icalgauge* gauge); -void icalfileset_clear(icalfileset* store); +/** + * Restrict the component returned by icalfileset_first, _next to those + * that pass the gauge. _clear removes the gauge + */ +icalerrorenum icalfileset_select(icalset* set, icalgauge* gauge); -/* Get and search for a component by uid */ -icalcomponent* icalfileset_fetch(icalfileset* cluster, const char* uid); -int icalfileset_has_uid(icalfileset* cluster, const char* uid); -icalcomponent* icalfileset_fetch_match(icalfileset* set, icalcomponent *c); +/** clear the gauge **/ +void icalfileset_clear(icalset* set); +/** Get and search for a component by uid **/ +icalcomponent* icalfileset_fetch(icalset* set, const char* uid); +int icalfileset_has_uid(icalset* set, const char* uid); +icalcomponent* icalfileset_fetch_match(icalset* set, icalcomponent *c); -/* Modify components according to the MODIFY method of CAP. Works on - the currently selected components. */ -icalerrorenum icalfileset_modify(icalfileset* store, icalcomponent *oldcomp, + +/** + * Modify components according to the MODIFY method of CAP. Works on the + * currently selected components. + */ +icalerrorenum icalfileset_modify(icalset* set, + icalcomponent *oldcomp, icalcomponent *newcomp); -/* Iterate through components. If a guage has been defined, these +/* Iterate through components. If a gauge has been defined, these will skip over components that do not pass the gauge */ -icalcomponent* icalfileset_get_current_component (icalfileset* cluster); -icalcomponent* icalfileset_get_first_component(icalfileset* cluster); -icalcomponent* icalfileset_get_next_component(icalfileset* cluster); -/* Return a reference to the internal component. You probably should - not be using this. */ +icalcomponent* icalfileset_get_current_component (icalset* cluster); +icalcomponent* icalfileset_get_first_component(icalset* cluster); +icalcomponent* icalfileset_get_next_component(icalset* cluster); + +/* External iterator for thread safety */ +icalsetiter icalfileset_begin_component(icalset* set, int kind, icalgauge* gauge); +icalcomponent * icalfilesetiter_to_next(icalset* set, icalsetiter *iter); +icalcomponent* icalfileset_form_a_matched_recurrence_component(icalsetiter* itr); + +/*********************************************************************** + Gauge Routines +***********************************************************************/ + +icalgauge* icalgauge_new_from_sql(char* sql, int expand); + +int icalgauge_get_expand(icalgauge* gauge); + +void icalgauge_free(icalgauge* gauge); + +/* Pending Implementation */ +/* char* icalgauge_as_sql(icalcomponent* gauge); */ + +void icalgauge_dump(icalgauge* gauge); + + +/** @brief Return true if comp matches the gauge. + * + * The component must be in + * cannonical form -- a VCALENDAR with one VEVENT, VTODO or VJOURNAL + * sub component + */ +int icalgauge_compare(icalgauge* g, icalcomponent* comp); + +/* Pending Implementation */ +/** Clone the component, but only return the properties + * specified in the gauge */ +/* icalcomponent* icalgauge_new_clone(icalgauge* g, icalcomponent* comp); */ + -icalcomponent* icalfileset_get_component(icalfileset* cluster); diff --git a/libical/src/python/Makefile.am b/libical/src/python/Makefile.am index 8eaf4a74cb..e221fa91c1 100644 --- a/libical/src/python/Makefile.am +++ b/libical/src/python/Makefile.am @@ -32,11 +32,17 @@ Libical.py \ LibicalWrap.i \ python-binding.txt \ test.py \ +Attendee.py \ Collection.py \ Component.py \ DerivedProperties.py \ +Duration.py \ +Error.py \ +Gauge.py \ +Period.py \ Property.py \ Store.py \ +Time.py \ ChangeLog diff --git a/libical/src/python/Period.py b/libical/src/python/Period.py new file mode 100644 index 0000000000..e8d9b3ed40 --- /dev/null +++ b/libical/src/python/Period.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +#====================================================================== +# FILE: Period.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 Property import Property +from types import DictType, StringType, IntType +from Time import Time +from Duration import Duration + +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'] diff --git a/libical/src/python/Property.py b/libical/src/python/Property.py index d63b265525..80c5864855 100644 --- a/libical/src/python/Property.py +++ b/libical/src/python/Property.py @@ -26,10 +26,10 @@ #====================================================================== from LibicalWrap import * -from types import * -import regsub +import re import base64 -from string import index, upper +from string import index, upper, split +from types import StringType #def icalerror_supress(arg): # pass @@ -41,6 +41,19 @@ def error_type(): error = icalerror_perror() return error[:index(error,':')] +def test_enum(prop,enum): + + kind = icalproperty_string_to_kind(prop) + e = icalproperty_string_to_enum(enum) + + t = icalproperty_enum_belongs_to_property(kind,e) + + if t == 1: + return 1 + + return None + + class Property: """ Represent any iCalendar Property. @@ -73,6 +86,8 @@ class Property: kind = icalproperty_string_to_kind(type) self._ref = icalproperty_new(kind) + if type.find("X-") == 0: + icalproperty_set_x_name(self._ref, type) if self._ref == None or self._ref == 'NULL': raise Property.ConstructorFailedError("Failed to construct Property") @@ -95,11 +110,7 @@ class Property: 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] + return icalproperty_get_name(self._ref) def ref(self,v=None): """ Return the internal reference to the libical icalproperty """ @@ -129,6 +140,7 @@ class Property: vt = kind elif self.__getitem__('VALUE'): vt = self.__getitem__('VALUE') + print "###########", self else: vt = 'NO' # Use the kind of the existing value @@ -136,42 +148,45 @@ class Property: icalerror_clear_errno() #e1=icalerror_supress("MALFORMEDDATA") - icalproperty_set_value_from_string(self._ref,v,vt) + if (self.name().find("X-") == 0) and type(v) is StringType: + v = icallangbind_quote_as_ical(v) + v = icallangbind_quote_as_ical(v) + + icalproperty_set_value_from_string(self._ref,str(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): + """ + Return a list of parameters + """ - d_string = icallangbind_property_eval_string(self._ref,":") - dict = eval(d_string) + params = [] - desc_keys = ('name', 'value', 'value_type', 'pid', 'ref', 'deleted' ) - - def foo(k,d=dict): - if d.has_key(k): del d[k] + p = icallangbind_get_first_parameter(self._ref) + + while p != None: + kv = split(icalparameter_as_ical_string(p),'=',2) + params.append(kv[0]) + p = icallangbind_get_next_parameter(self._ref) - map( foo, desc_keys) - - return filter(lambda p, s=self: s[p] != None, dict.keys()) + return params - def as_ical_string(self): - + "Return the property in iCalendar text format." 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 @@ -184,10 +199,17 @@ class Property: return self.__getitem__(key) + def __delitem__(self,key): + """ Remove Property Values by Name """ + key = upper(key) + + if self.__getitem__(key): + icalproperty_remove_parameter_by_name(self._ref,key) + def __str__(self): str = self.as_ical_string() - return regsub.gsub('\r?\n ?','',str) + return re.sub('\r?\n ?','',str) def __cmp__(self, other): s_str = str(self) @@ -196,599 +218,6 @@ class Property: 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 diff --git a/libical/src/python/Store.py b/libical/src/python/Store.py index 6d13bd5211..00b5b0c2d0 100644 --- a/libical/src/python/Store.py +++ b/libical/src/python/Store.py @@ -27,7 +27,8 @@ from LibicalWrap import * from Error import LibicalError -from Component import Component +from Component import Component, CloneComponent +from Gauge import Gauge class Store: """ @@ -88,20 +89,11 @@ class Store: class FileStore(Store): - def __init__(self, file,mode="r",flags=0664): - - _flags = icallangbind_string_to_open_flag(mode) - - - if _flags == -1: - raise Store.ConstructorFailedError("Illegal value for mode: "+mode) - + def __init__(self, file): e1=icalerror_supress("FILE") - self._ref = icalfileset_new_open(file,_flags,flags) + self._ref = icalfileset_new(file) icalerror_restore("FILE",e1) - print self._ref - if self._ref == None or self._ref == 'NULL': raise Store.ConstructorFailedError(file) @@ -121,33 +113,46 @@ class FileStore(Store): if not isinstance(comp,Component): raise Store.AddFailedError("Argument is not a component") - error = icalfileset_add_component(self._ref,comp) + error = icalfileset_add_component(self._ref,comp.ref()) def remove_component(self, comp): if not isinstance(comp,Component): raise Store.AddFailedError("Argument is not a component") - error = icalfileset_remove_component(self._ref,comp) - - + error = icalfileset_remove_component(self._ref,comp.ref()) def count_components(self, kind): - pass + _kind = icalcomponent_string_to_kind(kind) + + return icalfileset_count_components(self._ref, _kind) def select(self, gauge): - pass + error = icalfileset_select(self._ref, gauge.ref()) def clearSelect(self): - pass + icalfileset_clear(self._ref) def fetch(self, uid): - pass + comp_ref = icalfileset_fetch(self._ref, uid) + + if comp_ref == None: + return None + + return CloneComponent(comp_ref) def fetchMatch(self, comp): - pass + if not isinstance(comp,Component): + raise Store.AddFailedError("Argument is not a component") + + comp_ref = icalfileset_fetch_match(self._ref,comp.ref()) + + if comp_ref == None: + return None + + return CloneComponent(comp_ref) def modify(self, oldc, newc): - pass + pass def current_component(self): comp_ref = icalfileset_get_current_component(self._ref) @@ -155,7 +160,7 @@ class FileStore(Store): if comp_ref == None: return None - return Component(ref=comp_ref) + return CloneComponent(comp_ref) def first_component(self): comp_ref = icalfileset_get_first_component(self._ref) @@ -163,7 +168,7 @@ class FileStore(Store): if comp_ref == None: return None - return Component(ref=comp_ref) + return CloneComponent(comp_ref) def next_component(self): @@ -172,5 +177,5 @@ class FileStore(Store): if comp_ref == None: return None - return Component(ref=comp_ref) + return CloneComponent(comp_ref) diff --git a/libical/src/python/Time.py b/libical/src/python/Time.py new file mode 100644 index 0000000000..673607d7b4 --- /dev/null +++ b/libical/src/python/Time.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +#====================================================================== +# FILE: Time.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 Property import Property +from types import DictType, StringType, IntType, FloatType +from Duration import Duration + +class Time(Property): + """ Represent iCalendar DATE, TIME and DATE-TIME """ + def __init__(self, arg, name="DTSTART", zone=None): + """ + 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 + if zone: + self.tt = icaltime_from_timet_with_zone(int(arg),0,icaltimezone_get_builtin_timezone(zone)) + else: + self.tt = icaltime_from_timet_with_zone(int(arg),0,icaltimezone_get_utc_timezone()) + 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): + tz = icaltimezone_get_builtin_timezone(self.timezone()) + self.tt = icaltime_from_timet_with_zone(v,0,tz) + self._update_value() + + return icaltime_as_timet(self.tt) + + def is_utc(self): + """ Return a boolean indicating if time is in UTC """ + return icaltime_is_utc(self.tt) + + def is_date(self): + """ Return a boolean indicating if time is actually a date """ + return icaltime_is_date(self.tt) + + def timezone(self,v=None): + """ Return, set (if none) or alter the timezone for this time """ + + origtz = icaltime_get_tzid(self.tt) + + if (v != None): + assert(isinstance(v,StringType) ) + if (v == "UTC"): + tz = icaltimezone_get_utc_timezone() + del self['TZID'] + else: + tz = icaltimezone_get_builtin_timezone(v) + + if not origtz: + self.tt = icaltime_set_timezone(self.tt, tz) + else: + self.tt = icaltime_convert_to_zone(self.tt,tz) + + if (icaltime_get_tzid(self.tt) != "UTC"): + self['TZID'] = icaltime_get_tzid(self.tt) + + self._update_value() + return icaltime_get_tzid(self.tt) + + 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 __cmp__(self,other): + + if other == None: + return cmp(self.utc_seconds(),None) + + return cmp(self.utc_seconds(),other.utc_seconds()) + + + 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(),self.timezone()) + + 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" diff --git a/libical/src/python/littlefile.txt b/libical/src/python/littlefile.txt new file mode 100644 index 0000000000..cba0891115 --- /dev/null +++ b/libical/src/python/littlefile.txt @@ -0,0 +1,3 @@ +This is just a small file to test mime encoding. + +There isn't much here at all. diff --git a/libical/src/python/python-binding.txt b/libical/src/python/python-binding.txt index 7fc9e066bf..7f925e246f 100644 --- a/libical/src/python/python-binding.txt +++ b/libical/src/python/python-binding.txt @@ -149,7 +149,7 @@ it. that the specific interface has not implemented or cannot manage well. -4) Cller may create a new component, using a combination of the +4) Caller may create a new component, using a combination of the general and specific interfaces. The caller may send the message to another user via mail, or may submit it to the user's CAP server with the CAPStore class. @@ -159,7 +159,7 @@ Following are the methods in each of the classes. Component Construct from string - output string in ical form + Output string in ical form Get a Property by type Get a set of Properties by type @@ -177,7 +177,7 @@ Component Event, JournalEntry, Todo Construct from string - output string in ical form + Output string in ical form There are get/set accessors for every property. These are listed in a later section @@ -187,7 +187,7 @@ FreeBusy Construct from string Construct from arguments - output string in ical form + Output string in ical form Accessors for the following properties. See below for return types for these properties: @@ -211,7 +211,7 @@ TimezonePhase Construct from string Construct from arguments - output string in ical form + Output string in ical form Accessors for the following properties. See below for return types for these properties: @@ -227,7 +227,7 @@ TimeZone Construct from string Construct from arguments - output string in ical form + Output string in ical form Accessors for the following properties. See below for return types for these properties: @@ -243,7 +243,7 @@ Property Construct from string Construct from arguments - output string in ical form + Output string in ical form Dictionary access to parameters @@ -253,7 +253,7 @@ Attendee Construct from string Construct from arguments - output string in ical form + Output string in ical form Access to the following properties and parameters: cuid diff --git a/libical/src/python/test.py b/libical/src/python/test.py index 1fdf515408..4047e78c95 100644 --- a/libical/src/python/test.py +++ b/libical/src/python/test.py @@ -25,6 +25,7 @@ # the License at http://www.mozilla.org/MPL/ #====================================================================== +import LibicalWrap from Libical import * def error_type(): @@ -32,6 +33,10 @@ def error_type(): return error[:index(error,':')] comp_str = """ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//ABC Corporation//NONSGML My Product//EN +METHOD:REQUEST BEGIN:VEVENT ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;CUTYPE=GROUP:MAILTO:employee-A@host.com COMMENT: When in the course of writting comments and nonsense text\, it @@ -43,13 +48,15 @@ DURATION:P3DT4H25M FREEBUSY:19970101T120000/19970101T120000 FREEBUSY:19970101T120000/PT3H FREEBUSY:19970101T120000/PT3H -END:VEVENT""" +END:VEVENT +END:VCALENDAR""" def test_property(): print "--------------------------- Test Property ----------------------" - + + liw = LibicalWrap icalprop = liw.icalproperty_new_from_string("ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;CUTYPE=GROUP:MAILTO:employee-A@host.com") @@ -58,19 +65,43 @@ def test_property(): p = Property(ref=icalprop) print p.name() - print p.parameters() + print + print "Parameters:" + for param in p.parameters(): + print " ", param, " = ", p[param] + print print p['ROLE'] p['ROLE'] = 'INDIVIDUAL' print p['ROLE'] + p['ROLE'] = 'GROFROMBLATZ' + + print p['ROLE'] + + print + + p['X-MAN-FAVOURITE'] = 'Wolverine' + p['X-FILES-FAVOURITE'] = 'Mulder' + + print p['X-MAN-FAVOURITE'] + + assert(p['X-MAN-FAVOURITE'] == 'Wolverine') + assert(p['X-FILES-FAVOURITE'] == 'Mulder') + assert(p['X-FILES-FAVOURITE'] != 'Scully') + print p.value() p.value("mailto:Bob@bob.com") print p.value() print p.as_ical_string() + del p['ROLE'] + del p['X-MAN-FAVOURITE'] + + print p.as_ical_string() + try: p = Property() @@ -79,6 +110,22 @@ def test_property(): else: assert(0) + # X Property + p = Property("X-COMMENT") + + p.value("This is a sentence, with punctuation; indeed: it is") + print p + + p.value("This is not approved by the Ministry of Silly Walks") + print p + + + assert(test_enum('METHOD','PUBLISH')) + assert(not test_enum('METHOD','FOO')) + + assert(test_enum('ACTION','AUDIO')) + assert(not test_enum('ACTION','OPAQUE')) + def test_time(): "Test routine" @@ -99,12 +146,13 @@ def test_time(): t.timezone("America/Los_Angeles") print str(t) - assert(str(t)=='DTSTART;TZID=America/Los_Angeles:19970325T123010Z') + print t.timezone() + #assert(str(t)=='DTSTART;TZID=America/Los_Angeles:19970325T123010') + assert(str(t)=='DTSTART;TZID=/softwarestudio.org/Olson_20010626_2/America/Los_Angeles:19970325T043010') t.second(t.second()+80) - print t - + t.timezone("UTC") assert(t.minute() == 31) assert(t.second() == 30) @@ -120,6 +168,13 @@ def test_time(): assert(isinstance(t2,Time)) assert(t2.hour() == 11) + # test int args + t = Time(2) + print t + + # test float args + t = Time(2.5) + print t def test_period(): @@ -129,7 +184,6 @@ def test_period(): print p - assert(str(p) == 'FREEBUSY:19970101T180000Z/19970101T233000Z') print p.start() @@ -221,14 +275,25 @@ def test_component(): print "------------------- Test Component ----------------------" - c = Component(comp_str); - + c = NewComponent(comp_str); + props = c.properties() + for p in props: + print p.as_ical_string() + + inner = c.components()[0] + + print inner + print type(inner) + + + props = inner.properties() + for p in props: print p.as_ical_string() - dtstart = c.properties('DTSTART')[0] + dtstart = inner.properties('DTSTART')[0] print dtstart @@ -240,21 +305,21 @@ def test_component(): print "\n New hour: ", dtstart.hour() assert(dtstart.hour() == 17) - attendee = c.properties('ATTENDEE')[0] + attendee = inner.properties('ATTENDEE')[0] print attendee t = Time("20011111T123030") t.name('DTEND') - c.add_property(t) + inner.add_property(t) print c - dtstart1 = c.properties('DTSTART')[0] - dtstart2 = c.properties('DTSTART')[0] - dtstart3 = c.property('DTSTART') + dtstart1 = inner.properties('DTSTART')[0] + dtstart2 = inner.properties('DTSTART')[0] + dtstart3 = inner.property('DTSTART') assert(dtstart1 is dtstart2) assert(dtstart1 == dtstart2) @@ -266,53 +331,81 @@ def test_component(): p = Property(type="SUMMARY"); p.value("This is a summary") - c.properties().append(p) + inner.properties().append(p) - print c.as_ical_string() + print inner.as_ical_string() - p = c.properties("SUMMARY")[0] + p = inner.properties("SUMMARY")[0] assert(p!=None); print str(p) assert(str(p) == "SUMMARY:This is a summary") - c.properties()[:] = [p] + inner.properties()[:] = [p] + + print inner.as_ical_string() + + # test sequence + event = Event() - print c.as_ical_string() + try: + event.sequence("foo") + except TypeError: + pass + event.sequence(-1) + print event.sequence() + event.sequence(1) + event.sequence(88) + print event.sequence() + def test_event(): print "------------ Event Class ----------------------" + event = Event() + + event.method('REQUEST') + event.version('2.0') + event.created("20010313T123000Z") - #print "created =", event.created() - assert (event.created() == "20010313T123000Z") + print "created =", event.created() + assert (event.created() == Time("20010313T123000Z")) event.organizer("MAILTO:j_doe@nowhere.com") - org = event.properties('ORGANIZER')[0] - #print org.cn() + org = event.organizer() + print org.cn() org.cn('Jane Doe') assert (isinstance(org, Organizer)) - #print "organizer =", event.organizer() - assert (event.organizer() == "MAILTO:j_doe@nowhere.com") + print "organizer =", event.organizer() + assert (event.organizer().value() == "MAILTO:j_doe@nowhere.com") event.dtstart("20010401T183000Z") - #print "dtstart =", event.dtstart() - assert (event.dtstart()=="20010401T183000Z") + print "dtstart =", event.dtstart() + assert (event.dtstart()== Time("20010401T183000Z")) dtend = Time('20010401T190000Z', 'DTEND') event.dtend(dtend) - assert (event.dtend()==dtend.value()) - assert (event.dtend() == '20010401T190000Z') + assert (event.dtend() ==dtend ) + assert (event.dtend() == Time('20010401T190000Z')) att = Attendee() att.value('jsmith@nothere.com') event.attendees(('ef_hutton@listenup.com', att)) + + event.x_properties('X-TEST',('foo', 'bar')) + event.x_properties('X-TEST2',('foo, biz', 'bar, biz')) + + inner = event.components()[0] + for e in inner.properties('X-TEST'): + print " ", e.as_ical_string() + + assert(len(event.x_properties('X-TEST'))==2) - event.description("A short description. Longer ones break things.") + event.description("A short description. Longer ones break things. Really. What does it break. The code is supposed to handle realy long lines, longer, in fact, than any sane person would create except by writting a random text generator or by excerpting text from a less sane person. Actually, it did \"break\" and I had to remove an \n assert to fix it.") event.status('TeNtAtIvE') print event.as_ical_string() - + def test_derivedprop(): @@ -348,6 +441,138 @@ def test_derivedprop(): assert(0) except: pass +def test_gauge(): + print "------------ Gauge -----------------" + event = Event() + + event.method('REQUEST') + event.version('2.0') + event.created("20010313T123000Z") + event.organizer("MAILTO:j_doe@nowhere.com") + org = event.organizer() + org.cn('Jane Doe') + event.dtstart("20010401T183000Z") + dtend = Time('20010401T190000Z', 'DTEND') + event.dtend(dtend) + event.description("A short description.") + event.status('TeNtAtIvE') + + print event.as_ical_string() + + gauge = Gauge(sql="SELECT * FROM VEVENT WHERE DTSTART > '20010401T180000Z'") + + assert(gauge.compare(event) == 1) + + gauge = Gauge(sql="SELECT * FROM VEVENT WHERE DTSTART > '20010401T190000Z'") + + assert(gauge.compare(event) == 0) + +def do_test_store(storeobj=None, *args): + assert(storeobj != None) + store = storeobj(*args) + assert(store != None) + + print ">------------ ", + print store.__class__, + print "Store -----------------" + + + # create fileset + + event = Event() + + event.method('REQUEST') + event.version('2.0') + event.created("20010313T123000Z") + event.organizer("MAILTO:j_doe@nowhere.com") + event.dtstart("20010401T183000Z") + event.duration('PT3H') + + event.description("A short description.") + + # for i = 1 to 10 + # copy event + # munge uid and increment month + for i in range(1,11): + newevent = event.clone() + newevent.uid("%d@localhost" % (i,)) + newevent.dtstart().month( newevent.dtstart().month() + i ) + + #print ne + store.add_component(newevent) + + # commit + store.commit() + assert(store.count_components("VCALENDAR") == 10) + # free + del(store) + + # open again + store = storeobj(*args) + # assert count of components = 10 + assert(store.count_components("VCALENDAR") == 10) + + # print them out + # fetch by uid + n7 = store.fetch("7@localhost") + print n7 + # fetch by match + + n7m = store.fetchMatch(n7) + assert(str(n7) == str(n7m)) + + # modify in memory + n7.uid("42@localhost") + del(store) + del(n7) + + store = storeobj(*args) + assert(store.fetch("42@localhost") == None) + n7 = store.fetch("7@localhost") + n7.uid("42@localhost") + store.mark() + store.commit() + del(store) + store = storeobj(*args) + assert(store.fetch("7@localhost") == None) + + # fetch by gauge + + gauge = Gauge(sql="SELECT * FROM VEVENT WHERE DTSTART > '20010601T000000Z' AND DTSTART < '20010901T000000Z'") + + store.select(gauge) + + count = 0 + + c = store.first_component() + while c != None: + print c.uid() + print c.dtstart() + print + count = count + 1 + c = store.next_component() + + store.clearSelect() + + assert(count == 3) + + # remove all of them + c = store.first_component() + while c != None: + print c.uid() + store.remove_component(c) + c = store.first_component() + + assert(store.count_components("VCALENDAR") == 0) + store.commit() + assert(store.count_components("VCALENDAR") == 0) + # print them out + # assert count of components = 0 + + +def test_store(): + print "------------ Store -----------------" + do_test_store(FileStore,"filesetout.ics") def run_tests(): test_property() @@ -366,6 +591,11 @@ def run_tests(): #test_attach() + test_gauge() + + test_store() + + if __name__ == "__main__": -- cgit v1.2.3