[IMP] website_slides: send an email when an attendee finishes a course

PURPOSE

After this commit, an email will be sent to congratulate the attendee
for the completion of a course.

SPECIFICATION

Adds a new 'Completion Email' template field in the slide.channel model
that will be sent automatically after an attendee completes a course.

LINKS

Task ID : 2216352
PR : #49592
This commit is contained in:
Patrick Hoste
2020-08-24 13:42:54 +00:00
committed by Thibault Delavallée
parent 0b43747a4b
commit 375e38fcd8
6 changed files with 108 additions and 23 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
{
'name': 'eLearning',
'version': '2.2',
'version': '2.3',
'sequence': 125,
'summary': 'Manage and publish an eLearning platform',
'website': 'https://www.odoo.com/page/slides',
+32
View File
@@ -63,6 +63,38 @@
<field name="auto_delete" eval="True"/>
</record>
<!-- Completed Channel Message -->
<record id="mail_template_channel_completed" model="mail.template">
<field name="name">Completed Course</field>
<field name="model_id" ref="model_slide_channel_partner"/>
<field name="subject">Congratulation! You completed ${object.channel_id.name}</field>
<field name="email_from">${(object.channel_id.user_id.email_formatted or object.channel_id.user_id.company_id.catchall_formatted) | safe}</field>
<field name="partner_to">${object.partner_id.id}</field>
<field name="body_html" type="html">
<div style="margin: 0px; padding: 0px;">
<div style="margin: 0px; padding: 0px; font-size: 13px;">
<p style="margin: 0px;">Hello ${object.partner_id.name},</p><br/>
<p><b>Congratulations!</b></p>
<p>You've completed the course <b>${object.channel_id.name}</b></p>
<p>Check out the other available courses.</p><br/>
<div style="margin: 16px 8px 16px 8px; text-align: center;">
<a href="/slides/all" style="background-color: #875a7b; padding: 8px 16px 8px 16px; text-decoration: none; color: #fff; border-radius: 5px;">
Explore courses
</a>
</div>
Enjoy this exclusive content!
% if object.channel_id.user_id.signature
<br />
${object.channel_id.user_id.signature | safe}
% endif
</div>
</div>
</field>
<field name="auto_delete" eval="True"/>
<field name="lang">${object.partner_id.lang}</field>
</record>
<!-- Channel subtypes -->
<record id="mt_channel_slide_published" model="mail.message.subtype">
<field name="name">Presentation Published</field>
+8 -3
View File
@@ -11,8 +11,10 @@ class ResPartner(models.Model):
'slide.channel', 'slide_channel_partner', 'partner_id', 'channel_id',
string='eLearning Courses', groups="website_slides.group_website_slides_officer")
slide_channel_completed_ids = fields.One2many(
'slide.channel', string='Completed Courses', compute='_compute_slide_channel_completed_ids',
search='_search_slide_channel_completed_ids')
'slide.channel', string='Completed Courses',
compute='_compute_slide_channel_completed_ids',
search='_search_slide_channel_completed_ids',
groups="website_slides.group_website_slides_officer")
slide_channel_count = fields.Integer(
'Course Count', compute='_compute_slide_channel_count',
groups="website_slides.group_website_slides_officer")
@@ -28,7 +30,10 @@ class ResPartner(models.Model):
]).mapped('channel_id')
def _search_slide_channel_completed_ids(self, operator, value):
cp_done = self.env['slide.channel.partner'].search([('channel_id', operator, value), ('completed', '=', True)])
cp_done = self.env['slide.channel.partner'].sudo().search([
('channel_id', operator, value),
('completed', '=', True)
])
return [('id', 'in', cp_done.partner_id.ids)]
@api.depends('is_company')
+64 -11
View File
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import uuid
from collections import defaultdict
@@ -11,6 +12,8 @@ from odoo.addons.http_routing.models.ir_http import slug
from odoo.exceptions import AccessError
from odoo.osv import expression
_logger = logging.getLogger(__name__)
class ChannelUsersRelation(models.Model):
_name = 'slide.channel.partner'
@@ -38,21 +41,16 @@ class ChannelUsersRelation(models.Model):
mapped_data.setdefault(item['channel_id'][0], dict())
mapped_data[item['channel_id'][0]][item['partner_id'][0]] = item['__count']
partner_karma = dict.fromkeys(self.mapped('partner_id').ids, 0)
completed_records = self.env['slide.channel.partner']
for record in self:
record.completed_slides_count = mapped_data.get(record.channel_id.id, dict()).get(record.partner_id.id, 0)
record.completion = 100.0 if record.completed else round(100.0 * record.completed_slides_count / (record.channel_id.total_slides or 1))
if not record.completed and record.completed_slides_count >= record.channel_id.total_slides:
record.completed = True
partner_karma[record.partner_id.id] += record.channel_id.karma_gen_channel_finish
completed_records += record
partner_karma = {partner_id: karma_to_add
for partner_id, karma_to_add in partner_karma.items() if karma_to_add > 0}
if partner_karma:
users = self.env['res.users'].sudo().search([('partner_id', 'in', list(partner_karma.keys()))])
for user in users:
users.add_karma(partner_karma[user.partner_id.id])
if completed_records:
completed_records._set_as_completed()
completed_records._send_completed_mail()
def unlink(self):
"""
@@ -71,6 +69,58 @@ class ChannelUsersRelation(models.Model):
self.env['slide.slide.partner'].search(removed_slide_partner_domain).unlink()
return super(ChannelUsersRelation, self).unlink()
def _set_as_completed(self):
""" Set record as completed and compute karma gains """
partner_karma = dict.fromkeys(self.mapped('partner_id').ids, 0)
for record in self:
record.completed = True
partner_karma[record.partner_id.id] += record.channel_id.karma_gen_channel_finish
partner_karma = {
partner_id: karma_to_add
for partner_id, karma_to_add in partner_karma.items() if karma_to_add > 0
}
if partner_karma:
users = self.env['res.users'].sudo().search([('partner_id', 'in', list(partner_karma.keys()))])
for user in users:
users.add_karma(partner_karma[user.partner_id.id])
def _send_completed_mail(self):
""" Send an email to the attendee when he has successfully completed a course. """
template_to_records = dict()
for record in self:
template = record.channel_id.completed_template_id
if template:
records = template_to_records.setdefault(template, self.env['slide.channel.partner'])
records += record
for template, records in template_to_records.items():
record_email_values = template.generate_email(self.ids, ['subject', 'body_html', 'email_from', 'partner_to'])
mail_mail_values = []
for record in self:
email_values = record_email_values.get(record.id)
if not email_values or not email_values.get('partner_ids'):
continue
email_values.update(
author_id=self.channel_id.user_id.partner_id.id or self.env.company.partner_id.id,
auto_delete=True,
recipient_ids=[(4, pid) for pid in email_values['partner_ids']],
)
email_values['body_html'] = template._render_encapsulate(
'mail.mail_notification_light', email_values['body_html'],
add_context={
'message': self.env['mail.message'].sudo().new(dict(body=email_values['body_html'], record_name=record.channel_id.name)),
'model_description': _('Completed Course') # tde fixme: translate into partner lang
}
)
mail_mail_values.append(email_values)
if mail_mail_values:
self.env['mail.mail'].sudo().create(mail_mail_values)
class Channel(models.Model):
""" A channel is a container of slides. """
@@ -146,12 +196,15 @@ class Channel(models.Model):
" * post comment and review on training course;")
publish_template_id = fields.Many2one(
'mail.template', string='New Content Email',
help="Email template to send slide publication through email",
help="Email attendees once a new content is published",
default=lambda self: self.env['ir.model.data'].xmlid_to_res_id('website_slides.slide_template_published'))
share_template_id = fields.Many2one(
'mail.template', string='Share Template',
help="Email template used when sharing a slide",
default=lambda self: self.env['ir.model.data'].xmlid_to_res_id('website_slides.slide_template_shared'))
completed_template_id = fields.Many2one(
'mail.template', string='Completion Email', help="Email attendees once they've finished the course",
default=lambda self: self.env['ir.model.data'].xmlid_to_res_id('website_slides.mail_template_channel_completed'))
enroll = fields.Selection([
('public', 'Public'), ('invite', 'On Invitation')],
default='public', string='Enroll Policy', required=True,
@@ -13,13 +13,6 @@
</field>
</record>
<record id="slide_channel_partner_action" model="ir.actions.act_window">
<field name="name">Attendees</field>
<field name="res_model">slide.channel.partner</field>
<field name="view_mode">tree</field>
<field name="search_view_id" ref="website_slides.slide_channel_partner_view_search"/>
</record>
<record id="slide_channel_partner_view_tree" model="ir.ui.view">
<field name="name">slide.channel.partner.tree</field>
<field name="model">slide.channel.partner</field>
@@ -39,6 +32,7 @@
<field name="name">Attendees</field>
<field name="res_model">slide.channel.partner</field>
<field name="view_mode">tree,form</field>
<field name="search_view_id" ref="website_slides.slide_channel_partner_view_search"/>
</record>
</data>
</odoo>
@@ -102,8 +102,9 @@
<group>
<group name="communication" string="Communication">
<field string="Allow Rating" name="allow_comment"/>
<field name="publish_template_id" domain="[('model','=','slide.slide')]" groups="base.group_no_one"/>
<field name="publish_template_id" domain="[('model','=','slide.slide')]"/>
<field name="share_template_id" domain="[('model','=','slide.slide')]" groups="base.group_no_one"/>
<field name="completed_template_id"/>
</group>
<group name="display" string="Display">
<field name="visibility" widget="radio"/>