diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 8c31ce0ad70293d0dd58be3433d856f744407754..c40405f3e423aa84402c07145aee5800dc9b0c8d 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -36,12 +36,13 @@ import { MappingComponent } from './mapping/mapping.component'; import { NebularModule } from './nebular.module'; import { AppRoutingModule } from './app-routing.module'; -import { NbLayoutModule, NbThemeModule, NbTooltipModule, NbSpinnerModule, NbSelectModule, NbTabsetModule, NbAutocompleteModule, NbListModule, NbAccordionModule } from '@nebular/theme'; +import { NbLayoutModule, NbThemeModule, NbTooltipModule, NbSpinnerModule, NbSelectModule, NbTabsetModule, NbAutocompleteModule, NbListModule, NbAccordionModule, NbDialogModule } from '@nebular/theme'; import { DashboardComponent } from './dashboard/dashboard.component'; +import { FeedbackDialogComponent } from './mapping/dialog/feedback-dialog/feedback-dialog.component'; import { NbToastrModule } from '@nebular/theme'; import { DatasetsDialogComponent } from './datasets/datasets-dialog/datasets-dialog.component'; -import { NbDialogModule } from '@nebular/theme'; + @@ -61,6 +62,7 @@ import { NbDialogModule } from '@nebular/theme'; StatsComponent, DashboardComponent, MappingComponent, + FeedbackDialogComponent, DatasetsDialogComponent, ], imports: [ @@ -94,7 +96,7 @@ import { NbDialogModule } from '@nebular/theme'; NbToastrModule.forRoot(), NbDialogModule.forRoot(), ], - entryComponents: [DatasetsDialogComponent], + entryComponents: [DatasetsDialogComponent, FeedbackDialogComponent], providers: [ AppConfiguration, ParseXmlService, diff --git a/src/app/datasets/datasets.component.ts b/src/app/datasets/datasets.component.ts index f80db3dc76d9ea9eb84f546c6fb3d98a77aefcaf..f7db5d4bd21a71d1b3d53007cd34a46c2d880634 100644 --- a/src/app/datasets/datasets.component.ts +++ b/src/app/datasets/datasets.component.ts @@ -56,7 +56,7 @@ export class DatasetsComponent implements OnInit, OnChanges { } ngOnInit() { - this.dataSetService.itemsDataset = []; + this.dataSetService.resetDataset() } @@ -315,7 +315,7 @@ export class DatasetsComponent implements OnInit, OnChanges { for (const id of datasetId) { const requestPromise = this.launchRequest(datasetRequestPathName, datasetRequestHttpMethod, false, id) .then((dataset) => { - this.dataSetService.saveDatasets(dataset); + this.dataSetService.saveDatasets(dataset, id); }); requestPromises.push(requestPromise); } diff --git a/src/app/datasets/services/dataset-crud.service.ts b/src/app/datasets/services/dataset-crud.service.ts index 0199c6eb7dbbf31601e1136f9456867983a54341..73ba27ffa86b80dace78a65732911ec4e2bacce9 100644 --- a/src/app/datasets/services/dataset-crud.service.ts +++ b/src/app/datasets/services/dataset-crud.service.ts @@ -15,20 +15,22 @@ export class DatasetCrudService { fds2Token: string = this.sessionStorage.getFDPToken(); public results: string[] = []; itemsDataset: Object[] = []; + ids: number[] = []; constructor(private http: HttpClient, private appConfig: AppConfiguration, private parserService: ParseXmlService, private sessionStorage: TokenStorageService) { } - createDataset(data: string): Observable<any> { + async createDataset(data: string): Promise<any> { if (this.fds2Token) { - const httpOptions = { - headers: new HttpHeaders({ - 'Accept': 'text/turtle', - 'Content-Type': 'text/turtle', - 'Authorization': 'Bearer ' + this.fds2Token - }) - }; + const httpOptions = new Headers(); + httpOptions.append( 'Accept', 'text/turtle'); + httpOptions.append('Content-Type', 'text/turtle'); + httpOptions.append('Authorization', 'Bearer '+ this.fds2Token ); + + + const myInit = { method: 'POST', body: data , headers: httpOptions }; + const myRequest = new Request(`${FDP_URL}/dataset`, myInit); - return this.http.post(FDP_URL + "/dataset", data, httpOptions); + return fetch(myRequest, myInit) } } @@ -104,12 +106,16 @@ export class DatasetCrudService { getLocally<T = any>(path: string): Observable<T> { return this.http.get<T>(`${path}`); } - saveDatasets(obj: Object) { + saveDatasets(obj: Object, id: number) { this.itemsDataset.push(obj); + this.ids.push(id); } resetDataset() { this.itemsDataset = []; + this.ids = []; } + + } diff --git a/src/app/mapping/dialog/feedback-dialog/feedback-dialog.component.html b/src/app/mapping/dialog/feedback-dialog/feedback-dialog.component.html new file mode 100644 index 0000000000000000000000000000000000000000..176c0ca3ffd9bc21f19f36bfdef1cdf3c68d7aad --- /dev/null +++ b/src/app/mapping/dialog/feedback-dialog/feedback-dialog.component.html @@ -0,0 +1,30 @@ +<nb-card [size]="'medium'"> + <nb-card-header> + <h3 style="text-align: center;">Feedback: </h3> + </nb-card-header> + <nb-card-body> + <nb-card > + <nb-card-body> + <p class="error" *ngIf="data.notPostedMetadatas.length > 0">An error occurred while publishing the following Datasets: </p> + <ul class="error" *ngFor="let data of data.notPostedMetadatas"> + <li class="error">{{ data }}</li> + </ul> + </nb-card-body> + </nb-card> + <nb-card> + <nb-card-body> + <p class="success" *ngIf="data.postedMetadatas.length > 0">The following datasets have been published successfully: </p> + <ul *ngFor="let data of data.postedMetadatas"> + <li class="success" >{{ data }}</li> + </ul> + </nb-card-body> + </nb-card> + </nb-card-body> + <nb-card-footer> + <div class="row"> + <div class="button-center"> + <button nbButton status="primary" (click)="close()">Close</button> + </div> + </div> + </nb-card-footer> +</nb-card> \ No newline at end of file diff --git a/src/app/mapping/dialog/feedback-dialog/feedback-dialog.component.scss b/src/app/mapping/dialog/feedback-dialog/feedback-dialog.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..ee702541104cc5b078a21109158390c37dbe8e6d --- /dev/null +++ b/src/app/mapping/dialog/feedback-dialog/feedback-dialog.component.scss @@ -0,0 +1,8 @@ +.error { + color: crimson; +} + +.success { + color: forestgreen; +} + diff --git a/src/app/mapping/dialog/feedback-dialog/feedback-dialog.component.spec.ts b/src/app/mapping/dialog/feedback-dialog/feedback-dialog.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..a1639307eb8688facec2c3cbae846b028965fd97 --- /dev/null +++ b/src/app/mapping/dialog/feedback-dialog/feedback-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FeedbackDialogComponent } from './feedback-dialog.component'; + +describe('FeedbackDialogComponent', () => { + let component: FeedbackDialogComponent; + let fixture: ComponentFixture<FeedbackDialogComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ FeedbackDialogComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FeedbackDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/mapping/dialog/feedback-dialog/feedback-dialog.component.ts b/src/app/mapping/dialog/feedback-dialog/feedback-dialog.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..0cde1a03f9bf127e8a762746a1e417bdc0aef548 --- /dev/null +++ b/src/app/mapping/dialog/feedback-dialog/feedback-dialog.component.ts @@ -0,0 +1,24 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { NbDialogRef } from '@nebular/theme'; + +@Component({ + selector: 'app-feedback-dialog', + templateUrl: './feedback-dialog.component.html', + styleUrls: ['./feedback-dialog.component.scss'] +}) +export class FeedbackDialogComponent implements OnInit { + + + + constructor(private dialog: MatDialogRef<FeedbackDialogComponent>, + @Inject(MAT_DIALOG_DATA) public data: {postedMetadatas: string[], notPostedMetadatas: string[]} ) { } + + ngOnInit(): void { + } + + close() { + this.dialog.close(); + } + +} diff --git a/src/app/mapping/mapping.component.html b/src/app/mapping/mapping.component.html index b9235c6fccde0b333264266b9b92b9b7b7c8a75e..eea8261a50e724c521a6a8658b5f7837edaea0ac 100644 --- a/src/app/mapping/mapping.component.html +++ b/src/app/mapping/mapping.component.html @@ -1,8 +1,7 @@ <form #form="ngForm"> <div class="card-row"> <div class="card-col"> - <nb-card size="giant" [nbSpinner]="loading" nbSpinnerStatus="primary" nbSpinnerSize="large" - nbSpinnerMessage="Loading..."> + <nb-card size="giant" > <nb-card-header>Dataset metadata</nb-card-header> <nb-card-body> @@ -35,7 +34,7 @@ required #autoInput #{{dataset.identifier}}="ngModel" fullWidth id="{{dataset.identifier}}" name="{{dataset.identifier}}" nbInput (input)="onChange()" placeholder="Enter value" [nbAutocomplete]="auto" - [(ngModel)]="selectedPaths[index]" (focus)="reset()" /> + [(ngModel)]="selectedPaths[index]" autocomplete="false" (focus)="reset()" /> <div *ngIf="dataset.identifier.invalid && (dataset.identifier.dirty || dataset.identifier.touched)"> champ obligatoire</div> @@ -44,7 +43,7 @@ #autoInput #{{dataset.identifier}}="ngModel" fullWidth id="{{dataset.identifier}}" name="{{dataset.identifier}}" nbInput (input)="onChange()" placeholder="Enter value" [nbAutocomplete]="auto" - [(ngModel)]="selectedPaths[index]" (focus)="reset()" /> + [(ngModel)]="selectedPaths[index]" autocomplete="false" (focus)="reset()" /> <nb-autocomplete #auto> @@ -71,7 +70,7 @@ </form> <div class="card-row"> <div class="card-col"> - <nb-card [nbSpinner]="loading" nbSpinnerStatus="primary" nbSpinnerSize="large" nbSpinnerMessage="loading"> + <nb-card [nbSpinner]="loadingCr" nbSpinnerStatus="primary" nbSpinnerSize="large" nbSpinnerMessage="loading"> <nb-card-header>Map</nb-card-header> <nb-card-body> <nb-list> @@ -79,7 +78,7 @@ <nb-list-item *ngFor="let data of mappedMetadatas[index] | keyvalue; trackBy:trackByIndex; "> <div class="row"> <div class="col-5"><label for="{{data.key}}">{{data.key}}</label></div> - <div class="col-5"> <input nbInput autofocus [ngModel]="data.value" + <div class="col-5"> <input nbInput [ngModel]="data.value" (ngModelChange)="mappedMetadatas[index].set(data.key, $event)" /> </div> <div class="col-2"><button nbButton ghost> <nb-icon icon="trash-2-outline" status="danger" (click)="deleteProperty(data.key)"> @@ -108,11 +107,9 @@ </div> <div class="row"> <div class="button-center" *ngIf="!first "> - <button nbButton status="primary" (click)=" publishDataset()">Publish</button> + <button nbButton status="primary" (click)=" publishDataset()" [nbSpinner]="loading" nbSpinnerStatus="basic" >Publish</button> </div> </div> - <nb-alert status="success" *ngIf="messageOk" closable style="margin-top: 10px;">{{ messageOk }} - </nb-alert> </nb-card-footer> </nb-card> diff --git a/src/app/mapping/mapping.component.ts b/src/app/mapping/mapping.component.ts index 9c09a0918c31c30819be9397dc0dc99a9c497e90..0b54724dd06582b84f18413325ae0824ed56f5af 100644 --- a/src/app/mapping/mapping.component.ts +++ b/src/app/mapping/mapping.component.ts @@ -1,11 +1,17 @@ + +import { HttpResponse } from '@angular/common/http'; import { Component, Input, OnInit, ViewChild } from '@angular/core'; -import { Observable, of } from 'rxjs'; +import { MatDialog } from '@angular/material/dialog'; +import { Router } from '@angular/router'; + +import { Observable, of, Subscription } from 'rxjs'; import { map } from 'rxjs/operators'; import { environment } from 'src/environments/environment.prod'; import { DatasetCrudService } from '../datasets/services/dataset-crud.service'; -import { ParseXmlService } from '../services/parse-xml.service'; + import { Dataset } from './class/dataset'; +import { FeedbackDialogComponent } from './dialog/feedback-dialog/feedback-dialog.component'; @Component({ @@ -27,13 +33,14 @@ export class MappingComponent implements OnInit { index: number = 0 first: boolean = true; loading: boolean = false; + loadingCr = false; FDP_URL = environment.fdpUrl; - messageOk: string; + ids: number[]; @ViewChild('autoInput') input; @Input() catalogId: any; - constructor(private dataSetService: DatasetCrudService, private parserService: ParseXmlService) { } + constructor(private dataSetService: DatasetCrudService, private dialog: MatDialog, private route: Router) { } ngOnInit() { this.dataSetService.getLocally('./assets/dataset.json').subscribe( @@ -54,17 +61,16 @@ export class MappingComponent implements OnInit { populatecatalogue() { this.itemsdataset = this.dataSetService.itemsDataset; + this.ids = this.dataSetService.ids; + this.ids this.keys = []; - for (let i = 0; i < this.itemsdataset.length; i++) { - if (i === 0) { - this.getKeysFromMetadata(this.itemsdataset[i], ''); - this.keysMap.set(this.itemsdataset.length, this.keys); - this.filteredOptions = of(this.keysMap.get(1)); - } - } + this.getKeysFromMetadata(this.itemsdataset[0], ''); + this.keysMap.set(this.itemsdataset.length, this.keys); + this.filteredOptions = of(this.keysMap.get(1)); } createDataset(item: Object): Map<string, string> { + this.loadingCr = true; let mappedMetadata: Map<string, string> = new Map() for (let i = 0; i < this.selectedPaths.length; i++) { if (this.selectedPaths[i]) { @@ -72,18 +78,21 @@ export class MappingComponent implements OnInit { mappedMetadata.set(this.datasetModel[i].identifier, this.getValue(tab, item)); } } + this.loadingCr = false return mappedMetadata; } publishDataset() { let data: string = ''; let properties: string = ''; - let result = ''; - + let postedDatastes = []; + let notPostedDatasets = []; + this.loading = true; + const requestPromises: Promise<any>[] = []; for (let i = 0; i < this.mappedMetadatas.length; i++) { + let title = ""; properties = ""; - this.messageOk = ""; this.mappedMetadatas[i].forEach((value: string, key: string) => { if (key === 'dct:title') { title = value }; switch (key) { @@ -112,29 +121,65 @@ export class MappingComponent implements OnInit { a dcat:Dataset, dcat:Resource;\n\ dct:isPartOf c:'+ this.catalogId + ';\n' + properties + '.'; - let query = 'query=PREFIX dcat: <http://www.w3.org/ns/dcat#>\n\ + /*let query = 'query=PREFIX dcat: <http://www.w3.org/ns/dcat#>\n\ PREFIX dct: <http://purl.org/dc/terms/>\n\ SELECT ?dataset\n\ where {\n\ ?dataset a dcat:Dataset;\n\ dct:title "' + title + '" ;\n\ - dct:isPartOf <https://f2ds.eosc-pillar.eu/catalog/' + this.catalogId + '>. } ' - - console.log('data: ' + data); - - this.dataSetService.createDataset(data).subscribe( - r => { - console.log(r); - this.loading = true; - }, - () => { - this.messageOk = "Dataset published successfully" - this.loading = false; + dct:isPartOf <https://f2ds.eosc-pillar.eu/catalog/' + this.catalogId + '>. } '*/ + + + let requestPromise = this.dataSetService.createDataset(data).then((resp: HttpResponse<any>) => { + if (resp.status.toString().startsWith('2')) { + postedDatastes.push(this.ids[i]); + } else { + notPostedDatasets.push(this.ids[i]); } - ) + }) + requestPromises.push(requestPromise); } + Promise.all(requestPromises).finally(() => { + this.loading = false; + this.dialog.open(FeedbackDialogComponent, { + data: { + postedMetadatas: postedDatastes, + notPostedMetadatas: notPostedDatasets + } + }).afterClosed().subscribe(() => this.route.navigateByUrl('/dashboard')); + + }) } + /* function to get recursively all the keys and values from the JSON object */ + getKeysFromMetadata(obj: Object, keyParent: string) { + + Object.keys(obj).forEach(key => { + if (typeof obj[key] === 'object') { + if (Array.isArray(obj[key])) { + obj[key].forEach(e => { + if (typeof e === 'object') { + this.getKeysFromMetadata(e, keyParent + ' : ' + key + ' : ' + obj[key].indexOf(e)); + } else { + this.keys.push(keyParent + ' : ' + key + ' : ' + obj[key].indexOf(e)) + } + }); + } else { + if (keyParent) { + this.getKeysFromMetadata(obj[key], keyParent + ' : ' + key); + } else { + this.getKeysFromMetadata(obj[key], key); + } + } + } else { + if (keyParent) { + this.keys.push(keyParent + ' : ' + key) + } else { + this.keys.push(key) + } + } + }); + } private getValue(tab: string[], item: Object): string { let obj: Object @@ -150,12 +195,39 @@ export class MappingComponent implements OnInit { if (obj[tab[i]]) { return obj[tab[i]]; } else { - return 'undefined'; + if (i == tab.length - 2) { + this.getKeysFromMetadata(item, ""); + console.log(tab[i]) + this.keys.forEach(k => { + let table = k.split(" : "); + if (table[table.length - 2] === tab[i]) { + for (let i = 0; i < tab.length; i++) { + if (table.length == 1) { + return item[tab[0]]; + } else { + if (i == 0) { + obj = item[table[i]] + } else if (i < table.length - 1 && obj[table[i]]) { + obj = obj[table[i]]; + } else { + if (obj[table[i]]) { + return obj[table[i]]; + } else { + return 'undefined'; + } + } + } + } + } + }) + } } } } } } + + mapDataset() { this.mappedMetadatas = []; this.itemsdataset.forEach((dataset: Object) => { @@ -165,6 +237,7 @@ export class MappingComponent implements OnInit { this.first = false; } next() { + this.loadingCr = true; if (this.index < this.itemsdataset.length) { this.index += 1; this.createDataset(this.itemsdataset[this.index]) @@ -172,6 +245,7 @@ export class MappingComponent implements OnInit { } } prev() { + this.loadingCr = true; if (this.index > 0) { this.index -= 1; } @@ -184,36 +258,6 @@ export class MappingComponent implements OnInit { this.mappedMetadatas[this.index].delete(key); } - /* function to get recursively all the keys and values from the JSON object */ - getKeysFromMetadata(obj: Object, keyParent: string) { - - Object.keys(obj).forEach(key => { - if (typeof obj[key] === 'object') { - if (Array.isArray(obj[key])) { - obj[key].forEach(e => { - if (typeof e === 'object') { - this.getKeysFromMetadata(e, keyParent + ' : ' + key + ' : ' + obj[key].indexOf(e)); - } else { - this.keys.push(keyParent + ' : ' + key + ' : ' + obj[key].indexOf(e)) - } - }); - } else { - if (keyParent) { - this.getKeysFromMetadata(obj[key], keyParent + ' : ' + key); - } else { - this.getKeysFromMetadata(obj[key], key); - } - } - } else { - if (keyParent) { - this.keys.push(keyParent + ' : ' + key) - } else { - this.keys.push(key) - } - } - }); - } - trackByIndex(index: number, obj: any): any { return index; }