feat: initial backend codebase — EnCoach v3

Complete Odoo 19 backend with 25 custom addons:
- encoach_core: user/entity/role management
- encoach_api: REST API + JWT auth
- encoach_ai: OpenAI integration, AI settings, generation
- encoach_ai_course: AI-powered English & IELTS course generation
- encoach_exam_template/session: exam creation, structures, sessions
- encoach_scoring: AI auto-grading + manual approval
- encoach_vector: pgvector RAG integration
- encoach_adaptive: adaptive learning engine
- encoach_placement: placement testing
- encoach_taxonomy/resources: content taxonomy & resource management
- Plus 14 more modules for courses, branding, portal, etc.

Includes docs: user guide, generation report, developer workflow.

Made-with: Cursor
This commit is contained in:
Yamen Ahmad
2026-04-11 15:44:20 +04:00
commit 982d4bca30
371 changed files with 35211 additions and 0 deletions

View File

@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ============================================================ -->
<!-- Audio File Views -->
<!-- ============================================================ -->
<record id="view_audio_file_form" model="ir.ui.view">
<field name="name">encoach.audio.file.form</field>
<field name="model">encoach.audio.file</field>
<field name="arch" type="xml">
<form string="Audio File">
<sheet>
<div class="oe_title">
<h1>
<field name="topic" placeholder="Audio Topic"/>
</h1>
</div>
<group>
<group>
<field name="exam_type"/>
<field name="part"/>
<field name="context_type"/>
<field name="difficulty"/>
</group>
<group>
<field name="audio_url" widget="url"/>
<field name="ai_generated"/>
<field name="approved"/>
<field name="ielts_certified"/>
</group>
</group>
<notebook>
<page string="Transcript" name="transcript">
<field name="transcript" widget="text" placeholder="Audio transcript..."/>
</page>
<page string="Validation" name="validation">
<group>
<field name="generation_brief"/>
<field name="validation_errors"/>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="view_audio_file_list" model="ir.ui.view">
<field name="name">encoach.audio.file.list</field>
<field name="model">encoach.audio.file</field>
<field name="arch" type="xml">
<list string="Audio Files" decoration-success="approved == True">
<field name="topic"/>
<field name="exam_type"/>
<field name="part"/>
<field name="context_type"/>
<field name="difficulty"/>
<field name="approved"/>
</list>
</field>
</record>
<record id="view_audio_file_search" model="ir.ui.view">
<field name="name">encoach.audio.file.search</field>
<field name="model">encoach.audio.file</field>
<field name="arch" type="xml">
<search string="Search Audio Files">
<field name="topic"/>
<separator/>
<filter string="Academic" name="filter_academic" domain="[('exam_type', '=', 'academic')]"/>
<filter string="General Training" name="filter_general" domain="[('exam_type', '=', 'general_training')]"/>
<filter string="General English" name="filter_general_eng" domain="[('exam_type', '=', 'general_english')]"/>
<separator/>
<filter string="Part 1" name="filter_part1" domain="[('part', '=', 1)]"/>
<filter string="Part 2" name="filter_part2" domain="[('part', '=', 2)]"/>
<filter string="Part 3" name="filter_part3" domain="[('part', '=', 3)]"/>
<filter string="Part 4" name="filter_part4" domain="[('part', '=', 4)]"/>
<separator/>
<filter string="Approved" name="filter_approved" domain="[('approved', '=', True)]"/>
<filter string="Exam Type" name="group_exam_type" context="{'group_by': 'exam_type'}"/>
<filter string="Part" name="group_part" context="{'group_by': 'part'}"/>
<filter string="Difficulty" name="group_difficulty" context="{'group_by': 'difficulty'}"/>
</search>
</field>
</record>
<record id="action_audio_file" model="ir.actions.act_window">
<field name="name">Audio Files</field>
<field name="res_model">encoach.audio.file</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_audio_file_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Upload your first audio file
</p>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="action_content_pool" model="ir.actions.client">
<field name="name">Content Pool</field>
<field name="tag">encoach_content_pool</field>
</record>
<menuitem id="menu_content_pool_browser"
name="Content Pool Browser"
parent="encoach_core.menu_encoach_content"
action="action_content_pool"
sequence="5"/>
</odoo>

