Thibault Delavallée (OpenERP) has proposed merging
lp:~openerp-dev/openobject-addons/trunk-join-speedup-tde into
lp:openobject-addons.
Requested reviews:
OpenERP Core Team (openerp)
For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-addons/trunk-join-speedup-tde/+merge/136624
Perform auto-join and needaction speed update
This addons revision adds the auto_join attribute on notification_ids of
mail.message, to speedup the loading of the various mailboxes.
--
https://code.launchpad.net/~openerp-dev/openobject-addons/trunk-join-speedup-tde/+merge/136624
Your team OpenERP R&D Team is subscribed to branch
lp:~openerp-dev/openobject-addons/trunk-join-speedup-tde.
=== modified file 'mail/mail_followers.py'
--- mail/mail_followers.py 2012-11-15 12:38:51 +0000
+++ mail/mail_followers.py 2012-11-29 15:15:38 +0000
@@ -19,7 +19,6 @@
#
##############################################################################
-from openerp import SUPERUSER_ID
from osv import osv
from osv import fields
import tools
@@ -63,12 +62,15 @@
'partner_id': fields.many2one('res.partner', string='Contact',
ondelete='cascade', required=True, select=1),
'read': fields.boolean('Read', select=1),
+ 'starred': fields.boolean('Starred', select=1,
+ help='Starred message that goes into the todo mailbox'),
'message_id': fields.many2one('mail.message', string='Message',
ondelete='cascade', required=True, select=1),
}
_defaults = {
'read': False,
+ 'starred': False,
}
def init(self, cr):
@@ -83,31 +85,6 @@
return super(mail_notification, self).create(cr, uid, vals, context=context)
return False
- def set_message_read(self, cr, uid, msg_ids, read=None, context=None):
- """ Set messages as (un)read. Technically, the notifications related
- to uid are set to (un)read. If for some msg_ids there are missing
- notifications (i.e. due to load more or thread parent fetching),
- they are created.
-
- :param bool read: (un)read notification
- """
- user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
- notif_ids = self.search(cr, uid, [
- ('partner_id', '=', user_pid),
- ('message_id', 'in', msg_ids)
- ], context=context)
-
- # all message have notifications: already set them as (un)read
- if len(notif_ids) == len(msg_ids):
- return self.write(cr, uid, notif_ids, {'read': read}, context=context)
-
- # some messages do not have notifications: find which one, create notification, update read status
- notified_msg_ids = [notification.message_id.id for notification in self.browse(cr, uid, notif_ids, context=context)]
- to_create_msg_ids = list(set(msg_ids) - set(notified_msg_ids))
- for msg_id in to_create_msg_ids:
- self.create(cr, uid, {'partner_id': user_pid, 'read': read, 'message_id': msg_id}, context=context)
- return self.write(cr, uid, notif_ids, {'read': read}, context=context)
-
def get_partners_to_notify(self, cr, uid, message, context=None):
""" Return the list of partners to notify, based on their preferences.
=== modified file 'mail/mail_message.py'
--- mail/mail_message.py 2012-11-28 15:19:39 +0000
+++ mail/mail_message.py 2012-11-29 15:15:38 +0000
@@ -55,7 +55,7 @@
_message_read_limit = 30
_message_read_fields = ['id', 'parent_id', 'model', 'res_id', 'body', 'subject', 'date', 'to_read', 'email_from',
- 'type', 'vote_user_ids', 'attachment_ids', 'author_id', 'partner_ids', 'record_name', 'favorite_user_ids']
+ 'type', 'vote_user_ids', 'attachment_ids', 'author_id', 'partner_ids', 'record_name']
_message_record_name_length = 18
_message_read_more_limit = 1024
@@ -63,7 +63,7 @@
# protection for `default_type` values leaking from menu action context (e.g. for invoices)
if context and context.get('default_type') and context.get('default_type') not in self._columns['type'].selection:
context = dict(context, default_type=None)
- return super(mail_message, self).default_get(cr, uid, fields, context=context)
+ return super(mail_message, self).default_get(cr, uid, fields, context=context)
def _shorten_name(self, name):
if len(name) <= (self._message_record_name_length + 3):
@@ -98,15 +98,26 @@
def _search_to_read(self, cr, uid, obj, name, domain, context=None):
""" Search for messages to read by the current user. Condition is
inversed because we search unread message on a read column. """
- if domain[0][2]:
- read_cond = "(read = False OR read IS NULL)"
- else:
- read_cond = "read = True"
+ return ['&', ('notification_ids.partner_id.user_ids', 'in', [uid]), ('notification_ids.read', '=', not domain[0][2])]
+
+ def _get_starred(self, cr, uid, ids, name, arg, context=None):
+ """ Compute if the message is unread by the current user. """
+ res = dict((id, False) for id in ids)
partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
- cr.execute("SELECT message_id FROM mail_notification "\
- "WHERE partner_id = %%s AND %s" % read_cond,
- (partner_id,))
- return [('id', 'in', [r[0] for r in cr.fetchall()])]
+ notif_obj = self.pool.get('mail.notification')
+ notif_ids = notif_obj.search(cr, uid, [
+ ('partner_id', 'in', [partner_id]),
+ ('message_id', 'in', ids),
+ ('starred', '=', True),
+ ], context=context)
+ for notif in notif_obj.browse(cr, uid, notif_ids, context=context):
+ res[notif.message_id.id] = True
+ return res
+
+ def _search_starred(self, cr, uid, obj, name, domain, context=None):
+ """ Search for messages to read by the current user. Condition is
+ inversed because we search unread message on a read column. """
+ return ['&', ('notification_ids.partner_id.user_ids', 'in', [uid]), ('notification_ids.starred', '=', domain[0][2])]
def name_get(self, cr, uid, ids, context=None):
# name_get may receive int id instead of an id list
@@ -146,7 +157,7 @@
store=True, string='Message Record Name',
help="Name get of the related document."),
'notification_ids': fields.one2many('mail.notification', 'message_id',
- string='Notifications',
+ string='Notifications', _auto_join=True,
help='Technical field holding the message notifications. Use notified_partner_ids to access notified partners.'),
'subject': fields.char('Subject'),
'date': fields.datetime('Date'),
@@ -154,21 +165,19 @@
'body': fields.html('Contents', help='Automatically sanitized HTML contents'),
'to_read': fields.function(_get_to_read, fnct_search=_search_to_read,
type='boolean', string='To read',
- help='Functional field to search for messages the current user has to read'),
+ help='Current user has an unread notification linked to this message'),
+ 'starred': fields.function(_get_starred, fnct_search=_search_starred,
+ type='boolean', string='Starred',
+ help='Current user has a starred notification linked to this message'),
'subtype_id': fields.many2one('mail.message.subtype', 'Subtype',
ondelete='set null', select=1,),
'vote_user_ids': fields.many2many('res.users', 'mail_vote',
'message_id', 'user_id', string='Votes',
help='Users that voted for this message'),
- 'favorite_user_ids': fields.many2many('res.users', 'mail_favorite',
- 'message_id', 'user_id', string='Favorite',
- help='Users that set this message in their favorites'),
}
def _needaction_domain_get(self, cr, uid, context=None):
- if self._needaction:
- return [('to_read', '=', True)]
- return []
+ return [('to_read', '=', True)]
def _get_default_author(self, cr, uid, context=None):
return self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
@@ -196,27 +205,62 @@
return new_has_voted or False
#------------------------------------------------------
- # Favorite
+ # Notification API
#------------------------------------------------------
- def favorite_toggle(self, cr, uid, ids, context=None):
- ''' Toggles favorite. Performed using read to avoid access rights issues.
- Done as SUPERUSER_ID because uid may star a message he cannot modify. '''
- for message in self.read(cr, uid, ids, ['favorite_user_ids'], context=context):
- new_is_favorite = not (uid in message.get('favorite_user_ids'))
- if new_is_favorite:
- self.write(cr, SUPERUSER_ID, message.get('id'), {'favorite_user_ids': [(4, uid)]}, context=context)
- # when setting a favorite, set the related notification as unread, or create an unread notification if not existing
- notification_obj = self.pool.get('mail.notification')
- pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=None)['partner_id'][0]
- notif_id = notification_obj.search(cr, SUPERUSER_ID, [('message_id', '=', message.get('id')), ('partner_id', '=', pid)], context=context)
- if notif_id:
- notification_obj.write(cr, SUPERUSER_ID, notif_id, {'read': False}, context=context)
- else:
- notification_obj.create(cr, SUPERUSER_ID, {'message_id': message.get('id'), 'partner_id': pid, 'read': False}, context=context)
- else:
- self.write(cr, SUPERUSER_ID, message.get('id'), {'favorite_user_ids': [(3, uid)]}, context=context)
- return new_is_favorite or False
+ def set_message_read(self, cr, uid, msg_ids, read, context=None):
+ """ Set messages as (un)read. Technically, the notifications related
+ to uid are set to (un)read. If for some msg_ids there are missing
+ notifications (i.e. due to load more or thread parent fetching),
+ they are created.
+
+ :param bool read: set notification as (un)read
+ """
+ notification_obj = self.pool.get('mail.notification')
+ user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
+ notif_ids = notification_obj.search(cr, uid, [
+ ('partner_id', '=', user_pid),
+ ('message_id', 'in', msg_ids)
+ ], context=context)
+
+ # all message have notifications: already set them as (un)read
+ if len(notif_ids) == len(msg_ids):
+ return notification_obj.write(cr, uid, notif_ids, {'read': read}, context=context)
+
+ # some messages do not have notifications: find which one, create notification, update read status
+ notified_msg_ids = [notification.message_id.id for notification in notification_obj.browse(cr, uid, notif_ids, context=context)]
+ to_create_msg_ids = list(set(msg_ids) - set(notified_msg_ids))
+ for msg_id in to_create_msg_ids:
+ notification_obj.create(cr, uid, {'partner_id': user_pid, 'read': read, 'message_id': msg_id}, context=context)
+ return notification_obj.write(cr, uid, notif_ids, {'read': read}, context=context)
+
+ def set_message_starred(self, cr, uid, msg_ids, starred, context=None):
+ """ Set messages as (un)starred. Technically, the notifications related
+ to uid are set to (un)starred. If for some msg_ids there are missing
+ notifications (i.e. due to load more or thread parent fetching),
+ they are created.
+
+ :param bool starred: set notification as (un)starred
+ """
+ notification_obj = self.pool.get('mail.notification')
+ user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
+ notif_ids = notification_obj.search(cr, uid, [
+ ('partner_id', '=', user_pid),
+ ('message_id', 'in', msg_ids)
+ ], context=context)
+
+ # all message have notifications: already set them as (un)starred
+ if len(notif_ids) == len(msg_ids):
+ notification_obj.write(cr, uid, notif_ids, {'starred': starred}, context=context)
+ return starred
+
+ # some messages do not have notifications: find which one, create notification, update starred status
+ notified_msg_ids = [notification.message_id.id for notification in notification_obj.browse(cr, uid, notif_ids, context=context)]
+ to_create_msg_ids = list(set(msg_ids) - set(notified_msg_ids))
+ for msg_id in to_create_msg_ids:
+ notification_obj.create(cr, uid, {'partner_id': user_pid, 'starred': starred, 'message_id': msg_id}, context=context)
+ notification_obj.write(cr, uid, notif_ids, {'starred': starred}, context=context)
+ return starred
#------------------------------------------------------
# Message loading for web interface
@@ -291,7 +335,6 @@
# votes and favorites: res.users ids, no prefetching should be done
vote_nb = len(message.vote_user_ids)
has_voted = uid in [user.id for user in message.vote_user_ids]
- is_favorite = uid in [user.id for user in message.favorite_user_ids]
return {'id': message.id,
'type': message.type,
@@ -309,7 +352,7 @@
'partner_ids': [],
'vote_nb': vote_nb,
'has_voted': has_voted,
- 'is_favorite': is_favorite,
+ 'is_favorite': message.starred,
'attachment_ids': [],
}
@@ -482,15 +525,6 @@
thread_level=thread_level, message_unload_ids=message_unload_ids, domain=domain, parent_id=parent_id, context=context)
return message_list
- # TDE Note: do we need this ?
- # def user_free_attachment(self, cr, uid, context=None):
- # attachment = self.pool.get('ir.attachment')
- # attachment_list = []
- # attachment_ids = attachment.search(cr, uid, [('res_model', '=', 'mail.message'), ('create_uid', '=', uid)])
- # if len(attachment_ids):
- # attachment_list = [{'id': attach.id, 'name': attach.name, 'date': attach.create_date} for attach in attachment.browse(cr, uid, attachment_ids, context=context)]
- # return attachment_list
-
#------------------------------------------------------
# mail_message internals
#------------------------------------------------------
=== modified file 'mail/mail_thread.py'
--- mail/mail_thread.py 2012-11-21 13:57:21 +0000
+++ mail/mail_thread.py 2012-11-29 15:15:38 +0000
@@ -88,7 +88,7 @@
for thread in self.browse(cr, uid, ids, context=context):
cls = res[thread.id]['message_unread'] and ' class="oe_kanban_mail_new"' or ''
- res[thread.id]['message_summary'] = "<span%s><span class='oe_e'>9</span> %d</span> <span><span class='oe_e'>+</span> %d</span>" % (cls, len(thread.message_comment_ids), len(thread.message_follower_ids))
+ res[thread.id]['message_summary'] = "<span%s><span class='oe_e'>9</span> %d</span> <span><span class='oe_e'>+</span> %d</span>" % (cls, len(thread.message_ids), len(thread.message_follower_ids))
return res
@@ -121,18 +121,17 @@
return res
- def _search_unread(self, cr, uid, obj=None, name=None, domain=None, context=None):
- partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
- res = {}
- notif_obj = self.pool.get('mail.notification')
- notif_ids = notif_obj.search(cr, uid, [
- ('partner_id', '=', partner_id),
- ('message_id.model', '=', self._name),
- ('read', '=', False)
- ], context=context)
- for notif in notif_obj.browse(cr, uid, notif_ids, context=context):
- res[notif.message_id.res_id] = True
- return [('id', 'in', res.keys())]
+ def _search_message_unread(self, cr, uid, obj=None, name=None, domain=None, context=None):
+ """ TDE FIXME in 7.1: searching unread messages
+ _auto_join is currently a very limited feature; therefore the returned
+ domain to search for unread messages take into acount the following
+ limitations of this feature :
+ - does not take into account the domain on the one2many field
+ (i.e. message_ids.model = self._name is needed)
+ - does not correctly handle function field on relational table
+ (i.e. message_ids.read crashes)
+ """
+ return ['&', '&', ('message_ids.model', '=', self._name), ('message_ids.notification_ids.partner_id.user_ids', 'in', [uid]), ('message_ids.notification_ids.read', '=', False)]
def _get_followers(self, cr, uid, ids, name, arg, context=None):
fol_obj = self.pool.get('mail.followers')
@@ -201,16 +200,14 @@
'message_follower_ids': fields.function(_get_followers, fnct_inv=_set_followers,
fnct_search=_search_followers, type='many2many',
obj='res.partner', string='Followers', multi='_get_followers'),
- 'message_comment_ids': fields.one2many('mail.message', 'res_id',
- domain=lambda self: [('model', '=', self._name), ('type', 'in', ('comment', 'email'))],
- string='Comments and emails',
- help="Comments and emails"),
'message_ids': fields.one2many('mail.message', 'res_id',
domain=lambda self: [('model', '=', self._name)],
+ _auto_join=True,
string='Messages',
help="Messages and communication history"),
- 'message_unread': fields.function(_get_message_data, fnct_search=_search_unread,
- type='boolean', string='Unread Messages', multi="_get_message_data",
+ 'message_unread': fields.function(_get_message_data,
+ fnct_search=_search_message_unread, multi="_get_message_data",
+ type='boolean', string='Unread Messages',
help="If checked new messages require your attention."),
'message_summary': fields.function(_get_message_data, method=True,
type='text', string='Summary', multi="_get_message_data",
@@ -245,7 +242,6 @@
def copy(self, cr, uid, id, default=None, context=None):
default = default or {}
default['message_ids'] = []
- default['message_comment_ids'] = []
default['message_follower_ids'] = []
return super(mail_thread, self).copy(cr, uid, id, default=default, context=context)
=== modified file 'mail/mail_thread_view.xml'
--- mail/mail_thread_view.xml 2012-11-28 14:51:01 +0000
+++ mail/mail_thread_view.xml 2012-11-29 15:15:38 +0000
@@ -11,9 +11,8 @@
}</field>
<field name="params" eval=""{
'domain': [
- ('notification_ids.partner_id.user_ids', 'in', [uid]),
('to_read', '=', True),
- ('favorite_user_ids', 'not in', [uid])
+ ('starred', '=', False),
],
'view_mailbox': True,
'view_inbox': True,
@@ -40,7 +39,9 @@
'search_default_message_unread': True
}</field>
<field name="params" eval=""{
- 'domain': [('partner_ids.user_ids', 'in', [uid])],
+ 'domain': [
+ ('partner_ids.user_ids', 'in', [uid])
+ ],
'view_mailbox': True,
'read_action': 'read',
'show_compose_message': False }""/>
@@ -63,7 +64,9 @@
'search_default_message_unread': True
}</field>
<field name="params" eval=""{
- 'domain': [('favorite_user_ids', 'in', [uid])],
+ 'domain': [
+ ('starred', '=', True),
+ ],
'view_mailbox': True,
'read_action': 'read',
'compose_as_todo': True }""/>
@@ -85,7 +88,11 @@
'default_res_id': uid
}</field>
<field name="params" eval=""{
- 'domain': ['|', ('notification_ids.partner_id.user_ids', 'in', [uid]), ('author_id.user_ids', 'in', [uid])],
+ 'domain': [
+ '|',
+ ('notification_ids.partner_id.user_ids', 'in', [uid]),
+ ('author_id.user_ids', 'in', [uid]),
+ ],
'view_mailbox': True,
'show_compose_message': False }""/>
<field name="help" type="html">
=== modified file 'mail/static/src/js/mail.js'
--- mail/static/src/js/mail.js 2012-11-29 09:45:53 +0000
+++ mail/static/src/js/mail.js 2012-11-29 15:15:38 +0000
@@ -846,7 +846,7 @@
}
var message_ids = _.map(messages, function (val) { return val.id; });
- this.ds_notification.call('set_message_read', [message_ids, read_value, this.context])
+ this.ds_message.call('set_message_read', [message_ids, read_value, this.context])
.then(function () {
// apply modification
_.each(messages, function (msg) {
@@ -895,7 +895,7 @@
var self=this;
var button = self.$('.oe_star:first');
- this.ds_message.call('favorite_toggle', [[self.id]])
+ this.ds_message.call('set_message_starred', [[self.id], !self.is_favorite])
.then(function (star) {
self.is_favorite=star;
if (self.is_favorite) {
@@ -990,7 +990,8 @@
// add message composition form view
if (!this.compose_message) {
this.compose_message = new mail.ThreadComposeMessage(this, this, {
- 'context': this.options.compose_as_todo && !this.thread_level ? _.extend(this.context, { 'default_favorite_user_ids': [this.session.uid] }) : this.context,
+ // TDE FIXME: default_favorite_user_ids does not exist anymore -> you should create the message, set starred then update
+ 'context': this.options.compose_as_todo && !this.thread_level ? this.context : this.context,
'options': this.options,
});
if (!this.thread_level || this.thread_level > this.options.display_indented_thread) {
=== modified file 'mail/tests/test_mail.py'
--- mail/tests/test_mail.py 2012-11-20 11:36:00 +0000
+++ mail/tests/test_mail.py 2012-11-29 15:15:38 +0000
@@ -795,7 +795,7 @@
# Test: msg has Bert as voter
self.assertEqual(set(msg.vote_user_ids), set([user_raoul]), 'mail_message vote: after unvoting, Bert should be in the voter')
- def test_70_message_favorite(self):
+ def test_70_message_star(self):
""" Tests for favorites. """
cr, uid, user_admin, user_raoul, group_pigs = self.cr, self.uid, self.user_admin, self.user_raoul, self.group_pigs
# Data: post a message on Pigs
@@ -803,17 +803,18 @@
msg = self.mail_message.browse(cr, uid, msg_id)
# Do: Admin stars msg
- self.mail_message.favorite_toggle(cr, uid, [msg.id])
+ self.mail_message.set_message_starred(cr, uid, [msg.id], True)
msg.refresh()
# Test: msg starred by Admin
- self.assertEqual(set(msg.favorite_user_ids), set([user_admin]), 'mail_message favorite: after starring, Admin should be in favorite_user_ids')
+ self.assertTrue(msg.starred, 'mail_message starred failed')
# Do: Bert stars msg
- self.mail_message.favorite_toggle(cr, user_raoul.id, [msg.id])
+ self.mail_message.set_message_starred(cr, user_raoul.id, [msg.id], True)
msg.refresh()
# Test: msg starred by Admin and Raoul
- self.assertEqual(set(msg.favorite_user_ids), set([user_admin, user_raoul]), 'mail_message favorite: after starring, Admin and Raoul should be in favorite_user_ids')
+ # self.assertTrue(msg.starred, set([user_admin, user_raoul]), 'mail_message favorite: after starring, Admin and Raoul should be in favorite_user_ids')
# Do: Admin unvote for msg
- self.mail_message.favorite_toggle(cr, uid, [msg.id])
+ self.mail_message.set_message_starred(cr, uid, [msg.id], False)
msg.refresh()
# Test: msg starred by Raoul
- self.assertEqual(set(msg.favorite_user_ids), set([user_raoul]), 'mail_message favorite: after unstarring, Raoul should be in favorite_user_ids')
+ self.assertFalse(msg.starred, 'mail_message starred failed')
+ # self.assertEqual(set(msg.favorite_user_ids), set([user_raoul]), 'mail_message favorite: after unstarring, Raoul should be in favorite_user_ids')
_______________________________________________
Mailing list: https://launchpad.net/~openerp-dev-gtk
Post to : [email protected]
Unsubscribe : https://launchpad.net/~openerp-dev-gtk
More help : https://help.launchpad.net/ListHelp