committed
parent
e6d2cffddb
commit
381d7a7bdd
|
|
@ -198,6 +198,11 @@ class FormDuplicateBody(BaseModel):
|
||||||
is_duplicate: bool
|
is_duplicate: bool
|
||||||
|
|
||||||
|
|
||||||
|
class FormDataRatingBody(BaseModel):
|
||||||
|
favorite: bool | None = None
|
||||||
|
rating: float | None = None
|
||||||
|
|
||||||
|
|
||||||
@router.get("/sheet/form-data/sheets")
|
@router.get("/sheet/form-data/sheets")
|
||||||
async def fetch_form_data_sheets(
|
async def fetch_form_data_sheets(
|
||||||
current_user: dict = Depends(_FORM_DATA_READ),
|
current_user: dict = Depends(_FORM_DATA_READ),
|
||||||
|
|
@ -364,6 +369,23 @@ async def set_form_duplicate(
|
||||||
raise HTTPException(status_code=500,detail=str(e))
|
raise HTTPException(status_code=500,detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@router.patch("/sheet/form-data/{record_id}/rating")
|
||||||
|
async def set_form_rating(
|
||||||
|
record_id: str,
|
||||||
|
payload: FormDataRatingBody,
|
||||||
|
current_user: dict = Depends(_FORM_DATA_EDIT),
|
||||||
|
session: AsyncSession = Depends(get_session),
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
service=SheetFormData(session=session)
|
||||||
|
data=await service.set_rating(record_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.delete("/sheet/form-data/{tab}/delete")
|
@router.delete("/sheet/form-data/{tab}/delete")
|
||||||
async def delete_form_data_sheet(
|
async def delete_form_data_sheet(
|
||||||
tab: str,
|
tab: str,
|
||||||
|
|
|
||||||
|
|
@ -225,6 +225,8 @@ class FormDataColumn(str, Enum):
|
||||||
# Same vocabulary as inbox_messages — Import / Shortlist / Reject / Duplicate.
|
# Same vocabulary as inbox_messages — Import / Shortlist / Reject / Duplicate.
|
||||||
PROCESSING_STATE = "processing_state"
|
PROCESSING_STATE = "processing_state"
|
||||||
IS_DUPLICATE = "is_duplicate"
|
IS_DUPLICATE = "is_duplicate"
|
||||||
|
FAVORITE = "favorite"
|
||||||
|
RATING = "rating"
|
||||||
REAPPLIED = "reapplied"
|
REAPPLIED = "reapplied"
|
||||||
RAW_RECORD = "raw_record"
|
RAW_RECORD = "raw_record"
|
||||||
IMPORTED_AT = "imported_at"
|
IMPORTED_AT = "imported_at"
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,10 @@ class FormData(SQLModel, table=True):
|
||||||
processing_state: str = Field(default="unread", sa_column_kwargs={"server_default": "unread"})
|
processing_state: str = Field(default="unread", sa_column_kwargs={"server_default": "unread"})
|
||||||
is_duplicate: bool = Field(default=False, sa_column_kwargs={"server_default": "false"})
|
is_duplicate: bool = Field(default=False, sa_column_kwargs={"server_default": "false"})
|
||||||
|
|
||||||
|
# Recruiter-set, same shape as inbox.favorite/inbox.rating.
|
||||||
|
favorite: bool | None = Field(default=False)
|
||||||
|
rating: float | None = Field(default=0.0)
|
||||||
|
|
||||||
raw_record: dict | None = Field(default=None, sa_column=Column(JSONB))
|
raw_record: dict | None = Field(default=None, sa_column=Column(JSONB))
|
||||||
imported_at: datetime = Field(default_factory=_now, sa_type=DateTime(timezone=True))
|
imported_at: datetime = Field(default_factory=_now, sa_type=DateTime(timezone=True))
|
||||||
created_at: datetime = Field(default_factory=_now, sa_type=DateTime(timezone=True))
|
created_at: datetime = Field(default_factory=_now, sa_type=DateTime(timezone=True))
|
||||||
|
|
@ -439,6 +443,21 @@ class FormData(SQLModel, table=True):
|
||||||
await session.refresh(row)
|
await session.refresh(row)
|
||||||
return row
|
return row
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def set_rating_favorite(cls, session: AsyncSession, record_id, favorite=None, rating=None):
|
||||||
|
row = await cls.get_form_data_by_id(session, record_id)
|
||||||
|
if not row:
|
||||||
|
return None
|
||||||
|
if favorite is not None:
|
||||||
|
row.favorite = bool(favorite)
|
||||||
|
if rating is not None:
|
||||||
|
row.rating = float(rating)
|
||||||
|
row.updated_at = _now()
|
||||||
|
session.add(row)
|
||||||
|
await session.commit()
|
||||||
|
await session.refresh(row)
|
||||||
|
return row
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def link_manual_upload(cls, session: AsyncSession, record_id, manual_upload_candidate_id, *, commit: bool = True):
|
async def link_manual_upload(cls, session: AsyncSession, record_id, manual_upload_candidate_id, *, commit: bool = True):
|
||||||
row = await cls.get_form_data_by_id(session, record_id)
|
row = await cls.get_form_data_by_id(session, record_id)
|
||||||
|
|
|
||||||
|
|
@ -603,6 +603,15 @@ class SheetFormData(Sheet):
|
||||||
raise HTTPException(status_code=404,detail="Form data not found")
|
raise HTTPException(status_code=404,detail="Form data not found")
|
||||||
return await self.get_form_data_by_id(record_id)
|
return await self.get_form_data_by_id(record_id)
|
||||||
|
|
||||||
|
async def set_rating(self,record_id,payload):
|
||||||
|
fields={k:v for k,v in (payload or {}).items() if k in ("favorite","rating") and v is not None}
|
||||||
|
if not fields:
|
||||||
|
raise HTTPException(status_code=400,detail="favorite or rating is required")
|
||||||
|
updated=await FormData.set_rating_favorite(self._require_session(),record_id,**fields)
|
||||||
|
if not updated:
|
||||||
|
raise HTTPException(status_code=404,detail="Form data not found")
|
||||||
|
return await self.get_form_data_by_id(record_id)
|
||||||
|
|
||||||
async def get_counts(self,sheet=None,search=None,has_linkedin=None,has_resume=None,city=None,source=None,assigned=None,job_post_ids=None):
|
async def get_counts(self,sheet=None,search=None,has_linkedin=None,has_resume=None,city=None,source=None,assigned=None,job_post_ids=None):
|
||||||
return await FormData.count_processing(
|
return await FormData.count_processing(
|
||||||
self._require_session(),sheet=sheet,search=search,
|
self._require_session(),sheet=sheet,search=search,
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,11 @@ class CandidateUpdate(BaseModel):
|
||||||
rating: float | None = None
|
rating: float | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class ManualUploadRatingUpdate(BaseModel):
|
||||||
|
favorite: bool | None = None
|
||||||
|
rating: float | None = None
|
||||||
|
|
||||||
|
|
||||||
class InterviewCreate(BaseModel):
|
class InterviewCreate(BaseModel):
|
||||||
inbox_id: int
|
inbox_id: int
|
||||||
interview_date: datetime | None = None
|
interview_date: datetime | None = None
|
||||||
|
|
@ -1115,6 +1120,23 @@ async def update_candidate(
|
||||||
raise HTTPException(status_code=500,detail=str(e))
|
raise HTTPException(status_code=500,detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@router.patch("/candidate/manual-upload/rating")
|
||||||
|
async def update_manual_upload_rating(
|
||||||
|
id:str=Query(...),
|
||||||
|
payload:ManualUploadRatingUpdate=...,
|
||||||
|
current_user: dict = Depends(require_permission(PermissionTag.CANDIDATES_EDIT)),
|
||||||
|
session: AsyncSession = Depends(get_session),
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
service=CandidateView(session=session)
|
||||||
|
data=await service.update_manual_upload_rating(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("/candidate/history/fetch")
|
@router.get("/candidate/history/fetch")
|
||||||
async def fetch_candidate_history(
|
async def fetch_candidate_history(
|
||||||
user_id:str=Query(...),
|
user_id:str=Query(...),
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,10 @@ class Manual_UPLOAD_CANDIDATE(SQLModel, table=True):
|
||||||
referral_by: str = Field(default="", sa_column_kwargs={"server_default": ""})
|
referral_by: str = Field(default="", sa_column_kwargs={"server_default": ""})
|
||||||
file_name: str = Field(default="", sa_column_kwargs={"server_default": ""})
|
file_name: str = Field(default="", sa_column_kwargs={"server_default": ""})
|
||||||
file_path: str = Field(default="", sa_column_kwargs={"server_default": ""})
|
file_path: str = Field(default="", sa_column_kwargs={"server_default": ""})
|
||||||
|
# Recruiter-set, same shape as inbox.favorite/inbox.rating — this row's own
|
||||||
|
# copy, not derived from Inbox (manual_upload_candidate has no inbox link).
|
||||||
|
favorite: Optional[bool] = Field(default=False)
|
||||||
|
rating: Optional[float] = Field(default=0.0)
|
||||||
created_at: datetime = Field(default_factory=_now, sa_type=DateTime(timezone=True))
|
created_at: datetime = Field(default_factory=_now, sa_type=DateTime(timezone=True))
|
||||||
updated_at: datetime = Field(default_factory=_now, sa_type=DateTime(timezone=True))
|
updated_at: datetime = Field(default_factory=_now, sa_type=DateTime(timezone=True))
|
||||||
|
|
||||||
|
|
@ -395,6 +399,21 @@ class Manual_UPLOAD_CANDIDATE(SQLModel, table=True):
|
||||||
result = await session.execute(select(cls).where(cls.id == uid))
|
result = await session.execute(select(cls).where(cls.id == uid))
|
||||||
return result.scalars().first()
|
return result.scalars().first()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def set_rating_favorite(cls, session: AsyncSession, record_id, favorite=None, rating=None):
|
||||||
|
row=await cls.get_by_id(session,record_id)
|
||||||
|
if not row:
|
||||||
|
return None
|
||||||
|
if favorite is not None:
|
||||||
|
row.favorite=favorite
|
||||||
|
if rating is not None:
|
||||||
|
row.rating=rating
|
||||||
|
row.updated_at=_now()
|
||||||
|
session.add(row)
|
||||||
|
await session.commit()
|
||||||
|
await session.refresh(row)
|
||||||
|
return row
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def set_full_text(cls, session: AsyncSession, record_id, text, *, only_if_empty=True):
|
async def set_full_text(cls, session: AsyncSession, record_id, text, *, only_if_empty=True):
|
||||||
"""Keep extracted CV text on the bank/manual row (Inbox uses resume_text)."""
|
"""Keep extracted CV text on the bank/manual row (Inbox uses resume_text)."""
|
||||||
|
|
|
||||||
|
|
@ -202,6 +202,8 @@ def serialize_manual_upload_candidate(row) -> Dict[str,Any]:
|
||||||
"referral_by":row.referral_by,
|
"referral_by":row.referral_by,
|
||||||
"file_name":row.file_name,
|
"file_name":row.file_name,
|
||||||
"file_path":row.file_path,
|
"file_path":row.file_path,
|
||||||
|
"favorite":row.favorite,
|
||||||
|
"rating":row.rating,
|
||||||
"created_at":row.created_at.isoformat() if row.created_at else None,
|
"created_at":row.created_at.isoformat() if row.created_at else None,
|
||||||
"updated_at":row.updated_at.isoformat() if row.updated_at else None,
|
"updated_at":row.updated_at.isoformat() if row.updated_at else None,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1605,6 +1605,20 @@ class CandidateView:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500,detail=str(e))
|
raise HTTPException(status_code=500,detail=str(e))
|
||||||
|
|
||||||
|
async def update_manual_upload_rating(self,record_id,payload):
|
||||||
|
try:
|
||||||
|
fields={k:v for k,v in (payload or {}).items() if k in ("favorite","rating") and v is not None}
|
||||||
|
if not fields:
|
||||||
|
raise HTTPException(status_code=400,detail="favorite or rating is required")
|
||||||
|
row=await Manual_UPLOAD_CANDIDATE.set_rating_favorite(self.session,record_id,**fields)
|
||||||
|
if not row:
|
||||||
|
raise HTTPException(status_code=404,detail="Candidate not found")
|
||||||
|
return serialize_manual_upload_candidate(row)
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500,detail=str(e))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _attach_job_post(data,payload,*,as_assigned=False):
|
def _attach_job_post(data,payload,*,as_assigned=False):
|
||||||
"""Merge one serialized job post onto a candidate payload.
|
"""Merge one serialized job post onto a candidate payload.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue