PURPOSE Visitors are useful in terms of marketing analysis but they can bloat the database really quickly if you have a lot of traffic on your website. This commit aims to relieve the database by fully deleting inactive visitors. SPECS On an active website, you can easily reach hundreds or even thousands of visitors per day. While active visitors are useful for marketing purposes, inactive ones were archived after a period of inactivity (i.e: not connected for X days in a row, where X is configurable as a parameter and 30 by default). But archiving visitors is not really useful either. We don't see any specific cases where you would want to restore some visitors. That means we are better off unlinking the visitors completely to save database storage space. We want to make exceptions and avoid deletion of inactive visitors in some cases: - When they are linked to a partner (meaning most likely linked to a registered user) - When they are linked to leads - When they are registered to events Several tests were added to ensure that leads matching these conditions are not unlinked. We also took this opportunity to enforce the "_link_to_visitor" rules in those tests to make sure that: - When visitors are linked, the leads are merged into the main visitor - When visitors are linked, the event tickets are merged into the main visitor - When visitors are linked, the wishlisted tracks are merged into the main visitor This is also preliminary work for a commit that will move the push_token of website.visitors to a separate table. That will in turn allow us to link the push_tokens within the "_link_to_visitor" method and delete the linked visitor instead of archiving it. LINKS Task-2410217 Part-of: odoo/odoo#65113
98 lines
4.7 KiB
Python
98 lines
4.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import api, fields, models
|
|
from odoo.osv import expression
|
|
|
|
|
|
class WebsiteVisitor(models.Model):
|
|
_name = 'website.visitor'
|
|
_inherit = ['website.visitor']
|
|
|
|
event_registration_ids = fields.One2many(
|
|
'event.registration', 'visitor_id', string='Event Registrations',
|
|
groups="event.group_event_registration_desk")
|
|
event_registration_count = fields.Integer(
|
|
'# Registrations', compute='_compute_event_registration_count',
|
|
groups="event.group_event_registration_desk")
|
|
event_registered_ids = fields.Many2many(
|
|
'event.event', string="Registered Events",
|
|
compute="_compute_event_registered_ids", compute_sudo=True,
|
|
search="_search_event_registered_ids",
|
|
groups="event.group_event_registration_desk")
|
|
|
|
@api.depends('event_registration_ids')
|
|
def _compute_event_registration_count(self):
|
|
if self.ids:
|
|
read_group_res = self.env['event.registration'].read_group(
|
|
[('visitor_id', 'in', self.ids)],
|
|
['visitor_id'], ['visitor_id'])
|
|
visitor_mapping = dict(
|
|
(item['visitor_id'][0], item['visitor_id_count'])
|
|
for item in read_group_res)
|
|
else:
|
|
visitor_mapping = dict()
|
|
for visitor in self:
|
|
visitor.event_registration_count = visitor_mapping.get(visitor.id) or 0
|
|
|
|
@api.depends('event_registration_ids.email', 'event_registration_ids.mobile', 'event_registration_ids.phone')
|
|
def _compute_email_phone(self):
|
|
super(WebsiteVisitor, self)._compute_email_phone()
|
|
self.flush()
|
|
|
|
for visitor in self.filtered(lambda visitor: not visitor.email or not visitor.mobile):
|
|
linked_registrations = visitor.event_registration_ids.sorted(lambda reg: (reg.create_date, reg.id), reverse=False)
|
|
if not visitor.email:
|
|
visitor.email = next((reg.email for reg in linked_registrations if reg.email), False)
|
|
if not visitor.mobile:
|
|
visitor.mobile = next((reg.mobile or reg.phone for reg in linked_registrations if reg.mobile or reg.phone), False)
|
|
|
|
@api.depends('parent_id', 'event_registration_ids')
|
|
def _compute_event_registered_ids(self):
|
|
# include parent's registrations in a visitor o2m field. We don't add
|
|
# child one as child should not have registrations (moved to the parent)
|
|
for visitor in self:
|
|
all_registrations = visitor.event_registration_ids | visitor.parent_id.event_registration_ids
|
|
visitor.event_registered_ids = all_registrations.mapped('event_id')
|
|
|
|
def _search_event_registered_ids(self, operator, operand):
|
|
""" Search visitors with terms on events within their event registrations. E.g. [('event_registered_ids',
|
|
'in', [1, 2])] should return visitors having a registration on events 1, 2 as
|
|
well as their children for notification purpose. """
|
|
if operator == "not in":
|
|
raise NotImplementedError("Unsupported 'Not In' operation on visitors registrations")
|
|
|
|
all_registrations = self.env['event.registration'].sudo().search([
|
|
('event_id', operator, operand)
|
|
])
|
|
if all_registrations:
|
|
# search children, even archived one, to contact them
|
|
visitors = all_registrations.with_context(active_test=False).mapped('visitor_id')
|
|
children = self.env['website.visitor'].with_context(
|
|
active_test=False
|
|
).sudo().search([('parent_id', 'in', visitors.ids)])
|
|
visitor_ids = (visitors + children).ids
|
|
else:
|
|
visitor_ids = []
|
|
|
|
return [('id', 'in', visitor_ids)]
|
|
|
|
def _inactive_visitors_domain(self):
|
|
""" Visitors registered to events are considered always active and should not be deleted. """
|
|
domain = super()._inactive_visitors_domain()
|
|
return expression.AND([domain, [('event_registration_ids', '=', False)]])
|
|
|
|
def _link_to_partner(self, partner, update_values=None):
|
|
""" Propagate partner update to registration records """
|
|
if partner:
|
|
registration_wo_partner = self.event_registration_ids.filtered(lambda registration: not registration.partner_id)
|
|
if registration_wo_partner:
|
|
registration_wo_partner.partner_id = partner
|
|
super(WebsiteVisitor, self)._link_to_partner(partner, update_values=update_values)
|
|
|
|
def _link_to_visitor(self, target):
|
|
""" Override linking process to link registrations to the final visitor. """
|
|
self.event_registration_ids.write({'visitor_id': target.id})
|
|
|
|
return super(WebsiteVisitor, self)._link_to_visitor(target)
|