[Validator]: Updates & improvements.

1. JobResult.ts: Added "set:string" and "exceptionMessage: string" fields.
2. RulePerJob.ts: Removed "rule_description" field and added fields "requirement_level: string, description: string, fair_principles: string, fair_principles_tooltip: string, link: string;".
3. oaipmh-analysis.component.html: Added cards on the right for base url and set | Added requirement level to the rule name, updated field for description, added fair principles.
4. oaipmh-analysis.component.ts: Added "requirementLevelMapping" and initialize tooltip of fair principles.
5. oaipmh-history.component.html: Updated table to show multiple validations | Updated columns of table - removed actions and updated status (view results - gets us to the analysis, in progress, stoppes, error, errors - opens a modal with the exception message).
6. oaipmh-history.component.ts: Added method "getJobResults()" and methods for error modal | Added a limit of 120 requests (10 minutes) for repetable requests of getting jobResult to update status when status is in progress.
7. oaipmh-validator.component.ts: Updated label for guidelines | Updated validation checks | Added exception catch in getSets().
8. oaipmh-validator.service.ts: Added method "getJobResults(limit: number = 200)".
9. topmenu.component.html: Removed jobId from history demo page.
10. package-lock.json: Updated uikit version.
This commit is contained in:
Konstantina Galouni 2023-10-03 17:00:58 +03:00
parent 894792daae
commit b2cddcbfb6
11 changed files with 201 additions and 45 deletions

14
package-lock.json generated
View File

