[develop | DONE | ADDED]: Added relation between products (research product landing) and services (data sources - exclude service type).

1. result-preview.ts: Define interface RelationDatasource and added method "relationDatasourceConvert()".
2. resultLandingInfo.ts: Added fields "relatedServices" and "relatedServicesClassFilters".
3. resultLanding.service.ts: Call parsing for relations "dnet:result_datasource_relations".
4. parsingFunctions.class.ts: Added method "parseDatasources()".
5. resultLanding.component: Follow same schema for related research products (relatedResults) and related services (relatedServices), initialize relation fields and show them.
This commit is contained in:
Konstantina Galouni 2024-07-01 23:35:47 +03:00
parent 94e831c548
commit de793ab3ad
6 changed files with 202 additions and 58 deletions

View File

@ -1,4 +1,9 @@
import {HostedByCollectedFrom, Project, RelationResult} from "../../utils/result-preview/result-preview"; import {
HostedByCollectedFrom,
Project,
RelationDatasource,
RelationResult
} from "../../utils/result-preview/result-preview";
import {Context, Measure, Metric, MetricPerDatasource, Reference} from "../../utils/entities/resultLandingInfo"; import {Context, Measure, Metric, MetricPerDatasource, Reference} from "../../utils/entities/resultLandingInfo";
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {properties} from "../../../../environments/environment"; import {properties} from "../../../../environments/environment";
@ -527,6 +532,56 @@ export class ParsingFunctions {
return researchResults; return researchResults;
} }
// publication & dataset & software & orp landing : for relatedResearchResults
parseDatasources(datasources: RelationDatasource[], relation, provenanceAction: string, relationName: string): RelationDatasource[] {
let datasource: RelationDatasource = {
name: "",
id: "",
percentage: null,
percentageName: null,
class: "",
provenanceAction: provenanceAction,
relationName: relationName,
openaireCompatibility: ""
};
datasource['id'] = relation['to'].content;
if(relation["officialname"])
datasource.name = relation.officialname;
else {
datasource['name'] = "[no title available]";
}
let percentageName: string;
if (relation.trust) {
percentageName = "trust";
} else if (relation.similarity) {
percentageName = "similarity";
}
if (percentageName) {
datasource['percentage'] = Math.round(relation[percentageName] * 100);
datasource['percentageName'] = percentageName;
}
// type
if(relation.hasOwnProperty('datasourcetype') && relation['datasourcetype'].hasOwnProperty("classname")) {
datasource.class = relation['datasourcetype'].classname;
}
// compatibility
if(relation.hasOwnProperty("openairecompatibility")) {
datasource.openaireCompatibility = relation['openairecompatibility'].classname;
}
if(datasource.class !== "service" || properties.adminToolsPortalType == "eosc") {
if (datasources == undefined) {
datasources = [];
}
datasources.push(datasource);
}
return datasources;
}
sortByPercentage(results: RelationResult[]): RelationResult[] { sortByPercentage(results: RelationResult[]): RelationResult[] {
if (results) { if (results) {
return results.sort(function (a, b) { return results.sort(function (a, b) {

View File

@ -153,6 +153,10 @@
[tabTitle]="'Related research'" [tabId]="'all_related'" [tabTitle]="'Related research'" [tabId]="'all_related'"
[tabNumber]="resultLandingInfo.relatedResults.length"> [tabNumber]="resultLandingInfo.relatedResults.length">
</my-tab> </my-tab>
<my-tab *ngIf="resultLandingInfo.relatedServices?.length > 0"
[tabTitle]="openaireEntities.DATASOURCES" [tabId]="'dataProviders'"
[tabNumber]="resultLandingInfo.relatedServices.length">
</my-tab>
<my-tab *ngIf="resultLandingInfo.bioentities && bioentitiesNum> 0" <my-tab *ngIf="resultLandingInfo.bioentities && bioentitiesNum> 0"
[tabTitle]="'External Databases'" [tabId]="'bioentities'" [tabNumber]="bioentitiesNum"> [tabTitle]="'External Databases'" [tabId]="'bioentities'" [tabNumber]="bioentitiesNum">
</my-tab> </my-tab>
@ -179,9 +183,13 @@
</ng-container> </ng-container>
<ng-container *ngIf="resultLandingInfo.relatedResults?.length > 0"> <ng-container *ngIf="resultLandingInfo.relatedResults?.length > 0">
<div id="all_related" class="landing-section"> <div id="all_related" class="landing-section">
<ng-container *ngTemplateOutlet="relation_in_tab; context: { researchResults: filteredRelatedResults, header: ''}"></ng-container> <ng-container *ngTemplateOutlet="relation_in_tab; context: { related: filteredRelatedResults, props: relatedResults, header: ''}"></ng-container>
</div> </div>
</ng-container> </ng-container>
<div *ngIf="resultLandingInfo.relatedServices?.length > 0"
id="dataProviders" class="landing-section">
<ng-container *ngTemplateOutlet="relation_in_tab; context: { related: filteredRelatedServices, props: relatedServices, type: 'datasource', header: ''}"></ng-container>
</div>
<ng-container *ngIf="resultLandingInfo.bioentities && bioentitiesNum> 0"> <ng-container *ngIf="resultLandingInfo.bioentities && bioentitiesNum> 0">
<div id="bioentities" class="landing-section"> <div id="bioentities" class="landing-section">
<ng-container *ngTemplateOutlet="bioentities_content"></ng-container> <ng-container *ngTemplateOutlet="bioentities_content"></ng-container>
@ -457,6 +465,14 @@
</div> </div>
<hr> <hr>
</ng-container> </ng-container>
<ng-container *ngIf="resultLandingInfo.relatedServices?.length > 0">
<div class="clickable uk-flex uk-flex-middle uk-flex-between"
(click)="openFsModal(servicesFsModal, openaireEntities.DATASOURCES); onSelectActiveTab('dataProviders')">
<span>{{openaireEntities.DATASOURCES}}</span>
<icon name="chevron_right" [ratio]="1.5" [flex]="true"></icon>
</div>
<hr>
</ng-container>
<ng-container *ngIf="resultLandingInfo.bioentities && bioentitiesNum> 0"> <ng-container *ngIf="resultLandingInfo.bioentities && bioentitiesNum> 0">
<div class="clickable uk-flex uk-flex-middle uk-flex-between" (click)="openFsModal(bioentitiesFsModal, 'External Databases'); onSelectActiveTab('bioentities')"> <div class="clickable uk-flex uk-flex-middle uk-flex-between" (click)="openFsModal(bioentitiesFsModal, 'External Databases'); onSelectActiveTab('bioentities')">
<span>External Databases</span> <span>External Databases</span>
@ -638,7 +654,15 @@
<fs-modal #relatedResearchFsModal classTitle="uk-tile-default uk-border-bottom" <fs-modal #relatedResearchFsModal classTitle="uk-tile-default uk-border-bottom"
*ngIf="resultLandingInfo.relatedResults?.length > 0"> *ngIf="resultLandingInfo.relatedResults?.length > 0">
<div class="landing-section"> <div class="landing-section">
<ng-container *ngTemplateOutlet="relation_in_tab; context: { researchResults: filteredRelatedResults, header: ''}"></ng-container> <ng-container *ngTemplateOutlet="relation_in_tab; context: { related: filteredRelatedResults, props: relatedResults, header: ''}"></ng-container>
</div>
</fs-modal>
<fs-modal *ngIf="isMobile" #servicesFsModal classTitle="uk-tile-default uk-border-bottom">
<div *ngIf="activeTab == 'dataProviders' && resultLandingInfo.relatedServices?.length > 0"
class="landing-section">
<ng-container *ngTemplateOutlet="relation_in_tab; context: { related: filteredRelatedServices, props: relatedServices, type: 'datasource', header: ''}"></ng-container>
</div> </div>
</fs-modal> </fs-modal>
@ -763,34 +787,32 @@
</ul> </ul>
</ng-template> </ng-template>
<ng-template #relation_in_tab let-researchResults="researchResults" let-header="header"> <ng-template #relation_in_tab let-related="related" let-props="props" let-type="type" let-header="header">
<div <div *ngIf="related && related.length > 0" class="uk-margin-bottom">
*ngIf="researchResults && researchResults.length > 0"
class="uk-margin-bottom">
<h6 *ngIf="header">{{header}}</h6> <h6 *ngIf="header">{{header}}</h6>
<div *ngIf="relatedClassFilters?.length > 1" class="uk-margin-medium-bottom" <div *ngIf="props.classFilters?.length > 1" class="uk-margin-medium-bottom"
input type="select" placeholder="Filter by relation" inputClass="flat x-small" input type="select" placeholder="Filter by relation" inputClass="flat x-small"
[options]="relatedClassFilters" [(value)]="relatedClassSelected" [options]="props.classFilters" [(value)]="props.selectedClass"
(valueChange)="relatedClassChanged()"></div> (valueChange)="relatedClassChanged(type)"></div>
<results-and-pages <results-and-pages
[type]="openaireEntities.RESULTS" [type]="((type == 'datasource') ? openaireEntities.DATASOURCES : openaireEntities.RESULTS)"
[page]="relatedPage" [pageSize]="pageSize" [page]="props.page" [pageSize]="pageSize"
[totalResults]="researchResults.length"> [totalResults]="related.length">
</results-and-pages> </results-and-pages>
<ul class="uk-list uk-list-divider uk-margin"> <ul class="uk-list uk-list-divider uk-margin">
<li *ngFor="let item of researchResults.slice((relatedPage-1)*pageSize, relatedPage*pageSize)"> <li *ngFor="let item of related.slice((props.page-1)*pageSize, props.page*pageSize)">
<result-preview [properties]="properties" <result-preview [properties]="properties"
[result]="getResultPreview(item)" [provenanceActionVocabulary]="provenanceActionVocabulary" [result]="getResultPreview(item, type)" [provenanceActionVocabulary]="provenanceActionVocabulary"
[relationsVocabulary]="relationsVocabulary" [relationsVocabulary]="relationsVocabulary"
[isCard]="false" [prevPath]="prevPath"></result-preview> [isCard]="false" [prevPath]="prevPath"></result-preview>
</li> </li>
</ul> </ul>
<paging-no-load *ngIf="researchResults.length > pageSize" <paging-no-load *ngIf="related.length > pageSize"
class="uk-margin-top" class="uk-margin-top"
(pageChange)="updateRelatedPage($event)" (pageChange)="updateRelatedPage($event, type)"
[currentPage]="relatedPage" [size]="pageSize" [currentPage]="props.page" [size]="pageSize"
[totalResults]="researchResults.length"> [totalResults]="related.length">
</paging-no-load> </paging-no-load>
</div> </div>
</ng-template> </ng-template>

View File

@ -13,7 +13,7 @@ import {HelperFunctions} from '../../utils/HelperFunctions.class';
import {HelperService} from '../../utils/helper/helper.service'; import {HelperService} from '../../utils/helper/helper.service';
import {Location} from "@angular/common"; import {Location} from "@angular/common";
import {MetricsService} from "../../services/metrics.service"; import {MetricsService} from "../../services/metrics.service";
import {RelationResult, ResultPreview} from "../../utils/result-preview/result-preview"; import {RelationDatasource, RelationResult, ResultPreview} from "../../utils/result-preview/result-preview";
import {IndexInfoService} from "../../utils/indexInfo.service"; import {IndexInfoService} from "../../utils/indexInfo.service";
import {Identifier, StringUtils} from "../../utils/string-utils.class"; import {Identifier, StringUtils} from "../../utils/string-utils.class";
import {properties} from "../../../../environments/environment"; import {properties} from "../../../../environments/environment";
@ -95,12 +95,7 @@ export class ResultLandingComponent {
// Custom tab paging variables // Custom tab paging variables
public referencesPage: number = 1; public referencesPage: number = 1;
public bioentitiesPage: number = 1; public bioentitiesPage: number = 1;
public relatedPage: number = 1;
public similarPage: number = 1;
public supplementaryPage: number = 1;
public supplementedByPage: number = 1;
public organizationsPage: number = 1;
public openCitationsPage: number = 1;
public pageSize: number = 10; public pageSize: number = 10;
// Map counting variables // Map counting variables
@ -139,9 +134,28 @@ export class ResultLandingComponent {
public pid: string; public pid: string;
public contextsWithLink: any; public contextsWithLink: any;
public relatedClassFilters: Option[]=[{"label": "All relations", "value": ""}]; public relatedResults: {
public relatedClassSelected: string = ""; classFilters: Option[],
public filteredRelatedResults: RelationResult[]; selectedClass: string,
page: number
} = {
classFilters: [{"label": "All relations", "value": ""}],
selectedClass: "",
page: 1,
};
filteredRelatedResults: RelationResult[];
public relatedServices: {
classFilters: Option[],
selectedClass: string,
page: number
} = {
classFilters: [{"label": "All relations", "value": ""}],
selectedClass: "",
page: 1
}
filteredRelatedServices: RelationDatasource[];
public provenanceActionVocabulary = null; public provenanceActionVocabulary = null;
public relationsVocabulary = null; public relationsVocabulary = null;
@ -168,6 +182,7 @@ export class ResultLandingComponent {
@ViewChild('subjectsFsModal') subjectsFsModal: FullScreenModalComponent; @ViewChild('subjectsFsModal') subjectsFsModal: FullScreenModalComponent;
@ViewChild('referencesFsModal') referencesFsModal: FullScreenModalComponent; @ViewChild('referencesFsModal') referencesFsModal: FullScreenModalComponent;
@ViewChild('relatedResearchFsModal') relatedResearchFsModal: FullScreenModalComponent; @ViewChild('relatedResearchFsModal') relatedResearchFsModal: FullScreenModalComponent;
@ViewChild('servicesFsModal') servicesFsModal: FullScreenModalComponent;
@ViewChild('bioentitiesFsModal') bioentitiesFsModal: FullScreenModalComponent; @ViewChild('bioentitiesFsModal') bioentitiesFsModal: FullScreenModalComponent;
@ViewChild('compatibleEOSCFsModal') compatibleEOSCFsModal: FullScreenModalComponent; @ViewChild('compatibleEOSCFsModal') compatibleEOSCFsModal: FullScreenModalComponent;
@ViewChild('fundedByFsModal') fundedByFsModal: FullScreenModalComponent; @ViewChild('fundedByFsModal') fundedByFsModal: FullScreenModalComponent;
@ -558,20 +573,34 @@ export class ResultLandingComponent {
} }
} }
} }
this.relatedClassFilters = [{"label": "All relations", "value": ""}]; this.relatedResults.classFilters = [{"label": "All relations", "value": ""}];
if (this.resultLandingInfo.relatedClassFilters.size > 1) { if (this.resultLandingInfo.relatedClassFilters.size > 1) {
for (let relClass of this.resultLandingInfo.relatedClassFilters) { for (let relClass of this.resultLandingInfo.relatedClassFilters) {
this.relatedClassFilters.push({ this.relatedResults.classFilters.push({
"label": HelperFunctions.getVocabularyLabel(relClass, this.relationsVocabulary), "label": HelperFunctions.getVocabularyLabel(relClass, this.relationsVocabulary),
"value": relClass "value": relClass
}); });
} }
} else { } else {
this.relatedClassFilters.pop(); this.relatedResults.classFilters.pop();
} }
this.relatedClassSelected = ""; this.relatedResults.selectedClass = "";
this.filteredRelatedResults = this.resultLandingInfo.relatedResults; this.filteredRelatedResults = this.resultLandingInfo.relatedResults;
this.relatedServices.classFilters = [{"label": "All relations", "value": ""}];
if (this.resultLandingInfo.relatedServicesClassFilters.size > 1) {
for (let relClass of this.resultLandingInfo.relatedServicesClassFilters) {
this.relatedServices.classFilters.push({
"label": HelperFunctions.getVocabularyLabel(relClass, this.relationsVocabulary),
"value": relClass
});
}
} else {
this.relatedServices.classFilters.pop();
}
this.relatedServices.selectedClass = "";
this.filteredRelatedServices = this.resultLandingInfo.relatedServices
this.hasViews = false; this.hasViews = false;
this.hasDownloads = false; this.hasDownloads = false;
if (this.resultLandingInfo.measure && this.resultLandingInfo.measure.counts) { if (this.resultLandingInfo.measure && this.resultLandingInfo.measure.counts) {
@ -711,25 +740,14 @@ export class ResultLandingComponent {
this.scrollToTabTop('bioentities'); this.scrollToTabTop('bioentities');
} }
public updateRelatedPage($event) { public updateRelatedPage($event, type) {
this.relatedPage = $event.value; if(type == "datasource") {
this.relatedServices.page = $event.value;
this.scrollToTabTop("dataProviders");
} else {
this.relatedResults.page = $event.value;
this.scrollToTabTop('all_related'); this.scrollToTabTop('all_related');
} }
public updateSimilarPage($event) {
this.similarPage = $event.value;
}
public updateSupplementaryPage($event) {
this.supplementaryPage = $event.value;
}
public updateSupplementedByPage($event) {
this.supplementedByPage = $event.value;
}
public updateOrganizationsPage($event) {
this.organizationsPage = $event.value;
} }
scrollToTabTop(tabId:string){ scrollToTabTop(tabId:string){
@ -788,8 +806,11 @@ export class ResultLandingComponent {
this.alertModalDeletedByInference.open(); this.alertModalDeletedByInference.open();
} }
public getResultPreview(result: RelationResult): ResultPreview { public getResultPreview(result: RelationResult|RelationDatasource, type: string): ResultPreview {
return ResultPreview.relationResultConvert(result); if(type == "datasource") {
return ResultPreview.relationDatasourceConvert(<RelationDatasource>result);
}
return ResultPreview.relationResultConvert(<RelationResult>result);
} }
updateUrlWithType(pid) { updateUrlWithType(pid) {
@ -983,9 +1004,14 @@ export class ResultLandingComponent {
this.contextsWithLink = contextsWithLink; this.contextsWithLink = contextsWithLink;
} }
public relatedClassChanged() { public relatedClassChanged(type) {
this.relatedPage = 1; if(type == "datasource") {
this.filteredRelatedResults = this.resultLandingInfo.relatedResults.filter(result => !this.relatedClassSelected || result.relationName.toLowerCase() == this.relatedClassSelected.toLowerCase()); this.relatedServices.page = 1;
this.filteredRelatedServices = this.resultLandingInfo.relatedServices.filter(result => !this.relatedServices.selectedClass || result.relationName.toLowerCase() == this.relatedServices.selectedClass.toLowerCase());
} else {
this.relatedResults.page = 1;
this.filteredRelatedResults = this.resultLandingInfo.relatedResults.filter(result => !this.relatedResults.selectedClass || result.relationName.toLowerCase() == this.relatedResults.selectedClass.toLowerCase());
}
} }
public viewAllOrganizationsClick() { public viewAllOrganizationsClick() {

View File

@ -210,6 +210,19 @@ export class ResultLandingService {
this.resultLandingInfo.relatedResults = this.parsingFunctions.parseResults(this.resultLandingInfo.relatedResults, relation, provenanceAction, relationName); this.resultLandingInfo.relatedResults = this.parsingFunctions.parseResults(this.resultLandingInfo.relatedResults, relation, provenanceAction, relationName);
} else if (relation['to'].class && relation['to'].class.toLowerCase() == "hasauthorinstitution") { } else if (relation['to'].class && relation['to'].class.toLowerCase() == "hasauthorinstitution") {
this.resultLandingInfo.organizations = this.parseRelatedOrganizations(this.resultLandingInfo.organizations, relation); this.resultLandingInfo.organizations = this.parseRelatedOrganizations(this.resultLandingInfo.organizations, relation);
} else if (relation['to'].scheme && relation['to'].scheme == "dnet:result_datasource_relations") {
let relationName: string = relation.to.class;
if (!this.resultLandingInfo.relatedServicesClassFilters.has(relationName)) {
this.resultLandingInfo.relatedServicesClassFilters.add(relationName);
}
let provenanceAction: string = relation.provenanceaction;
// this.resultLandingInfo.relatedResults = this.parsingFunctions.parseResults(this.resultLandingInfo.relatedResults, relation, provenanceAction, relationName);
if (this.resultLandingInfo.relatedServices == undefined) {
this.resultLandingInfo.relatedServices = [];
}
this.resultLandingInfo.relatedServices = this.parsingFunctions.parseDatasources(this.resultLandingInfo.relatedServices, relation, provenanceAction, relationName);
} }
} }
} }

View File

@ -3,7 +3,7 @@ import {
HostedByCollectedFrom, HostedByCollectedFrom,
Journal, OARoutes, Journal, OARoutes,
Organization, Organization,
Project, Project, RelationDatasource,
RelationResult RelationResult
} from "../result-preview/result-preview"; } from "../result-preview/result-preview";
import {isArray} from "rxjs/internal-compatibility"; import {isArray} from "rxjs/internal-compatibility";
@ -194,6 +194,9 @@ export class ResultLandingInfo {
organizations: Organization[]; organizations: Organization[];
openCitations: { "url": string, "title": string, "year": string, "doi": string, "authors": string[] }[]; openCitations: { "url": string, "title": string, "year": string, "doi": string, "authors": string[] }[];
relatedServices: RelationDatasource[];
relatedServicesClassFilters: Set<string> = new Set();
// DATASET // DATASET
subtitle: string; subtitle: string;

View File

@ -37,6 +37,17 @@ export interface RelationResult {
relationName?: string; relationName?: string;
} }
export interface RelationDatasource {
name: string;
id: string;
percentage: number;
percentageName?: string;
class: string
provenanceAction?: string;
relationName?: string;
openaireCompatibility: string;
}
export interface Project { export interface Project {
id: string; id: string;
acronym: string; acronym: string;
@ -271,6 +282,20 @@ export class ResultPreview {
return resultPreview; return resultPreview;
} }
public static relationDatasourceConvert(result: RelationDatasource): ResultPreview {
let resultPreview: ResultPreview = new ResultPreview();
resultPreview.id = result.id;
resultPreview.title = result.name;
resultPreview.resultType = "dataprovider";
resultPreview.types = [result.class];
resultPreview.relationName = result.relationName;
resultPreview.relation = result.percentageName;
resultPreview.percentage = result.percentage;
resultPreview.provenanceAction = result.provenanceAction;
resultPreview.compatibility = result.openaireCompatibility;
return resultPreview;
}
public static organizationConvert(result: Organization, relation: string = 'trust'): ResultPreview { public static organizationConvert(result: Organization, relation: string = 'trust'): ResultPreview {
let resultPreview: ResultPreview = new ResultPreview(); let resultPreview: ResultPreview = new ResultPreview();
resultPreview.id = result.id; resultPreview.id = result.id;