Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: search textGranularity considers all canvases #2517

Merged
merged 4 commits into from
Dec 16, 2024
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
29 changes: 23 additions & 6 deletions packages/portal/src/composables/itemMediaPresentation.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { computed, ref } from 'vue';
import uniq from 'lodash/uniq';

import EuropeanaMediaAnnotationList from '@/utils/europeana/media/AnnotationList.js';
import EuropeanaMediaPresentation from '@/utils/europeana/media/Presentation.js';
Expand Down Expand Up @@ -71,6 +72,15 @@ const hasSearchService = computed(() => {
return !!searchServiceUri.value;
});

const searchTextGranularity = computed(() => {
const granularities = uniq(
canvases.value?.map((canvas) => canvas.annotations?.[0]?.textGranularity)
.filter(Boolean)
.flat()// || []
);
return granularities.includes('line') ? 'line' : granularities[0];
});

const resource = computed(() => {
return canvas.value?.resource;
});
Expand Down Expand Up @@ -105,11 +115,13 @@ const fetchAnnotations = async(uri, { params = {} } = {}) => {
}

// TODO: make into a new computed?
let textGranularity;
if (Array.isArray(annotationTextGranularity.value)) {
textGranularity = annotationTextGranularity.value.includes('line') ? 'line' : annotationTextGranularity.value[0];
} else {
textGranularity = annotationTextGranularity.value;
let textGranularity = params.textGranularity;
if (!textGranularity) {
if (Array.isArray(annotationTextGranularity.value)) {
textGranularity = annotationTextGranularity.value.includes('line') ? 'line' : annotationTextGranularity.value[0];
} else {
textGranularity = annotationTextGranularity.value;
}
}

return await EuropeanaMediaAnnotationList.from(uri, { params: { textGranularity, ...params } });
Expand Down Expand Up @@ -160,7 +172,10 @@ const searchAnnotations = async(query) => {
annotationSearchResults.value = [];
annotationSearchHits.value = [];
} else {
const list = await fetchAnnotations(searchServiceUri.value, { params: { query } });
const list = await fetchAnnotations(
searchServiceUri.value,
{ params: { query, textGranularity: searchTextGranularity.value } }
);
annotationSearchResults.value = list.items;
annotationSearchHits.value = list.hits || [];
}
Expand Down Expand Up @@ -191,6 +206,7 @@ export default function useItemMediaPresentation() {
annotationTextGranularity,
activeAnnotation,
canvas,
canvases,
fetchAnnotations,
fetchCanvasAnnotations,
fetchPresentation,
Expand All @@ -205,6 +221,7 @@ export default function useItemMediaPresentation() {
presentation,
searchAnnotations,
searchServiceUri,
searchTextGranularity,
setActiveAnnotation,
setHoveredAnnotation,
setPage,
Expand Down
1 change: 1 addition & 0 deletions packages/portal/src/utils/europeana/media/Annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export default class EuropeanaMediaAnnotation extends Base {
const parsed = {
id: data.id, // TODO: bloats size of data; how to alleviate?
body: data.body || data.resource,
motivation: data.motivation,
target: data.target || data.on
};

Expand Down
113 changes: 111 additions & 2 deletions packages/portal/tests/unit/composables/itemMediaPresentation.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,27 @@ const listResponseData = {
}
}]
};
const searchPath = '/presentation/123/abc/search';
const searchUri = `${origin}${searchPath}`;
const searchResponseData = {
id: searchUri,
type: 'AnnotationPage',
items: [
{
id: 'https://iiif.example.org/annotation/123/abc',
type: 'Annotation',
motivation: 'transcribing'
}
],
hits: [
{
type: 'Hit',
annotations: [
'https://iiif.example.org/annotation/123/abc'
]
}
]
};
const manifestPath = '/presentation/123/abc/manifest';
const manifestUri = `${origin}${manifestPath}`;
const manifestResponseData = {
Expand All @@ -37,7 +58,7 @@ const manifestResponseData = {
service: [
{
'@context': 'http://iiif.io/api/search/1/context.json',
id: 'https://iiif.example.org/presentation/123/abc/search',
id: searchUri,
profile: 'http://iiif.io/api/search/1/search'
}
],
Expand Down Expand Up @@ -80,7 +101,10 @@ const presentationValue = new EuropeanaMediaPresentation({
],
resource: {}
}
]
],
search: {
id: searchUri
}
});

describe('useItemMediaPresentation', () => {
Expand Down Expand Up @@ -194,6 +218,91 @@ describe('useItemMediaPresentation', () => {
});
});

describe('searchTextGranularity', () => {
it('picks "line" granularity from canvas annotations if available', () => {
const { searchTextGranularity, presentation } = useItemMediaPresentation();
presentation.value = new EuropeanaMediaPresentation({
canvases: [
{ annotations: [{ textGranularity: ['page'] }] },
{ annotations: [{ textGranularity: ['block', 'line'] }] }
]
});

const granularity = searchTextGranularity.value;

expect(granularity).toBe('line');
});

it('picks first granularity from canvas annotations if "line" not available', () => {
const { searchTextGranularity, presentation } = useItemMediaPresentation();
presentation.value = new EuropeanaMediaPresentation({
canvases: [
{ annotations: [{ textGranularity: ['page'] }] },
{ annotations: [{ textGranularity: ['block'] }] }
]
});

const granularity = searchTextGranularity.value;

expect(granularity).toBe('page');
});

it('is undefined if no canvas annotation granularities', () => {
const { searchTextGranularity, presentation } = useItemMediaPresentation();
presentation.value = new EuropeanaMediaPresentation({
canvases: [
{ annotations: [{}] },
{ annotations: [{}] }
]
});

const granularity = searchTextGranularity.value;

expect(granularity).toBeUndefined();
});
});

describe('searchAnnotations', () => {
const textGranularity = 'line';

describe('when there is a query', () => {
const query = 'dublin';

beforeEach(() => {
nock(origin).get(searchPath).query({ query, textGranularity }).reply(200, searchResponseData);
});

it('runs search for query and text granularity', async() => {
const { presentation, searchAnnotations } = useItemMediaPresentation();
presentation.value = new EuropeanaMediaPresentation({
canvases: [
{ annotations: [{ textGranularity: [textGranularity] }] }
],
search: { id: searchUri }
});

await searchAnnotations(query);

expect(nock.isDone()).toBe(true);
});

it('stores results and hits', async() => {
const { annotationSearchHits, annotationSearchResults, presentation, searchAnnotations } = useItemMediaPresentation();
presentation.value = new EuropeanaMediaPresentation({
canvases: [
{ annotations: [{ textGranularity: [textGranularity] }] }
],
search: { id: searchUri }
});

await searchAnnotations(query);

expect(annotationSearchHits.value).toEqual(searchResponseData.hits);
expect(annotationSearchResults.value[0].id).toBe(searchResponseData.items[0].id);
});
});
});

describe('pageForAnnotationTarget', () => {
it('finds the page/canvas number for an annotation target', () => {
const { pageForAnnotationTarget, presentation } = useItemMediaPresentation();
Expand Down
Loading