feat: complete all 12 missing spec features from EnCoach v3.0 audit

- Add student_progress, course_section models and module_resources M2M
- Activate encoach_resources module with security, views, and ielts_certified field
- Add password strength indicator JS on registration page
- Rewrite onboarding wizard step 4 as placement test prompt with skip option
- Dynamic goal list fetched from exam templates in DB with fallback
- B1 default CEFR level assigned when placement test is skipped
- Teacher review dashboard with 2 API endpoints for AI content approval
- IELTS examiner review interface with review_status, examiner_notes fields
- Content source gate: mandatory review for AI, spot-check for certified
- Resource-to-learning-style matching in adaptive engine
- Daily cron job for teacher no-progress alerts via mail.activity
- Entity student placement redirect on dashboard access
- Fix Odoo 19 compat: search view groups, ir.cron fields, OWL xml templates

Made-with: Cursor
This commit is contained in:
Yamen Ahmad
2026-04-08 03:12:00 +04:00
parent e30e52e21e
commit 5ff9f12de7
39 changed files with 1106 additions and 87 deletions

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,16 @@
{
'name': 'EnCoach Resources',
'version': '19.0.1.0',
'category': 'Education',
'summary': 'Learning resources management with module assignment',
'author': 'EnCoach',
'license': 'LGPL-3',
'depends': ['encoach_core', 'encoach_taxonomy'],
'data': [
'security/ir.model.access.csv',
'views/resource_views.xml',
],
'installable': True,
'application': False,
'auto_install': False,
}

View File

@@ -0,0 +1 @@
from . import resource

View File

@@ -37,6 +37,7 @@ class EncoachResource(models.Model):
vocab_band = fields.Char(size=50)
ai_generated = fields.Boolean(default=False)
approved = fields.Boolean(default=False)
ielts_certified = fields.Boolean(default=False)
def to_api_dict(self):
self.ensure_one()

View File

@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_encoach_resource_user,encoach.resource.user,model_encoach_resource,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_encoach_resource_user encoach.resource.user model_encoach_resource base.group_user 1 1 1 1

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="view_encoach_resource_form" model="ir.ui.view">
<field name="name">encoach.resource.form</field>
<field name="model">encoach.resource</field>
<field name="arch" type="xml">
<form string="Resource">
<header>
<field name="review_status" widget="statusbar" statusbar_visible="pending,approved,rejected"/>
</header>
<sheet>
<group>
<group>
<field name="name"/>
<field name="type"/>
<field name="difficulty"/>
<field name="duration_minutes"/>
<field name="cefr_level"/>
</group>
<group>
<field name="subject_id"/>
<field name="topic_ids" widget="many2many_tags"/>
<field name="grammar_topic"/>
<field name="vocab_band"/>
<field name="ai_generated"/>
<field name="approved"/>
</group>
</group>
<group string="Content">
<field name="url"/>
<field name="file" filename="name"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="view_encoach_resource_list" model="ir.ui.view">
<field name="name">encoach.resource.list</field>
<field name="model">encoach.resource</field>
<field name="arch" type="xml">
<list string="Resources">
<field name="name"/>
<field name="type"/>
<field name="cefr_level"/>
<field name="difficulty"/>
<field name="duration_minutes"/>
<field name="review_status" widget="badge" decoration-info="review_status == 'pending'" decoration-success="review_status == 'approved'" decoration-danger="review_status == 'rejected'"/>
<field name="ai_generated"/>
</list>
</field>
</record>
<record id="view_encoach_resource_search" model="ir.ui.view">
<field name="name">encoach.resource.search</field>
<field name="model">encoach.resource</field>
<field name="arch" type="xml">
<search string="Search Resources">
<field name="name"/>
<filter string="AI Generated" name="ai_generated" domain="[('ai_generated', '=', True)]"/>
<filter string="Pending Review" name="pending_review" domain="[('review_status', '=', 'pending')]"/>
<filter string="Approved" name="approved" domain="[('review_status', '=', 'approved')]"/>
<separator/>
<filter string="Type" name="group_type" context="{'group_by': 'type'}"/>
<filter string="CEFR Level" name="group_cefr" context="{'group_by': 'cefr_level'}"/>
<filter string="Review Status" name="group_review" context="{'group_by': 'review_status'}"/>
</search>
</field>
</record>
<record id="action_encoach_resource" model="ir.actions.act_window">
<field name="name">Resources</field>
<field name="res_model">encoach.resource</field>
<field name="view_mode">list,form</field>
</record>
</odoo>