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

Coar 3.0 resource types #1747

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
4 changes: 3 additions & 1 deletion src/app/admin/admin-search-page/admin-search.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { CollectionAdminSearchResultGridElementComponent } from './admin-search-
import { ItemAdminSearchResultActionsComponent } from './admin-search-results/item-admin-search-result-actions.component';
import { JournalEntitiesModule } from '../../entity-groups/journal-entities/journal-entities.module';
import { ResearchEntitiesModule } from '../../entity-groups/research-entities/research-entities.module';
import { ResourceTypeModule } from '../../entity-groups/resource-type/resource-type.module';
import { SearchModule } from '../../shared/search/search.module';

const ENTRY_COMPONENTS = [
Expand All @@ -28,7 +29,8 @@ const ENTRY_COMPONENTS = [
SearchModule,
SharedModule.withEntryComponents(),
JournalEntitiesModule.withEntryComponents(),
ResearchEntitiesModule.withEntryComponents()
ResearchEntitiesModule.withEntryComponents(),
ResourceTypeModule.withEntryComponents()
],
declarations: [
AdminSearchPageComponent,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { MetadataTranslationService } from './metadata-translation.service';
import { DSpaceObject } from '../../shared/dspace-object.model';

describe('MetadataTranslationService', () => {
let testedService: MetadataTranslationService;

const dcKey = 'dc.title';
const englishTitle = 'english title';
const frenchTitle = 'titre français';
const spanishTitle = 'titulo español';
const emptyLanguageTitle = 'empty language title';

const dso = Object.assign(new DSpaceObject(), {
metadata: {
'dc.title': [
{ value: englishTitle, language: 'en' },
{ value: frenchTitle, language: 'fr' },
{ value: spanishTitle, language: 'es' },
{ value: emptyLanguageTitle }
],
'other.key': [{ value: 'other value' }],
'other.en.key': [{ value: 'other en value', language: 'en' }]
}
});

const dsoNoEnglishNoFrench = Object.assign(new DSpaceObject(), {
metadata: {
'dc.title': [
{ value: spanishTitle, language: 'es' },
{ value: emptyLanguageTitle }
],
'other.key': [{ value: 'other value' }],
'other.en.key': [{ value: 'other en value', language: 'en' }]
}
});

const localServiceStubFrench: any = {
getCurrentLanguageCode(): string { return 'fr'; },
};
const localServiceStubEnglish: any = {
getCurrentLanguageCode(): string { return 'en'; },
};
const localServiceStubGerman: any = {
getCurrentLanguageCode(): string { return 'de'; },
};

beforeEach(() => {
testedService = new MetadataTranslationService(localServiceStubFrench);
});

describe('currentLanguageValue', () => {
it('should return metadata value for the current language code', () => {
let value = testedService.currentLanguageValue(dso, dcKey);
expect(value).toEqual(frenchTitle);
});
});

describe('defaultLanguageValue', () => {
it('should return metadata value for the default language code', () => {
let value = testedService.defaultLanguageValue(dso, dcKey);
expect(value).toEqual(englishTitle);
});
});

describe('emptyLanguageValue', () => {
it('should return metadata value without language code', () => {
let value = testedService.emptyLanguageValue(dso, dcKey);
expect(value).toEqual(emptyLanguageTitle);
});
});

describe('currentLanguageValueOrDefault', () => {
it('should return english metadata value with english locale', () => {
testedService = new MetadataTranslationService(localServiceStubEnglish);
let value = testedService.currentLanguageValueOrDefault(dso, dcKey);
expect(value).toEqual(englishTitle);
});
it('should return french metadata value with french locale', () => {
let value = testedService.currentLanguageValueOrDefault(dso, dcKey);
expect(value).toEqual(frenchTitle);
});
it('should display default language metadata value if locale is not available', () => {
testedService = new MetadataTranslationService(localServiceStubGerman);
let value = testedService.currentLanguageValueOrDefault(dso, dcKey);
expect(value).toEqual(englishTitle);
});
it('should display empty language metadata value if both locale and default language are not availables', () => {
let value = testedService.currentLanguageValueOrDefault(dsoNoEnglishNoFrench, dcKey);
expect(value).toEqual(emptyLanguageTitle);
});

});

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Inject, Injectable } from '@angular/core';
import { isEmpty, isNotEmpty } from '../../../shared/empty.util';
import { LocaleService } from '../locale.service';
import { MetadataValue, MetadataValueFilter } from '../../shared/metadata.models';
import { DSpaceObject } from '../../shared/dspace-object.model';
import { isUndefined } from '../../../shared/empty.util';

@Injectable({
providedIn: 'root'
})
/**
* Translate metadata from a DSpaceObject using the current language code from the locale service.
*/
export class MetadataTranslationService {

/**
* The filtered metadata language
*/
language: string;

private currentLanguageFilter: MetadataValueFilter;

readonly defaultFilter: MetadataValueFilter = { language: 'en' };

constructor(
protected localeService: LocaleService,
) {
this.currentLanguageFilter = { language: this.localeService.getCurrentLanguageCode() };
}

/* First metadata value for the current language from the locale service */
currentLanguageValue(dso: DSpaceObject, keyOrKeys: string | string[]): string {
const md = dso.firstMetadata(keyOrKeys, this.currentLanguageFilter);
return this.getValue(md);
}

/* First metadata value for the defaultFilter defined language (en) */
defaultLanguageValue(dso: DSpaceObject, keyOrKeys: string | string[]): string {
const md = dso.firstMetadata(keyOrKeys, this.defaultFilter);
return this.getValue(md);
}

/* First metadata value without language defined */
emptyLanguageValue(dso: DSpaceObject, keyOrKeys: string | string[]): string {
const md = dso.allMetadata(keyOrKeys).find(val => isEmpty(val.language));
return this.getValue(md);
}

/* First metadata value for the current language. Then falls back on default then empty language value. */
currentLanguageValueOrDefault(dso: DSpaceObject, keyOrKeys: string | string[]): string {
let mdValue = this.currentLanguageValue(dso, keyOrKeys);
if (!mdValue) {
mdValue = this.defaultLanguageValue(dso, keyOrKeys);
}
if (!mdValue) {
mdValue = this.emptyLanguageValue(dso, keyOrKeys);
}
return String(mdValue);
}

private getValue(mdValue: MetadataValue): string {
return isUndefined(mdValue) ? undefined : mdValue.value;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<ds-resource-type-search-result-grid-element [showLabel]="showLabel" [object]="{ indexableObject: object, hitHighlights: {} }" [linkType]="linkType">
</ds-resource-type-search-result-grid-element>
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { ResourceTypeGridElementComponent } from './resource-type-grid-element.component';

import { Item } from '../../../core/shared/item.model';
import { of as observableOf } from 'rxjs';
import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
import { buildPaginatedList } from '../../../core/data/paginated-list.model';
import { PageInfo } from '../../../core/shared/page-info.model';
import { TestBed, waitForAsync } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TruncatePipe } from '../../../shared/utils/truncate.pipe';
import { TruncatableService } from '../../../shared/truncatable/truncatable.service';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';

const mockItem = Object.assign(new Item(), {
bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])),
metadata: {
'resourcetypes.​name': [
{
language: null,
value: 'doctoral thesis'
}
],
'resourcetypes.definition': [
{
language: null,
value: 'A thesis reporting the research undertaken during a period of graduate study leading to a doctoral degree.'
}
],
'resourcetypes.preferredLabels': [
{
language: 'en',
value: 'doctoral thesis'
},
{
language: 'es',
value: 'tesis doctoral'
},
{
language: 'fr',
value: 'thèse de doctorat'
},
{
language: 'de',
value: 'Dissertation'
},
{
language: 'it',
value: 'tesi di dottorato'
},
],
'resourcetypes.​relatedTerms': [
{
language: null,
value: 'Broad Match: http://purl.org/eprint/type/Thesis'
}
],
'resourcetypes.uri': [
{
language: null,
value: 'http://purl.org/coar/resource_type/c_db06'
}
]
}
});

describe('ResourceTypeGridElementComponent', () => {
let comp;
let fixture;

const truncatableServiceStub: any = {
isCollapsed: (id: number) => observableOf(true),
};

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [NoopAnimationsModule],
declarations: [ResourceTypeGridElementComponent, TruncatePipe],
providers: [
{ provide: TruncatableService, useValue: truncatableServiceStub },
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(ResourceTypeGridElementComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));

beforeEach(waitForAsync(() => {
fixture = TestBed.createComponent(ResourceTypeGridElementComponent);
comp = fixture.componentInstance;
}));

describe(`when the resource type is rendered`, () => {
beforeEach(() => {
comp.object = mockItem;
fixture.detectChanges();
});

it(`should contain a ResourceTypeGridElementComponent`, () => {
const resourceTypeGridElement = fixture.debugElement.query(By.css(`ds-resource-type-search-result-grid-element`));
expect(resourceTypeGridElement).not.toBeNull();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Component } from '@angular/core';
import { ViewMode } from '../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { AbstractListableElementComponent } from '../../../shared/object-collection/shared/object-collection-element/abstract-listable-element.component';
import { Item } from '../../../core/shared/item.model';

@listableObjectComponent('ResourceType', ViewMode.GridElement)
@Component({
selector: 'ds-resource-type-grid-element',
styleUrls: ['./resource-type-grid-element.component.scss'],
templateUrl: './resource-type-grid-element.component.html',
})
/**
* The component for displaying a grid element for an item of the type ResourceType
*/
export class ResourceTypeGridElementComponent extends AbstractListableElementComponent<Item> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<ds-resource-type-search-result-list-element [object]="{ indexableObject: object, hitHighlights: {} }"
[linkType]="linkType"
[showLabel]="showLabel"
[value]="value">
</ds-resource-type-search-result-list-element>
Loading