[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 {Injectable} from '@angular/core';
import {properties} from "../../../../environments/environment";
@ -526,6 +531,56 @@ export class ParsingFunctions {
researchResults.push(researchResult);
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[] {
if (results) {

View File

@ -153,6 +153,10 @@
[tabTitle]="'Related research'" [tabId]="'all_related'"
[tabNumber]="resultLandingInfo.relatedResults.length">
</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"
[tabTitle]="'External Databases'" [tabId]="'bioentities'" [tabNumber]="bioentitiesNum">
</my-tab>
@ -179,9 +183,13 @@
</ng-container>
<ng-container *ngIf="resultLandingInfo.relatedResults?.length > 0">
<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>
</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">
<div id="bioentities" class="landing-section">
<ng-container *ngTemplateOutlet="bioentities_content"></ng-container>
@ -457,6 +465,14 @@
</div>
<hr>
</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">
<div class="clickable uk-flex uk-flex-middle uk-flex-between" (click)="openFsModal(bioentitiesFsModal, 'External Databases'); onSelectActiveTab('bioentities')">
<span>External Databases</span>
@ -638,7 +654,15 @@
<fs-modal #relatedResearchFsModal classTitle="uk-tile-default uk-border-bottom"
*ngIf="resultLandingInfo.relatedResults?.length > 0">
<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>
</fs-modal>
@ -763,34 +787,32 @@
</ul>
</ng-template>
<ng-template #relation_in_tab let-researchResults="researchResults" let-header="header">
<div
*ngIf="researchResults && researchResults.length > 0"
class="uk-margin-bottom">
<ng-template #relation_in_tab let-related="related" let-props="props" let-type="type" let-header="header">
<div *ngIf="related && related.length > 0" class="uk-margin-bottom">
<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"
[options]="relatedClassFilters" [(value)]="relatedClassSelected"
(valueChange)="relatedClassChanged()"></div>
[options]="props.classFilters" [(value)]="props.selectedClass"
(valueChange)="relatedClassChanged(type)"></div>
<results-and-pages
[type]="openaireEntities.RESULTS"
[page]="relatedPage" [pageSize]="pageSize"
[totalResults]="researchResults.length">
[type]="((type == 'datasource') ? openaireEntities.DATASOURCES : openaireEntities.RESULTS)"
[page]="props.page" [pageSize]="pageSize"
[totalResults]="related.length">
</results-and-pages>
<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]="getResultPreview(item)" [provenanceActionVocabulary]="provenanceActionVocabulary"
[result]="getResultPreview(item, type)" [provenanceActionVocabulary]="provenanceActionVocabulary"
[relationsVocabulary]="relationsVocabulary"
[isCard]="false" [prevPath]="prevPath"></result-preview>
</li>
</ul>
<paging-no-load *ngIf="researchResults.length > pageSize"
<paging-no-load *ngIf="related.length > pageSize"
class="uk-margin-top"
(pageChange)="updateRelatedPage($event)"
[currentPage]="relatedPage" [size]="pageSize"
[totalResults]="researchResults.length">
(pageChange)="updateRelatedPage($event, type)"
[currentPage]="props.page" [size]="pageSize"
[totalResults]="related.length">
</paging-no-load>
</div>
</ng-template>

View File

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

View File

@ -210,6 +210,19 @@ export class ResultLandingService {
this.resultLandingInfo.relatedResults = this.parsingFunctions.parseResults(this.resultLandingInfo.relatedResults, relation, provenanceAction, relationName);
} else if (relation['to'].class && relation['to'].class.toLowerCase() == "hasauthorinstitution") {
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,
Journal, OARoutes,
Organization,
Project,
Project, RelationDatasource,
RelationResult
} from "../result-preview/result-preview";
import {isArray} from "rxjs/internal-compatibility";
@ -194,6 +194,9 @@ export class ResultLandingInfo {
organizations: Organization[];
openCitations: { "url": string, "title": string, "year": string, "doi": string, "authors": string[] }[];
relatedServices: RelationDatasource[];
relatedServicesClassFilters: Set<string> = new Set();
// DATASET
subtitle: string;

View File

@ -37,6 +37,17 @@ export interface RelationResult {
relationName?: string;
}
export interface RelationDatasource {
name: string;
id: string;
percentage: number;
percentageName?: string;
class: string
provenanceAction?: string;
relationName?: string;
openaireCompatibility: string;
}
export interface Project {
id: string;
acronym: string;
@ -271,6 +282,20 @@ export class 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 {
let resultPreview: ResultPreview = new ResultPreview();
resultPreview.id = result.id;