Permalink
Browse files

[IMP] mail, hr_holidays: improve channel_info

`channel_info` can be called on multiple channels especially during
the init_messaging. The current implementation will perform read and
other queries in the loop. This commits aims to refactor `channel_info`
in ordher to read all informations in database out of the loop.

Computation of member informations is now the same as for `direct_partner`
As a side effect direct partner will have an email adress and members will have
im_status and out_of_office message if the partner is in a
channel of type chat. It would be easier to compute im_status in all case but
this could create performances issues when calling channel info on channel with
hundreds of users.

channel_fetch_preview will make a query in database in any case but seems to work ok
in api multi. We can put it out of the loop.

44ac790 fix shouldn't be broken by this refactoring since we are no longer using the many2many to find direct_partners. As a side effect inactive partners will appear in members.
  • Loading branch information...
Xavier-Do committed Feb 7, 2019
1 parent c691acb commit 0da9d717ab9355ff01940eb70bab61778e2d7451
Showing with 60 additions and 45 deletions.
  1. +9 −16 addons/hr_holidays/models/mail_channel.py
  2. +51 −29 addons/mail/models/mail_channel.py
@@ -8,14 +8,10 @@ class Channel(models.Model):
_inherit = 'mail.channel'

@api.multi
def channel_info(self, extra_info=False):
channel_infos = super(Channel, self).channel_info(extra_info)
partners_on_leave = []
for channel_info in channel_infos:
if 'direct_partner' in channel_info:
for direct_partner in channel_info['direct_partner']:
if 'leave' in direct_partner['im_status']:
partners_on_leave.append(direct_partner['id'])
def partner_info(self, all_partners, direct_partners):
partner_infos = super(Channel, self).partner_info(all_partners, direct_partners)
# only search for leave out_of_office_message if im_status is on leave
partners_on_leave = [partner_id for partner_id in direct_partners.ids if 'leave' in partner_infos[partner_id]['im_status']]
if partners_on_leave:
now = fields.Datetime.now()
self.env.cr.execute('''SELECT res_users.partner_id as partner_id, hr_leave.out_of_office_message as out_of_office_message, hr_leave.date_to as date_to
@@ -26,11 +22,8 @@ def channel_info(self, extra_info=False):
AND hr_leave.date_from <= %s
AND hr_leave.date_to >= %s
AND res_users.partner_id in %s''', (now, now, tuple(partners_on_leave)))
out_of_office_info = dict(((res['partner_id'], res) for res in self.env.cr.dictfetchall()))
for channel_info in channel_infos:
if 'direct_partner' in channel_info:
for direct_partner in channel_info['direct_partner']:
if 'leave' in direct_partner['im_status']:
direct_partner['out_of_office_date_end'] = out_of_office_info.get(direct_partner['id'], {}).get('date_to')
direct_partner['out_of_office_message'] = out_of_office_info.get(direct_partner['id'], {}).get('out_of_office_message')
return channel_infos
out_of_office_infos = dict(((res['partner_id'], res) for res in self.env.cr.dictfetchall()))
for partner_id, out_of_office_info in out_of_office_infos.items():
partner_infos[partner_id]['out_of_office_date_end'] = out_of_office_info['date_to']
partner_infos[partner_id]['out_of_office_message'] = out_of_office_info['out_of_office_message']
return partner_infos
@@ -540,21 +540,48 @@ def _channel_message_notifications(self, message):
notifications.append([channel.uuid, dict(message_values)])
return notifications

@api.model
def partner_info(self, all_partners, direct_partners):
"""
Return the information needed by channel to display channel members
:param partners: list of res.parner():
:param partners: list of res.parner():
:returns: a list of {id', 'name', 'email'} for each partner and adds {im_status, out_of_office_message} for direct_partners.
:rtype : list(dict)
"""
partner_infos = {partner['id']: partner for partner in all_partners.sudo().read(['id', 'name', 'email'])}
# add im _status and out_of_office_message for direct_partners
direct_partners_im_status = {partner['id']: partner for partner in direct_partners.sudo().read(['im_status'])}
partner_infos.update(direct_partners_im_status)
for user in self.env['res.users'].search([('partner_id', 'in', direct_partners.ids), ('out_of_office_message', '!=', False)]):
if user.out_of_office_message:
partner_infos[user.partner_id.id]['out_of_office_message'] = user.out_of_office_message
return partner_infos

@api.multi
def channel_info(self, extra_info = False):
def channel_info(self, extra_info=False):
""" Get the informations header for the current channels
:returns a list of channels values
:rtype : list(dict)
"""
if not self:
return []
channel_infos = []

