Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MIG] mail_tracking: Migration to 11.0 #244

Merged
merged 28 commits into from
May 7, 2018

Conversation

chienandalu
Copy link
Member

Mail tracking

This module shows email notification tracking status for any messages in
mail thread (chatter). Each notified partner will have an intuitive icon just
right to his name.

cc @Tecnativa

@rafaelbn rafaelbn added this to the 11.0 milestone Mar 20, 2018
@rafaelbn rafaelbn requested review from yajo and rafaelbn March 20, 2018 10:52
@rafaelbn
Copy link
Member

thanks @chienandalu ! please could you review travis? is in red

@chienandalu
Copy link
Member Author

@rafaelbn I'm on it. I had an old v11 instance in wich test worked. So I have to debug what change in mail is making the tests fail

@chienandalu
Copy link
Member Author

So travis won't pass due to this issue: odoo/odoo#23808 Wich affects to mail_digest module here: https://github.com/OCA/social/blob/11.0/mail_digest/models/res_partner.py#L80

Without that module installed tests pass with no problem at all in my local...

@pedrobaeza
Copy link
Member

@simahawk maybe you can find a workaround for making green again the branch

@simahawk
Copy link
Contributor

@pedrobaeza branch 11.0 is green AFAIS. For the issue here I've no clue ATM.

@chienandalu can you report here which domain do you get and which is the offending leaf here?

Then you could improve the test cases or add a new one here so that we are sure that this case too is covered w/ tests.

BTW I'll have to work on mail_digest for some improvements in the next 2 weeks. I will have a look at this if not solved yet.

@pedrobaeza pedrobaeza mentioned this pull request Mar 24, 2018
26 tasks
@chienandalu
Copy link
Member Author

Hi @simahawk You can test it like this:

  1. Add a normal partner to the setup.
  2. Add this partner to the message.
  3. Assert the partner is found (it won't be due to the bug)
def test_notify_domains_only_recipients(self):
    # we don't set recipients
    # because we call `_get_notify_by_email_domain` directly
    self.partner1.real_user_id.notification_type = 'email'
    self.partner2.real_user_id.notification_type = 'email'
    partners = self.partner1 + self.partner2
    # followers
    self.partner3.message_subscribe(self.partner2.ids)
    # partner1 is the only recipient
    message = self.message_model.create({
        'body': 'My Body',
        'res_id': self.partner3.id,
        'model': 'res.partner',
        'partner_ids': [(4, self.partner1.id), (4, self.partner_alt.id)]
    })
    domain = partners._get_notify_by_email_domain(message)
    # we find both of them since partner2 is a follower
    self._assert_found(self.partner1, domain)
    self._assert_found(self.partner2, domain)
    self._assert_found(self.partner_alt, domain)
    # no one here in digest mode
    domain = partners._get_notify_by_email_domain(message, digest=True)
    self._assert_found(self.partner1, domain, not_found=True)
    self._assert_found(self.partner2, domain, not_found=True)

@simahawk
Copy link
Contributor

@chienandalu if w/ "normal partner" you mean a partner w/ no user... then you are right. It does not pass. I'm checking.

@chienandalu
Copy link
Member Author

@simahawk Sure, that's what I meant 😅

@simahawk
Copy link
Contributor

simahawk commented Apr 4, 2018

@chienandalu review wlcm :) #253

@pedrobaeza
Copy link
Member

Please rebase

@chienandalu chienandalu force-pushed the 11.0-mig-mail_tracking branch 2 times, most recently from de57fec to 74f5229 Compare April 5, 2018 07:34
@chienandalu
Copy link
Member Author

Rebased and a little commit squash

@pedrobaeza
Copy link
Member

@yajo please review

@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drop coding headers

reg = registry(db)
except OperationalError:
_logger.warning("Selected BD '%s' not found", db)
except: # pragma: no cover
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

except Exception: and remove the comment

if match:
try:
tracking_email_id = int(match.group(1))
except: # pragma: no cover
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

except Exception or ValueError if is related to int call

mail_server = mail_server_ids[0] if mail_server_ids else None
if mail_server:
smtp_server_used = mail_server.smtp_host
else: # pragma: no cover
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment needed for?

if tracking.partner_id:
partners_already |= tracking.partner_id
# Search all recipients for this message
if message.partner_ids:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

recordsets operation can be slow compared to normal sets operations.
You could build a set or a dict ID: name (the only values that you are going to use below)

