Added upper limit to browse by date

This commit is contained in:
Michael Spalti
2023-03-28 13:18:34 -07:00
parent 1ddeeed081
commit 5b33c49ccc
3 changed files with 66 additions and 29 deletions

View File

@@ -22,6 +22,7 @@ import { PaginationService } from '../../core/pagination/pagination.service';
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
import { APP_CONFIG } from '../../../config/app-config.interface'; import { APP_CONFIG } from '../../../config/app-config.interface';
import { environment } from '../../../environments/environment'; import { environment } from '../../../environments/environment';
import { SortDirection } from '../../core/cache/models/sort-options.model';
describe('BrowseByDatePageComponent', () => { describe('BrowseByDatePageComponent', () => {
let comp: BrowseByDatePageComponent; let comp: BrowseByDatePageComponent;
@@ -49,12 +50,22 @@ describe('BrowseByDatePageComponent', () => {
] ]
} }
}); });
const lastItem = Object.assign(new Item(), {
id: 'last-item-id',
metadata: {
'dc.date.issued': [
{
value: '1960-01-01'
}
]
}
});
const mockBrowseService = { const mockBrowseService = {
getBrowseEntriesFor: (options: BrowseEntrySearchOptions) => toRemoteData([]), getBrowseEntriesFor: (options: BrowseEntrySearchOptions) => toRemoteData([]),
getBrowseItemsFor: (value: string, options: BrowseEntrySearchOptions) => toRemoteData([firstItem]), getBrowseItemsFor: (value: string, options: BrowseEntrySearchOptions) => toRemoteData([firstItem]),
getFirstItemFor: () => createSuccessfulRemoteDataObject$(firstItem) getFirstItemFor: (definition: string, scope?: string, sortDirection?: SortDirection) => null
}; };
const mockDsoService = { const mockDsoService = {
findById: () => createSuccessfulRemoteDataObject$(mockCommunity) findById: () => createSuccessfulRemoteDataObject$(mockCommunity)
@@ -91,9 +102,14 @@ describe('BrowseByDatePageComponent', () => {
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(BrowseByDatePageComponent); fixture = TestBed.createComponent(BrowseByDatePageComponent);
const browseService = fixture.debugElement.injector.get(BrowseService);
spyOn(browseService, 'getFirstItemFor')
// ok to expect the default browse as first param since we just need the mock items obtained via sort direction.
.withArgs('author', undefined, SortDirection.ASC).and.returnValue(createSuccessfulRemoteDataObject$(firstItem))
.withArgs('author', undefined, SortDirection.DESC).and.returnValue(createSuccessfulRemoteDataObject$(lastItem));
comp = fixture.componentInstance; comp = fixture.componentInstance;
fixture.detectChanges();
route = (comp as any).route; route = (comp as any).route;
fixture.detectChanges();
}); });
it('should initialize the list of items', () => { it('should initialize the list of items', () => {
@@ -107,6 +123,7 @@ describe('BrowseByDatePageComponent', () => {
}); });
it('should create a list of startsWith options with the current year first', () => { it('should create a list of startsWith options with the current year first', () => {
expect(comp.startsWithOptions[0]).toEqual(new Date().getUTCFullYear()); //expect(comp.startsWithOptions[0]).toEqual(new Date().getUTCFullYear());
expect(comp.startsWithOptions[0]).toEqual(1960);
}); });
}); });

View File

