Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion products/signals/backend/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,13 @@ def validate(self, attrs: dict) -> dict:
class SignalReportSerializer(serializers.ModelSerializer):
artefact_count = serializers.IntegerField(read_only=True)
priority = serializers.SerializerMethodField(
help_text="P0–P4 from the latest actionability judgment artefact (when present).",
help_text="P0–P4 from the latest priority judgment artefact (when present).",
)
actionability = serializers.SerializerMethodField(
help_text="Actionability choice from the latest actionability judgment artefact (when present).",
)
already_addressed = serializers.SerializerMethodField(
help_text="Whether the issue appears already fixed, from the actionability judgment artefact.",
)
is_suggested_reviewer = serializers.BooleanField(read_only=True, default=False)

Expand All @@ -131,10 +137,30 @@ class Meta:
"updated_at",
"artefact_count",
"priority",
"actionability",
"already_addressed",
"is_suggested_reviewer",
]
read_only_fields = fields

def _get_actionability_artefact_data(self, obj: SignalReport) -> dict | None:
prefetched = getattr(obj, "prefetched_actionability_artefacts", None)
if prefetched is not None:
art = prefetched[0] if prefetched else None
else:
art = (
obj.artefacts.filter(type=SignalReportArtefact.ArtefactType.ACTIONABILITY_JUDGMENT)
.order_by("-created_at")
.first()
)
if art is None:
return None
try:
data = json.loads(art.content)
except (json.JSONDecodeError, TypeError, ValueError):
return None
return data if isinstance(data, dict) else None

def get_priority(self, obj: SignalReport) -> str | None:
prefetched = getattr(obj, "prefetched_priority_artefacts", None)
if prefetched is not None:
Expand All @@ -156,6 +182,21 @@ def get_priority(self, obj: SignalReport) -> str | None:
p = data.get("priority")
return p if isinstance(p, str) else None

def get_actionability(self, obj: SignalReport) -> str | None:
data = self._get_actionability_artefact_data(obj)
if data is None:
return None
# Support both agentic ("actionability") and legacy ("choice") field names
value = data.get("actionability") or data.get("choice")
return value if isinstance(value, str) else None

def get_already_addressed(self, obj: SignalReport) -> bool | None:
data = self._get_actionability_artefact_data(obj)
if data is None:
return None
value = data.get("already_addressed")
return value if isinstance(value, bool) else None


class SignalReportArtefactSerializer(serializers.ModelSerializer):
content = serializers.SerializerMethodField()
Expand Down
9 changes: 8 additions & 1 deletion products/signals/backend/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,14 @@ def safely_get_queryset(self, queryset):
type=SignalReportArtefact.ArtefactType.PRIORITY_JUDGMENT
).order_by("-created_at"),
to_attr="prefetched_priority_artefacts",
)
),
Prefetch(
"artefacts",
queryset=SignalReportArtefact.objects.filter(
type=SignalReportArtefact.ArtefactType.ACTIONABILITY_JUDGMENT
).order_by("-created_at"),
to_attr="prefetched_actionability_artefacts",
),
)

# Annotate is_suggested_reviewer by resolving the current user's GitHub login
Expand Down
Loading