forms done

Edition_Fomrs
ahmed.mujtaba 2026-09-11 18:37:16 +05:00
parent 80a3b290b9
commit a021d60fc3
5 changed files with 55 additions and 12 deletions

View File

@ -16,10 +16,14 @@ class Position(BaseModel):
date_needed:Optional[date]
type:Optional[EmploymentType]
job_description:Optional[str]
period_from:Optional[date]=None
period_to:Optional[date]=None
jd_available:Optional[bool]=None
class InternalRecommendate(BaseModel):
employee_name:Optional[str]=None
employee_department:Optional[str]=None
entity:Optional[str]=None
class ReplacementFor(BaseModel):
to_replace:Optional[str]

View File

@ -34,9 +34,13 @@ class Requisition(SQLModel, table=True):
),
)
job_description: Optional[str] = None
period_from: Optional[Date] = None
period_to: Optional[Date] = None
jd_available: Optional[bool] = None
employee_name: Optional[str] = None
employee_department: Optional[str] = None
entity: Optional[str] = None
to_replace: Optional[str] = None
grade: Optional[str] = None
@ -139,8 +143,12 @@ class Requisition(SQLModel, table=True):
date_needed=position.get("date_needed") if position.get("date_needed") else None,
employment_type=EmploymentType(position.get("type")) if position.get("type") else None,
job_description=position.get("job_description") if position.get("job_description") else None,
period_from=position.get("period_from") if position.get("period_from") else None,
period_to=position.get("period_to") if position.get("period_to") else None,
jd_available=position.get("jd_available") if position.get("jd_available") is not None else None,
employee_name=referral.get("employee_name") if referral.get("employee_name") else None,
employee_department=referral.get("employee_department") if referral.get("employee_department") else None,
entity=referral.get("entity") if referral.get("entity") else None,
to_replace=replacement.get("to_replace") if replacement.get("to_replace") else None,
grade=replacement.get("grade") if replacement.get("grade") else None,
recruitment_title=replacement.get("title") if replacement.get("title") else None,
@ -183,6 +191,12 @@ class Requisition(SQLModel, table=True):
row.employment_type = EmploymentType(position.get("type")) if position.get("type") else None
if "job_description" in position:
row.job_description = position.get("job_description") if position.get("job_description") else None
if "period_from" in position:
row.period_from = position.get("period_from") if position.get("period_from") else None
if "period_to" in position:
row.period_to = position.get("period_to") if position.get("period_to") else None
if "jd_available" in position:
row.jd_available = position.get("jd_available") if position.get("jd_available") is not None else None
if "replacement_for" in fields:
replacement = fields.get("replacement_for") if fields.get("replacement_for") else {}
if "to_replace" in replacement:
@ -205,6 +219,8 @@ class Requisition(SQLModel, table=True):
row.employee_name = referral.get("employee_name") if referral.get("employee_name") else None
if "employee_department" in referral:
row.employee_department = referral.get("employee_department") if referral.get("employee_department") else None
if "entity" in referral:
row.entity = referral.get("entity") if referral.get("entity") else None
if "initiated_by" in fields:
row.initiated_by = fields.get("initiated_by") if fields.get("initiated_by") else None
if "initiated_date" in fields:

View File

