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

280 lines
6.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/round format mappings.
(str, Enum) like inbox/enums.py: members compare to and serialize as plain strings.
Non-string mappings (month pairs, ordinal slot+pattern) use plain Enum.
"""
from enum import Enum
class FormDataField(str, Enum):
"""Canonical FormData column keys (plus title, which stays in JSONB only)."""
NAME = "name"
DEGREE = "degree"
EXPERIENCE = "experience"
AGE = "age"
FAMILY_DETAILS = "family_details"
TITLE = "title"
class NameAlias(str, Enum):
NAME = "name"
NAMES = "names"
CANDIDATE_NAME = "candidate name"
CANDIDATE = "candidate"
@classmethod
def has(cls, value) -> bool:
return value in cls._value2member_map_
class DegreeAlias(str, Enum):
EDUCATION = "education"
DEGREE = "degree"
QUALIFICATION = "qualification"
@classmethod
def has(cls, value) -> bool:
return value in cls._value2member_map_
class ExperienceAlias(str, Enum):
EXPERIENCE = "experience"
EXP = "exp"
YEARS_OF_EXPERIENCE = "years of experience"
TOTAL_EXPERIENCE = "total experience"
@classmethod
def has(cls, value) -> bool:
return value in cls._value2member_map_
class AgeAlias(str, Enum):
AGE = "age"
@classmethod
def has(cls, value) -> bool:
return value in cls._value2member_map_
class FamilyDetailsAlias(str, Enum):
FAMILY_DETAILS = "family details"
MARITAL_STATUS = "marital status"
MARITAL = "marital"
@classmethod
def has(cls, value) -> bool:
return value in cls._value2member_map_
class TitleAlias(str, Enum):
"""No FormData column — recognised so headers are not treated as unknown noise."""
TITLE = "title"
DESIGNATION = "designation"
ROLE = "role"
POSITION = "position"
TEAM = "team"
JOB_TITLE = "job title"
AREA_OF_EXPERTISE = "area of expertise"
DEPARTMENT = "department"
@classmethod
def has(cls, value) -> bool:
return value in cls._value2member_map_
# FormDataField → alias Enum. Order is match priority for overlapping startswith hits.
FIELD_ALIAS_ENUMS = {
FormDataField.NAME: NameAlias,
FormDataField.DEGREE: DegreeAlias,
FormDataField.EXPERIENCE: ExperienceAlias,
FormDataField.AGE: AgeAlias,
FormDataField.FAMILY_DETAILS: FamilyDetailsAlias,
FormDataField.TITLE: TitleAlias,
}
class RoundRole(str, Enum):
"""Interview-round column roles resolved left-to-right into four slots."""
DATE = "date"
BY = "by"
STATUS = "status"
NOTES = "notes"
RESULT = "result"
class ConductedByAlias(str, Enum):
"""Header spellings that map to RoundRole.BY."""
CONDUCTED_BY = "conducted by"
INTERVIEWED_BY = "interviewed by"
INTERVIEW_BY = "interview by"
CONDUCTED = "conducted"
BY = "by"
@classmethod
def has(cls, value) -> bool:
return value in cls._value2member_map_
@classmethod
def contained_in(cls, text: str) -> bool:
return any(member.value in text for member in cls if " " in member.value)
class NotesToken(str, Enum):
"""Substrings that classify a header as RoundRole.NOTES."""
NOTE = "note"
REMARK = "remark"
COMMENT = "comment"
@classmethod
def contained_in(cls, text: str) -> bool:
return any(member.value in text for member in cls)
# -- Round → FormData column names (slot 0..3 = definition order) ------------
class RoundDateColumn(str, Enum):
R1 = "interview_date"
R2 = "second_interview_date"
R3 = "third_interview_date"
R4 = "fourth_interview_date"
@classmethod
def ordered(cls) -> tuple[str, ...]:
return tuple(member.value for member in cls)
class RoundByColumn(str, Enum):
R1 = "interview_by"
R2 = "second_interview_by"
R3 = "third_interview_by"
R4 = "fourth_interview_by"
@classmethod
def ordered(cls) -> tuple[str, ...]:
return tuple(member.value for member in cls)
class RoundTimeColumn(str, Enum):
R1 = "interview_time"
R2 = "second_interview_time"
R3 = "third_interview_time"
R4 = "fourth_interview_time"
@classmethod
def ordered(cls) -> tuple[str, ...]:
return tuple(member.value for member in cls)
class RoundStatusColumn(str, Enum):
R1 = "interview_status"
R2 = "second_interview_status"
R3 = "third_interview_status"
R4 = "fourth_interview_status"
@classmethod
def ordered(cls) -> tuple[str, ...]:
return tuple(member.value for member in cls)
class RoundNotesColumn(str, Enum):
R1 = "interview_notes"
R2 = "second_interview_notes"
R3 = "third_interview_notes"
R4 = "fourth_interview_notes"
@classmethod
def ordered(cls) -> tuple[str, ...]:
return tuple(member.value for member in cls)
class RoundResultColumn(str, Enum):
R1 = "interview_result"
R2 = "second_interview_result"
R3 = "third_interview_result"
R4 = "fourth_interview_result"
@classmethod
def ordered(cls) -> tuple[str, ...]:
return tuple(member.value for member in cls)
# -- 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]
class RoundOrdinal(Enum):
"""Interview-round ordinal in a header → slot index 0..3. value is (slot, regex)."""
FIRST = (0, r"(?:1st|first|01st)")
SECOND = (1, r"(?:2nd|second|02nd)")
THIRD = (2, r"(?:3rd|third|03rd)")
FOURTH = (3, r"(?:4th|fourth|04th)")
@property
def slot(self) -> int:
return self.value[0]
@property
def pattern(self) -> str:
return self.value[1]