X-Git-Url: http://git.inspyration.org/?a=blobdiff_plain;f=addons%2Fgoogle_calendar%2Fgoogle_calendar.py;fp=addons%2Fgoogle_calendar%2Fgoogle_calendar.py;h=dd1d45c05fe32519ea2877ca8d9c6757a189c92d;hb=adf07a9490436211d16da8b1355c3bae4448a54a;hp=b8b60141e978072fd6939931b84ec3af6df9d779;hpb=5087612d1da85a8661f7aa124969b2ee9e155c10;p=odoo%2Fodoo.git diff --git a/addons/google_calendar/google_calendar.py b/addons/google_calendar/google_calendar.py index b8b6014..dd1d45c 100644 --- a/addons/google_calendar/google_calendar.py +++ b/addons/google_calendar/google_calendar.py @@ -1,31 +1,15 @@ -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2012 OpenERP SA (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. - -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## +# -*- coding: utf-8 -*- import operator import simplejson -import urllib +import urllib2 +import openerp from openerp import tools from openerp import SUPERUSER_ID - +from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT +from openerp.tools.translate import _ +from openerp.http import request from datetime import datetime, timedelta from dateutil import parser import pytz @@ -35,6 +19,13 @@ import logging _logger = logging.getLogger(__name__) +def status_response(status, substr=False): + if substr: + return int(str(status)[0]) + else: + return status_response(status, substr=True) == 2 + + class Meta(type): """ This Meta class allow to define class as a structure, and so instancied variable in __init__ to avoid to have side effect alike 'static' variable """ @@ -90,7 +81,7 @@ class SyncEvent(object): def __getitem__(self, key): return getattr(self, key) - def compute_OP(self): + def compute_OP(self, modeFull=True): #If event are already in Gmail and in OpenERP if self.OE.found and self.GG.found: #If the event has been deleted from one side, we delete on other side ! @@ -120,7 +111,6 @@ class SyncEvent(object): else: if not self.OE.synchro or self.OE.synchro.split('.')[0] < self.OE.update.split('.')[0]: self.OP = Update('OE', 'Event already updated by another user, but not synchro with my google calendar') - #import ipdb; ipdb.set_trace(); else: self.OP = NothingToDo("", 'Not update needed') else: @@ -128,11 +118,13 @@ class SyncEvent(object): # New in openERP... Create on create_events of synchronize function elif self.OE.found and not self.GG.found: - #Has been deleted from gmail if self.OE.status: - self.OP = Delete('OE', 'Removed from GOOGLE') + self.OP = Delete('OE', 'Update or delete from GOOGLE') else: - self.OP = NothingToDo("", "Already Deleted in gmail and unlinked in OpenERP") + if not modeFull: + self.OP = Delete('GG', 'Deleted from OpenERP, need to delete it from Gmail if already created') + else: + self.OP = NothingToDo("", "Already Deleted in gmail and unlinked in OpenERP") elif self.GG.found and not self.OE.found: tmpSrc = 'GG' if not self.GG.status and not self.GG.isInstance: @@ -151,11 +143,11 @@ class SyncEvent(object): return self.__repr__() def __repr__(self): - myPrint = "---- A SYNC EVENT ---" + myPrint = "\n\n---- A SYNC EVENT ---" myPrint += "\n ID OE: %s " % (self.OE.event and self.OE.event.id) myPrint += "\n ID GG: %s " % (self.GG.event and self.GG.event.get('id', False)) - myPrint += "\n Name OE: %s " % (self.OE.event and self.OE.event.name) - myPrint += "\n Name GG: %s " % (self.GG.event and self.GG.event.get('summary', False)) + myPrint += "\n Name OE: %s " % (self.OE.event and self.OE.event.name.encode('utf8')) + myPrint += "\n Name GG: %s " % (self.GG.event and self.GG.event.get('summary', '').encode('utf8')) myPrint += "\n Found OE:%5s vs GG: %5s" % (self.OE.found, self.GG.found) myPrint += "\n Recurrence OE:%5s vs GG: %5s" % (self.OE.isRecurrence, self.GG.isRecurrence) myPrint += "\n Instance OE:%5s vs GG: %5s" % (self.OE.isInstance, self.GG.isInstance) @@ -207,15 +199,15 @@ class google_calendar(osv.AbstractModel): STR_SERVICE = 'calendar' _name = 'google.%s' % STR_SERVICE - def generate_data(self, cr, uid, event, context=None): + def generate_data(self, cr, uid, event, isCreating=False, context=None): if event.allday: - start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T').split('T')[0] - end_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date, tools.DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(hours=event.duration), context=context).isoformat('T').split('T')[0] + start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.start, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T').split('T')[0] + final_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.start, tools.DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(hours=event.duration) + timedelta(days=isCreating and 1 or 0), context=context).isoformat('T').split('T')[0] type = 'date' vstype = 'dateTime' else: - start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T') - end_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date_deadline, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T') + start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.start, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T') + final_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.stop, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T') type = 'dateTime' vstype = 'date' attendee_list = [] @@ -235,7 +227,7 @@ class google_calendar(osv.AbstractModel): 'timeZone': 'UTC' }, "end": { - type: end_date, + type: final_date, vstype: None, 'timeZone': 'UTC' }, @@ -256,10 +248,9 @@ class google_calendar(osv.AbstractModel): def create_an_event(self, cr, uid, event, context=None): gs_pool = self.pool['google.service'] + data = self.generate_data(cr, uid, event, isCreating=True, context=context) - data = self.generate_data(cr, uid, event, context=context) - - url = "/calendar/v3/calendars/%s/events?fields=%s&access_token=%s" % ('primary', urllib.quote('id,updated'), self.get_token(cr, uid, context)) + url = "/calendar/v3/calendars/%s/events?fields=%s&access_token=%s" % ('primary', urllib2.quote('id,updated'), self.get_token(cr, uid, context)) headers = {'Content-type': 'application/json', 'Accept': 'text/plain'} data_json = simplejson.dumps(data) @@ -276,38 +267,89 @@ class google_calendar(osv.AbstractModel): return gs_pool._do_request(cr, uid, url, params, headers, type='DELETE', context=context) - def get_event_dict(self, cr, uid, token=False, nextPageToken=False, context=None): + def get_calendar_primary_id(self, cr, uid, context=None): + params = { + 'fields': 'id', + 'access_token': self.get_token(cr, uid, context) + } + headers = {'Content-type': 'application/json', 'Accept': 'text/plain'} + + url = "/calendar/v3/calendars/primary" + + try: + st, content = self.pool['google.service']._do_request(cr, uid, url, params, headers, type='GET', context=context) + except Exception, e: + + if (e.code == 401): # Token invalid / Acces unauthorized + error_msg = "Your token is invalid or has been revoked !" + + registry = openerp.modules.registry.RegistryManager.get(request.session.db) + with registry.cursor() as cur: + self.pool['res.users'].write(cur, uid, [uid], {'google_calendar_token': False, 'google_calendar_token_validity': False}, context=context) + + raise self.pool.get('res.config.settings').get_config_warning(cr, _(error_msg), context=context) + raise + + return status_response(st) and content['id'] or False + + def get_event_synchro_dict(self, cr, uid, lastSync=False, token=False, nextPageToken=False, context=None): if not token: token = self.get_token(cr, uid, context) - gs_pool = self.pool['google.service'] - params = { 'fields': 'items,nextPageToken', 'access_token': token, 'maxResults': 1000, - 'timeMin': self.get_start_time_to_synchro(cr, uid, context=context).strftime("%Y-%m-%dT%H:%M:%S.%fz"), + #'timeMin': self.get_minTime(cr, uid, context=context).strftime("%Y-%m-%dT%H:%M:%S.%fz"), } + + if lastSync: + params['updatedMin'] = lastSync.strftime("%Y-%m-%dT%H:%M:%S.%fz") + params['showDeleted'] = True + else: + params['timeMin'] = self.get_minTime(cr, uid, context=context).strftime("%Y-%m-%dT%H:%M:%S.%fz") + headers = {'Content-type': 'application/json', 'Accept': 'text/plain'} url = "/calendar/v3/calendars/%s/events" % 'primary' if nextPageToken: params['pageToken'] = nextPageToken - content = gs_pool._do_request(cr, uid, url, params, headers, type='GET', context=context) + status, content = self.pool['google.service']._do_request(cr, uid, url, params, headers, type='GET', context=context) google_events_dict = {} - for google_event in content['items']: google_events_dict[google_event['id']] = google_event - if content.get('nextPageToken', False): - google_events_dict.update(self.get_event_dict(cr, uid, token, content['nextPageToken'], context=context)) + if content.get('nextPageToken'): + google_events_dict.update( + self.get_event_synchro_dict(cr, uid, lastSync=lastSync, token=token, nextPageToken=content['nextPageToken'], context=context) + ) + return google_events_dict + def get_one_event_synchro(self, cr, uid, google_id, context=None): + token = self.get_token(cr, uid, context) + + params = { + 'access_token': token, + 'maxResults': 1000, + 'showDeleted': True, + } + + headers = {'Content-type': 'application/json', 'Accept': 'text/plain'} + + url = "/calendar/v3/calendars/%s/events/%s" % ('primary', google_id) + try: + status, content = self.pool['google.service']._do_request(cr, uid, url, params, headers, type='GET', context=context) + except: + _logger.info("Calendar Synchro - In except of get_one_event_synchro") + pass + + return status_response(status) and content or False + def update_to_google(self, cr, uid, oe_event, google_event, context): calendar_event = self.pool['calendar.event'] - gs_pool = self.pool['google.service'] url = "/calendar/v3/calendars/%s/events/%s?fields=%s&access_token=%s" % ('primary', google_event['id'], 'id,updated', self.get_token(cr, uid, context)) headers = {'Content-type': 'application/json', 'Accept': 'text/plain'} @@ -315,7 +357,7 @@ class google_calendar(osv.AbstractModel): data['sequence'] = google_event.get('sequence', 0) data_json = simplejson.dumps(data) - content = gs_pool._do_request(cr, uid, url, data_json, headers, type='PATCH', context=context) + status, content = self.pool['google.service']._do_request(cr, uid, url, data_json, headers, type='PATCH', context=context) update_date = datetime.strptime(content['updated'], "%Y-%m-%dT%H:%M:%S.%fz") calendar_event.write(cr, uid, [oe_event.id], {'oe_update_date': update_date}) @@ -324,15 +366,13 @@ class google_calendar(osv.AbstractModel): self.pool['calendar.attendee'].write(cr, uid, [context['curr_attendee']], {'oe_synchro_date': update_date}, context) def update_an_event(self, cr, uid, event, context=None): - gs_pool = self.pool['google.service'] - data = self.generate_data(cr, uid, event, context=context) url = "/calendar/v3/calendars/%s/events/%s" % ('primary', event.google_internal_event_id) headers = {} data['access_token'] = self.get_token(cr, uid, context) - response = gs_pool._do_request(cr, uid, url, data, headers, type='GET', context=context) + status, response = self.pool['google.service']._do_request(cr, uid, url, data, headers, type='GET', context=context) #TO_CHECK : , if http fail, no event, do DELETE ? return response @@ -379,7 +419,12 @@ class google_calendar(osv.AbstractModel): if self.get_need_synchro_attendee(cr, uid, context=context): attendee_id = res_partner_obj.search(cr, uid, [('email', '=', google_attendee['email'])], context=context) if not attendee_id: - attendee_id = [res_partner_obj.create(cr, uid, {'email': google_attendee['email'], 'customer': False, 'name': google_attendee.get("displayName", False) or google_attendee['email']}, context=context)] + data = { + 'email': google_attendee['email'], + 'customer': False, + 'name': google_attendee.get("displayName", False) or google_attendee['email'] + } + attendee_id = [res_partner_obj.create(cr, uid, data, context=context)] attendee = res_partner_obj.read(cr, uid, attendee_id[0], ['email'], context=context) partner_record.append((4, attendee.get('id'))) attendee['partner_id'] = attendee.pop('id') @@ -388,26 +433,25 @@ class google_calendar(osv.AbstractModel): UTC = pytz.timezone('UTC') if single_event_dict.get('start') and single_event_dict.get('end'): # If not cancelled + if single_event_dict['start'].get('dateTime', False) and single_event_dict['end'].get('dateTime', False): date = parser.parse(single_event_dict['start']['dateTime']) - date_deadline = parser.parse(single_event_dict['end']['dateTime']) - delta = date_deadline.astimezone(UTC) - date.astimezone(UTC) + stop = parser.parse(single_event_dict['end']['dateTime']) date = str(date.astimezone(UTC))[:-6] - date_deadline = str(date_deadline.astimezone(UTC))[:-6] + stop = str(stop.astimezone(UTC))[:-6] allday = False else: - date = (single_event_dict['start']['date'] + ' 00:00:00') - date_deadline = (single_event_dict['end']['date'] + ' 00:00:00') - d_start = datetime.strptime(date, "%Y-%m-%d %H:%M:%S") - d_end = datetime.strptime(date_deadline, "%Y-%m-%d %H:%M:%S") - delta = (d_end - d_start) + date = (single_event_dict['start']['date']) + stop = (single_event_dict['end']['date']) + d_end = datetime.strptime(stop, DEFAULT_SERVER_DATE_FORMAT) allday = True + d_end = d_end + timedelta(days=-1) + stop = d_end.strftime(DEFAULT_SERVER_DATE_FORMAT) - result['duration'] = (delta.seconds / 60) / 60.0 + delta.days * 24 update_date = datetime.strptime(single_event_dict['updated'], "%Y-%m-%dT%H:%M:%S.%fz") result.update({ - 'date': date, - 'date_deadline': date_deadline, + 'start': date, + 'stop': stop, 'allday': allday }) result.update({ @@ -419,7 +463,6 @@ class google_calendar(osv.AbstractModel): 'location': single_event_dict.get('location', False), 'class': single_event_dict.get('visibility', 'public'), 'oe_update_date': update_date, - # 'google_internal_event_id': single_event_dict.get('id',False), }) if single_event_dict.get("recurrence", False): @@ -431,7 +474,6 @@ class google_calendar(osv.AbstractModel): elif type == "copy": result['recurrence'] = True res = calendar_event.write(cr, uid, [event['id']], result, context=context) - elif type == "create": res = calendar_event.create(cr, uid, result, context=context) @@ -439,18 +481,81 @@ class google_calendar(osv.AbstractModel): self.pool['calendar.attendee'].write(cr, uid, [context['curr_attendee']], {'oe_synchro_date': update_date, 'google_internal_event_id': single_event_dict.get('id', False)}, context) return res - def synchronize_events(self, cr, uid, ids, context=None): - # Create all new events from OpenERP into Gmail, if that is not recurrent event - self.create_new_events(cr, uid, context=context) - self.bind_recurring_events_to_google(cr, uid, context) - res = self.update_events(cr, uid, context) + def remove_references(self, cr, uid, context=None): + current_user = self.pool['res.users'].browse(cr, uid, uid, context=context) + reset_data = { + 'google_calendar_rtoken': False, + 'google_calendar_token': False, + 'google_calendar_token_validity': False, + 'google_calendar_last_sync_date': False, + 'google_calendar_cal_id': False, + } + + all_my_attendees = self.pool['calendar.attendee'].search(cr, uid, [('partner_id', '=', current_user.partner_id.id)], context=context) + self.pool['calendar.attendee'].write(cr, uid, all_my_attendees, {'oe_synchro_date': False, 'google_internal_event_id': False}, context=context) + current_user.write(reset_data, context=context) + return True + + def synchronize_events(self, cr, uid, ids, lastSync=True, context=None): + if context is None: + context = {} + + # def isValidSync(syncToken): + # gs_pool = self.pool['google.service'] + # params = { + # 'maxResults': 1, + # 'fields': 'id', + # 'access_token': self.get_token(cr, uid, context), + # 'syncToken': syncToken, + # } + # url = "/calendar/v3/calendars/primary/events" + # status, response = gs_pool._do_request(cr, uid, url, params, type='GET', context=context) + # return int(status) != 410 + current_user = self.pool['res.users'].browse(cr, uid, uid, context=context) + + context_with_time = dict(context.copy(), ask_time=True) + current_google = self.get_calendar_primary_id(cr, uid, context=context_with_time) + + if current_user.google_calendar_cal_id: + if current_google != current_user.google_calendar_cal_id: + return { + "status": "need_reset", + "info": { + "old_name": current_user.google_calendar_cal_id, + "new_name": current_google + }, + "url": '' + } + + if lastSync and self.get_last_sync_date(cr, uid, context=context) and not self.get_disable_since_synchro(cr, uid, context=context): + lastSync = self.get_last_sync_date(cr, uid, context) + _logger.info("Calendar Synchro - MODE SINCE_MODIFIED : %s !" % lastSync.strftime(DEFAULT_SERVER_DATETIME_FORMAT)) + else: + lastSync = False + _logger.info("Calendar Synchro - MODE FULL SYNCHRO FORCED") + else: + current_user.write({'google_calendar_cal_id': current_google}, context=context) + lastSync = False + _logger.info("Calendar Synchro - MODE FULL SYNCHRO - NEW CAL ID") + + new_ids = [] + new_ids += self.create_new_events(cr, uid, context=context) + new_ids += self.bind_recurring_events_to_google(cr, uid, context) + + res = self.update_events(cr, uid, lastSync, context) + + current_user.write({'google_calendar_last_sync_date': context_with_time.get('ask_time')}, context=context) return { "status": res and "need_refresh" or "no_new_event_form_google", "url": '' } def create_new_events(self, cr, uid, context=None): + if context is None: + context = {} + + new_ids = [] ev_obj = self.pool['calendar.event'] att_obj = self.pool['calendar.attendee'] user_obj = self.pool['res.users'] @@ -458,32 +563,43 @@ class google_calendar(osv.AbstractModel): context_norecurrent = context.copy() context_norecurrent['virtual_id'] = False - my_att_ids = att_obj.search(cr, uid, [('partner_id', '=', myPartnerID), ('google_internal_event_id', '=', False), '|', - ('event_id.date_deadline', '>', self.get_start_time_to_synchro(cr, uid, context).strftime("%Y-%m-%d %H:%M:%S")), - ('event_id.end_date', '>', self.get_start_time_to_synchro(cr, uid, context).strftime("%Y-%m-%d %H:%M:%S")), + ('event_id.stop', '>', self.get_minTime(cr, uid, context=context).strftime(DEFAULT_SERVER_DATETIME_FORMAT)), + ('event_id.final_date', '>', self.get_minTime(cr, uid, context=context).strftime(DEFAULT_SERVER_DATETIME_FORMAT)), ], context=context_norecurrent) - for att in att_obj.browse(cr, uid, my_att_ids, context=context): if not att.event_id.recurrent_id or att.event_id.recurrent_id == 0: - response = self.create_an_event(cr, uid, att.event_id, context=context) - update_date = datetime.strptime(response['updated'], "%Y-%m-%dT%H:%M:%S.%fz") - ev_obj.write(cr, uid, att.event_id.id, {'oe_update_date': update_date}) - att_obj.write(cr, uid, [att.id], {'google_internal_event_id': response['id'], 'oe_synchro_date': update_date}) - cr.commit() + st, response = self.create_an_event(cr, uid, att.event_id, context=context) + if status_response(st): + update_date = datetime.strptime(response['updated'], "%Y-%m-%dT%H:%M:%S.%fz") + ev_obj.write(cr, uid, att.event_id.id, {'oe_update_date': update_date}) + new_ids.append(response['id']) + att_obj.write(cr, uid, [att.id], {'google_internal_event_id': response['id'], 'oe_synchro_date': update_date}) + cr.commit() + else: + _logger.warning("Impossible to create event %s. [%s]" % (att.event_id.id, st)) + _logger.warning("Response : %s" % response) + return new_ids + + def get_context_no_virtual(self, context): + context_norecurrent = context.copy() + context_norecurrent['virtual_id'] = False + context_norecurrent['active_test'] = False + return context_norecurrent - def bind_recurring_events_to_google(self, cr, uid, context): + def bind_recurring_events_to_google(self, cr, uid, context=None): + if context is None: + context = {} + + new_ids = [] ev_obj = self.pool['calendar.event'] att_obj = self.pool['calendar.attendee'] user_obj = self.pool['res.users'] myPartnerID = user_obj.browse(cr, uid, uid, context=context).partner_id.id - context_norecurrent = context.copy() - context_norecurrent['virtual_id'] = False - context_norecurrent['active_test'] = False - + context_norecurrent = self.get_context_no_virtual(context) my_att_ids = att_obj.search(cr, uid, [('partner_id', '=', myPartnerID), ('google_internal_event_id', '=', False)], context=context_norecurrent) for att in att_obj.browse(cr, uid, my_att_ids, context=context): @@ -500,11 +616,20 @@ class google_calendar(osv.AbstractModel): if new_google_internal_event_id: #TODO WARNING, NEED TO CHECK THAT EVENT and ALL instance NOT DELETE IN GMAIL BEFORE ! - self.update_recurrent_event_exclu(cr, uid, new_google_internal_event_id, source_attendee_record.google_internal_event_id, att.event_id, context=context) - att_obj.write(cr, uid, [att.id], {'google_internal_event_id': new_google_internal_event_id}, context=context) - cr.commit() + try: + st, response = self.update_recurrent_event_exclu(cr, uid, new_google_internal_event_id, source_attendee_record.google_internal_event_id, att.event_id, context=context) + if status_response(st): + att_obj.write(cr, uid, [att.id], {'google_internal_event_id': new_google_internal_event_id}, context=context) + new_ids.append(new_google_internal_event_id) + cr.commit() + else: + _logger.warning("Impossible to create event %s. [%s]" % (att.event_id.id, st)) + _logger.warning("Response : %s" % response) + except: + pass + return new_ids - def update_events(self, cr, uid, context=None): + def update_events(self, cr, uid, lastSync=False, context=None): if context is None: context = {} @@ -512,20 +637,64 @@ class google_calendar(osv.AbstractModel): user_obj = self.pool['res.users'] att_obj = self.pool['calendar.attendee'] myPartnerID = user_obj.browse(cr, uid, uid, context=context).partner_id.id + context_novirtual = self.get_context_no_virtual(context) + + if lastSync: + try: + all_event_from_google = self.get_event_synchro_dict(cr, uid, lastSync=lastSync, context=context) + except urllib2.HTTPError, e: + if e.code == 410: # GONE, Google is lost. + # we need to force the rollback from this cursor, because it locks my res_users but I need to write in this tuple before to raise. + cr.rollback() + registry = openerp.modules.registry.RegistryManager.get(request.session.db) + with registry.cursor() as cur: + self.pool['res.users'].write(cur, uid, [uid], {'google_calendar_last_sync_date': False}, context=context) + error_key = simplejson.loads(e.read()) + error_key = error_key.get('error', {}).get('message', 'nc') + error_msg = "Google are lost... the next synchro will be a full synchro. \n\n %s" % error_key + raise self.pool.get('res.config.settings').get_config_warning(cr, _(error_msg), context=context) + + my_google_att_ids = att_obj.search(cr, uid, [ + ('partner_id', '=', myPartnerID), + ('google_internal_event_id', 'in', all_event_from_google.keys()) + ], context=context_novirtual) + + my_openerp_att_ids = att_obj.search(cr, uid, [ + ('partner_id', '=', myPartnerID), + ('event_id.oe_update_date', '>', lastSync and lastSync.strftime(DEFAULT_SERVER_DATETIME_FORMAT) or self.get_minTime(cr, uid, context).strftime(DEFAULT_SERVER_DATETIME_FORMAT)), + ('google_internal_event_id', '!=', False), + ], context=context_novirtual) + + my_openerp_googleinternal_ids = att_obj.read(cr, uid, my_openerp_att_ids, ['google_internal_event_id', 'event_id'], context=context_novirtual) + + if self.get_print_log(cr, uid, context=context): + _logger.info("Calendar Synchro - \n\nUPDATE IN GOOGLE\n%s\n\nRETRIEVE FROM OE\n%s\n\nUPDATE IN OE\n%s\n\nRETRIEVE FROM GG\n%s\n\n" % (all_event_from_google, my_google_att_ids, my_openerp_att_ids, my_openerp_googleinternal_ids)) + + for giid in my_openerp_googleinternal_ids: + active = True # if not sure, we request google + if giid.get('event_id'): + active = calendar_event.browse(cr, uid, int(giid.get('event_id')[0]), context=context_novirtual).active + + if giid.get('google_internal_event_id') and not all_event_from_google.get(giid.get('google_internal_event_id')) and active: + one_event = self.get_one_event_synchro(cr, uid, giid.get('google_internal_event_id'), context=context) + if one_event: + all_event_from_google[one_event['id']] = one_event + + my_att_ids = list(set(my_google_att_ids + my_openerp_att_ids)) - context_novirtual = context.copy() - context_novirtual['virtual_id'] = False - context_novirtual['active_test'] = False - - all_event_from_google = self.get_event_dict(cr, uid, context=context) + else: + domain = [ + ('partner_id', '=', myPartnerID), + ('google_internal_event_id', '!=', False), + '|', + ('event_id.stop', '>', self.get_minTime(cr, uid, context).strftime(DEFAULT_SERVER_DATETIME_FORMAT)), + ('event_id.final_date', '>', self.get_minTime(cr, uid, context).strftime(DEFAULT_SERVER_DATETIME_FORMAT)), + ] + + # Select all events from OpenERP which have been already synchronized in gmail + my_att_ids = att_obj.search(cr, uid, domain, context=context_novirtual) + all_event_from_google = self.get_event_synchro_dict(cr, uid, lastSync=False, context=context) - # Select all events from OpenERP which have been already synchronized in gmail - my_att_ids = att_obj.search(cr, uid, [('partner_id', '=', myPartnerID), - ('google_internal_event_id', '!=', False), - '|', - ('event_id.date_deadline', '>', self.get_start_time_to_synchro(cr, uid, context).strftime("%Y-%m-%d %H:%M:%S")), - ('event_id.end_date', '>', self.get_start_time_to_synchro(cr, uid, context).strftime("%Y-%m-%d %H:%M:%S")), - ], context=context_novirtual) event_to_synchronize = {} for att in att_obj.browse(cr, uid, my_att_ids, context=context): event = att.event_id @@ -576,8 +745,10 @@ class google_calendar(osv.AbstractModel): ###################### for base_event in event_to_synchronize: for current_event in event_to_synchronize[base_event]: - event_to_synchronize[base_event][current_event].compute_OP() - #print event_to_synchronize[base_event] + event_to_synchronize[base_event][current_event].compute_OP(modeFull=not lastSync) + if self.get_print_log(cr, uid, context=context): + if not isinstance(event_to_synchronize[base_event][current_event].OP, NothingToDo): + _logger.info(event_to_synchronize[base_event]) ###################### # DO ACTION # @@ -615,30 +786,38 @@ class google_calendar(osv.AbstractModel): if actSrc == 'OE': self.delete_an_event(cr, uid, current_event[0], context=context) elif actSrc == 'GG': - new_google_event_id = event.GG.event['id'].rsplit('_', 1)[1] - if 'T' in new_google_event_id: - new_google_event_id = new_google_event_id.replace('T', '')[:-1] - else: - new_google_event_id = new_google_event_id + "000000" - - if event.GG.status: - parent_event = {} - if event_to_synchronize[base_event][0][1].OE.event_id: - parent_event['id'] = "%s-%s" % (event_to_synchronize[base_event][0][1].OE.event_id, new_google_event_id) - else: - main_ev = att_obj.search_read(cr, uid, [('google_internal_event_id', '=', event.GG.event['id'].rsplit('_', 1)[0])], fields=['event_id'], context=context) - parent_event['id'] = "%s-%s" % (main_ev[0].get('event_id')[0], new_google_event_id) - res = self.update_from_google(cr, uid, parent_event, event.GG.event, "copy", context) - else: - if event_to_synchronize[base_event][0][1].OE.event_id: - parent_oe_id = event_to_synchronize[base_event][0][1].OE.event_id - calendar_event.unlink(cr, uid, "%s-%s" % (parent_oe_id, new_google_event_id), unlink_level=1, context=context) + new_google_event_id = event.GG.event['id'].rsplit('_', 1)[1] + if 'T' in new_google_event_id: + new_google_event_id = new_google_event_id.replace('T', '')[:-1] + else: + new_google_event_id = new_google_event_id + "000000" + + if event.GG.status: + parent_event = {} + if not event_to_synchronize[base_event][0][1].OE.event_id: + main_ev = att_obj.search_read(cr, uid, [('google_internal_event_id', '=', event.GG.event['id'].rsplit('_', 1)[0])], fields=['event_id'], context=context_novirtual) + event_to_synchronize[base_event][0][1].OE.event_id = main_ev[0].get('event_id')[0] + + parent_event['id'] = "%s-%s" % (event_to_synchronize[base_event][0][1].OE.event_id, new_google_event_id) + res = self.update_from_google(cr, uid, parent_event, event.GG.event, "copy", context) + else: + parent_oe_id = event_to_synchronize[base_event][0][1].OE.event_id + calendar_event.unlink(cr, uid, "%s-%s" % (parent_oe_id, new_google_event_id), can_be_deleted=True, context=context) elif isinstance(actToDo, Delete): if actSrc == 'GG': - self.delete_an_event(cr, uid, current_event[0], context=context) + try: + self.delete_an_event(cr, uid, current_event[0], context=context) + except Exception, e: + error = simplejson.loads(e.read()) + error_nr = error.get('error', {}).get('code') + # if already deleted from gmail or never created + if error_nr in (404, 410,): + pass + else: + raise e elif actSrc == 'OE': - calendar_event.unlink(cr, uid, event.OE.event_id, unlink_level=0, context=context) + calendar_event.unlink(cr, uid, event.OE.event_id, can_be_deleted=False, context=context) return True def check_and_sync(self, cr, uid, oe_event, google_event, context): @@ -659,7 +838,7 @@ class google_calendar(osv.AbstractModel): url = "/calendar/v3/calendars/%s/events/%s" % ('primary', instance_id) - content = gs_pool._do_request(cr, uid, url, params, headers, type='GET', context=context) + st, content = gs_pool._do_request(cr, uid, url, params, headers, type='GET', context=context) return content.get('sequence', 0) ################################# ## MANAGE CONNEXION TO GMAIL ## @@ -667,13 +846,16 @@ class google_calendar(osv.AbstractModel): def get_token(self, cr, uid, context=None): current_user = self.pool['res.users'].browse(cr, uid, uid, context=context) - - if datetime.strptime(current_user.google_calendar_token_validity.split('.')[0], "%Y-%m-%d %H:%M:%S") < (datetime.now() + timedelta(minutes=1)): + if not current_user.google_calendar_token_validity or \ + datetime.strptime(current_user.google_calendar_token_validity.split('.')[0], DEFAULT_SERVER_DATETIME_FORMAT) < (datetime.now() + timedelta(minutes=1)): self.do_refresh_token(cr, uid, context=context) current_user.refresh() - return current_user.google_calendar_token + def get_last_sync_date(self, cr, uid, context=None): + current_user = self.pool['res.users'].browse(cr, uid, uid, context=context) + return current_user.google_calendar_last_sync_date and datetime.strptime(current_user.google_calendar_last_sync_date, DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(minutes=0) or False + def do_refresh_token(self, cr, uid, context=None): current_user = self.pool['res.users'].browse(cr, uid, uid, context=context) gs_pool = self.pool['google.service'] @@ -711,14 +893,18 @@ class google_calendar(osv.AbstractModel): vals['google_%s_token' % self.STR_SERVICE] = all_token.get('access_token') self.pool['res.users'].write(cr, SUPERUSER_ID, uid, vals, context=context) - def get_start_time_to_synchro(self, cr, uid, context=None): - # WILL BE AN IR CONFIG PARAMETER - beginning from SAAS4 - number_of_week = 13 + def get_minTime(self, cr, uid, context=None): + number_of_week = self.pool['ir.config_parameter'].get_param(cr, uid, 'calendar.week_synchro', default=13) return datetime.now() - timedelta(weeks=number_of_week) def get_need_synchro_attendee(self, cr, uid, context=None): - # WILL BE AN IR CONFIG PARAMETER - beginning from SAAS4 - return True + return self.pool['ir.config_parameter'].get_param(cr, uid, 'calendar.block_synchro_attendee', default=True) + + def get_disable_since_synchro(self, cr, uid, context=None): + return self.pool['ir.config_parameter'].get_param(cr, uid, 'calendar.block_since_synchro', default=False) + + def get_print_log(self, cr, uid, context=None): + return self.pool['ir.config_parameter'].get_param(cr, uid, 'calendar.debug_print', default=False) class res_users(osv.Model): @@ -728,16 +914,22 @@ class res_users(osv.Model): 'google_calendar_rtoken': fields.char('Refresh Token'), 'google_calendar_token': fields.char('User token'), 'google_calendar_token_validity': fields.datetime('Token Validity'), + 'google_calendar_last_sync_date': fields.datetime('Last synchro date'), + 'google_calendar_cal_id': fields.char('Calendar ID', help='Last Calendar ID who has been synchronized. If it is changed, we remove \ +all links between GoogleID and OpenERP Google Internal ID') } class calendar_event(osv.Model): _inherit = "calendar.event" + def get_fields_need_update_google(self, cr, uid, context=None): + return ['name', 'description', 'allday', 'date', 'date_end', 'stop', 'attendee_ids', 'location', 'class', 'active'] + def write(self, cr, uid, ids, vals, context=None): if context is None: context = {} - sync_fields = set(['name', 'description', 'date', 'date_closed', 'date_deadline', 'attendee_ids', 'location', 'class']) + sync_fields = set(self.get_fields_need_update_google(cr, uid, context)) if (set(vals.keys()) & sync_fields) and 'oe_update_date' not in vals.keys() and 'NewMeeting' not in context: vals['oe_update_date'] = datetime.now() @@ -754,6 +946,9 @@ class calendar_event(osv.Model): default['oe_update_date'] = False return super(calendar_event, self).copy(cr, uid, id, default, context) + def unlink(self, cr, uid, ids, can_be_deleted=False, context=None): + return super(calendar_event, self).unlink(cr, uid, ids, can_be_deleted=can_be_deleted, context=context) + _columns = { 'oe_update_date': fields.datetime('OpenERP Update Date'), } @@ -763,7 +958,7 @@ class calendar_attendee(osv.Model): _inherit = 'calendar.attendee' _columns = { - 'google_internal_event_id': fields.char('Google Calendar Event Id', size=256), + 'google_internal_event_id': fields.char('Google Calendar Event Id'), 'oe_synchro_date': fields.datetime('OpenERP Synchro Date'), } _sql_constraints = [('google_id_uniq', 'unique(google_internal_event_id,partner_id,event_id)', 'Google ID should be unique!')]