356 lines
20 KiB
XML
356 lines
20 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<odoo>
|
|
<!-- ============================================================ -->
|
|
<!-- Quiz Views -->
|
|
<!-- ============================================================ -->
|
|
|
|
<record id="view_genius_quiz_tree" model="ir.ui.view">
|
|
<field name="name">genius.quiz.tree</field>
|
|
<field name="model">genius.quiz</field>
|
|
<field name="arch" type="xml">
|
|
<tree string="Quizzes">
|
|
<field name="name"/>
|
|
<field name="question_count"/>
|
|
<field name="passing_score"/>
|
|
<field name="attempt_count"/>
|
|
<field name="avg_score"/>
|
|
<field name="pass_rate"/>
|
|
</tree>
|
|
</field>
|
|
</record>
|
|
|
|
<record id="view_genius_quiz_form" model="ir.ui.view">
|
|
<field name="name">genius.quiz.form</field>
|
|
<field name="model">genius.quiz</field>
|
|
<field name="arch" type="xml">
|
|
<form string="Quiz">
|
|
<header>
|
|
<button name="action_analyze_difficulty" string="Analyze" type="object"
|
|
icon="fa-bar-chart"
|
|
attrs="{'invisible': [('attempt_count', '=', 0)]}"/>
|
|
<button name="action_reset_statistics" string="Reset Stats" type="object"
|
|
icon="fa-refresh"
|
|
confirm="This will delete all attempts and statistics. Continue?"
|
|
attrs="{'invisible': [('attempt_count', '=', 0)]}"/>
|
|
<button name="action_duplicate_quiz" string="Duplicate" type="object" icon="fa-copy"/>
|
|
</header>
|
|
<sheet>
|
|
<div class="oe_button_box" name="button_box">
|
|
<button class="oe_stat_button" icon="fa-question-circle">
|
|
<field name="question_count" widget="statinfo" string="Questions"/>
|
|
</button>
|
|
<button name="action_view_attempts" class="oe_stat_button" icon="fa-pencil-square" type="object">
|
|
<field name="attempt_count" widget="statinfo" string="Attempts"/>
|
|
</button>
|
|
<button class="oe_stat_button" icon="fa-check-circle">
|
|
<field name="pass_rate" widget="statinfo" string="Pass Rate"/>
|
|
</button>
|
|
</div>
|
|
<div class="oe_title d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<label for="name" class="oe_edit_only"/>
|
|
<h1>
|
|
<field name="name" placeholder="Quiz Title"/>
|
|
</h1>
|
|
</div>
|
|
<!-- Genius Test Button - Right side of title -->
|
|
<div class="genius-test-mode-container ml-auto" attrs="{'invisible': [('question_count', '=', 0)]}">
|
|
<field name="test_mode_opener" widget="genius_quiz_test_button"/>
|
|
</div>
|
|
</div>
|
|
<group>
|
|
<group string="Configuration">
|
|
<field name="passing_score"/>
|
|
<field name="time_limit_minutes"/>
|
|
<field name="max_attempts"/>
|
|
</group>
|
|
<group string="Behavior">
|
|
<field name="shuffle_questions"/>
|
|
<field name="show_correct_answers"/>
|
|
<field name="sample_size"/>
|
|
</group>
|
|
</group>
|
|
<notebook>
|
|
<page string="Questions" name="questions">
|
|
<field name="question_ids" context="{'default_quiz_id': active_id}">
|
|
<tree>
|
|
<field name="sequence" widget="handle"/>
|
|
<field name="question_text"/>
|
|
<field name="question_type"/>
|
|
<field name="points"/>
|
|
</tree>
|
|
</field>
|
|
</page>
|
|
<page string="Feedback & Customization" name="feedback">
|
|
<group>
|
|
<separator string="Success Feedback" colspan="2"/>
|
|
<field name="success_message" nolabel="1" placeholder="Message shown when user passes..."/>
|
|
|
|
<separator string="Failure Feedback" colspan="2"/>
|
|
<field name="fail_message" nolabel="1" placeholder="Message shown when user fails..."/>
|
|
</group>
|
|
</page>
|
|
<page string="Description" name="description">
|
|
<field name="description" placeholder="Internal notes or description..."/>
|
|
</page>
|
|
<page string="Statistics" name="stats">
|
|
<group>
|
|
<group>
|
|
<field name="attempt_count" readonly="1"/>
|
|
<field name="avg_score" readonly="1"/>
|
|
</group>
|
|
<group>
|
|
<field name="pass_rate" readonly="1"/>
|
|
</group>
|
|
</group>
|
|
</page>
|
|
<page string="Certificate" name="certificate">
|
|
<group>
|
|
<group string="Branding">
|
|
<field name="certificate_logo" widget="image" class="oe_avatar" options="{'size': [90, 90]}"/>
|
|
<field name="certificate_secondary_logo" widget="image" class="oe_avatar" options="{'size': [90, 90]}"/>
|
|
<field name="certificate_title" placeholder="CERTIFICATE"/>
|
|
<field name="certificate_issuer" placeholder="Training Department"/>
|
|
</group>
|
|
<group string="Signature & Stamp">
|
|
<field name="certificate_signature_image" widget="image" class="oe_avatar" options="{'size': [150, 60]}"/>
|
|
<field name="certificate_stamp" widget="image" class="oe_avatar" options="{'size': [90, 90]}"/>
|
|
<field name="certificate_signature_name" placeholder="Training Director"/>
|
|
<field name="certificate_signature_title" placeholder="Director of Training"/>
|
|
</group>
|
|
</group>
|
|
<group string="Achievement Description">
|
|
<field name="certificate_body_template" nolabel="1" colspan="2"
|
|
placeholder="has successfully completed the training course and demonstrated mastery..."/>
|
|
</group>
|
|
<div class="alert alert-info">
|
|
<i class="fa fa-info-circle"/> <strong>Variables:</strong>
|
|
<code>{user_name}</code> <code>{topic_name}</code> <code>{quiz_name}</code>
|
|
<br/><small class="text-muted">Note: Score and Date are displayed automatically in the certificate footer.</small>
|
|
</div>
|
|
<div class="mt-3">
|
|
<button name="action_preview_certificate" type="object" class="btn btn-primary" icon="fa-eye">
|
|
<i class="fa fa-eye"/> Preview Certificate
|
|
</button>
|
|
</div>
|
|
</page>
|
|
</notebook>
|
|
</sheet>
|
|
</form>
|
|
</field>
|
|
</record>
|
|
|
|
<record id="action_genius_quiz" model="ir.actions.act_window">
|
|
<field name="name">Quizzes</field>
|
|
<field name="res_model">genius.quiz</field>
|
|
<field name="view_mode">tree,form</field>
|
|
</record>
|
|
|
|
<!-- Menu Item -->
|
|
<menuitem id="menu_quizzes"
|
|
name="Quizzes"
|
|
parent="menu_tour_genius_root"
|
|
action="action_genius_quiz"
|
|
sequence="20"
|
|
groups="tour_genius.group_genius_instructor"/>
|
|
|
|
<record id="view_genius_question_form" model="ir.ui.view">
|
|
<field name="name">genius.quiz.question.form</field>
|
|
<field name="model">genius.quiz.question</field>
|
|
<field name="arch" type="xml">
|
|
<form string="Question">
|
|
<sheet>
|
|
<!-- Question Preview Title -->
|
|
<div class="oe_title">
|
|
<label for="name" class="oe_edit_only" string="Question Preview"/>
|
|
<h1>
|
|
<field name="name" readonly="1"/>
|
|
</h1>
|
|
</div>
|
|
|
|
<!-- Main Configuration -->
|
|
<group>
|
|
<group string="Configuration">
|
|
<field name="question_type" widget="radio"/>
|
|
<field name="points"/>
|
|
</group>
|
|
<group string="Status">
|
|
<field name="active" widget="boolean_toggle"/>
|
|
<field name="sequence" groups="base.group_no_one"/>
|
|
</group>
|
|
</group>
|
|
|
|
<!-- Question Content -->
|
|
<separator string="Question Content"/>
|
|
<field name="question_text" nolabel="1" placeholder="Enter your question here..."/>
|
|
|
|
<!-- Dynamic Hint Based on Type -->
|
|
<div class="alert alert-info" role="alert" attrs="{'invisible': [('question_type', 'not in', ['fill_blank'])]}">
|
|
<i class="fa fa-info-circle"/> <strong>Tip:</strong> Write your question with a blank to fill, e.g., "The capital of France is ___".
|
|
</div>
|
|
<div class="alert alert-info" role="alert" attrs="{'invisible': [('question_type', '!=', 'ordering')]}">
|
|
<i class="fa fa-info-circle"/> <strong>Tip:</strong> Add items below. The sequence order you set here is the <em>correct</em> order.
|
|
</div>
|
|
|
|
<notebook>
|
|
<!-- Answer Options (Single/Multiple Choice) -->
|
|
<page string="Answer Options" name="answers"
|
|
attrs="{'invisible': [('question_type', 'not in', ['single', 'multiple'])]}">
|
|
<field name="answer_ids">
|
|
<tree editable="bottom">
|
|
<field name="sequence" widget="handle"/>
|
|
<field name="answer_text" placeholder="Enter answer option..."/>
|
|
<field name="is_correct"/>
|
|
<field name="feedback" placeholder="Optional feedback..."/>
|
|
</tree>
|
|
</field>
|
|
<div class="text-muted mt-2">
|
|
<i class="fa fa-lightbulb-o"/>
|
|
<span attrs="{'invisible': [('question_type', '!=', 'single')]}">
|
|
Mark exactly <strong>one</strong> answer as correct.
|
|
</span>
|
|
<span attrs="{'invisible': [('question_type', '!=', 'multiple')]}">
|
|
Mark <strong>all</strong> correct answers.
|
|
</span>
|
|
</div>
|
|
</page>
|
|
|
|
<!-- Ordering Items -->
|
|
<page string="Items to Order" name="ordering"
|
|
attrs="{'invisible': [('question_type', '!=', 'ordering')]}">
|
|
<field name="answer_ids">
|
|
<tree editable="bottom">
|
|
<field name="sequence" widget="handle"/>
|
|
<field name="answer_text" placeholder="Enter item..."/>
|
|
</tree>
|
|
</field>
|
|
<div class="text-muted mt-2">
|
|
<i class="fa fa-lightbulb-o"/> Drag items to set the <strong>correct order</strong>. Users will see them shuffled.
|
|
</div>
|
|
</page>
|
|
|
|
<!-- Text Answer (Short Answer / Fill in the Blank) -->
|
|
<page string="Correct Answer" name="text_answer"
|
|
attrs="{'invisible': [('question_type', 'not in', ['short_answer', 'fill_blank'])]}">
|
|
<group>
|
|
<group>
|
|
<field name="correct_short_answer" placeholder="Expected answer..." attrs="{'required': [('question_type', 'in', ['short_answer', 'fill_blank'])]}"/>
|
|
<field name="answer_alternatives" placeholder="color, colour, Color"/>
|
|
</group>
|
|
<group>
|
|
<field name="case_sensitive"/>
|
|
</group>
|
|
</group>
|
|
<div class="text-muted">
|
|
<i class="fa fa-lightbulb-o"/> Use <strong>Alternative Answers</strong> for synonyms or spelling variations (comma-separated).
|
|
</div>
|
|
</page>
|
|
|
|
<!-- Explanation & Media -->
|
|
<page string="Explanation & Media" name="explanation">
|
|
<group>
|
|
<group string="Question Image">
|
|
<field name="image" widget="image" class="oe_avatar" options="{'size': [200, 200]}" nolabel="1"/>
|
|
</group>
|
|
<group string="Explanation">
|
|
<field name="explanation" nolabel="1" placeholder="Why is this the correct answer? (Shown after answering)"/>
|
|
</group>
|
|
</group>
|
|
</page>
|
|
</notebook>
|
|
</sheet>
|
|
</form>
|
|
</field>
|
|
</record>
|
|
|
|
<!-- Quiz Attempt Views -->
|
|
<record id="view_genius_quiz_attempt_tree" model="ir.ui.view">
|
|
<field name="name">genius.quiz.attempt.tree</field>
|
|
<field name="model">genius.quiz.attempt</field>
|
|
<field name="arch" type="xml">
|
|
<tree string="Quiz Attempts" create="false" edit="false">
|
|
<field name="user_id"/>
|
|
<field name="quiz_id"/>
|
|
<field name="started_at"/>
|
|
<field name="score"/>
|
|
<field name="is_passed"/>
|
|
<field name="state"/>
|
|
</tree>
|
|
</field>
|
|
</record>
|
|
|
|
<record id="view_genius_quiz_attempt_form" model="ir.ui.view">
|
|
<field name="name">genius.quiz.attempt.form</field>
|
|
<field name="model">genius.quiz.attempt</field>
|
|
<field name="arch" type="xml">
|
|
<form string="Quiz Attempt">
|
|
<header>
|
|
<button name="action_submit" string="Submit Quiz" type="object" state="in_progress" class="oe_highlight" attrs="{'invisible': [('state', '!=', 'in_progress')]}"/>
|
|
<field name="state" widget="statusbar" statusbar_visible="in_progress,submitted"/>
|
|
</header>
|
|
<sheet>
|
|
<group>
|
|
<group>
|
|
<field name="quiz_id" readonly="1"/>
|
|
<field name="user_id" readonly="1"/>
|
|
</group>
|
|
<group>
|
|
<field name="started_at" readonly="1"/>
|
|
<field name="score" attrs="{'invisible': [('state', '!=', 'submitted')]}"/>
|
|
<field name="is_passed" attrs="{'invisible': [('state', '!=', 'submitted')]}"/>
|
|
<field name="show_correct_answers" invisible="1"/>
|
|
</group>
|
|
</group>
|
|
|
|
<!-- Success Messages -->
|
|
<div class="alert alert-success" role="alert" attrs="{'invisible': ['|', '|', ('state', '!=', 'submitted'), ('is_passed', '=', False), ('success_message', '!=', False)]}">
|
|
<strong>Congratulations!</strong> You passed this quiz.
|
|
</div>
|
|
<field name="success_message" widget="html" class="alert alert-success" attrs="{'invisible': ['|', '|', ('state', '!=', 'submitted'), ('is_passed', '=', False), ('success_message', '=', False)]}"/>
|
|
|
|
<!-- Failure Messages -->
|
|
<div class="alert alert-danger" role="alert" attrs="{'invisible': ['|', '|', ('state', '!=', 'submitted'), ('is_passed', '=', True), ('fail_message', '!=', False)]}">
|
|
<strong>Try Again!</strong> Only <field name="points_earned"/> points earned.
|
|
</div>
|
|
<field name="fail_message" widget="html" class="alert alert-danger" attrs="{'invisible': ['|', '|', ('state', '!=', 'submitted'), ('is_passed', '=', True), ('fail_message', '=', False)]}"/>
|
|
|
|
<notebook>
|
|
<page string="Questions">
|
|
<field name="response_ids">
|
|
<tree editable="bottom" create="0" delete="0">
|
|
<field name="question_id" readonly="1"/>
|
|
<field name="text_answer" attrs="{'readonly': [('parent.state', '=', 'submitted')]}"/>
|
|
<!-- Assume we need a way to select answers. Standard Odoo tree generic is hard for M2M selection.
|
|
Ideally this should be a form view or web client.
|
|
For now, just showing structure. -->
|
|
<!-- Domain ensures we only see answers for THIS question -->
|
|
<field name="selected_answer_ids" widget="many2many_tags"
|
|
domain="[('question_id', '=', question_id)]"
|
|
attrs="{'readonly': [('parent.state', '=', 'submitted')]}"/>
|
|
<field name="is_correct" readonly="1" attrs="{'invisible': [('parent.show_correct_answers', '=', False)]}"/>
|
|
<field name="explanation" widget="html" optional="hide" readonly="1" attrs="{'invisible': [('parent.show_correct_answers', '=', False)]}"/>
|
|
</tree>
|
|
</field>
|
|
</page>
|
|
</notebook>
|
|
</sheet>
|
|
</form>
|
|
</field>
|
|
</record>
|
|
|
|
<record id="action_genius_quiz_attempt" model="ir.actions.act_window">
|
|
<field name="name">My Attempts</field>
|
|
<field name="res_model">genius.quiz.attempt</field>
|
|
<field name="view_mode">tree,form</field>
|
|
<field name="domain">[('user_id', '=', uid)]</field>
|
|
</record>
|
|
|
|
<menuitem id="menu_genius_quiz_attempt"
|
|
name="My Quizzes"
|
|
parent="menu_training"
|
|
action="action_genius_quiz_attempt"
|
|
sequence="20"
|
|
groups="tour_genius.group_genius_user"/>
|
|
|
|
</odoo>
|