From edcc0def5d881133902d636e97b89494106957e1 Mon Sep 17 00:00:00 2001 From: hz Date: Thu, 23 Jul 2026 23:14:38 -0500 Subject: [PATCH] Fix CCS category order search --- api/QuestionsApi.Tests/CcsCaseTests.cs | 19 +++++++++++++++++++ api/QuestionsApi/Services/CcsStore.cs | 15 ++++++++++++++- scripts/ui/ccs_player.gd | 7 +++++++ tests/smoke/uat-smoke.spec.mjs | 7 +++++++ 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/api/QuestionsApi.Tests/CcsCaseTests.cs b/api/QuestionsApi.Tests/CcsCaseTests.cs index 0410b07..766aadf 100644 --- a/api/QuestionsApi.Tests/CcsCaseTests.cs +++ b/api/QuestionsApi.Tests/CcsCaseTests.cs @@ -143,6 +143,25 @@ public sealed class CcsCaseTests Assert.True(CcsRatingStore.ExpectedScore(800, 1000) < 0.25); } + [Theory] + [InlineData("medication", "medication")] + [InlineData("medication", "medications")] + [InlineData("medication", "treatment")] + [InlineData("procedure", "procedures")] + [InlineData("laboratory", "tests")] + [InlineData("diagnostic", "study")] + public void CcsOrderSearchMatchesClinicalCategories(string category, string query) + { + var order = new CcsOrderDefinition + { + Code = "order", + CanonicalName = "Unrelated canonical name", + Category = category + }; + + Assert.True(CcsStore.Rank(order, query) < 1000); + } + [Fact] public void ScoringReviewIncludesExpectedAndObservedEvidence() { diff --git a/api/QuestionsApi/Services/CcsStore.cs b/api/QuestionsApi/Services/CcsStore.cs index 1d286f9..3095986 100644 --- a/api/QuestionsApi/Services/CcsStore.cs +++ b/api/QuestionsApi/Services/CcsStore.cs @@ -695,12 +695,25 @@ public sealed class CcsStore PresentingLocation = value.PresentingLocation, RealTimeMinutes = value.RealTimeMinutes, Difficulty = value.Difficulty, Tags = value.Tags }; - private static int Rank(CcsOrderDefinition order, string query) + internal static int Rank(CcsOrderDefinition order, string query) { var values = new[] { order.CanonicalName }.Concat(order.Aliases).Select(value => value.ToLowerInvariant()); if (values.Any(value => value == query)) return 0; if (values.Any(value => value.StartsWith(query))) return 1; if (values.Any(value => value.Contains(query))) return 2; + if (CategorySearchTerms(order.Category).Contains(query, StringComparer.OrdinalIgnoreCase)) return 3; return 1000; } + + private static IReadOnlyList CategorySearchTerms(string category) => + category.Trim().ToLowerInvariant() switch + { + "laboratory" => ["laboratory", "laboratories", "lab", "labs", "test", "tests", "diagnostic", "diagnostics", "study", "studies"], + "diagnostic" => ["diagnostic", "diagnostics", "test", "tests", "study", "studies"], + "medication" => ["medication", "medications", "medicine", "medicines", "drug", "drugs", "treatment", "treatments", "therapy", "therapies"], + "procedure" => ["procedure", "procedures", "intervention", "interventions", "treatment", "treatments"], + "monitoring" => ["monitor", "monitoring", "treatment", "treatments"], + "disposition" => ["disposition", "admission", "admit", "transfer", "discharge"], + _ => [category.Trim().ToLowerInvariant()] + }; } diff --git a/scripts/ui/ccs_player.gd b/scripts/ui/ccs_player.gd index fc760a2..7855bff 100644 --- a/scripts/ui/ccs_player.gd +++ b/scripts/ui/ccs_player.gd @@ -608,6 +608,13 @@ func _submit_search() -> void: func _render_search() -> void: _clear(search_results) + if CCSService.orders.is_empty(): + _set_status( + "No matching orders found. Try a clinical name or category." + if search.text.strip_edges().length() >= 2 + else "") + return + _set_status("") for raw_order in CCSService.orders: var order: Dictionary = raw_order var code := str(order.get("code", "")) diff --git a/tests/smoke/uat-smoke.spec.mjs b/tests/smoke/uat-smoke.spec.mjs index 5c27669..bde4da8 100644 --- a/tests/smoke/uat-smoke.spec.mjs +++ b/tests/smoke/uat-smoke.spec.mjs @@ -190,6 +190,13 @@ test("public API and disposable learner journey", async () => { expect(activeCcsAttempt).toBeTruthy(); expect(attempt.caseExternalId).toBeTruthy(); expect(attempt.state).toBe("active"); + const { data: medicationOrders } = await call("/api/ccs/orders/search?q=medications&limit=20", { token }); + expect(Array.isArray(medicationOrders)).toBeTruthy(); + expect(medicationOrders.length).toBeGreaterThan(0); + expect(medicationOrders.some((order) => order.category === "medication")).toBeTruthy(); + const { data: testOrders } = await call("/api/ccs/orders/search?q=tests&limit=20", { token }); + expect(Array.isArray(testOrders)).toBeTruthy(); + expect(testOrders.some((order) => ["laboratory", "diagnostic"].includes(order.category))).toBeTruthy(); const { data: opened } = await call(`/api/ccs/attempts/${activeCcsAttempt}/commands`, { method: "POST", token, body: { commandId: `smoke-opening-${suffix}`, expectedRevision: attempt.revision,