diff --git a/lib/dpul_collections/indexing_pipeline/figgy/hydration_cache_entry.ex b/lib/dpul_collections/indexing_pipeline/figgy/hydration_cache_entry.ex index 6b211663..c592a65c 100644 --- a/lib/dpul_collections/indexing_pipeline/figgy/hydration_cache_entry.ex +++ b/lib/dpul_collections/indexing_pipeline/figgy/hydration_cache_entry.ex @@ -32,10 +32,38 @@ defmodule DpulCollections.IndexingPipeline.Figgy.HydrationCacheEntry do years_is: extract_years(data), display_date_s: format_date(metadata), page_count_i: page_count(metadata), - image_service_urls_ss: image_service_urls(metadata, related_data) + image_service_urls_ss: image_service_urls(metadata, related_data), + primary_thumbnail_service_url_s: primary_thumbnail_service_url(metadata, related_data) } end + defp primary_thumbnail_service_url( + %{"thumbnail_id" => thumbnail_id} = metadata, + %{"member_ids" => member_data} = related_data + ) + when is_list(thumbnail_id) do + thumbnail_member = + thumbnail_id + |> Enum.at(0, %{}) + |> Map.get("id") + |> then(fn id -> member_data[id] end) + + if is_nil(thumbnail_member) do + # When thumbnail id does not correspond to a related FileSet, + # use the first image service url + image_service_urls(metadata, related_data) + |> Enum.at(0) + else + extract_service_url(thumbnail_member) + end + end + + defp primary_thumbnail_service_url(metadata, related_data) do + # When the thumbnail id is not set, use first image service url + image_service_urls(metadata, related_data) + |> Enum.at(0) + end + defp image_service_urls(%{"member_ids" => member_ids}, related_data) do member_ids |> Enum.map(&extract_service_url(&1, related_data)) diff --git a/lib/dpul_collections/item.ex b/lib/dpul_collections/item.ex index 2d6d8d74..344708a0 100644 --- a/lib/dpul_collections/item.ex +++ b/lib/dpul_collections/item.ex @@ -8,6 +8,7 @@ defmodule DpulCollections.Item do :page_count, :url, :image_service_urls, + :primary_thumbnail_service_url, :description ] @@ -25,6 +26,7 @@ defmodule DpulCollections.Item do page_count: doc["page_count_i"], url: generate_url(id, slug), image_service_urls: doc["image_service_urls_ss"] || [], + primary_thumbnail_service_url: doc["primary_thumbnail_service_url_s"], description: doc["description_txtm"] || [] } end diff --git a/lib/dpul_collections/solr.ex b/lib/dpul_collections/solr.ex index 3a49e0f4..dffbb1e5 100644 --- a/lib/dpul_collections/solr.ex +++ b/lib/dpul_collections/solr.ex @@ -19,7 +19,8 @@ defmodule DpulCollections.Solr do "page_count_i", "detectlang_ss", "slug_s", - "image_service_urls_ss" + "image_service_urls_ss", + "primary_thumbnail_service_url_s" ] @spec query(map(), String.t()) :: map() diff --git a/lib/dpul_collections_web/live/item_live.ex b/lib/dpul_collections_web/live/item_live.ex index a1caa5eb..75a0daa6 100644 --- a/lib/dpul_collections_web/live/item_live.ex +++ b/lib/dpul_collections_web/live/item_live.ex @@ -38,7 +38,7 @@ defmodule DpulCollectionsWeb.ItemLive do
main image display
<.thumbs - :for={{thumb, thumb_num} <- Enum.with_index(@item.image_service_urls)} + :for={{thumb, thumb_num} <- thumbnail_service_urls(5, @item)} :if={@item.page_count} thumb={thumb} thumb_num={thumb_num} @@ -343,4 +343,28 @@ defmodule DpulCollectionsWeb.SearchLive do type == :post -> [{"...", false}, {page, false}] end end + + defp thumbnail_service_urls(max_thumbnails, item) do + thumbnail_service_urls( + max_thumbnails, + item.image_service_urls, + item.primary_thumbnail_service_url + ) + end + + defp thumbnail_service_urls(max_thumbnails, image_service_urls, nil) do + # Truncate image service urls to max value + image_service_urls + |> Enum.slice(0, max_thumbnails) + |> Enum.with_index() + end + + defp thumbnail_service_urls(max_thumbnails, image_service_urls, primary_thumbnail_service_url) do + # Move thumbnail url to front of list and then truncate to max value + image_service_urls + |> Enum.filter(&(&1 != primary_thumbnail_service_url)) + |> List.insert_at(0, primary_thumbnail_service_url) + |> Enum.slice(0, max_thumbnails) + |> Enum.with_index() + end end diff --git a/test/dpul_collections/indexing_pipeline/figgy/hydration_cache_entry_test.exs b/test/dpul_collections/indexing_pipeline/figgy/hydration_cache_entry_test.exs index 9f5d333f..3fe7f4a1 100644 --- a/test/dpul_collections/indexing_pipeline/figgy/hydration_cache_entry_test.exs +++ b/test/dpul_collections/indexing_pipeline/figgy/hydration_cache_entry_test.exs @@ -68,7 +68,8 @@ defmodule DpulCollections.IndexingPipeline.Figgy.HydrationCacheEntryTest do "internal_resource" => "EphemeraFolder", "metadata" => %{ "member_ids" => [%{"id" => "1"}], - "title" => ["test title 4"] + "title" => ["test title 4"], + "thumbnail_id" => [%{"id" => "1"}] } } }) @@ -79,6 +80,115 @@ defmodule DpulCollections.IndexingPipeline.Figgy.HydrationCacheEntryTest do assert doc[:image_service_urls_ss] == [ "https://iiif-cloud.princeton.edu/iiif/2/0c%2Fff%2F89%2F0cff895a01ea48959c3da8c6eaab4017%2Fintermediate_file" ] + + # Has thumbnail url + assert doc[:primary_thumbnail_service_url_s] == + "https://iiif-cloud.princeton.edu/iiif/2/0c%2Fff%2F89%2F0cff895a01ea48959c3da8c6eaab4017%2Fintermediate_file" + end + + test "uses first image service url when there is no thumbnail_id property" do + {:ok, entry} = + IndexingPipeline.write_hydration_cache_entry(%{ + cache_version: 0, + record_id: "0cff895a-01ea-4895-9c3d-a8c6eaab4013", + source_cache_order: ~U[2018-03-09 20:19:35.465203Z], + related_data: %{ + "member_ids" => %{ + "1" => %{ + "internal_resource" => "FileSet", + "id" => "9ad621a7b-01ea-4895-9c3d-a8c6eaab4013", + "metadata" => %{ + "file_metadata" => [ + %{ + "id" => %{"id" => "0cff895a-01ea-4895-9c3d-a8c6eaab4017"}, + "internal_resource" => "FileMetadata", + "mime_type" => ["image/tiff"], + "use" => [%{"@id" => "http://pcdm.org/use#ServiceFile"}] + } + ] + } + } + } + }, + data: %{ + "id" => "0cff895a-01ea-4895-9c3d-a8c6eaab4013", + "internal_resource" => "EphemeraFolder", + "metadata" => %{ + "member_ids" => [%{"id" => "1"}], + "title" => ["test title 4"] + } + } + }) + + doc = HydrationCacheEntry.to_solr_document(entry) + + assert doc[:primary_thumbnail_service_url_s] == + "https://iiif-cloud.princeton.edu/iiif/2/0c%2Fff%2F89%2F0cff895a01ea48959c3da8c6eaab4017%2Fintermediate_file" + end + + test "uses first image service url when thumbnail id does not point to related FileSet" do + {:ok, entry} = + IndexingPipeline.write_hydration_cache_entry(%{ + cache_version: 0, + record_id: "0cff895a-01ea-4895-9c3d-a8c6eaab4013", + source_cache_order: ~U[2018-03-09 20:19:35.465203Z], + related_data: %{ + "member_ids" => %{ + "1" => %{ + "internal_resource" => "FileSet", + "id" => "9ad621a7b-01ea-4895-9c3d-a8c6eaab4013", + "metadata" => %{ + "file_metadata" => [ + %{ + "id" => %{"id" => "0cff895a-01ea-4895-9c3d-a8c6eaab4017"}, + "internal_resource" => "FileMetadata", + "mime_type" => ["image/tiff"], + "use" => [%{"@id" => "http://pcdm.org/use#ServiceFile"}] + } + ] + } + } + } + }, + data: %{ + "id" => "0cff895a-01ea-4895-9c3d-a8c6eaab4013", + "internal_resource" => "EphemeraFolder", + "metadata" => %{ + "member_ids" => [%{"id" => "1"}], + "title" => ["test title 4"], + "thumbnail_id" => [%{"id" => "9"}] + } + } + }) + + doc = HydrationCacheEntry.to_solr_document(entry) + + assert doc[:primary_thumbnail_service_url_s] == + "https://iiif-cloud.princeton.edu/iiif/2/0c%2Fff%2F89%2F0cff895a01ea48959c3da8c6eaab4017%2Fintermediate_file" + end + + test "does not add a thumbnail service url when there are no image members" do + {:ok, entry} = + IndexingPipeline.write_hydration_cache_entry(%{ + cache_version: 0, + record_id: "0cff895a-01ea-4895-9c3d-a8c6eaab4013", + source_cache_order: ~U[2018-03-09 20:19:35.465203Z], + related_data: %{ + "member_ids" => %{} + }, + data: %{ + "id" => "0cff895a-01ea-4895-9c3d-a8c6eaab4013", + "internal_resource" => "EphemeraFolder", + "metadata" => %{ + "member_ids" => [], + "title" => ["test title 4"] + } + } + }) + + doc = HydrationCacheEntry.to_solr_document(entry) + + assert doc[:primary_thumbnail_service_url_s] == nil end test "can handle when members do not have the correct file metadata type" do diff --git a/test/dpul_collections/indexing_pipeline/integration/full_integration_test.exs b/test/dpul_collections/indexing_pipeline/integration/full_integration_test.exs index 68b72741..e86abbdc 100644 --- a/test/dpul_collections/indexing_pipeline/integration/full_integration_test.exs +++ b/test/dpul_collections/indexing_pipeline/integration/full_integration_test.exs @@ -196,5 +196,8 @@ defmodule DpulCollections.IndexingPipeline.FiggyFullIntegrationTest do "https://iiif-cloud.princeton.edu/iiif/2/5e%2F24%2Faf%2F5e24aff45b2e4c9aaba3f05321d1c797%2Fintermediate_file" | _rest ] = document["image_service_urls_ss"] + + assert "https://iiif-cloud.princeton.edu/iiif/2/5e%2F24%2Faf%2F5e24aff45b2e4c9aaba3f05321d1c797%2Fintermediate_file" = + document["primary_thumbnail_service_url_s"] end end diff --git a/test/dpul_collections_web/live/item_live_test.exs b/test/dpul_collections_web/live/item_live_test.exs index 72ae8873..3f05d990 100644 --- a/test/dpul_collections_web/live/item_live_test.exs +++ b/test/dpul_collections_web/live/item_live_test.exs @@ -19,6 +19,7 @@ defmodule DpulCollectionsWeb.ItemLiveTest do "https://example.com/iiif/2/image1", "https://example.com/iiif/2/image2" ], + primary_thumbnail_service_url_s: "https://example.com/iiif/2/image2", description_txtm: ["This is a test description"] }, %{ @@ -29,7 +30,8 @@ defmodule DpulCollectionsWeb.ItemLiveTest do image_service_urls_ss: [ "https://example.com/iiif/2/image1", "https://example.com/iiif/2/image2" - ] + ], + primary_thumbnail_service_url_s: "https://example.com/iiif/2/image1" }, %{ id: 3, @@ -39,7 +41,8 @@ defmodule DpulCollectionsWeb.ItemLiveTest do image_service_urls_ss: [ "https://example.com/iiif/2/image1", "https://example.com/iiif/2/image2" - ] + ], + primary_thumbnail_service_url_s: "https://example.com/iiif/2/image1" } ], active_collection() @@ -108,10 +111,10 @@ defmodule DpulCollectionsWeb.ItemLiveTest do "Download" ) - # Large thumbnail renders + # Large thumbnail renders using thumbnail service url assert view |> has_element?( - ".primary-thumbnail img[src='https://example.com/iiif/2/image1/full/525,800/0/default.jpg']" + ".primary-thumbnail img[src='https://example.com/iiif/2/image2/full/525,800/0/default.jpg']" ) assert view diff --git a/test/dpul_collections_web/live/search_live_test.exs b/test/dpul_collections_web/live/search_live_test.exs index 0e7e9c51..90aa8c22 100644 --- a/test/dpul_collections_web/live/search_live_test.exs +++ b/test/dpul_collections_web/live/search_live_test.exs @@ -53,27 +53,38 @@ defmodule DpulCollectionsWeb.SearchLiveTest do end test "GET /search renders thumbnails for each resource", %{conn: conn} do - {:ok, view, _html} = live(conn, "/search?") + {:ok, _view, html} = live(conn, "/search?") - assert view - |> has_element?( - "#item-1 img[src='https://example.com/iiif/2/image1/square/350,350/0/default.jpg']" - ) + {:ok, document} = + html + |> Floki.parse_document() - assert view - |> has_element?( - "#item-1 img[src='https://example.com/iiif/2/image2/square/350,350/0/default.jpg']" - ) + # There should be a maximum of 5 thumbnails on the search results page + assert document |> Floki.find("#item-1 > div > img") |> Enum.count() == 5 - assert view - |> has_element?( - "#item-2 img[src='https://example.com/iiif/2/image1/square/350,350/0/default.jpg']" - ) + # Odd numbered documents in test data do not have a thumbnail id + # so the order of thumbnails should be the same as the image member order + assert document + |> Floki.attribute("#item-1 > div > :first-child", "src") == [ + "https://example.com/iiif/2/image1/square/350,350/0/default.jpg" + ] - assert view - |> has_element?( - "#item-2 img[src='https://example.com/iiif/2/image2/square/350,350/0/default.jpg']" - ) + assert document + |> Floki.attribute("#item-1 > div > :nth-child(2)", "src") == [ + "https://example.com/iiif/2/image2/square/350,350/0/default.jpg" + ] + + # Even numbered documents in test data have a thumbnail id so the order + # of thumbnails should be different from the image member order + assert document + |> Floki.attribute("#item-2 > div > :first-child", "src") == [ + "https://example.com/iiif/2/image2/square/350,350/0/default.jpg" + ] + + assert document + |> Floki.attribute("#item-2 > div > :nth-child(2)", "src") == [ + "https://example.com/iiif/2/image1/square/350,350/0/default.jpg" + ] end test "searching filters results", %{conn: conn} do diff --git a/test/support/solr_test_support.ex b/test/support/solr_test_support.ex index 6f9a88a8..ff4ae6d0 100644 --- a/test/support/solr_test_support.ex +++ b/test/support/solr_test_support.ex @@ -2,7 +2,17 @@ defmodule SolrTestSupport do def mock_solr_documents(count \\ 100) do for n <- 1..count do date = 2025 - n - page_count = Enum.random(1..10) + + # Equals the number of image service urls + page_count = 7 + + # Assign thumbnail urls to even numbered documents. + # Used for testing thumbnail rendering order + thumbnail_url = + cond do + rem(n, 2) == 0 -> "https://example.com/iiif/2/image2" + true -> nil + end %{ id: n, @@ -12,8 +22,14 @@ defmodule SolrTestSupport do page_count_i: page_count, image_service_urls_ss: [ "https://example.com/iiif/2/image1", - "https://example.com/iiif/2/image2" - ] + "https://example.com/iiif/2/image2", + "https://example.com/iiif/2/image3", + "https://example.com/iiif/2/image4", + "https://example.com/iiif/2/image5", + "https://example.com/iiif/2/image6", + "https://example.com/iiif/2/image7" + ], + primary_thumbnail_service_url_s: thumbnail_url } end end