mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-17 15:03:07 +00:00
83707: Control collection harvest from the UI
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
<div *ngVar="(contentSource$ |async) as contentSource">
|
||||
<div class="container-fluid" *ngIf="shouldShow">
|
||||
<h4>{{ 'collection.source.controls.head' | translate }}</h4>
|
||||
<div>
|
||||
<span class="font-weight-bold">{{'collection.source.controls.harvest.status' | translate}}</span>
|
||||
<span>{{contentSource?.harvestStatus}}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-weight-bold">{{'collection.source.controls.harvest.start' | translate}}</span>
|
||||
<span>{{contentSource?.harvestStartTime ? contentSource?.harvestStartTime : 'collection.source.controls.harvest.no-information'|translate }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-weight-bold">{{'collection.source.controls.harvest.last' | translate}}</span>
|
||||
<span>{{contentSource?.lastHarvested ? contentSource?.lastHarvested : 'collection.source.controls.harvest.no-information'|translate }}</span>
|
||||
</div>
|
||||
|
||||
<button class=" btn btn-secondary"
|
||||
[disabled]="!(isEnabled)"
|
||||
(click)="testConfiguration(contentSource)">
|
||||
<span class="d-none d-sm-inline">{{'collection.source.controls.test.submit' | translate}}</span>
|
||||
</button>
|
||||
<button class="btn btn-primary"
|
||||
[disabled]="!(isEnabled)"
|
||||
(click)="importNow()">
|
||||
<span class="d-none d-sm-inline">{{'collection.source.controls.import.submit' | translate}}</span>
|
||||
</button>
|
||||
<button class="btn btn-primary"
|
||||
[disabled]="!(isEnabled)"
|
||||
(click)="resetAndReimport()">
|
||||
<span class="d-none d-sm-inline"> {{'collection.source.controls.reset.submit' | translate}}</span>
|
||||
</button>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,150 @@
|
||||
import { Component, Input, OnDestroy } from '@angular/core';
|
||||
import { ScriptDataService } from '../../../../core/data/processes/script-data.service';
|
||||
import { ContentSource } from '../../../../core/shared/content-source.model';
|
||||
import { ProcessDataService } from '../../../../core/data/processes/process-data.service';
|
||||
import {
|
||||
getAllCompletedRemoteData,
|
||||
getAllSucceededRemoteDataPayload,
|
||||
getFirstCompletedRemoteData,
|
||||
getFirstSucceededRemoteDataPayload
|
||||
} from '../../../../core/shared/operators';
|
||||
import { filter, map, switchMap, tap } from 'rxjs/operators';
|
||||
import { hasValue, hasValueOperator } from '../../../../shared/empty.util';
|
||||
import { ProcessStatus } from '../../../../process-page/processes/process-status.model';
|
||||
import { Subscription } from 'rxjs/internal/Subscription';
|
||||
import { RequestService } from '../../../../core/data/request.service';
|
||||
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
||||
import { Collection } from '../../../../core/shared/collection.model';
|
||||
import { CollectionDataService } from '../../../../core/data/collection-data.service';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Process } from '../../../../process-page/processes/process.model';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-collection-source-controls',
|
||||
templateUrl: './collection-source-controls.component.html',
|
||||
})
|
||||
export class CollectionSourceControlsComponent implements OnDestroy {
|
||||
|
||||
@Input() isEnabled: boolean;
|
||||
@Input() collection: Collection;
|
||||
@Input() shouldShow: boolean;
|
||||
|
||||
contentSource$: Observable<ContentSource>;
|
||||
private subs: Subscription[] = [];
|
||||
|
||||
constructor(private scriptDataService: ScriptDataService,
|
||||
private processDataService: ProcessDataService,
|
||||
private requestService: RequestService,
|
||||
private notificationsService: NotificationsService,
|
||||
private collectionService: CollectionDataService,
|
||||
private translateService: TranslateService,
|
||||
private httpClient: HttpClient,
|
||||
private bitstreamService: BitstreamDataService
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.contentSource$ = this.collectionService.findByHref(this.collection._links.self.href, false).pipe(
|
||||
getAllSucceededRemoteDataPayload(),
|
||||
switchMap((collection) => this.collectionService.getContentSource(collection.uuid, false)),
|
||||
getAllSucceededRemoteDataPayload()
|
||||
);
|
||||
}
|
||||
|
||||
testConfiguration(contentSource) {
|
||||
this.subs.push(this.scriptDataService.invoke('harvest', [
|
||||
{name: '-g', value: null},
|
||||
{name: '-a', value: contentSource.oaiSource},
|
||||
{name: '-i', value: contentSource.oaiSetId},
|
||||
], []).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
tap((rd) => {
|
||||
if (rd.hasFailed) {
|
||||
this.notificationsService.error(this.translateService.get('collection.source.controls.test.submit.error'));
|
||||
}
|
||||
}),
|
||||
filter((rd) => rd.hasSucceeded && hasValue(rd.payload)),
|
||||
switchMap((rd) => this.processDataService.findById(rd.payload.processId, false)),
|
||||
getAllCompletedRemoteData(),
|
||||
filter((rd) => !rd.isStale && (rd.hasSucceeded || rd.hasFailed)),
|
||||
map((rd) => rd.payload),
|
||||
hasValueOperator(),
|
||||
).subscribe((process: Process) => {
|
||||
if (process.processStatus.toString() !== ProcessStatus[ProcessStatus.COMPLETED].toString() &&
|
||||
process.processStatus.toString() !== ProcessStatus[ProcessStatus.FAILED].toString()) {
|
||||
setTimeout(() => {
|
||||
this.requestService.setStaleByHrefSubstring(process._links.self.href);
|
||||
}, 5000);
|
||||
}
|
||||
if (process.processStatus.toString() === ProcessStatus[ProcessStatus.FAILED].toString()) {
|
||||
this.notificationsService.error(this.translateService.get('collection.source.controls.test.failed'));
|
||||
}
|
||||
if (process.processStatus.toString() === ProcessStatus[ProcessStatus.COMPLETED].toString()) {
|
||||
this.bitstreamService.findByHref(process._links.output.href, false).pipe(getFirstSucceededRemoteDataPayload()).subscribe((bitstream) => {
|
||||
this.httpClient.get(bitstream._links.content.href, {responseType: 'text'}).subscribe((data: any) => {
|
||||
const output = data.replaceAll(new RegExp('.*\\@(.*)', 'g'), '$1')
|
||||
.replaceAll('The script has started', '')
|
||||
.replaceAll('The script has completed', '');
|
||||
this.notificationsService.success(this.translateService.get('collection.source.controls.test.completed'), output);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
importNow() {
|
||||
this.subs.push(this.scriptDataService.invoke('harvest', [
|
||||
{name: '-r', value: null},
|
||||
{name: '-e', value: 'dspacedemo+admin@gmail.com'},
|
||||
{name: '-c', value: this.collection.uuid},
|
||||
], [])
|
||||
.pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
tap((rd) => {
|
||||
if (rd.hasFailed) {
|
||||
this.notificationsService.error(this.translateService.get('collection.source.controls.test.submit.error'));
|
||||
}
|
||||
}),
|
||||
filter((rd) => rd.hasSucceeded && hasValue(rd.payload)),
|
||||
switchMap((rd) => this.processDataService.findById(rd.payload.processId, false)),
|
||||
getAllCompletedRemoteData(),
|
||||
filter((rd) => !rd.isStale && (rd.hasSucceeded || rd.hasFailed)),
|
||||
map((rd) => rd.payload),
|
||||
hasValueOperator(),
|
||||
).subscribe((process) => {
|
||||
if (process.processStatus.toString() !== ProcessStatus[ProcessStatus.COMPLETED].toString() &&
|
||||
process.processStatus.toString() !== ProcessStatus[ProcessStatus.FAILED].toString()) {
|
||||
setTimeout(() => {
|
||||
this.requestService.setStaleByHrefSubstring(process._links.self.href);
|
||||
this.requestService.setStaleByHrefSubstring(this.collection._links.self.href);
|
||||
}, 5000);
|
||||
}
|
||||
if (process.processStatus.toString() === ProcessStatus[ProcessStatus.FAILED].toString()) {
|
||||
this.notificationsService.error(this.translateService.get('collection.source.controls.import.failed'));
|
||||
}
|
||||
if (process.processStatus.toString() === ProcessStatus[ProcessStatus.COMPLETED].toString()) {
|
||||
this.notificationsService.success(this.translateService.get('collection.source.controls.import.completed'));
|
||||
setTimeout(() => {
|
||||
this.requestService.setStaleByHrefSubstring(this.collection._links.self.href);
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
resetAndReimport() {
|
||||
// TODO implement when a single option is present
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subs.forEach((sub) => {
|
||||
if (hasValue(sub)) {
|
||||
sub.unsubscribe();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@@ -11,7 +11,8 @@
|
||||
class="fas fa-undo-alt"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.metadata.reinstate-button" | translate}}</span>
|
||||
</button>
|
||||
<button class="btn btn-primary" [disabled]="!(hasChanges() | async) || !isValid() || (initialHarvestType === harvestTypeNone && contentSource.harvestType === initialHarvestType)"
|
||||
<button class="btn btn-primary"
|
||||
[disabled]="!(hasChanges() | async) || !isValid() || (initialHarvestType === harvestTypeNone && contentSource.harvestType === initialHarvestType)"
|
||||
(click)="onSubmit()"><i
|
||||
class="fas fa-save"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.metadata.save-button" | translate}}</span>
|
||||
@@ -19,12 +20,15 @@
|
||||
</div>
|
||||
<h4>{{ 'collection.edit.tabs.source.head' | translate }}</h4>
|
||||
<div *ngIf="contentSource" class="form-check mb-4">
|
||||
<input type="checkbox" class="form-check-input" id="externalSourceCheck" [checked]="(contentSource?.harvestType !== harvestTypeNone)" (change)="changeExternalSource()">
|
||||
<label class="form-check-label" for="externalSourceCheck">{{ 'collection.edit.tabs.source.external' | translate }}</label>
|
||||
<input type="checkbox" class="form-check-input" id="externalSourceCheck"
|
||||
[checked]="(contentSource?.harvestType !== harvestTypeNone)" (change)="changeExternalSource()">
|
||||
<label class="form-check-label"
|
||||
for="externalSourceCheck">{{ 'collection.edit.tabs.source.external' | translate }}</label>
|
||||
</div>
|
||||
<ds-loading *ngIf="!contentSource" [message]="'loading.content-source' | translate"></ds-loading>
|
||||
<h4 *ngIf="contentSource && (contentSource?.harvestType !== harvestTypeNone)">{{ 'collection.edit.tabs.source.form.head' | translate }}</h4>
|
||||
</div>
|
||||
<div class="row">
|
||||
<ds-form *ngIf="formGroup && contentSource && (contentSource?.harvestType !== harvestTypeNone)"
|
||||
[formId]="'collection-source-form-id'"
|
||||
[formGroup]="formGroup"
|
||||
@@ -35,8 +39,11 @@
|
||||
(dfChange)="onChange($event)"
|
||||
(submitForm)="onSubmit()"
|
||||
(cancel)="onCancel()"></ds-form>
|
||||
<div class="container-fluid" *ngIf="(contentSource?.harvestType !== harvestTypeNone)">
|
||||
<div class="d-inline-block float-right">
|
||||
</div>
|
||||
<div class="container mt-2" *ngIf="(contentSource?.harvestType !== harvestTypeNone)">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="d-inline-block float-right ml-1">
|
||||
<button class=" btn btn-danger" *ngIf="!(isReinstatable() | async)"
|
||||
[disabled]="!(hasChanges() | async)"
|
||||
(click)="discard()"><i
|
||||
@@ -48,10 +55,20 @@
|
||||
class="fas fa-undo-alt"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.metadata.reinstate-button" | translate}}</span>
|
||||
</button>
|
||||
<button class="btn btn-primary" [disabled]="!(hasChanges() | async) || !isValid() || (initialHarvestType === harvestTypeNone && contentSource.harvestType === initialHarvestType)"
|
||||
<button class="btn btn-primary"
|
||||
[disabled]="!(hasChanges() | async) || !isValid() || (initialHarvestType === harvestTypeNone && contentSource.harvestType === initialHarvestType)"
|
||||
(click)="onSubmit()"><i
|
||||
class="fas fa-save"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.metadata.save-button" | translate}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ds-collection-source-controls
|
||||
[isEnabled]="!(hasChanges()|async)"
|
||||
[shouldShow]="contentSource?.harvestType !== harvestTypeNone"
|
||||
[collection]="(collectionRD$ |async)?.payload"
|
||||
>
|
||||
</ds-collection-source-controls>
|
||||
|
||||
|
@@ -380,7 +380,7 @@ export class CollectionSourceComponent extends AbstractTrackableComponent implem
|
||||
switchMap((uuid) => this.collectionService.getHarvesterEndpoint(uuid)),
|
||||
take(1)
|
||||
).subscribe((endpoint) => this.requestService.removeByHrefSubstring(endpoint));
|
||||
|
||||
this.requestService.setStaleByHrefSubstring(this.contentSource._links.self.href);
|
||||
// Update harvester
|
||||
this.collectionRD$.pipe(
|
||||
getFirstSucceededRemoteData(),
|
||||
|
@@ -9,6 +9,7 @@ import { CollectionCurateComponent } from './collection-curate/collection-curate
|
||||
import { CollectionSourceComponent } from './collection-source/collection-source.component';
|
||||
import { CollectionAuthorizationsComponent } from './collection-authorizations/collection-authorizations.component';
|
||||
import { CollectionFormModule } from '../collection-form/collection-form.module';
|
||||
import { CollectionSourceControlsComponent } from './collection-source/collection-source-controls/collection-source-controls.component';
|
||||
|
||||
/**
|
||||
* Module that contains all components related to the Edit Collection page administrator functionality
|
||||
@@ -26,6 +27,8 @@ import { CollectionFormModule } from '../collection-form/collection-form.module'
|
||||
CollectionRolesComponent,
|
||||
CollectionCurateComponent,
|
||||
CollectionSourceComponent,
|
||||
|
||||
CollectionSourceControlsComponent,
|
||||
CollectionAuthorizationsComponent
|
||||
]
|
||||
})
|
||||
|
@@ -138,7 +138,7 @@ export class CollectionDataService extends ComColDataService<Collection> {
|
||||
* Get the collection's content harvester
|
||||
* @param collectionId
|
||||
*/
|
||||
getContentSource(collectionId: string): Observable<RemoteData<ContentSource>> {
|
||||
getContentSource(collectionId: string, useCachedVersionIfAvailable = true): Observable<RemoteData<ContentSource>> {
|
||||
const href$ = this.getHarvesterEndpoint(collectionId).pipe(
|
||||
isNotEmptyOperator(),
|
||||
take(1)
|
||||
@@ -146,7 +146,7 @@ export class CollectionDataService extends ComColDataService<Collection> {
|
||||
|
||||
href$.subscribe((href: string) => {
|
||||
const request = new ContentSourceRequest(this.requestService.generateRequestId(), href);
|
||||
this.requestService.send(request, true);
|
||||
this.requestService.send(request, useCachedVersionIfAvailable);
|
||||
});
|
||||
|
||||
return this.rdbService.buildSingle<ContentSource>(href$);
|
||||
|
@@ -70,6 +70,15 @@ export class ContentSource extends CacheableObject {
|
||||
*/
|
||||
metadataConfigs: MetadataConfig[];
|
||||
|
||||
@autoserializeAs('harvest_status')
|
||||
harvestStatus: string;
|
||||
|
||||
@autoserializeAs('harvest_start_time')
|
||||
harvestStartTime: string;
|
||||
|
||||
@autoserializeAs('last_harvested')
|
||||
lastHarvested: string;
|
||||
|
||||
/**
|
||||
* The {@link HALLink}s for this ContentSource
|
||||
*/
|
||||
|
@@ -881,6 +881,22 @@
|
||||
"collection.select.table.title": "Title",
|
||||
|
||||
|
||||
"collection.source.controls.head": "Harvest Controls",
|
||||
"collection.source.controls.test.submit.error": "Something went wrong with initiating the testing of the settings",
|
||||
"collection.source.controls.test.failed": "The script to test the settings has failed",
|
||||
"collection.source.controls.test.completed": "The script to test the settings has successfully finished",
|
||||
"collection.source.controls.test.submit": "Test configuration",
|
||||
"collection.source.controls.import.submit.success": "The import has been successfully initiated",
|
||||
"collection.source.controls.import.submit.error": "Something went wrong with initiating the import",
|
||||
"collection.source.controls.import.submit": "Import now",
|
||||
"collection.source.controls.import.failed": "An error occurred during the import",
|
||||
"collection.source.controls.import.completed": "The import completed",
|
||||
"collection.source.controls.reset.submit": "Reset and reimport",
|
||||
"collection.source.controls.harvest.status": "Harvest status:",
|
||||
"collection.source.controls.harvest.start": "Harvest start time:",
|
||||
"collection.source.controls.harvest.last": "Last time harvested:",
|
||||
"collection.source.controls.harvest.no-information": "N/A",
|
||||
|
||||
|
||||
"collection.source.update.notifications.error.content": "The provided settings have been tested and didn't work.",
|
||||
|
||||
|
Reference in New Issue
Block a user