from fastapi import HTTPException from sqlalchemy.ext.asyncio import AsyncSession from job.candidate.models import Feedback from job.feedback.models import FeedbackTemplates from job.feedback.serializers import serialize_feedback, serialize_feedback_template from job.history.enums import HistoryEvent from job.history.views import HistoryRecorder class FeedbackView: def __init__(self,session:AsyncSession): self.session=session async def _load(self,record_id): return await Feedback.get_feedback_by_id(self.session,record_id) async def get_feedback(self,feedback_id=None,inbox_id=None): if feedback_id: row=await self._load(feedback_id) if not row: raise HTTPException(status_code=404,detail="Feedback not found") return serialize_feedback(row) if inbox_id is None: raise HTTPException(status_code=400,detail="feedback_id or inbox_id is required") rows=await Feedback.get_feedback_by_inbox(self.session,int(inbox_id)) return [serialize_feedback(r) for r in rows] async def create_feedback(self,payload,current_user): fields={ "review":payload.get("review") or "", "financial_status":payload.get("financial_status") or "", "score":payload.get("score") if payload.get("score") is not None else 0.0, "note":payload.get("note"), "inbox_id":payload.get("inbox_id"), "reviewed_by":payload.get("reviewed_by") or ( current_user.get("id") if isinstance(current_user,dict) else None ), } row=await Feedback.insert_feedback(self.session,fields) desc=row.note or None await HistoryRecorder(self.session).record( HistoryEvent.FEEDBACK_CREATED.value, current_user=current_user,inbox_id=row.inbox_id, entity_type="feedback",entity_id=row.id, to_value=row.review or None,description=desc,commit=True, ) row=await self._load(row.id) return serialize_feedback(row) async def update_feedback(self,feedback_id,payload,current_user=None): allowed=("review","financial_status","score","note","inbox_id","reviewed_by") fields={k:v for k,v in payload.items() if v is not None and k in allowed} if not fields: raise HTTPException(status_code=400,detail="No fields to update") before=await self._load(feedback_id) if not before: raise HTTPException(status_code=404,detail="Feedback not found") old_review=before.review or "" old_score=before.score row=await Feedback.update_feedback(self.session,feedback_id,fields) if not row: raise HTTPException(status_code=404,detail="Feedback not found") desc=row.note if old_score!=row.score: desc=f"score {old_score} → {row.score}" await HistoryRecorder(self.session).record( HistoryEvent.FEEDBACK_UPDATED.value, current_user=current_user,inbox_id=row.inbox_id, entity_type="feedback",entity_id=row.id, from_value=old_review,to_value=row.review or None, description=desc,commit=True, ) row=await self._load(row.id) return serialize_feedback(row) async def get_templates(self): rows,total=await FeedbackTemplates.fetch_templates(self.session) return [serialize_feedback_template(r) for r in rows],total async def create_template(self,payload,current_user): name=(payload.get("name") or "").strip() if not name: raise HTTPException(status_code=422,detail="name is required") criteria=payload.get("criteria") or [] if not isinstance(criteria,list) or not all(isinstance(c,str) for c in criteria): raise HTTPException(status_code=422,detail="criteria must be a list of strings") fields={ "name":name, "department":payload.get("department"), "criteria":[c.strip() for c in criteria if str(c).strip()], "is_active":payload.get("is_active") if payload.get("is_active") is not None else True, "created_by":current_user.get("id") if isinstance(current_user,dict) else None, } row=await FeedbackTemplates.insert_template(self.session,fields) return serialize_feedback_template(row) async def update_template(self,template_id,payload): allowed=("name","department","criteria","is_active") fields={} for key in allowed: if key not in payload: continue value=payload[key] if key=="name": value=(value or "").strip() if not value: raise HTTPException(status_code=422,detail="name cannot be blank") elif key=="criteria": if value is not None and (not isinstance(value,list) or not all(isinstance(c,str) for c in value)): raise HTTPException(status_code=422,detail="criteria must be a list of strings") value=[c.strip() for c in (value or []) if str(c).strip()] fields[key]=value if not fields: raise HTTPException(status_code=400,detail="No fields to update") row=await FeedbackTemplates.update_template(self.session,template_id,fields) if not row: raise HTTPException(status_code=404,detail="Template not found") return serialize_feedback_template(row) async def delete_template(self,template_id): row=await FeedbackTemplates.soft_delete_template(self.session,template_id) if not row: raise HTTPException(status_code=404,detail="Template not found") return {"id":str(row.id),"deleted":True}