HR-ATS-Portal/backend/job/app.py

178 lines
6.2 KiB
Python

from fastapi import APIRouter,Depends,Query
from fastapi.responses import JSONResponse
from fastapi import HTTPException
from db_setup import get_session
from job.candidate.views import CandidateScoring,FileRead
from sqlalchemy.ext.asyncio import AsyncSession
from users.permissions import PermissionTag, require_permission
from job.job_post.views import JobPost,JobPostCreate
import logging
from job.job_post.plugins import PlatformAlias
from fastapi import UploadFile, File, Form
from pydantic import BaseModel
from dotenv import load_dotenv
from datetime import datetime, time, timezone
load_dotenv()
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
router = APIRouter()
@router.get("/jobs/alias")
async def get_job_alias():
try:
alias_lst=[k.name for k in PlatformAlias]
return JSONResponse(content={"data":alias_lst,"status_code":200})
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500,detail=str(e))
@router.post("/candidate/cv_upload")
async def cv_upload(
file: UploadFile = File(...),
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_CREATE)),
session: AsyncSession = Depends(get_session),
):
try:
file_content = await file.read()
logger.info(f"Received file: {file.filename} ({len(file_content)} bytes)")
service=FileRead(session=session,filename=file.filename,file=file_content)
data=await service.read_file()
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("/candidate/inbox-match")
async def candidate_inbox_match(
inbox_message_id: str = Query(...),
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_EDIT)),
session: AsyncSession = Depends(get_session),
):
try:
service=FileRead(session=session)
data=await service.match_inbox_cv(inbox_message_id)
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("/job/post-job")
async def post_job(
payload: JobPostCreate,
current_user: dict = Depends(require_permission(PermissionTag.JOB_BOARD_CREATE)),
session: AsyncSession = Depends(get_session),
):
try:
service=JobPost(session=session)
data=payload.model_dump()
if data['mode']=="customScheduled":
data['due_at']=datetime.combine(
data['scheduler_date'],
data['scheduler_time'] or time(0, 0, 0),
tzinfo=timezone.utc,
).strftime("%Y-%m-%dT%H:%M:%S.000Z")
result=await service.post_job(data,current_user)
return JSONResponse(content={"data":result,"status_code":200})
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500,detail=str(e))
@router.get("/job/buffer/channels")
async def buffer_channels(
current_user: dict = Depends(require_permission(PermissionTag.JOB_BOARD_VIEW)),
session: AsyncSession = Depends(get_session),
):
try:
service=JobPost(session=session)
data=await service.list_channels()
return JSONResponse(content={"data":data,"total":len(data),"status_code":200})
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500,detail=str(e))
class InboxScoreRequest(BaseModel):
job_id: str
message_ids: list[str] # inbox_messages PK uuids, not Graph message ids
@router.post("/candidate/score")
async def score_candidates(
job_id: str = Form(...),
files: list[UploadFile] = File(...),
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_CREATE)),
session: AsyncSession = Depends(get_session),
):
"""Score uploaded CV PDFs against a job post; persists and returns the leaderboard."""
try:
pairs=[(f.filename,await f.read()) for f in files]
service=CandidateScoring(session=session)
data=await service.score_uploads(job_id,pairs,current_user)
return JSONResponse(content={"data":data,"total":len(data),"status_code":200})
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500,detail=str(e))
@router.post("/candidate/score_inbox")
async def score_inbox_candidates(
payload: InboxScoreRequest,
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_CREATE)),
session: AsyncSession = Depends(get_session),
):
"""Score the decoded attachments of inbox messages against a job post."""
try:
service=CandidateScoring(session=session)
data=await service.score_inbox(payload.job_id,payload.message_ids,current_user)
return JSONResponse(content={"data":data,"total":len(data),"status_code":200})
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500,detail=str(e))
@router.get("/candidate/fetch")
async def fetch_candidates(
job_id: str = Query(...),
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_VIEW)),
session: AsyncSession = Depends(get_session),
):
"""Persisted leaderboard for a job: completed by score desc, failures last."""
try:
service=CandidateScoring(session=session)
data=await service.fetch_candidates(job_id)
return JSONResponse(content={"data":data,"total":len(data),"status_code":200})
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500,detail=str(e))
@router.get("/candidate/fetch_by_id")
async def fetch_candidate_by_id(
candidate_id: str = Query(...),
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_VIEW)),
session: AsyncSession = Depends(get_session),
):
try:
service=CandidateScoring(session=session)
data=await service.fetch_candidate_by_id(candidate_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))