first commit
This commit is contained in:
commit
f7b6572ded
2
__init__.py
Normal file
2
__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import models
|
||||
23
__manifest__.py
Normal file
23
__manifest__.py
Normal file
@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
{
|
||||
'name': 'DISC Personality Test Survey',
|
||||
'version': '1.0',
|
||||
'category': 'Human Resources',
|
||||
'summary': 'DISC Personality Assessment Test in Survey module',
|
||||
'description': """
|
||||
This module adds a DISC Personality Test to the Odoo Survey module.
|
||||
It includes 24 sets of questions where participants choose the 'Most' (Paling)
|
||||
and 'Least' (Kurang) descriptive traits.
|
||||
""",
|
||||
'depends': ['survey'],
|
||||
'data': [
|
||||
'data/survey_data.xml',
|
||||
'views/survey_user_input_views.xml',
|
||||
'views/survey_templates.xml',
|
||||
'report/survey_disc_report.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'application': True,
|
||||
'license': 'LGPL-3',
|
||||
}
|
||||
BIN
__pycache__/__init__.cpython-312.pyc
Normal file
BIN
__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
1092
data/survey_data.xml
Normal file
1092
data/survey_data.xml
Normal file
File diff suppressed because it is too large
Load Diff
3
models/__init__.py
Normal file
3
models/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import survey_question_answer
|
||||
from . import survey_user_input
|
||||
BIN
models/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
models/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
models/__pycache__/survey_question_answer.cpython-312.pyc
Normal file
BIN
models/__pycache__/survey_question_answer.cpython-312.pyc
Normal file
Binary file not shown.
BIN
models/__pycache__/survey_user_input.cpython-312.pyc
Normal file
BIN
models/__pycache__/survey_user_input.cpython-312.pyc
Normal file
Binary file not shown.
21
models/survey_question_answer.py
Normal file
21
models/survey_question_answer.py
Normal file
@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models, fields
|
||||
|
||||
class SurveyQuestionAnswer(models.Model):
|
||||
_inherit = 'survey.question.answer'
|
||||
|
||||
disc_category_p = fields.Selection([
|
||||
('D', 'Dominance'),
|
||||
('I', 'Influence'),
|
||||
('S', 'Steadiness'),
|
||||
('C', 'Conscientiousness'),
|
||||
('*', 'Star (No Score)'),
|
||||
], string='DISC Category (Most/P)')
|
||||
|
||||
disc_category_k = fields.Selection([
|
||||
('D', 'Dominance'),
|
||||
('I', 'Influence'),
|
||||
('S', 'Steadiness'),
|
||||
('C', 'Conscientiousness'),
|
||||
('*', 'Star (No Score)'),
|
||||
], string='DISC Category (Least/K)')
|
||||
122
models/survey_user_input.py
Normal file
122
models/survey_user_input.py
Normal file
@ -0,0 +1,122 @@
|
||||
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')
|
||||
89
report/survey_disc_report.xml
Normal file
89
report/survey_disc_report.xml
Normal file
@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="action_report_disc_result" model="ir.actions.report">
|
||||
<field name="name">DISC Assessment Result</field>
|
||||
<field name="model">survey.user_input</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">survey_disc_test.report_disc_result</field>
|
||||
<field name="report_file">survey_disc_test.report_disc_result</field>
|
||||
<field name="print_report_name">'DISC_Result_%s' % (object.partner_id.name or 'User')</field>
|
||||
<field name="binding_model_id" ref="model_survey_user_input"/>
|
||||
<field name="binding_type">report</field>
|
||||
</record>
|
||||
|
||||
<template id="report_disc_result">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="o">
|
||||
<t t-call="web.external_layout">
|
||||
<div class="page">
|
||||
<div class="text-center">
|
||||
<h2>DISC Personality Assessment Report</h2>
|
||||
<hr/>
|
||||
</div>
|
||||
|
||||
<div class="row mt32">
|
||||
<div class="col-6">
|
||||
<strong>Participant:</strong> <span t-field="o.partner_id.name"/><br/>
|
||||
<strong>Email:</strong> <span t-field="o.email"/><br/>
|
||||
<strong>Date:</strong> <span t-field="o.create_date" t-options='{"widget": "date"}'/>
|
||||
</div>
|
||||
<div class="col-6 text-right">
|
||||
<h3>Profile: <span t-field="o.disc_profile" class="text-primary"/></h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt32 text-center">
|
||||
<div class="col-4">
|
||||
<table class="table table-sm border">
|
||||
<thead class="bg-primary text-white">
|
||||
<tr><th colspan="2">GRAPH 1: MOST (MASK)</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>D</td><td t-field="o.p_d"/></tr>
|
||||
<tr><td>I</td><td t-field="o.p_i"/></tr>
|
||||
<tr><td>S</td><td t-field="o.p_s"/></tr>
|
||||
<tr><td>C</td><td t-field="o.p_c"/></tr>
|
||||
<tr><td>*</td><td t-field="o.p_star"/></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<table class="table table-sm border">
|
||||
<thead class="bg-primary text-white">
|
||||
<tr><th colspan="2">GRAPH 2: LEAST (CORE)</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>D</td><td t-field="o.k_d"/></tr>
|
||||
<tr><td>I</td><td t-field="o.k_i"/></tr>
|
||||
<tr><td>S</td><td t-field="o.k_s"/></tr>
|
||||
<tr><td>C</td><td t-field="o.k_c"/></tr>
|
||||
<tr><td>*</td><td t-field="o.k_star"/></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<table class="table table-sm border">
|
||||
<thead class="bg-primary text-white">
|
||||
<tr><th colspan="2">GRAPH 3: CHANGE (MIRROR)</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>D</td><td t-field="o.c_d"/></tr>
|
||||
<tr><td>I</td><td t-field="o.c_i"/></tr>
|
||||
<tr><td>S</td><td t-field="o.c_s"/></tr>
|
||||
<tr><td>C</td><td t-field="o.c_c"/></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt32 p-3 bg-light border">
|
||||
<h4>Behavioral Interpretation</h4>
|
||||
<p>Based on the scores above, the participant exhibits a <strong><span t-field="o.disc_profile"/></strong> personality profile.</p>
|
||||
<p>This report provides an automated scoring based on the 24-question DISC assessment. For a deeper analysis, please consult with a behavioral specialist.</p>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
23
views/survey_templates.xml
Normal file
23
views/survey_templates.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="survey_fill_form_done_inherit_disc" inherit_id="survey.survey_fill_form_done">
|
||||
<xpath expr="//div[hasclass('o_survey_finished')]" position="inside">
|
||||
<t t-if="survey.title == 'DISC Personality Test'">
|
||||
<div class="mt-4 p-4 bg-light border rounded text-center">
|
||||
<h3 class="text-primary">Your DISC Profile</h3>
|
||||
<h2 class="display-4 font-weight-bold" t-esc="answer.disc_profile"/>
|
||||
<p class="lead mt-3">Thank you for completing the assessment!</p>
|
||||
</div>
|
||||
<!-- Hide the default scoring -->
|
||||
<style>
|
||||
.o_survey_finished div:has(> h1:contains("You scored")),
|
||||
.o_survey_finished div:has(> h1:contains("Congratulations")),
|
||||
.o_survey_finished .text-danger,
|
||||
.o_survey_finished .text-success {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
45
views/survey_user_input_views.xml
Normal file
45
views/survey_user_input_views.xml
Normal file
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="survey_user_input_view_form_inherit_disc" model="ir.ui.view">
|
||||
<field name="name">survey.user.input.view.form.inherit.disc</field>
|
||||
<field name="model">survey.user_input</field>
|
||||
<field name="inherit_id" ref="survey.survey_user_input_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//notebook" position="inside">
|
||||
<page string="DISC Result" invisible="not p_d and not p_i and not p_s and not p_c">
|
||||
<group string="DISC Profile Summary">
|
||||
<field name="disc_profile" readonly="1" class="oe_inline oe_bold" style="font-size: 20px;"/>
|
||||
</group>
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<group string="Graph 1: MOST (Mask)">
|
||||
<field name="p_d" string="D"/>
|
||||
<field name="p_i" string="I"/>
|
||||
<field name="p_s" string="S"/>
|
||||
<field name="p_c" string="C"/>
|
||||
<field name="p_star" string="*"/>
|
||||
</group>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<group string="Graph 2: LEAST (Core)">
|
||||
<field name="k_d" string="D"/>
|
||||
<field name="k_i" string="I"/>
|
||||
<field name="k_s" string="S"/>
|
||||
<field name="k_c" string="C"/>
|
||||
<field name="k_star" string="*"/>
|
||||
</group>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<group string="Graph 3: CHANGE (Mirror)">
|
||||
<field name="c_d" string="D"/>
|
||||
<field name="c_i" string="I"/>
|
||||
<field name="c_s" string="S"/>
|
||||
<field name="c_c" string="C"/>
|
||||
</group>
|
||||
</div>
|
||||
</div>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Loading…
Reference in New Issue
Block a user