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="&quot;{
                 '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="&quot;{
-                'domain': [('partner_ids.user_ids', 'in', [uid])],
+                'domain': [
+                    ('partner_ids.user_ids', 'in', [uid])
+                ],
                 'view_mailbox': True,
                 'read_action': 'read',
                 'show_compose_message': False }&quot;"/>
@@ -63,7 +64,9 @@
               'search_default_message_unread': True
             }</field>
             <field name="params" eval="&quot;{
-                'domain': [('favorite_user_ids', 'in', [uid])],
+                'domain': [
+                    ('starred', '=', True),
+                ],
                 'view_mailbox': True,
                 'read_action': 'read',
                 'compose_as_todo': True }&quot;"/>
@@ -85,7 +88,11 @@
               'default_res_id': uid
             }</field>
             <field name="params" eval="&quot;{
-                '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 }&quot;"/>
             <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

Reply via email to