diff options
author | JP Rosevear <jpr@src.gnome.org> | 2001-04-18 01:23:25 +0800 |
---|---|---|
committer | JP Rosevear <jpr@src.gnome.org> | 2001-04-18 01:23:25 +0800 |
commit | 0d90c71a6a7960e6220958883885b8486eb2d43a (patch) | |
tree | d4ae4f4e49d5a27ce5b4b20eaad805068de51eab /libical/src/python/Component.py | |
parent | a8ed26a0d30ee93b457da41c1aa0fb04f44db50e (diff) | |
download | gsoc2013-evolution-0d90c71a6a7960e6220958883885b8486eb2d43a.tar.gz gsoc2013-evolution-0d90c71a6a7960e6220958883885b8486eb2d43a.tar.zst gsoc2013-evolution-0d90c71a6a7960e6220958883885b8486eb2d43a.zip |
Initial revision
svn path=/trunk/; revision=9413
Diffstat (limited to 'libical/src/python/Component.py')
-rw-r--r-- | libical/src/python/Component.py | 670 |
1 files changed, 670 insertions, 0 deletions
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 <eric@softwarestudio.org> +# (C) COPYRIGHT 2001, Patrick Lewis <plewis@inetarena.com> +# +# 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) + |