[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:
committed by
Thibault Delavallée
parent
0b43747a4b
commit
375e38fcd8
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
Reference in New Issue
Block a user