# all relations partner_channel on those channels
all_partner_channel = self.env['mail.channel.partner'].search([('channel_id', 'in', self.ids)])

# all partner infos on those channels
partner_infos = all_partner_channel.mapped('partner_id').read(['id', 'name', 'email'])
channel_dict = {channel.id: channel for channel in self}
all_partners = all_partner_channel.mapped('partner_id')
direct_channel_partners = all_partner_channel.filtered(lambda pc: channel_dict[pc.channel_id.id].channel_type == 'chat')
direct_partners = direct_channel_partners.mapped('partner_id')
partner_infos = self.partner_info(all_partners, direct_partners)

# add last message preview (only used in mobile)
addPreview = self._context.get('isMobile', False) or True
if addPreview:
channel_previews = {channel_preview['id']: channel_preview for channel_preview in self.channel_fetch_preview()}

# for each channel, build the information header and include the logged partner information
for channel in self:
info = {
'id': channel.id,
@@ -572,46 +599,39 @@ def channel_info(self, extra_info = False):
}
if extra_info:
info['info'] = extra_info
# add the partner for 'direct mesage' channel
if channel.channel_type == 'chat':
info['direct_partner'] = (channel.sudo()
.with_context(active_test=False)
.channel_partner_ids
.filtered(lambda p: p.id != self.env.user.partner_id.id)
.read(['id', 'name', 'im_status']))
for direct_partner in info['direct_partner']:
users = self.env['res.partner'].browse(direct_partner['id']).user_ids
if users:
direct_partner['out_of_office_message'] = users[0].out_of_office_message

# add last message preview (only used in mobile)
if self._context.get('isMobile', False):
last_message = channel.channel_fetch_preview()
if last_message:
info['last_message'] = last_message[0].get('last_message')
if addPreview:
if channel in channel_previews:
info['last_message'] = channel_previews[channel]

# listeners of the channel
channel_partners = all_partner_channel.filtered(lambda pc: channel.id == pc.channel_id.id)

# find the channel partner state, if logged user
partner_channel = self.env['mail.channel.partner']
if self.env.user and self.env.user.partner_id:
partner_channel = channel_partners.filtered(lambda pc: pc.partner_id.id == self.env.user.partner_id.id)
# add the partner for 'direct mesage' channel
if channel.channel_type == 'chat':
# direct_partner should be removed from channel info since we can find it from members and channel_type
# we keep it know to avoid change tests and javascript
direct_partner = direct_channel_partners.filtered(lambda pc: pc.partner_id.id != self.env.user.partner_id.id)
info['direct_partner'] = [partner_infos[direct_partner[0].partner_id.id]]
# add needaction and unread counter, since the user is logged
info['message_needaction_counter'] = channel.message_needaction_counter
info['message_unread_counter'] = channel.message_unread_counter

# add user session state, if available and if user is logged
if len(partner_channel.ids):
partner_channel = partner_channel[0]
info['state'] = partner_channel.fold_state or 'open'
info['is_minimized'] = partner_channel.is_minimized
info['seen_message_id'] = partner_channel.seen_message_id.id
info['custom_channel_name'] = partner_channel.custom_channel_name
# add user session state, if available and if user is logged
partner_channel = channel_partners.filtered(lambda pc: pc.partner_id.id == self.env.user.partner_id.id)
if partner_channel:
partner_channel = partner_channel[0]
info['state'] = partner_channel.fold_state or 'open'
info['is_minimized'] = partner_channel.is_minimized
info['seen_message_id'] = partner_channel.seen_message_id.id
info['custom_channel_name'] = partner_channel.custom_channel_name

# add members infos
partner_ids = channel_partners.mapped('partner_id').ids
info['members'] = [partner_info for partner_info in partner_infos if partner_info['id'] in partner_ids]
info['members'] = [partner_infos[partner] for partner in partner_ids]
info['seen_partners_info'] = [{
'partner_id': cp.partner_id.id,
'fetched_message_id': cp.fetched_message_id.id,
@@ -929,6 +949,8 @@ def _channel_fetch_listeners_where_clause(self, uuid):
@api.multi
def channel_fetch_preview(self):
""" Return the last message of the given channels """
if not self:
return []
self._cr.execute("""
SELECT mail_channel_id AS id, MAX(mail_message_id) AS message_id
FROM mail_message_mail_channel_rel

0 comments on commit 0da9d71

Please sign in to comment.