@ -19,7 +19,7 @@
"@angular/router": "^16.1.8",
"rxjs": "~7.5.0",
"tslib": "^2.3.0",
"uikit": "3.13.10",
"uikit": "3.16.24",
"zone.js": "~0.13.1"
},
"devDependencies": {
@ -11575,9 +11575,9 @@
}
},
"node_modules/uikit": {
"version": "3.13.10",
"resolved": "https://registry.npmjs.org/uikit/-/uikit-3.13.10.tgz",
"integrity": "sha512-kxSPDT7HiwiQW3e++V8LfIe7/E1L5viPXH4P2yxyDIaU6CP1gWh2Ggy6skc/ziZNlY3CqI2JJkOU2/5zd+NaVw=="
"version": "3.16.24",
"resolved": "https://registry.npmjs.org/uikit/-/uikit-3.16.24.tgz",
"integrity": "sha512-b33pxCinNlgrkQf+VUNk7AzN0wFWo41C5avYbXQTtt41xOAlJD2mBQHNwlTm8s8uo+CZa3djX1inJLuY2JwusQ=="
},
"node_modules/unicode-canonical-property-names-ecmascript": {
"version": "2.0.0",
@ -20994,9 +20994,9 @@
"dev": true
},
"uikit": {
"version": "3.13.10",
"resolved": "https://registry.npmjs.org/uikit/-/uikit-3.13.10.tgz",
"integrity": "sha512-kxSPDT7HiwiQW3e++V8LfIe7/E1L5viPXH4P2yxyDIaU6CP1gWh2Ggy6skc/ziZNlY3CqI2JJkOU2/5zd+NaVw=="
"version": "3.16.24",
"resolved": "https://registry.npmjs.org/uikit/-/uikit-3.16.24.tgz",
"integrity": "sha512-b33pxCinNlgrkQf+VUNk7AzN0wFWo41C5avYbXQTtt41xOAlJD2mBQHNwlTm8s8uo+CZa3djX1inJLuY2JwusQ=="
},
"unicode-canonical-property-names-ecmascript": {
"version": "2.0.0",

@ -1 +1 @@
Subproject commit 11d0be4ffd6649189f87c91aed1c2d47c5d9b2c8
Subproject commit 6d7f6eca720d18fe694f13e380c1ede1f46303a8

View File

@ -1,6 +1,7 @@
export class JobResult {
id: string;
baseUrl: string;
set: string;
numberOfRecords: number;
guidelines: string;
startDate: Date;
@ -9,6 +10,7 @@ export class JobResult {
progress: Progress;
status: Status;
score: number;
exceptionMessage: string;
}
export enum Status {

View File

@ -1,9 +1,15 @@
export class RulePerJob {
rule_name: string;
rule_description: string;
rule_weight: number;
guidelines: string;
rule_status: Status;
"requirement_level": string;
"description": string;
"fair_principles": string;
"fair_principles_tooltip": string;
"link": string;
passed_records: number;
failed_records: number;
// warnings: string[];

View File

@ -15,8 +15,16 @@
<tbody class="uk-text-small">
<tr *ngFor="let ruleResult of rules">
<td>
<div>{{ruleResult.rule_name ? ruleResult.rule_name : '-'}}</div>
<div *ngIf="ruleResult.rule_description" class="uk-text-small uk-text-meta">{{ruleResult.rule_description}}</div>
<div>
<span>{{ruleResult.rule_name ? ruleResult.rule_name : '-'}}</span>
<span *ngIf="ruleResult.requirement_level">
<span> (</span>
<ng-container *ngIf="requirementLevelMapping.has(ruleResult.requirement_level)">{{requirementLevelMapping.get(ruleResult.requirement_level)}}</ng-container>
<ng-container *ngIf="!requirementLevelMapping.has(ruleResult.requirement_level)">{{ruleResult.requirement_level}}</ng-container>
<span>)</span>
</span>
</div>
<div *ngIf="ruleResult.description" class="uk-text-small uk-text-meta">{{ruleResult.description}}</div>
</td>
<td class="uk-text-center uk-text-normal">
<!-- <span uk-tooltip="Findable">F</span>-->
@ -26,7 +34,8 @@
<!-- <span uk-tooltip="Interoperable">I</span>-->
<!-- <span>, </span>-->
<!-- <span uk-tooltip="Reusable">R</span>-->
-
<span *ngIf="ruleResult.fair_principles" [attr.uk-tooltip]="ruleResult.fair_principles_tooltip">{{ruleResult.fair_principles}}</span>
<span *ngIf="!ruleResult.fair_principles">-</span>
</td>
<!-- <td class="uk-text-center">{{(ruleResult.rule_weight != undefined && ruleResult.rule_weight != null) ? ruleResult.rule_weight : '-'}}</td>-->
<!-- <td class="uk-text-center">{{ruleResult.passed_records | number}}/{{(ruleResult.passed_records+ruleResult.failed_records) | number}}</td>-->
@ -198,10 +207,34 @@
<div class="uk-sticky" uk-sticky="end: true" [attr.offset]="offset">
<div class="uk-overflow-auto uk-height-1-1 uk-padding">
<div class="uk-card uk-card-default">
<div class="uk-card-body uk-card-small">
<div class="uk-flex uk-flex-middle">
<icon name="link" ratio="1.2" class="uk-text-background"></icon>
<div class="uk-margin-left uk-width-expand uk-text-break">
<div class="uk-text-meta uk-text-xsmall">Base url</div>
<div class="uk-text-primary uk-text-bold uk-text-default">{{jobResult.baseUrl}}</div>
</div>
</div>
</div>
</div>
<div *ngIf="jobResult.set" class="uk-margin-top uk-card uk-card-default">
<div class="uk-card-body uk-card-small">
<div class="uk-flex uk-flex-middle">
<icon name="workspaces" ratio="1.2" class="uk-text-background"></icon>
<div class="uk-margin-left uk-width-expand uk-text-break">
<div class="uk-text-meta uk-text-xsmall">Set</div>
<div class="uk-text-primary uk-text-bold uk-text-default">{{jobResult.set}}</div>
</div>
</div>
</div>
</div>
<div class="uk-margin-top uk-card uk-card-default">
<div class="uk-card-body uk-card-small">
<div class="uk-flex uk-flex-middle">
<icon name="gavel" ratio="1.2" class="uk-text-background"></icon>
<div class="uk-margin-left">
<div class="uk-margin-left uk-width-expand uk-text-break">
<div class="uk-text-meta uk-text-xsmall">Guidelines</div>
<div class="uk-text-primary uk-text-bold uk-text-default">{{jobResult.guidelines}}</div>
</div>
@ -212,7 +245,7 @@
<div class="uk-card-body uk-card-small">
<div class="uk-flex uk-flex-top">
<icon name="today" ratio="1.2" class="uk-text-background"></icon>
<div class="uk-margin-left">
<div class="uk-margin-left uk-width-expand uk-text-break">
<div class="uk-text-meta uk-text-xsmall">Started</div>
<div class="uk-text-primary uk-text-bold uk-text-default">{{jobResult.startDate | date: 'yyyy-MM-dd, HH:mm:ss'}}</div>
<div class="uk-text-meta uk-text-xsmall uk-margin-small-top"><span *ngIf="!jobResult.endDate">Not yet </span>Ended</div>
@ -225,7 +258,7 @@
<div class="uk-card-body uk-card-small">
<div class="uk-flex uk-flex-middle">
<icon name="schedule" ratio="1.2" class="uk-text-background"></icon>
<div class="uk-margin-left">
<div class="uk-margin-left uk-width-expand uk-text-break">
<div class="uk-text-meta uk-text-xsmall">Duration</div>
<div class="uk-text-primary uk-text-bold uk-text-default" [title]="jobResult.progress" [attr.uk-tooltip]="!jobResult.endDate ? 'cls: uk-active' : 'cls: uk-invisible'">
<span *ngIf="jobDuration.years">{{jobDuration.years}} years</span>

View File

@ -53,6 +53,14 @@ export class OaipmhAnalysisComponent implements OnInit {
['OpenAIRE FAIR Guidelines for Data Repositories Profile', 'oai_datacite']
]);
public requirementLevelMapping: Map<string, string> = new Map([
["MANDATORY", "M"],
["MANDATORY_IF_APPLICABLE", "MA"],
["RECOMMENDED", "R"],
["OPTIONAL", "O"]
]);
constructor(private route: ActivatedRoute, private cdr: ChangeDetectorRef, private validator: OaipmhValidatorService) {}
ngOnInit(): void {
@ -100,6 +108,32 @@ export class OaipmhAnalysisComponent implements OnInit {
new Map<string, {"analysisResult": RulePerJob[], "successfulAnalysisResult": RulePerJob[], "warningAnalysisResult": RulePerJob[], "failedAnalysisResult": RulePerJob[] }>;
for(let rulePerJob of result) {
if(rulePerJob.fair_principles) {
if(rulePerJob.fair_principles.includes("F")) {
rulePerJob.fair_principles_tooltip = "Findable";
}
if(rulePerJob.fair_principles.includes("F") &&
(rulePerJob.fair_principles.includes("A") || rulePerJob.fair_principles.includes("I") || rulePerJob.fair_principles.includes("R"))) {
rulePerJob.fair_principles_tooltip += ", ";
}
if(rulePerJob.fair_principles.includes("A")) {
rulePerJob.fair_principles_tooltip += "Accessible";
}
if(rulePerJob.fair_principles.includes("A") &&
(rulePerJob.fair_principles.includes("I") || rulePerJob.fair_principles.includes("R"))) {
rulePerJob.fair_principles_tooltip += ", ";
}
if(rulePerJob.fair_principles.includes("I")) {
rulePerJob.fair_principles_tooltip += "Interoperable";
}
if(rulePerJob.fair_principles.includes("I") && rulePerJob.fair_principles.includes("R")) {
rulePerJob.fair_principles_tooltip += ", ";
}
if(rulePerJob.fair_principles.includes("R")) {
rulePerJob.fair_principles_tooltip += "Reusable";
}
}
if(!validationResult.has(rulePerJob.guidelines)) {
validationResult.set(rulePerJob.guidelines, {analysisResult: [], successfulAnalysisResult: [], warningAnalysisResult: [], failedAnalysisResult: []});
}

View File

@ -7,10 +7,12 @@
<div class="uk-margin-large-top">
<h1 class="uk-h5">Validator's History</h1>
<div *ngIf="!result" class="uk-alert uk-alert-primary">
<!-- <paging-prev-next totalResults="20" baseUrl="/oaipmh-history"></paging-prev-next>-->
<div *ngIf="!results || results.length == 0" class="uk-alert uk-alert-primary">
No validated metadata record yet
</div>
<div *ngIf="result" class="uk-margin-large-top">
<div *ngIf="results && results.length > 0" class="uk-margin-large-top">
<table class="uk-table uk-table-middle uk-table-divider uk-table-striped">
<thead>
<tr>
@ -21,11 +23,11 @@
<th class="uk-text-center uk-width-medium">Guidelines</th>
<!-- <th class="uk-text-center uk-width-small">Progress</th>-->
<th class="uk-text-center uk-width-small">Status</th>
<th class="uk-width-medium">Actions</th>
<!-- <th class="uk-width-medium">Actions</th>-->
</tr>
</thead>
<tbody>
<tr>
<tr *ngFor="let result of results">
<td class="uk-text-truncate">{{result.baseUrl}}</td>
<!-- <td class="uk-text-center">{{result.score ? result.score : '-'}}</td>-->
<!-- <td *ngIf="result.fairScore" class="uk-text-center">{{result.fairScore ? result.fairScore : '-'}}</td>-->
@ -34,27 +36,52 @@
<!-- <td class="uk-text-center">{{result.progress}}</td>-->
<!-- <td class="uk-text-center">{{result.status}}</td>-->
<td class="uk-text-center">
<span *ngIf="result.progress == Progress.COMPLETED"
class="uk-label" [ngClass]="result.status == Status.SUCCESS ? 'uk-label-success' : 'uk-label-danger'">
{{result.status}}
</span>
<span *ngIf="result.progress != Progress.COMPLETED"
class="uk-label" [ngClass]="result.progress == Progress.IN_PROGRESS ? 'uk-label-warning' : 'uk-label-danger'">
{{result.progress}}
</span>
</td>
<td>
<a class="uk-button-link uk-flex uk-flex-middle" [ngClass]="result.progress != Progress.COMPLETED ? 'uk-disabled uk-link-muted' : ''"
<!-- <span *ngIf="result.progress == Progress.COMPLETED"-->
<!-- class="uk-label" [ngClass]="result.status == Status.SUCCESS ? 'uk-label-success' : 'uk-label-danger'">-->
<!-- {{result.status}}-->
<!-- </span>-->
<a *ngIf="result.progress == Progress.COMPLETED" class="clickable uk-text-success"
routerLink="/oaipmh-analysis" [queryParams]="{'jobId': result.id}">
<icon name="visibility" flex="true" class="uk-margin-small-right"></icon>
<ng-container>View Results</ng-container>
<!-- <ng-container *ngIf="validationAnalysis">Hide Results</ng-container>-->
View results
<!-- <ng-container *ngIf="validationAnalysis">Hide Results</ng-container>-->
</a>
<span *ngIf="result.progress == Progress.IN_PROGRESS" class="uk-text-warning">
In progess
</span>
<span *ngIf="result.progress == Progress.STOPPED && result.status != Status.FAILURE && !result.exceptionMessage" class="uk-text-danger">
Stopped
</span>
<span *ngIf="result.status == Status.FAILURE && !result.exceptionMessage && result.progress != Progress.COMPLETED" class="uk-text-danger">
Error
</span>
<span *ngIf="result.exceptionMessage" class="clickable uk-text-danger" (click)="viewErrors(result.exceptionMessage)">
Errors
</span>
</td>
<!-- <td>-->
<!-- <a class="uk-button-link uk-flex uk-flex-middle" [ngClass]="result.progress != Progress.COMPLETED ? 'uk-disabled uk-link-muted' : ''"-->
<!-- routerLink="/oaipmh-analysis" [queryParams]="{'jobId': result.id}">-->
<!-- <icon name="visibility" flex="true" class="uk-margin-small-right"></icon>-->
<!-- <ng-container>View Results</ng-container>-->
<!--&lt;!&ndash; <ng-container *ngIf="validationAnalysis">Hide Results</ng-container>&ndash;&gt;-->
<!-- </a>-->
<!-- </td>-->
<!-- SUCCESS, FAILURE, IN_PROGRESS, COMPLETED, STOPPED-->
</tr>
</tbody>
</table>
<div *ngIf="warning" class="uk-alert uk-alert-warning">Please reload the page to get the updated status.</div>
<modal-alert #errorsModal large="true" (alertOutput)="errorsModalOpen=false" classTitle="uk-border-bottom">
<div *ngIf="errorsModalOpen" class="uk-modal-body uk-height-min-medium uk-width-expand">
<div *ngIf="!error">No errors available</div>
<div *ngIf="error" [class.uk-margin-medium-bottom]="error">
<div class="uk-text-danger">{{error}}</div>
</div>
</div>
</modal-alert>
</div>
</div>
</div>

View File

@ -1,4 +1,4 @@
import {Component, OnInit} from '@angular/core';
import {Component, OnInit, ViewChild} from '@angular/core';
import {OaipmhValidatorService} from "../../../services/oaipmh-validator.service";
import {JobResult, Progress, Status} from "../../entities/JobResult";
import {ActivatedRoute} from "@angular/router";
@ -12,10 +12,16 @@ import {filter, repeat, take} from "rxjs/operators";
styleUrls: ['./oaipmh-history.component.less']
})
export class OaipmhHistoryComponent implements OnInit {
public result: JobResult;
public warning: boolean = false;
public results: JobResult[];
public jobId: string = "";
public breadcrumbs: Breadcrumb[] = [{name: 'home', route: '/'}, {name: 'Validator\'s History'}];
protected readonly Progress = Progress;
protected readonly Status = Status;
@ViewChild('errorsModal') errorsModal;
public errorsModalOpen: boolean = false;
public error: string = null;
subscriptions = [];
@ -24,11 +30,13 @@ export class OaipmhHistoryComponent implements OnInit {
ngOnInit(): void {
this.subscriptions.push(this.route.queryParams.subscribe(params => {
this.jobId = params['jobId'];
this.result = null;
this.results = [];
this.warning = false;
if(!this.jobId) {
this.jobId = "825";
this.getJobResults();
} else {
this.getJobResult();
}
this.getJobResult();
}));
}
@ -41,11 +49,18 @@ export class OaipmhHistoryComponent implements OnInit {
}
public getJobResult() {
let count: number = 0;
this.subscriptions.push(this.validator.getJobResult(this.jobId)
.pipe(
repeat({ delay: 5000 }),
filter((result: JobResult) => {
this.result = result;
this.results = [];
this.results.push(result);
count++;
if(count == 120 && result.progress == Progress.IN_PROGRESS) {
this.warning = true;
return true;
}
return result.progress !== Progress.IN_PROGRESS
}),
take(1)
@ -54,5 +69,25 @@ export class OaipmhHistoryComponent implements OnInit {
);
}
protected readonly Status = Status;
public getJobResults() {
this.subscriptions.push(this.validator.getJobResults().subscribe(
(results: JobResult[]) => {
this.results = results;
}),
);
}
public viewErrors(error: string) {
this.error = error;
this.openErrorsModal();
this.errorsModalOpen = true;
}
public openErrorsModal() {
this.errorsModalOpen = true;
this.errorsModal.cancelButton = false;
this.errorsModal.okButton = false;
this.errorsModal.alertTitle = "Errors";
this.errorsModal.open();
}
}

View File

@ -7,6 +7,8 @@ import {JobResult} from "../../entities/JobResult";
import {Router} from "@angular/router";
import {Subscriber} from "rxjs";
declare var UIkit: any;
@Component({
selector: 'app-oaipmh-validator',
templateUrl: './oaipmh-validator.component.html',
@ -18,7 +20,7 @@ export class OaipmhValidatorComponent implements OnInit {
@ViewChild("right_sidebar_footer") right_sidebar_footer;
public options: Option[] = [
{label: 'OpenAIRE Guidelines for Data Archives Profile v2', value: 'OpenAIRE Guidelines for Data Archives Profile v2'},
{label: 'OpenAIRE Guidelines for Data Archives Profile v2 & OpenAIRE FAIR Guidelines for Data Repositories Profile', value: 'OpenAIRE Guidelines for Data Archives Profile v2'},
{label: 'OpenAIRE Guidelines for Literature Repositories Profile v3', value: 'OpenAIRE Guidelines for Literature Repositories Profile v3'},
{label: 'OpenAIRE Guidelines for Literature Repositories Profile v4', value: 'OpenAIRE Guidelines for Literature Repositories Profile v4'},
{label: 'OpenAIRE FAIR Guidelines for Data Repositories Profile', value: 'OpenAIRE FAIR Guidelines for Data Repositories Profile'}
@ -40,7 +42,7 @@ export class OaipmhValidatorComponent implements OnInit {
private cdr: ChangeDetectorRef,
private validator: OaipmhValidatorService) {
this.form = this.fb.group({
url: this.fb.control("", StringUtils.urlValidator()),//[Validators.required/*, Validators.email*/]),
url: this.fb.control("", [Validators.required, StringUtils.urlValidator()]),//[Validators.required/*, Validators.email*/]),
guidelines: this.fb.control("", Validators.required),
recordsNum: this.fb.control(null, Validators.required),
set: this.fb.control('all', Validators.required)
@ -113,9 +115,21 @@ export class OaipmhValidatorComponent implements OnInit {
this.router.navigate(['/oaipmh-history'], {
queryParams: { 'jobId': result.id }
});
},
error => {
// if(error.status == 400) {
UIkit.notification((error.error && error.error.message) ? error.error.message: error.message, {
status: 'danger',
timeout: 4000,
pos: 'bottom-right'
});
// }
// if(error.status == 422) {
// this.router.navigate(['/oaipmh-history'], {
// queryParams: { 'jobId': error.error.id }
// })
// }
}
));
}
protected readonly innerHeight = innerHeight;
}

View File

@ -30,6 +30,11 @@ export class OaipmhValidatorService {
return this.http.get(url);
}
getJobResults(limit: number = 200) {
let url: string = environment.validatorAPI + "jobs/latest?limit="+limit;
return this.http.get(url);
}
getSets(baseUrl) {
let url: string = environment.validatorAPI + "getSets?baseUrl="+baseUrl;
return this.http.get(url).pipe(map(res => res['set']));

View File

@ -106,7 +106,7 @@
<a routerLink="/oaipmh">Validate [demo]</a>
</li>
<li class="uk-parent">
<a routerLink="/oaipmh-history" [queryParams]="{jobId: 825}">History [demo]</a>
<a routerLink="/oaipmh-history">History [demo]</a>
</li>
<li class="uk-parent">
<a routerLink="/oaipmh-analysis" [queryParams]="{jobId: 825}">Analysis [demo]</a>