HR-ATS-Portal/backend/g_sheet/enums.py

249 lines
8.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""Sheet header aliases, FormData keys, and date format mappings.
(str, Enum) like inbox/enums.py: members compare to and serialize as plain strings.
Non-string mappings (month pairs) use plain Enum.
"""
from enum import Enum
class AliasEnum(str, Enum):
"""Member-less base so alias enums share one `has` without 25 copies."""
@classmethod
def has(cls, value) -> bool:
return value in cls._value2member_map_
class FormDataField(str, Enum):
"""Canonical FormData column keys for the recruitment screening sheet."""
SERIAL_NO = "serial_no"
ENTRY_YEAR = "entry_year"
ENTRY_MONTH = "entry_month"
ENTRY_DATE = "entry_date"
ENTRY_TIME = "entry_time"
SCREENED_BY = "screened_by"
NAME = "name"
HR_COMMENTS = "hr_comments"
CANDIDATE_NUMBER = "candidate_number"
CANDIDATE_EMAIL = "candidate_email"
PROFILE_LINK = "profile_link"
AREA_OF_EXPERTISE = "area_of_expertise"
REQUISITION_NUMBER = "requisition_number"
POSITION_SUITABLE_FOR = "position_suitable_for"
SOURCE_OF_APPLICATION = "source_of_application"
AGE = "age"
MARITAL_STATUS = "marital_status"
DEGREE = "degree"
UNIVERSITY = "university"
EXPERIENCE = "experience"
EXPERIENCE_DETAILS = "experience_details"
AREA_OF_RESIDENCE = "area_of_residence"
COMMUNICATION_SKILLS = "communication_skills"
PREFERRED_TIMINGS = "preferred_timings"
HO_AVAILABILITY = "ho_availability"
CURRENT_COMPANY = "current_company"
REASON_FOR_LEAVING = "reason_for_leaving"
NOTICE_PERIOD = "notice_period"
CURRENT_SALARY = "current_salary"
EXPECTED_SALARY = "expected_salary"
PROS = "pros"
CONS = "cons"
# canonical (lowercased, whitespace-collapsed, punctuation-stripped) header -> field.
# The sheet's own spelling is listed first; the rest are tolerated synonyms.
HEADER_ALIASES: dict[FormDataField, tuple[str, ...]] = {
FormDataField.SERIAL_NO: ("um", "sr", "sr no", "s no", "serial", "serial no"),
FormDataField.ENTRY_YEAR: ("year", "year of graduation"),
FormDataField.ENTRY_MONTH: ("month",),
FormDataField.ENTRY_DATE: ("date", "entry date", "date of entry", "timestamp", "time stamp"),
FormDataField.ENTRY_TIME: ("time of entry", "entry time", "time"),
FormDataField.SCREENED_BY: (
"screened by", "screened", "interviewed by", "conducted by", "recruiter",
),
FormDataField.NAME: (
"candidate name", "full name", "name", "names", "candidate",
),
FormDataField.HR_COMMENTS: ("hr comments", "hr comment", "comments", "remarks"),
FormDataField.CANDIDATE_NUMBER: (
"candidate number", "contact number", "phone number", "phone", "mobile", "contact",
),
FormDataField.CANDIDATE_EMAIL: ("candidate email", "email", "email address"),
FormDataField.PROFILE_LINK: (
"profile link", "linkedin profile link", "cv link", "resume link",
"drop your updated resume", "profile",
),
FormDataField.AREA_OF_EXPERTISE: (
"area of expertise", "area of interest", "expertise",
),
FormDataField.REQUISITION_NUMBER: ("requisition number", "requisition", "req no"),
FormDataField.POSITION_SUITABLE_FOR: (
"position suitable for", "position applied for", "position",
"designation", "job title", "role", "title",
),
FormDataField.SOURCE_OF_APPLICATION: (
"source of application", "source", "application source",
"where did you hear about the position you're applying for",
),
FormDataField.AGE: ("age",),
FormDataField.MARITAL_STATUS: ("marital status", "marital", "family details"),
FormDataField.DEGREE: (
"education", "educational degree", "degree", "qualification",
),
FormDataField.UNIVERSITY: ("university of graduation", "university", "institute", "college"),
FormDataField.EXPERIENCE: ("experience", "total experience", "years of experience", "exp"),
FormDataField.EXPERIENCE_DETAILS: ("experience details", "experience detail"),
FormDataField.AREA_OF_RESIDENCE: (
"area of residence", "residing city", "residing country",
"residence", "location", "address",
),
FormDataField.COMMUNICATION_SKILLS: ("communication skills", "communication"),
FormDataField.PREFERRED_TIMINGS: ("preferred timings", "preferred timing", "shift"),
FormDataField.HO_AVAILABILITY: (
"availability to work in the h.o", "availability to work in the ho",
"ho availability", "availability", "are you willing to relocate",
),
FormDataField.CURRENT_COMPANY: ("current company", "current employer", "company", "employer"),
FormDataField.REASON_FOR_LEAVING: ("reason for leaving", "reason of leaving", "reason"),
FormDataField.NOTICE_PERIOD: (
"how soon can you join us", "how soon can you join",
"notice period", "joining", "availability to join",
),
FormDataField.CURRENT_SALARY: ("current salary", "present salary", "salary"),
FormDataField.EXPECTED_SALARY: ("expected salary", "salary expectation", "expected"),
FormDataField.PROS: ("pros", "strengths"),
FormDataField.CONS: ("cons", "weaknesses"),
}
def _build_alias_to_field() -> dict[str, FormDataField]:
inverted: dict[str, FormDataField] = {}
for field, aliases in HEADER_ALIASES.items():
for alias in aliases:
if alias in inverted:
raise ValueError(
f"duplicate header alias {alias!r}: "
f"{inverted[alias].value} and {field.value}"
)
inverted[alias] = field
return inverted
ALIAS_TO_FIELD: dict[str, FormDataField] = _build_alias_to_field()
class FormDataColumn(str, Enum):
"""FormData API / ORM field names in serialize order.
Broader than FormDataField: includes id, sheet meta, derived parsers
(age_raw, *_salary_value), raw_record, and timestamps.
"""
ID = "id"
SHEET = "sheet"
JOB_POST_ID = "job_post_id"
ROW_NUMBER = "row_number"
SERIAL_NO = "serial_no"
ENTRY_YEAR = "entry_year"
ENTRY_MONTH = "entry_month"
ENTRY_DATE = "entry_date"
ENTRY_TIME = "entry_time"
SCREENED_BY = "screened_by"
NAME = "name"
HR_COMMENTS = "hr_comments"
CANDIDATE_NUMBER = "candidate_number"
CANDIDATE_EMAIL = "candidate_email"
PROFILE_LINK = "profile_link"
AREA_OF_EXPERTISE = "area_of_expertise"
REQUISITION_NUMBER = "requisition_number"
POSITION_SUITABLE_FOR = "position_suitable_for"
SOURCE_OF_APPLICATION = "source_of_application"
AGE = "age"
AGE_RAW = "age_raw"
MARITAL_STATUS = "marital_status"
DEGREE = "degree"
UNIVERSITY = "university"
EXPERIENCE = "experience"
EXPERIENCE_DETAILS = "experience_details"
AREA_OF_RESIDENCE = "area_of_residence"
COMMUNICATION_SKILLS = "communication_skills"
PREFERRED_TIMINGS = "preferred_timings"
HO_AVAILABILITY = "ho_availability"
CURRENT_COMPANY = "current_company"
REASON_FOR_LEAVING = "reason_for_leaving"
NOTICE_PERIOD = "notice_period"
CURRENT_SALARY = "current_salary"
CURRENT_SALARY_VALUE = "current_salary_value"
EXPECTED_SALARY = "expected_salary"
EXPECTED_SALARY_VALUE = "expected_salary_value"
PROS = "pros"
CONS = "cons"
RAW_RECORD = "raw_record"
IMPORTED_AT = "imported_at"
CREATED_AT = "created_at"
UPDATED_AT = "updated_at"
# Ordered values for serialize_form_data / model_fields assertions.
FORM_DATA_FIELDS: tuple[str, ...] = tuple(member.value for member in FormDataColumn)
# -- Date parsing ------------------------------------------------------------
class DateFormat(str, Enum):
"""strptime patterns tried in definition order.
DD/MM before MM/DD: 14/10/20 is ambiguous and DD/MM is the local convention.
"""
D_MON_Y_DASH = "%d-%b-%Y"
D_MONTH_Y_DASH = "%d-%B-%Y"
D_MON_Y_SPACE = "%d %b %Y"
D_MONTH_Y_SPACE = "%d %B %Y"
DMY_SLASH = "%d/%m/%Y"
DMY_SLASH_SHORT = "%d/%m/%y"
DMY_DASH = "%d-%m-%Y"
DMY_DASH_SHORT = "%d-%m-%y"
ISO = "%Y-%m-%d"
DMY_DOT = "%d.%m.%Y"
DMY_DOT_SHORT = "%d.%m.%y"
MDY_SLASH = "%m/%d/%Y"
MDY_SLASH_SHORT = "%m/%d/%y"
MON_D_Y = "%b %d %Y"
MONTH_D_Y = "%B %d %Y"
D_MON_Y_SHORT = "%d-%b-%y"
D_MON_Y_SPACE_SHORT = "%d %b %y"
D_MON_Y_SLASH = "%d/%b/%Y"
D_MON_Y_SLASH_SHORT = "%d/%b/%y"
class DateTimeSeparator(str, Enum):
"""Separators that split a date cell into date + time tails."""
DASH = " - "
EN_DASH = " "
EM_DASH = ""
SLASH_SPACE = "/ "
PIPE = " | "
class MonthNormalisation(Enum):
"""Sheet month spellings → %b-safe short form. value is (source, short)."""
SEPTEMBER = ("september", "sep")
SEPT = ("sept", "sep")
JULY = ("july", "jul")
JUNE = ("june", "jun")
APRIL = ("april", "apr")
MARCH = ("march", "mar")
@property
def source(self) -> str:
return self.value[0]
@property
def short(self) -> str:
return self.value[1]