From 98bdb241ded7beb4a2d958387ad2d5ec620feb3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 25 Jul 2019 10:50:35 +0000 Subject: [PATCH] [IMP] website_slides: add an overview (kanban view) on course model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PURPOSE eLearning should have its own application in manager. Indeed this is becoming a big application and having it embedded inside Website application is not enough anymore. It should also hold updated and easy-to-use menus, navigation, actions and views in backend. SPECIFICATIONS Add course (slide.channel) kanban view. Each tile should contain * Title of the course (make it big, o_primary) * Tags below the title * Primary button "New Lesson" * Statistics : * Attendees * Running (progress != 100%) * (if one content is certificate) Certified or Finished * See * v1: https://drive.google.com/a/odoo.com/file/d/13K8FmkbwmBG4JQ5gH3Q5T8OlWPs3blcu/view?usp=drivesdk With FP remarks https://drive.google.com/a/odoo.com/file/d/1mb7LUT0VUNs5fMcKB9c0Bfaqk2-XFFuu/view?usp=drivesdk * remove globe, type and visibility of course, lessen padding between rating views and watch time; * for rating, instead of 8.00 => 5 reviews (4.5/5); * replace "View Lesson" / "Invite" by "View Course" => Send to the front-end * In the menu of the card, add: * Invite (same behavior than before, when it was a primary btn) * click on x Contents: land on the contents, group by category with sequence * re-order of the 3 infos/Stats at the bottom of the card: 1. Content, 2. Attendee, 3. Certified * + number: font-weight: 500 + font-size 1.2em * + labels: remove text-uppercase, + text-color: #666666 * + the "whole box" is clickable LINKS Task 1978729 PR #35061 Co-Authored-By: Thibault Delavallée Co-Authored-By: Jérémy Hennecart --- addons/website_slides/__manifest__.py | 1 + .../models/res_config_settings.py | 4 +- addons/website_slides/models/slide_channel.py | 64 +++++++++++-- .../static/src/scss/slide_channel_views.scss | 7 ++ addons/website_slides/views/assets.xml | 1 + .../views/res_config_settings_views.xml | 6 +- .../views/res_partner_views.xml | 5 + .../views/slide_channel_views.xml | 96 ++++++++++++++++++- .../views/slide_channel_views.xml | 15 +++ 9 files changed, 182 insertions(+), 17 deletions(-) create mode 100644 addons/website_slides/static/src/scss/slide_channel_views.scss diff --git a/addons/website_slides/__manifest__.py b/addons/website_slides/__manifest__.py index 5373f5bd6de..1da8adc4eee 100644 --- a/addons/website_slides/__manifest__.py +++ b/addons/website_slides/__manifest__.py @@ -29,6 +29,7 @@ Featuring 'views/res_config_settings_views.xml', 'views/res_partner_views.xml', 'views/rating_rating_views.xml', + 'views/res_partner_views.xml', 'views/slide_question_views.xml', 'views/slide_slide_views.xml', 'views/slide_channel_partner_views.xml', diff --git a/addons/website_slides/models/res_config_settings.py b/addons/website_slides/models/res_config_settings.py index fa9e7508afc..8085cd407c8 100644 --- a/addons/website_slides/models/res_config_settings.py +++ b/addons/website_slides/models/res_config_settings.py @@ -8,6 +8,6 @@ class ResConfigSettings(models.TransientModel): _inherit = "res.config.settings" website_slide_google_app_key = fields.Char(related='website_id.website_slide_google_app_key', readonly=False) - module_website_sale_slides = fields.Boolean(string="Sell courses") - module_website_slides_forum = fields.Boolean(string="Forum on Courses") + module_website_sale_slides = fields.Boolean(string="Sell on eCommerce") + module_website_slides_forum = fields.Boolean(string="Forum") module_website_slides_survey = fields.Boolean(string="Certifications") diff --git a/addons/website_slides/models/slide_channel.py b/addons/website_slides/models/slide_channel.py index 4496c59b391..78c32b55b74 100644 --- a/addons/website_slides/models/slide_channel.py +++ b/addons/website_slides/models/slide_channel.py @@ -73,6 +73,7 @@ class ChannelUsersRelation(models.Model): def _post_completion_hook(self): pass + class Channel(models.Model): """ A channel is a container of slides. """ _name = 'slide.channel' @@ -96,6 +97,7 @@ class Channel(models.Model): string="Course type", default="documentation", required=True) sequence = fields.Integer(default=10, help='Display order') user_id = fields.Many2one('res.users', string='Responsible', default=lambda self: self.env.uid) + color = fields.Integer('Color Index', default=0, help='Used to decorate kanban view') tag_ids = fields.Many2many( 'slide.channel.tag', 'slide_channel_tag_rel', 'channel_id', 'tag_id', string='Tags', help='Used to categorize and filter displayed channels/courses') @@ -151,6 +153,7 @@ class Channel(models.Model): 'res.partner', 'slide_channel_partner', 'channel_id', 'partner_id', string='Members', help="All members of the channel.", context={'active_test': False}) members_count = fields.Integer('Attendees count', compute='_compute_members_count') + members_done_count = fields.Integer('Attendees Done Count', compute='_compute_members_done_count') is_member = fields.Boolean(string='Is Member', compute='_compute_is_member') channel_partner_ids = fields.One2many('slide.channel.partner', 'channel_id', string='Members Information', groups='website.group_website_publisher') upload_group_ids = fields.Many2many( @@ -184,6 +187,13 @@ class Channel(models.Model): for channel in self: channel.members_count = data.get(channel.id, 0) + @api.depends('channel_partner_ids.channel_id', 'channel_partner_ids.completed') + def _compute_members_done_count(self): + read_group_res = self.env['slide.channel.partner'].sudo().read_group(['&', ('channel_id', 'in', self.ids), ('completed', '=', True)], ['channel_id'], 'channel_id') + data = dict((res['channel_id'][0], res['channel_id_count']) for res in read_group_res) + for channel in self: + channel.members_done_count = data.get(channel.id, 0) + @api.depends('channel_partner_ids.partner_id') @api.model def _compute_is_member(self): @@ -366,21 +376,34 @@ class Channel(models.Model): # Business / Actions # --------------------------------------------------------- - def action_redirect_to_members(self): - action = self.env.ref('website_slides.slide_channel_partner_action').read()[0] - action['view_mode'] = 'tree' - action['domain'] = [('channel_id', 'in', self.ids)] + def action_redirect_to_members(self, state=None): + action = self.env.ref('website_slides.res_partner_action_slide_channel').read()[0] + action['context'] = {'active_test': False} + if state == 'running': + action['domain'] = [('id', 'in', self.env['slide.channel.partner'].sudo().search([ + ('channel_id', 'in', self.ids), + ('completed', '=', False) + ]).mapped('partner_id').ids)] + elif state == 'completed': + action['domain'] = [('id', 'in', self.env['slide.channel.partner'].sudo().search([ + ('channel_id', 'in', self.ids), + ('completed', '=', True) + ]).mapped('partner_id').ids)] + else: + action['domain'] = [('id', 'in', self.mapped('partner_ids').ids)] if len(self) == 1: - action['context'] = {'default_channel_id': self.id} - + action['display_name'] = _('Attendees of %s') % self.name + action['context'] = {'active_test': False, 'default_channel_id': self.id} return action + def action_redirect_to_running_members(self): + return self.action_redirect_to_members('running') + + def action_redirect_to_done_members(self): + return self.action_redirect_to_members('completed') + def action_channel_invite(self): self.ensure_one() - - if self.enroll != 'invite': - raise UserError(_("You cannot send invitations for channels that are not set as 'invite'.")) - template = self.env.ref('website_slides.mail_template_slide_channel_invite', raise_if_not_found=False) local_context = dict( @@ -474,6 +497,27 @@ class Channel(models.Model): if removed_channel_partner_domain: self.env['slide.channel.partner'].sudo().search(removed_channel_partner_domain).unlink() + def action_view(self): + return { + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'slide.channel', + 'res_id': self.id, + } + + def action_view_slides(self): + action = self.env.ref('website_slides.slide_slide_action').read()[0] + action['context'] = {'default_channel_id': self.id} + action['domain'] = [('channel_id', "=", self.id)] + return action + + def action_view_ratings(self): + action = self.env.ref('website_slides.rating_rating_action_slide_channel').read()[0] + action['name'] = _('Rating of %s') % (self.name) + action['domain'] = [('res_id', 'in', self.ids)] + return action + # --------------------------------------------------------- # Rating Mixin API # --------------------------------------------------------- diff --git a/addons/website_slides/static/src/scss/slide_channel_views.scss b/addons/website_slides/static/src/scss/slide_channel_views.scss new file mode 100644 index 00000000000..3bd8203c4eb --- /dev/null +++ b/addons/website_slides/static/src/scss/slide_channel_views.scss @@ -0,0 +1,7 @@ +$o-kanban-large-record-width: 400px; + +.o_kanban_view.o_slide_channel_kanban { + .o_kanban_record { + width: $o-kanban-large-record-width + } +} diff --git a/addons/website_slides/views/assets.xml b/addons/website_slides/views/assets.xml index 1c990e7ad50..62129e58e6b 100644 --- a/addons/website_slides/views/assets.xml +++ b/addons/website_slides/views/assets.xml @@ -4,6 +4,7 @@ diff --git a/addons/website_slides/views/res_config_settings_views.xml b/addons/website_slides/views/res_config_settings_views.xml index 6925fbc33b5..3e5a64010f0 100644 --- a/addons/website_slides/views/res_config_settings_views.xml +++ b/addons/website_slides/views/res_config_settings_views.xml @@ -38,7 +38,7 @@
@@ -50,7 +50,7 @@
@@ -62,7 +62,7 @@
diff --git a/addons/website_slides/views/res_partner_views.xml b/addons/website_slides/views/res_partner_views.xml index a5e7161c00b..5455a0195f5 100644 --- a/addons/website_slides/views/res_partner_views.xml +++ b/addons/website_slides/views/res_partner_views.xml @@ -36,4 +36,9 @@ + + Attendees + res.partner + kanban,tree,form + diff --git a/addons/website_slides/views/slide_channel_views.xml b/addons/website_slides/views/slide_channel_views.xml index edd662541e5..898599ede25 100644 --- a/addons/website_slides/views/slide_channel_views.xml +++ b/addons/website_slides/views/slide_channel_views.xml @@ -218,11 +218,103 @@ + + slide.channel.view.kanban + slide.channel + + + + + + +
+ +
+
+
+ + + +
+
+ +
+
+
+
+
+
+ +
+
+
+ + + reviews (/5) + +
+
+ + +
+
+ + +
+
+
+ +
+
+
+
+
+
+
+ eLearning Overview slide.channel - tree,form - + kanban,tree,form +

Create a course diff --git a/addons/website_slides_survey/views/slide_channel_views.xml b/addons/website_slides_survey/views/slide_channel_views.xml index e28b67997cf..2b93ef0dd2a 100644 --- a/addons/website_slides_survey/views/slide_channel_views.xml +++ b/addons/website_slides_survey/views/slide_channel_views.xml @@ -10,4 +10,19 @@ + + + slide.channel.view.kanban.inherit.survey + slide.channel + + + + + + + Certified + Finished + + +