@api.model
def email_is_bounced(self, email):
if email:
return len(self._email_score_tracking_filter([
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't be better to use search_count here, especially if - as stated above - we gonna have tons of records?

if event and event.recipient_address:
recipients.append(event.recipient_address)
else:
recipients = list(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

list() needed for? mapped already returns an iterable...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This oddities tend to come from 2to3. I don't know exactly attending to wich rule. Fixed anyway.

this.bind_events();
},
render: function(messages, options) {
var MessageModel = new data.DataSetSearch(this, 'mail.message', session.context);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DataSet* are deprecate and models too. You should use _rpc

@pedrobaeza
Copy link
Member

@chienandalu please finish this so that we can merge it.

@chienandalu
Copy link
Member Author

Thanks for your comments @simahawk Take a look now

Copy link
Contributor

@simahawk simahawk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chienandalu small glitches but I'm approving so that I won't block you anymore once you've addressed them 😉

@@ -35,8 +34,8 @@ def tracking_status(self):
res = {}
for message in self:
partner_trackings = []
partners_already = self.env['res.partner']
partners = self.env['res.partner']
partners_already = []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't need this list, the dict is enough. lambda x: x.id not in partners will work.

@@ -22,7 +21,7 @@ def _env_get(db, callback, tracking_id, event_type, **kw):
reg = registry(db)
except OperationalError:
_logger.warning("Selected BD '%s' not found", db)
except: # pragma: no cover
except Exception: # pragma: no cover
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove pragma comment

partners[p.id] = p.name
# If there is partners not included, then status is 'unknown'
partner_trackings += [
('unkown', False, partners[x], x) for x in partners.keys()]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

('unkown', False, pid, name) for pid, name in partners.items()

@rafaelbn
Copy link
Member

Please @yajo make a review here in order to merge. Thank you!

@pedrobaeza
Copy link
Member

Take into account #257

Copy link
Member

@yajo yajo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The migration looks good overall, but there are some points where things have changed with no good reason for it, and there is another where a change would be really appreciated. More details below.

Thanks!

partners[p.id] = p.name
for p in message.needaction_partner_ids.filtered(
lambda x: x.id not in partners_already):
partners[p.id] = p.name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm not wrong, with the change in this method you're doing the same thing as before, but avoiding the use of recordsets. However recordsets are not harmful; they help making code more understandable.

Would you mind leaving it as it was? Thanks 😊

@@ -102,10 +101,10 @@ def _email_score_tracking_filter(self, domain, order='time desc',
@api.model
def email_is_bounced(self, email):
if email:
return len(self._email_score_tracking_filter([
return self.search_count([
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For what I can see here, we have 2 options:

  1. Remove the _email_score_tracking_filter method, since it is not being used now.

  2. Change the _email_score_tracking_filter signature to be (*args, **kwargs), which get passed down to self.search(*args, **kwargs).

    Then, call here to _email_score_tracking_filter, we add count=True as an argument, which acts just like search_count, but makes the count in the DB side, which is also more performant.

I think you should check if any submodule overwrites the method, to make the sane choice.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems that nobody is using this. I chose to erase the method

@@ -197,7 +196,7 @@ def _partners_email_bounced_set(self, reason, event=None):
if event and event.recipient_address:
recipients.append(event.recipient_address)
else:
recipients = list(filter(None, self.mapped('recipient_address')))
recipients = [x for x in self.mapped('recipient_address') if x]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see no benefit on this change.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for the sake of clarity

};
this.do_action(action);
},
bind_events: function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to remove this method and add the events as a class property using the extension method you can see here.

This way we leverage Odoo framework to do the binding...

},
start: function () {
this._super();
this.bind_events();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... and we avoid the need to overwrite this method too.

});
self._rpc({
model: 'mail.message',
method: 'tracking_status',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This thing you're doing here is not exactly wrong, but think about it: every time the chat thread is rendered (which can happens i.e. when removing a message), you're making an additional RPC call and waiting for it to resolve until you actually render the thread.

Since reading mail threads is so common in Odoo, they designed it in a way that uses the web bus and caches messages internally, so it reduces RPC calls and improves responsiveness. Here, we're introducing a bottleneck that honestly I'd prefer to avoid.

By digging into the made calls, you can see that the web client ultimately calls the model's message_fetch method. Then, it would be much better to simply overwrite that method in the backend and avoid these RPC calls. The JS code would be much less too now.

This would be a really big performance improvement. Would you do it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I'd say that what you propose is alredy happening here: https://github.com/OCA/social/pull/244/files#diff-4e1cf268ea32e1266c1e39d18db0d429R66 Wich is a common point in the already exisiting rpc calls. Indeed, this code could be erased with no harm. Don't you think this method was made to have tighter check of the thread messages status?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand. For what I can see, this could be erased. Could you check please?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to work as before. I guess it will lower down the refresh rate but, as you said, it will release a good bunch of unnecesary rpc calls.

@rafaelbn
Copy link
Member

@chienandalu please review @yajo comments please. Thanks!

@rafaelbn
Copy link
Member

@chienandalu please remember to add #257 !! Thanks!

oca-transbot and others added 21 commits May 4, 2018 10:13
As regular users can't access this object.
* [FIX] Only one data-odoo-tracking-email tag in each email
* [FIX] Get status even in multicompany instances
On our server,
queries based on "mail_tracking_event"."tracking_email_id" improved from 501,924 ms to 1,840 ms
queries based on "mail_tracking_email"."mail_message_id" improved from 167,436 ms to 3,223 ms

The last ones are run several times when a thread has many messages
@pedrobaeza
Copy link
Member

@yajo give your final review

@yajo yajo merged commit bae9999 into OCA:11.0 May 7, 2018
@yajo yajo deleted the 11.0-mig-mail_tracking branch May 7, 2018 09:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet