EnCoach Odoo 19 custom modules
Full backend implementation with custom Odoo modules: - encoach_api: Core API, user management, JWT auth - encoach_exam: Exam generation (reading, writing, listening, speaking) - encoach_evaluate: AI-powered evaluation (writing, speaking) - encoach_training: Training tips and walkthrough - encoach_storage: File storage management - encoach_payment: Stripe, PayPal, Paymob integration - encoach_mail: Email notifications Made-with: Cursor
This commit is contained in:
2
encoach_exam/models/__init__.py
Normal file
2
encoach_exam/models/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from . import exam
|
||||
from . import approval_workflow
|
||||
58
encoach_exam/models/approval_workflow.py
Normal file
58
encoach_exam/models/approval_workflow.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from odoo import models, fields
|
||||
|
||||
WORKFLOW_STATUS = [
|
||||
("pending", "Pending"),
|
||||
("approved", "Approved"),
|
||||
("rejected", "Rejected"),
|
||||
]
|
||||
|
||||
|
||||
class EncoachApprovalWorkflow(models.Model):
|
||||
_name = "encoach.approval.workflow"
|
||||
_description = "EnCoach Exam Approval Workflow"
|
||||
|
||||
exam_id = fields.Many2one("encoach.exam", ondelete="cascade", index=True)
|
||||
creator_id = fields.Many2one("res.users", default=lambda self: self.env.uid)
|
||||
entity_id = fields.Many2one("encoach.entity")
|
||||
name = fields.Char()
|
||||
modules = fields.Json(help='List of modules, e.g. ["reading","writing"]')
|
||||
steps = fields.Json(
|
||||
help="Array of workflow step objects with stepType, stepNumber, "
|
||||
"completed, rejected, completedBy, assignees, etc.",
|
||||
)
|
||||
status = fields.Selection(WORKFLOW_STATUS, default="pending", required=True)
|
||||
start_date = fields.Datetime(default=fields.Datetime.now)
|
||||
legacy_id = fields.Char(index=True)
|
||||
|
||||
def action_approve(self):
|
||||
for rec in self:
|
||||
rec.status = "approved"
|
||||
if rec.exam_id:
|
||||
rec.exam_id.approved = True
|
||||
|
||||
def action_reject(self):
|
||||
for rec in self:
|
||||
rec.status = "rejected"
|
||||
if rec.exam_id:
|
||||
rec.exam_id.approved = False
|
||||
|
||||
def action_reset(self):
|
||||
for rec in self:
|
||||
rec.status = "pending"
|
||||
|
||||
def to_encoach_dict(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
"id": self.id,
|
||||
"name": self.name or "",
|
||||
"entityId": self.entity_id.id if self.entity_id else None,
|
||||
"requester": self.creator_id.id if self.creator_id else None,
|
||||
"startDate": (
|
||||
int(self.start_date.timestamp() * 1000)
|
||||
if self.start_date else None
|
||||
),
|
||||
"modules": self.modules or [],
|
||||
"examId": self.exam_id.id if self.exam_id else None,
|
||||
"status": self.status,
|
||||
"steps": self.steps or [],
|
||||
}
|
||||
77
encoach_exam/models/exam.py
Normal file
77
encoach_exam/models/exam.py
Normal file
@@ -0,0 +1,77 @@
|
||||
from odoo import models, fields
|
||||
|
||||
MODULE_SELECTION = [
|
||||
("reading", "Reading"),
|
||||
("listening", "Listening"),
|
||||
("writing", "Writing"),
|
||||
("speaking", "Speaking"),
|
||||
("level", "Level"),
|
||||
]
|
||||
|
||||
VARIANT_SELECTION = [
|
||||
("full", "Full"),
|
||||
("partial", "Partial"),
|
||||
]
|
||||
|
||||
ACCESS_SELECTION = [
|
||||
("public", "Public"),
|
||||
("private", "Private"),
|
||||
("entity", "Entity"),
|
||||
]
|
||||
|
||||
|
||||
class EncoachExam(models.Model):
|
||||
_name = "encoach.exam"
|
||||
_description = "EnCoach Exam"
|
||||
|
||||
module = fields.Selection(MODULE_SELECTION, required=True, index=True)
|
||||
min_timer = fields.Integer(string="Minimum Timer (minutes)", default=0)
|
||||
is_diagnostic = fields.Boolean(default=False)
|
||||
variant = fields.Selection(VARIANT_SELECTION)
|
||||
difficulty = fields.Json(help="List of CEFR difficulty levels, e.g. ['A1','B2']")
|
||||
owner_ids = fields.Many2many(
|
||||
"res.users", "encoach_exam_owner_rel", "exam_id", "user_id",
|
||||
string="Owners",
|
||||
)
|
||||
entity_ids = fields.Many2many(
|
||||
"encoach.entity", "encoach_exam_entity_rel", "exam_id", "entity_id",
|
||||
string="Entities",
|
||||
)
|
||||
shuffle = fields.Boolean(default=False)
|
||||
access = fields.Selection(ACCESS_SELECTION, default="public", required=True)
|
||||
label = fields.Char()
|
||||
requires_approval = fields.Boolean(default=False)
|
||||
approved = fields.Boolean(default=False)
|
||||
parts = fields.Json(help="Nested exam structure (parts, exercises, questions)")
|
||||
legacy_id = fields.Char(index=True)
|
||||
|
||||
def action_approve(self):
|
||||
"""Set exam as approved (used by form view Approve button)."""
|
||||
self.write({"approved": True})
|
||||
|
||||
def to_encoach_dict(self):
|
||||
self.ensure_one()
|
||||
difficulty = self.difficulty or []
|
||||
if isinstance(difficulty, str):
|
||||
import json
|
||||
difficulty = json.loads(difficulty)
|
||||
return {
|
||||
"id": self.id,
|
||||
"module": self.module,
|
||||
"minTimer": self.min_timer,
|
||||
"isDiagnostic": self.is_diagnostic,
|
||||
"variant": self.variant,
|
||||
"difficulty": difficulty,
|
||||
"owners": self.owner_ids.ids,
|
||||
"entities": [str(eid) for eid in self.entity_ids.ids],
|
||||
"shuffle": self.shuffle,
|
||||
"access": self.access,
|
||||
"label": self.label or "",
|
||||
"requiresApproval": self.requires_approval,
|
||||
"approved": self.approved,
|
||||
"parts": self.parts or [],
|
||||
"createdBy": self.create_uid.id if self.create_uid else None,
|
||||
"createdAt": (
|
||||
self.create_date.isoformat() if self.create_date else None
|
||||
),
|
||||
}
|
||||
Reference in New Issue
Block a user