From f80c08086461ecc8da19f0d5a74c4ce18c461f88 Mon Sep 17 00:00:00 2001 From: shankar ambady Date: Thu, 6 Nov 2025 14:56:40 -0500 Subject: [PATCH 01/18] adding facet display field and migration --- ...100_learningresourceofferor_display_facet.py | 17 +++++++++++++++++ learning_resources/models.py | 2 ++ 2 files changed, 19 insertions(+) create mode 100644 learning_resources/migrations/0100_learningresourceofferor_display_facet.py diff --git a/learning_resources/migrations/0100_learningresourceofferor_display_facet.py b/learning_resources/migrations/0100_learningresourceofferor_display_facet.py new file mode 100644 index 0000000000..13054b65cc --- /dev/null +++ b/learning_resources/migrations/0100_learningresourceofferor_display_facet.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.25 on 2025-11-06 19:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("learning_resources", "0099_alter_learningresource_resource_type"), + ] + + operations = [ + migrations.AddField( + model_name="learningresourceofferor", + name="display_facet", + field=models.BooleanField(default=True), + ), + ] diff --git a/learning_resources/models.py b/learning_resources/models.py index f9c946d360..a922fd5c75 100644 --- a/learning_resources/models.py +++ b/learning_resources/models.py @@ -173,6 +173,8 @@ class LearningResourceOfferor(TimestampedModel): more_information = models.URLField(blank=True) # This field name means "value proposition" value_prop = models.TextField(blank=True) + # whether or not to show this offeror as a facet in the UI + display_facet = models.BooleanField(default=True) @cached_property def channel_url(self): From 3ddc0c81546c609f82f0ff632c86c8fe75a5d439 Mon Sep 17 00:00:00 2001 From: shankar ambady Date: Fri, 7 Nov 2025 18:21:50 -0500 Subject: [PATCH 02/18] adding display facet to opensearch --- learning_resources/serializers.py | 2 +- learning_resources_search/constants.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/learning_resources/serializers.py b/learning_resources/serializers.py index 9b13d0291b..08d123379d 100644 --- a/learning_resources/serializers.py +++ b/learning_resources/serializers.py @@ -113,7 +113,7 @@ class LearningResourceOfferorSerializer(serializers.ModelSerializer): class Meta: model = models.LearningResourceOfferor - fields = ("code", "name", "channel_url") + fields = ("code", "name", "channel_url", "display_facet") class LearningResourceOfferorDetailSerializer(LearningResourceOfferorSerializer): diff --git a/learning_resources_search/constants.py b/learning_resources_search/constants.py index e1e0bc0207..5971041419 100644 --- a/learning_resources_search/constants.py +++ b/learning_resources_search/constants.py @@ -194,6 +194,7 @@ class FilterConfig: "properties": { "code": {"type": "keyword"}, "name": {"type": "keyword"}, + "display_facet": {"type": "boolean"}, "channel_url": {"type": "keyword"}, }, }, From 204e9e077bf5a5296ff4513ac748da05fa8137b2 Mon Sep 17 00:00:00 2001 From: shankar ambady Date: Mon, 10 Nov 2025 12:57:17 -0500 Subject: [PATCH 03/18] make sure we only show offerrors with display_facet set to true --- .../SearchDisplay/SearchDisplay.tsx | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx b/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx index 95018492b1..86ab7b0548 100644 --- a/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx +++ b/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx @@ -34,7 +34,8 @@ import { ResourceCategoryEnum, SearchModeEnumDescriptions, } from "api" -import { useLearningResourcesSearch } from "api/hooks/learningResources" +import { learningResourceQueries } from "api/hooks/learningResources" +import { keepPreviousData, useQuery } from "@tanstack/react-query" import { useAdminSearchParams } from "api/hooks/adminSearchParams" import { AvailableFacets, @@ -568,12 +569,25 @@ const SearchDisplay: React.FC = ({ facetNames, page, ]) - - const { data, isLoading, isFetching } = useLearningResourcesSearch( - allParams as LRSearchRequest, - { keepPreviousData: true }, - ) - + const { data, isLoading, isFetching } = useQuery({ + ...learningResourceQueries.search(allParams as LRSearchRequest), + placeholderData: keepPreviousData, + select: (data) => { + const metadata = data.metadata + const offerors = Object.fromEntries( + data.results + .map((item) => item.offered_by) + .filter((value) => value && value.display_facet) + .map((value) => [value.code, value]), + ) + // only show offerors with display_facet set + metadata.aggregations.offered_by = + metadata.aggregations.offered_by.filter( + (value) => value && value.key in offerors, + ) + return { ...data, metadata } + }, + }) const { data: user } = useUserMe() const [mobileDrawerOpen, setMobileDrawerOpen] = React.useState(false) From c706d417e768d481688eaf59a75f044182f877e2 Mon Sep 17 00:00:00 2001 From: shankar ambady Date: Mon, 10 Nov 2025 14:03:31 -0500 Subject: [PATCH 04/18] regenerate spec --- frontends/api/src/generated/v0/api.ts | 18 ++++++++++++++++++ frontends/api/src/generated/v1/api.ts | 18 ++++++++++++++++++ openapi/specs/v0.yaml | 6 ++++++ openapi/specs/v1.yaml | 6 ++++++ 4 files changed, 48 insertions(+) diff --git a/frontends/api/src/generated/v0/api.ts b/frontends/api/src/generated/v0/api.ts index 49f4ba2a50..fd4558e580 100644 --- a/frontends/api/src/generated/v0/api.ts +++ b/frontends/api/src/generated/v0/api.ts @@ -2714,6 +2714,12 @@ export interface LearningResourceOfferor { * @memberof LearningResourceOfferor */ channel_url: string | null + /** + * + * @type {boolean} + * @memberof LearningResourceOfferor + */ + display_facet?: boolean } /** * Serializer for LearningResourceOfferor with all details @@ -2793,6 +2799,12 @@ export interface LearningResourceOfferorDetail { * @memberof LearningResourceOfferorDetail */ value_prop?: string + /** + * + * @type {boolean} + * @memberof LearningResourceOfferorDetail + */ + display_facet?: boolean } /** * Serializer for LearningResourceOfferor with all details @@ -2866,6 +2878,12 @@ export interface LearningResourceOfferorDetailRequest { * @memberof LearningResourceOfferorDetailRequest */ value_prop?: string + /** + * + * @type {boolean} + * @memberof LearningResourceOfferorDetailRequest + */ + display_facet?: boolean } /** * Serializer for LearningResourcePlatform diff --git a/frontends/api/src/generated/v1/api.ts b/frontends/api/src/generated/v1/api.ts index ce9ce600b1..a7d91871b8 100644 --- a/frontends/api/src/generated/v1/api.ts +++ b/frontends/api/src/generated/v1/api.ts @@ -2911,6 +2911,12 @@ export interface LearningResourceOfferor { * @memberof LearningResourceOfferor */ channel_url: string | null + /** + * + * @type {boolean} + * @memberof LearningResourceOfferor + */ + display_facet?: boolean } /** * Serializer for LearningResourceOfferor with all details @@ -2990,6 +2996,12 @@ export interface LearningResourceOfferorDetail { * @memberof LearningResourceOfferorDetail */ value_prop?: string + /** + * + * @type {boolean} + * @memberof LearningResourceOfferorDetail + */ + display_facet?: boolean } /** * Serializer for LearningResourceOfferor with basic details @@ -3009,6 +3021,12 @@ export interface LearningResourceOfferorRequest { * @memberof LearningResourceOfferorRequest */ name: string + /** + * + * @type {boolean} + * @memberof LearningResourceOfferorRequest + */ + display_facet?: boolean } /** * Serializer for LearningResourcePlatform diff --git a/openapi/specs/v0.yaml b/openapi/specs/v0.yaml index df688e73ff..5c23c5dbb1 100644 --- a/openapi/specs/v0.yaml +++ b/openapi/specs/v0.yaml @@ -3379,6 +3379,8 @@ components: type: string readOnly: true nullable: true + display_facet: + type: boolean required: - channel_url - code @@ -3435,6 +3437,8 @@ components: maxLength: 200 value_prop: type: string + display_facet: + type: boolean required: - channel_url - code @@ -3495,6 +3499,8 @@ components: maxLength: 200 value_prop: type: string + display_facet: + type: boolean required: - code - name diff --git a/openapi/specs/v1.yaml b/openapi/specs/v1.yaml index a48bc94032..a8ed037cdb 100644 --- a/openapi/specs/v1.yaml +++ b/openapi/specs/v1.yaml @@ -11626,6 +11626,8 @@ components: type: string readOnly: true nullable: true + display_facet: + type: boolean required: - channel_url - code @@ -11682,6 +11684,8 @@ components: maxLength: 200 value_prop: type: string + display_facet: + type: boolean required: - channel_url - code @@ -11698,6 +11702,8 @@ components: type: string minLength: 1 maxLength: 256 + display_facet: + type: boolean required: - code - name From 675b0fd8e12e7582191c5f49d027e513931f4d70 Mon Sep 17 00:00:00 2001 From: shankar ambady Date: Mon, 10 Nov 2025 14:12:15 -0500 Subject: [PATCH 05/18] check null --- .../main/src/page-components/SearchDisplay/SearchDisplay.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx b/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx index 86ab7b0548..cd601c1180 100644 --- a/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx +++ b/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx @@ -578,7 +578,7 @@ const SearchDisplay: React.FC = ({ data.results .map((item) => item.offered_by) .filter((value) => value && value.display_facet) - .map((value) => [value.code, value]), + .map((value) => [value?.code, value]), ) // only show offerors with display_facet set metadata.aggregations.offered_by = From b84c6e621138d28ce74217beb95e4e679ae235e6 Mon Sep 17 00:00:00 2001 From: shankar ambady Date: Mon, 10 Nov 2025 14:14:22 -0500 Subject: [PATCH 06/18] fix test --- learning_resources/serializers_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/learning_resources/serializers_test.py b/learning_resources/serializers_test.py index c153f0ce11..16e358eb41 100644 --- a/learning_resources/serializers_test.py +++ b/learning_resources/serializers_test.py @@ -524,6 +524,7 @@ def test_content_file_serializer(settings, expected_types, has_channels): "offered_by": { "name": content_file.run.learning_resource.offered_by.name, "code": content_file.run.learning_resource.offered_by.code, + "display_facet": True, "channel_url": frontend_absolute_url( f"/c/unit/{Channel.objects.get(unit_detail__unit=content_file.run.learning_resource.offered_by).name}/" ) From ccb01b43f6b3938b75ba30ae1335a9380dec883d Mon Sep 17 00:00:00 2001 From: shankar ambady Date: Mon, 10 Nov 2025 15:24:44 -0500 Subject: [PATCH 07/18] fixing test --- .../app-pages/SearchPage/SearchPage.test.tsx | 11 ++++++++ .../SearchDisplay/SearchDisplay.tsx | 25 +++++++++++++------ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx b/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx index 92a365a7de..e935cdd34b 100644 --- a/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx +++ b/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx @@ -583,13 +583,24 @@ describe("Search Page Tabs", () => { test("Facet 'Offered By' uses API response for names", async () => { const offerors = factories.learningResources.offerors({ count: 3 }) + + const resources = factories.learningResources.resources({ + count: 3, + }).results + + for (const [i, v] of resources.entries()) { + v.offered_by = offerors.results[i] + v.offered_by.display_facet = true + } setMockApiResponses({ offerors, search: { + results: resources, metadata: { aggregations: { offered_by: offerors.results.map((o, i) => ({ key: o.code, + display_facet: true, doc_count: 10 + i, })), }, diff --git a/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx b/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx index cd601c1180..0d6ac8e2b8 100644 --- a/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx +++ b/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx @@ -573,19 +573,30 @@ const SearchDisplay: React.FC = ({ ...learningResourceQueries.search(allParams as LRSearchRequest), placeholderData: keepPreviousData, select: (data) => { - const metadata = data.metadata + // Handle missing data gracefully + if (!data.metadata.aggregations.offered_by || data.results.length === 0) { + return data + } const offerors = Object.fromEntries( data.results .map((item) => item.offered_by) .filter((value) => value && value.display_facet) - .map((value) => [value?.code, value]), + .map((value) => [value.code, value]), ) + // only show offerors with display_facet set - metadata.aggregations.offered_by = - metadata.aggregations.offered_by.filter( - (value) => value && value.key in offerors, - ) - return { ...data, metadata } + return { + ...data, + metadata: { + ...data.metadata, + aggregations: { + ...data.metadata.aggregations, + offered_by: data.metadata.aggregations.offered_by.filter( + (value) => value && value.key in offerors, + ), + }, + }, + } }, }) const { data: user } = useUserMe() From 4eb43ae010f2f7bc7017f78827e3db9532f2c9ea Mon Sep 17 00:00:00 2001 From: shankar ambady Date: Mon, 10 Nov 2025 15:32:50 -0500 Subject: [PATCH 08/18] lint fix --- .../main/src/page-components/SearchDisplay/SearchDisplay.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx b/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx index 0d6ac8e2b8..52425f4b27 100644 --- a/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx +++ b/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx @@ -581,7 +581,7 @@ const SearchDisplay: React.FC = ({ data.results .map((item) => item.offered_by) .filter((value) => value && value.display_facet) - .map((value) => [value.code, value]), + .map((value) => [value?.code, value]), ) // only show offerors with display_facet set From 253fd322006e658944214897242a1efe5dc119db Mon Sep 17 00:00:00 2001 From: shankar ambady Date: Mon, 10 Nov 2025 15:49:52 -0500 Subject: [PATCH 09/18] adding test for offered by filter --- .../app-pages/SearchPage/SearchPage.test.tsx | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx b/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx index e935cdd34b..6d301407d3 100644 --- a/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx +++ b/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx @@ -600,7 +600,6 @@ test("Facet 'Offered By' uses API response for names", async () => { aggregations: { offered_by: offerors.results.map((o, i) => ({ key: o.code, - display_facet: true, doc_count: 10 + i, })), }, @@ -629,6 +628,56 @@ test("Facet 'Offered By' uses API response for names", async () => { expect(offeror2).toBeVisible() }) +test("Facet 'Offered By' only shows facets with 'display_facet' set to true", async () => { + const offerors = factories.learningResources.offerors({ count: 3 }) + + const resources = factories.learningResources.resources({ + count: 3, + }).results + + for (const [i, v] of resources.entries()) { + v.offered_by = offerors.results[i] + } + resources[0].offered_by.display_facet = true + resources[1].offered_by.display_facet = false + resources[2].offered_by.display_facet = false + + setMockApiResponses({ + offerors, + search: { + results: resources, + metadata: { + aggregations: { + offered_by: offerors.results.map((o, i) => ({ + key: o.code, + doc_count: 10 + i, + })), + }, + suggestions: [], + }, + }, + }) + renderWithProviders() + const showFacetButton = await screen.findByRole("button", { + name: /Offered By/i, + }) + + await user.click(showFacetButton) + + const offeror0 = await screen.findByRole("checkbox", { + name: `${offerors.results[0].name} 10`, + }) + const offeror1 = screen.queryByRole("checkbox", { + name: `${offerors.results[1].name} 11`, + }) + const offeror2 = screen.queryByRole("checkbox", { + name: `${offerors.results[2].name} 12`, + }) + expect(offeror0).toBeVisible() + expect(offeror1).not.toBeInTheDocument() + expect(offeror2).not.toBeInTheDocument() +}) + test("Set sort", async () => { setMockApiResponses({ search: { count: 137 } }) From 261c1b8b5c6139409de73ec7ae10b1cc488691ac Mon Sep 17 00:00:00 2001 From: shankar ambady Date: Mon, 10 Nov 2025 15:57:59 -0500 Subject: [PATCH 10/18] lint fix --- frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx b/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx index 6d301407d3..13ee5a1fe0 100644 --- a/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx +++ b/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx @@ -638,9 +638,9 @@ test("Facet 'Offered By' only shows facets with 'display_facet' set to true", as for (const [i, v] of resources.entries()) { v.offered_by = offerors.results[i] } - resources[0].offered_by.display_facet = true - resources[1].offered_by.display_facet = false - resources[2].offered_by.display_facet = false + resources[0]?.offered_by.display_facet = true + resources[1]?.offered_by.display_facet = false + resources[2]?.offered_by.display_facet = false setMockApiResponses({ offerors, From d6e0b8a65126a58e84808846c677b037451b4b0d Mon Sep 17 00:00:00 2001 From: shankar ambady Date: Mon, 10 Nov 2025 16:13:15 -0500 Subject: [PATCH 11/18] lint fix --- frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx b/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx index 13ee5a1fe0..12d28bae60 100644 --- a/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx +++ b/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx @@ -638,9 +638,9 @@ test("Facet 'Offered By' only shows facets with 'display_facet' set to true", as for (const [i, v] of resources.entries()) { v.offered_by = offerors.results[i] } - resources[0]?.offered_by.display_facet = true - resources[1]?.offered_by.display_facet = false - resources[2]?.offered_by.display_facet = false + resources[0]?.offered_by?.display_facet = true + resources[1]?.offered_by?.display_facet = false + resources[2]?.offered_by?.display_facet = false setMockApiResponses({ offerors, From e4b9c0140945d00d57f7c73ba5c3cd6bb12dbf77 Mon Sep 17 00:00:00 2001 From: shankar ambady Date: Mon, 10 Nov 2025 16:21:57 -0500 Subject: [PATCH 12/18] lint fix --- frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx b/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx index 12d28bae60..86d5c78606 100644 --- a/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx +++ b/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx @@ -638,9 +638,9 @@ test("Facet 'Offered By' only shows facets with 'display_facet' set to true", as for (const [i, v] of resources.entries()) { v.offered_by = offerors.results[i] } - resources[0]?.offered_by?.display_facet = true - resources[1]?.offered_by?.display_facet = false - resources[2]?.offered_by?.display_facet = false + resources[0]!.offered_by!.display_facet = true + resources[1]!.offered_by!.display_facet = false + resources[2]!.offered_by!.display_facet = false setMockApiResponses({ offerors, From efbd257ac22f9bd16f054a4d36b067acdf5abb16 Mon Sep 17 00:00:00 2001 From: shankar ambady Date: Mon, 10 Nov 2025 17:08:26 -0500 Subject: [PATCH 13/18] fix flaky test --- vector_search/tasks.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/vector_search/tasks.py b/vector_search/tasks.py index b9e724e695..36d475e8e8 100644 --- a/vector_search/tasks.py +++ b/vector_search/tasks.py @@ -381,7 +381,8 @@ def embeddings_healthcheck(): """ Check for missing embeddings and summaries in Qdrant and log warnings to Sentry """ - remaining_content_files = [] + + remaining_content_file_ids = [] remaining_resources = [] resource_point_ids = {} all_resources = LearningResource.objects.filter( @@ -405,10 +406,14 @@ def embeddings_healthcheck(): ) content_file_point_ids[point_id] = {"key": cf.key, "id": cf.id} for batch in chunks(content_file_point_ids.keys(), chunk_size=200): - remaining_content_files.extend( - filter_existing_qdrant_points_by_ids( - batch, collection_name=CONTENT_FILES_COLLECTION_NAME - ) + remaining_content_files = filter_existing_qdrant_points_by_ids( + batch, collection_name=CONTENT_FILES_COLLECTION_NAME + ) + remaining_content_file_ids.extend( + [ + content_file_point_ids.get(p, {}).get("id") + for p in remaining_content_files + ] ) for batch in chunks( @@ -422,16 +427,13 @@ def embeddings_healthcheck(): ) ) - remaining_content_file_ids = [ - content_file_point_ids.get(p, {}).get("id") for p in remaining_content_files - ] remaining_resource_ids = [ resource_point_ids.get(p, {}).get("id") for p in remaining_resources ] missing_summaries = _missing_summaries() log.info( "Embeddings healthcheck found %d missing content file embeddings", - len(remaining_content_files), + len(remaining_content_file_ids), ) log.info( "Embeddings healthcheck found %d missing resource embeddings", @@ -442,12 +444,12 @@ def embeddings_healthcheck(): len(missing_summaries), ) - if len(remaining_content_files) > 0: + if len(remaining_content_file_ids) > 0: _sentry_healthcheck_log( "embeddings", "missing_content_file_embeddings", { - "count": len(remaining_content_files), + "count": len(remaining_content_file_ids), "ids": remaining_content_file_ids, "run_ids": set( ContentFile.objects.filter( @@ -455,7 +457,7 @@ def embeddings_healthcheck(): ).values_list("run__run_id", flat=True)[:100] ), }, - f"Warning: {len(remaining_content_files)} missing content file " + f"Warning: {len(remaining_content_file_ids)} missing content file " "embeddings detected", ) From 1178ed6b4ca7c71d59a5c3d9255515cf97378140 Mon Sep 17 00:00:00 2001 From: shankar ambady Date: Wed, 12 Nov 2025 11:21:54 -0500 Subject: [PATCH 14/18] removed use of 'in' operator --- .../main/src/page-components/SearchDisplay/SearchDisplay.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx b/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx index 52425f4b27..905fd5b1fe 100644 --- a/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx +++ b/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx @@ -592,7 +592,7 @@ const SearchDisplay: React.FC = ({ aggregations: { ...data.metadata.aggregations, offered_by: data.metadata.aggregations.offered_by.filter( - (value) => value && value.key in offerors, + (value) => value && Object.keys(offerors).includes(value.key), ), }, }, From ffbd6c418356a64e238b2dc0bb60e353b3d11296 Mon Sep 17 00:00:00 2001 From: shankar ambady Date: Wed, 12 Nov 2025 13:18:17 -0500 Subject: [PATCH 15/18] remove display_facet from search index --- learning_resources_search/constants.py | 1 - 1 file changed, 1 deletion(-) diff --git a/learning_resources_search/constants.py b/learning_resources_search/constants.py index 5971041419..e1e0bc0207 100644 --- a/learning_resources_search/constants.py +++ b/learning_resources_search/constants.py @@ -194,7 +194,6 @@ class FilterConfig: "properties": { "code": {"type": "keyword"}, "name": {"type": "keyword"}, - "display_facet": {"type": "boolean"}, "channel_url": {"type": "keyword"}, }, }, From f1828195912dac9cb342e4a94412b57a81fa0271 Mon Sep 17 00:00:00 2001 From: shankar ambady Date: Wed, 12 Nov 2025 13:44:11 -0500 Subject: [PATCH 16/18] get offeror directly from object in db --- .../SearchDisplay/SearchDisplay.tsx | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx b/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx index 905fd5b1fe..6a1c64e487 100644 --- a/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx +++ b/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx @@ -19,7 +19,7 @@ import { childCheckboxStyles, VisuallyHidden, } from "@mitodl/smoot-design" - +import { keyBy } from "lodash" import { RiCloseLine, RiArrowLeftLine, @@ -28,13 +28,15 @@ import { RiArrowUpSLine, RiArrowDownSLine, } from "@remixicon/react" - +import { + useOfferorsList, + learningResourceQueries, +} from "api/hooks/learningResources" import { LearningResourcesSearchApiLearningResourcesSearchRetrieveRequest as LRSearchRequest, ResourceCategoryEnum, SearchModeEnumDescriptions, } from "api" -import { learningResourceQueries } from "api/hooks/learningResources" import { keepPreviousData, useQuery } from "@tanstack/react-query" import { useAdminSearchParams } from "api/hooks/adminSearchParams" import { @@ -569,22 +571,25 @@ const SearchDisplay: React.FC = ({ facetNames, page, ]) + const offerorsQuery = useOfferorsList() + const offerors = useMemo(() => { + return keyBy(offerorsQuery.data?.results ?? [], (o) => o.code) + }, [offerorsQuery.data?.results]) + const { data, isLoading, isFetching } = useQuery({ ...learningResourceQueries.search(allParams as LRSearchRequest), placeholderData: keepPreviousData, select: (data) => { // Handle missing data gracefully - if (!data.metadata.aggregations.offered_by || data.results.length === 0) { + if (data.results.length === 0) { return data } - const offerors = Object.fromEntries( - data.results - .map((item) => item.offered_by) - .filter((value) => value && value.display_facet) - .map((value) => [value?.code, value]), - ) // only show offerors with display_facet set + const displayOfferors = Object.values(offerors) + .filter((value) => value.code && value.display_facet) + .map((value) => value?.code) + return { ...data, metadata: { @@ -592,7 +597,7 @@ const SearchDisplay: React.FC = ({ aggregations: { ...data.metadata.aggregations, offered_by: data.metadata.aggregations.offered_by.filter( - (value) => value && Object.keys(offerors).includes(value.key), + (value) => value && displayOfferors.includes(value.key), ), }, }, From 2359b36fa5eedadff88e486962c84d9a9924e8d5 Mon Sep 17 00:00:00 2001 From: shankar ambady Date: Wed, 12 Nov 2025 13:56:52 -0500 Subject: [PATCH 17/18] fix tests --- .../app-pages/SearchPage/SearchPage.test.tsx | 19 +++++++++---------- .../SearchDisplay/SearchDisplay.tsx | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx b/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx index 86d5c78606..f68e097a16 100644 --- a/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx +++ b/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx @@ -583,15 +583,13 @@ describe("Search Page Tabs", () => { test("Facet 'Offered By' uses API response for names", async () => { const offerors = factories.learningResources.offerors({ count: 3 }) - + for (const offeror of offerors.results) { + offeror.display_facet = true + } const resources = factories.learningResources.resources({ count: 3, }).results - for (const [i, v] of resources.entries()) { - v.offered_by = offerors.results[i] - v.offered_by.display_facet = true - } setMockApiResponses({ offerors, search: { @@ -635,15 +633,15 @@ test("Facet 'Offered By' only shows facets with 'display_facet' set to true", as count: 3, }).results - for (const [i, v] of resources.entries()) { + for (const [i, v] of offerors.results.entries()) { v.offered_by = offerors.results[i] } - resources[0]!.offered_by!.display_facet = true - resources[1]!.offered_by!.display_facet = false - resources[2]!.offered_by!.display_facet = false + + offerors.results[0].display_facet = true + offerors.results[1]!.display_facet = false + offerors.results[2]!.display_facet = false setMockApiResponses({ - offerors, search: { results: resources, metadata: { @@ -656,6 +654,7 @@ test("Facet 'Offered By' only shows facets with 'display_facet' set to true", as suggestions: [], }, }, + offerors: offerors, }) renderWithProviders() const showFacetButton = await screen.findByRole("button", { diff --git a/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx b/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx index 6a1c64e487..e8c6927dab 100644 --- a/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx +++ b/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx @@ -581,7 +581,7 @@ const SearchDisplay: React.FC = ({ placeholderData: keepPreviousData, select: (data) => { // Handle missing data gracefully - if (data.results.length === 0) { + if (!data.metadata.aggregations.offered_by || data.results.length === 0) { return data } From 5f4135c528d973de6d03ffec42fdf391432db326 Mon Sep 17 00:00:00 2001 From: shankar ambady Date: Wed, 12 Nov 2025 14:15:05 -0500 Subject: [PATCH 18/18] fix typecheck --- frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx b/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx index f68e097a16..16099f0853 100644 --- a/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx +++ b/frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx @@ -633,10 +633,6 @@ test("Facet 'Offered By' only shows facets with 'display_facet' set to true", as count: 3, }).results - for (const [i, v] of offerors.results.entries()) { - v.offered_by = offerors.results[i] - } - offerors.results[0].display_facet = true offerors.results[1]!.display_facet = false offerors.results[2]!.display_facet = false