diff --git a/addons/mass_mailing_slides/views/slide_channel_views.xml b/addons/mass_mailing_slides/views/slide_channel_views.xml index 9b7f35b202c..66265fa222c 100644 --- a/addons/mass_mailing_slides/views/slide_channel_views.xml +++ b/addons/mass_mailing_slides/views/slide_channel_views.xml @@ -5,16 +5,11 @@ slide.channel - - - - Mail Attendees - - - + + + @@ -23,10 +18,13 @@ slide.channel - - - Mail Attendees - + + + + Contact Attendees + + diff --git a/addons/website_slides/__manifest__.py b/addons/website_slides/__manifest__.py index 2c3acef508f..d16c423565c 100644 --- a/addons/website_slides/__manifest__.py +++ b/addons/website_slides/__manifest__.py @@ -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', diff --git a/addons/website_slides/data/mail_data.xml b/addons/website_slides/data/mail_data.xml index f7fa34203ee..93b3a996cf6 100644 --- a/addons/website_slides/data/mail_data.xml +++ b/addons/website_slides/data/mail_data.xml @@ -63,6 +63,38 @@ + + + Completed Course + + Congratulation! You completed ${object.channel_id.name} + ${(object.channel_id.user_id.email_formatted or object.channel_id.user_id.company_id.catchall_formatted) | safe} + ${object.partner_id.id} + + + + Hello ${object.partner_id.name}, + Congratulations! + You've completed the course ${object.channel_id.name} + Check out the other available courses. + + + + Explore courses + + + Enjoy this exclusive content! + % if object.channel_id.user_id.signature + + ${object.channel_id.user_id.signature | safe} + % endif + + + + + ${object.partner_id.lang} + + Presentation Published diff --git a/addons/website_slides/models/res_partner.py b/addons/website_slides/models/res_partner.py index a36b23227c8..54fd52e96d4 100644 --- a/addons/website_slides/models/res_partner.py +++ b/addons/website_slides/models/res_partner.py @@ -9,9 +9,32 @@ class ResPartner(models.Model): slide_channel_ids = fields.Many2many( 'slide.channel', 'slide_channel_partner', 'partner_id', 'channel_id', - string='eLearning Courses') - slide_channel_count = fields.Integer('Course Count', compute='_compute_slide_channel_count') - slide_channel_company_count = fields.Integer('Company Course Count', compute='_compute_slide_channel_company_count') + 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', + 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") + slide_channel_company_count = fields.Integer( + 'Company Course Count', compute='_compute_slide_channel_company_count', + groups="website_slides.group_website_slides_officer") + + def _compute_slide_channel_completed_ids(self): + for partner in self: + partner.slide_channel_completed_ids = self.env['slide.channel.partner'].search([ + ('partner_id', '=', partner.id), + ('completed', '=', True) + ]).mapped('channel_id') + + def _search_slide_channel_completed_ids(self, operator, value): + 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') def _compute_slide_channel_count(self): diff --git a/addons/website_slides/models/slide_channel.py b/addons/website_slides/models/slide_channel.py index 1479a51722d..890863155c7 100644 --- a/addons/website_slides/models/slide_channel.py +++ b/addons/website_slides/models/slide_channel.py @@ -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, diff --git a/addons/website_slides/static/src/scss/slide_views.scss b/addons/website_slides/static/src/scss/slide_views.scss index 035f4699309..6afd73e9423 100644 --- a/addons/website_slides/static/src/scss/slide_views.scss +++ b/addons/website_slides/static/src/scss/slide_views.scss @@ -7,6 +7,11 @@ $o-kanban-large-record-width: 400px; .o_kanban_record { width: $o-kanban-large-record-width; } + .o_kanban_slides_card_manage_pane { + > div { + @include o-kanban-dashboard-dropdown-link; + } + } } table.o_section_list_view tr.o_data_row.o_is_section { diff --git a/addons/website_slides/views/slide_channel_partner_views.xml b/addons/website_slides/views/slide_channel_partner_views.xml index 20227f7c5d3..8b88417d96b 100644 --- a/addons/website_slides/views/slide_channel_partner_views.xml +++ b/addons/website_slides/views/slide_channel_partner_views.xml @@ -13,13 +13,6 @@ - - Attendees - slide.channel.partner - tree - - - slide.channel.partner.tree slide.channel.partner @@ -39,6 +32,7 @@ Attendees slide.channel.partner tree,form + diff --git a/addons/website_slides/views/slide_channel_views.xml b/addons/website_slides/views/slide_channel_views.xml index f207e36a50c..04a6363fc94 100644 --- a/addons/website_slides/views/slide_channel_views.xml +++ b/addons/website_slides/views/slide_channel_views.xml @@ -102,8 +102,9 @@ - + + @@ -214,20 +215,26 @@ - - - - Delete - - - Edit - - - Lessons - - - Invite - + + + + + + + + Delete + + + + Edit + + + Lessons + + + Invite + +
Hello ${object.partner_id.name},
Congratulations!
You've completed the course ${object.channel_id.name}
Check out the other available courses.