from odoo import models, fields, api, _ from odoo.exceptions import ValidationError class SurveyUserInput(models.Model): _inherit = 'survey.user_input' # MOST (Graph 1) p_d = fields.Integer('P: D', compute='_compute_disc_scores', store=True) p_i = fields.Integer('P: I', compute='_compute_disc_scores', store=True) p_s = fields.Integer('P: S', compute='_compute_disc_scores', store=True) p_c = fields.Integer('P: C', compute='_compute_disc_scores', store=True) p_star = fields.Integer('P: *', compute='_compute_disc_scores', store=True) # LEAST (Graph 2) k_d = fields.Integer('K: D', compute='_compute_disc_scores', store=True) k_i = fields.Integer('K: I', compute='_compute_disc_scores', store=True) k_s = fields.Integer('K: S', compute='_compute_disc_scores', store=True) k_c = fields.Integer('K: C', compute='_compute_disc_scores', store=True) k_star = fields.Integer('K: *', compute='_compute_disc_scores', store=True) # CHANGE (Graph 3) c_d = fields.Integer('C: D', compute='_compute_disc_scores', store=True) c_i = fields.Integer('C: I', compute='_compute_disc_scores', store=True) c_s = fields.Integer('C: S', compute='_compute_disc_scores', store=True) c_c = fields.Integer('C: C', compute='_compute_disc_scores', store=True) disc_profile = fields.Char('DISC Profile', compute='_compute_disc_scores', store=True) def _mark_done(self): """ Strictly enforce DISC validation: Exactly 1 P and 1 K per set. """ for rec in self: # Group lines by question lines_by_question = {} for line in rec.user_input_line_ids: if not line.matrix_row_id or not line.suggested_answer_id: continue q_id = line.question_id if q_id not in lines_by_question: lines_by_question[q_id] = [] lines_by_question[q_id].append(line) # We expect 24 sets if len(lines_by_question) < 24: raise ValidationError(_("You must answer all 24 sets of the assessment.")) for q_id, lines in lines_by_question.items(): p_count = len([l for l in lines if l.suggested_answer_id.value == 'P']) k_count = len([l for l in lines if l.suggested_answer_id.value == 'K']) if p_count != 1 or k_count != 1: raise ValidationError( _("Invalid selection in '%s'. You must select exactly one 'P' (Most) and one 'K' (Least).") % q_id.title ) return super(SurveyUserInput, self)._mark_done() @api.depends('user_input_line_ids', 'user_input_line_ids.suggested_answer_id') def _compute_disc_scores(self): for rec in self: # Initialize counts counts = { 'p': {'D': 0, 'I': 0, 'S': 0, 'C': 0, '*': 0}, 'k': {'D': 0, 'I': 0, 'S': 0, 'C': 0, '*': 0} } # Group lines by question to check for multiple selections per set lines_by_question = {} for line in rec.user_input_line_ids: if not line.matrix_row_id or not line.suggested_answer_id: continue q_id = line.question_id.id if q_id not in lines_by_question: lines_by_question[q_id] = [] lines_by_question[q_id].append(line) # Process each question set for q_id, lines in lines_by_question.items(): p_selections = [l for l in lines if l.suggested_answer_id.value == 'P'] k_selections = [l for l in lines if l.suggested_answer_id.value == 'K'] # Validation: We only count if exactly one P and one K are selected per set # This enforces the "1 P and 1 K per set" rule if len(p_selections) == 1: line = p_selections[0] cat = line.matrix_row_id.disc_category_p if cat in counts['p']: counts['p'][cat] += 1 if len(k_selections) == 1: line = k_selections[0] cat = line.matrix_row_id.disc_category_k if cat in counts['k']: counts['k'][cat] += 1 # Update fields rec.p_d = counts['p']['D'] rec.p_i = counts['p']['I'] rec.p_s = counts['p']['S'] rec.p_c = counts['p']['C'] rec.p_star = counts['p']['*'] rec.k_d = counts['k']['D'] rec.k_i = counts['k']['I'] rec.k_s = counts['k']['S'] rec.k_c = counts['k']['C'] rec.k_star = counts['k']['*'] # Graph 3: Change (P - K) rec.c_d = rec.p_d - rec.k_d rec.c_i = rec.p_i - rec.k_i rec.c_s = rec.p_s - rec.k_s rec.c_c = rec.p_c - rec.k_c # Determine Profile (based on highest score in Graph 3) scores = {'D': rec.c_d, 'I': rec.c_i, 'S': rec.c_s, 'C': rec.c_c} top_trait = max(scores, key=scores.get) profile_map = { 'D': 'Director (Dominance)', 'I': 'Influencer (Influence)', 'S': 'Steady (Steadiness)', 'C': 'Compliant (Compliance)' } rec.disc_profile = profile_map.get(top_trait, 'Mixed')