mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
External source items import code completed
This commit is contained in:
@@ -2,7 +2,7 @@ import { NO_ERRORS_SCHEMA } from '@angular/core';
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { ImportExternalPageComponent } from './import-external-page.component';
|
import { ImportExternalPageComponent } from './import-external-page.component';
|
||||||
|
|
||||||
fdescribe('ImportExternalPageComponent', () => {
|
describe('ImportExternalPageComponent', () => {
|
||||||
let component: ImportExternalPageComponent;
|
let component: ImportExternalPageComponent;
|
||||||
let fixture: ComponentFixture<ImportExternalPageComponent>;
|
let fixture: ComponentFixture<ImportExternalPageComponent>;
|
||||||
|
|
||||||
|
@@ -74,4 +74,21 @@ describe('ExternalSourceService', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getAllExternalSources', () => {
|
||||||
|
it('should call findAll', () => {
|
||||||
|
spyOn(service, 'findAll');
|
||||||
|
service.getAllExternalSources();
|
||||||
|
expect(service.findAll).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getExternalSource', () => {
|
||||||
|
it('should call findById', () => {
|
||||||
|
const externalSourceId = 'orcidV2';
|
||||||
|
spyOn(service, 'findById');
|
||||||
|
service.getExternalSource(externalSourceId);
|
||||||
|
expect(service.findById).toHaveBeenCalledWith(externalSourceId);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -11,7 +11,7 @@ import { NotificationsService } from '../../shared/notifications/notifications.s
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { FindListOptions, GetRequest } from './request.models';
|
import { FindListOptions, GetRequest } from './request.models';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import { distinctUntilChanged, map, switchMap, take, flatMap } from 'rxjs/operators';
|
import { distinctUntilChanged, map, switchMap, take, flatMap, catchError } from 'rxjs/operators';
|
||||||
import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
|
import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
|
||||||
import { hasValue, isNotEmptyOperator } from '../../shared/empty.util';
|
import { hasValue, isNotEmptyOperator } from '../../shared/empty.util';
|
||||||
import { configureRequest } from '../shared/operators';
|
import { configureRequest } from '../shared/operators';
|
||||||
@@ -60,6 +60,20 @@ export class ExternalSourceService extends DataService<ExternalSource> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a single external source.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* The external source id.
|
||||||
|
* @param linksToFollow
|
||||||
|
* List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved.
|
||||||
|
* @return Observable<RemoteData<ExternalSource>>
|
||||||
|
* The list of the external sources.
|
||||||
|
*/
|
||||||
|
getExternalSource(id: string, ...linksToFollow: Array<FollowLinkConfig<ExternalSource>>): Observable<RemoteData<ExternalSource>> {
|
||||||
|
return this.findById(id, ...linksToFollow);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the list of external sources.
|
* Return the list of external sources.
|
||||||
*
|
*
|
||||||
|
@@ -41,7 +41,7 @@ export class SubmissionImportExternalSearchbarComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* The init external source value.
|
* The init external source value.
|
||||||
*/
|
*/
|
||||||
@Input() public initExternalSourceValue: string;
|
@Input() public initExternalSourceData: ExternalSourceData;
|
||||||
/**
|
/**
|
||||||
* The selected external sources.
|
* The selected external sources.
|
||||||
*/
|
*/
|
||||||
@@ -49,7 +49,7 @@ export class SubmissionImportExternalSearchbarComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* The list of external sources.
|
* The list of external sources.
|
||||||
*/
|
*/
|
||||||
public sourceList: SourceElement[] = [];
|
public sourceList: SourceElement[];
|
||||||
/**
|
/**
|
||||||
* The string used to search items in the external sources.
|
* The string used to search items in the external sources.
|
||||||
*/
|
*/
|
||||||
@@ -87,8 +87,12 @@ export class SubmissionImportExternalSearchbarComponent implements OnInit {
|
|||||||
* Component initialization and retrieve first page of external sources.
|
* Component initialization and retrieve first page of external sources.
|
||||||
*/
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.selectedElement = {
|
||||||
|
id: '',
|
||||||
|
name: 'loading'
|
||||||
|
};
|
||||||
this.searchString = '';
|
this.searchString = '';
|
||||||
this.selectedElement = { id: '', name: 'loading' };
|
this.sourceList = [];
|
||||||
this.findListOptions = Object.assign({}, new FindListOptions(), {
|
this.findListOptions = Object.assign({}, new FindListOptions(), {
|
||||||
elementsPerPage: 5,
|
elementsPerPage: 5,
|
||||||
currentPage: 0,
|
currentPage: 0,
|
||||||
@@ -102,14 +106,16 @@ export class SubmissionImportExternalSearchbarComponent implements OnInit {
|
|||||||
}),
|
}),
|
||||||
getFirstSucceededRemoteDataPayload()
|
getFirstSucceededRemoteDataPayload()
|
||||||
).subscribe((externalSource: PaginatedList<ExternalSource>) => {
|
).subscribe((externalSource: PaginatedList<ExternalSource>) => {
|
||||||
let initEntry;
|
|
||||||
externalSource.page.forEach((element) => {
|
externalSource.page.forEach((element) => {
|
||||||
this.sourceList.push({ id: element.id, name: element.name });
|
this.sourceList.push({ id: element.id, name: element.name });
|
||||||
if (isEmpty(initEntry) || this.initExternalSourceValue === element.id) {
|
if (this.initExternalSourceData.sourceId === element.id) {
|
||||||
initEntry = { id: element.id, name: element.name }
|
this.selectedElement = { id: element.id, name: element.name };
|
||||||
|
this.searchString = this.initExternalSourceData.query;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.selectedElement = initEntry;
|
if (this.selectedElement.id === '') {
|
||||||
|
this.selectedElement = this.sourceList[0];
|
||||||
|
}
|
||||||
this.pageInfo = externalSource.pageInfo;
|
this.pageInfo = externalSource.pageInfo;
|
||||||
this.cdr.detectChanges();
|
this.cdr.detectChanges();
|
||||||
});
|
});
|
||||||
|
@@ -3,15 +3,36 @@
|
|||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<h2 id="header" class="pb-2">{{'submission.import-external.title' | translate}}</h2>
|
<h2 id="header" class="pb-2">{{'submission.import-external.title' | translate}}</h2>
|
||||||
<ds-submission-import-external-searchbar
|
<ds-submission-import-external-searchbar
|
||||||
[initExternalSourceValue]="''"
|
[initExternalSourceData]="routeData"
|
||||||
(externalSourceData) = "getExternalsourceData($event)">
|
(externalSourceData) = "getExternalsourceData($event)">
|
||||||
</ds-submission-import-external-searchbar>
|
</ds-submission-import-external-searchbar>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div *ngIf="routeData.sourceId !== ''" class="col-md-12">
|
||||||
DATA<br>
|
<ng-container *ngVar="(entriesRD$ | async) as entriesRD">
|
||||||
{{(externalSourceData)?externalSourceData.sourceId:''}} : {{(externalSourceData)?externalSourceData.query:''}}
|
<h3 *ngIf="entriesRD?.payload?.page?.length !== 0">{{ 'submission.sections.describe.relationship-lookup.selection-tab.title.' + routeData.sourceId | translate}}</h3>
|
||||||
|
<ds-viewable-collection *ngIf="entriesRD?.hasSucceeded && !(isLoading$ | async) && entriesRD?.payload?.page?.length > 0" @fadeIn
|
||||||
|
[objects]="entriesRD"
|
||||||
|
[selectionConfig]="{ repeatable: repeatable, listId: listId }"
|
||||||
|
[config]="initialPagination"
|
||||||
|
[hideGear]="true"
|
||||||
|
[context]="context"
|
||||||
|
[importable]="true"
|
||||||
|
[importConfig]="importConfig"
|
||||||
|
(importObject)="import($event)">
|
||||||
|
</ds-viewable-collection>
|
||||||
|
<ds-loading *ngIf="(isLoading$ | async)"
|
||||||
|
message="{{'loading.search-results' | translate}}"></ds-loading>
|
||||||
|
<div *ngIf="!(isLoading$ | async) && entriesRD?.payload?.page?.length === 0" id="empty-external-entry-list">
|
||||||
|
{{ 'search.results.empty' | translate }}
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="routeData.sourceId === ''" class="col-md-12">
|
||||||
|
<div class="jumbotron">
|
||||||
|
<p class="lead">{{'submission.import-external.page.hint' | translate}}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@@ -1,7 +1,22 @@
|
|||||||
import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { Observable, of as observableOf } from 'rxjs';
|
import { Router } from '@angular/router';
|
||||||
|
import { combineLatest, BehaviorSubject } from 'rxjs';
|
||||||
import { ExternalSourceService } from '../../core/data/external-source.service';
|
import { ExternalSourceService } from '../../core/data/external-source.service';
|
||||||
import { ExternalSourceData } from './import-external-searchbar/submission-import-external-searchbar.component';
|
import { ExternalSourceData } from './import-external-searchbar/submission-import-external-searchbar.component';
|
||||||
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
|
import { PaginatedList } from '../../core/data/paginated-list';
|
||||||
|
import { ExternalSourceEntry } from '../../core/shared/external-source-entry.model';
|
||||||
|
import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service';
|
||||||
|
import { switchMap, filter } from 'rxjs/operators';
|
||||||
|
import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
|
||||||
|
import { Context } from '../../core/shared/context.model';
|
||||||
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
|
import { RouteService } from '../../core/services/route.service';
|
||||||
|
import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
|
||||||
|
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import { SubmissionImportExternalPreviewComponent } from './import-external-preview/submission-import-external-preview.component';
|
||||||
|
import { fadeIn } from '../../shared/animations/fade';
|
||||||
|
import { createPaginatedList } from '../../shared/testing/utils.test';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component allows to submit a new workspaceitem importing the data from an external source.
|
* This component allows to submit a new workspaceitem importing the data from an external source.
|
||||||
@@ -9,27 +24,129 @@ import { ExternalSourceData } from './import-external-searchbar/submission-impor
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-submission-import-external',
|
selector: 'ds-submission-import-external',
|
||||||
styleUrls: ['./submission-import-external.component.scss'],
|
styleUrls: ['./submission-import-external.component.scss'],
|
||||||
templateUrl: './submission-import-external.component.html'
|
templateUrl: './submission-import-external.component.html',
|
||||||
|
animations: [ fadeIn ]
|
||||||
})
|
})
|
||||||
export class SubmissionImportExternalComponent {
|
export class SubmissionImportExternalComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* The external source search data.
|
* The external source search data from the routing service.
|
||||||
*/
|
*/
|
||||||
public externalSourceData: ExternalSourceData;
|
public routeData: ExternalSourceData;
|
||||||
|
/**
|
||||||
|
* The displayed list of entries
|
||||||
|
*/
|
||||||
|
public entriesRD$: BehaviorSubject<RemoteData<PaginatedList<ExternalSourceEntry>>>;
|
||||||
|
/**
|
||||||
|
* TRUE if the REST service is called to retrieve the external source items
|
||||||
|
*/
|
||||||
|
public isLoading$: BehaviorSubject<boolean>;
|
||||||
|
/**
|
||||||
|
* Configuration to use for the import buttons
|
||||||
|
*/
|
||||||
|
public importConfig: { buttonLabel: string };
|
||||||
|
/**
|
||||||
|
* Suffix for button label
|
||||||
|
*/
|
||||||
|
public label: string;
|
||||||
|
/**
|
||||||
|
* The ID of the list to add/remove selected items to/from
|
||||||
|
*/
|
||||||
|
public listId: string;
|
||||||
|
/**
|
||||||
|
* TRUE if the selection is repeatable
|
||||||
|
*/
|
||||||
|
public repeatable: boolean;
|
||||||
|
/**
|
||||||
|
* The initial pagination options
|
||||||
|
*/
|
||||||
|
public initialPagination = Object.assign(new PaginationComponentOptions(), {
|
||||||
|
id: 'submission-external-source-relation-list',
|
||||||
|
pageSize: 5
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* The context to displaying lists for
|
||||||
|
*/
|
||||||
|
public context: Context;
|
||||||
|
/**
|
||||||
|
* The modal for the entry preview
|
||||||
|
*/
|
||||||
|
modalRef: NgbModalRef;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the component variables.
|
* Initialize the component variables.
|
||||||
|
* @param {SearchConfigurationService} searchConfigService
|
||||||
* @param {ExternalSourceService} externalService
|
* @param {ExternalSourceService} externalService
|
||||||
|
* @param {RouteService} routeService
|
||||||
|
* @param {Router} router
|
||||||
|
* @param {NgbModal} modalService
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
|
public searchConfigService: SearchConfigurationService,
|
||||||
private externalService: ExternalSourceService,
|
private externalService: ExternalSourceService,
|
||||||
private cdr: ChangeDetectorRef,
|
private routeService: RouteService,
|
||||||
|
private router: Router,
|
||||||
|
private modalService: NgbModal,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the data to submit a search.
|
* Get the entries for the selected external source and set initial configuration.
|
||||||
*/
|
*/
|
||||||
public getExternalsourceData(event: ExternalSourceData) {
|
ngOnInit(): void {
|
||||||
this.externalSourceData = event;
|
this.label = 'Journal';
|
||||||
|
this.listId = 'list-submission-external-sources';
|
||||||
|
this.context = Context.SubmissionModal;
|
||||||
|
this.repeatable = false;
|
||||||
|
this.routeData = { sourceId: '', query: '' };
|
||||||
|
this.importConfig = {
|
||||||
|
buttonLabel: 'submission.sections.describe.relationship-lookup.external-source.import-button-title.' + this.label
|
||||||
|
};
|
||||||
|
this.entriesRD$ = new BehaviorSubject(createSuccessfulRemoteDataObject(createPaginatedList([])));
|
||||||
|
this.isLoading$ = new BehaviorSubject(false);
|
||||||
|
combineLatest(
|
||||||
|
[
|
||||||
|
this.routeService.getQueryParameterValue('source'),
|
||||||
|
this.routeService.getQueryParameterValue('query')
|
||||||
|
]).pipe(
|
||||||
|
filter(([source, query]) => source && query && source !== '' && query !== ''),
|
||||||
|
filter(([source, query]) => source !== this.routeData.sourceId || query !== this.routeData.query),
|
||||||
|
switchMap(([source, query]) => {
|
||||||
|
this.routeData.sourceId = source;
|
||||||
|
this.routeData.query = query;
|
||||||
|
this.isLoading$.next(true);
|
||||||
|
return this.searchConfigService.paginatedSearchOptions.pipe(
|
||||||
|
switchMap((searchOptions: PaginatedSearchOptions) => {
|
||||||
|
return this.externalService.getExternalSourceEntries(this.routeData.sourceId, searchOptions);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
).subscribe((rdData) => {
|
||||||
|
this.entriesRD$.next(rdData);
|
||||||
|
this.isLoading$.next(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data from the searchbar and changes the router data.
|
||||||
|
*/
|
||||||
|
public getExternalsourceData(event: ExternalSourceData): void {
|
||||||
|
this.router.navigate(
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
queryParams: { source: event.sourceId, query: event.query },
|
||||||
|
replaceUrl: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display an item preview by opening up an import modal window.
|
||||||
|
* @param entry The entry to import
|
||||||
|
*/
|
||||||
|
public import(entry): void {
|
||||||
|
this.modalRef = this.modalService.open(SubmissionImportExternalPreviewComponent, {
|
||||||
|
size: 'lg',
|
||||||
|
});
|
||||||
|
const modalComp = this.modalRef.componentInstance;
|
||||||
|
modalComp.externalSourceEntry = entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -30,6 +30,7 @@ import { SubmissionSubmitComponent } from './submit/submission-submit.component'
|
|||||||
import { storeModuleConfig } from '../app.reducer';
|
import { storeModuleConfig } from '../app.reducer';
|
||||||
import { SubmissionImportExternalComponent } from './import-external/submission-import-external.component';
|
import { SubmissionImportExternalComponent } from './import-external/submission-import-external.component';
|
||||||
import { SubmissionImportExternalSearchbarComponent } from './import-external/import-external-searchbar/submission-import-external-searchbar.component';
|
import { SubmissionImportExternalSearchbarComponent } from './import-external/import-external-searchbar/submission-import-external-searchbar.component';
|
||||||
|
import { SubmissionImportExternalPreviewComponent } from './import-external/import-external-preview/submission-import-external-preview.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -59,12 +60,15 @@ import { SubmissionImportExternalSearchbarComponent } from './import-external/im
|
|||||||
SubmissionSectionUploadFileViewComponent,
|
SubmissionSectionUploadFileViewComponent,
|
||||||
SubmissionImportExternalComponent,
|
SubmissionImportExternalComponent,
|
||||||
SubmissionImportExternalSearchbarComponent,
|
SubmissionImportExternalSearchbarComponent,
|
||||||
|
SubmissionImportExternalPreviewComponent
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
SubmissionSectionUploadComponent,
|
SubmissionSectionUploadComponent,
|
||||||
SubmissionSectionformComponent,
|
SubmissionSectionformComponent,
|
||||||
SubmissionSectionLicenseComponent,
|
SubmissionSectionLicenseComponent,
|
||||||
SubmissionSectionContainerComponent],
|
SubmissionSectionContainerComponent,
|
||||||
|
SubmissionImportExternalPreviewComponent
|
||||||
|
],
|
||||||
exports: [
|
exports: [
|
||||||
SubmissionEditComponent,
|
SubmissionEditComponent,
|
||||||
SubmissionFormComponent,
|
SubmissionFormComponent,
|
||||||
|
@@ -2402,6 +2402,8 @@
|
|||||||
|
|
||||||
"submission.import-external.title": "Import metadata from an external source",
|
"submission.import-external.title": "Import metadata from an external source",
|
||||||
|
|
||||||
|
"submission.import-external.page.hint": "Search items using external sources.",
|
||||||
|
|
||||||
"submission.import-external.back-to-my-dspace": "Back to MyDSpace",
|
"submission.import-external.back-to-my-dspace": "Back to MyDSpace",
|
||||||
|
|
||||||
"submission.import-external.search.placeholder": "Search the external source",
|
"submission.import-external.search.placeholder": "Search the external source",
|
||||||
@@ -2424,6 +2426,12 @@
|
|||||||
|
|
||||||
"submission.import-external.source.lcname": "Library of Congress Names",
|
"submission.import-external.source.lcname": "Library of Congress Names",
|
||||||
|
|
||||||
|
"submission.import-external.preview.title": "Item Preview",
|
||||||
|
|
||||||
|
"submission.import-external.preview.subtitle": "The metadata below was imported from an external source. It will be pre-filled when you start the submission.",
|
||||||
|
|
||||||
|
"submission.import-external.preview.button.import": "Start submission",
|
||||||
|
|
||||||
|
|
||||||
"submission.sections.describe.relationship-lookup.close": "Close",
|
"submission.sections.describe.relationship-lookup.close": "Close",
|
||||||
|
|
||||||
@@ -2555,6 +2563,8 @@
|
|||||||
|
|
||||||
"submission.sections.describe.relationship-lookup.selection-tab.title.orcidV2": "Search Results",
|
"submission.sections.describe.relationship-lookup.selection-tab.title.orcidV2": "Search Results",
|
||||||
|
|
||||||
|
"submission.sections.describe.relationship-lookup.selection-tab.title.orcidv2": "Search Results",
|
||||||
|
|
||||||
"submission.sections.describe.relationship-lookup.selection-tab.title.lcname": "Search Results",
|
"submission.sections.describe.relationship-lookup.selection-tab.title.lcname": "Search Results",
|
||||||
|
|
||||||
"submission.sections.describe.relationship-lookup.name-variant.notification.content": "Would you like to save \"{{ value }}\" as a name variant for this person so you and others can reuse it for future submissions? If you don\'t you can still use it for this submission.",
|
"submission.sections.describe.relationship-lookup.name-variant.notification.content": "Would you like to save \"{{ value }}\" as a name variant for this person so you and others can reuse it for future submissions? If you don\'t you can still use it for this submission.",
|
||||||
|
Reference in New Issue
Block a user