[IMP] calendar: Forward port of Saas 14.3 calendar perf yti

Forward port of https://github.com/odoo/odoo/pull/71808

closes odoo/odoo#72043

X-original-commit: 4f13a2214aed380c8aea984d3414caca506401bf
Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
Signed-off-by: Arnaud Joset <arj-odoo@users.noreply.github.com>
This commit is contained in:
fw-bot
2021-06-11 12:11:14 +00:00
committed by Arnaud Joset
parent 80d643c141
commit 36bca6eb52
5 changed files with 112 additions and 27 deletions
+5 -1
View File
@@ -4,6 +4,7 @@ import uuid
import base64
import logging
from collections import defaultdict
from odoo import api, fields, models, _
from odoo.addons.base.models.res_partner import _tz_get
from odoo.exceptions import UserError
@@ -76,11 +77,14 @@ class Attendee(models.Model):
raise UserError(_('You cannot duplicate a calendar attendee.'))
def _subscribe_partner(self):
mapped_followers = defaultdict(lambda: self.env['calendar.event'])
for event in self.event_id:
partners = (event.attendee_ids & self).partner_id - event.message_partner_ids
# 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)
mapped_followers[partners] |= event
for partners, events in mapped_followers.items():
events.message_subscribe(partner_ids=partners.ids)
def _unsubscribe_partner(self):
for event in self.event_id:
+35 -23
View File
@@ -68,6 +68,7 @@ class Meeting(models.Model):
if 'res_model_id' not in defaults and 'res_model_id' in fields and \
self.env.context.get('active_model') and self.env.context['active_model'] != 'calendar.event':
defaults['res_model_id'] = self.env['ir.model'].sudo().search([('model', '=', self.env.context['active_model'])], limit=1).id
defaults['res_model'] = self.env.context.get('active_model')
if 'res_id' not in defaults and 'res_id' in fields and \
defaults.get('res_model_id') and self.env.context.get('active_id'):
defaults['res_id'] = self.env.context['active_id']
@@ -354,25 +355,32 @@ class Meeting(models.Model):
for vals in vals_list
]
for values in vals_list:
# created from calendar: try to create an activity on the related record
if not values.get('activity_ids'):
defaults = self.default_get(['activity_ids', 'res_model_id', 'res_id', 'user_id'])
defaults = self.default_get(['activity_ids', 'res_model_id', 'res_id', 'user_id', 'res_model'])
meeting_activity_type = self.env['mail.activity.type'].search([('category', '=', 'meeting')], limit=1)
# get list of models ids and filter out None values directly
model_ids = list(filter(bool, {values.get('res_model_id', defaults.get('res_model_id')) for values in vals_list}))
model_name = defaults.get('res_model')
valid_activity_model_ids = model_name and self.env[model_name].sudo().browse(model_ids).filtered(lambda m: 'activity_ids' in m).ids or []
if meeting_activity_type and not defaults.get('activity_ids'):
for values in vals_list:
# created from calendar: try to create an activity on the related record
if values.get('activity_ids'):
continue
res_model_id = values.get('res_model_id', defaults.get('res_model_id'))
res_id = values.get('res_id', defaults.get('res_id'))
user_id = values.get('user_id', defaults.get('user_id'))
if not defaults.get('activity_ids') and res_model_id and res_id:
if hasattr(self.env[self.env['ir.model'].sudo().browse(res_model_id).model], 'activity_ids'):
meeting_activity_type = self.env['mail.activity.type'].search([('category', '=', 'meeting')], limit=1)
if meeting_activity_type:
activity_vals = {
'res_model_id': res_model_id,
'res_id': res_id,
'activity_type_id': meeting_activity_type.id,
}
if user_id:
activity_vals['user_id'] = user_id
values['activity_ids'] = [(0, 0, activity_vals)]
if not res_model_id or not res_id:
continue
if res_model_id not in valid_activity_model_ids:
continue
activity_vals = {
'res_model_id': res_model_id,
'res_id': res_id,
'activity_type_id': meeting_activity_type.id,
}
if user_id:
activity_vals['user_id'] = user_id
values['activity_ids'] = [(0, 0, activity_vals)]
# Automatically add the current partner when creating an event if there is none (happens when we quickcreate an event)
self_partner_id = [(4, self.env.user.partner_id.id)]
@@ -556,10 +564,13 @@ class Meeting(models.Model):
added_partner_ids += [command[1]] if command[1] not in self.partner_ids.ids else []
# commands 0 and 1 not supported
attendees_to_unlink = self.env['calendar.attendee'].search([
('event_id', 'in', self.ids),
('partner_id', 'in', removed_partner_ids),
])
if not self:
attendees_to_unlink = self.env['calendar.attendee']
else:
attendees_to_unlink = self.env['calendar.attendee'].search([
('event_id', 'in', self.ids),
('partner_id', 'in', removed_partner_ids),
])
attendee_commands += [[2, attendee.id] for attendee in attendees_to_unlink] # Removes and delete
attendee_commands += [
@@ -650,8 +661,8 @@ class Meeting(models.Model):
def _setup_alarms(self):
""" Schedule cron triggers for future events """
cron = self.env.ref('calendar.ir_cron_scheduler_alarm').sudo()
alarm_manager = self.env['calendar.alarm_manager']
alarm_types = self._get_trigger_alarm_types()
events_to_notify = self.env['calendar.event']
for event in self:
for alarm in (alarm for alarm in event.alarm_ids if alarm.alarm_type in alarm_types):
@@ -661,8 +672,9 @@ class Meeting(models.Model):
cron._trigger(at=at)
if any(alarm.alarm_type == 'notification' for alarm in event.alarm_ids):
# filter events before notifying attendees through calendar_alarm_manager
need_notifs = event.filtered(lambda ev: ev.alarm_ids and ev.stop >= fields.Datetime.now())
alarm_manager._notify_next_alarm(need_notifs.partner_ids.ids)
events_to_notify |= event.filtered(lambda ev: ev.alarm_ids and ev.stop >= fields.Datetime.now())
if events_to_notify:
self.env['calendar.alarm_manager']._notify_next_alarm(events_to_notify.partner_ids.ids)
# ------------------------------------------------------------
# RECURRENCY
@@ -8,8 +8,10 @@ from unittest.mock import patch
from odoo.addons.google_calendar.utils.google_calendar import GoogleCalendarService
from odoo.addons.google_calendar.models.res_users import User
from odoo.addons.google_calendar.tests.test_sync_common import TestSyncGoogle, patch_api
from odoo.tests.common import users, warmup
from odoo.tests import tagged
@tagged('odoo2google')
@patch.object(User, '_get_google_calendar_token', lambda user: 'dummy-token')
class TestSyncOdoo2Google(TestSyncGoogle):
@@ -53,6 +55,73 @@ class TestSyncOdoo2Google(TestSyncGoogle):
'extendedProperties': {'shared': {'%s_odoo_id' % self.env.cr.dbname: event.id}}
})
@patch_api
@users('__system__')
@warmup
def test_event_creation_perf(self):
EVENT_COUNT = 100
partners = self.env['res.partner'].create([
{'name': 'Jean-Luc %s' % (i), 'email': 'jean-luc-%s@opoo.com' % (i)} for i in range(EVENT_COUNT)])
alarm = self.env['calendar.alarm'].create({
'name': 'Notif',
'alarm_type': 'notification',
'interval': 'minutes',
'duration': 18,
})
partner_model = self.env.ref('base.model_res_partner')
partner = self.env['res.partner'].search([], limit=1)
with self.assertQueryCount(__system__=1211):
events = self.env['calendar.event'].create([{
'name': "Event %s" % (i),
'start': datetime(2020, 1, 15, 8, 0),
'stop': datetime(2020, 1, 15, 18, 0),
'partner_ids': [(4, partners[i].id), (4, self.env.user.partner_id.id)],
'alarm_ids': [(4, alarm.id)],
'privacy': 'private',
'need_sync': False,
'res_model_id': partner_model.id,
'res_id': partner.id,
} for i in range(EVENT_COUNT)])
events._sync_odoo2google(self.google_service)
with self.assertQueryCount(__system__=129):
events.unlink()
@patch_api
@users('__system__')
@warmup
def test_recurring_event_creation_perf(self):
partner = self.env['res.partner'].create({'name': 'Jean-Luc', 'email': 'jean-luc@opoo.com'})
alarm = self.env['calendar.alarm'].create({
'name': 'Notif',
'alarm_type': 'notification',
'interval': 'minutes',
'duration': 18,
})
partner_model = self.env.ref('base.model_res_partner')
partner = self.env['res.partner'].search([], limit=1)
with self.assertQueryCount(__system__=3637):
event = self.env['calendar.event'].create({
'name': "Event",
'start': datetime(2020, 1, 15, 8, 0),
'stop': datetime(2020, 1, 15, 18, 0),
'partner_ids': [(4, partner.id)],
'alarm_ids': [(4, alarm.id)],
'privacy': 'private',
'need_sync': False,
'interval': 1,
'recurrency': True,
'rrule_type': 'daily',
'end_type': 'forever',
'res_model_id': partner_model.id,
'res_id': partner.id,
})
with self.assertQueryCount(__system__=2191):
event.unlink()
def test_event_without_user(self):
event = self.env['calendar.event'].create({
'name': "Event",
@@ -313,7 +313,7 @@ class TestCompanyLeave(TransactionCase):
})
company_leave._compute_date_from_to()
with self.assertQueryCount(__system__=843, admin=865):
with self.assertQueryCount(__system__=845, admin=865):
# Original query count: 1987
# Without tracking/activity context keys: 5154
company_leave.action_validate()
@@ -31,7 +31,7 @@ class TestWorkEntryHolidaysPerformance(TestWorkEntryHolidaysBase):
self.richard_emp.generate_work_entries(date(2018, 1, 1), date(2018, 1, 2))
leave = self.create_leave(datetime(2018, 1, 1, 7, 0), datetime(2018, 1, 1, 18, 0))
with self.assertQueryCount(__system__=94, admin=95):
with self.assertQueryCount(__system__=96, admin=97):
leave.action_validate()
leave.action_refuse()