[REF] calendar: Lint + Clean code

This commit is contained in:
Yannick Tivisse
2020-04-09 10:59:34 +02:00
committed by Lucas Lefèvre
parent f2b6918649
commit 1a9be3a869
29 changed files with 220 additions and 241 deletions
+29 -18
View File
@@ -11,15 +11,20 @@ from odoo.tools.misc import get_lang
class CalendarController(http.Controller):
# YTI Note: Keep id and kwargs only for retrocompatibility purpose
@http.route('/calendar/meeting/accept', type='http', auth="calendar")
def accept(self, token, action, id, **kwargs):
attendee = request.env['calendar.attendee'].search([('access_token', '=', token), ('state', '!=', 'accepted')])
def accept_meeting(self, token, id, **kwargs):
attendee = request.env['calendar.attendee'].search([
('access_token', '=', token),
('state', '!=', 'accepted')])
attendee.do_accept()
return self.view(token, action, id, view='form')
return self.view_meeting(token, id)
@http.route('/calendar/recurrence/accept', type='http', auth="calendar")
def accept_recurrence(self, token, action, id, **kwargs):
attendee = request.env['calendar.attendee'].sudo().search([('access_token', '=', token), ('state', '!=', 'accepted')])
def accept_recurrence(self, token, id, **kwargs):
attendee = request.env['calendar.attendee'].sudo().search([
('access_token', '=', token),
('state', '!=', 'accepted')])
if attendee:
attendees = request.env['calendar.attendee'].sudo().search([
('event_id', 'in', attendee.event_id.recurrence_id.calendar_event_ids.ids),
@@ -27,11 +32,21 @@ class CalendarController(http.Controller):
('state', '!=', 'accepted'),
])
attendees.do_accept()
return self.view(token, action, id, view='form')
return self.view_meeting(token, id)
@http.route('/calendar/meeting/decline', type='http', auth="calendar")
def decline_meeting(self, token, id, **kwargs):
attendee = request.env['calendar.attendee'].search([
('access_token', '=', token),
('state', '!=', 'declined')])
attendee.do_decline()
return self.view_meeting(token, id)
@http.route('/calendar/recurrence/decline', type='http', auth="calendar")
def decline_recurrence(self, token, action, id, **kwargs):
attendee = request.env['calendar.attendee'].sudo().search([('access_token', '=', token), ('state', '!=', 'declined')])
def decline_recurrence(self, token, id, **kwargs):
attendee = request.env['calendar.attendee'].sudo().search([
('access_token', '=', token),
('state', '!=', 'declined')])
if attendee:
attendees = request.env['calendar.attendee'].sudo().search([
('event_id', 'in', attendee.event_id.recurrence_id.calendar_event_ids.ids),
@@ -39,17 +54,13 @@ class CalendarController(http.Controller):
('state', '!=', 'declined'),
])
attendees.do_decline()
return self.view(token, action, id, view='form')
@http.route('/calendar/meeting/decline', type='http', auth="calendar")
def decline(self, token, action, id, **kwargs):
attendee = request.env['calendar.attendee'].search([('access_token', '=', token), ('state', '!=', 'declined')])
attendee.do_decline()
return self.view(token, action, id, view='form')
return self.view_meeting(token, id)
@http.route('/calendar/meeting/view', type='http', auth="calendar")
def view(self, token, action, id, view='calendar'):
attendee = request.env['calendar.attendee'].search([('access_token', '=', token), ('event_id', '=', int(id))])
def view_meeting(self, token, id, **kwargs):
attendee = request.env['calendar.attendee'].search([
('access_token', '=', token),
('event_id', '=', int(id))])
if not attendee:
return request.not_found()
timezone = attendee.partner_id.tz
@@ -78,5 +89,5 @@ class CalendarController(http.Controller):
return request.env['calendar.alarm_manager'].get_next_notif()
@http.route('/calendar/notify_ack', type='json', auth="user")
def notify_ack(self, type=''):
def notify_ack(self):
return request.env['res.partner'].sudo()._set_calendar_last_notif_ack()
+9 -9
View File
@@ -19,13 +19,13 @@
</p>
<div style="text-align: center; margin: 16px 0px 16px 0px;">
% set target = 'recurrence' if recurrent else 'meeting'
<a href="/calendar/${target}/accept?db=${'dbname' in ctx and ctx['dbname'] or ''}&amp;token=${object.access_token}&amp;action=${'action_id' in ctx and ctx['action_id'] or ''}&amp;id=${object.event_id.id}"
<a href="/calendar/${target}/accept?token=${object.access_token}&amp;id=${object.event_id.id}"
style="padding: 5px 10px; color: #FFFFFF; text-decoration: none; background-color: #875A7B; border: 1px solid #875A7B; border-radius: 3px">
Accept</a>
<a href="/calendar/${target}/decline?db=${'dbname' in ctx and ctx['dbname'] or '' }&amp;token=${object.access_token}&amp;action=${'action_id' in ctx and ctx['action_id'] or ''}&amp;id=${object.event_id.id}"
<a href="/calendar/${target}/decline?token=${object.access_token}&amp;id=${object.event_id.id}"
style="padding: 5px 10px; color: #FFFFFF; text-decoration: none; background-color: #875A7B; border: 1px solid #875A7B; border-radius: 3px">
Decline</a>
<a href="/calendar/meeting/view?db=${'dbname' in ctx and ctx['dbname'] or ''}&amp;token=${object.access_token}&amp;action=${'action_id' in ctx and ctx['action_id'] or ''}&amp;id=${object.event_id.id}"
<a href="/calendar/meeting/view?token=${object.access_token}&amp;id=${object.event_id.id}"
style="padding: 5px 10px; color: #FFFFFF; text-decoration: none; background-color: #875A7B; border: 1px solid #875A7B; border-radius: 3px">
View</a>
</div>
@@ -110,13 +110,13 @@
</p>
<div style="text-align: center; margin: 16px 0px 16px 0px;">
% set target = 'recurrence' if recurrent else 'meeting'
<a href="/calendar/${target}/accept?db=${'dbname' in ctx and ctx['dbname'] or ''}&amp;token=${object.access_token}&amp;action=${'action_id' in ctx and ctx['action_id'] or ''}&amp;id=${object.event_id.id}"
<a href="/calendar/${target}/accept?token=${object.access_token}&amp;id=${object.event_id.id}"
style="padding: 5px 10px; color: #FFFFFF; text-decoration: none; background-color: #875A7B; border: 1px solid #875A7B; border-radius: 3px">
Accept</a>
<a href="/calendar/${target}/decline?db=${'dbname' in ctx and ctx['dbname'] or '' }&amp;token=${object.access_token}&amp;action=${'action_id' in ctx and ctx['action_id'] or ''}&amp;id=${object.event_id.id}"
<a href="/calendar/${target}/decline?token=${object.access_token}&amp;id=${object.event_id.id}"
style="padding: 5px 10px; color: #FFFFFF; text-decoration: none; background-color: #875A7B; border: 1px solid #875A7B; border-radius: 3px">
Decline</a>
<a href="/calendar/meeting/view?db=${'dbname' in ctx and ctx['dbname'] or ''}&amp;token=${object.access_token}&amp;action=${'action_id' in ctx and ctx['action_id'] or ''}&amp;id=${object.event_id.id}"
<a href="/calendar/meeting/view?token=${object.access_token}&amp;id=${object.event_id.id}"
style="padding: 5px 10px; color: #FFFFFF; text-decoration: none; background-color: #875A7B; border: 1px solid #875A7B; border-radius: 3px">
View</a>
</div>
@@ -204,13 +204,13 @@
This is a reminder for the below event :
</p>
<div style="text-align: center; margin: 16px 0px 16px 0px;">
<a href="/calendar/meeting/accept?db=${'dbname' in ctx and ctx['dbname'] or ''}&amp;token=${object.access_token}&amp;action=${'action_id' in ctx and ctx['action_id'] or ''}&amp;id=${object.event_id.id}"
<a href="/calendar/meeting/accept?token=${object.access_token}&amp;event_id=${object.event_id.id}"
style="padding: 5px 10px; color: #FFFFFF; text-decoration: none; background-color: #875A7B; border: 1px solid #875A7B; border-radius: 3px">
Accept</a>
<a href="/calendar/meeting/decline?db=${'dbname' in ctx and ctx['dbname'] or '' }&amp;token=${object.access_token}&amp;action=${'action_id' in ctx and ctx['action_id'] or ''}&amp;id=${object.event_id.id}"
<a href="/calendar/meeting/decline?token=${object.access_token}&amp;id=${object.event_id.id}"
style="padding: 5px 10px; color: #FFFFFF; text-decoration: none; background-color: #875A7B; border: 1px solid #875A7B; border-radius: 3px">
Decline</a>
<a href="/calendar/meeting/view?db=${'dbname' in ctx and ctx['dbname'] or ''}&amp;token=${object.access_token}&amp;action=${'action_id' in ctx and ctx['action_id'] or ''}&amp;id=${object.event_id.id}"
<a href="/calendar/meeting/view?token=${object.access_token}&amp;id=${object.event_id.id}"
style="padding: 5px 10px; color: #FFFFFF; text-decoration: none; background-color: #875A7B; border: 1px solid #875A7B; border-radius: 3px">
View</a>
</div>
+14 -5
View File
@@ -23,13 +23,20 @@ class Alarm(models.Model):
_interval_selection = {'minutes': 'Minutes', 'hours': 'Hours', 'days': 'Days'}
name = fields.Char('Name', translate=True, required=True)
alarm_type = fields.Selection([('notification', 'Notification'), ('email', 'Email')], string='Type', required=True, default='email')
alarm_type = fields.Selection(
[('notification', 'Notification'), ('email', 'Email')],
string='Type', required=True, default='email')
duration = fields.Integer('Remind Before', required=True, default=1)
interval = fields.Selection(list(_interval_selection.items()), 'Unit', required=True, default='hours')
duration_minutes = fields.Integer('Duration in minutes', search='_search_duration_minutes', compute='_compute_duration_minutes', store=True, help="Duration in minutes")
interval = fields.Selection(
list(_interval_selection.items()), 'Unit', required=True, default='hours')
duration_minutes = fields.Integer(
'Duration in minutes', store=True,
search='_search_duration_minutes', compute='_compute_duration_minutes',
help="Duration in minutes")
def _search_duration_minutes(self, operator, value):
return ['|', '|',
return [
'|', '|',
'&', ('interval', '=', 'minutes'), ('duration', operator, value),
'&', ('interval', '=', 'hours'), ('duration', operator, value / 60),
'&', ('interval', '=', 'days'), ('duration', operator, value / 60 / 24),
@@ -38,7 +45,9 @@ class Alarm(models.Model):
@api.onchange('duration', 'interval', 'alarm_type')
def _onchange_duration_interval(self):
display_interval = self._interval_selection.get(self.interval, '')
display_alarm_type = {key: value for key, value in self._fields['alarm_type']._description_selection(self.env)}[self.alarm_type]
display_alarm_type = {
key: value for key, value in self._fields['alarm_type']._description_selection(self.env)
}[self.alarm_type]
self.name = "%s - %s %s" % (display_alarm_type, self.duration, display_interval)
def _update_cron(self):
@@ -2,7 +2,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import datetime
from datetime import timedelta
from odoo import api, fields, models
@@ -11,7 +10,6 @@ _logger = logging.getLogger(__name__)
class AlarmManager(models.AbstractModel):
_name = 'calendar.alarm_manager'
_description = 'Event Alarm Manager'
@@ -77,12 +75,12 @@ class AlarmManager(models.AbstractModel):
tuple_params += (seconds,)
self._cr.execute("""
WITH calcul_delta AS (%s)
SELECT *
FROM ( %s WHERE cal.active = True ) AS ALL_EVENTS
WHERE ALL_EVENTS.first_alarm < %s
AND ALL_EVENTS.last_event_date > (now() at time zone 'utc')
""" % (delta_request, base_request, first_alarm_max_value), tuple_params)
WITH calcul_delta AS (%s)
SELECT *
FROM ( %s WHERE cal.active = True ) AS ALL_EVENTS
WHERE ALL_EVENTS.first_alarm < %s
AND ALL_EVENTS.last_event_date > (now() at time zone 'utc')
""" % (delta_request, base_request, first_alarm_max_value), tuple_params)
for event_id, first_alarm, last_alarm, first_meeting, last_meeting, min_duration, max_duration, rule in self._cr.fetchall():
result[event_id] = {
+14 -10
View File
@@ -3,7 +3,6 @@
import uuid
import base64
import logging
from collections import defaultdict
from odoo import api, fields, models, _
from odoo.exceptions import UserError
@@ -13,7 +12,6 @@ _logger = logging.getLogger(__name__)
class Attendee(models.Model):
""" Calendar Attendee Information """
_name = 'calendar.attendee'
_rec_name = 'common_name'
_description = 'Calendar Attendee Information'
@@ -28,13 +26,15 @@ class Attendee(models.Model):
('accepted', 'Accepted'),
]
event_id = fields.Many2one('calendar.event', 'Meeting linked', required=True, ondelete='cascade')
event_id = fields.Many2one(
'calendar.event', 'Meeting linked', required=True, ondelete='cascade')
partner_id = fields.Many2one('res.partner', 'Contact', required=True, readonly=True)
state = fields.Selection(STATE_SELECTION, string='Status', readonly=True, default='needsAction',
help="Status of the attendee's participation")
common_name = fields.Char('Common name', compute='_compute_common_name', store=True)
email = fields.Char('Email', related='partner_id.email', help="Email of Invited Person")
availability = fields.Selection([('free', 'Free'), ('busy', 'Busy')], 'Free/Busy', readonly=True)
availability = fields.Selection(
[('free', 'Free'), ('busy', 'Busy')], 'Free/Busy', readonly=True)
access_token = fields.Char('Invitation Token', default=_default_access_token)
recurrence_id = fields.Many2one('calendar.recurrence', related='event_id.recurrence_id')
@@ -50,8 +50,8 @@ class Attendee(models.Model):
values['state'] = 'accepted'
if not values.get("email") and values.get("common_name"):
common_nameval = values.get("common_name").split(':')
email = [x for x in common_nameval if '@' in x] # TODO JEM : should be refactored
values['email'] = email and email[0] or ''
email = [x for x in common_nameval if '@' in x]
values['email'] = email[0] if email else ''
values['common_name'] = values.get("common_name")
attendees = super().create(vals_list)
attendees._subscribe_partner()
@@ -64,7 +64,8 @@ class Attendee(models.Model):
def _subscribe_partner(self):
for event in self.event_id:
partners = (event.attendee_ids & self).partner_id - event.message_partner_ids
partners -= self.env.user.partner_id # current user is automatically added as followers, don't add it twice.
# current user is automatically added as followers, don't add it twice.
partners -= self.env.user.partner_id
event.message_subscribe(partner_ids=partners.ids)
def _unsubscribe_partner(self):
@@ -147,12 +148,15 @@ class Attendee(models.Model):
def do_accept(self):
""" Marks event invitation as Accepted. """
for attendee in self:
attendee.event_id.message_post(body=_("%s has accepted invitation") % (attendee.common_name), subtype_xmlid="calendar.subtype_invitation")
attendee.event_id.message_post(
body=_("%s has accepted invitation") % (attendee.common_name),
subtype_xmlid="calendar.subtype_invitation")
return self.write({'state': 'accepted'})
def do_decline(self):
""" Marks event invitation as Declined. """
for attendee in self:
attendee.event_id.message_post(body=_("%s has declined invitation") % (attendee.common_name), subtype_xmlid="calendar.subtype_invitation")
attendee.event_id.message_post(
body=_("%s has declined invitation") % (attendee.common_name),
subtype_xmlid="calendar.subtype_invitation")
return self.write({'state': 'declined'})
+53 -39
View File
@@ -1,11 +1,9 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import babel.dates
import datetime
import math
from datetime import timedelta
from dateutil.relativedelta import relativedelta
import math
import babel.dates
import logging
import pytz
@@ -19,10 +17,8 @@ from odoo.tools.misc import get_lang
from odoo.tools import pycompat
from odoo.exceptions import UserError, ValidationError
_logger = logging.getLogger(__name__)
SORT_ALIASES = {
'start': 'sort_start',
'start_date': 'sort_start',
@@ -45,12 +41,6 @@ def get_weekday_occurence(date):
class Meeting(models.Model):
""" Model for Calendar Event
Special context keys :
- `no_mail_to_attendees` : disabled sending email to attendees when creating/editing a meeting
"""
_name = 'calendar.event'
_description = "Calendar Event"
_order = "start desc"
@@ -188,27 +178,45 @@ class Meeting(models.Model):
name = fields.Char('Meeting Subject', required=True)
attendee_status = fields.Selection(Attendee.STATE_SELECTION, string='Attendee Status', compute='_compute_attendee')
attendee_status = fields.Selection(
Attendee.STATE_SELECTION, string='Attendee Status', compute='_compute_attendee')
display_time = fields.Char('Event Time', compute='_compute_display_time')
start = fields.Datetime('Start', required=True, tracking=True, default=fields.Date.today, help="Start date of an event, without time for full days events")
stop = fields.Datetime('Stop', required=True, tracking=True, default=fields.Date.today, compute='_compute_stop', readonly=False, store=True,
help="Stop date of an event, without time for full days events")
start = fields.Datetime(
'Start', required=True, tracking=True, default=fields.Date.today,
help="Start date of an event, without time for full days events")
stop = fields.Datetime(
'Stop', required=True, tracking=True, default=fields.Date.today,
compute='_compute_stop', readonly=False, store=True,
help="Stop date of an event, without time for full days events")
allday = fields.Boolean('All Day', default=False)
start_date = fields.Date('Start Date', compute='_compute_dates', inverse='_inverse_dates', store=True, tracking=True)
stop_date = fields.Date('End Date', compute='_compute_dates', inverse='_inverse_dates', store=True, tracking=True)
event_tz = fields.Selection('_event_tz_get', string='Timezone', default=lambda self: self.env.context.get('tz') or self.user_id.tz)
start_date = fields.Date(
'Start Date', store=True, tracking=True,
compute='_compute_dates', inverse='_inverse_dates')
stop_date = fields.Date(
'End Date', store=True, tracking=True,
compute='_compute_dates', inverse='_inverse_dates')
event_tz = fields.Selection(
'_event_tz_get', string='Timezone',
default=lambda self: self.env.context.get('tz') or self.user_id.tz)
duration = fields.Float('Duration', compute='_compute_duration', store=True, readonly=False)
description = fields.Text('Description')
privacy = fields.Selection([('public', 'Everyone'), ('private', 'Only me'), ('confidential', 'Only internal users')], 'Privacy', default='public', required=True)
privacy = fields.Selection(
[('public', 'Everyone'),
('private', 'Only me'),
('confidential', 'Only internal users')],
'Privacy', default='public', required=True)
location = fields.Char('Location', tracking=True, help="Location of Event")
show_as = fields.Selection([('free', 'Free'), ('busy', 'Busy')], 'Show Time as', default='busy', required=True)
show_as = fields.Selection(
[('free', 'Free'),
('busy', 'Busy')], 'Show Time as', default='busy', required=True)
# linked document
# LUL TODO use fields.Reference ?
res_id = fields.Integer('Document ID')
res_model_id = fields.Many2one('ir.model', 'Document Model', ondelete='cascade')
res_model = fields.Char('Document Model Name', related='res_model_id.model', readonly=True, store=True)
res_model = fields.Char(
'Document Model Name', related='res_model_id.model', readonly=True, store=True)
activity_ids = fields.One2many('mail.activity', 'calendar_event_id', string='Activities')
#redifine message_ids to remove autojoin to avoid search to crash in get_recurrent_ids
@@ -235,7 +243,8 @@ class Meeting(models.Model):
# RECURRENCE FIELD
recurrency = fields.Boolean('Recurrent', help="Recurrent Event")
recurrence_id = fields.Many2one('calendar.recurrence', string="Recurrence Rule", index=True)
recurrence_id = fields.Many2one(
'calendar.recurrence', string="Recurrence Rule", index=True)
recurrence_update = fields.Selection([
('self_only', "This event"),
('future_events', "This and following events"),
@@ -246,16 +255,22 @@ class Meeting(models.Model):
# Those field are pseudo-related fields of recurrence_id.
# They can't be "real" related fields because it should work at record creation
# when recurrence_id is not created yet.
# If some of these fields are set and recurrence_id does not exists, a `calendar.recurrence.rule`
# will be dynamically created.
# If some of these fields are set and recurrence_id does not exists,
# a `calendar.recurrence.rule` will be dynamically created.
rrule = fields.Char('Recurrent Rule', compute='_compute_recurrence', readonly=False)
rrule_type = fields.Selection(RRULE_TYPE_SELECTION, string='Recurrence',
help="Let the event automatically repeat at that interval",
compute='_compute_recurrence', readonly=False)
event_tz = fields.Selection(_tz_get, string='Timezone', compute='_compute_recurrence', readonly=False)
end_type = fields.Selection(END_TYPE_SELECTION, string='Recurrence Termination', compute='_compute_recurrence', readonly=False)
interval = fields.Integer(string='Repeat Every', help="Repeat every (Days/Week/Month/Year)", compute='_compute_recurrence', readonly=False)
count = fields.Integer(string='Repeat', help="Repeat x times", compute='_compute_recurrence', readonly=False)
event_tz = fields.Selection(
_tz_get, string='Timezone', compute='_compute_recurrence', readonly=False)
end_type = fields.Selection(
END_TYPE_SELECTION, string='Recurrence Termination',
compute='_compute_recurrence', readonly=False)
interval = fields.Integer(
string='Repeat Every', compute='_compute_recurrence', readonly=False,
help="Repeat every (Days/Week/Month/Year)")
count = fields.Integer(
string='Repeat', help="Repeat x times", compute='_compute_recurrence', readonly=False)
mo = fields.Boolean('Mon', compute='_compute_recurrence', readonly=False)
tu = fields.Boolean('Tue', compute='_compute_recurrence', readonly=False)
we = fields.Boolean('Wed', compute='_compute_recurrence', readonly=False)
@@ -263,7 +278,8 @@ class Meeting(models.Model):
fr = fields.Boolean('Fri', compute='_compute_recurrence', readonly=False)
sa = fields.Boolean('Sat', compute='_compute_recurrence', readonly=False)
su = fields.Boolean('Sun', compute='_compute_recurrence', readonly=False)
month_by = fields.Selection(MONTH_BY_SELECTION, string='Option', compute='_compute_recurrence', readonly=False)
month_by = fields.Selection(
MONTH_BY_SELECTION, string='Option', compute='_compute_recurrence', readonly=False)
day = fields.Integer('Date of month', compute='_compute_recurrence', readonly=False)
weekday = fields.Selection(WEEKDAY_SELECTION, compute='_compute_recurrence', readonly=False)
byday = fields.Selection(BYDAY_SELECTION, compute='_compute_recurrence', readonly=False)
@@ -280,8 +296,10 @@ class Meeting(models.Model):
@api.depends('allday', 'start', 'stop')
def _compute_dates(self):
""" Adapt the value of start_date(time)/stop_date(time) according to start/stop fields and allday. Also, compute
the duration for not allday meeting ; otherwise the duration is set to zero, since the meeting last all the day.
""" Adapt the value of start_date(time)/stop_date(time)
according to start/stop fields and allday. Also, compute
the duration for not allday meeting ; otherwise the
duration is set to zero, since the meeting last all the day.
"""
for meeting in self:
if meeting.allday and meeting.start and meeting.stop:
@@ -380,8 +398,7 @@ class Meeting(models.Model):
if idate:
if allday:
return idate
else:
return idate.replace(tzinfo=pytz.timezone('UTC'))
return idate.replace(tzinfo=pytz.timezone('UTC'))
return False
try:
@@ -749,8 +766,6 @@ class Meeting(models.Model):
private_fields = set(groupby) - self._get_public_fields()
if not self.env.su and private_fields:
raise UserError(_("Grouping by %s is not allowed." % ', '.join([self._fields[field_name].string for field_name in private_fields])))
if 'date' in groupby:
raise UserError(_('Group by date is not supported, use the calendar view instead.'))
return super(Meeting, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
def unlink(self):
@@ -797,7 +812,6 @@ class Meeting(models.Model):
attendee = self.attendee_ids.filtered(lambda x: x.partner_id == self.env.user.partner_id)
if status == 'accepted':
return attendee.do_accept()
elif status == 'declined':
if status == 'declined':
return attendee.do_decline()
else:
return attendee.do_tentative()
return attendee.do_tentative()
+19 -12
View File
@@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime, time
import pytz
from datetime import datetime, time
from dateutil import rrule
from dateutil.relativedelta import relativedelta, MO
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models, _
from odoo.exceptions import UserError
@@ -91,9 +91,12 @@ class RecurrenceRule(models.Model):
_description = 'Event Recurrence Rule'
name = fields.Char(compute='_compute_name', store=True)
base_event_id = fields.Many2one('calendar.event', ondelete='set null', copy=False)
base_event_id = fields.Many2one(
'calendar.event', ondelete='set null', copy=False) # store=False ?
calendar_event_ids = fields.One2many('calendar.event', 'recurrence_id')
event_tz = fields.Selection(_tz_get, string='Timezone', default=lambda self: self.env.context.get('tz') or self.env.user.tz)
event_tz = fields.Selection(
_tz_get, string='Timezone',
default=lambda self: self.env.context.get('tz') or self.env.user.tz)
rrule = fields.Char(compute='_compute_rrule', inverse='_inverse_rrule', store=True)
dtstart = fields.Datetime(compute='_compute_dtstart')
rrule_type = fields.Selection(RRULE_TYPE_SELECTION, default='weekly')
@@ -132,8 +135,8 @@ class RecurrenceRule(models.Model):
if recurrence.rrule_type == 'weeky':
weekdays = recurrence._get_week_days()
fields = (self._fields[weekday_to_field(w)] for w in weekdays)
on = _("on %s,") % ", ".join([field.string for field in fields])
weekday_fields = (self._fields[weekday_to_field(w)] for w in weekdays)
on = _("on %s,") % ", ".join([field.string for field in weekday_fields])
elif recurrence.rrule_type == 'monthly':
if recurrence.month_by == 'day':
weekday_label = dict(BYDAY_SELECTION)[recurrence.byday]
@@ -154,7 +157,9 @@ class RecurrenceRule(models.Model):
for recurrence in self:
recurrence.dtstart = start_mapping.get(recurrence.id)
@api.depends('byday', 'until', 'rrule_type', 'month_by', 'interval', 'count', 'end_type', 'mo', 'tu', 'we', 'th', 'fr', 'sa', 'su', 'day', 'weekday')
@api.depends(
'byday', 'until', 'rrule_type', 'month_by', 'interval', 'count', 'end_type',
'mo', 'tu', 'we', 'th', 'fr', 'sa', 'su', 'day', 'weekday')
def _compute_rrule(self):
for recurrence in self:
recurrence.rrule = recurrence._rrule_serialize()
@@ -168,7 +173,8 @@ class RecurrenceRule(models.Model):
def _reconcile_events(self, ranges):
"""
:param ranges: iterable of tuples (datetime_start, datetime_stop)
:return: tuple (events of the recurrence already in sync with ranges, and ranges not covered by any events)
:return: tuple (events of the recurrence already in sync with ranges,
and ranges not covered by any events)
"""
ranges = set(ranges)
@@ -328,16 +334,17 @@ class RecurrenceRule(models.Model):
def _get_start_of_period(self, dt):
if self.rrule_type == 'daily':
return dt
start = dt
elif self.rrule_type == 'weekly':
lang = self.env['res.lang']._lang_get(self.env.user.lang)
week_start = int(lang.week_start) # lang.week_start ranges from '1' to '7'
week_start = rrule.weekday(week_start - 1) # expects an int from 0 to 6
return dt + relativedelta(weekday=week_start(-1))
start = dt + relativedelta(weekday=week_start(-1))
elif self.rrule_type == 'monthly':
return dt + relativedelta(day=1)
start = dt + relativedelta(day=1)
elif self.rrule_type == 'yearly':
return dt
start = dt
return start
def _get_first_event(self, include_outliers=False):
if not self.calendar_event_ids:
+2 -2
View File
@@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from werkzeug.exceptions import BadRequest
from odoo import models
from odoo.http import request
from werkzeug.exceptions import BadRequest
class IrHttp(models.AbstractModel):
_inherit = 'ir.http'
+3 -3
View File
@@ -9,7 +9,8 @@ from odoo import api, fields, models
class Partner(models.Model):
_inherit = 'res.partner'
calendar_last_notif_ack = fields.Datetime('Last notification marked as read from base Calendar', default=fields.Datetime.now)
calendar_last_notif_ack = fields.Datetime(
'Last notification marked as read from base Calendar', default=fields.Datetime.now)
def get_attendee_detail(self, meeting_id):
""" Return a list of tuple (id, name, status)
@@ -32,6 +33,5 @@ class Partner(models.Model):
@api.model
def _set_calendar_last_notif_ack(self):
partner = self.env['res.users'].browse(self.env.context.get('uid',self.env.uid)).partner_id
partner = self.env['res.users'].browse(self.env.context.get('uid', self.env.uid)).partner_id
partner.write({'calendar_last_notif_ack': datetime.now()})
return
+6 -2
View File
@@ -23,9 +23,13 @@ class Users(models.Model):
return ['&', '|',
'&',
'|', ['start', '>=', fields.Datetime.to_string(start_dt)], ['stop', '>=', fields.Datetime.to_string(start_dt)],
'|',
['start', '>=', fields.Datetime.to_string(start_dt)],
['stop', '>=', fields.Datetime.to_string(start_dt)],
['start', '<=', fields.Datetime.to_string(end_dt)],
'&', ['allday', '=', True], ['start_date', '=', fields.Date.to_string(start_date)],
'&',
['allday', '=', True],
['start_date', '=', fields.Date.to_string(start_date)],
('attendee_ids.partner_id', '=', self.env.user.partner_id.id)]
@api.model
@@ -5,21 +5,21 @@
<record id="calendar_event_rule_my" model="ir.rule">
<field name="name">Own events</field>
<field ref="model_calendar_event" name="model_id"/>
<field name="domain_force">[('partner_ids','in',user.partner_id.id)]</field>
<field name="domain_force">[('partner_ids', 'in', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
</record>
<record id="calendar_event_rule_employee" model="ir.rule">
<field ref="model_calendar_event" name="model_id"/>
<field name="name">All Calendar Event for employees</field>
<field name="domain_force">[(1,'=',1)]</field>
<field name="domain_force">[(1, '=', 1)]</field>
<field eval="[(4,ref('base.group_user'))]" name="groups"/>
</record>
<record id="calendar_attendee_rule_my" model="ir.rule">
<field name="name">Own attendees</field>
<field ref="model_calendar_attendee" name="model_id"/>
<field name="domain_force">[(1,'=',1)]</field>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
</record>
@@ -10,7 +10,6 @@ var WebClient = require('web.WebClient');
var FieldMany2ManyTags = relationalFields.FieldMany2ManyTags;
var CalendarNotification = Notification.extend({
template: "CalendarNotification",
xmlDependencies: (Notification.prototype.xmlDependencies || [])
@@ -22,7 +22,7 @@ odoo.define('calendar.CalendarController', function (require) {
},
}],
}).open();
})
});
},
// TODO factorize duplicated code
@@ -15,10 +15,7 @@ odoo.define('calendar.CalendarModel', function (require) {
'recurrence_update': event.recurrenceUpdate,
});
}
});
return CalendarModel;
});
+9 -9
View File
@@ -251,7 +251,7 @@ class TestCalendar(SavepointCaseWithUserDemo):
Check that mail are NOT sent to the attendees when detaching a recurring event
"""
def _test_one_mail_per_attendee(self, m, partners):
def _test_one_mail_per_attendee(self, partners):
# check that every attendee receive a (single) mail for the event
for partner in partners:
mail = self.env['mail.message'].sudo().search([
@@ -260,9 +260,9 @@ class TestCalendar(SavepointCaseWithUserDemo):
self.assertEqual(len(mail), 1)
partners = [
self.env['res.partner'].create({'name':'testuser0','email': u'bob@example.com'}),
self.env['res.partner'].create({'name':'testuser1','email': u'alice@example.com'}),
]
self.env['res.partner'].create({'name': 'testuser0', 'email': u'bob@example.com'}),
self.env['res.partner'].create({'name': 'testuser1', 'email': u'alice@example.com'}),
]
partner_ids = [(6, False, [p.id for p in partners]),]
now = fields.Datetime.now()
m = self.CalendarEvent.create({
@@ -276,13 +276,13 @@ class TestCalendar(SavepointCaseWithUserDemo):
})
# every partner should have 1 mail sent
_test_one_mail_per_attendee(self, m, partners)
_test_one_mail_per_attendee(self, partners)
# adding more partners to the event
partners.extend([
self.env['res.partner'].create({'name':'testuser2','email': u'marc@example.com'}),
self.env['res.partner'].create({'name':'testuser3','email': u'carl@example.com'}),
self.env['res.partner'].create({'name':'testuser4','email': u'alain@example.com'}),
self.env['res.partner'].create({'name': 'testuser2', 'email': u'marc@example.com'}),
self.env['res.partner'].create({'name': 'testuser3', 'email': u'carl@example.com'}),
self.env['res.partner'].create({'name': 'testuser4', 'email': u'alain@example.com'}),
])
partner_ids = [(6, False, [p.id for p in partners]),]
m.write({
@@ -291,4 +291,4 @@ class TestCalendar(SavepointCaseWithUserDemo):
})
# more email should be sent
_test_one_mail_per_attendee(self, m, partners)
_test_one_mail_per_attendee(self, partners)
+1
View File
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
from . import controllers
+10 -16
View File
@@ -8,10 +8,7 @@ import logging
import requests
from werkzeug import urls
from odoo import api, fields, models, registry, _
from odoo.exceptions import UserError
from odoo.http import request
from odoo import api, fields, models, _
_logger = logging.getLogger(__name__)
@@ -113,7 +110,7 @@ class GoogleService(models.AbstractModel):
'redirect_uri': base_url + '/google_account/authentication'
}
try:
dummy, response, dummy = self._do_request(GOOGLE_TOKEN_ENDPOINT, params=data, headers=headers, type='POST', preuri='')
dummy, response, dummy = self._do_request(GOOGLE_TOKEN_ENDPOINT, params=data, headers=headers, method='POST', preuri='')
access_token = response.get('access_token')
refresh_token = response.get('refresh_token')
ttl = response.get('expires_in')
@@ -122,14 +119,13 @@ class GoogleService(models.AbstractModel):
error_msg = _("Something went wrong during your token generation. Maybe your Authorization Code is invalid")
raise self.env['res.config.settings'].get_config_warning(error_msg)
# TODO JEM : remove preuri param, and rename type into method
@api.model
def _do_request(self, uri, params=None, headers=None, type='POST', preuri="https://www.googleapis.com", timeout=TIMEOUT):
def _do_request(self, uri, params=None, headers=None, method='POST', preuri="https://www.googleapis.com", timeout=TIMEOUT):
""" Execute the request to Google API. Return a tuple ('HTTP_CODE', 'HTTP_RESPONSE')
:param uri : the url to contact
:param params : dict or already encoded parameters for the request to make
:param headers : headers of request
:param type : the method to use to make the request
:param method : the method to use to make the request
:param preuri : pre url to prepend to param uri.
"""
if params is None:
@@ -137,16 +133,16 @@ class GoogleService(models.AbstractModel):
if headers is None:
headers = {}
_logger.debug("Uri: %s - Type : %s - Headers: %s - Params : %s !", (uri, type, headers, params))
_logger.debug("Uri: %s - Type : %s - Headers: %s - Params : %s !", (uri, method, headers, params))
ask_time = fields.Datetime.now()
try:
if type.upper() in ('GET', 'DELETE'):
res = requests.request(type.lower(), preuri + uri, params=params, timeout=timeout)
elif type.upper() in ('POST', 'PATCH', 'PUT'):
res = requests.request(type.lower(), preuri + uri, data=params, headers=headers, timeout=timeout)
if method.upper() in ('GET', 'DELETE'):
res = requests.request(method.lower(), preuri + uri, params=params, timeout=timeout)
elif method.upper() in ('POST', 'PATCH', 'PUT'):
res = requests.request(method.lower(), preuri + uri, data=params, headers=headers, timeout=timeout)
else:
raise Exception(_('Method not supported [%s] not in [GET, POST, PUT, PATCH or DELETE]!') % (type))
raise Exception(_('Method not supported [%s] not in [GET, POST, PUT, PATCH or DELETE]!') % (method))
res.raise_for_status()
status = res.status_code
@@ -160,8 +156,6 @@ class GoogleService(models.AbstractModel):
except:
pass
except requests.HTTPError as error:
# if error.response.status_code == 401:
# raise InvalidToken
if error.response.status_code in (204, 404):
status = error.response.status_code
response = ""
-52
View File
@@ -1,52 +0,0 @@
Google Calendar
---------------
Get your meetings, your leaves... Get your calendar anywhere and never
forget an event, fully synchronized with the Odoo <a href="https://www.odoo.com/page/crm">Open Source CRM</a>.
Keep an eye on your events
--------------------------
See easily the purpose of the meeting, the start time and also the
attendee(s)... All that without click on anything...
Create so easily an event
-------------------------
In just one click you can create an event... You can drag and drop your event
if you want moved it to another timing. You can shrink or extend the event if
you need to change the start's hours or the duration of your meeting.
Create recurrent event
----------------------
You can also create recurrent events with only one event. You need to create
an event each monday of the week ? With only one it's possible, you could
specify the recurrence and if one of this event is moved, or deleted, it's not
a problem, you can untie your event from the others recurrences.
See all events you wants
------------------------
See in your calendar, the event from others peoples where your are attendee,
but also their events by simply adding your favorites coworkers. Every
coworker will have their own color in your calendar, and every attendee will
have their avatar in the event...
Get an email
------------
You will receive an email at creation of an event where you are attendee, but
also when this event is updated for some fields as date start, ...
Be notified
-----------
You can ask to have a alarm of type 'notification' in your Odoo. You will have
a notification in you Odoo which ever the page you are.
Google Calendar
---------------
With this plugin, you can synchronize your Odoo calendar with Google Calendar.
+6 -3
View File
@@ -10,9 +10,12 @@ class GoogleCalendarController(http.Controller):
@http.route('/google_calendar/sync_data', type='json', auth='user')
def sync_data(self, model, **kw):
""" This route/function is called when we want to synchronize Odoo calendar with Google Calendar
Function return a dictionary with the status : need_config_from_admin, need_auth, need_refresh, success if not calendar_event
The dictionary may contains an url, to allow Odoo Client to redirect user on this URL for authorization for example
""" This route/function is called when we want to synchronize Odoo
calendar with Google Calendar.
Function return a dictionary with the status : need_config_from_admin, need_auth,
need_refresh, success if not calendar_event
The dictionary may contains an url, to allow Odoo Client to redirect user on
this URL for authorization for example
"""
if model == 'calendar.event':
GoogleCal = GoogleCalendarService(request.env['google.service'])
+5 -5
View File
@@ -12,7 +12,8 @@ class Meeting(models.Model):
_name = 'calendar.event'
_inherit = ['calendar.event', 'google.calendar.sync']
google_id = fields.Char('Google Calendar Event Id', compute='_compute_google_id', store=True, readonly=False)
google_id = fields.Char(
'Google Calendar Event Id', compute='_compute_google_id', store=True, readonly=False)
@api.depends('recurrence_id.google_id')
def _compute_google_id(self):
@@ -65,9 +66,6 @@ class Meeting(models.Model):
'recurrency': google_event.is_recurrent()
}
if google_event.reminders and google_event.reminders.get('useDefault'):
values
if not google_event.is_recurrence():
values['google_id'] = google_event.id
if google_event.start.get('dateTime'):
@@ -91,7 +89,9 @@ class Meeting(models.Model):
emails = [a.get('email') for a in google_event.attendees]
existing_attendees = self.env['calendar.attendee']
if google_event.exists(self.env):
existing_attendees = self.env['calendar.attendee'].search([('event_id', '=', google_event.odoo_id(self.env)), ('email', 'in', emails)])
existing_attendees = self.env['calendar.attendee'].search([
('event_id', '=', google_event.odoo_id(self.env)),
('email', 'in', emails)])
attendees_by_emails = {a.email: a for a in existing_attendees}
for attendee in google_event.attendees:
email = attendee.get('email')
@@ -8,7 +8,6 @@ from odoo import api, fields, models
from addons.google_calendar.utils.google_calendar import GoogleCalendarService
class RecurrenceRule(models.Model):
_name = 'calendar.recurrence'
_inherit = ['calendar.recurrence', 'google.calendar.sync']
@@ -121,4 +120,4 @@ class RecurrenceRule(models.Model):
'%s_odoo_id' % self.env.cr.dbname: self.id,
},
}
return values
return values
+5 -7
View File
@@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
from contextlib import contextmanager
from functools import wraps
import requests
import pytz
from functools import wraps
from dateutil.parser import parse
from contextlib import contextmanager
from odoo import api, fields, models, registry, _
from odoo.tools import ormcache_context
@@ -52,7 +53,7 @@ def google_calendar_token(user):
# Rollback manually first to avoid concurrent access errors/deadlocks.
user.env.cr.rollback()
with user.pool.cursor() as cr:
env= user.env(cr=cr)
env = user.env(cr=cr)
user.with_env(env)._set_auth_tokens(False, False, 0)
raise e
@@ -155,7 +156,7 @@ class GoogleSync(models.AbstractModel):
cancelled_odoo._cancel()
synced_records = new_odoo + cancelled_odoo
for gevent in existing - cancelled :
for gevent in existing - cancelled:
# Last updated wins.
# This could be dangerous if google server time and odoo server time are different
updated = parse(gevent.updated)
@@ -169,7 +170,6 @@ class GoogleSync(models.AbstractModel):
@after_commit
def _google_delete(self, google_service: GoogleCalendarService, google_id, timeout=TIMEOUT):
print('DELETE', google_id)
with google_calendar_token(self.env.user.sudo()) as token:
if token:
google_service.delete(google_id, token=token, timeout=timeout)
@@ -177,7 +177,6 @@ class GoogleSync(models.AbstractModel):
@after_commit
def _google_patch(self, google_service: GoogleCalendarService, google_id, values, timeout=TIMEOUT):
print('Patch', google_id)
with google_calendar_token(self.env.user.sudo()) as token:
if token:
google_service.patch(google_id, values, token=token, timeout=timeout)
@@ -185,7 +184,6 @@ class GoogleSync(models.AbstractModel):
@after_commit
def _google_insert(self, google_service: GoogleCalendarService, values, timeout=TIMEOUT):
print('insert')
if not values:
return
with google_calendar_token(self.env.user.sudo()) as token:
+1 -1
View File
@@ -62,7 +62,7 @@ class User(models.Model):
}
try:
dummy, response, dummy = self.env['google.service']._do_request(GOOGLE_TOKEN_ENDPOINT, params=data, headers=headers, type='POST', preuri='')
dummy, response, dummy = self.env['google.service']._do_request(GOOGLE_TOKEN_ENDPOINT, params=data, headers=headers, method='POST', preuri='')
ttl = response.get('expires_in')
self.write({
'google_calendar_token': response.get('access_token'),
@@ -262,7 +262,7 @@ class TestSyncGoogle2Odoo(SavepointCase):
'reminders': {'useDefault': True},
'updated': self.now,
},
{ # Third event has been moved
{ # Third event has been moved
'id': '%s_20200113' % recurrence_id,
'summary': 'Pricing new update',
'start': {'date': '2020-01-18'},
@@ -308,4 +308,3 @@ class TestSyncGoogle2Odoo(SavepointCase):
self.assertEqual(events[1].start_date, date(2020, 1, 15))
self.assertEqual(events[0].google_id, '%s_20200108' % recurrence_id)
self.assertEqual(events[1].google_id, '%s_20200115' % recurrence_id)
@@ -184,7 +184,6 @@ class TestSyncOdoo2Google(SavepointCase):
'recurrency': True,
'rrule': 'FREQ=WEEKLY;COUNT=2;BYDAY=WE',
})
event.recurrence_id
to_delete = self.env['calendar.event'].with_context(active_test=False).search([('google_id', '=', google_id)])
self.assertTrue(to_delete)
self.assertFalse(to_delete.active)
@@ -37,25 +37,20 @@ class GoogleCalendarService():
if sync_token:
params['syncToken'] = sync_token
try:
status, data, time = self.google_service._do_request(url, params, headers, type='GET', timeout=timeout)
status, data, time = self.google_service._do_request(url, params, headers, method='GET', timeout=timeout)
except requests.HTTPError as e:
if e.response.status_code == 410 and 'fullSyncRequired' in str(e.response.content):
raise InvalidSyncToken("Invalid sync token. Full sync required")
raise e
import pprint
pprint.pprint(data)
events = data.get('items', [])
next_page_token = data.get('nextPageToken')
while next_page_token:
params = {'access_token': token, 'pageToken': next_page_token}
status, data, time = self.google_service._do_request(url, params, headers, type='GET', timeout=timeout)
status, data, time = self.google_service._do_request(url, params, headers, method='GET', timeout=timeout)
next_page_token = data.get('nextPageToken')
events += data.get('items', [])
pprint.pprint(data)
next_sync_token = data.get('nextSyncToken')
default_reminders = data.get('defaultReminders')
@@ -63,31 +58,26 @@ class GoogleCalendarService():
@requires_auth_token
def insert(self, values, token=None, timeout=TIMEOUT):
print("INSERT")
url = "/calendar/v3/calendars/primary/events"
headers = {'Content-type': 'application/json', 'Authorization': 'Bearer %s' % token}
if not values.get('id'):
values['id'] = uuid4().hex
import pprint
pprint.pprint(values)
self.google_service._do_request(url, json.dumps(values), headers, type='POST', timeout=timeout)
self.google_service._do_request(url, json.dumps(values), headers, method='POST', timeout=timeout)
return values['id']
@requires_auth_token
def patch(self, event_id, values, token=None, timeout=TIMEOUT):
print("PATCH")
url = "/calendar/v3/calendars/primary/events/%s" % event_id
headers = {'Content-type': 'application/json', 'Authorization': 'Bearer %s' % token}
self.google_service._do_request(url, json.dumps(values), headers, type='PUT', timeout=timeout)
self.google_service._do_request(url, json.dumps(values), headers, method='PUT', timeout=timeout)
@requires_auth_token
def delete(self, event_id, token=None, timeout=TIMEOUT):
print("DELETE")
url = "/calendar/v3/calendars/primary/events/%s" % event_id
headers = {'Content-type': 'application/json'}
params = {'access_token': token}
try:
self.google_service._do_request(url, params, headers=headers, type='DELETE', timeout=timeout)
self.google_service._do_request(url, params, headers=headers, method='DELETE', timeout=timeout)
except requests.HTTPError as e:
# For some unknown reason Google can also return a 403 response when the event is already cancelled.
if e.response.status_code not in (410, 403):
+2 -2
View File
@@ -169,6 +169,6 @@ class GoogleEvent(abc.Set):
def _get_model(self, env):
if all([e.is_recurrence() for e in self]):
return env['calendar.recurrence']
elif all([not e.is_recurrence() for e in self]):
if all([not e.is_recurrence() for e in self]):
return env['calendar.event']
raise TypeError("Mixing Google events and Google recurrences")
raise TypeError("Mixing Google events and Google recurrences")
@@ -1 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import reset_account
+12 -10
View File
@@ -12,13 +12,13 @@ class ResetGoogleAccount(models.TransientModel):
_description = 'Google Calendar Account Reset'
user_id = fields.Many2one('res.users', required=True)
delete_policy = fields.Selection([
('dont_delete', "Leave them untouched"),
('delete_google', "Delete from the current Google Calendar account"),
('delete_odoo', "Delete from Odoo"),
('delete_both', "Delete from both"),
], string="User's Existing Events", required=True, default='dont_delete',
help="This will only affect events for which the user is the owner")
delete_policy = fields.Selection(
[('dont_delete', "Leave them untouched"),
('delete_google', "Delete from the current Google Calendar account"),
('delete_odoo', "Delete from Odoo"),
('delete_both', "Delete from both"),
], string="User's Existing Events", required=True, default='dont_delete',
help="This will only affect events for which the user is the owner")
sync_policy = fields.Selection([
('new', "Synchronize only new events"),
('all', "Synchronize all existing events"),
@@ -27,13 +27,15 @@ class ResetGoogleAccount(models.TransientModel):
def reset_account(self):
google = GoogleCalendarService(self.env['google.service'])
events = self.env['calendar.event'].search([('user_id', '=', self.user_id.id), ('google_id', '!=', False)])
if self.delete_policy in ('delete_google' , 'delete_both'):
events = self.env['calendar.event'].search([
('user_id', '=', self.user_id.id),
('google_id', '!=', False)])
if self.delete_policy in ('delete_google', 'delete_both'):
with google_calendar_token(self.user_id) as token:
for event in events:
google.delete(event.google_id, token=token)
if self.delete_policy in ('delete_odoo' , 'delete_both'):
if self.delete_policy in ('delete_odoo', 'delete_both'):
events.google_id = False
events.unlink()