36 lines
1.8 KiB
Python
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)) |