kaauh_ats/recruitment/score_utils.py
2025-11-17 09:33:47 +03:00

36 lines
1.8 KiB
Python

from django.db.models import Value, IntegerField, CharField, F
from django.db.models.functions import Coalesce, Cast, Replace, NullIf, KeyTextTransform
# Define the path to the match score
# Based on your tracebacks, the path is: ai_analysis_data -> analysis_data -> match_score
SCORE_PATH_RAW = F('ai_analysis_data__analysis_data__match_score')
# Define a robust annotation expression for safely extracting and casting the match score.
# This sequence handles three common failure points:
# 1. Missing keys (handled by Coalesce).
# 2. Textual scores (e.g., "N/A" or "") (handled by NullIf).
# 3. Quoted numeric scores (e.g., "50") from JSONB extraction (handled by Replace).
def get_safe_score_annotation():
"""
Returns a Django Expression object that safely extracts a score from the
JSONField, cleans it, and casts it to an IntegerField.
"""
# 1. Extract the JSON value as text and force a CharField for cleaning functions
# Using the double-underscore path is equivalent to the KeyTextTransform
# for the final nested key in a PostgreSQL JSONField.
extracted_text = Cast(SCORE_PATH_RAW, output_field=CharField())
# 2. Clean up any residual double-quotes that sometimes remain if the data
# was stored as a quoted string (e.g., "50")
cleaned_text = Replace(extracted_text, Value('"'), Value(''))
# 3. Use NullIf to convert the cleaned text to NULL if it is an empty string
# (or if it was a non-numeric string like "N/A" after quote removal)
null_if_empty = NullIf(cleaned_text, Value(''))
# 4. Cast the result (which is now either a clean numeric string or NULL) to an IntegerField.
final_cast = Cast(null_if_empty, output_field=IntegerField())
# 5. Use Coalesce to ensure NULL scores (from errors or missing data) default to 0.
return Coalesce(final_cast, Value(0))