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
108 lines
3.1 KiB
Python
108 lines
3.1 KiB
Python
import logging
|
|
|
|
import httpx
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
ELAI_BASE_URL = "https://apis.elai.io/api/v1/videos"
|
|
|
|
|
|
class EncoachElaiService:
|
|
|
|
def __init__(self, env):
|
|
self.env = env
|
|
self.token = (
|
|
env["ir.config_parameter"]
|
|
.sudo()
|
|
.get_param("encoach.elai_token", "")
|
|
)
|
|
|
|
def _headers(self):
|
|
return {
|
|
"Authorization": f"Bearer {self.token}",
|
|
"Content-Type": "application/json",
|
|
}
|
|
|
|
def create_video(self, text, avatar_code):
|
|
"""Create and render an ELAI video.
|
|
|
|
Returns the video_id for status polling, or None on failure.
|
|
"""
|
|
avatar = (
|
|
self.env["encoach.elai.avatar"]
|
|
.sudo()
|
|
.search([("avatar_code", "=", avatar_code)], limit=1)
|
|
)
|
|
if not avatar:
|
|
_logger.error("Avatar not found: %s", avatar_code)
|
|
return None
|
|
|
|
payload = {
|
|
"name": f"EnCoach Speaking - {avatar.name}",
|
|
"slides": [
|
|
{
|
|
"avatar": {
|
|
"code": avatar.avatar_code,
|
|
"canvas": avatar.canvas or "default",
|
|
"voiceId": avatar.voice_id or "",
|
|
"voiceProvider": avatar.voice_provider or "",
|
|
},
|
|
"speech": text,
|
|
},
|
|
],
|
|
}
|
|
|
|
try:
|
|
resp = httpx.post(
|
|
ELAI_BASE_URL,
|
|
headers=self._headers(),
|
|
json=payload,
|
|
timeout=30,
|
|
)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
video_id = data.get("_id") or data.get("id")
|
|
|
|
self._render_video(video_id)
|
|
return video_id
|
|
except Exception:
|
|
_logger.exception("ELAI video creation failed")
|
|
return None
|
|
|
|
def _render_video(self, video_id):
|
|
try:
|
|
httpx.post(
|
|
f"{ELAI_BASE_URL}/render/{video_id}",
|
|
headers=self._headers(),
|
|
timeout=30,
|
|
)
|
|
except Exception:
|
|
_logger.exception("ELAI render request failed for %s", video_id)
|
|
|
|
def poll_status(self, video_id):
|
|
"""Poll ELAI for video rendering status.
|
|
|
|
Returns dict with 'status' and optionally 'url' keys.
|
|
"""
|
|
try:
|
|
resp = httpx.get(
|
|
f"{ELAI_BASE_URL}/{video_id}",
|
|
headers=self._headers(),
|
|
timeout=30,
|
|
)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
status = data.get("status", "unknown")
|
|
result = {"status": status}
|
|
if status == "ready":
|
|
result["url"] = data.get("url", "")
|
|
return result
|
|
except Exception:
|
|
_logger.exception("ELAI status poll failed for %s", video_id)
|
|
return {"status": "error"}
|
|
|
|
def get_avatars(self):
|
|
"""List all configured ELAI avatars."""
|
|
avatars = self.env["encoach.elai.avatar"].sudo().search([])
|
|
return [a.to_encoach_dict() for a in avatars]
|