diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 895fa509ecad543c9f8f49852de3f56842bec466..16c8bbc2057b5f2fdf556aeb01b38a8deec93d71 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -12,7 +12,8 @@ import { FdpGuard } from './authentication/services/fdp.guard'; import { RepositoryinfoComponent } from './repositoryinfo/repositoryinfo.component'; import { PublishApiComponent } from './publishapi/publishapi.component'; import {SemanticEnrichmentComponent} from './semantic-enrichment/semantic-enrichment.component'; -import {EditionComponent} from './edition/edition.component'; +import {CatalogComponent} from './catalog/catalog.component'; +import {DatasetComponent} from './dataset/dataset.component'; export interface ICustomRoute extends Route { name?: string; @@ -28,7 +29,8 @@ const routes: ICustomRoute[] = [ { path: 'repositoryinfo', component: RepositoryinfoComponent }, { path: 'stats', component: StatsComponent }, { path: 'publishapi', component: PublishApiComponent }, - { path: 'edit/:id', component: EditionComponent} + { path: 'catalog/:id', component: CatalogComponent }, + { path: 'dataset/:id', component: DatasetComponent } ] }, {path: 'fdpsignin', component: SignupComponent, canActivate: [AuthGuardService]}, diff --git a/src/app/app.module.ts b/src/app/app.module.ts index f199402feeddbcfd8cfbc8596d5c0c502d6a8448..424778697dc9e2f9bad3019bbf4f3fc00e77f536 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -48,7 +48,8 @@ import { SemanticEnrichmentComponent } from './semantic-enrichment/semantic-enri import {MatAutocompleteModule} from '@angular/material/autocomplete'; import {MatFormFieldModule} from '@angular/material/form-field'; import { OrderByScorePipe } from './semantic-enrichment/pipes/order-by-score.pipe'; -import { EditionComponent } from './edition/edition.component'; +import { CatalogComponent } from './catalog/catalog.component'; +import { DatasetComponent } from './dataset/dataset.component'; @@ -70,7 +71,8 @@ import { EditionComponent } from './edition/edition.component'; CallbackComponent, SemanticEnrichmentComponent, OrderByScorePipe, - EditionComponent, + CatalogComponent, + DatasetComponent, ], imports: [ diff --git a/src/app/authentication/services/token-storage.service.ts b/src/app/authentication/services/token-storage.service.ts index b949094e4f57e64c3ac0d6223782f02720adcd33..7056492bacbae28dc574e2a8024a827a91cf2408 100644 --- a/src/app/authentication/services/token-storage.service.ts +++ b/src/app/authentication/services/token-storage.service.ts @@ -17,9 +17,9 @@ export class TokenStorageService { public saveTokenSmartHarveser(SmartHarvesterToken: string): void { window.sessionStorage.removeItem(TOKEN_KEY); - + window.sessionStorage.setItem(TOKEN_KEY, SmartHarvesterToken); - + } public saveTokenFDP(fdpToken: string): void { @@ -41,11 +41,11 @@ export class TokenStorageService { public getUser(): any { return JSON.parse(sessionStorage.getItem(USER_KEY)); } - public removeToken(){ + public removeToken() { window.sessionStorage.removeItem(TOKEN_KEY); window.sessionStorage.removeItem(USER_KEY); } - + } diff --git a/src/app/authentication/signup/signup.component.ts b/src/app/authentication/signup/signup.component.ts index 3955dc577421b017f48b4a8bd46f85f189717325..030c76831e097801e5a4ddc3503e2d0a5692d9a9 100644 --- a/src/app/authentication/signup/signup.component.ts +++ b/src/app/authentication/signup/signup.component.ts @@ -23,14 +23,14 @@ export class SignupComponent implements OnInit, OnDestroy { errorMessage = ''; showPassword = false; user: SmartHarvesterUser = new SmartHarvesterUser(); - userFDP: SmartHarvesterUser = new SmartHarvesterUser();$ + userFDP: SmartHarvesterUser = new SmartHarvesterUser(); private _isDead = new Subject(); constructor(private authService: AuthService, private router: Router, private storageService: TokenStorageService, private http: HttpClient) { - + } - + ngOnInit(): void { const httpOptions = { @@ -97,7 +97,7 @@ export class SignupComponent implements OnInit, OnDestroy { err => console.log(err), () => this.storageService.removeToken() ); - + } getInputType() { diff --git a/src/app/catalog/catalog.component.html b/src/app/catalog/catalog.component.html new file mode 100644 index 0000000000000000000000000000000000000000..9eeaf2c3457c26ce1e2889833a6beb27ec8ea06c --- /dev/null +++ b/src/app/catalog/catalog.component.html @@ -0,0 +1,34 @@ +<ng-container *ngIf="!isLoading else loading"> + <ng-container *ngFor="let dataset of datasets" [nbSpinner]="isLoading" > + <nb-card accent="info" class="card-background" (click)="edit(dataset.id)"> + <nb-card-header>{{dataset.title}}</nb-card-header> + <nb-card-body> + {{dataset.description}} + </nb-card-body> + <nb-card-footer> + <ul> + <li *ngIf="dataset.keywords">keywords: <span *ngFor="let keyword of dataset.keywords | keyvalue">{{keyword.key}}, </span></li> + <li *ngIf="dataset.conceptIri">themes: <span *ngFor="let theme of dataset.conceptIri ">{{theme}}, </span></li> + </ul> + </nb-card-footer> + </nb-card> + </ng-container> +</ng-container> + +<ng-template #loading xmlns="http://www.w3.org/1999/html"> + <nb-card size="small" [nbSpinner]="true" nbSpinnerStatus="primary" nbSpinnerSize="giant"></nb-card> +</ng-template> + +<ng-template #dialog let-data let-ref="dialogRef"> + <nb-card> + <nb-card-body>{{ messageError }}</nb-card-body> + <nb-card-footer> + <button nbButton (click)="getDatasetByCatalogId(); ref.close()">Retry</button> + <button nbButton (click)="ref.close()">Close</button> + </nb-card-footer> + </nb-card> +</ng-template> + + + + diff --git a/src/app/catalog/catalog.component.scss b/src/app/catalog/catalog.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..33f8498ca3005c20900b3235396cb7bf2ade47ca --- /dev/null +++ b/src/app/catalog/catalog.component.scss @@ -0,0 +1,13 @@ +.button-center{ + vertical-align: middle; + margin: auto +} + +.half-width { + width: 50%; +} +.card-background{ + card-background-color: #a5a5a5; + cursor: pointer; +} + diff --git a/src/app/catalog/catalog.component.ts b/src/app/catalog/catalog.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..39cf50d1ac33c786691a5a55d37af674c3186e81 --- /dev/null +++ b/src/app/catalog/catalog.component.ts @@ -0,0 +1,67 @@ +import {Component, OnInit, TemplateRef, ViewChild} from '@angular/core'; +import {ActivatedRoute, Router} from '@angular/router'; +import {CatalogService} from './service/catalog.service'; +import {KeywordResponse} from '../mapping/class/dataset'; +import {Observable, of} from 'rxjs'; +import {ESModel, Result} from '../semantic-enrichment/ESModel'; +import {map} from 'rxjs/operators'; +import {PostService} from '../semantic-enrichment/services/post.service'; +import {NbDialogService} from '@nebular/theme'; +import {MatAutocompleteSelectedEvent} from '@angular/material/autocomplete'; +import {ConceptsRequest, DataConcept} from '../semantic-enrichment/ConceptsRequest'; +import {FeedbackDialogComponent} from '../mapping/dialog/feedback-dialog/feedback-dialog.component'; +import {MatDialog} from '@angular/material/dialog'; + +@Component({ + selector: 'app-catalog', + templateUrl: './catalog.component.html', + styleUrls: ['./catalog.component.scss'] +}) +export class CatalogComponent implements OnInit { + + @ViewChild('dialog') dialog: TemplateRef<any>; + datasets: KeywordResponse[] = []; + isLoading: boolean; + messageError: string; + values: string[] = []; + id: string; + + constructor(private route: ActivatedRoute, private service: CatalogService, + private dialogService: NbDialogService, + private router: Router) { + + } + + ngOnInit(): void { + this.getDatasetByCatalogId(); + + } + + getDatasetByCatalogId() { + this.isLoading = true; + this.route.params.subscribe(param => { + this.service.getCatalog(param.id).subscribe( + (response: KeywordResponse[]) => { + this.datasets = response; + }, + error => { + this.isLoading = false; + this.messageError = error.message; + this.openDialog(this.dialog); + }, + () => this.isLoading = false + ); + }); + } + + openDialog(dialog: TemplateRef<any>) { + this.dialogService.open(dialog, { + context: { + }, + }); + } + + edit(id: string) { + this.router.navigate(['/dashboard/dataset', id]); + } +} diff --git a/src/app/edition/service/edit.service.ts b/src/app/catalog/service/catalog.service.ts similarity index 76% rename from src/app/edition/service/edit.service.ts rename to src/app/catalog/service/catalog.service.ts index abd1f0ea80e25bc404cba7dc426868dc49520e72..317fb599c6b376cb3831fbfeb74d07666b41b9a9 100644 --- a/src/app/edition/service/edit.service.ts +++ b/src/app/catalog/service/catalog.service.ts @@ -10,7 +10,7 @@ import {ConceptsRequest} from '../../semantic-enrichment/ConceptsRequest'; @Injectable({ providedIn: 'root' }) -export class EditService { +export class CatalogService { fdpToken = this.tokenService.getFDPToken(); fdpURl = environment.fdpUrl; @@ -35,6 +35,21 @@ export class EditService { ); } + getDataset(datasetId: string): Observable<KeywordResponse> { + const httpOptions = { + headers: new HttpHeaders({ + Authorization: 'Bearer ' + this.smartharveserToken + }) + }; + if (!environment.staging && !environment.production) { + this.fdpURl = this.fdpURl.replace(':8080', ''); + } + return this.http.get<KeywordResponse>( + `${this.smartHarvesterUrl}/harvester/api/dataset/${datasetId}?baseUrl=${this.fdpURl}&token=${this.fdpToken}`, + httpOptions + ); + } + editDataset(data: ConceptsRequest): Observable<any> { const httpOptionsFDP = { headers: new HttpHeaders({ diff --git a/src/app/dashboard/dashboard.component.html b/src/app/dashboard/dashboard.component.html index 0b462b964bf6830f13e00198f8f0d1aa3d2861d1..bfdedf94031f4b2f64503192144358113f4240ab 100644 --- a/src/app/dashboard/dashboard.component.html +++ b/src/app/dashboard/dashboard.component.html @@ -5,30 +5,29 @@ <img width="80" alt="Angular Logo" src="assets/images/logo.png" /> </a> <h3 style="width: 100%;text-align: center;"> <strong></strong></h3> - + <!--User badge--> - - <nb-user style="margin-right: 10px;" - name="{{user.firstName}}" + + <nb-user style="margin-right: 10px;" + name="{{user.firstName}}" title="{{user.lastName}}" [nbContextMenu]="menuItems" - nbContextMenuTag="my-context-menu" + nbContextMenuTag="my-context-menu" badgePosition="right"> </nb-user> <button (click)="logout()" nbContextMenuPlacement="right" status="danger" outline nbButton>Logout</button> - + </nb-layout-header> - + <nb-sidebar responsive start state="compacted"> <!--Menu Items--> <nb-menu [items]="menuItems" autoCollapse="true"></nb-menu> </nb-sidebar> <nb-layout-column> - <app-stats *ngIf="routerUrl === '/dashboard'"></app-stats> + <app-stats *ngIf="routerUrl === '/dashboard' && loadComponent"></app-stats> <router-outlet></router-outlet> </nb-layout-column> <nb-layout-footer>Contact us</nb-layout-footer> - + </nb-layout> - \ No newline at end of file diff --git a/src/app/dashboard/dashboard.component.ts b/src/app/dashboard/dashboard.component.ts index abde7a53cb4a242f79f73a85ff0d69f5c70b1e12..fe0bad2a18a1a6137ba8b39706ba21b000a6a00e 100644 --- a/src/app/dashboard/dashboard.component.ts +++ b/src/app/dashboard/dashboard.component.ts @@ -18,6 +18,7 @@ export class DashboardComponent implements OnInit { user: SmartHarvesterUser = new SmartHarvesterUser(); routerUrl: string; + loadComponent: boolean; private _isDead = new Subject(); menuItems: NbMenuItem[] = [ { @@ -102,6 +103,7 @@ export class DashboardComponent implements OnInit { this.user.lastName = data['principal']['userInfo']['familyName']; this.user.firstName = data['principal']['userInfo']['givenName']; this.tokeService.saveUser(this.user); + this.loadComponent = true; }, err => console.log(err.error.message), () => {}); @@ -121,6 +123,6 @@ export class DashboardComponent implements OnInit { err => console.log(err), () => this.tokeService.removeToken() ); - + } } diff --git a/src/app/dataset/dataset.component.html b/src/app/dataset/dataset.component.html new file mode 100644 index 0000000000000000000000000000000000000000..23b9a6554d5ac147ed7bc706b373a7dc31af2f98 --- /dev/null +++ b/src/app/dataset/dataset.component.html @@ -0,0 +1,155 @@ + + +<ng-template #noConcept> + <nb-card> + <nb-card-body> + <h5>No concepts found...</h5> + </nb-card-body> + </nb-card> +</ng-template> + +<ng-template #noKeywordsFound> + <nb-card> + <nb-card-body> + <h5>No keywords found...</h5> + </nb-card-body> + </nb-card> +</ng-template> + + +<ng-container *ngIf="!isLoading; else loading"> + <ng-container> + <h4><a href="{{dataset.url}}" target="_blank">{{dataset.title}}</a> + </h4> + + <nb-card *ngIf="dataset.keywords; else noKeywordsFound" [size]="'tiny'"> + <nb-card-header> + Keyword(s) found + </nb-card-header> + <nb-list> + <nb-list-item *ngFor="let keyword of dataset.keywords | keyvalue"> + {{ keyword.key }} + </nb-list-item> + </nb-list> + </nb-card> + + <nb-card *ngIf="dataset.conceptIri.length > 0; else noConcept; let i = index" [size]="'tiny'"> + <nb-card-header> + Concept iri found + </nb-card-header> + <nb-list> + <nb-list-item *ngFor="let k of dataset.conceptIri"> + <span><button nbButton ghost> + <nb-icon icon="trash-2-outline" status="danger" + (click)="deleteExistingConcepts(i)"> + </nb-icon> + </button></span> {{ k }} + </nb-list-item> + </nb-list> + </nb-card> + <nb-card> + <nb-card-body> + <mat-form-field class="half-width"> + <input + type="text" + placeholder="enter a value with at least 4 characters" + [(ngModel)]="value" + (ngModelChange)="onModelChange($event)" + matInput + [matAutocomplete]="auto"> + <mat-autocomplete #auto="matAutocomplete" [displayWith]="viewHandle" + (optionSelected)="getResult($event)"> + <mat-option + *ngFor="let option of filteredOptions | async" + [value]="option"> + {{option.source.document.label}} + </mat-option> + </mat-autocomplete> + + </mat-form-field> + <div class="mt0-m"> + <table *ngIf="autocompleteResults.length > 0"> + <thead style="background-color: #3366ff"> + <th></th> + <th class="text-center">label</th> + <th class="text-center">definition</th> + <th class="text-center">synonyms</th> + </thead> + <tbody> + <tr *ngFor="let value of autocompleteResults; let i = index "> + <td> + <button nbButton ghost> + <nb-icon icon="trash-2-outline" status="danger" + (click)="deleteProperty(i)"> + </nb-icon> + </button> + </td> + <td class="text-center"><a href="{{value.source.document.iri}}" + target="_blank">{{ value.source.document.label }}</a></td> + <td class="text-center">{{ value.source.document.description }}</td> + <td class="text-center">{{ value.source.document.synonyms }}</td> + + </tr> + </tbody> + </table> + </div> + </nb-card-body> + </nb-card> + <nb-card [size]="'small'" *ngIf="dataset.keywords"> + <nb-card-body> + <nb-tabset> + <nb-tab *ngFor="let objet of dataset.keywords | keyvalue" tabTitle="{{(objet.key)}}"> + + <table *ngIf="objet.value.results; else noConcept"> + <thead style="background-color: #3366ff"> + <th></th> + <th class="text-center">label</th> + <th class="text-center">definition</th> + <th class="text-center">synonyms</th> + <th class="text-center">score</th> + </thead> + <tbody> + <tr *ngFor="let result of (objet.value).results "> + <td class="text-center"> + <nb-checkbox [(checked)]="result.checked"></nb-checkbox> + </td> + <td class="text-center"><a href="{{result.source.document.iri}}" + target="_blank">{{ result.source.document.label }}</a></td> + <td class="text-center">{{ result.source.document.description }}</td> + <td class="text-center">{{ result.source.document.synonyms }}</td> + <td class="text-center">{{ result.score | number: '2.2-2' }}</td> + </tr> + </tbody> + </table> + </nb-tab> + </nb-tabset> + </nb-card-body> + </nb-card> + + </ng-container> + + + <div class="row"> + <div class="button-center"> + <button nbButton status="primary" (click)="onSubmit()" [nbSpinner]="loadingPublish" [disabled]="loadingPublish" + nbSpinnerStatus="basic">Edit + </button> + </div> + </div> +</ng-container> + + +<ng-template #loading xmlns="http://www.w3.org/1999/html"> + <nb-card size="small" [nbSpinner]="true" nbSpinnerStatus="primary" nbSpinnerSize="giant"></nb-card> +</ng-template> + +<ng-template #dialog let-data let-ref="dialogRef"> + <nb-card> + <nb-card-body>{{ messageError }}</nb-card-body> + <nb-card-footer> + <button nbButton (click)="getDataset(); ref.close()">Retry</button> + <button nbButton (click)="ref.close()">Close</button> + </nb-card-footer> + </nb-card> +</ng-template> + diff --git a/src/app/edition/edition.component.scss b/src/app/dataset/dataset.component.scss similarity index 100% rename from src/app/edition/edition.component.scss rename to src/app/dataset/dataset.component.scss diff --git a/src/app/edition/edition.component.ts b/src/app/dataset/dataset.component.ts similarity index 51% rename from src/app/edition/edition.component.ts rename to src/app/dataset/dataset.component.ts index 75d94d5d8dcd31952e391bb6b79a73cfa58bf7bf..156fdaa1e70a62d4a4183379f1152369e400be73 100644 --- a/src/app/edition/edition.component.ts +++ b/src/app/dataset/dataset.component.ts @@ -1,61 +1,53 @@ import {Component, OnInit, TemplateRef, ViewChild} from '@angular/core'; import {ActivatedRoute} from '@angular/router'; -import {EditService} from './service/edit.service'; import {KeywordResponse} from '../mapping/class/dataset'; -import {Observable, of} from 'rxjs'; -import {Result} from '../semantic-enrichment/ESModel'; -import {map} from 'rxjs/operators'; +import {CatalogService} from '../catalog/service/catalog.service'; import {PostService} from '../semantic-enrichment/services/post.service'; import {NbDialogService} from '@nebular/theme'; +import {Observable, of} from 'rxjs'; +import {map} from 'rxjs/operators'; import {MatAutocompleteSelectedEvent} from '@angular/material/autocomplete'; +import {ESModel, Result} from '../semantic-enrichment/ESModel'; import {ConceptsRequest, DataConcept} from '../semantic-enrichment/ConceptsRequest'; import {FeedbackDialogComponent} from '../mapping/dialog/feedback-dialog/feedback-dialog.component'; import {MatDialog} from '@angular/material/dialog'; @Component({ - selector: 'app-edition', - templateUrl: './edition.component.html', - styleUrls: ['./edition.component.scss'] + selector: 'app-dataset', + templateUrl: './dataset.component.html', + styleUrls: ['./dataset.component.scss'] }) -export class EditionComponent implements OnInit { +export class DatasetComponent implements OnInit { @ViewChild('dialog') dialog: TemplateRef<any>; - param: string; - datasets: KeywordResponse[] = []; + dataset: KeywordResponse; isLoading: boolean; + loadingPublish: boolean; messageError: string; filteredOptions: Observable<Result[]>; - autocompleteMap: Map<string, Result[]> = new Map<string, Result[]>(); - values: string[] = []; - id: string; - loadingPublish: boolean; - + autocompleteResults: Result[] = []; + value = ''; - constructor(private route: ActivatedRoute, private service: EditService, - private semanticService: PostService, private dialogService: NbDialogService, - private matDialog: MatDialog) { - - } + constructor(private route: ActivatedRoute, private service: CatalogService, + private semanticService: PostService, private dialogService: NbDialogService, private matDialog: MatDialog) { } ngOnInit(): void { - this.getDatasetByCatalogId(); + this.getDataset(); + } + openDialog(dialog: TemplateRef<any>) { + this.dialogService.open(dialog, { + context: { + }, + }); } - getDatasetByCatalogId() { + getDataset() { this.isLoading = true; this.route.params.subscribe(param => { - this.service.getCatalog(param.id).subscribe( - (response: KeywordResponse[]) => { - this.datasets = response, - this.datasets.forEach((resp: KeywordResponse) => { - this.autocompleteMap.set(resp.url, []); - if (resp.concepts) { - resp.concepts.results.forEach((result: Result) => { - result.checked = false; - }); - } - }); + this.service.getDataset(param.id).subscribe( + (response: KeywordResponse) => { + this.dataset = response; }, error => { this.isLoading = false; @@ -66,6 +58,7 @@ export class EditionComponent implements OnInit { ); }); } + filter(val: string): Observable<any> { if (val.length > 3) { return this.semanticService.getData(val) @@ -75,13 +68,6 @@ export class EditionComponent implements OnInit { return of([]); } - openDialog(dialog: TemplateRef<any>) { - this.dialogService.open(dialog, { - context: { - }, - }); - } - viewHandle(value: any) { if (typeof value !== 'string' && typeof value !== 'undefined' && null !== value) { if (value.source ) { @@ -90,69 +76,65 @@ export class EditionComponent implements OnInit { } return value; } + onModelChange(value: string) { Promise.resolve(null).then(() => this.filteredOptions = this.filter(value)); } - setId(id: string) { - this.id = id; + deleteProperty(i: number) { + this.autocompleteResults.splice(i, 1); } - setIri(option: MatAutocompleteSelectedEvent) { - const iris = this.autocompleteMap.get(this.id); - if (iris) { - iris.push(option.option.value); - } - this.autocompleteMap.set(this.id, iris); - console.log(this.autocompleteMap); + getResult($event: MatAutocompleteSelectedEvent) { + console.log($event); + this.autocompleteResults.push($event.option.value); } - deleteProperty(datasetKeyword: string, i: number) { - const results: Result[] = this.autocompleteMap.get(datasetKeyword); - results.splice(i, 1); - this.autocompleteMap.set(datasetKeyword, results); + deleteExistingConcepts(i) { + this.dataset.conceptIri.splice(i, 1); } - onSubmit() { const mappingData = new ConceptsRequest(); mappingData.fdpToken = this.service.fdpToken; mappingData.dataConcepts = []; - this.datasets.forEach((data: KeywordResponse) => { - if ((null !== data.concepts && data.concepts.results.filter(e => e.checked).length > 0) || - this.autocompleteMap.get(data.url) && this.autocompleteMap.get(data.url).length > 0) { - const concepts = new DataConcept(); - concepts.url = data.url; - concepts.iris = []; - if (data.concepts) { - data.concepts.results.forEach((result: Result) => { + const concepts = new DataConcept(); + concepts.url = this.dataset.url; + concepts.iris = []; + Object.values(this.dataset.keywords).forEach((data: ESModel) => { + if ((null !== data && data.results.filter(e => e.checked).length > 0)) { + if (data) { + data.results.forEach((result: Result) => { if (result.checked) { concepts.iris.push(result.source.document.iri); } }); } - if (this.autocompleteMap.get(data.url) && this.autocompleteMap.get(data.url).length > 0) { - this.autocompleteMap.get(data.url).forEach((result: Result) => concepts.iris.push(result.source.document.iri)); - } - mappingData.dataConcepts.push(concepts); } }); + if (this.autocompleteResults && this.autocompleteResults.length > 0) { + this.autocompleteResults.forEach((result: Result) => concepts.iris.push(result.source.document.iri)); + } + if (this.dataset.conceptIri && this.dataset.conceptIri.length > 0) { + this.dataset.conceptIri.forEach((iri: string) => concepts.iris.push(iri)); + } + mappingData.dataConcepts.push(concepts); console.log(mappingData); const postedDatasets = []; const notPostedDatasets = []; this.loadingPublish = true; this.service.editDataset(mappingData).subscribe((resp) => { - resp.publishedUrl.forEach(e => postedDatasets.push(e)); - resp.notPublishedUrl.forEach(e => notPostedDatasets.push(e)); - this.matDialog.open(FeedbackDialogComponent, { - data: { - postedMetadatas: postedDatasets, - notPostedMetadatas: notPostedDatasets - } - }).afterClosed().subscribe(); - }, + resp.publishedUrl.forEach(e => postedDatasets.push(e)); + resp.notPublishedUrl.forEach(e => notPostedDatasets.push(e)); + this.matDialog.open(FeedbackDialogComponent, { + data: { + postedMetadatas: postedDatasets, + notPostedMetadatas: notPostedDatasets + } + }).afterClosed().subscribe(); + }, error => { this.loadingPublish = false; this.matDialog.open(FeedbackDialogComponent, { diff --git a/src/app/datasets/datasets.component.ts b/src/app/datasets/datasets.component.ts index 6b38a7ad754411d9dca50458a731cde872decdb2..7b4ff85d44279faff694666aa1ddd5deb3177364 100644 --- a/src/app/datasets/datasets.component.ts +++ b/src/app/datasets/datasets.component.ts @@ -10,6 +10,7 @@ import { MatDialog } from '@angular/material/dialog'; import { HttpClient } from '@angular/common/http'; import { HttpMethod } from '../publishapi/class/http-enum'; import { Observable } from 'rxjs'; +import {TokenStorageService} from '../authentication/services/token-storage.service'; interface RequestInfo { value?: string; @@ -54,7 +55,8 @@ export class DatasetsComponent implements OnInit, AfterViewChecked, AfterContent constructor( private dataSetService: DatasetCrudService, public dialog: MatDialog, - private httpClient: HttpClient + private httpClient: HttpClient, + private storageService: TokenStorageService ) { } get urlRepo(): string { diff --git a/src/app/edition/edition.component.html b/src/app/edition/edition.component.html deleted file mode 100644 index a648b16fa69ab67efc0ada71dabd922b7274a9ac..0000000000000000000000000000000000000000 --- a/src/app/edition/edition.component.html +++ /dev/null @@ -1,152 +0,0 @@ -<ng-template #dialog let-data let-ref="dialogRef"> - <nb-card> - <nb-card-body>{{ messageError }}</nb-card-body> - <nb-card-footer> - <button nbButton (click)="getDatasetByCatalogId();ref.close()">Retry</button> - <button nbButton (click)="ref.close()">Close</button> - </nb-card-footer> - </nb-card> -</ng-template> - -<mat-autocomplete #auto="matAutocomplete" [displayWith]="viewHandle" (optionSelected)="setIri($event)"> - <mat-option - *ngFor="let option of filteredOptions | async" - [value]="option"> - {{option.source.document.label}} - </mat-option> -</mat-autocomplete> - -<ng-template #noConcept> - <nb-card> - <nb-card-body> - <h3>No concepts found...</h3> - </nb-card-body> - </nb-card> -</ng-template> - -<ng-template #noKeywordsFound> - <nb-card> - <nb-card-body> - <h3>No keywords found...</h3> - </nb-card-body> - </nb-card> -</ng-template> - -<ng-template #loading xmlns="http://www.w3.org/1999/html"> - <nb-card size="small" [nbSpinner]="true" nbSpinnerStatus="primary" nbSpinnerSize="giant"></nb-card> -</ng-template> -<ng-container *ngIf="!isLoading; else loading"> - <ng-container *ngFor="let keyword of datasets; let i = index"> - <nb-accordion> - <nb-accordion-item> - <nb-accordion-item-header><a href="{{keyword.url}}" target="_blank">{{keyword.title}}</a> - <div> - <nb-icon icon="checkmark-circle-2-outline" [status]=""></nb-icon> - </div> - </nb-accordion-item-header> - - <nb-accordion-item-body> - <nb-card *ngIf="keyword.keywords.length > 0; else noKeywordsFound" [size]="'tiny'"> - <nb-card-header> - Keyword(s) found - </nb-card-header> - <nb-list> - <nb-list-item *ngFor="let k of keyword.keywords"> - {{ k }} - </nb-list-item> - </nb-list> - </nb-card> - - <nb-card *ngIf="keyword.conceptIri.length > 0; else noConcept" [size]="'tiny'"> - <nb-card-header> - Concept iri found - </nb-card-header> - <nb-list> - <nb-list-item *ngFor="let k of keyword.conceptIri"> - {{ k }} - </nb-list-item> - </nb-list> - </nb-card> - <nb-card> - <nb-card-body> - <mat-form-field class="half-width"> - <input - type="text" - placeholder="enter a value with at least 4 characters" - [(ngModel)]="values[i]" - (ngModelChange)="onModelChange($event)" - matInput - [matAutocomplete]="auto" - (focusin)="setId(keyword.url)"> - - </mat-form-field> - <div class="mt0-m"> - <table *ngIf="autocompleteMap.get(keyword.url).length > 0"> - <thead style="background-color: #3366ff"> - <th></th> - <th class="text-center">label</th> - <th class="text-center">definition</th> - <th class="text-center">synonyms</th> - </thead> - <tbody> - <tr *ngFor="let value of autocompleteMap.get(keyword.url); let i = index "> - <td> - <button nbButton ghost> - <nb-icon icon="trash-2-outline" status="danger" - (click)="deleteProperty(keyword.url, i)"> - </nb-icon> - </button> - </td> - <td class="text-center"><a href="{{value.source.document.iri}}" - target="_blank">{{ value.source.document.label }}</a></td> - <td class="text-center">{{ value.source.document.description }}</td> - <td class="text-center">{{ value.source.document.synonyms }}</td> - - </tr> - </tbody> - </table> - </div> - </nb-card-body> - </nb-card> - <nb-card [size]="'small'" *ngIf="keyword.concepts"> - <table> - <thead style="background-color: #3366ff"> - <th></th> - <th class="text-center">label</th> - <th class="text-center">definition</th> - <th class="text-center">synonyms</th> - <th class="text-center">score</th> - </thead> - <tbody> - <tr *ngFor="let objet of keyword.concepts.results | orderByScore"> - <td class="text-center"> - <nb-checkbox [(checked)]="objet.checked"></nb-checkbox> - </td> - <td class="text-center"><a href="{{objet.source.document.iri}}" - target="_blank">{{ objet.source.document.label }}</a></td> - <td class="text-center">{{ objet.source.document.description }}</td> - <td class="text-center">{{ objet.source.document.synonyms }}</td> - <td class="text-center">{{ objet.score | number: '2.2-2' }}</td> - </tr> - </tbody> - </table> - </nb-card> - </nb-accordion-item-body> - </nb-accordion-item> - </nb-accordion> - - </ng-container> - - - <div class="row"> - <div class="button-center"> - <button nbButton status="primary" (click)="onSubmit()" [nbSpinner]="loadingPublish" [disabled]="loadingPublish" - nbSpinnerStatus="basic">Edit - </button> - </div> - </div> -</ng-container> - - - - diff --git a/src/app/edition/edition.component.spec.ts b/src/app/edition/edition.component.spec.ts deleted file mode 100644 index 393a216f5a0c052b2c9b7446301255ac50181b8f..0000000000000000000000000000000000000000 --- a/src/app/edition/edition.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { EditionComponent } from './edition.component'; - -describe('EditionComponent', () => { - let component: EditionComponent; - let fixture: ComponentFixture<EditionComponent>; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ EditionComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(EditionComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/mapping/class/dataset.ts b/src/app/mapping/class/dataset.ts index e35e74671491cf5355b2d2092ec81cabae5c0d6e..8945deba597bc3be672c12e23f5041be7facd8c6 100644 --- a/src/app/mapping/class/dataset.ts +++ b/src/app/mapping/class/dataset.ts @@ -73,8 +73,8 @@ export class KeywordResponse { public id: string; public url: string; public title: string; - public keywords: string[]; + public description: string; + public keywords: Map<string, ESModel>; public conceptIri: string[]; - public concepts: ESModel; } diff --git a/src/app/search/search.component.ts b/src/app/search/search.component.ts index 400b8535051b631318bbf3f7bad7f4c25739f9c6..b2cd5025b755ec12376f6ed4eda58c894378420a 100644 --- a/src/app/search/search.component.ts +++ b/src/app/search/search.component.ts @@ -1,8 +1,7 @@ -import { Component, OnInit } from '@angular/core'; -import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { ParseXmlService } from '../services/parse-xml.service'; -import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; -import { environment } from 'src/environments/environment'; +import {Component, OnInit} from '@angular/core'; +import {ParseXmlService} from '../services/parse-xml.service'; +import {FormBuilder} from '@angular/forms'; +import {environment} from 'src/environments/environment'; export interface formData { @@ -21,7 +20,7 @@ export class SearchComponent implements OnInit { this.searchForm = this.formBuilder.group({ inputSearchTerm: '', }); - } + } public results: string[] = []; @@ -43,19 +42,18 @@ export class SearchComponent implements OnInit { this.firstTime = false; const term = formData.inputSearchTerm; this.searchedTerm = term; - const query = 'PREFIX dcat: <http://www.w3.org/ns/dcat#>\n\ - PREFIX dcterms: <http://purl.org/dc/terms/>\n\ - SELECT DISTINCT ?title ?description ?keyword ?uri ?dataset \n\ - WHERE {\n\?dataset a dcat:Dataset ;\n\ - dcterms:title ?title ;\n\ - dcterms:description ?description; \n\ - dcterms:isPartOf* <' + environment.fdpUrl + '>;\n\ - dcat:keyword ?keyword ; \n\ - FILTER (contains( lcase(str(?description)), "' + - term.toLowerCase() + '") || contains( lcase(str(?title)), "' + term.toLowerCase() + '") ||' + - ' contains( lcase(str(?keyword)), "' + term.toLowerCase() + '"))\n\.\n\ - }\n\ - GROUP BY ?dataset '; + const query = 'PREFIX dcat: <http://www.w3.org/ns/dcat#> ' + + 'PREFIX dcterms: <http://purl.org/dc/terms/> ' + + 'SELECT DISTINCT ?title ?description ?keyword ?uri ?dataset ' + + 'WHERE {?dataset a dcat:Dataset ; ' + + 'dcterms:title ?title ; ' + + 'dcterms:description ?description; ' + + 'dcterms:isPartOf* <' + environment.fdpUrl + '>; ' + + 'dcat:keyword ?keyword ; ' + + 'FILTER (contains( lcase(str(?description)), "' + + term.toLowerCase() + '") || contains( lcase(str(?title)), "' + term.toLowerCase() + '") ||' + + ' contains( lcase(str(?keyword)), "' + term.toLowerCase() + '")). ' + + '} ' ; this.parserService.getXmlResult(query).subscribe( data => { diff --git a/src/app/semantic-enrichment/ESModel.ts b/src/app/semantic-enrichment/ESModel.ts index fa08b7b3bd6573917d16308502501edaab5cf9e6..114fa739a4c8cd99f71313b1d8d440d28d608fe2 100644 --- a/src/app/semantic-enrichment/ESModel.ts +++ b/src/app/semantic-enrichment/ESModel.ts @@ -12,17 +12,19 @@ export class Result { public score: number; public source: Source; - constructor(result: Result, checked: boolean) { - this.source = result.source; - this.score = result.score; - this.checked = checked; + constructor() { + this.source = new Source(); } + } export class Source { public document: Document; public timestamp: Date; + constructor() { + this.document = new Document(); + } } export class Document { diff --git a/src/app/semantic-enrichment/semantic-enrichment.component.html b/src/app/semantic-enrichment/semantic-enrichment.component.html index 97e211f45ce60c4182c9eaa93bf77e843d547086..b10cb0e55d3bbcfa9c99db80a2a8ae1b06378c49 100644 --- a/src/app/semantic-enrichment/semantic-enrichment.component.html +++ b/src/app/semantic-enrichment/semantic-enrichment.component.html @@ -2,12 +2,20 @@ <nb-card> <nb-card-body>{{ errorMess }}</nb-card-body> <nb-card-footer> - <button nbButton (click)="getConceptsByKeywords();ref.close()">Retry</button> + <button nbButton (click)=" getConceptsByKeywords();ref.close()">Retry</button> <button nbButton (click)="ref.close()">Close</button> </nb-card-footer> </nb-card> </ng-template> +<ng-template #noConcept> + <nb-card> + <nb-card-body> + <h5>No concepts found...</h5> + </nb-card-body> + </nb-card> +</ng-template> + <mat-autocomplete #auto="matAutocomplete" [displayWith]="viewHandle" (optionSelected)="setIri($event)"> <mat-option *ngFor="let option of filteredOptions | async" @@ -16,6 +24,7 @@ </mat-option> </mat-autocomplete> + <ng-template #noKeywordsFound> <nb-card> <nb-card-body> @@ -31,21 +40,25 @@ <ng-container *ngFor="let keyword of response; let i = index"> <nb-accordion> <nb-accordion-item> - <nb-accordion-item-header ><a href="{{keyword.url}}" target="_blank">{{keyword.title}}</a> - <div><nb-icon icon="checkmark-circle-2-outline" [status]="getStatus(keyword.concepts, autocompleteMap.get(keyword.id))"></nb-icon></div> + <nb-accordion-item-header><a href="{{keyword.url}}" target="_blank">{{keyword.title}}</a> + <div> + <nb-icon icon="checkmark-circle-2-outline" + [status]="getStatus(getValues(keyword.keywords), autocompleteMap.get(keyword.id))"></nb-icon> + </div> </nb-accordion-item-header> <nb-accordion-item-body> - <nb-card *ngIf="keyword.keywords.length > 0; else noKeywordsFound" [size]="'tiny'"> + <nb-card *ngIf="getkeys(keyword.keywords).length > 0; else noKeywordsFound" [size]="'tiny'"> <nb-card-header> Keyword(s) found </nb-card-header> <nb-list> - <nb-list-item *ngFor="let k of keyword.keywords"> + <nb-list-item *ngFor="let k of getkeys(keyword.keywords)"> {{ k }} </nb-list-item> </nb-list> </nb-card> + <nb-card> <nb-card-body> <mat-form-field class="half-width"> @@ -87,28 +100,35 @@ </div> </nb-card-body> </nb-card> - <nb-card [size]="'small'" *ngIf="keyword.concepts"> - <table> - <thead style="background-color: #3366ff"> - <th></th> - <th class="text-center">label</th> - <th class="text-center">definition</th> - <th class="text-center">synonyms</th> - <th class="text-center">score</th> - </thead> - <tbody> - <tr *ngFor="let objet of keyword.concepts.results | orderByScore"> - <td class="text-center"> - <nb-checkbox [(checked)]="objet.checked"></nb-checkbox> - </td> - <td class="text-center"><a href="{{objet.source.document.iri}}" - target="_blank">{{ objet.source.document.label }}</a></td> - <td class="text-center">{{ objet.source.document.description }}</td> - <td class="text-center">{{ objet.source.document.synonyms }}</td> - <td class="text-center">{{ objet.score | number: '2.2-2' }}</td> - </tr> - </tbody> - </table> + <nb-card [size]="'small'" *ngIf="getValues(keyword.keywords)"> + <nb-card-body> + <nb-tabset> + <nb-tab *ngFor="let objet of keyword.keywords | keyvalue" tabTitle="{{(objet.key)}}"> + + <table *ngIf="objet.value.results; else noConcept"> + <thead style="background-color: #3366ff"> + <th></th> + <th class="text-center">label</th> + <th class="text-center">definition</th> + <th class="text-center">synonyms</th> + <th class="text-center">score</th> + </thead> + <tbody> + <tr *ngFor="let result of (objet.value).results "> + <td class="text-center"> + <nb-checkbox [(checked)]="result.checked"></nb-checkbox> + </td> + <td class="text-center"><a href="{{result.source.document.iri}}" + target="_blank">{{ result.source.document.label }}</a></td> + <td class="text-center">{{ result.source.document.description }}</td> + <td class="text-center">{{ result.source.document.synonyms }}</td> + <td class="text-center">{{ result.score | number: '2.2-2' }}</td> + </tr> + </tbody> + </table> + </nb-tab> + </nb-tabset> + </nb-card-body> </nb-card> </nb-accordion-item-body> </nb-accordion-item> diff --git a/src/app/semantic-enrichment/semantic-enrichment.component.ts b/src/app/semantic-enrichment/semantic-enrichment.component.ts index 49368abfd8600d180b4d8364aa3e7c070f6ab699..6cb87fc4cb97253519fb4cb440b5eebf76e701f7 100644 --- a/src/app/semantic-enrichment/semantic-enrichment.component.ts +++ b/src/app/semantic-enrichment/semantic-enrichment.component.ts @@ -68,12 +68,16 @@ export class SemanticEnrichmentComponent implements OnInit { this.response = response; this.response.forEach((resp: KeywordResponse) => { this.autocompleteMap.set(resp.id, []); - if (resp.concepts) { - resp.concepts.results.forEach((result: Result) => { - if (resp.concepts.results.indexOf(result) === 0) { - result.checked = true; - } else { - result.checked = false; + if (resp.keywords) { + Object.values(resp.keywords).forEach((value: ESModel) => { + if (value.results) { + value.results.forEach((result: Result) => { + if (value.results.indexOf(result) === 0) { + result.checked = true; + } else { + result.checked = false; + } + }); } }); } @@ -107,8 +111,8 @@ export class SemanticEnrichmentComponent implements OnInit { } viewHandle(value: any) { - if (typeof value !== 'string' && typeof value !== 'undefined') { - if (value.source) { + if (typeof value !== 'string' && typeof value !== 'undefined' && null !== value) { + if (value.source ) { return value.source.document.label; } } @@ -121,20 +125,25 @@ export class SemanticEnrichmentComponent implements OnInit { mappingData.fdpToken = this.data.fdpToken; mappingData.dataConcepts = []; this.response.forEach((data: KeywordResponse) => { - const concepts = new DataConcept(); - concepts.id = data.id; - concepts.iris = []; - if (data.concepts) { - data.concepts.results.forEach((result: Result) => { - if (result.checked) { - concepts.iris.push(result.source.document.iri); + if (data.keywords) { + const concepts = new DataConcept(); + concepts.id = data.id; + concepts.iris = []; + Object.values(data.keywords).forEach((value: ESModel) => { + + if (value && value.results) { + value.results.forEach((result: Result) => { + if (result.checked) { + concepts.iris.push(result.source.document.iri); + } + }); + } + if (this.autocompleteMap.get(data.id) && this.autocompleteMap.get(data.id).length > 0) { + this.autocompleteMap.get(data.id).forEach((result: Result) => concepts.iris.push(result.source.document.iri)); } + mappingData.dataConcepts.push(concepts); }); } - if (this.autocompleteMap.get(data.id) && this.autocompleteMap.get(data.id).length > 0) { - this.autocompleteMap.get(data.id).forEach((result: Result) => concepts.iris.push(result.source.document.iri)); - } - mappingData.dataConcepts.push(concepts); }); console.log(mappingData); @@ -190,11 +199,20 @@ export class SemanticEnrichmentComponent implements OnInit { this.autocompleteMap.set(datasetKeyword, results); } - getStatus(concepts: ESModel, results: Result[]) { + getStatus(concepts: ESModel[], results: Result[]) { if (!concepts && results.length === 0) { return 'danger'; } return 'success'; } + + getValues(dataMap: Map<string, ESModel>): ESModel[] { + return Object.values(dataMap); + } + + getkeys(dataMap: Map<string, ESModel>): string[] { + return Object.keys(dataMap); + } + } diff --git a/src/app/services/catalog.service.ts b/src/app/services/catalog.service.ts index 4e871c42e85c6a6c94609197e8c3abb2f6e1d72c..0dbc3578c12e9b3b7094af24b0c7a1369fa4adb7 100644 --- a/src/app/services/catalog.service.ts +++ b/src/app/services/catalog.service.ts @@ -3,6 +3,8 @@ import { Injectable } from '@angular/core'; import { environment } from 'src/environments/environment'; import { TokenStorageService } from '../authentication/services/token-storage.service'; import { SmartHarvesterUser } from '../user/model/user'; +import {map, takeUntil} from 'rxjs/operators'; +import {Observable} from 'rxjs'; export interface FdpApiResponseItem { subject: { @@ -46,6 +48,7 @@ export class CatalogService { getAllCatalogId() { const user: SmartHarvesterUser = this.tokenService.getUser(); + return this.http.get<{ catId: string, uuid: string }[]>(this.smartApiUrl + '/user/' + user.email + '/catalogs', this.httpOptionsSmartHarvester); } @@ -60,4 +63,8 @@ export class CatalogService { deleteCatalog(catId: string) { return this.http.delete(this.smartApiUrl + '/catalogs/' + catId, this.httpOptionsSmartHarvester); } + + getUser(): Observable<any> { + return this.http.get(this.smartApiUrl + '/harvester/api/username', this.httpOptionsSmartHarvester); + } } diff --git a/src/app/stats/stats.component.html b/src/app/stats/stats.component.html index 2777398cf9895fbe52e38228e387731d9889d0fc..7b37955b8eb1783514e03ddcaef339a9935d960c 100644 --- a/src/app/stats/stats.component.html +++ b/src/app/stats/stats.component.html @@ -21,7 +21,7 @@ </thead> <tbody> <tr *ngFor="let cat of catalogList"> - <td style="text-align: center;"><a [routerLink]="['/dashboard/edit', cat.catId]">{{cat.title}}</a></td> + <td style="text-align: center;"><a [routerLink]="['/dashboard/catalog', cat.catId]">{{cat.title}}</a></td> <td style="text-align: center;"><strong>{{cat.count}}</strong></td> </tr> </tbody>