View File

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ============================================================ -->
<!-- Exam Assignment Views -->
<!-- ============================================================ -->
<record id="view_exam_assignment_form" model="ir.ui.view">
<field name="name">encoach.exam.assignment.form</field>
<field name="model">encoach.exam.assignment</field>
<field name="arch" type="xml">
<form string="Exam Assignment">
<header>
<field name="status" widget="statusbar" statusbar_visible="assigned,started,completed,expired"/>
</header>
<sheet>
<group>
<group>
<field name="exam_id"/>
<field name="student_id"/>
<field name="batch_id"/>
</group>
<group>
<field name="access_start"/>
<field name="access_end"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="view_exam_assignment_list" model="ir.ui.view">
<field name="name">encoach.exam.assignment.list</field>
<field name="model">encoach.exam.assignment</field>
<field name="arch" type="xml">
<list string="Exam Assignments" decoration-info="status == 'assigned'" decoration-warning="status == 'started'" decoration-success="status == 'completed'" decoration-muted="status == 'expired'">
<field name="exam_id"/>
<field name="student_id"/>
<field name="batch_id"/>
<field name="access_start"/>
<field name="access_end"/>
<field name="status"/>
</list>
</field>
</record>
<record id="view_exam_assignment_search" model="ir.ui.view">
<field name="name">encoach.exam.assignment.search</field>
<field name="model">encoach.exam.assignment</field>
<field name="arch" type="xml">
<search string="Search Exam Assignments">
<field name="exam_id"/>
<field name="student_id"/>
<field name="batch_id"/>
<separator/>
<filter string="Assigned" name="filter_assigned" domain="[('status', '=', 'assigned')]"/>
<filter string="Started" name="filter_started" domain="[('status', '=', 'started')]"/>
<filter string="Completed" name="filter_completed" domain="[('status', '=', 'completed')]"/>
<filter string="Expired" name="filter_expired" domain="[('status', '=', 'expired')]"/>
<filter string="Status" name="group_status" context="{'group_by': 'status'}"/>
<filter string="Exam" name="group_exam" context="{'group_by': 'exam_id'}"/>
<filter string="Batch" name="group_batch" context="{'group_by': 'batch_id'}"/>
</search>
</field>
</record>
<record id="action_exam_assignment" model="ir.actions.act_window">
<field name="name">Exam Assignments</field>
<field name="res_model">encoach.exam.assignment</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_exam_assignment_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Assign your first exam to students
</p>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ============================================================ -->
<!-- Custom Exam Views -->
<!-- ============================================================ -->
<record id="view_exam_custom_form" model="ir.ui.view">
<field name="name">encoach.exam.custom.form</field>
<field name="model">encoach.exam.custom</field>
<field name="arch" type="xml">
<form string="Custom Exam">
<header>
<field name="status" widget="statusbar" statusbar_visible="draft,published,archived"/>
</header>
<sheet>
<div class="oe_title">
<h1>
<field name="title" placeholder="Exam Title"/>
</h1>
</div>
<group>
<group>
<field name="template_id"/>
<field name="subject_id"/>
<field name="entity_id"/>
<field name="teacher_id"/>
</group>
<group>
<field name="total_time_min"/>
<field name="pass_threshold"/>
<field name="results_release_mode"/>
<field name="randomize_questions"/>
</group>
</group>
<group>
<field name="description" placeholder="Exam description..."/>
</group>
<notebook>
<page string="Sections" name="sections">
<field name="section_ids">
<list editable="bottom">
<field name="sequence" widget="handle"/>
<field name="title"/>
<field name="skill"/>
<field name="question_count"/>
<field name="time_limit_min" string="Time (min)"/>
<field name="scoring_method"/>
</list>
</field>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="view_exam_custom_list" model="ir.ui.view">
<field name="name">encoach.exam.custom.list</field>
<field name="model">encoach.exam.custom</field>
<field name="arch" type="xml">
<list string="Custom Exams" decoration-info="status == 'draft'" decoration-success="status == 'published'">
<field name="title"/>
<field name="subject_id"/>
<field name="entity_id"/>
<field name="teacher_id"/>
<field name="total_time_min" string="Time (min)"/>
<field name="status"/>
</list>
</field>
</record>
<record id="view_exam_custom_search" model="ir.ui.view">
<field name="name">encoach.exam.custom.search</field>
<field name="model">encoach.exam.custom</field>
<field name="arch" type="xml">
<search string="Search Custom Exams">
<field name="title"/>
<field name="subject_id"/>
<field name="entity_id"/>
<field name="teacher_id"/>
<separator/>
<filter string="Draft" name="filter_draft" domain="[('status', '=', 'draft')]"/>
<filter string="Published" name="filter_published" domain="[('status', '=', 'published')]"/>
<filter string="Archived" name="filter_archived" domain="[('status', '=', 'archived')]"/>
<filter string="Status" name="group_status" context="{'group_by': 'status'}"/>
<filter string="Subject" name="group_subject" context="{'group_by': 'subject_id'}"/>
<filter string="Entity" name="group_entity" context="{'group_by': 'entity_id'}"/>
</search>
</field>
</record>
<record id="action_exam_custom" model="ir.actions.act_window">
<field name="name">Custom Exams</field>
<field name="res_model">encoach.exam.custom</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_exam_custom_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create your first custom exam
</p>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ============================================================ -->
<!-- Exam Template Module Menu Items -->
<!-- ============================================================ -->
<menuitem id="menu_exam_templates"
name="Templates"
parent="encoach_core.menu_encoach_exams"
action="action_exam_template"
sequence="1"/>
<menuitem id="menu_exam_question_bank"
name="Question Bank"
parent="encoach_core.menu_encoach_exams"
action="action_question"
sequence="2"/>
<menuitem id="menu_exam_passages"
name="Passages"
parent="encoach_core.menu_encoach_exams"
action="action_passage"
sequence="3"/>
<menuitem id="menu_exam_audio_files"
name="Audio Files"
parent="encoach_core.menu_encoach_exams"
action="action_audio_file"
sequence="4"/>
<menuitem id="menu_exam_writing_prompts"
name="Writing Prompts"
parent="encoach_core.menu_encoach_exams"
action="action_writing_prompt"
sequence="5"/>
<menuitem id="menu_exam_speaking_cards"
name="Speaking Cards"
parent="encoach_core.menu_encoach_exams"
action="action_speaking_card"
sequence="6"/>
<menuitem id="menu_exam_rubrics"
name="Rubrics"
parent="encoach_core.menu_encoach_exams"
action="action_rubric"
sequence="7"/>
<menuitem id="menu_exam_custom_exams"
name="Custom Exams"
parent="encoach_core.menu_encoach_exams"
action="action_exam_custom"
sequence="8"/>
<menuitem id="menu_exam_assignments"
name="Exam Assignments"
parent="encoach_core.menu_encoach_exams"
action="action_exam_assignment"
sequence="9"/>
</odoo>

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ============================================================ -->
<!-- Exam Template Views -->
<!-- ============================================================ -->
<record id="view_exam_template_form" model="ir.ui.view">
<field name="name">encoach.exam.template.form</field>
<field name="model">encoach.exam.template</field>
<field name="arch" type="xml">
<form string="Exam Template">
<sheet>
<div class="oe_button_box" name="button_box"/>
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
<div class="oe_title">
<h1>
<field name="name" placeholder="Template Name"/>
</h1>
</div>
<group>
<group>
<field name="code"/>
<field name="type"/>
<field name="subject_id"/>
<field name="entity_id"/>
<field name="teacher_id"/>
</group>
<group>
<field name="total_time_min"/>
<field name="pass_threshold"/>
<field name="results_release_mode"/>
<field name="randomize_questions"/>
<field name="editable"/>
<field name="active" invisible="1"/>
</group>
</group>
<group>
<field name="description" placeholder="Describe this exam template..."/>
</group>
<notebook>
<page string="Structure" name="structure">
<field name="structure" widget="text"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="view_exam_template_list" model="ir.ui.view">
<field name="name">encoach.exam.template.list</field>
<field name="model">encoach.exam.template</field>
<field name="arch" type="xml">
<list string="Exam Templates">
<field name="name"/>
<field name="code"/>
<field name="type"/>
<field name="subject_id"/>
<field name="entity_id"/>
<field name="total_time_min" string="Time (min)"/>
<field name="active"/>
</list>
</field>
</record>
<record id="view_exam_template_search" model="ir.ui.view">
<field name="name">encoach.exam.template.search</field>
<field name="model">encoach.exam.template</field>
<field name="arch" type="xml">
<search string="Search Exam Templates">
<field name="name"/>
<field name="code"/>
<field name="subject_id"/>
<field name="entity_id"/>
<separator/>
<filter string="International" name="filter_international" domain="[('type', '=', 'international')]"/>
<filter string="Custom" name="filter_custom" domain="[('type', '=', 'custom')]"/>
<separator/>
<filter string="Active" name="filter_active" domain="[('active', '=', True)]"/>
<filter string="Archived" name="filter_archived" domain="[('active', '=', False)]"/>
<filter string="Type" name="group_type" context="{'group_by': 'type'}"/>
<filter string="Subject" name="group_subject" context="{'group_by': 'subject_id'}"/>
<filter string="Entity" name="group_entity" context="{'group_by': 'entity_id'}"/>
</search>
</field>
</record>
<record id="action_exam_template" model="ir.actions.act_window">
<field name="name">Exam Templates</field>
<field name="res_model">encoach.exam.template</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_exam_template_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create your first exam template
</p>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="action_exam_validation" model="ir.actions.client">
<field name="name">Exam Validation</field>
<field name="tag">encoach_exam_validation</field>
</record>
<menuitem id="menu_exam_validation"
name="Exam Validation"
parent="encoach_core.menu_encoach_content"
action="action_exam_validation"
sequence="10"/>
</odoo>