@@ -1,11 +1,10 @@
import { ChangeDetectorRef, Component, Inject } from '@angular/core'; import { ChangeDetectorRef, Component, Inject } from '@angular/core';
import { import {
BrowseByMetadataPageComponent, BrowseByMetadataPageComponent,
browseParamsToOptions, getBrowseSearchOptions browseParamsToOptions,
getBrowseSearchOptions
} from '../browse-by-metadata-page/browse-by-metadata-page.component'; } from '../browse-by-metadata-page/browse-by-metadata-page.component';
import { combineLatest as observableCombineLatest } from 'rxjs'; import { combineLatest as observableCombineLatest } from 'rxjs';
import { RemoteData } from '../../core/data/remote-data';
import { Item } from '../../core/shared/item.model';
import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { hasValue, isNotEmpty } from '../../shared/empty.util';
import { ActivatedRoute, Params, Router } from '@angular/router'; import { ActivatedRoute, Params, Router } from '@angular/router';
import { BrowseService } from '../../core/browse/browse.service'; import { BrowseService } from '../../core/browse/browse.service';
@@ -16,7 +15,9 @@ import { map } from 'rxjs/operators';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
import { isValidDate } from '../../shared/date.util'; import { isValidDate } from '../../shared/date.util';
import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface'; import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface';
import { RemoteData } from '../../core/data/remote-data';
import { Item } from '../../core/shared/item.model';
@Component({ @Component({
selector: 'ds-browse-by-date-page', selector: 'ds-browse-by-date-page',
@@ -72,30 +73,24 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent {
/** /**
* Update the StartsWith options * Update the StartsWith options
* In this implementation, it creates a list of years starting from now, going all the way back to the earliest * In this implementation, it creates a list of years starting from the most recent item or the current year, going
* date found on an item within this scope. The further back in time, the bigger the change in years become to avoid * all the way back to the earliest date found on an item within this scope. The further back in time, the bigger
* extremely long lists with a one-year difference. * the change in years become to avoid extremely long lists with a one-year difference.
* To determine the change in years, the config found under GlobalConfig.BrowseBy is used for this. * To determine the change in years, the config found under GlobalConfig.BrowseBy is used for this.
* @param definition The metadata definition to fetch the first item for * @param definition The metadata definition to fetch the first item for
* @param metadataKeys The metadata fields to fetch the earliest date from (expects a date field) * @param metadataKeys The metadata fields to fetch the earliest date from (expects a date field)
* @param scope The scope under which to fetch the earliest item for * @param scope The scope under which to fetch the earliest item for
*/ */
updateStartsWithOptions(definition: string, metadataKeys: string[], scope?: string) { updateStartsWithOptions(definition: string, metadataKeys: string[], scope?: string) {
const firstItemRD = this.browseService.getFirstItemFor(definition, scope, SortDirection.ASC);
const lastItemRD = this.browseService.getFirstItemFor(definition, scope, SortDirection.DESC);
this.subs.push( this.subs.push(
this.browseService.getFirstItemFor(definition, scope).subscribe((firstItemRD: RemoteData<Item>) => { observableCombineLatest([firstItemRD, lastItemRD]).subscribe(([firstItem, lastItem]) => {
let lowerLimit = this.appConfig.browseBy.defaultLowerLimit; let lowerLimit = this.getLimit(firstItem, metadataKeys, this.appConfig.browseBy.defaultLowerLimit);
if (hasValue(firstItemRD.payload)) { let upperLimit = this.getLimit(lastItem, metadataKeys, new Date().getUTCFullYear());
const date = firstItemRD.payload.firstMetadataValue(metadataKeys);
if (isNotEmpty(date) && isValidDate(date)) {
const dateObj = new Date(date);
// TODO: it appears that getFullYear (based on local time) is sometimes unreliable. Switching to UTC.
lowerLimit = isNaN(dateObj.getUTCFullYear()) ? lowerLimit : dateObj.getUTCFullYear();
}
}
const options = []; const options = [];
const currentYear = new Date().getUTCFullYear(); const oneYearBreak = Math.floor((upperLimit - this.appConfig.browseBy.oneYearLimit) / 5) * 5;
const oneYearBreak = Math.floor((currentYear - this.appConfig.browseBy.oneYearLimit) / 5) * 5; const fiveYearBreak = Math.floor((upperLimit - this.appConfig.browseBy.fiveYearLimit) / 10) * 10;
const fiveYearBreak = Math.floor((currentYear - this.appConfig.browseBy.fiveYearLimit) / 10) * 10;
if (lowerLimit <= fiveYearBreak) { if (lowerLimit <= fiveYearBreak) {
lowerLimit -= 10; lowerLimit -= 10;
} else if (lowerLimit <= oneYearBreak) { } else if (lowerLimit <= oneYearBreak) {
@@ -103,7 +98,7 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent {
} else { } else {
lowerLimit -= 1; lowerLimit -= 1;
} }
let i = currentYear; let i = upperLimit;
while (i > lowerLimit) { while (i > lowerLimit) {
options.push(i); options.push(i);
if (i <= fiveYearBreak) { if (i <= fiveYearBreak) {
@@ -121,4 +116,24 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent {
}) })
); );
} }
/**
* Returns the year from the item metadata field or the limit.
* @param itemRD the item remote data
* @param metadataKeys The metadata fields to fetch the earliest date from (expects a date field)
* @param limit the limit to use if the year can't be found in metadata
* @private
*/
private getLimit(itemRD: RemoteData<Item>, metadataKeys: string[], limit: number): number {
if (hasValue(itemRD.payload)) {
const date = itemRD.payload.firstMetadataValue(metadataKeys);
if (isNotEmpty(date) && isValidDate(date)) {
const dateObj = new Date(date);
// TODO: it appears that getFullYear (based on local time) is sometimes unreliable. Switching to UTC.
return isNaN(dateObj.getUTCFullYear()) ? limit : dateObj.getUTCFullYear();
} else {
return new Date().getUTCFullYear();
}
}
}
} }

View File

@@ -22,6 +22,7 @@ import { BrowseEntrySearchOptions } from './browse-entry-search-options.model';
import { HrefOnlyDataService } from '../data/href-only-data.service'; import { HrefOnlyDataService } from '../data/href-only-data.service';
import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { BrowseDefinitionDataService } from './browse-definition-data.service'; import { BrowseDefinitionDataService } from './browse-definition-data.service';
import { SortDirection } from '../cache/models/sort-options.model';
export const BROWSE_LINKS_TO_FOLLOW: FollowLinkConfig<BrowseEntry | Item>[] = [ export const BROWSE_LINKS_TO_FOLLOW: FollowLinkConfig<BrowseEntry | Item>[] = [
@@ -160,8 +161,9 @@ export class BrowseService {
* Get the first item for a metadata definition in an optional scope * Get the first item for a metadata definition in an optional scope
* @param definition * @param definition
* @param scope * @param scope
* @param sortDirection optional sort parameter
*/ */
getFirstItemFor(definition: string, scope?: string): Observable<RemoteData<Item>> { getFirstItemFor(definition: string, scope?: string, sortDirection?: SortDirection): Observable<RemoteData<Item>> {
const href$ = this.getBrowseDefinitions().pipe( const href$ = this.getBrowseDefinitions().pipe(
getBrowseDefinitionLinks(definition), getBrowseDefinitionLinks(definition),
hasValueOperator(), hasValueOperator(),
@@ -177,6 +179,9 @@ export class BrowseService {
} }
args.push('page=0'); args.push('page=0');
args.push('size=1'); args.push('size=1');
if (sortDirection) {
args.push('sort=default,' + sortDirection);
}
if (isNotEmpty(args)) { if (isNotEmpty(args)) {
href = new URLCombiner(href, `?${args.join('&')}`).toString(); href = new URLCombiner(href, `?${args.join('&')}`).toString();
} }