From 14abd4fdf714ce280ab07998073328eb4d76bc09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 28 May 2020 10:38:32 +0000 Subject: [PATCH] [IMP] digest: improve digest by adding daily sending and preferences PURPOSE Make digest email and tips more appealing. The goals of these tips are * to encourage the adoption of other apps (Did you know ?); * to make Odoo look more fun (Fun tips and tricks, young and dynamic style); * to show social proof and increase trust (emphasis on already existing projects / customers to); SPECIFICATIONS Add a daily digest option, allowing to send digests on a daily basis. It will be the default settings to help users coming back to odoo. Add a slowdown heuristics. If not any user targeted by a daily digest logs himself within 3 days, digest is slowed down to a weekly setting to avoid spam. Add a preference section in template, to hold notably links to some configuration steps sent mainly for admin / CEOs. Improve access on models. LINKS Task ID 2197417 PR odoo/odoo#51619 --- addons/digest/controllers/portal.py | 25 ++++++++- addons/digest/data/digest_data.xml | 12 +++++ addons/digest/data/digest_template_data.xml | 12 +++++ addons/digest/data/digest_tips_data.xml | 5 +- addons/digest/models/digest.py | 57 +++++++++++++++++---- addons/digest/security/ir.model.access.csv | 2 +- 6 files changed, 99 insertions(+), 14 deletions(-) diff --git a/addons/digest/controllers/portal.py b/addons/digest/controllers/portal.py index 0e7f8a5b4e5..451956a7bfa 100644 --- a/addons/digest/controllers/portal.py +++ b/addons/digest/controllers/portal.py @@ -1,15 +1,36 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from werkzeug.exceptions import Forbidden +from werkzeug.urls import url_encode + +from odoo import _ from odoo.http import Controller, request, route class DigestController(Controller): @route('/digest//unsubscribe', type='http', website=True, auth='user') - def digest_unsubscribe(self, digest_id, **post): - digest = request.env['digest.digest'].sudo().browse(digest_id) + def digest_unsubscribe(self, digest_id): + digest = request.env['digest.digest'].browse(digest_id).exists() digest.action_unsubcribe() return request.render('digest.portal_digest_unsubscribed', { 'digest': digest, }) + + @route('/digest//set_periodicity', type='http', website=True, auth='user') + def digest_set_periodicity(self, digest_id, periodicity='weekly'): + if not request.env.user.has_group('base.group_erp_manager'): + raise Forbidden() + if periodicity not in ('daily', 'weekly', 'monthly', 'quarterly'): + raise ValueError(_('Invalid periodicity set on digest')) + + digest = request.env['digest.digest'].browse(digest_id).exists() + digest.action_set_periodicity(periodicity) + + url_params = { + 'model': digest._name, + 'id': digest.id, + 'active_id': digest.id, + } + return request.redirect('/web?#%s' % url_encode(url_params)) diff --git a/addons/digest/data/digest_data.xml b/addons/digest/data/digest_data.xml index 67bd019bc00..80678308c79 100644 --- a/addons/digest/data/digest_data.xml +++ b/addons/digest/data/digest_data.xml @@ -53,4 +53,16 @@ + + + diff --git a/addons/digest/data/digest_template_data.xml b/addons/digest/data/digest_template_data.xml index dc5109c4b56..448fa8b2033 100644 --- a/addons/digest/data/digest_template_data.xml +++ b/addons/digest/data/digest_template_data.xml @@ -12,6 +12,7 @@ % set data = object.compute_kpis(company, user) % set tips = object.compute_tips(company, user, tips_count=ctx.get('tips_count', 1)) % set kpi_actions = object.compute_kpis_actions(company, user) + % set preferences = object.compute_preferences(company, user) % set kpis = data.yesterday.keys() @@ -46,6 +47,7 @@
+ % for tip in tips:
@@ -53,6 +55,7 @@
+ % endfor
@@ -137,6 +140,15 @@ % endfor + % if preferences: + + + + +
+ ${preferences | safe} +
+ % endif % if user.has_group('base.group_system'): diff --git a/addons/digest/data/digest_tips_data.xml b/addons/digest/data/digest_tips_data.xml index 375dd660681..ae7ed018ed8 100644 --- a/addons/digest/data/digest_tips_data.xml +++ b/addons/digest/data/digest_tips_data.xml @@ -2,9 +2,10 @@ - Weekly Stats in Odoo + Awesome Stats with Odoo + daily - + True True diff --git a/addons/digest/models/digest.py b/addons/digest/models/digest.py index 4b33ebc7937..3c39f49e244 100644 --- a/addons/digest/models/digest.py +++ b/addons/digest/models/digest.py @@ -2,7 +2,6 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import logging -import math import pytz from datetime import datetime, date @@ -23,10 +22,11 @@ class Digest(models.Model): # Digest description name = fields.Char(string='Name', required=True, translate=True) user_ids = fields.Many2many('res.users', string='Recipients', domain="[('share', '=', False)]") - periodicity = fields.Selection([('weekly', 'Weekly'), + periodicity = fields.Selection([('daily', 'Daily'), + ('weekly', 'Weekly'), ('monthly', 'Monthly'), ('quarterly', 'Quarterly')], - string='Periodicity', default='weekly', required=True) + string='Periodicity', default='daily', required=True) next_run_date = fields.Date(string='Next Send Date') template_id = fields.Many2one('mail.template', string='Email Template', domain="[('model','=','digest.digest')]", @@ -77,19 +77,21 @@ class Digest(models.Model): @api.model def create(self, vals): - vals['next_run_date'] = date.today() + relativedelta(days=3) - return super(Digest, self).create(vals) + digest = super(Digest, self).create(vals) + if not digest.next_run_date: + digest.next_run_date = digest._get_next_run_date() + return digest # ------------------------------------------------------------ # ACTIONS # ------------------------------------------------------------ def action_subscribe(self): - if self.env.user not in self.user_ids: + if self.env.user.has_group('base.group_user') and self.env.user not in self.user_ids: self.sudo().user_ids |= self.env.user def action_unsubcribe(self): - if self.env.user in self.user_ids: + if self.env.user.has_group('base.group_user') and self.env.user in self.user_ids: self.sudo().user_ids -= self.env.user def action_activate(self): @@ -98,10 +100,16 @@ class Digest(models.Model): def action_deactivate(self): self.state = 'deactivated' + def action_set_periodicity(self, periodicity): + self.periodicity = periodicity + def action_send(self): + to_slowdown = self._check_daily_logs() for digest in self: for user in digest.user_ids: - digest._action_send_to_user(user, tips_count=1) + digest.with_context(digest_slowdown=digest in to_slowdown)._action_send_to_user(user, tips_count=1) + if digest in to_slowdown: + digest.write({'periodicity': 'weekly'}) digest.next_run_date = digest._get_next_run_date() def _action_send_to_user(self, user, tips_count=1): @@ -167,7 +175,7 @@ class Digest(models.Model): self.env['mail.render.mixin']._render_template(tools.html_sanitize(tip.tip_description), 'digest.tip', tip.ids, post_process=True)[tip.id] for tip in tips ] - # tip.user_ids += user + tip.user_ids += user return tip_descriptions def compute_kpis_actions(self, company, user): @@ -178,8 +186,27 @@ class Digest(models.Model): """ return {} + def compute_preferences(self, company, user): + """ Give an optional text for preferences, like a shortcut for configuration. + + :return string: html to put in template + """ + preferences = self.env['mail.render.mixin']._render_template( + 'digest.digest_section_preferences', + 'digest.digest', + self.ids, + engine='qweb', + add_context={ + 'company': company, + 'user': user, + }, + post_process=True)[self.id] + return preferences + def _get_next_run_date(self): self.ensure_one() + if self.periodicity == 'daily': + delta = relativedelta(days=1) if self.periodicity == 'weekly': delta = relativedelta(weeks=1) elif self.periodicity == 'monthly': @@ -216,6 +243,18 @@ class Digest(models.Model): margin = float_round((float(value-previous_value) / previous_value or 1) * 100, precision_digits=2) return margin + def _check_daily_logs(self): + three_days_ago = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) - relativedelta(days=3) + to_slowdown = self.env['digest.digest'] + for digest in self.filtered(lambda digest: digest.periodicity == 'daily'): + users_logs = self.env['res.users.log'].sudo().search_count([ + ('create_uid', 'in', digest.user_ids.ids), + ('create_date', '>=', three_days_ago) + ]) + if not users_logs: + to_slowdown += digest + return to_slowdown + def _format_currency_amount(self, amount, currency_id): pre = currency_id.position == 'before' symbol = u'{symbol}'.format(symbol=currency_id.symbol or '') diff --git a/addons/digest/security/ir.model.access.csv b/addons/digest/security/ir.model.access.csv index 2891b43b673..49ef355262e 100644 --- a/addons/digest/security/ir.model.access.csv +++ b/addons/digest/security/ir.model.access.csv @@ -2,4 +2,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_digest_digest_system,digest.digest.administration,model_digest_digest,base.group_erp_manager,1,1,1,1 access_digest_digest_user,digest.digest.user,model_digest_digest,base.group_user,1,0,0,0 access_digest_tip_system,digest.tip.administration,model_digest_tip,base.group_erp_manager,1,1,1,1 -access_digest_tip_user,digest.tip.user,model_digest_tip,base.group_user,0,0,0,0 +access_digest_tip_user,digest.tip.user,model_digest_tip,base.group_user,1,0,0,0