Files
Yamen Ahmad 907a5c0e92 feat(v3): restructure project + add complete frontend
- Restructure: move backend from new_project/ to backend/
- Add full React/TypeScript frontend (37 pages, 17 services, 16 type defs, 11 query hooks)
- Add docs/ with SRS specs, user stories, and workflow documentation
- Update .gitignore for new directory layout

Workflows implemented:
  WF1 User Signup, WF2 Placement Test, WF3 Exam Configuration,
  WF4 General English Exam, WF5 Course Generation,
  WF6 Entity Student Onboarding, AI Course Generation,
  Adaptive Learning Engine UI, White-Label Branding, Score Release

Made-with: Cursor
2026-04-10 17:26:42 +04:00

60 lines
1.9 KiB
Python

import hashlib
import random
import string
from datetime import timedelta
from odoo import fields
class OtpService:
def __init__(self, env):
self.env = env
def _hash_otp(self, otp_code):
return hashlib.sha256(otp_code.encode()).hexdigest()
def generate(self, email):
"""Create a 6-digit OTP, store its SHA-256 hash, return plaintext."""
otp_code = ''.join(random.choices(string.digits, k=6))
otp_hash = self._hash_otp(otp_code)
expires_at = fields.Datetime.now() + timedelta(minutes=15)
self.env['encoach.otp'].sudo().create({
'email': email,
'otp_hash': otp_hash,
'expires_at': expires_at,
})
return otp_code
def verify(self, email, otp_code):
"""Find unexpired, unused record and check SHA-256 hash match."""
otp_hash = self._hash_otp(otp_code)
now = fields.Datetime.now()
record = self.env['encoach.otp'].sudo().search([
('email', '=', email),
('otp_hash', '=', otp_hash),
('used', '=', False),
('expires_at', '>=', now),
], limit=1, order='created_at desc')
if record:
record.write({'used': True})
return True
return False
def can_resend(self, email):
"""Check if resend_count < 3 for the latest OTP."""
record = self.env['encoach.otp'].sudo().search([
('email', '=', email),
], limit=1, order='created_at desc')
return record and record.resend_count < 3
def mark_resend(self, email):
"""Increment resend_count on the latest OTP for this email."""
record = self.env['encoach.otp'].sudo().search([
('email', '=', email),
], limit=1, order='created_at desc')
if record:
record.write({'resend_count': record.resend_count + 1})