Skip to content
Snippets Groups Projects
Commit 14abd4fd authored by Thibault Delavallée's avatar Thibault Delavallée
Browse files

[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
parent add1a04a
Branches
Tags
No related merge requests found
# -*- 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/<int:digest_id>/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/<int:digest_id>/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))
......@@ -53,4 +53,16 @@
</body>
</html>
</template>
<!-- DIGEST PART: PREFERENCES -->
<template id="digest_section_preferences">
<p t-if="object._context.get('digest_slowdown')" class="font-12" style="font-size: 15px; color: #6b6d70">
We've noticed you did not connect these last few days so we've automatically switched your preference to weekly Digests.
</p>
<p t-elif="user.has_group('base.group_erp_manager') and object.periodicity == 'daily'" class="font-12" style="font-size: 15px; color: #6b6d70">
Prefer a broader overview ?
<a t-att-href="'/digest/%s/set_periodicity?periodicity=weekly' % object.id" target="_blank" style="color:#875A7B; font-weight: bold;">Switch to weekly Digests.</a>
</p>
<p t-else=""/>
</template>
</data></odoo>
......@@ -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()
<table cellspacing="0" cellpadding="0" align="center" border="0" bgcolor="#eeeeee" style="width:100%; font-family: Arial,Helvetica,Verdana,sans-serif;">
<tr class="mobile-hide" bgcolor="#875a7b"><td height="70"> </td></tr>
......@@ -46,6 +47,7 @@
<table bgcolor="#ffffff" cellspacing="0" cellpadding="0" width="650" align="center" border="0" style="border: 1px solid #eeeeee; border-bottom: none;width: 100%; max-width: 650px; padding:30px 30px">
<tr>
<td>
% for tip in tips:
<table cellspacing="0" cellpadding="0" border="0" width="580" align="center" style="width:100%; max-width:580px;">
<tr>
<td valign="top">
......@@ -53,6 +55,7 @@
</td>
</tr>
</table>
% endfor
</td>
</tr>
</table>
......@@ -137,6 +140,15 @@
</tr>
</table>
% endfor
% if preferences:
<table bgcolor="#ffffff" cellspacing="0" cellpadding="0" width="650" align="center" border="0" style="border-left: 1px solid #eeeeee; border-right: 1px solid #eeeeee; width: 100%; max-width: 650px; padding:30px 30px">
<tr>
<td bgcolor="#fafafa" width="100%" height="50" align="center">
${preferences | safe}
</td>
</tr>
</table>
% endif
% if user.has_group('base.group_system'):
<table bgcolor="#ffffff" cellspacing="0" cellpadding="0" width="650" align="center" border="0" style="border-left: 1px solid #eeeeee; border-right: 1px solid #eeeeee; width: 100%; max-width: 650px; padding:30px 30px">
<tr>
......
......@@ -2,9 +2,10 @@
<odoo>
<data noupdate="1">
<record id="digest_digest_default" model="digest.digest">
<field name="name">Weekly Stats in Odoo</field>
<field name="name">Awesome Stats with Odoo</field>
<field name="periodicity">daily</field>
<field name="user_ids" eval="[(4, ref('base.user_admin'))]"/>
<field name="next_run_date" eval="(DateTime.now() + timedelta(days=7)).strftime('%Y-%m-%d')"/>
<field name="next_run_date" eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d')"/>
<field name="kpi_res_users_connected">True</field>
<field name="kpi_mail_message_total">True</field>
</record>
......
......@@ -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 '')
......
......@@ -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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment