"""REST endpoints for AI coaching — matches frontend coaching.service.ts.""" import json import logging from odoo import http from odoo.http import request, Response _logger = logging.getLogger(__name__) def _json_response(data, status=200): return Response(json.dumps(data, default=str), status=status, content_type="application/json") def _get_json(): try: return json.loads(request.httprequest.data or "{}") except Exception: return {} class CoachController(http.Controller): """Handles /api/coach/* endpoints consumed by frontend AI coaching components.""" def _get_coach(self): from odoo.addons.encoach_ai.services.coach_service import CoachService return CoachService(request.env) # ── POST /api/coach/chat — AiAssistantDrawer.tsx ── @http.route("/api/coach/chat", type="http", auth="user", methods=["POST"], csrf=False) def coach_chat(self, **kw): body = _get_json() try: coach = self._get_coach() result = coach.chat( body.get("message", ""), history=body.get("history", []), student_context=body.get("context"), ) return _json_response(result) except Exception as e: _logger.exception("Coach chat failed") return _json_response({"reply": f"I'm having trouble right now. Error: {e}"}) # ── GET /api/coach/tip — AiTipBanner.tsx ── @http.route("/api/coach/tip", type="http", auth="user", methods=["GET"], csrf=False) def coach_tip(self, **kw): context = request.params.get("context", "general") try: coach = self._get_coach() return _json_response(coach.get_tip(context)) except Exception as e: return _json_response({"tip": "Keep practising every day — consistency beats intensity!", "category": "general"}) # ── POST /api/coach/explain — AiGradeExplainer.tsx ── @http.route("/api/coach/explain", type="http", auth="user", methods=["POST"], csrf=False) def coach_explain(self, **kw): body = _get_json() try: coach = self._get_coach() result = coach.explain( body.get("score_data", {}), body.get("student_context", ""), ) return _json_response(result) except Exception as e: return _json_response({"explanation": f"Could not generate explanation: {e}"}) # ── POST /api/coach/suggest — AiStudyCoach.tsx ── @http.route("/api/coach/suggest", type="http", auth="user", methods=["POST"], csrf=False) def coach_suggest(self, **kw): body = _get_json() try: coach = self._get_coach() return _json_response(coach.suggest(body)) except Exception as e: return _json_response({ "suggestion": "Focus on your weakest skill for 30 minutes daily.", "focus_areas": ["writing", "speaking"], "daily_plan": [], "motivation": "Every expert was once a beginner!", }) # ── POST /api/coach/writing-help — AiWritingHelper.tsx ── @http.route("/api/coach/writing-help", type="http", auth="user", methods=["POST"], csrf=False) def coach_writing_help(self, **kw): body = _get_json() try: coach = self._get_coach() result = coach.writing_help( body.get("task", ""), body.get("draft", ""), body.get("help_type", "improve"), ) return _json_response(result) except Exception as e: return _json_response({"improved_text": "", "changes": [], "tips": [str(e)]}) # ── POST /api/coach/hint — (unused component, wired for completeness) ── @http.route("/api/coach/hint", type="http", auth="user", methods=["POST"], csrf=False) def coach_hint(self, **kw): body = _get_json() try: coach = self._get_coach() return _json_response(coach.get_hint(body)) except Exception as e: return _json_response({"hint": "Think about the key words in the question.", "strategy": "keyword_focus"})