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/ChangeLog | 109 +++++ libical/src/python/Collection.py | 124 +++++ libical/src/python/Component.py | 670 +++++++++++++++++++++++++ libical/src/python/DerivedProperties.py | 59 +++ libical/src/python/Libical.py | 39 ++ libical/src/python/LibicalWrap.i | 352 ++++++++++++++ libical/src/python/Makefile | 369 ++++++++++++++ libical/src/python/Makefile.am | 42 ++ libical/src/python/Makefile.in | 369 ++++++++++++++ libical/src/python/Property.py | 839 ++++++++++++++++++++++++++++++++ libical/src/python/Store.py | 176 +++++++ libical/src/python/python-binding.txt | 434 +++++++++++++++++ libical/src/python/test.py | 373 ++++++++++++++ 13 files changed, 3955 insertions(+) create mode 100644 libical/src/python/ChangeLog create mode 100644 libical/src/python/Collection.py create mode 100644 libical/src/python/Component.py create mode 100644 libical/src/python/DerivedProperties.py create mode 100644 libical/src/python/Libical.py create mode 100644 libical/src/python/LibicalWrap.i create mode 100644 libical/src/python/Makefile create mode 100644 libical/src/python/Makefile.am create mode 100644 libical/src/python/Makefile.in create mode 100644 libical/src/python/Property.py create mode 100644 libical/src/python/Store.py create mode 100644 libical/src/python/python-binding.txt create mode 100644 libical/src/python/test.py (limited to 'libical/src/python') diff --git a/libical/src/python/ChangeLog b/libical/src/python/ChangeLog new file mode 100644 index 0000000000..7b16306ce5 --- /dev/null +++ b/libical/src/python/ChangeLog @@ -0,0 +1,109 @@ +2001-03-13 Eric Busboom + + * Component.py Added Component.property() + +2001-03-10 Patrick Lewis + + * Added __str__ method to Collection.Collection + + * Component.Component can now be initialized without arguments + + * Made _singular_property and _multiple_properties (in Component) + useful for nearly all the specific component interfaces + + * Changed Property.Attendee and Property.Organizer to allow creation + with no arguments + + * Filled in Todo skeleton + + * Added test function for an Event + + +2001-03-05 Eric Busboom + + * Property.py Added a lot of exception code to signal failure to + create a Property. + + * DerivedProperties.py Added derived property classes for RDATE + and TRIGGER, two properties that can have one of two value types. + + +2001-03-04 Eric Busboom + + * Property.pm Added Property.ConstructorFailedError exception + + * Component.pm fixed bug in Collection.__setslice__. "," used + instead of ":" + +2001-03-04 Patrick Lewis + + * Split Libical.py file into Component.py, Property.py, Collection.py, + and Store.py + + * Added test_* functions to test.py + + * Changed component bindings to return a Collection when objects can + have multiple values + + * Changed Component object to allow for creation of an object without + an initial string + + * Added Todo and Journal events + +2001-02-28 Eric Busboom + + * Property Remove most internal data. The property now work + alsmost entirely off of the icalproperty that it holds a reference + to. Made changes in all derived Properties to accomodate the + change. + + * Property Added __del__ + + * Component Component.properties() now caches properties that it + constructs, so two calls to properties() to that get the same + icalproperty will also get the same Property. + + * Property Added Property.__cmp__ to test equality of properties + based on ical string values + +2001-02-27 Eric Busboom + + * Property Added Property.ref() to set/get the reference to the + Property's internal icalproperty + + * Property Property._update_value now changes the icalproperty + value if a reference has been set. + + * Component re-instituted Component.properties(). The routine now + adds a 'ref' key to the dict that holds the python pointer + string. The C hex value of the pointer is in the 'pid' key + + +2001-02-27 Patrick Lewis + + * Backed out changes to Component removing comp_p; + Component.comp_p should be restored + +2001-02-26 Eric Busboom + + * Period Added test routine,test_period() + + * Period implemented methods in period + + * Time Addedd addition and subtraction operators + +2001-02-25 Eric Busboom + + * Libical.py Added test routine for time, time_test() + + * Libical.py Remove end of line chars ('\r\n" ) from + Property._str__. Caller should add these lines itself + + * Liical.py CHanges Time._update_values to set time VALUE type + based on use of is_date, rather than length of string. + + * Libical.py Removed call to _update_value in TIme::timezone + + + * Libical.py changed update_value to _update_value + diff --git a/libical/src/python/Collection.py b/libical/src/python/Collection.py new file mode 100644 index 0000000000..48092aadaa --- /dev/null +++ b/libical/src/python/Collection.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +#====================================================================== +# FILE: Collection.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 types import * + +class Collection: + """A group of components that can be modified somewhat like a list. + + Usage: + Collection(componet, propSequence) + + component is a Component object + propSequence is a list or tuple of Property (or subclass of Property) + of objects already in component + """ + + def __init__(self, component, propSequence): + self._properties = list(propSequence[:]) + self._component = component + + def __getslice__(self, beg, end): + return Collection(self._component, self._properties[beg:end]) + + def __setslice__(self, beg, end, sequence): + + if not isinstance(sequence,ListType): + raise TypeError, "must assign list (not instance) to slice" + + oldProps = self._properties[beg:end] + + for p in oldProps: + self._component.remove_property(p) + + self._properties[beg:end] = sequence + for p in sequence: + self._component.add_property(p) + + def __getitem__(self, i): + return self._properties[i] + + def __setitem__(self, i, prop): + self._component.remove_property(self._properties[i]) + self._component.add_property(prop) + self._properties[i]=prop + + def __delitem__(self, i): + self._component.remove_property(self._properties[i]) + del self._properties[i] + + def __len__(self): + return len(self._properties) + + def __str__(self): + s = "[ " + if len(self._properties) > 0: + s = s + str(self._properties[0]) + for p in self._properties[1:]: + s = "%s, %s" % (s, p) + s = s + " ]" + return s + + def append(self, property): + self._properties.append(property) + self._component.add_property(property) + +class ComponentCollection: + + def __init__(self, parent, componentSequence): + self._parent = parent + self._components = list(componentSequence[:]) + + def __getslice__(self, beg, end): + return ComponentCollection(self._parent, self._components[beg:end]) + + def __setslice__(self, beg, end, sequence): + oldComps = self._components[beg:end] + self._components.__setslice__(beg, end, sequence) + for c in sequence: + self._components.addComponent(c) + for c in oldComps: + self._parent.remove_component(c) + + def __getitem__(self, i): + return self._components[i] + + def __setitem__(self, i, prop): + self._parent.remove_component(self._components[i]) + self._parent.add_property(prop) + self._components[i]=prop + + def __delitem__(self, i): + self._parent.remove_componet(self._components[i]) + del self._components[i] + + def __len__(self): + return len(self._components) + + def append(self, property): + self._components.append(property) + self._parent.addComponent(property) diff --git a/libical/src/python/Component.py b/libical/src/python/Component.py new file mode 100644 index 0000000000..f4399f6c20 --- /dev/null +++ b/libical/src/python/Component.py @@ -0,0 +1,670 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +#====================================================================== +# FILE: Component.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 * +from Property import * +from Collection import * + +class Component: + + def __init__(self,str=None, component_kind="ANY", ref=None): + + if ref != None: + self._ref = ref + 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) + + self.cached_props = {} + + def __del__(self): + if self._ref != None and \ + icalcomponent_get_parent(self._ref) != None: + + for k in self.cached_props.keys(): + del self.cached_props[k] + + icalcomponent_free(self._ref) + self._ref = None + + def _prop_from_ref(self,p): + + d_string = icallangbind_property_eval_string(p,":") + d = eval(d_string) + d['ref'] = p + + if not self.cached_props.has_key(p): + + if d['value_type'] == 'DATE-TIME' or d['value_type'] == 'DATE': + prop = Time(d,) + elif d['value_type'] == 'PERIOD': + prop = Period(d) + elif d['value_type'] == 'DURATION': + prop = Duration(d) + elif d['name'] == 'ATTACH': + prop = Attach(d) + elif d['name'] == 'ATTENDEE': + prop = Attendee(d) + elif d['name'] == 'ORGANIZER': + prop = Organizer(d) + else: + prop=Property(ref=p) + + self.cached_props[p] = prop + + def property(self, type): + + p = icallangbind_get_first_property(self._ref,type) + + if p !='NULL': + self._prop_from_ref(p) + prop = self.cached_props[p] + return prop + else : + return None + + def properties(self,type='ANY'): + """ + Return a list of Property instances, each representing a + property of the type 'type.' + """ + + props = [] + + p = icallangbind_get_first_property(self._ref,type) + + while p !='NULL': + self._prop_from_ref(p) + prop = self.cached_props[p] + props.append(prop) + p = icallangbind_get_next_property(self._ref,type) + + return Collection(self,props) + + def add_property(self, prop): + "Adds the property object to the component." + + if not isinstance(prop,Property): + raise TypeError + + prop_p = prop.ref() + + if not prop_p: + s = str(prop) + prop_p = icalproperty_new_from_string(s) + + if prop_p == 'NULL': + raise "Bad property string: " + s + + prop.ref(prop_p) + + if icalproperty_get_parent(prop_p)=='NULL': + icalcomponent_add_property(self._ref, prop_p) + elif icalproperty_get_parent(prop_p) != self._ref: + raise "Property is already a child of another component" + + + def remove_property(self,prop): + + if prop.ref() and self.cached_props.has_key(prop.ref()): + + del self.cached_props[prop.ref()] + icalcomponent_remove_property(self._ref,prop.ref()) + + def components(self,type='ANY'): + comps = [] + + return comps + + def add_component(self, componentObj): + "Adds a child component." + pass + + + def remove_component(self, component): + "Removes a child component" + pass + + def as_ical_string(self): + return self.__str__() + + def __str__(self): + + return icalcomponent_as_ical_string(self._ref) + + + +def NewComponent(comp): + "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 + else: + compStr = icalcomponent_as_ical_string(comp) + + kind = icalcomponent_isa(comp) + kindStr = icalenum_component_kind_to_string(kind) + # Do I need to free kind? (I think not). + + if kindStr == 'VEVENT': + newComp = Event(compStr) + elif kindStr == 'VTODO': + newComp = Todo(compStr) + elif kindStr == 'VJOURNAL': + newComp = Journal(compstr) + else: + newComp = Component(compStr) + + # 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 + + +class GenericComponent(Component): + + def __init__(self): + + # Component.__init__(self, str) # Call from subclasses + self._recurrence_set=None + + def _singular_property(self, name, value_type, value=None, + property_obj=None, enumerated_values=None): + """Sets or gets the value of a method which exists once per Component. + + This is a constructor method for properties without a strictly defined + object.""" + + curr_properties = self.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() + else: + raise ValueError, "too many properties of type %s" % propType + + # Set the value + else: + # Check if value is in enumerated_values + if enumerated_values: + value = upper(value) + if value not in enumerated_values: + raise ValueError, "%s is not one of %s" \ + % (value, enumerated_values) + + # Create the new property + if property_obj: + if not isinstance(value, property_obj): + # Create a special property_obj property + if property_obj == Time: + p = Time(value, name) + ## p.value_type(value_type) + else: + p = property_obj() + ## p.value_type(value_type) + p.value(value) + else: + p = value # value is already a property_obj + else: + # Create a generic property + p = Property(name) + ## p.value_type(value_type) + p.value(value) + + if len(curr_properties) == 1: + self.remove_property(curr_properties[0]) + elif len(curr_properties) > 1: + raise ValueError, "too many properties of type %s" % propType + + self.add_property(p) + + def method(self, v=None): + "Sets or returns the value of the METHOD property." + return self._singular_property("METHOD", "TEXT", v) + + def prodid(self, v=None): + "Sets or returns the value of the PRODID property." + return self._singular_property("PRODID", "TEXT", v) + + def calscale(self, v=None): + "Sets or returns the value of the CALSCALE property." + return self._singular_property("CALSCALE", "TEXT", v) + + 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) + return self._singular_property('CLASS', 'TEXT', v) + + def created(self, v=None): + """Sets or returns the value of the CREATED property. + + Usage: + created(time_obj) # Set the value using a Time object + created('19970101T123000Z') # Set using an iCalendar string + created(982362522) # Set using seconds + created() # Return an iCalendar string + """ + return self._singular_property("CREATED", "DATE-TIME", v, Time) + + def description(self, v=None): + "Sets or returns the value of the DESCRIPTION property." + return self._singular_property("DESCRIPTION", "TEXT", v) + + def dtstamp(self, v=None): + """Sets or returns the value of the DTSTAMP property. + + Usage: + dtstamp(time_obj) # Set the value using a Time object + dtstamp('19970101T123000Z')# Set using an iCalendar string + dtstamp(982362522) # Set using seconds + dtstamp() # Return an iCalendar string + """ + return self._singular_property("DTSTAMP", "DATE-TIME", v, Time) + + def dtstart(self, v=None): + """Sets or returns the value of the DTSTART property. + + Usage: + dtstart(time_obj) # Set the value using a Time object + dtstart('19970101T123000Z') # Set the value as an iCalendar string + dtstart(982362522) # Set the value using seconds (time_t) + dtstart() # Return the time as an iCalendar string + """ + return self._singular_property("DTSTART", "DATE-TIME", v, Time) + + def last_modified(self, v=None): + """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 + """ + return self._singular_property("LAST-MODIFIED", "DATE-TIME", v, Time) + + def organizer(self, v=None): + """Sets or gets the value of the ORGANIZER property. + + Usage: + organizer(orgObj) # Set value using an organizer object + organizer('MAILTO:jd@not.com') # Set value using a CAL-ADDRESS string + organizer() # Return a CAL-ADDRESS string + """ + return self._singular_property('ORGANIZER', 'CAL-ADDRESS', v, + Organizer) + + def recurrence_id(self, v=None): + """Sets or gets the value for the RECURRENCE-ID property. + + Usage: + recurrence_id(recIdObj) # Set using a Recurrence_Id object + recurrence_id("19700801T133000") # Set using an iCalendar string + recurrence_id(8349873494) # Set using seconds from epoch + recurrence_id() # Return an iCalendar string + """ + return self._singular_property('RECURRENCE-ID', 'DATE-TIME', v, + Recurrence_Id) + + def sequence(self, v=None): + """Sets or gets the SEQUENCE value of the Event. + + Usage: + sequence(1) # Set the value using an integer + sequence('2') # Set the value using a string containing an integer + sequence() # Return an integer + """ + if isinstance(v, StringType): + v = int(str) + return self._singular_property('SEQUENCE', 'INTEGER', v) + + def summary(self, v=None): + "Sets or gets the SUMMARY value of the Event." + return self._singular_property('SUMMARY', 'TEXT', v) + + def uid(self, v=None): + "Sets or gets the UID of the Event." + return self._singular_property('UID', 'TEXT', v) + + def url(self, v=None): + """Sets or returns the URL property.""" + return self._singular_property('URL', 'URI', v) + + #### + # Not quite sure if this is how we want to handle recurrence rules, but + # this is a start. + + def recurrence_set(self): + "Returns the Events RecurrenceSet object." + if self._recurrence_set == None: # i.e haven't initialized one + self._recurrence_set = RecurrenceSet() + return self._recurrence_set + + ### + # Alarm interface. Returns an ComponentCollection. + + def alarms(self, values=None): + """Sets or returns ALARM components. + + Examples: + alarms((alarm1,)) # Set using Alarm component + alarms() # Returns an ComponentCollection of all Alarms + """ + if values!=None: + for alarm in values: + self.addComponent(alarm) + else: + return ComponentCollection(self, self.components('VALARM')) + + #### + # Methods that deal with Properties that can occur multiple times are + # below. They use the Collection class to return their Properties. + + def _multiple_properties(self, name, value_type, values, + property_obj=None): + "Processes set/get for Properties that can have multiple instances." + + # Set value + if values!=None: + if not isinstance(values, TupleType) \ + and not isinstance(values, ListType): + raise TypeError, "%s is not a tuple or list." + + # Delete old properties + for p in self.properties(name): + self.remove_property(p) + + for v in values: + if property_obj: # Specialized properties + if not isinstance(v, property_obj): # Make new object + new_prop = property_obj() + new_prop.value(v) + else: # Use existing object + new_prop = v + else: # Generic properties + new_prop= Property() + new_prop.name(name) + # new_prop.value_type(value_type) + new_prop.value(v) + + self.add_property(new_prop) + + # Get value + else: + return Collection(self, self.properties(name)) + + def attachments(self, values=None): + """Sets or returns a Collection of Attach properties. + + 'values' can be a sequence containing URLs (strings) and/or file-ish + objects. + """ + return self._multiple_properties("ATTACH", "", value, Attach) + + def attendees(self, value=None): + """Sets attendees or returns a Collection of Attendee objects. + + If setting the attendees, pass a sequence as the argument. + Examples: + # Set using Attendee objects + attendees((attObj1, attObj2)) + # Set using a CAL-ADDRESS string + attendees(['MAILTO:jdoe@somewhere.com']) + # Set using a combination of Attendee objects and strings + attendees(['MAILTO:jdoe@somewhere.com', attObj1]) + # Returns a list of Attendee objects + attendees() + + When setting the attendees, any previous Attendee objects in the Event + are overwritten. If you want to add to the Attendees, one way to do it + is: + + attendees().append(Attendee('MAILTO:jdoe@nothere.com')) + """ + return self._multiple_properties("ATTENDEE", "", value, Attendee) + + def categories(self, value=None): + """Sets categories or returns a Collection of CATEGORIES properties. + + If setting the categories, pass a sequence as the argument. + Examples: + # Set using string[s] + categories(('APPOINTMENT', 'EDUCATION')) + # Returns a list of Category properites + categories() + + When setting the attendees, any previous category Properties in the + Event are overwritten. If you want to add to the categories, one way + to do it is: + + new_cat=Property('CATEGORIES') + new_cat.value_type('TEXT') + new_cat.value('PERSONAL') + categories().append(new_cat) + """ + return self._multiple_properties("CATEGORIES", "TEXT", value) + + def comments(self, value=None): + "Sets or returns a Collection of COMMENT properties." + return self._multiple_properties('COMMENT', 'TEXT', value) + + def contacts(self, value=None): + "Sets or returns a Collection of CONTACT properties." + return self._multiple_properties('CONTACT', 'TEXT', value) + + def related_tos(self, value=None): + "Sets or returns a Collectoin of RELATED-TO properties." + return self._multiple_properties('RELATED-TO', 'TEXT', value) + + +class Event(GenericComponent): + "The iCalendar Event object." + + def __init__(self, str=None): + Component.__init__(self, str, "VEVENT") + GenericComponent.__init__(self) + + 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. + + Usage: + dtend(time_obj) # Set the value using a Time object + dtend('19970101T123000Z') # Set the value as an iCalendar string + dtend(982362522) # Set the value using seconds (time_t) + dtend() # Return the time as an iCalendar string + + If the dtend value is being set and duration() has a value, the + duration property will be removed. + """ + if v != None: + duration = self.properties('DURATION') + for d in duration: # Clear DURATION properties + self.remove_property(d) + return self._singular_property("DTEND", "DATE-TIME", v, Time) + + def duration(self, v=None): + """Sets or returns the value of the duration property. + + Usage: + duration(dur_obj) # Set the value using a Duration object + duration("P3DT12H") # Set value as an iCalendar string + duration(3600) # Set duration using seconds + duration() # Return duration as an iCalendar string + + If the duration value is being set and dtend() has a value, the dtend + property will be removed. + """ + + if v != None: + dtend = self.properites('DTEND') + for d in dtend: + self.remove_property(d) # Clear DTEND properties + return self._singular_property("DURATION", "DURATION", v, Duration) + + def status(self, v=None): + "Sets or returns the value of the STATUS property." + + # These values are only good for VEVENT components (i.e. don't copy + # & paste into VTODO or VJOURNAL + valid_values=('TENTATIVE', 'CONFIRMED', 'CANCELLED') + return self._singular_property("STATUS", "TEXT", v, + enumerated_values=valid_values) + + def geo(self, v=None): + """Sets or returns the value of the GEO property. + + Usage: + geo(value) or + geo() # Returns the icalendar string + + 'value' is either a icalendar GEO string or a sequence with two 'float' + numbers. + + Examples: + geo('40.232;-115.9531') # Set value using string + geo((40.232, -115.9531)) # Set value using a sequence + geo() # Returns "40.232;-115.9531" + + To get the GEO property represented as a tuple and numbers instead of + the iCalendar string, use geo_get_tuple(). + """ + + if isinstance(v, ListType) or isinstance(v, TupleType): + v = "%s;%s" % (float(v[0]), float(v[1])) + return self._singular_property("GEO", "FLOAT", v) + + def geo_get_tuple(self): + """Returns the GEO property as a tuple.""" + + geo = self.geo() + geo = split(geo, ';') + return float(geo[0]), float(geo[1]) + + def location(self, v=None): + """Sets or returns the LOCATION property.""" + return self._singular_property("LOCATION", "TEXT", v) + + def transp(self, v=None): + """Sets or returns the TRANSP property.""" + ok_values = ('OPAQUE', 'TRANSPARENT') + return self._singular_property('TRANSP', 'TEXT', v, + enumerated_values=ok_values) + + def resources(self, v=None): + pass + +class Todo(GenericComponent): + "The iCalendar TODO component." + + 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) + + def geo(self, value=None): + if isinstance(v, ListType) or isinstance(v, TupleType): + v = "%s;%s" % (float(v[0]), float(v[1])) + return self._singular_property("GEO", "FLOAT", value) + + def location(self, value=None): + return self._singular_property('LOCATION', 'TEXT', value) + + def percent(self, value=None): + if value!=None: + value = str(int(value)) + return self._singular_property('PERCENT', 'INTEGER', value) + + def status(self, value=None): + if value!=None: + value=upper(value) + ok_values = ('NEEDS-ACTION', 'COMPLETED', 'IN-PROCESS', 'CANCELLED') + return self._singular_property('STATUS', 'TEXT', value, + enumerated_values=ok_values) + + def due(self, value=None): + if value != None: + duration = self.properties('DURATION') + for d in duration: + self.remove_property(d) # Clear DURATION properties + return self._singular_property('DUE', 'DATE-TIME', value, Time) + + def duration(self, value=None): + if value != None: + due = self.properites('DUE') + for d in due: + self.remove_property(d) # Clear DUE properties + return self._singular_property("DURATION", "DURATION", value, Duration) + + def resources(): + pass + + +class Journal(GenericComponent): + "The iCalendar JOURNAL component." + + 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) + 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 new file mode 100644 index 0000000000..b557a1f7d7 --- /dev/null +++ b/libical/src/python/DerivedProperties.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +#====================================================================== +# FILE: DerivedProperties.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 Property import Time, Period, Duration + +def RDate(arg): + + class RDate_Time(Time): + def __init__(self,arg): Time.__init__(self,arg,"RDATE") + + class RDate_Period(Period): + def __init__(self,arg): Period.__init__(self,arg,"RDATE") + + p = None + for c in [RDate_Time, RDate_Period]: + try: return c(arg) + except Property.ConstructorFailedError, d: pass + raise Property.ConstructorFailedError("Failed to construct RDATE from "+str(arg)) + + +def Trigger(arg): + class Trigger_Time(Time): + def __init__(self,arg): Time.__init__(self,arg,"TRIGGER") + + class Trigger_Duration(Duration): + def __init__(self,arg): Duration.__init__(self,arg,"TRIGGER") + + p = None + for c in [Trigger_Duration, Trigger_Time]: + try: return c(arg) + except Property.ConstructorFailedError, d: pass + raise Property.ConstructorFailedError("Failed to construct TRIGGER from "+str(arg)) + + + diff --git a/libical/src/python/Libical.py b/libical/src/python/Libical.py new file mode 100644 index 0000000000..78a0fff8ff --- /dev/null +++ b/libical/src/python/Libical.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +#====================================================================== +# FILE: Libical.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/ +#====================================================================== + + +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 Store import Store, FileStore diff --git a/libical/src/python/LibicalWrap.i b/libical/src/python/LibicalWrap.i new file mode 100644 index 0000000000..c6b6740383 --- /dev/null +++ b/libical/src/python/LibicalWrap.i @@ -0,0 +1,352 @@ +/* -*- Mode: C -*-*/ +/*====================================================================== + FILE: ical.i + + (C) COPYRIGHT 1999 Eric Busboom + http://www.softwarestudio.org + + The contents of this file are subject to the Mozilla Public License + Version 1.0 (the "License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and + limitations under the License. + + The original author is Eric Busboom + + Contributions from: + Graham Davison (g.m.davison@computer.org) + + ======================================================================*/ + +%module LibicalWrap + + +%{ +#include "ical.h" +#include "icalss.h" + +#include /* for size_t */ +#include + +%} + + +#include "fcntl.h" /* For Open flags */ + +typedef void icalcomponent; +typedef void icalproperty; + +icalcomponent* icalparser_parse_string(char* str); + + +icalcomponent* icalcomponent_new(int kind); +icalcomponent* icalcomponent_new_clone(icalcomponent* component); +icalcomponent* icalcomponent_new_from_string(char* str); + +char* icalcomponent_as_ical_string(icalcomponent* component); + +void icalcomponent_free(icalcomponent* component); +int icalcomponent_count_errors(icalcomponent* component); +void icalcomponent_strip_errors(icalcomponent* component); +void icalcomponent_convert_errors(icalcomponent* component); + +icalproperty* icalcomponent_get_current_property(icalcomponent* component); + +icalproperty* icalcomponent_get_first_property(icalcomponent* component, + int kind); +icalproperty* icalcomponent_get_next_property(icalcomponent* component, + int kind); + +icalcomponent* icalcomponent_get_current_component (icalcomponent* component); + +icalcomponent* icalcomponent_get_first_component(icalcomponent* component, + int kind); +icalcomponent* icalcomponent_get_next_component(icalcomponent* component, + int kind); + +void icalcomponent_add_property(icalcomponent* component, + icalproperty* property); + +void icalcomponent_remove_property(icalcomponent* component, + icalproperty* property); + + +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); + +icalproperty* icalproperty_new_from_string(char* str); + +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); + + +icalcomponent* icalproperty_get_parent(icalproperty* property); + +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); + +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); + + +/* Iterate through properties and components using strings for the kind */ +icalproperty* icallangbind_get_first_property(icalcomponent *c, + const char* prop); + +icalproperty* icallangbind_get_next_property(icalcomponent *c, + const char* prop); + +icalcomponent* icallangbind_get_first_component(icalcomponent *c, + const char* comp); + +icalcomponent* icallangbind_get_next_component(icalcomponent *c, + const char* comp); + + +/* Return a string that can be evaluated in perl or python to + generated a hash that holds the property's name, value and + parameters. Sep is the hash seperation string, "=>" for perl and + ":" for python */ +const char* icallangbind_property_eval_string(icalproperty* prop, char* sep); + +int icallangbind_string_to_open_flag(const char* str); + +/*********************************************************************** + Time routines +***********************************************************************/ + + +struct icaltimetype +{ + int year; + int month; + int day; + 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); + +/* 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); + +/* Return a string represention of the time, in RFC2445 format. The + string is owned by libical */ +char* icaltime_as_ical_string(struct icaltimetype tt); + +/* create a time from an ISO format string */ +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); + +/* Return true of the time is null. */ +int icaltime_is_null_time(struct icaltimetype t); + +/* Returns false if the time is clearly invalid, but is not null. This + is usually the result of creating a new time type buy not clearing + it, or setting one of the flags to an illegal value. */ +int icaltime_is_valid_time(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 + arithmetic on times without worrying about overflow or + underflow. */ +struct icaltimetype icaltime_normalize(struct icaltimetype t); + +/* Return the day of the year of the given time */ +short icaltime_day_of_year(struct icaltimetype t); + +/* Create a new time, given a day of year and a year. */ +struct icaltimetype icaltime_from_day_of_year(short doy, short year); + +/* Return the day of the week of the given time. Sunday is 0 */ +short icaltime_day_of_week(struct icaltimetype t); + +/* Return the day of the year for the Sunday of the week that the + 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); + +/* Return -1, 0, or 1 to indicate that ab */ +int icaltime_compare(struct icaltimetype a,struct icaltimetype b); + +/* like icaltime_compare, but only use the date parts. */ +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); + + +/*********************************************************************** + Duration Routines +***********************************************************************/ + + +struct icaldurationtype +{ + int is_neg; + unsigned int days; + unsigned int weeks; + unsigned int hours; + unsigned int minutes; + unsigned int seconds; +}; + +struct icaldurationtype icaldurationtype_from_int(int t); +struct icaldurationtype icaldurationtype_from_string(const char*); +int icaldurationtype_as_int(struct icaldurationtype duration); +char* icaldurationtype_as_ical_string(struct icaldurationtype d); +struct icaldurationtype icaldurationtype_null_duration(); +int icaldurationtype_is_null_duration(struct icaldurationtype d); + +struct icaltimetype icaltime_add(struct icaltimetype t, + struct icaldurationtype d); + +struct icaldurationtype icaltime_subtract(struct icaltimetype t1, + struct icaltimetype t2); + + +/*********************************************************************** + Period Routines +***********************************************************************/ + + +struct icalperiodtype +{ + struct icaltimetype start; + struct icaltimetype end; + struct icaldurationtype duration; +}; + +struct icalperiodtype icalperiodtype_from_string (const char* str); + +const char* icalperiodtype_as_ical_string(struct icalperiodtype p); +struct icalperiodtype icalperiodtype_null_period(); +int icalperiodtype_is_null_period(struct icalperiodtype p); +int icalperiodtype_is_valid_period(struct icalperiodtype p); + + +/*********************************************************************** + Storage Routines +***********************************************************************/ + +icalfileset* icalfileset_new(const char* path); + +/* Like _new, but takes open() flags for opening the file */ +icalfileset* icalfileset_new_open(const char* path, + int flags, int mode); + +void icalfileset_free(icalfileset* cluster); + +const char* icalfileset_path(icalfileset* 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); + +icalerrorenum icalfileset_add_component(icalfileset* cluster, + icalcomponent* child); + +icalerrorenum icalfileset_remove_component(icalfileset* cluster, + icalcomponent* child); + +int icalfileset_count_components(icalfileset* cluster, + icalcomponent_kind 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); + +/* 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); + + +/* Modify components according to the MODIFY method of CAP. Works on + the currently selected components. */ +icalerrorenum icalfileset_modify(icalfileset* store, icalcomponent *oldcomp, + icalcomponent *newcomp); + +/* Iterate through components. If a guage 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_component(icalfileset* cluster); diff --git a/libical/src/python/Makefile b/libical/src/python/Makefile new file mode 100644 index 0000000000..39e3a6ed1e --- /dev/null +++ b/libical/src/python/Makefile @@ -0,0 +1,369 @@ +# Generated automatically from Makefile.in by configure. +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = /bin/sh + +srcdir = . +top_srcdir = ../.. +prefix = /opt/gnome +exec_prefix = ${prefix} + +bindir = ${exec_prefix}/bin +sbindir = ${exec_prefix}/sbin +libexecdir = ${exec_prefix}/libexec +datadir = ${prefix}/share +sysconfdir = ${prefix}/etc +sharedstatedir = ${prefix}/com +localstatedir = ${prefix}/var +libdir = ${exec_prefix}/lib +infodir = ${prefix}/info +mandir = ${prefix}/man +includedir = ${prefix}/include +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/libical +pkglibdir = $(libdir)/libical +pkgincludedir = $(includedir)/libical + +top_builddir = ../.. + +ACLOCAL = aclocal +AUTOCONF = autoconf +AUTOMAKE = automake +AUTOHEADER = autoheader + +INSTALL = /usr/bin/install -c +INSTALL_PROGRAM = ${INSTALL} $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_SCRIPT = ${INSTALL_PROGRAM} +transform = s,x,x, + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = i686-pc-linux-gnu +host_triplet = i686-pc-linux-gnu +AR = ar +AS = @AS@ +CC = gcc +DLLTOOL = @DLLTOOL@ +LEX = flex +LIBTOOL = $(SHELL) $(top_builddir)/libtool +LN_S = ln -s +MAINT = # +MAKEINFO = makeinfo +OBJDUMP = @OBJDUMP@ +PACKAGE = libical +PERL = perl +PY_CFLAGS = -I/usr/include/python1.5 +PY_EXTRA_LIBS = -lieee -ldl -lpthread +PY_LIBS = python1.5 +PY_LIB_LOC = -L/usr/lib/python1.5/config +RANLIB = ranlib +VERSION = 0.23a +YACC = bison -y +python_val = true + +lib_LTLIBRARIES = libLibicalWrap.la + +libLibicalWrap_la_SOURCES = LibicalWrap.c + +INCLUDES = -I$(top_builddir) -I$(top_srcdir)/src -I$(top_builddir)/src -I$(top_srcdir)/src/libical -I$(srcdir)/libical -I$(top_srcdir)/src/libicalss -I$(srcdir)/libicalss $(PY_CFLAGS) + + +LDADD = ../libical/libical.la ../libicalss/libicalss.la + +CLEANFILES = LibicalWrap.c LibicalWrap_wrap.doc Libical.pyc LibicalWrap.so + +EXTRA_DIST = Libical.py LibicalWrap.i python-binding.txt test.py Collection.py Component.py DerivedProperties.py Property.py Store.py ChangeLog + +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../../config.h +CONFIG_CLEAN_FILES = +LTLIBRARIES = $(lib_LTLIBRARIES) + + +DEFS = -DHAVE_CONFIG_H -I. -I$(srcdir) -I../.. +CPPFLAGS = +LDFLAGS = +LIBS = +libLibicalWrap_la_LDFLAGS = +libLibicalWrap_la_LIBADD = +libLibicalWrap_la_OBJECTS = LibicalWrap.lo +CFLAGS = -g -O2 +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +DIST_COMMON = ChangeLog Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = gtar +GZIP_ENV = --best +DEP_FILES = .deps/LibicalWrap.P +SOURCES = $(libLibicalWrap_la_SOURCES) +OBJECTS = $(libLibicalWrap_la_OBJECTS) + +all: all-redirect +.SUFFIXES: +.SUFFIXES: .S .c .lo .o .s +$(srcdir)/Makefile.in: # Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu src/python/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(BUILT_SOURCES) + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +mostlyclean-libLTLIBRARIES: + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + +distclean-libLTLIBRARIES: + +maintainer-clean-libLTLIBRARIES: + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(libdir) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + echo "$(LIBTOOL) --mode=install $(INSTALL) $$p $(DESTDIR)$(libdir)/$$p"; \ + $(LIBTOOL) --mode=install $(INSTALL) $$p $(DESTDIR)$(libdir)/$$p; \ + else :; fi; \ + done + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/$$p; \ + done + +.s.o: + $(COMPILE) -c $< + +.S.o: + $(COMPILE) -c $< + +mostlyclean-compile: + -rm -f *.o core *.core + +clean-compile: + +distclean-compile: + -rm -f *.tab.c + +maintainer-clean-compile: + +.s.lo: + $(LIBTOOL) --mode=compile $(COMPILE) -c $< + +.S.lo: + $(LIBTOOL) --mode=compile $(COMPILE) -c $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + +maintainer-clean-libtool: + +libLibicalWrap.la: $(libLibicalWrap_la_OBJECTS) $(libLibicalWrap_la_DEPENDENCIES) + $(LINK) -rpath $(libdir) $(libLibicalWrap_la_LDFLAGS) $(libLibicalWrap_la_OBJECTS) $(libLibicalWrap_la_LIBADD) $(LIBS) + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = src/python + +distdir: $(DISTFILES) + here=`cd $(top_builddir) && pwd`; \ + top_distdir=`cd $(top_distdir) && pwd`; \ + distdir=`cd $(distdir) && pwd`; \ + cd $(top_srcdir) \ + && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnu src/python/Makefile + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$d/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done + +DEPS_MAGIC := $(shell mkdir .deps > /dev/null 2>&1 || :) + +-include $(DEP_FILES) + +mostlyclean-depend: + +clean-depend: + +distclean-depend: + -rm -rf .deps + +maintainer-clean-depend: + +%.o: %.c + @echo '$(COMPILE) -c $<'; \ + $(COMPILE) -Wp,-MD,.deps/$(*F).pp -c $< + @-cp .deps/$(*F).pp .deps/$(*F).P; \ + tr ' ' '\012' < .deps/$(*F).pp \ + | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \ + >> .deps/$(*F).P; \ + rm .deps/$(*F).pp + +%.lo: %.c + @echo '$(LTCOMPILE) -c $<'; \ + $(LTCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $< + @-sed -e 's/^\([^:]*\)\.o[ ]*:/\1.lo \1.o :/' \ + < .deps/$(*F).pp > .deps/$(*F).P; \ + tr ' ' '\012' < .deps/$(*F).pp \ + | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \ + >> .deps/$(*F).P; \ + rm -f .deps/$(*F).pp +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: install-libLTLIBRARIES +install-exec: install-exec-am + +install-data-am: +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: uninstall-libLTLIBRARIES +uninstall: uninstall-am +all-am: Makefile $(LTLIBRARIES) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + $(mkinstalldirs) $(DESTDIR)$(libdir) + + +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-libLTLIBRARIES mostlyclean-compile \ + mostlyclean-libtool mostlyclean-tags mostlyclean-depend \ + mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-libLTLIBRARIES clean-compile clean-libtool clean-tags \ + clean-depend clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-libLTLIBRARIES distclean-compile \ + distclean-libtool distclean-tags distclean-depend \ + distclean-generic clean-am + -rm -f libtool + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-libLTLIBRARIES \ + maintainer-clean-compile maintainer-clean-libtool \ + maintainer-clean-tags maintainer-clean-depend \ + maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: mostlyclean-libLTLIBRARIES distclean-libLTLIBRARIES \ +clean-libLTLIBRARIES maintainer-clean-libLTLIBRARIES \ +uninstall-libLTLIBRARIES install-libLTLIBRARIES mostlyclean-compile \ +distclean-compile clean-compile maintainer-clean-compile \ +mostlyclean-libtool distclean-libtool clean-libtool \ +maintainer-clean-libtool tags mostlyclean-tags distclean-tags \ +clean-tags maintainer-clean-tags distdir mostlyclean-depend \ +distclean-depend clean-depend maintainer-clean-depend info-am info \ +dvi-am dvi check check-am installcheck-am installcheck install-exec-am \ +install-exec install-data-am install-data install-am install \ +uninstall-am uninstall all-redirect all-am all installdirs \ +mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean + + +all: LibicalWrap.so + +LibicalWrap.c: LibicalWrap.i + swig -python -o LibicalWrap.c LibicalWrap.i + +# This part should be done with libtool, but I don't know how to do +# it. Libtool needs to generate a shared library in this directory +# regardless of the value of AM_DISABLE_SHARED +LibicalWrap.so: LibicalWrap.c + ld -shared -o LibicalWrap.so LibicalWrap.o ../libical/.libs/libical.a ../libicalss/.libs/libicalss.a + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/libical/src/python/Makefile.am b/libical/src/python/Makefile.am new file mode 100644 index 0000000000..8eaf4a74cb --- /dev/null +++ b/libical/src/python/Makefile.am @@ -0,0 +1,42 @@ + +lib_LTLIBRARIES = libLibicalWrap.la + +libLibicalWrap_la_SOURCES = LibicalWrap.c + +INCLUDES = \ + -I$(top_builddir) \ + -I$(top_srcdir)/src \ + -I$(top_builddir)/src \ + -I$(top_srcdir)/src/libical \ + -I$(top_builddir)/src/libical \ + -I$(top_srcdir)/src/libicalss \ + $(PY_CFLAGS) + +LDADD = ../libical/libical.la ../libicalss/libicalss.la + +all: LibicalWrap.so + +LibicalWrap.c: LibicalWrap.i + swig -python -o LibicalWrap.c LibicalWrap.i + +# This part should be done with libtool, but I don't know how to do +# it. Libtool needs to generate a shared library in this directory +# regardless of the value of AM_DISABLE_SHARED +LibicalWrap.so: LibicalWrap.c + ld -shared -o LibicalWrap.so LibicalWrap.o ../libical/.libs/libical.a ../libicalss/.libs/libicalss.a + +CLEANFILES = LibicalWrap.c LibicalWrap_wrap.doc Libical.pyc LibicalWrap.so + +EXTRA_DIST = \ +Libical.py \ +LibicalWrap.i \ +python-binding.txt \ +test.py \ +Collection.py \ +Component.py \ +DerivedProperties.py \ +Property.py \ +Store.py \ +ChangeLog + + diff --git a/libical/src/python/Makefile.in b/libical/src/python/Makefile.in new file mode 100644 index 0000000000..ec74701b19 --- /dev/null +++ b/libical/src/python/Makefile.in @@ -0,0 +1,369 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AR = @AR@ +AS = @AS@ +CC = @CC@ +DLLTOOL = @DLLTOOL@ +LEX = @LEX@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +OBJDUMP = @OBJDUMP@ +PACKAGE = @PACKAGE@ +PERL = @PERL@ +PY_CFLAGS = @PY_CFLAGS@ +PY_EXTRA_LIBS = @PY_EXTRA_LIBS@ +PY_LIBS = @PY_LIBS@ +PY_LIB_LOC = @PY_LIB_LOC@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +YACC = @YACC@ +python_val = @python_val@ + +lib_LTLIBRARIES = libLibicalWrap.la + +libLibicalWrap_la_SOURCES = LibicalWrap.c + +INCLUDES = -I$(top_builddir) -I$(top_srcdir)/src -I$(top_builddir)/src -I$(top_srcdir)/src/libical -I$(srcdir)/libical -I$(top_srcdir)/src/libicalss -I$(srcdir)/libicalss $(PY_CFLAGS) + + +LDADD = ../libical/libical.la ../libicalss/libicalss.la + +CLEANFILES = LibicalWrap.c LibicalWrap_wrap.doc Libical.pyc LibicalWrap.so + +EXTRA_DIST = Libical.py LibicalWrap.i python-binding.txt test.py Collection.py Component.py DerivedProperties.py Property.py Store.py ChangeLog + +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../../config.h +CONFIG_CLEAN_FILES = +LTLIBRARIES = $(lib_LTLIBRARIES) + + +DEFS = @DEFS@ -I. -I$(srcdir) -I../.. +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +libLibicalWrap_la_LDFLAGS = +libLibicalWrap_la_LIBADD = +libLibicalWrap_la_OBJECTS = LibicalWrap.lo +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +DIST_COMMON = ChangeLog Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = gtar +GZIP_ENV = --best +DEP_FILES = .deps/LibicalWrap.P +SOURCES = $(libLibicalWrap_la_SOURCES) +OBJECTS = $(libLibicalWrap_la_OBJECTS) + +all: all-redirect +.SUFFIXES: +.SUFFIXES: .S .c .lo .o .s +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu src/python/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(BUILT_SOURCES) + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +mostlyclean-libLTLIBRARIES: + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + +distclean-libLTLIBRARIES: + +maintainer-clean-libLTLIBRARIES: + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(libdir) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + echo "$(LIBTOOL) --mode=install $(INSTALL) $$p $(DESTDIR)$(libdir)/$$p"; \ + $(LIBTOOL) --mode=install $(INSTALL) $$p $(DESTDIR)$(libdir)/$$p; \ + else :; fi; \ + done + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/$$p; \ + done + +.s.o: + $(COMPILE) -c $< + +.S.o: + $(COMPILE) -c $< + +mostlyclean-compile: + -rm -f *.o core *.core + +clean-compile: + +distclean-compile: + -rm -f *.tab.c + +maintainer-clean-compile: + +.s.lo: + $(LIBTOOL) --mode=compile $(COMPILE) -c $< + +.S.lo: + $(LIBTOOL) --mode=compile $(COMPILE) -c $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + +maintainer-clean-libtool: + +libLibicalWrap.la: $(libLibicalWrap_la_OBJECTS) $(libLibicalWrap_la_DEPENDENCIES) + $(LINK) -rpath $(libdir) $(libLibicalWrap_la_LDFLAGS) $(libLibicalWrap_la_OBJECTS) $(libLibicalWrap_la_LIBADD) $(LIBS) + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = src/python + +distdir: $(DISTFILES) + here=`cd $(top_builddir) && pwd`; \ + top_distdir=`cd $(top_distdir) && pwd`; \ + distdir=`cd $(distdir) && pwd`; \ + cd $(top_srcdir) \ + && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnu src/python/Makefile + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$d/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done + +DEPS_MAGIC := $(shell mkdir .deps > /dev/null 2>&1 || :) + +-include $(DEP_FILES) + +mostlyclean-depend: + +clean-depend: + +distclean-depend: + -rm -rf .deps + +maintainer-clean-depend: + +%.o: %.c + @echo '$(COMPILE) -c $<'; \ + $(COMPILE) -Wp,-MD,.deps/$(*F).pp -c $< + @-cp .deps/$(*F).pp .deps/$(*F).P; \ + tr ' ' '\012' < .deps/$(*F).pp \ + | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \ + >> .deps/$(*F).P; \ + rm .deps/$(*F).pp + +%.lo: %.c + @echo '$(LTCOMPILE) -c $<'; \ + $(LTCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $< + @-sed -e 's/^\([^:]*\)\.o[ ]*:/\1.lo \1.o :/' \ + < .deps/$(*F).pp > .deps/$(*F).P; \ + tr ' ' '\012' < .deps/$(*F).pp \ + | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \ + >> .deps/$(*F).P; \ + rm -f .deps/$(*F).pp +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: install-libLTLIBRARIES +install-exec: install-exec-am + +install-data-am: +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: uninstall-libLTLIBRARIES +uninstall: uninstall-am +all-am: Makefile $(LTLIBRARIES) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + $(mkinstalldirs) $(DESTDIR)$(libdir) + + +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-libLTLIBRARIES mostlyclean-compile \ + mostlyclean-libtool mostlyclean-tags mostlyclean-depend \ + mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-libLTLIBRARIES clean-compile clean-libtool clean-tags \ + clean-depend clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-libLTLIBRARIES distclean-compile \ + distclean-libtool distclean-tags distclean-depend \ + distclean-generic clean-am + -rm -f libtool + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-libLTLIBRARIES \ + maintainer-clean-compile maintainer-clean-libtool \ + maintainer-clean-tags maintainer-clean-depend \ + maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: mostlyclean-libLTLIBRARIES distclean-libLTLIBRARIES \ +clean-libLTLIBRARIES maintainer-clean-libLTLIBRARIES \ +uninstall-libLTLIBRARIES install-libLTLIBRARIES mostlyclean-compile \ +distclean-compile clean-compile maintainer-clean-compile \ +mostlyclean-libtool distclean-libtool clean-libtool \ +maintainer-clean-libtool tags mostlyclean-tags distclean-tags \ +clean-tags maintainer-clean-tags distdir mostlyclean-depend \ +distclean-depend clean-depend maintainer-clean-depend info-am info \ +dvi-am dvi check check-am installcheck-am installcheck install-exec-am \ +install-exec install-data-am install-data install-am install \ +uninstall-am uninstall all-redirect all-am all installdirs \ +mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean + + +all: LibicalWrap.so + +LibicalWrap.c: LibicalWrap.i + swig -python -o LibicalWrap.c LibicalWrap.i + +# This part should be done with libtool, but I don't know how to do +# it. Libtool needs to generate a shared library in this directory +# regardless of the value of AM_DISABLE_SHARED +LibicalWrap.so: LibicalWrap.c + ld -shared -o LibicalWrap.so LibicalWrap.o ../libical/.libs/libical.a ../libicalss/.libs/libicalss.a + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: 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 + + diff --git a/libical/src/python/Store.py b/libical/src/python/Store.py new file mode 100644 index 0000000000..6d13bd5211 --- /dev/null +++ b/libical/src/python/Store.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +#====================================================================== +# FILE: Store.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 Error import LibicalError +from Component import Component + +class Store: + """ + Base class for several component storage methods + """ + + class AddFailedError(LibicalError): + "Failed to add a property to the file store" + + class ConstructorFailedError(LibicalError): + "Failed to create a Store " + + def __init__(self): + pass + + def path(self): + pass + + def mark(self): + pass + + def commit(self): + pass + + def add_component(self, comp): + pass + + def remove_component(self, comp): + pass + + def count_components(self, kind): + pass + + def select(self, gauge): + pass + + def clearSelect(self): + pass + + def fetch(self, uid): + pass + + def fetchMatch(self, comp): + pass + + def modify(self, oldc, newc): + pass + + def current_component(self): + pass + + def first_component(self): + pass + + def next_component(self): + pass + + +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) + + e1=icalerror_supress("FILE") + self._ref = icalfileset_new_open(file,_flags,flags) + icalerror_restore("FILE",e1) + + print self._ref + + if self._ref == None or self._ref == 'NULL': + raise Store.ConstructorFailedError(file) + + def __del__(self): + icalfileset_free(self._ref) + + def path(self): + return icalfileset_path(self._ref) + + def mark(self): + icalfileset_mark(self._ref) + + def commit(self): + icalfileset_commit(self._ref) + + def add_component(self, comp): + if not isinstance(comp,Component): + raise Store.AddFailedError("Argument is not a component") + + error = icalfileset_add_component(self._ref,comp) + + 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) + + + + def count_components(self, kind): + pass + + def select(self, gauge): + pass + + def clearSelect(self): + pass + + def fetch(self, uid): + pass + + def fetchMatch(self, comp): + pass + + def modify(self, oldc, newc): + pass + + def current_component(self): + comp_ref = icalfileset_get_current_component(self._ref) + + if comp_ref == None: + return None + + return Component(ref=comp_ref) + + def first_component(self): + comp_ref = icalfileset_get_first_component(self._ref) + + if comp_ref == None: + return None + + return Component(ref=comp_ref) + + def next_component(self): + + comp_ref = icalfileset_get_next_component(self._ref) + + if comp_ref == None: + return None + + return Component(ref=comp_ref) + diff --git a/libical/src/python/python-binding.txt b/libical/src/python/python-binding.txt new file mode 100644 index 0000000000..7fc9e066bf --- /dev/null +++ b/libical/src/python/python-binding.txt @@ -0,0 +1,434 @@ + +Classes for python binding to libical +(Indentation indicates inheritance) + + Component + Event + JournalEntry + Todo + FreeBusy + Timezone + Alarm + AudioAlarm + EmailAlarm + ProcedureAlarm + DisplayAlarm + + Property + Attendee + Organizer + Status + Error + + Time + Period + Date + RecurrenceSet + + Timezone + TimezonePhase + + Store + FileStore + DirStore + CAPStore + HeapStore + MySQLStore + + + +Component is the central class in the design. The component can be +though of as a container for child components and properties, or as +representation of a specific kind of iCal object. The first +interface offers general property and component accessors, and the +second treats some types of objects in a special way. + +The general interface offers simple manipulators for child property +and components, and it only works with immediate children. So, given +the Component: + + BEGIN:VCALENDAR + METHOD:PUBLISH + BEGIN:VEVENT + BEGIN:VALARM + COMMENT: An Alarm + END:VALARM + END:VEVENT + END:VCALENDAR + +A caller would have to descend three levels to access the COMMENT +property in the alarm, but only one to access the METHOD property. + +Libical is almost entirely dedicated to the general interface; it +includes all of the *get_first_*, *_next_next_*, *_add_* and *_remove_* +routines. + +The specific interface works with derived classes of Property and +Component. In this interface, the caller could get the COMMENT in the +alarm in the example in two steps. First the caller would ask the +Event object for its alarms, then then the caller would ask the first +Alarm object for its comment. + +The Specific interface is part of the +derived classes of Component. This interface names all of the types of +properties and components that can be accessed from the derived +component type. + +In libical, the specific interface is primarily the "convenience +routines in icalcomponent.h, such as: + + + struct icaltimetype icalcomponent_get_dtend(icalcomponent* comp); + void icalcomponent_set_dtend(icalcomponent* comp, struct icaltimetype v); + + void icalcomponent_set_duration(icalcomponent* comp, + struct icaldurationtype v); + struct icaldurationtype icalcomponent_get_duration(icalcomponent* comp); + +These routines are smarter and do more work than the general +interface. From the general interface, you can set the DTEND +property of a component by creating the property and adding it to a +component. But, if the component already has a DURATION property, then +this is an error -- a component can't have both. + +icalcomponent_set_dtend determines if the component already has a +DURATION. If it does, it substracts the dtstart time from the new +dtend time and sets the duration to that. Otherwise, it creates aor +changes the DTEND. + +Also, icalcomponent_set_duration works the same regardless if the +component is a VCALENDAR or a VEVENT. If it is a VCALENDAR, the +routine descends into the VEVENT before making any changes. If it is +allready a VEVENT ( or VTODO or VJOURNAL ) the routine just makes the +changes. With icalcomponent_add_property, you need to do this check +yourself. + +( There should probably be a class between Component +and Event ,JournalEntry and Todo that names all of the properties and +components. Then Event, JournalEntry and Todo would remove the +accessors that did not apply to them. ) + +Parameters are accessed as dictionaries from Property or as attributes +of a derived class of Property. Both the names and the values of the +parameters are strings. + +The Store hierarchy is a interface to various ways to store iCal +components. Each of these has the same interface as its corresponding +libical module: + + FileStore icalfileset Store iCal components in a single file + DirStore icaldirset Use multiple files, one per month of + DTSTART + CAPStore Access components in a CAP server + HeapStore Components stored in memory + MySQLStore Components stored in a MySQL + database. + +The only iCal value objects that will be implemented as Python classes +are Time, Duration and Period. RecurrenceSet handles recurrence +rules. It replaces the properties RRULE, RDATE, EXRULE and EXDATE. + +(Not all of the libical modules are implemented ) + + +How to Use the library +---------------------- + +The most common usecases will be something like this: + +1)Caller opens a Store object on a file, server or database. The +caller retrieves one or more component. Some of the components will be +booked on the user's calendar. Other components will be messages that +other users have sent, like requests for meetings. + +2) The caller will use the Specific interface ( using methods specific +to each property ) to example the component and decide what to do with +it. + +3) Rarely, the caller will access the general interface to do things +that the specific interface has not implemented or cannot manage +well. + +4) Cller 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. + + +Following are the methods in each of the classes. + +Component + Construct from string + output string in ical form + + Get a Property by type + Get a set of Properties by type + Remove a Property by reference + + Get a Component by type + Get a set of Components by type + Remove a Component by reference + + Validate the component ( insert properties for errors ) + Count error properties + Remove error properties + Convert error properties into REQUEST-STATUS properties + +Event, JournalEntry, Todo + + Construct from string + output string in ical form + + There are get/set accessors for every property. These are + listed in a later section + + +FreeBusy + + Construct from string + Construct from arguments + output string in ical form + + Accessors for the following properties. See below for return + types for these properties: + + method + prodid + attendee + dtstamp + dtstart + freebusy + organizer + uid + comment + contact + request-status + url + duration + sequence + +TimezonePhase + + Construct from string + Construct from arguments + output string in ical form + + Accessors for the following properties. See below for return + types for these properties: + + tzname + offsetto + offsetfrom + rrule + rdate + comment + +TimeZone + + Construct from string + Construct from arguments + output string in ical form + + Accessors for the following properties. See below for return + types for these properties: + + tzid + last-modified + tzurl + standard (returns TimezonePhase) + daylight (returns TimezonePhase) + + +Property + + Construct from string + Construct from arguments + output string in ical form + + Dictionary access to parameters + + Get/set value + +Attendee + + Construct from string + Construct from arguments + output string in ical form + + Access to the following properties and parameters: + cuid + cutype + member + role + rsvp + delto + delfrom + sentby + cn + dir + language + +Organizer + + Access to the following properties: + common_name + dir + sentby + language + + +Time +Date +Period + + Same interfaces as libical modules. + +RecurenceSet + I don't know -- need to think about it more. + +Store + Similar methods to the icalset modules. + + +RFC2445 Properties + +This is a list of all of the RFC2445 properties and their associated +VALUE type. + + +Property Value +---------------------------- +CALSCALE TEXT +METHOD TEXT +PRODID TEXT +VERSION TEXT +CATEGORIES TEXT +CLASS TEXT +COMMENT TEXT +DESCRIPTION TEXT +LOCATION TEXT +PERCENT-COMPLETE INTEGER +PRIORITY INTEGER +RESOURCES TEXT +STATUS STATUS +SUMMARY TEXT +COMPLETED DATE-TIME +FREEBUSY PERIOD +TRANSP TEXT +TZNAME TEXT +TZOFFSETFROM UTC-OFFSET +TZOFFSETTO UTC-OFFSET +TZURL URI +TZID TEXT +ATTENDEE CAL-ADDRESS +CONTACT TEXT +ORGANIZER CAL-ADDRESS +RELATED-TO TEXT +URL URI +UID TEXT +EXRULE RECUR +RRULE RECUR +ACTION TEXT +REPEAT INTEGER +CREATED DATE-TIME +DTSTAMP DATE-TIME +LAST-MODIFIED DATE-TIME +SEQUENCE INTEGER +X TEXT +REQUEST-STATUS STRING +ATTACH URL, BINARY +GEO FLOAT +DTEND DATE-TIME +DUE DATE-TIME +DTSTART DATE-TIME +RECURRENCE-ID DATE-TIME +EXDATE DATE-TIME +RDATE DATE-TIME, PERIOD +TRIGGER DATE-TIME, DURATION +DURATION DURATION + +Some of the properties can appear multiple times in a component, other +can appear only once. For a particular component, the number of times +a property can apper may also change with the METHOD property +associated with the component. This list shows which properties can +appear multiple times for some ( but not all ) combinations of +component type and METHOD value: + +ATTACH +ATTENDEE +CATEGORIES +COMMENT +CONTACT +EXDATE +EXRULE +FREEBUSY +NONE +RDATE +RELATEDTO +REQUESTSTATUS +RESOURCES +RRULE +TZNAME +TZURL +X + + +This is a list of the value types and their associated python return +types. A lowercased return type is a python builtin, and an +uppercase-first-letter is a class in the library: + + +Value Return Type +----------------------------------- +BINARY file +BOOLEAN number +CAL-ADDRESS string +DATE Time +DATE-TIME Time +DURATION Duration +FLOAT number +GEO tuple +INTEGER number +METHOD string +PERIOD Period +RECUR RecurrenceSet +TEXT string +TIME Time +URI string +UTC-OFFSET number +STATUS string +X string + + +I suspect that the Component class should use the three previous +tables to create the property accessor methods on the fly. If the +method generation system is general enough, we could also use it for +other classes, such as Alarm, Timezone, Attendee, Organizer and +Freebusy. + + +Memory Handling +--------------- + +One of the things that made the perl binding to libcal difficult was +that if a Component (in perl) returns a reference to a child Component +(in perl), the child Component (in perl) will contain a reference to a +libical component. When the caller is done with the child component in +perl, the caller just drops it and lets perl reclaim the memory. + +The destructor for Component needs to free the libical component that +it holds a reference to, but the child's libical component is still +being used by libical. So perl frees the libical component and later, +libical tries to reference freed memory. + +The situation is actually a little better than that, because libical +will throw an error when you try to free a component that is still +attached to a parent. + +So, the perl library has to be able to determine when the destructor +should free a libical component or not. The perl library calls +icalcomponent_get_parent(), and if is it non-zero, it does not free +the component. + +It is not hard, just more code, and something to watch out for. + diff --git a/libical/src/python/test.py b/libical/src/python/test.py new file mode 100644 index 0000000000..1fdf515408 --- /dev/null +++ b/libical/src/python/test.py @@ -0,0 +1,373 @@ +#!/usr/bin/env python +# -*- Mode: python -*- +#====================================================================== +# FILE: test.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 Libical import * + +def error_type(): + error = icalerror_perror() + return error[:index(error,':')] + +comp_str = """ +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 + becomes necessary to insert a newline +DTSTART:19972512T120000 +DTSTART:19970101T120000Z +DTSTART:19970101 +DURATION:P3DT4H25M +FREEBUSY:19970101T120000/19970101T120000 +FREEBUSY:19970101T120000/PT3H +FREEBUSY:19970101T120000/PT3H +END:VEVENT""" + + +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") + + print liw.icalproperty_as_ical_string(icalprop) + + p = Property(ref=icalprop) + + print p.name() + print p.parameters() + print p['ROLE'] + + p['ROLE'] = 'INDIVIDUAL' + + print p['ROLE'] + + print p.value() + p.value("mailto:Bob@bob.com") + print p.value() + + + print p.as_ical_string() + + try: + p = Property() + except Property.ConstructorFailedError: + pass + else: + assert(0) + +def test_time(): + "Test routine" + + print"-------------------Test Time --------------------------------" + + t = Time("19970325T123010Z",'DTSTART') + + assert(t.year() == 1997) + assert(t.month() == 3) + assert(t.day() == 25) + assert(t.hour() == 12) + assert(t.minute() == 30) + assert(t.second() == 10) + assert(t.is_utc()) + assert(not t.is_date()) + + print t + + t.timezone("America/Los_Angeles") + print str(t) + assert(str(t)=='DTSTART;TZID=America/Los_Angeles:19970325T123010Z') + + t.second(t.second()+80) + + print t + + assert(t.minute() == 31) + assert(t.second() == 30) + + d = Duration(3600,"DURATION") + t2 = t + d + + print t2 + assert(t2.hour() == 13) + + t2 = t - d + + print t2 + assert(isinstance(t2,Time)) + assert(t2.hour() == 11) + + +def test_period(): + + print"-------------------Test Period--------------------------------" + + p = Period("19970101T180000Z/19970101T233000Z") + + print p + + + assert(str(p) == 'FREEBUSY:19970101T180000Z/19970101T233000Z') + + print p.start() + assert(str(p.start()) == 'DTSTART:19970101T180000Z') + + print p.end() + assert(str(p.end()) == 'DTEND:19970101T233000Z') + + print p.duration() + assert(str(p.duration()) == 'DURATION:PT5H30M') + p = None + + p = Period("19970101T180000Z/PT5H30M") + print p + + print p.start() + assert(str(p.start()) == 'DTSTART:19970101T180000Z') + + print p.end() + assert(str(p.end()) == 'DTEND:19970101T233000Z') + + print p.duration() + assert(str(p.duration()) == 'DURATION:PT5H30M') + + +def test_duration(): + + print "-------------- Test Duration ----------------" + + # Ical string + + d = Duration("P3DT4H25M") + + print str(d) + + assert(str(d) == "DURATION:P3DT4H25M") + + print d.seconds() + + assert(d.seconds() == 275100) + + # seconds + + d = Duration(-275100) + + print str(d) + + assert(str(d) == "DURATION:-P3DT4H25M") + + print d.seconds() + + assert(d.seconds() == -275100) + + #error + + try: + d = Duration("P10WT7M") + print str(d) + assert(0) + except: pass + + try: + d = Duration("Pgiberish") + print str(d) + assert(0) + except: + pass + + + +def test_attach(): + + file = open('littlefile.txt') + attachProp = Attach(file) + file.close() + attachProp.fmttype('text/ascii') + print "\n" + attachProp.name() + print attachProp.value_type() + print attachProp.fmttype() + attachProp['fmttype']=None + print "Calling value()" + print attachProp.value() + print "Calling asIcalString()" + print attachProp.as_ical_string() + + +def test_component(): + + print "------------------- Test Component ----------------------" + + + c = Component(comp_str); + + props = c.properties() + + for p in props: + print p.as_ical_string() + + dtstart = c.properties('DTSTART')[0] + + print dtstart + + print "\n Orig hour: ", dtstart.hour() + assert(dtstart.hour() == 12) + + dtstart.hour(dtstart.hour() + 5) + + print "\n New hour: ", dtstart.hour() + assert(dtstart.hour() == 17) + + attendee = c.properties('ATTENDEE')[0] + + print attendee + + t = Time("20011111T123030") + t.name('DTEND') + + c.add_property(t) + + + print c + + dtstart1 = c.properties('DTSTART')[0] + dtstart2 = c.properties('DTSTART')[0] + dtstart3 = c.property('DTSTART') + + assert(dtstart1 is dtstart2) + assert(dtstart1 == dtstart2) + + assert(dtstart1 is dtstart3) + assert(dtstart1 == dtstart3) + + + p = Property(type="SUMMARY"); + p.value("This is a summary") + + c.properties().append(p) + + print c.as_ical_string() + + p = c.properties("SUMMARY")[0] + assert(p!=None); + print str(p) + assert(str(p) == "SUMMARY:This is a summary") + + c.properties()[:] = [p] + + print c.as_ical_string() + + +def test_event(): + print "------------ Event Class ----------------------" + event = Event() + event.created("20010313T123000Z") + #print "created =", event.created() + assert (event.created() == "20010313T123000Z") + + event.organizer("MAILTO:j_doe@nowhere.com") + org = event.properties('ORGANIZER')[0] + #print org.cn() + org.cn('Jane Doe') + assert (isinstance(org, Organizer)) + #print "organizer =", event.organizer() + assert (event.organizer() == "MAILTO:j_doe@nowhere.com") + + event.dtstart("20010401T183000Z") + #print "dtstart =", event.dtstart() + assert (event.dtstart()=="20010401T183000Z") + + dtend = Time('20010401T190000Z', 'DTEND') + event.dtend(dtend) + assert (event.dtend()==dtend.value()) + assert (event.dtend() == '20010401T190000Z') + + att = Attendee() + att.value('jsmith@nothere.com') + event.attendees(('ef_hutton@listenup.com', att)) + + event.description("A short description. Longer ones break things.") + event.status('TeNtAtIvE') + + print event.as_ical_string() + + +def test_derivedprop(): + + print "------------ Derived Properties -----------------" + + p = RDate("20011111T123030") + + print str(p) + + + p = RDate("19970101T120000/19970101T123000") + + print str(p) + + try: + p = RDate("P3DT4H25M") + print str(p) + assert(0) + except: pass + + + p = Trigger("P3DT4H25M") + + print str(p) + + p = Trigger("20011111T123030") + + print str(p) + + try: + p = Trigger("19970101T120000/19970101T123000") + print str(p) + assert(0) + except: pass + + +def run_tests(): + test_property() + + test_time() + + test_period() + + test_component() + + test_duration() + + test_derivedprop() + + test_event() + + #test_attach() + + + +if __name__ == "__main__": + run_tests() + -- cgit v1.2.3