View File

@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ============================================================ -->
<!-- Passage Views -->
<!-- ============================================================ -->
<record id="view_passage_form" model="ir.ui.view">
<field name="name">encoach.passage.form</field>
<field name="model">encoach.passage</field>
<field name="arch" type="xml">
<form string="Reading Passage">
<sheet>
<div class="oe_title">
<h1>
<field name="topic_category" placeholder="Topic Category"/>
</h1>
</div>
<group>
<group>
<field name="exam_type"/>
<field name="section_num"/>
<field name="difficulty"/>
<field name="status"/>
</group>
<group>
<field name="cefr_level"/>
<field name="word_count"/>
<field name="ai_generated"/>
<field name="approved"/>
<field name="ielts_certified"/>
</group>
</group>
<notebook>
<page string="Body Text" name="body_text">
<field name="body_text" widget="text" placeholder="Paste or write the passage text..."/>
</page>
<page string="Validation" name="validation">
<group>
<field name="generation_brief"/>
<field name="validation_errors"/>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="view_passage_list" model="ir.ui.view">
<field name="name">encoach.passage.list</field>
<field name="model">encoach.passage</field>
<field name="arch" type="xml">
<list string="Passages" decoration-success="approved == True" decoration-muted="status == 'retired'">
<field name="topic_category"/>
<field name="exam_type"/>
<field name="difficulty"/>
<field name="status"/>
<field name="word_count"/>
<field name="approved"/>
</list>
</field>
</record>
<record id="view_passage_search" model="ir.ui.view">
<field name="name">encoach.passage.search</field>
<field name="model">encoach.passage</field>
<field name="arch" type="xml">
<search string="Search Passages">
<field name="topic_category"/>
<separator/>
<filter string="Academic" name="filter_academic" domain="[('exam_type', '=', 'academic')]"/>
<filter string="General Training" name="filter_general" domain="[('exam_type', '=', 'general_training')]"/>
<filter string="General English" name="filter_general_eng" domain="[('exam_type', '=', 'general_english')]"/>
<separator/>
<filter string="Draft" name="filter_draft" domain="[('status', '=', 'draft')]"/>
<filter string="Active" name="filter_active" domain="[('status', '=', 'active')]"/>
<filter string="Flagged" name="filter_flagged" domain="[('status', '=', 'flagged')]"/>
<separator/>
<filter string="Easy" name="filter_easy" domain="[('difficulty', '=', 'easy')]"/>
<filter string="Medium" name="filter_medium" domain="[('difficulty', '=', 'medium')]"/>
<filter string="Hard" name="filter_hard" domain="[('difficulty', '=', 'hard')]"/>
<separator/>
<filter string="Approved" name="filter_approved" domain="[('approved', '=', True)]"/>
<filter string="Not Approved" name="filter_not_approved" domain="[('approved', '=', False)]"/>
<filter string="Exam Type" name="group_exam_type" context="{'group_by': 'exam_type'}"/>
<filter string="Status" name="group_status" context="{'group_by': 'status'}"/>
<filter string="Difficulty" name="group_difficulty" context="{'group_by': 'difficulty'}"/>
</search>
</field>
</record>
<record id="action_passage" model="ir.actions.act_window">
<field name="name">Passages</field>
<field name="res_model">encoach.passage</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_passage_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create your first reading passage
</p>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ============================================================ -->
<!-- Question Views -->
<!-- ============================================================ -->
<record id="view_question_form" model="ir.ui.view">
<field name="name">encoach.question.form</field>
<field name="model">encoach.question</field>
<field name="arch" type="xml">
<form string="Question">
<sheet>
<group>
<group string="Classification">
<field name="skill"/>
<field name="question_type"/>
<field name="difficulty"/>
<field name="status"/>
</group>
<group string="Taxonomy">
<field name="subject_id"/>
<field name="topic_id"/>
<field name="marks"/>
<field name="source_type"/>
<field name="source_id" invisible="not source_type"/>
</group>
</group>
<group string="Question Content">
<field name="stem" widget="text" placeholder="Enter the question stem..."/>
</group>
<group>
<group>
<field name="options" widget="text" placeholder='JSON array: ["A", "B", "C", "D"]'/>
</group>
<group>
<field name="correct_answer" widget="text"/>
</group>
</group>
<notebook>
<page string="IRT Parameters" name="irt">
<group>
<group>
<field name="irt_a" string="Discrimination (a)"/>
<field name="irt_b" string="Difficulty (b)"/>
<field name="irt_c" string="Guessing (c)"/>
</group>
</group>
</page>
<page string="Flags" name="flags">
<group>
<group>
<field name="ai_generated"/>
<field name="ielts_certified"/>
<field name="format_validated"/>
</group>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="view_question_list" model="ir.ui.view">
<field name="name">encoach.question.list</field>
<field name="model">encoach.question</field>
<field name="arch" type="xml">
<list string="Questions" decoration-muted="status == 'retired'" decoration-danger="status == 'flagged'">
<field name="stem" string="Question" widget="char"/>
<field name="skill"/>
<field name="question_type"/>
<field name="difficulty"/>
<field name="status"/>
<field name="marks"/>
</list>
</field>
</record>
<record id="view_question_search" model="ir.ui.view">
<field name="name">encoach.question.search</field>
<field name="model">encoach.question</field>
<field name="arch" type="xml">
<search string="Search Questions">
<field name="stem"/>
<field name="subject_id"/>
<field name="topic_id"/>
<separator/>
<filter string="Listening" name="filter_listening" domain="[('skill', '=', 'listening')]"/>
<filter string="Reading" name="filter_reading" domain="[('skill', '=', 'reading')]"/>
<filter string="Writing" name="filter_writing" domain="[('skill', '=', 'writing')]"/>
<filter string="Speaking" name="filter_speaking" domain="[('skill', '=', 'speaking')]"/>
<filter string="Grammar" name="filter_grammar" domain="[('skill', '=', 'grammar')]"/>
<filter string="Vocabulary" name="filter_vocabulary" domain="[('skill', '=', 'vocabulary')]"/>
<separator/>
<filter string="MCQ" name="filter_mcq" domain="[('question_type', '=', 'mcq')]"/>
<filter string="Gap Fill" name="filter_gap_fill" domain="[('question_type', '=', 'gap_fill')]"/>
<filter string="Matching" name="filter_matching" domain="[('question_type', '=', 'matching')]"/>
<separator/>
<filter string="Easy" name="filter_easy" domain="[('difficulty', '=', 'easy')]"/>
<filter string="Medium" name="filter_medium" domain="[('difficulty', '=', 'medium')]"/>
<filter string="Hard" name="filter_hard" domain="[('difficulty', '=', 'hard')]"/>
<separator/>
<filter string="Draft" name="filter_draft" domain="[('status', '=', 'draft')]"/>
<filter string="Active" name="filter_active" domain="[('status', '=', 'active')]"/>
<filter string="Flagged" name="filter_flagged" domain="[('status', '=', 'flagged')]"/>
<filter string="Skill" name="group_skill" context="{'group_by': 'skill'}"/>
<filter string="Type" name="group_type" context="{'group_by': 'question_type'}"/>
<filter string="Difficulty" name="group_difficulty" context="{'group_by': 'difficulty'}"/>
<filter string="Status" name="group_status" context="{'group_by': 'status'}"/>
<filter string="Subject" name="group_subject" context="{'group_by': 'subject_id'}"/>
</search>
</field>
</record>
<record id="action_question" model="ir.actions.act_window">
<field name="name">Question Bank</field>
<field name="res_model">encoach.question</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_question_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Add your first question to the bank
</p>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ============================================================ -->
<!-- Rubric Views -->
<!-- ============================================================ -->
<record id="view_rubric_form" model="ir.ui.view">
<field name="name">encoach.rubric.form</field>
<field name="model">encoach.rubric</field>
<field name="arch" type="xml">
<form string="Rubric">
<sheet>
<div class="oe_title">
<h1>
<field name="name" placeholder="Rubric Name"/>
</h1>
</div>
<group>
<group>
<field name="skill"/>
<field name="exam_type"/>
</group>
</group>
<notebook>
<page string="Criteria" name="criteria">
<field name="criteria" widget="text" placeholder="Define rubric criteria (JSON or structured text)..."/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="view_rubric_list" model="ir.ui.view">
<field name="name">encoach.rubric.list</field>
<field name="model">encoach.rubric</field>
<field name="arch" type="xml">
<list string="Rubrics">
<field name="name"/>
<field name="skill"/>
<field name="exam_type"/>
</list>
</field>
</record>
<record id="view_rubric_search" model="ir.ui.view">
<field name="name">encoach.rubric.search</field>
<field name="model">encoach.rubric</field>
<field name="arch" type="xml">
<search string="Search Rubrics">
<field name="name"/>
<separator/>
<filter string="Writing" name="filter_writing" domain="[('skill', '=', 'writing')]"/>
<filter string="Speaking" name="filter_speaking" domain="[('skill', '=', 'speaking')]"/>
<separator/>
<filter string="Academic" name="filter_academic" domain="[('exam_type', '=', 'academic')]"/>
<filter string="General Training" name="filter_general" domain="[('exam_type', '=', 'general_training')]"/>
<filter string="Skill" name="group_skill" context="{'group_by': 'skill'}"/>
<filter string="Exam Type" name="group_exam_type" context="{'group_by': 'exam_type'}"/>
</search>
</field>
</record>
<record id="action_rubric" model="ir.actions.act_window">
<field name="name">Rubrics</field>
<field name="res_model">encoach.rubric</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_rubric_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create your first scoring rubric
</p>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ============================================================ -->
<!-- Speaking Card Views -->
<!-- ============================================================ -->
<record id="view_speaking_card_form" model="ir.ui.view">
<field name="name">encoach.speaking.card.form</field>
<field name="model">encoach.speaking.card</field>
<field name="arch" type="xml">
<form string="Speaking Card">
<sheet>
<div class="oe_title">
<h1>
<field name="topic" placeholder="Speaking Topic"/>
</h1>
</div>
<group>
<group>
<field name="part"/>
<field name="difficulty"/>
<field name="linked_card_id"/>
<field name="rubric_id"/>
</group>
<group>
<field name="ai_generated"/>
<field name="approved"/>
<field name="ielts_certified"/>
</group>
</group>
<notebook>
<page string="Questions" name="questions">
<field name="questions" widget="text" placeholder="Questions for the candidate..."/>
</page>
<page string="Bullet Points" name="bullet_points">
<field name="bullet_points" widget="text"/>
</page>
<page string="Model Response" name="model_response">
<field name="model_response" widget="text"/>
</page>
<page string="Validation" name="validation">
<group>
<field name="generation_brief"/>
<field name="validation_errors"/>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="view_speaking_card_list" model="ir.ui.view">
<field name="name">encoach.speaking.card.list</field>
<field name="model">encoach.speaking.card</field>
<field name="arch" type="xml">
<list string="Speaking Cards" decoration-success="approved == True">
<field name="topic"/>
<field name="part"/>
<field name="difficulty"/>
<field name="linked_card_id"/>
<field name="approved"/>
<field name="ielts_certified"/>
</list>
</field>
</record>
<record id="view_speaking_card_search" model="ir.ui.view">
<field name="name">encoach.speaking.card.search</field>
<field name="model">encoach.speaking.card</field>
<field name="arch" type="xml">
<search string="Search Speaking Cards">
<field name="topic"/>
<separator/>
<filter string="Part 1" name="filter_part1" domain="[('part', '=', 1)]"/>
<filter string="Part 2" name="filter_part2" domain="[('part', '=', 2)]"/>
<filter string="Part 3" name="filter_part3" domain="[('part', '=', 3)]"/>
<separator/>
<filter string="Easy" name="filter_easy" domain="[('difficulty', '=', 'easy')]"/>
<filter string="Medium" name="filter_medium" domain="[('difficulty', '=', 'medium')]"/>
<filter string="Hard" name="filter_hard" domain="[('difficulty', '=', 'hard')]"/>
<separator/>
<filter string="Approved" name="filter_approved" domain="[('approved', '=', True)]"/>
<filter string="Part" name="group_part" context="{'group_by': 'part'}"/>
<filter string="Difficulty" name="group_difficulty" context="{'group_by': 'difficulty'}"/>
</search>
</field>
</record>
<record id="action_speaking_card" model="ir.actions.act_window">
<field name="name">Speaking Cards</field>
<field name="res_model">encoach.speaking.card</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_speaking_card_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create your first speaking cue card
</p>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ============================================================ -->
<!-- Writing Prompt Views -->
<!-- ============================================================ -->
<record id="view_writing_prompt_form" model="ir.ui.view">
<field name="name">encoach.writing.prompt.form</field>
<field name="model">encoach.writing.prompt</field>
<field name="arch" type="xml">
<form string="Writing Prompt">
<sheet>
<group>
<group>
<field name="exam_type"/>
<field name="task"/>
<field name="writing_type"/>
<field name="min_words"/>
</group>
<group>
<field name="rubric_id"/>
<field name="visual_url" widget="url"/>
<field name="ai_generated"/>
<field name="approved"/>
<field name="ielts_certified"/>
</group>
</group>
<notebook>
<page string="Prompt" name="prompt">
<field name="prompt_text" widget="text" placeholder="Write the prompt instructions..."/>
</page>
<page string="Model Answer" name="model_answer">
<field name="model_answer" widget="text"/>
</page>
<page string="Validation" name="validation">
<group>
<field name="generation_brief"/>
<field name="validation_errors"/>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="view_writing_prompt_list" model="ir.ui.view">
<field name="name">encoach.writing.prompt.list</field>
<field name="model">encoach.writing.prompt</field>
<field name="arch" type="xml">
<list string="Writing Prompts" decoration-success="approved == True">
<field name="exam_type"/>
<field name="task"/>
<field name="writing_type"/>
<field name="min_words"/>
<field name="rubric_id"/>
<field name="approved"/>
</list>
</field>
</record>
<record id="view_writing_prompt_search" model="ir.ui.view">
<field name="name">encoach.writing.prompt.search</field>
<field name="model">encoach.writing.prompt</field>
<field name="arch" type="xml">
<search string="Search Writing Prompts">
<field name="writing_type"/>
<separator/>
<filter string="Academic" name="filter_academic" domain="[('exam_type', '=', 'academic')]"/>
<filter string="General Training" name="filter_general" domain="[('exam_type', '=', 'general_training')]"/>
<separator/>
<filter string="Task 1" name="filter_task1" domain="[('task', '=', 'task1')]"/>
<filter string="Task 2" name="filter_task2" domain="[('task', '=', 'task2')]"/>
<separator/>
<filter string="Approved" name="filter_approved" domain="[('approved', '=', True)]"/>
<filter string="Exam Type" name="group_exam_type" context="{'group_by': 'exam_type'}"/>
<filter string="Task" name="group_task" context="{'group_by': 'task'}"/>
</search>
</field>
</record>
<record id="action_writing_prompt" model="ir.actions.act_window">
<field name="name">Writing Prompts</field>
<field name="res_model">encoach.writing.prompt</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_writing_prompt_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create your first writing prompt
</p>
</field>
</record>
</odoo>