Fix model-controller mismatches and routing issues

- ticket: add user_id, priority, category fields; fix _serialize_ticket -> _serialize
- package: fix is_active -> active (Odoo auto-filter), remove invalid entity_id filter
- walkthrough: add sequence field; fix controller to use name/content instead of title/steps
- users: fix encoach_user_type -> encoach_type, encoach_entity_id -> entity_rel_ids.entity_id
- generation: use EncoachGenerationService class instead of nonexistent ORM model,
  fix /api/exam/level/ route conflict (separate GET and POST URLs)

Made-with: Cursor
This commit is contained in:
Talal Sharabi
2026-03-15 01:08:36 +04:00
parent f5b627256f
commit bf3981028b
7 changed files with 62 additions and 48 deletions

View File

@@ -9,6 +9,13 @@ from .base import EncoachMixin
_logger = logging.getLogger(__name__)
def _get_gen_service():
from odoo.addons.encoach_ai_generation.services.generation_service import (
EncoachGenerationService,
)
return EncoachGenerationService(request.env)
class GenerationController(EncoachMixin, http.Controller):
# ------------------------------------------------- reading passage
@@ -28,9 +35,9 @@ class GenerationController(EncoachMixin, http.Controller):
topic = kwargs.get('topic')
word_count = int(kwargs.get('word_count', 500))
GenService = request.env['encoach.ai.generation'].sudo()
result = GenService.generate_reading_passage(
passage=passage,
service = _get_gen_service()
result = service.generate_reading_passage(
passage_num=passage,
topic=topic,
word_count=word_count,
)
@@ -57,10 +64,10 @@ class GenerationController(EncoachMixin, http.Controller):
if not text or not exercises:
return self._error_response('text and exercises are required', 400)
GenService = request.env['encoach.ai.generation'].sudo()
result = GenService.generate_reading_exercises(
service = _get_gen_service()
result = service.generate_reading_exercises(
text=text,
exercises=exercises,
exercises_config=exercises,
difficulty=difficulty,
)
@@ -86,8 +93,8 @@ class GenerationController(EncoachMixin, http.Controller):
topic = kwargs.get('topic')
difficulty = kwargs.get('difficulty')
GenService = request.env['encoach.ai.generation'].sudo()
result = GenService.generate_listening_dialog(
service = _get_gen_service()
result = service.generate_listening_dialog(
section=section,
topic=topic,
difficulty=difficulty,
@@ -115,10 +122,10 @@ class GenerationController(EncoachMixin, http.Controller):
if not text or not exercises:
return self._error_response('text and exercises are required', 400)
GenService = request.env['encoach.ai.generation'].sudo()
result = GenService.generate_listening_exercises(
service = _get_gen_service()
result = service.generate_listening_exercises(
text=text,
exercises=exercises,
exercises_config=exercises,
difficulty=difficulty,
)
@@ -144,8 +151,8 @@ class GenerationController(EncoachMixin, http.Controller):
topic = kwargs.get('topic')
difficulty = kwargs.get('difficulty')
GenService = request.env['encoach.ai.generation'].sudo()
result = GenService.generate_writing_task(
service = _get_gen_service()
result = service.generate_writing_task(
task=task,
topic=topic,
difficulty=difficulty,
@@ -175,9 +182,9 @@ class GenerationController(EncoachMixin, http.Controller):
second_topic = kwargs.get('second_topic')
difficulty = kwargs.get('difficulty')
GenService = request.env['encoach.ai.generation'].sudo()
result = GenService.generate_speaking_task(
task=task,
service = _get_gen_service()
result = service.generate_speaking_task(
part=task,
topic=topic,
first_topic=first_topic,
second_topic=second_topic,
@@ -189,8 +196,8 @@ class GenerationController(EncoachMixin, http.Controller):
_logger.exception("Generate speaking task error")
return self._error_response('Internal server error', 500)
# --------------------------------------------- level exercises
@http.route('/api/exam/level/', type='http', auth='public', methods=['POST', 'OPTIONS'], csrf=False, cors='*')
# --------------------------------------------- level exercises (POST)
@http.route('/api/exam/level/generate', type='http', auth='public', methods=['POST', 'OPTIONS'], csrf=False, cors='*')
def generate_level_exercises(self, **kwargs):
"""Generate level-test exercises using GPT-4o."""
try:
@@ -205,9 +212,9 @@ class GenerationController(EncoachMixin, http.Controller):
if not exercises:
return self._error_response('exercises specification is required', 400)
GenService = request.env['encoach.ai.generation'].sudo()
result = GenService.generate_level_exercises(
exercises=exercises,
service = _get_gen_service()
result = service.generate_level_exercises(
exercises_config=exercises,
difficulty=difficulty,
)
@@ -242,11 +249,9 @@ class GenerationController(EncoachMixin, http.Controller):
image_data = base64.b64encode(image_file.read()).decode('utf-8')
mime_type = image_file.content_type or 'image/png'
GenService = request.env['encoach.ai.generation'].sudo()
result = GenService.generate_writing_with_image(
service = _get_gen_service()
result = service.generate_writing_task(
task=task,
image_b64=image_data,
mime_type=mime_type,
difficulty=difficulty,
)
@@ -255,8 +260,8 @@ class GenerationController(EncoachMixin, http.Controller):
_logger.exception("Generate writing attachment error")
return self._error_response('Internal server error', 500)
# -------------------------------------------- pre-built level exam
@http.route('/api/exam/level/', type='http', auth='public', methods=['GET', 'OPTIONS'], csrf=False, cors='*')
# -------------------------------------------- pre-built level exam (GET)
@http.route('/api/exam/level', type='http', auth='public', methods=['GET', 'OPTIONS'], csrf=False, cors='*')
def get_level_exam(self, **kwargs):
"""Return a pre-built level exam (no AI generation)."""
try:
@@ -296,7 +301,7 @@ class GenerationController(EncoachMixin, http.Controller):
return self._error_response('Internal server error', 500)
# -------------------------------------------- level exam import
@http.route('/api/exam/level/import/', type='http', auth='public', methods=['POST', 'OPTIONS'], csrf=False, cors='*')
@http.route('/api/exam/level/import', type='http', auth='public', methods=['POST', 'OPTIONS'], csrf=False, cors='*')
def import_level_exam(self, **kwargs):
"""Import level exam from uploaded file."""
try:
@@ -309,9 +314,8 @@ class GenerationController(EncoachMixin, http.Controller):
if not exercises_file:
return self._error_response('exercises file is required', 400)
import json as _json
content = exercises_file.read().decode('utf-8')
data = _json.loads(content)
data = json.loads(content)
exam = request.env['encoach.exam'].sudo().create({
'module': 'level',
@@ -325,7 +329,7 @@ class GenerationController(EncoachMixin, http.Controller):
return self._error_response('Internal server error', 500)
# -------------------------------------------- custom level exam
@http.route('/api/exam/level/custom/', type='http', auth='public', methods=['POST', 'OPTIONS'], csrf=False, cors='*')
@http.route('/api/exam/level/custom', type='http', auth='public', methods=['POST', 'OPTIONS'], csrf=False, cors='*')
def generate_custom_level(self, **kwargs):
"""Generate custom level exam from JSON specification."""
try:
@@ -336,9 +340,9 @@ class GenerationController(EncoachMixin, http.Controller):
body = self._get_json_body()
spec = body.get('spec', body)
GenService = request.env['encoach.ai.generation'].sudo()
result = GenService.generate_level_exercises(
exercises=spec.get('exercises', []),
service = _get_gen_service()
result = service.generate_level_exercises(
exercises_config=spec.get('exercises', []),
difficulty=spec.get('difficulty', ['B1', 'B2']),
)
return self._json_response({'exercises': result})