Add issue workflow progress tracker
This commit is contained in:
@@ -258,6 +258,97 @@ def _stage_label_for_workflow(request_status: str, media_status: str) -> str:
|
||||
return "Approved"
|
||||
|
||||
|
||||
ISSUE_WORKFLOW_STAGES = (
|
||||
("reported", "Reported"),
|
||||
("review", "Under review"),
|
||||
("planned", "Fix planned"),
|
||||
("repair", "Fix underway"),
|
||||
("confirmation", "Confirm fix"),
|
||||
("resolved", "Resolved"),
|
||||
)
|
||||
|
||||
ISSUE_STATUS_TO_STAGE: Dict[str, Tuple[int, str, str, str]] = {
|
||||
"new": (
|
||||
0,
|
||||
"Issue received",
|
||||
"Your report has been logged and is waiting for the support team to review it.",
|
||||
"active",
|
||||
),
|
||||
"triaging": (
|
||||
1,
|
||||
"Being investigated",
|
||||
"The support team is checking the report and identifying the right fix.",
|
||||
"active",
|
||||
),
|
||||
"planned": (
|
||||
2,
|
||||
"Fix ready to begin",
|
||||
"The problem has been reviewed and the next action has been selected.",
|
||||
"active",
|
||||
),
|
||||
"in_progress": (
|
||||
3,
|
||||
"Fix in progress",
|
||||
"Work is underway on the affected content or service.",
|
||||
"active",
|
||||
),
|
||||
"blocked": (
|
||||
3,
|
||||
"Fix needs attention",
|
||||
"Work has paused because the support team needs another service, resource, or decision before continuing.",
|
||||
"attention",
|
||||
),
|
||||
"awaiting_confirmation": (
|
||||
4,
|
||||
"Waiting for confirmation",
|
||||
"A fix has been applied. Magent is waiting for the reporter to confirm that the problem is gone.",
|
||||
"active",
|
||||
),
|
||||
"done": (
|
||||
5,
|
||||
"Issue resolved",
|
||||
"The reported problem has been fixed and the issue is complete.",
|
||||
"complete",
|
||||
),
|
||||
"closed": (
|
||||
5,
|
||||
"Issue resolved",
|
||||
"The reported problem has been fixed and the issue is closed.",
|
||||
"complete",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def _issue_workflow_payload(status: Any) -> Dict[str, Any]:
|
||||
normalized_status = str(status or "new").strip().lower()
|
||||
stage_index, headline, message, state = ISSUE_STATUS_TO_STAGE.get(
|
||||
normalized_status,
|
||||
ISSUE_STATUS_TO_STAGE["new"],
|
||||
)
|
||||
steps = []
|
||||
for index, (key, label) in enumerate(ISSUE_WORKFLOW_STAGES):
|
||||
step_state = (
|
||||
"complete"
|
||||
if index < stage_index or (index == stage_index and state == "complete")
|
||||
else "active"
|
||||
if index == stage_index
|
||||
else "waiting"
|
||||
)
|
||||
if index == stage_index and state == "attention":
|
||||
step_state = "attention"
|
||||
steps.append({"key": key, "label": label, "state": step_state})
|
||||
return {
|
||||
"current_step": stage_index + 1,
|
||||
"total_steps": len(ISSUE_WORKFLOW_STAGES),
|
||||
"stage": ISSUE_WORKFLOW_STAGES[stage_index][0],
|
||||
"stage_label": ISSUE_WORKFLOW_STAGES[stage_index][1],
|
||||
"headline": headline,
|
||||
"message": message,
|
||||
"state": state,
|
||||
"steps": steps,
|
||||
}
|
||||
|
||||
|
||||
def _normalize_request_pipeline(
|
||||
request_status: Optional[str],
|
||||
media_status: Optional[str],
|
||||
@@ -419,6 +510,7 @@ def _serialize_item(item: Dict[str, Any], user: Dict[str, Any]) -> Dict[str, Any
|
||||
"related_item_id": _normalize_int(item.get("related_item_id"), "related_item_id"),
|
||||
"is_resolved": bool(_clean_text(item.get("issue_resolved_at"))),
|
||||
"resolved_at": _clean_text(item.get("issue_resolved_at")),
|
||||
"workflow": _issue_workflow_payload(item.get("status")),
|
||||
"confirmation": {
|
||||
"status": resolution.get("status"),
|
||||
"attempts_sent": int(resolution.get("attemptsSent") or 0),
|
||||
|
||||
@@ -1592,6 +1592,29 @@ class PortalWorkflowTests(TempDatabaseMixin, unittest.TestCase):
|
||||
self.assertEqual(workflow.get("request_status"), "approved")
|
||||
self.assertEqual(workflow.get("media_status"), "processing")
|
||||
|
||||
def test_issue_status_maps_to_public_workflow_progress(self) -> None:
|
||||
item = {
|
||||
"kind": "issue",
|
||||
"status": "blocked",
|
||||
"issue_type": "playback",
|
||||
"created_by_username": "tester",
|
||||
}
|
||||
serialized = portal_router._serialize_item(item, {"username": "tester", "role": "user"})
|
||||
workflow = (serialized.get("issue") or {}).get("workflow") or {}
|
||||
|
||||
self.assertEqual(workflow.get("current_step"), 4)
|
||||
self.assertEqual(workflow.get("stage"), "repair")
|
||||
self.assertEqual(workflow.get("state"), "attention")
|
||||
self.assertEqual(len(workflow.get("steps") or []), 6)
|
||||
self.assertEqual((workflow.get("steps") or [])[3].get("state"), "attention")
|
||||
|
||||
def test_resolved_issue_completes_the_public_workflow(self) -> None:
|
||||
workflow = portal_router._issue_workflow_payload("closed")
|
||||
|
||||
self.assertEqual(workflow.get("current_step"), 6)
|
||||
self.assertEqual(workflow.get("state"), "complete")
|
||||
self.assertTrue(all(step.get("state") == "complete" for step in workflow.get("steps") or []))
|
||||
|
||||
def test_invalid_pipeline_transition_is_rejected(self) -> None:
|
||||
with self.assertRaises(HTTPException) as context:
|
||||
portal_router._validate_pipeline_transition(
|
||||
|
||||
Reference in New Issue
Block a user