@ -80,7 +80,8 @@ FORM_DEFINITIONS = {
"criteria": [
{"key": "core_job_knowledge", "label": "Core Job Knowledge & Domain Expertise"},
{"key": "relevant_experience", "label": "Depth of Relevant Experience"},
{"key": "problem_solving", "label": "Problem Solving & Analytical Reasoning"},
{"key": "problem_solving", "label": "Problem Solving"},
{"key": "analytical_reasoning", "label": "Analytical Reasoning"},
{"key": "tools_proficiency", "label": "Technical Tools & Systems Proficiency"},
{"key": "quality_of_work", "label": "Quality of Work & Attention to Detail"},
],
@ -109,27 +110,41 @@ FORM_DEFINITIONS = {
),
"has_recommendation": True,
},
# form_type/section/field keys below stay "cultural_fit"/"cultural"/"cultural_note" —
# renamed labels only. Titles and criterion labels are denormalized into every
# saved row at write time (see module docstring), so historical rows keep the
# "Cultural Fit" wording they were saved under while new rows pick up the fuller
# revision 2 "HR Evaluation" section below; the key stays stable so old rows keep
# validating and combined_summary()'s "cultural" lookup keeps matching both.
"cultural_fit": {
"title": "Cultural Fit",
"title": "HR Evaluation",
"source": "Annexure E - Interview Evaluation Form",
"scale_note": RATING_SCALE_NOTE,
"sections": [
{
"key": "cultural",
"title": "CULTURAL FIT",
"average_label": "CULTURAL FIT SECTION",
"title": "HR EVALUATION",
"average_label": "HR EVALUATION SECTION",
"criteria": [
{"key": "company_values", "label": "Alignment with Company Values"},
{"key": "basic_jd_requirement", "label": "Basic JD requirement"},
{"key": "company_values", "label": "Alignment with Company Culture"},
{"key": "professionalism", "label": "Professionalism & Integrity"},
{"key": "collaboration", "label": "Collaboration & Team Orientation"},
{"key": "adaptability", "label": "Adaptability to Change"},
{"key": "work_ethic", "label": "Work Ethic & Reliability"},
{"key": "adaptability", "label": "Adaptability"},
{"key": "agility", "label": "Agility"},
{"key": "work_ethic", "label": "Work Ethics"},
{"key": "communication_articulation", "label": "Communication & Articulation"},
{"key": "problem_solving_orientation", "label": "Problem Solving & Solution Orientation"},
{"key": "critical_thinking", "label": "Critical Thinking & Analytical Capability"},
{"key": "initiative", "label": "Initiative & Proactiveness"},
{"key": "decision_making", "label": "Decision Making"},
{"key": "leadership", "label": "Leadership"},
],
},
],
"fields": (
_EVALUATION_HEADER_FIELDS
+ [{"key": "cultural_note", "label": "Cultural Fit — Notes", "kind": "text"}]
+ [{"key": "cultural_note", "label": "HR Evaluation — Notes", "kind": "text"}]
+ _EVALUATION_FOOTER_FIELDS
),
"has_recommendation": True,
@ -174,6 +189,7 @@ FORM_DEFINITIONS = {
{"key": "internal_recommendation", "label": "INCASE OF INTERNAL RECOMMENDATE", "kind": "bool"},
{"key": "recommended_employee_name", "label": "EMPLOYEE NAME", "kind": "text"},
{"key": "recommended_employee_department", "label": "EMPLOYEE DEPARTMENT", "kind": "text"},
{"key": "entity", "label": "Entity", "kind": "text"},
{"key": "initiated_by", "label": "Initiated By — Name", "kind": "text"},
{"key": "initiated_date", "label": "Initiated By — Date", "kind": "date"},
{"key": "recommended_by", "label": "Recommended By — Name (Director)", "kind": "text"},
@ -354,7 +370,10 @@ def combined_summary(rows):
`rows` are candidate_forms records (attribute access: form_type, created_at,
sections). The latest interview_analysis row supplies the technical and
behavioral averages, the latest cultural_fit row the cultural average.
behavioral averages, the latest cultural_fit row the "cultural" section
average cultural_fit's own section carries the fuller HR Evaluation
criteria as of revision 2, but the key stays "cultural" so this lookup
(and the `cultural_avg` key below) don't need to change with it.
The combined overall (mean of the three section averages, 2 dp, already
ranged onto 0100) appears only once all three exist. Returns None when
neither evaluation exists. Legacy 14 section averages are converted

View File

@ -85,6 +85,9 @@ def serialize_requisition(row) -> dict:
"date_needed": _date(row.date_needed),
"type": _enum(row.employment_type),
"job_description": row.job_description,
"period_from": _date(row.period_from),
"period_to": _date(row.period_to),
"jd_available": row.jd_available,
},
"replacement_for": {
"to_replace": row.to_replace,
@ -98,6 +101,7 @@ def serialize_requisition(row) -> dict:
"refferal_by": {
"employee_name": row.employee_name,
"employee_department": row.employee_department,
"entity": row.entity,
},
"initiated_by": row.initiated_by,
"initiated_date": _date(row.initiated_date),

View File

@ -52,7 +52,7 @@ class TestNormalizeSections:
normalized, overall = normalize_sections("interview_analysis", [])
assert [s["key"] for s in normalized] == ["technical", "behavioral"]
technical = normalized[0]
assert len(technical["criteria"]) == 5
assert len(technical["criteria"]) == 6
assert technical["criteria"][0]["label"] == "Core Job Knowledge & Domain Expertise"
assert technical["average"] is None
assert overall is None
@ -149,8 +149,8 @@ class TestDefinitions:
def test_paper_parity_criterion_counts(self):
ia = FORM_DEFINITIONS["interview_analysis"]
cf = FORM_DEFINITIONS["cultural_fit"]
assert [len(s["criteria"]) for s in ia["sections"]] == [5, 5]
assert [len(s["criteria"]) for s in cf["sections"]] == [5]
assert [len(s["criteria"]) for s in ia["sections"]] == [6, 5]
assert [len(s["criteria"]) for s in cf["sections"]] == [13]
def test_stage_gate_vocabulary(self):
assert set(FORM_READY_STATUSES) == {"INTERVIEW", "OFFER", "HIRED", "APPROVED"}