[ADD,IMP]: CALDAV: * Added new objects in base_calendar for attribute-field mapping
[odoo/odoo.git] / addons / base_calendar / base_calendar.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #    
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
19 #
20 ##############################################################################
21
22 from datetime import datetime, timedelta
23 from dateutil import parser
24 from dateutil.rrule import *
25 from osv import osv, fields
26 import pooler
27 import re
28 import vobject
29
30 # O-1  Optional and can come only once
31 # O-n  Optional and can come more than once
32 # R-1  Required and can come only once
33 # R-n  Required and can come more than once
34
35 def uid2openobjectid(cr, uidval, oomodel, rdate):
36     __rege = re.compile(r'OpenObject-([\w|\.]+)_([0-9]+)@(\w+)$')
37     wematch = __rege.match(uidval.encode('utf8'))
38     if not wematch:
39         return (False, None)
40     else:
41         model, id, dbname = wematch.groups()
42         model_obj = pooler.get_pool(cr.dbname).get(model)
43         if (not model == oomodel) or (not dbname == cr.dbname):
44             return (False, None)
45         qry = 'select distinct(id) from %s' % model_obj._table
46         if rdate:
47             qry += " where recurrent_id='%s'" % (rdate)
48             cr.execute(qry)
49             r_id = cr.fetchone()
50             if r_id:
51                 return (id, r_id[0])
52         cr.execute(qry)
53         ids = map(lambda x: str(x[0]), cr.fetchall())
54         if id in ids:
55             return (id, None)
56         return False
57
58 def openobjectid2uid(cr, uidval, oomodel):
59     value = 'OpenObject-%s_%s@%s' % (oomodel, uidval, cr.dbname)
60     return value
61
62 def get_attribute_mapping(cr, uid, context={}):
63         pool = pooler.get_pool(cr.dbname)
64         field_obj = pool.get('basic.calendar.fields')
65         fids = field_obj.search(cr, uid, [])
66         res = {}
67         for field in field_obj.browse(cr, uid, fids):
68             attr = field.attribute
69             res[attr] = {}
70             res[attr]['field'] = field.field_id.name
71             res[attr]['type'] = field.field_id.ttype
72             if res[attr]['type'] in ('one2many', 'many2many', 'many2one'):
73                 res[attr]['object'] = field.field_id.relation
74             elif res[attr]['type'] in ('selection'):
75                 res[attr]['mapping'] = field.info
76         return res
77     
78 def map_data(cr, uid, obj):
79     vals = {}
80     for map_dict in obj.__attribute__:
81         map_val = obj.ical_get(map_dict, 'value')
82         field = obj.ical_get(map_dict, 'field')
83         field_type = obj.ical_get(map_dict, 'type')
84         if field:
85             if field_type == 'selection':
86                 if not map_val:
87                     continue
88                 mapping = obj.__attribute__[map_dict].get('mapping', False)
89                 if mapping:
90                     map_val = mapping[map_val.lower()]
91                 else:
92                     map_val = map_val.lower()
93             if field_type == 'many2many':
94                 ids = []
95                 if not map_val:
96                     vals[field] = ids
97                     continue
98                 model = obj.__attribute__[map_dict].get('object', False)
99                 modobj = obj.pool.get(model)
100                 for map_vall in map_val:
101                     id = modobj.create(cr, uid, map_vall)
102                     ids.append(id)
103                 vals[field] = [(6, 0, ids)]
104                 continue
105             if field_type == 'many2one':
106                 id = None
107                 if not map_val or not isinstance(map_val, dict):
108                     vals[field] = id
109                     continue
110                 model = obj.__attribute__[map_dict].get('object', False)
111                 modobj = obj.pool.get(model)
112                 id = modobj.create(cr, uid, map_val)
113                 vals[field] = id
114                 continue
115             if map_val:
116                 vals[field] = map_val
117     return vals
118
119 class CalDAV(object):
120     __attribute__ = {
121     }
122     def get_recurrent_dates(self, rrulestring, exdate, startdate=None):
123         if not startdate:
124             startdate = datetime.now()
125         rset1 = rrulestr(rrulestring, dtstart=startdate, forceset=True)
126
127         for date in exdate:
128             datetime_obj = todate(date)
129             rset1._exdate.append(datetime_obj)
130         re_dates = map(lambda x:x.strftime('%Y-%m-%d %H:%M:%S'), rset1._iter())
131         return re_dates
132
133     def ical_set(self, name, value, type):
134         if name in self.__attribute__ and self.__attribute__[name]:
135             self.__attribute__[name][type] = value
136         return True
137
138     def ical_get(self, name, type):
139         if self.__attribute__.get(name):
140             val = self.__attribute__.get(name).get(type, None)
141             valtype =  self.__attribute__.get(name).get('type', None)
142             if type == 'value':
143                 if valtype and valtype == 'datetime' and val:
144                     if isinstance(val, list):
145                         val = ','.join(map(lambda x: x.strftime('%Y-%m-%d %H:%M:%S'), val))
146                     else:
147                         val = val.strftime('%Y-%m-%d %H:%M:%S')
148                 if valtype and valtype == 'integer' and val:
149                     val = int(val)
150             return  val
151         else:
152             return  self.__attribute__.get(name, None)
153
154     def ical_reset(self, type):
155         for name in self.__attribute__:
156             if self.__attribute__[name]:
157                 self.__attribute__[name][type] = None
158         return True
159
160     def export_ical(self, cr, uid, datas, vobj=None, context={}):
161         ical = vobject.iCalendar()
162         for data in datas:
163             vevent = ical.add(vobj)
164             for field in self.__attribute__.keys():
165                 map_field = self.ical_get(field, 'field')
166                 map_type = self.ical_get(field, 'type')
167                 if map_field in data.keys():
168                     if field == 'uid':
169                         model = context.get('model', None)
170                         if not model:
171                             continue
172                         uidval = openobjectid2uid(cr, data[map_field], model)
173                         model_obj = self.pool.get(model)
174                         cr.execute('select id from %s  where recurrent_uid=%s' 
175                                                % (model_obj._table, data[map_field]))
176                         r_ids = map(lambda x: x[0], cr.fetchall())
177                         if r_ids: 
178                             rdata = self.pool.get(model).read(cr, uid, r_ids)
179                             rcal = self.export_ical(cr, uid, rdata, context=context)
180                             for revents in rcal.contents['vevent']:
181                                 ical.contents['vevent'].append(revents)
182                         if data.get('recurrent_uid', None):
183                             uidval = openobjectid2uid(cr, data['recurrent_uid'], model)
184                         vevent.add('uid').value = uidval
185                     elif field == 'attendee' and data[map_field]:
186                         model = self.__attribute__[field].get('object', False)
187                         attendee_obj = self.pool.get('basic.calendar.attendee')
188                         vevent = attendee_obj.export_ical(cr, uid, model, \
189                                      data[map_field], vevent, context=context)
190                     elif field == 'valarm' and data[map_field]:
191                         model = self.__attribute__[field].get('object', False)
192                         alarm_obj = self.pool.get('basic.calendar.alarm')
193                         vevent = alarm_obj.export_ical(cr, uid, model, \
194                                     data[map_field][0], vevent, context=context)
195                     elif data[map_field]:
196                         if map_type == "text":
197                             vevent.add(field).value = str(data[map_field])
198                         elif map_type == 'datetime' and data[map_field]:
199                             if field in ('exdate'):
200                                 vevent.add(field).value = [parser.parse(data[map_field])]
201                             else:
202                                 vevent.add(field).value = parser.parse(data[map_field])
203                         elif map_type == "timedelta":
204                             vevent.add(field).value = timedelta(hours=data[map_field])
205                         elif map_type == "many2one":
206                             vevent.add(field).value = [data.get(map_field)[1]]
207                         if self.__attribute__.get(field).has_key('mapping'):
208                             for key1, val1 in self.ical_get(field, 'mapping').items():
209                                 if val1 == data[map_field]:
210                                     vevent.add(field).value = key1
211         return ical
212
213     def import_ical(self, cr, uid, ical_data):
214         parsedCal = vobject.readOne(ical_data)
215         att_data = []
216         res = []
217         for child in parsedCal.getChildren():
218             for cal_data in child.getChildren():
219                 if cal_data.name.lower() == 'attendee':
220                     attendee = self.pool.get('basic.calendar.attendee')
221                     att_data.append(attendee.import_ical(cr, uid, cal_data))
222                     self.ical_set(cal_data.name.lower(), att_data, 'value')
223                     continue
224                 if cal_data.name.lower() == 'valarm':
225                     alarm = self.pool.get('basic.calendar.alarm')
226                     vals = alarm.import_ical(cr, uid, cal_data)
227                     self.ical_set(cal_data.name.lower(), vals, 'value')
228                     continue
229                 if cal_data.name.lower() in self.__attribute__:
230                     self.ical_set(cal_data.name.lower(), cal_data.value, 'value')
231             if child.name.lower() in ('vevent', 'vtodo'):
232                 vals = map_data(cr, uid, self)
233             else:
234                 vals = {}
235                 continue
236             if vals: res.append(vals)
237             self.ical_reset('value')
238         return res
239
240
241 class Calendar(CalDAV, osv.osv_memory):
242     _name = 'basic.calendar'
243     __attribute__ = {
244         'prodid': None, # Use: R-1, Type: TEXT, Specifies the identifier for the product that created the iCalendar object.
245         'version': None, # Use: R-1, Type: TEXT, Specifies the identifier corresponding to the highest version number
246                            #             or the minimum and maximum range of the iCalendar specification
247                            #             that is required in order to interpret the iCalendar object.
248         'calscale': None, # Use: O-1, Type: TEXT, Defines the calendar scale used for the calendar information specified in the iCalendar object.
249         'method': None, # Use: O-1, Type: TEXT, Defines the iCalendar object method associated with the calendar object.
250         'vevent': None, # Use: O-n, Type: Collection of Event class
251         'vtodo': None, # Use: O-n, Type: Collection of ToDo class
252         'vjournal': None, # Use: O-n, Type: Collection of Journal class
253         'vfreebusy': None, # Use: O-n, Type: Collection of FreeBusy class
254         'vtimezone': None, # Use: O-n, Type: Collection of Timezone class
255     }
256
257 Calendar()
258
259
260 class basic_calendar_fields_type(osv.osv):
261     _name = 'basic.calendar.fields.type'
262     _description = 'Calendar fields type'
263
264     _columns = {
265                 'name': fields.char('Name', size=64), 
266                 'object_id': fields.many2one('ir.model', 'Object'), 
267                 }
268
269 basic_calendar_fields_type()
270
271 class basic_calendar_fields(osv.osv):
272     _name = 'basic.calendar.fields'
273     _description = 'Calendar fields'
274     _rec_name = 'attribute_id'
275
276     _columns = {
277             'attribute_id': fields.many2one('basic.calendar.fields.type', \
278                                     'Attribute', size=64), 
279             'attribute': fields.related('attribute_id', 'name', size=64, \
280                                  type='char', string='Attribute Name', \
281                                  store=True), 
282             'object_id': fields.related('attribute_id', 'object_id', \
283                              type='many2one', relation='ir.model', store=True,\
284                              string='Object'), 
285             'field_id': fields.many2one('ir.model.fields', 'OpenObject Field'), 
286             'info': fields.text('Other info'), 
287             'value': fields.text('Value', help="For some attribute that \
288 have some default value"), 
289             }
290
291 basic_calendar_fields()
292
293 class Event(CalDAV, osv.osv_memory):
294     _name = 'basic.calendar.event'
295     __attribute__ = {
296         'class': None, # Use: O-1, Type: TEXT, Defines the access classification for a calendar  component like "PUBLIC" / "PRIVATE" / "CONFIDENTIAL"
297         'created': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that the calendar information  was created by the calendar user agent in the calendar store.
298         'description': None, # Use: O-1, Type: TEXT, Provides a more complete description of the calendar component, than that provided by the "SUMMARY" property.
299         'dtstart': None, # Use: O-1, Type: DATE-TIME, Specifies when the calendar component begins.
300         'geo': None, # Use: O-1, Type: FLOAT, Specifies information related to the global position for the activity specified by a calendar component.
301         'last-mod': None, # Use: O-1, Type: DATE-TIME        Specifies the date and time that the information associated with the calendar component was last revised in the calendar store.
302         'location': None, # Use: O-1, Type: TEXT            Defines the intended venue for the activity defined by a calendar component.
303         'organizer': None, # Use: O-1, Type: CAL-ADDRESS, Defines the organizer for a calendar component.
304         'priority': None, # Use: O-1, Type: INTEGER, Defines the relative priority for a calendar component.
305         'dtstamp': None, # Use: O-1, Type: DATE-TIME, Indicates the date/time that the instance of the iCalendar object was created.
306         'seq': None, # Use: O-1, Type: INTEGER, Defines the revision sequence number of the calendar component within a sequence of revision.
307         'status': None, # Use: O-1, Type: TEXT, Defines the overall status or confirmation for the calendar component.
308         'summary': None, # Use: O-1, Type: TEXT, Defines a short summary or subject for the calendar component.
309         'transp': None, # Use: O-1, Type: TEXT, Defines whether an event is transparent or not to busy time searches.
310         'uid': None, # Use: O-1, Type: TEXT, Defines the persistent, globally unique identifier for the calendar component.
311         'url': None, # Use: O-1, Type: URL, Defines a Uniform Resource Locator (URL) associated with the iCalendar object.
312         'recurid': None, 
313         'attach': None, # Use: O-n, Type: BINARY, Provides the capability to associate a document object with a calendar component.
314         'attendee': None, # Use: O-n, Type: CAL-ADDRESS, Defines an "Attendee" within a calendar component.
315         'categories': None, # Use: O-n, Type: TEXT, Defines the categories for a calendar component.
316         'comment': None, # Use: O-n, Type: TEXT, Specifies non-processing information intended to provide a comment to the calendar user.
317         'contact': None, # Use: O-n, Type: TEXT, Used to represent contact information or alternately a  reference to contact information associated with the calendar component.
318         'exdate': None, # Use: O-n, Type: DATE-TIME, Defines the list of date/time exceptions for a recurring calendar component.
319         'exrule': None, # Use: O-n, Type: RECUR, Defines a rule or repeating pattern for an exception to a recurrence set.
320         'rstatus': None, 
321         'related': None, # Use: O-n, Specify the relationship of the alarm trigger with respect to the start or end of the calendar component.
322                                 #  like A trigger set 5 minutes after the end of the event or to-do.---> TRIGGER;related=END:PT5M
323         'resources': None, # Use: O-n, Type: TEXT, Defines the equipment or resources anticipated for an activity specified by a calendar entity like RESOURCES:EASEL,PROJECTOR,VCR, LANGUAGE=fr:1 raton-laveur
324         'rdate': None, # Use: O-n, Type: DATE-TIME, Defines the list of date/times for a recurrence set.
325         'rrule': None, # Use: O-n, Type: RECUR, Defines a rule or repeating pattern for recurring events, to-dos, or time zone definitions.
326         'x-prop': None, 
327         'duration': None, # Use: O-1, Type: DURATION, Specifies a positive duration of time.
328         'dtend': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that a calendar component ends.
329     }
330     def export_ical(self, cr, uid, datas, vobj='vevent', context={}):
331         return super(Event, self).export_ical(cr, uid, datas, 'vevent', context=context)
332
333 Event()
334
335 class ToDo(CalDAV, osv.osv_memory):
336     _name = 'basic.calendar.todo'
337
338     __attribute__ = {
339                 'class': None, 
340                 'completed': None, 
341                 'created': None, 
342                 'description': None, 
343                 'dtstamp': None, 
344                 'dtstart': None, 
345                 'duration': None, 
346                 'due': None, 
347                 'geo': None, 
348                 'last-mod ': None, 
349                 'location': None, 
350                 'organizer': None, 
351                 'percent': None, 
352                 'priority': None, 
353                 'recurid': None, 
354                 'seq': None, 
355                 'status': None, 
356                 'summary': None, 
357                 'uid': None, 
358                 'url': None, 
359                 'attach': None, 
360                 'attendee': None, 
361                 'categories': None, 
362                 'comment': None, 
363                 'contact': None, 
364                 'exdate': None, 
365                 'exrule': None, 
366                 'rstatus': None, 
367                 'related': None, 
368                 'resources': None, 
369                 'rdate': None, 
370                 'rrule': None, 
371             }
372
373     def export_ical(self, cr, uid, datas, vobj='vevent', context={}):
374         return super(ToDo, self).export_ical(cr, uid, datas, 'vtodo', context=context)
375
376 ToDo()
377
378 class Journal(CalDAV):
379     __attribute__ = {
380     }
381
382 class FreeBusy(CalDAV):
383     __attribute__ = {
384     'contact': None, # Use: O-1, Type: Text, Represent contact information or alternately a  reference to contact information associated with the calendar component.
385     'dtstart': None, # Use: O-1, Type: DATE-TIME, Specifies when the calendar component begins.
386     'dtend': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that a calendar component ends.
387     'duration': None, # Use: O-1, Type: DURATION, Specifies a positive duration of time.
388     'dtstamp': None, # Use: O-1, Type: DATE-TIME, Indicates the date/time that the instance of the iCalendar object was created.
389     'organizer': None, # Use: O-1, Type: CAL-ADDRESS, Defines the organizer for a calendar component.
390     'uid': None, # Use: O-1, Type: Text, Defines the persistent, globally unique identifier for the calendar component.
391     'url': None, # Use: O-1, Type: URL, Defines a Uniform Resource Locator (URL) associated with the iCalendar object.
392     'attendee': None, # Use: O-n, Type: CAL-ADDRESS, Defines an "Attendee" within a calendar component.
393     'comment': None, # Use: O-n, Type: TEXT, Specifies non-processing information intended to provide a comment to the calendar user.
394     'freebusy': None, # Use: O-n, Type: PERIOD, Defines one or more free or busy time intervals.
395     'rstatus': None, 
396     'X-prop': None, 
397     }
398
399
400 class Timezone(CalDAV):
401     __attribute__ = {
402     'tzid': None, # Use: R-1, Type: Text, Specifies the text value that uniquely identifies the "VTIMEZONE" calendar component.
403     'last-mod': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that the information associated with the calendar component was last revised in the calendar store.
404     'tzurl': None, # Use: O-1, Type: URI, Provides a means for a VTIMEZONE component to point to a network location that can be used to retrieve an up-to-date version of itself.
405     'standardc': {'tzprop': None}, # Use: R-1,
406     'daylightc': {'tzprop': None}, # Use: R-1,
407     'x-prop': None, # Use: O-n, Type: Text,
408     }
409
410
411 class Alarm(CalDAV, osv.osv_memory):
412     _name = 'basic.calendar.alarm'
413     __attribute__ = {
414     'action': None, # Use: R-1, Type: Text, defines the action to be invoked when an alarm is triggered LIKE "AUDIO" / "DISPLAY" / "EMAIL" / "PROCEDURE"
415     'description': None, #      Type: Text, Provides a more complete description of the calendar component, than that provided by the "SUMMARY" property. Use:- R-1 for DISPLAY,Use:- R-1 for EMAIL,Use:- R-1 for PROCEDURE
416     'summary': None, # Use: R-1, Type: Text        Which contains the text to be used as the message subject. Use for EMAIL
417     'attendee': None, # Use: R-n, Type: CAL-ADDRESS, Contain the email address of attendees to receive the message. It can also include one or more. Use for EMAIL
418     'trigger': None, # Use: R-1, Type: DURATION, The "TRIGGER" property specifies a duration prior to the start of an event or a to-do. The "TRIGGER" edge may be explicitly set to be relative to the "START" or "END" of the event or to-do with the "related" parameter of the "TRIGGER" property. The "TRIGGER" property value type can alternatively be set to an absolute calendar date and time of day value. Use for all action like AUDIO, DISPLAY, EMAIL and PROCEDURE
419     'duration': None, #           Type: DURATION, Duration' and 'repeat' are both optional, and MUST NOT occur more than once each, but if one occurs, so MUST the other. Use:- 0-1 for AUDIO, EMAIL and PROCEDURE, Use:- 0-n for DISPLAY
420     'repeat': None, #           Type: INTEGER, Duration' and 'repeat' are both optional, and MUST NOT occur more than once each, but if one occurs, so MUST the other. Use:- 0-1 for AUDIO, EMAIL and PROCEDURE, Use:- 0-n for DISPLAY
421     'attach': None, # Use:- O-n: which MUST point to a sound resource, which is rendered when the alarm is triggered for AUDIO, Use:- O-n: which are intended to be sent as message attachments for EMAIL, Use:- R-1:which MUST point to a procedure resource, which is invoked when the alarm is triggered for PROCEDURE.
422     'x-prop': None, 
423     }
424
425     def export_ical(self, cr, uid, model, alarm_id, vevent, context={}):
426         valarm = vevent.add('valarm')
427         alarm_object = self.pool.get(model)
428         alarm_data = alarm_object.read(cr, uid, alarm_id, [])
429
430         # Compute trigger data
431         interval = alarm_data['trigger_interval']
432         occurs = alarm_data['trigger_occurs']
433         duration = (occurs == 'after' and alarm_data['trigger_duration']) \
434                                         or -(alarm_data['trigger_duration'])
435         related = alarm_data['trigger_related']
436         trigger = valarm.add('TRIGGER')
437         trigger.params['related'] = [related.upper()]
438         if interval == 'days':
439             delta = timedelta(days=duration)
440         if interval == 'hours':
441             delta = timedelta(hours=duration)
442         if interval == 'minutes':
443             delta = timedelta(minutes=duration)
444         trigger.value = delta
445
446         # Compute other details
447         valarm.add('DESCRIPTION').value = alarm_data['name']
448         valarm.add('ACTION').value = alarm_data['action']
449
450
451     def import_ical(self, cr, uid, ical_data):
452         for child in ical_data.getChildren():
453             if child.name.lower() == 'trigger':
454                 seconds = child.value.seconds
455                 days = child.value.days
456                 diff = (days * 86400) +  seconds
457                 interval = 'days'
458                 related = 'before'
459                 if not seconds:
460                     duration = abs(days)
461                     related = days > 0 and 'after' or 'before'
462                 elif (abs(diff) / 3600) == 0:
463                     duration = abs(diff / 60)
464                     interval = 'minutes'
465                     related = days >= 0 and 'after' or 'before'
466                 else:
467                     duration = abs(diff / 3600)
468                     interval = 'hours'
469                     related = days >= 0 and 'after' or 'before'
470                 self.ical_set('trigger_interval', interval, 'value')
471                 self.ical_set('trigger_duration', duration, 'value')
472                 self.ical_set('trigger_occurs', related.lower(), 'value')
473                 if child.params:
474                     if child.params.get('related'):
475                         self.ical_set('trigger_related', child.params.get('related')[0].lower(), 'value')
476             else:
477                 self.ical_set(child.name.lower(), child.value.lower(), 'value')
478         vals = map_data(cr, uid, self)
479         return vals
480
481 Alarm()
482
483 class Attendee(CalDAV, osv.osv_memory):
484     _name = 'basic.calendar.attendee'
485     __attribute__ = {
486     'cutype': None, # Use: 0-1    Specify the type of calendar user specified by the property like "INDIVIDUAL"/"GROUP"/"RESOURCE"/"ROOM"/"UNKNOWN".
487     'member': None, # Use: 0-1    Specify the group or list membership of the calendar user specified by the property.
488     'role': None, # Use: 0-1    Specify the participation role for the calendar user specified by the property like "CHAIR"/"REQ-PARTICIPANT"/"OPT-PARTICIPANT"/"NON-PARTICIPANT"
489     'partstat': None, # Use: 0-1    Specify the participation status for the calendar user specified by the property. like use for VEVENT:- "NEEDS-ACTION"/"ACCEPTED"/"DECLINED"/"TENTATIVE"/"DELEGATED", use for VTODO:-"NEEDS-ACTION"/"ACCEPTED"/"DECLINED"/"TENTATIVE"/"DELEGATED"/"COMPLETED"/"IN-PROCESS" and use for VJOURNAL:- "NEEDS-ACTION"/"ACCEPTED"/"DECLINED".
490     'rsvp': None, # Use: 0-1    Specify whether there is an expectation of a favor of a reply from the calendar user specified by the property value like TRUE / FALSE.
491     'delegated-to': None, # Use: 0-1    Specify the calendar users to whom the calendar user specified by the property has delegated participation.
492     'delegated-from': None, # Use: 0-1    Specify the calendar users that have delegated their participation to the calendar user specified by the property.
493     'sent-by': None, # Use: 0-1    Specify the calendar user that is acting on behalf of the calendar user specified by the property.
494     'cn': None, # Use: 0-1    Specify the common name to be associated with the calendar user specified by the property.
495     'dir': None, # Use: 0-1    Specify reference to a directory entry associated with the calendar user specified by the property.
496     'language': None, # Use: 0-1    Specify the language for text values in a property or property parameter.
497     }
498
499     def import_ical(self, cr, uid, ical_data):
500         for para in ical_data.params:
501             if para.lower() == 'cn':
502                 self.ical_set(para.lower(), ical_data.params[para][0]+':'+ \
503                         ical_data.value, 'value')
504             else:
505                 self.ical_set(para.lower(), ical_data.params[para][0].lower(), 'value')
506         if not ical_data.params.get('CN'):
507             self.ical_set('cn', ical_data.value, 'value')
508         vals = map_data(cr, uid, self)
509         return vals
510
511     def export_ical(self, cr, uid, model, attendee_ids, vevent, context={}):
512         attendee_object = self.pool.get(model)
513         for attendee in attendee_object.read(cr, uid, attendee_ids, []):
514             attendee_add = vevent.add('attendee')
515             for a_key, a_val in attendee_object.__attribute__.items():
516                 if attendee[a_val['field']] and a_val['field'] != 'cn':
517                     if a_val['type'] == 'text':
518                         attendee_add.params[a_key] = [str(attendee[a_val['field']])]
519                     elif a_val['type'] == 'boolean':
520                         attendee_add.params[a_key] = [str(attendee[a_val['field']])]
521                 if a_val['field'] == 'cn':
522                     cn_val = [str(attendee[a_val['field']])]
523             attendee_add.params['CN'] = cn_val 
524         return vevent
525
526 Attendee()
527
528
529 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: