465 lines
16 KiB
Python
465 lines
16 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 FileRead,CandidateView
|
|
from job.interviews.views import Interview
|
|
from job.notes.views import Note
|
|
from job.activity.views import ActivityLog
|
|
from job.feedback.views import FeedbackView
|
|
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 dotenv import load_dotenv
|
|
from datetime import datetime, time, timezone
|
|
from pydantic import BaseModel
|
|
from uuid import UUID
|
|
|
|
load_dotenv()
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class CandidateUpdate(BaseModel):
|
|
favorite: bool | None = None
|
|
rating: float | None = None
|
|
|
|
|
|
class InterviewCreate(BaseModel):
|
|
inbox_id: int
|
|
interview_date: datetime | None = None
|
|
interview_time: datetime | None = None
|
|
interview_type: str | None = None
|
|
interview_status: str | None = None
|
|
|
|
|
|
class InterviewUpdate(BaseModel):
|
|
interview_date: datetime | None = None
|
|
interview_time: datetime | None = None
|
|
interview_type: str | None = None
|
|
interview_status: str | None = None
|
|
inbox_id: int | None = None
|
|
|
|
|
|
class NoteCreate(BaseModel):
|
|
user_id: UUID
|
|
note: str
|
|
|
|
|
|
class NoteUpdate(BaseModel):
|
|
note: str | None = None
|
|
|
|
|
|
class ActivityCreate(BaseModel):
|
|
message_id: UUID | None = None
|
|
user_id: UUID | None = None
|
|
inbox_id: int | None = None
|
|
activity_type: str | None = None
|
|
activity_status: str | None = None
|
|
description: str | None = None
|
|
activity_date: datetime | None = None
|
|
activity_time: datetime | None = None
|
|
|
|
|
|
class FeedbackCreate(BaseModel):
|
|
inbox_id: int | None = None
|
|
review: str | None = None
|
|
financial_status: str | None = None
|
|
score: float | None = None
|
|
note: str | None = None
|
|
reviewed_by: UUID | None = None
|
|
|
|
|
|
class FeedbackUpdate(BaseModel):
|
|
review: str | None = None
|
|
financial_status: str | None = None
|
|
score: float | None = None
|
|
note: str | None = None
|
|
inbox_id: int | None = None
|
|
reviewed_by: UUID | None = None
|
|
|
|
|
|
|
|
@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/create/candidate")
|
|
async def create_manual_candidate(
|
|
file: UploadFile = File(...),
|
|
candidate_email: str | None = Form(None),
|
|
candidate_name: str | None = Form(None),
|
|
candidate_phone: str | None = Form(None),
|
|
job_post_id: str | None = Form(None),
|
|
current_company: str | None = Form(None),
|
|
platform: str | None = Form(None),
|
|
experience: str | None = Form(None),
|
|
status: str | None = Form(None),
|
|
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)")
|
|
reader=FileRead(session=session,filename=file.filename,file=file_content)
|
|
parsed=await reader.injest_manual_upload()
|
|
service=CandidateView(session=session)
|
|
data=await service.create_candidate(
|
|
candidate_email=candidate_email,
|
|
candidate_name=candidate_name,
|
|
candidate_phone=candidate_phone,
|
|
job_post_id=job_post_id,
|
|
current_company=current_company,
|
|
platform=platform,
|
|
experience=experience,
|
|
status=status,
|
|
full_text=parsed.get("text") or "",
|
|
current_user=current_user.get("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("/candidate/cv_upload")
|
|
async def cv_upload(
|
|
file: UploadFile = File(...),
|
|
candidate_email: str | None = Form(None),
|
|
candidate_name: str | None = Form(None),
|
|
candidate_phone: str | None = Form(None),
|
|
job_post_id: str | None = Form(None),
|
|
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.ingest_upload(
|
|
candidate_email=candidate_email,candidate_name=candidate_name,
|
|
)
|
|
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))
|
|
|
|
|
|
@router.get("/job/fetch")
|
|
async def fetch_job_posts(
|
|
search: str | None = Query(None),
|
|
top: int | None = Query(None),
|
|
skip: int = Query(0, ge=0),
|
|
ids: str | None = Query(None),
|
|
active_only: bool = Query(True),
|
|
current_user: dict = Depends(require_permission(PermissionTag.JOB_BOARD_VIEW)),
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
try:
|
|
service=JobPost(session=session)
|
|
id_list=[x.strip() for x in (ids or "").split(",") if x.strip()] or None
|
|
data,total=await service.fetch_job_posts(
|
|
search=search,
|
|
top=top,
|
|
skip=skip,
|
|
ids=id_list,
|
|
active_only=active_only,
|
|
)
|
|
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.get("/candidate/fetch")
|
|
async def fetch_candidate(
|
|
user_id:str=Query(None),
|
|
limit:int=Query(10),
|
|
offset:int=Query(0),
|
|
search:str=Query(None),
|
|
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_VIEW)),
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
try:
|
|
service=CandidateView(session=session)
|
|
data=await service.get_candidate(user_id=user_id,limit=limit,offset=offset,search=search)
|
|
|
|
|
|
total=await service.count_candidates(user_id=user_id,search=search) if isinstance(data,list) else 1
|
|
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("/candidate/update")
|
|
async def update_candidate(
|
|
user_id:str=Query(...),
|
|
payload:CandidateUpdate=...,
|
|
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_EDIT)),
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
try:
|
|
service=CandidateView(session=session)
|
|
data=await service.update_candidate(user_id,payload.model_dump(exclude_unset=True))
|
|
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("/interview/fetch")
|
|
async def fetch_interview(
|
|
interview_id:str=Query(None),
|
|
inbox_id:int=Query(None),
|
|
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_VIEW)),
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
try:
|
|
service=Interview(session=session)
|
|
data=await service.get_interview(interview_id=interview_id,inbox_id=inbox_id)
|
|
total=1 if isinstance(data,dict) else len(data)
|
|
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.post("/interview/create")
|
|
async def create_interview(
|
|
payload:InterviewCreate,
|
|
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_CREATE)),
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
try:
|
|
service=Interview(session=session)
|
|
data=await service.create_interview(payload.model_dump(exclude_unset=True))
|
|
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("/interview/update")
|
|
async def update_interview(
|
|
interview_id:str=Query(...),
|
|
payload:InterviewUpdate=...,
|
|
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_EDIT)),
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
try:
|
|
service=Interview(session=session)
|
|
data=await service.update_interview(interview_id,payload.model_dump(exclude_unset=True))
|
|
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("/notes/fetch")
|
|
async def fetch_notes(
|
|
note_id:str=Query(None),
|
|
user_id:str=Query(None),
|
|
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_VIEW)),
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
try:
|
|
service=Note(session=session)
|
|
data=await service.get_note(note_id=note_id,user_id=user_id)
|
|
total=1 if isinstance(data,dict) else len(data)
|
|
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.post("/notes/create")
|
|
async def create_note(
|
|
payload:NoteCreate,
|
|
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_CREATE)),
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
try:
|
|
service=Note(session=session)
|
|
data=await service.create_note(payload.model_dump(exclude_unset=True),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("/notes/update")
|
|
async def update_note(
|
|
note_id:str=Query(...),
|
|
payload:NoteUpdate=...,
|
|
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_EDIT)),
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
try:
|
|
service=Note(session=session)
|
|
data=await service.update_note(note_id,payload.model_dump(exclude_unset=True))
|
|
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("/activity/fetch")
|
|
async def fetch_activity(
|
|
activity_id:str=Query(None),
|
|
inbox_id:int=Query(None),
|
|
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_VIEW)),
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
try:
|
|
service=ActivityLog(session=session)
|
|
data=await service.get_activity(activity_id=activity_id,inbox_id=inbox_id)
|
|
total=1 if isinstance(data,dict) else len(data)
|
|
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.post("/activity/create")
|
|
async def create_activity(
|
|
payload:ActivityCreate,
|
|
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_CREATE)),
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
try:
|
|
service=ActivityLog(session=session)
|
|
data=await service.create_activity(payload.model_dump(exclude_unset=True))
|
|
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("/feedback/fetch")
|
|
async def fetch_feedback(
|
|
feedback_id:str=Query(None),
|
|
inbox_id:int=Query(None),
|
|
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_VIEW)),
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
try:
|
|
service=FeedbackView(session=session)
|
|
data=await service.get_feedback(feedback_id=feedback_id,inbox_id=inbox_id)
|
|
total=1 if isinstance(data,dict) else len(data)
|
|
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.post("/feedback/create")
|
|
async def create_feedback(
|
|
payload:FeedbackCreate,
|
|
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_CREATE)),
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
try:
|
|
service=FeedbackView(session=session)
|
|
data=await service.create_feedback(payload.model_dump(exclude_unset=True),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("/feedback/update")
|
|
async def update_feedback(
|
|
feedback_id:str=Query(...),
|
|
payload:FeedbackUpdate=...,
|
|
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_EDIT)),
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
try:
|
|
service=FeedbackView(session=session)
|
|
data=await service.update_feedback(feedback_id,payload.model_dump(exclude_unset=True))
|
|
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))
|