from fastapi import APIRouter,Depends, Query from fastapi.responses import JSONResponse from fastapi import HTTPException from pydantic import BaseModel from db_setup import get_session from inbox.enums import Candidate_application_Status from sqlalchemy.ext.asyncio import AsyncSession from inbox.views import Email from users.permissions import PermissionTag, require_permission from dotenv import load_dotenv load_dotenv() router = APIRouter() class AssignJobPostBody(BaseModel): job_post_id: str | None = None class ProcessingStateBody(BaseModel): processing_state: str class DuplicateBody(BaseModel): is_duplicate: bool class TriageOverrideBody(BaseModel): is_application: bool class EmailSendBody(BaseModel): to: str subject: str body: str content_type: str | None = "html" inbox_id: int | None = None class EmailReplyBody(BaseModel): record_id: str body: str @router.get("/email/fetch") async def fetch_email( top:int=Query(100), skip:int=Query(0,ge=0), test_on: bool = Query(True), token: str | None = Query(None), session: AsyncSession = Depends(get_session), ): try: service=Email(session=session,token=token) if not service.token: raise HTTPException(status_code=401,detail="Unauthorized") data=await service.service_email(top,skip) value=data.get("value") items_lst=[] # Classify the whole page first, bounded-parallel, then replay it in upstream # order: the inserts stay serial on the one request session and pending_match_ids # keeps the sequence it has today. decisions=await service.triage_round([item.get("id") for item in value]) for item in value: message_id=item.get("id") service_per_email=await service.get_email_by_id(message_id,test_on,decision=decisions.get(str(message_id))) items_lst.append({"message_id":message_id,"email_contents":service_per_email}) if service.pending_match_ids: await service.enqueue_matching(list(service.pending_match_ids),force=False) skipped=len(service.skipped_message_ids) triage={"ingested":len(items_lst)-skipped,"skipped":skipped,"errors":len(service.triage_errors)} account_setup=[] if test_on: return JSONResponse(content={"data":items_lst,"triage":triage,"status_code":200}) if service.pending_confirmation_emails: account_setup=await service.send_account_setup(list(service.pending_confirmation_emails)) return JSONResponse(content={"data":items_lst,"account_setup":account_setup,"triage":triage,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.get("/inbox/fetch") async def fetch_inbox( record_id: str | None = Query(None), search: str | None = Query(None), top: int | None = Query(None), skip: int = Query(0, ge=0), session: AsyncSession = Depends(get_session), ): try: service=Email(session=session) if record_id: item=await service.get_inbox_message_by_id(record_id) return JSONResponse(content={"data":item,"total":1,"status_code":200}) items=await service.get_inbox_messages(top,skip,search) total=await service.count_inbox_messages(search) return JSONResponse(content={"data":items,"total":total,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.post("/inbox/{record_id}/match") async def rematch_inbox( record_id: str, current_user: dict = Depends(require_permission(PermissionTag.INBOX_EDIT)), session: AsyncSession = Depends(get_session), ): try: service=Email(session=session) queued_id=await service.queue_rematch(record_id) task_ids=await service.enqueue_matching([queued_id], force=True) return JSONResponse(content={"data":{"queued":True,"task_ids":task_ids},"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.patch("/inbox/{record_id}/assign-job-post") async def assign_job_post( record_id: str, payload: AssignJobPostBody, current_user: dict = Depends(require_permission(PermissionTag.INBOX_EDIT)), session: AsyncSession = Depends(get_session), ): try: service=Email(session=session) data=await service.assign_job_post(record_id,payload.job_post_id) return JSONResponse(content={"data":data,"total":1,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.post("/inbox/{record_id}/read") async def mark_inbox_read( record_id: str, current_user: dict = Depends(require_permission(PermissionTag.INBOX_EDIT)), session: AsyncSession = Depends(get_session), ): try: service=Email(session=session) data=await service.mark_read(record_id) return JSONResponse(content={"data":data,"total":1,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.get("/inbox/{record_id}/read-status") async def get_inbox_read_status( record_id: str, token: str | None = Query(None), current_user: dict = Depends(require_permission(PermissionTag.INBOX_EDIT)), session: AsyncSession = Depends(get_session), ): try: service=Email(session=session,token=token) data=await service.refresh_read_status(record_id) return JSONResponse(content={"data":data,"total":1,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.get("/inbox/all-applications") async def get_all_applications( record_id: str | None = Query(None), application_status: Candidate_application_Status = Query(default=Candidate_application_Status.CLOSED), isread: bool = Query(default=True), assigned: bool | None = Query(default=None), search: str | None = Query(None), top: int | None = Query(None), skip: int = Query(0, ge=0), current_user: dict = Depends(require_permission(PermissionTag.INBOX_VIEW)), session: AsyncSession = Depends(get_session), ): try: service=Email(session=session) if application_status in (Candidate_application_Status.PROCESS, Candidate_application_Status.REJECTED, Candidate_application_Status.SCREENING, Candidate_application_Status.ASSESSMENT, Candidate_application_Status.INTERVIEW, Candidate_application_Status.OFFER, Candidate_application_Status.HIRED): items=await service.get_all_applications(top, skip, search, application_status=application_status, assigned=assigned) total=await service.count_inbox_messages(search, application_status=application_status, assigned=assigned) return JSONResponse(content={"data":items,"total":total,"status_code":200}) if isread==False: items=await service.get_all_applications(top, skip, search, isread=False, assigned=assigned) total=await service.count_inbox_messages(search, isread=False, assigned=assigned) return JSONResponse(content={"data":items,"total":total,"status_code":200}) if record_id: item=await service.get_application_by_id(record_id) return JSONResponse(content={"data":item,"total":1,"status_code":200}) items=await service.get_all_applications(top,skip,search,assigned=assigned) total=await service.count_inbox_messages(search,assigned=assigned) return JSONResponse(content={"data":items,"total":total,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.get("/inbox/counts") async def get_inbox_counts( current_user: dict = Depends(require_permission(PermissionTag.INBOX_VIEW)), session: AsyncSession = Depends(get_session), ): try: service=Email(session=session) data=await service.get_counts() return JSONResponse(content={"data":data,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.get("/inbox/triage") async def fetch_triage( search: str | None = Query(None), is_application: bool | None = Query(None), status: str | None = Query(None), top: int = Query(100), skip: int = Query(0, ge=0), current_user: dict = Depends(require_permission(PermissionTag.INBOX_VIEW)), session: AsyncSession = Depends(get_session), ): try: service=Email(session=session) data=await service.get_triage_messages(top,skip,search,is_application,status) total=await service.count_triage(search,is_application,status) return JSONResponse(content={"data":data,"total":total,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.patch("/inbox/triage/{record_id}/override") async def override_triage( record_id: str, payload: TriageOverrideBody, current_user: dict = Depends(require_permission(PermissionTag.INBOX_EDIT)), session: AsyncSession = Depends(get_session), ): try: service=Email(session=session) data=await service.override_triage(record_id,payload.is_application,current_user) return JSONResponse(content={"data":data,"total":1,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.patch("/inbox/{record_id}/processing-state") async def set_processing_state( record_id: str, payload: ProcessingStateBody, current_user: dict = Depends(require_permission(PermissionTag.INBOX_EDIT)), session: AsyncSession = Depends(get_session), ): try: service=Email(session=session) data=await service.set_processing_state(record_id,payload.processing_state) return JSONResponse(content={"data":data,"total":1,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.patch("/inbox/{record_id}/duplicate") async def set_duplicate( record_id: str, payload: DuplicateBody, current_user: dict = Depends(require_permission(PermissionTag.INBOX_EDIT)), session: AsyncSession = Depends(get_session), ): try: service=Email(session=session) data=await service.set_duplicate(record_id,payload.is_duplicate) return JSONResponse(content={"data":data,"total":1,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.post("/email/send") async def send_email( payload: EmailSendBody, current_user: dict = Depends(require_permission(PermissionTag.INBOX_EDIT)), session: AsyncSession = Depends(get_session), ): try: service=Email(session=session) data=await service.send_email(payload.model_dump(exclude_unset=True),current_user) return JSONResponse(content={"data":data,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e)) @router.post("/email/reply") async def reply_email( payload: EmailReplyBody, current_user: dict = Depends(require_permission(PermissionTag.INBOX_EDIT)), session: AsyncSession = Depends(get_session), ): try: service=Email(session=session) data=await service.reply_email(payload.model_dump(exclude_unset=True),current_user) return JSONResponse(content={"data":data,"status_code":200}) except HTTPException: raise except Exception as e: raise HTTPException(status_code=500,detail=str(e))