Added validation history and validation analysis pages and updated validatorAPI to use the new version using camel.

This commit is contained in:
Konstantina Galouni 2023-07-14 18:22:25 +03:00
parent 32cbf775f9
commit 32078e8732
25 changed files with 906 additions and 9 deletions

View File

@ -21,7 +21,7 @@
"rxjs": "~7.5.0",
"tslib": "^2.3.0",
"zone.js": "~0.11.4",
"uikit": "3.12.0"
"uikit": "3.13.10"
},
"devDependencies": {
"@angular-devkit/build-angular": "^14.1.0",

View File

@ -1,6 +1,9 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { SingleRecordValidatorComponent } from "./pages/single-record-validator/single-record-validator.component";
import {OaipmhValidatorComponent} from "./pages/oaipmh-validator/validation-settings/oaipmh-validator.component";
import {OaipmhHistoryComponent} from "./pages/oaipmh-validator/validation-history/oaipmh-history.component";
import {OaipmhAnalysisComponent} from "./pages/oaipmh-validator/validation-analysis/oaipmh-analysis.component";
const routes: Routes = [
{
@ -12,6 +15,18 @@ const routes: Routes = [
path: 'single-record-validate',
component: SingleRecordValidatorComponent
},
{
path: 'oaipmh-analysis',
component: OaipmhAnalysisComponent
},
{
path: 'oaipmh-history',
component: OaipmhHistoryComponent
},
{
path: 'oaipmh',
component: OaipmhValidatorComponent
}
];
@NgModule({

View File

@ -11,13 +11,20 @@ import {HttpClient, HttpClientModule} from "@angular/common/http";
import {InputModule} from "./shared/utils/input/input.module";
import {AlertModalModule} from "./shared/utils/modal/alertModal.module";
import {IconsModule} from "./shared/utils/icons/icons.module";
import {OaipmhValidatorComponent} from "./pages/oaipmh-validator/validation-settings/oaipmh-validator.component";
import {OaipmhHistoryComponent} from "./pages/oaipmh-validator/validation-history/oaipmh-history.component";
import {OaipmhAnalysisComponent} from "./pages/oaipmh-validator/validation-analysis/oaipmh-analysis.component";
import {BreadcrumbsModule} from "./shared/utils/breadcrumbs/breadcrumbs.module";
@NgModule({
declarations: [
AppComponent,
TopmenuComponent,
SidebarComponent,
SingleRecordValidatorComponent
SingleRecordValidatorComponent,
OaipmhValidatorComponent,
OaipmhHistoryComponent,
OaipmhAnalysisComponent
],
imports: [
BrowserModule,
@ -27,7 +34,8 @@ import {IconsModule} from "./shared/utils/icons/icons.module";
HttpClientModule,
InputModule,
AlertModalModule,
IconsModule
IconsModule,
BreadcrumbsModule
],
providers: [],
bootstrap: [AppComponent]

View File

@ -0,0 +1,4 @@
export class Issue {
description: string;
records: string[];
}

View File

@ -0,0 +1,23 @@
export class JobResult {
id: string;
baseUrl: string;
numberOfRecords: number;
guidelines: string;
startDate: Date;
endDate: Date;
recordsTested: number;
progress: Progress;
status: Status;
score: number;
}
export enum Status {
SUCCESS = "SUCCESS",
FAILURE = "FAILURE"
}
export enum Progress {
IN_PROGRESS = "IN_PROGRESS",
COMPLETED = "COMPLETED",
STOPPED = "STOPPED"
}

View File

@ -0,0 +1,20 @@
export class RulePerJob {
rule_name: string;
rule_description: string;
rule_weight: number;
guidelines: string;
rule_status: Status;
passed_records: number;
failed_records: number;
// warnings: string[];
// errors: string[];
internal_error: string;
has_errors: boolean;
has_warnings: boolean;
}
export enum Status {
SUCCESS = "SUCCESS",
FAILURE = "FAILURE",
ERROR = "ERROR"
}

View File

@ -0,0 +1,249 @@
<ng-template #rules_analysis let-rules=rules>
<div *ngIf="rules?.length == 0" class="uk-margin-medium-top uk-text-center">No rules available</div>
<table *ngIf="rules?.length > 0" class="uk-margin-medium-top uk-table uk-table-middle uk-table-divider uk-table-striped">
<thead>
<tr>
<th class="uk-width-auto uk-width-2-6@xl">Rule Name</th>
<th class="uk-text-center uk-width-auto uk-width-1-6@xl">FAIR Principles</th>
<!-- <th class="uk-text-center">Rule Weight</th>-->
<!-- <th class="uk-text-center"># Of Records</th>-->
<th class="uk-text-center uk-width-auto uk-width-1-6@xl">Success Rate</th>
<th class="uk-text-center uk-width-small uk-width-1-6@xl">Status</th>
<th class="uk-text-center uk-width-small uk-width-1-6@xl">Issues</th>
</tr>
</thead>
<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>
</td>
<td class="uk-text-center uk-text-normal">
<!-- <span>F</span>-->
<!-- <span>, </span>-->
<!-- <span>A</span>-->
<!-- <span>, </span>-->
<!-- <span uk-tooltip="Interoperability">I</span>-->
<!-- <span>, </span>-->
<!-- <span>R</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>-->
<td>
<div class="uk-text-xsmall">{{ruleResult.passed_records | number}}/{{jobResult.numberOfRecords | number}}</div>
<div class="uk-text-center">
<progress class="analysis-progress uk-progress uk-margin-remove-bottom" [value]="ruleResult.passed_records*100/jobResult.numberOfRecords" max="100"></progress>
<div class="uk-text-center uk-text-xsmall uk-text-primary" style="position:relative; top: -19px;">{{ruleResult.passed_records*100/jobResult.numberOfRecords}}%</div>
</div>
</td>
<td class="uk-text-center">
<span class="uk-label" [ngClass]="ruleResult.rule_status == Status.SUCCESS ? 'uk-label-success' : 'uk-label-danger'">{{ruleResult.rule_status}}</span>
</td>
<td class="uk-text-center">
<a *ngIf="ruleResult.has_warnings" class="uk-text-warning" (click)="getWarnings(ruleResult)">Warnings</a>
<div *ngIf="ruleResult.has_warnings && (ruleResult.has_errors || ruleResult.internal_error)">&</div>
<a *ngIf="ruleResult.has_errors || ruleResult.internal_error" class="uk-text-danger" (click)="getErrors(ruleResult)">Errors</a>
<!-- <div *ngIf="ruleResult.internal_error" class="uk-text-danger">error</div>-->
<div *ngIf="!ruleResult.has_warnings && !ruleResult.has_errors && !ruleResult.internal_error">
<icon class="uk-text-success" name="check" ratio="1"></icon>
</div>
</td>
</tr>
</tbody>
</table>
</ng-template>
<div id="page_content">
<div id="page_content_header">
<div class="analysis uk-grid uk-margin-remove-left" uk-grid>
<div class="uk-width-expand uk-container uk-container-large">
<div id="analysis-center-content" class="uk-width-expand uk-section uk-section-small uk-container uk-container-large">
<!-- <div class="uk-section uk-section-small">-->
<breadcrumbs [breadcrumbs]="breadcrumbs"></breadcrumbs>
<div class="uk-margin-large-top">
<h1 class="uk-h5">Validation Result Analysis</h1>
<hr>
<div *ngIf="!validationResult || validationResult.size == 0" class="uk-alert uk-alert-primary">
No validated metadata record yet
</div>
<div *ngIf="validationResult?.size > 0">
<ul uk-tab>
<li *ngFor="let result of validationResult.keys()" class="uk-width-auto@l uk-width-medium">
<a><span class="uk-text-truncate" [attr.uk-tooltip]="result">{{result}}</span></a></li>
</ul>
<ul class="uk-switcher uk-margin">
<li *ngFor="let result of validationResult.values()">
<ul class="uk-subnav uk-subnav-pill-alt uk-margin" uk-switcher>
<li class="uk-active"><a>All Rules ({{result.analysisResult.length | number}})</a></li>
<li><a>Successful ({{result.successfulAnalysisResult.length | number}})</a></li>
<li><a>Warnings ({{result.warningAnalysisResult.length | number}})</a></li>
<li><a>Failed ({{result.failedAnalysisResult.length | number}})</a></li>
</ul>
<ul class="uk-switcher uk-margin">
<li>
<ng-container *ngTemplateOutlet="rules_analysis; context: {rules: result.analysisResult}"></ng-container>
</li>
<li>
<ng-container *ngTemplateOutlet="rules_analysis; context: {rules: result.successfulAnalysisResult}"></ng-container>
</li>
<li>
<ng-container *ngTemplateOutlet="rules_analysis; context: {rules: result.warningAnalysisResult}"></ng-container>
</li>
<li>
<ng-container *ngTemplateOutlet="rules_analysis; context: {rules: result.failedAnalysisResult}"></ng-container>
</li>
</ul>
</li>
</ul>
<!-- <h6>Validation Result Analysis</h6>-->
</div>
<modal-alert #warningsModal large="true" (alertOutput)="warningsModalOpen=false" classTitle="uk-border-bottom">
<div *ngIf="warningsModalOpen" class="uk-modal-body uk-height-min-medium uk-width-expand">
<div *ngIf="warnings.length == 0">No warnings available</div>
<div *ngIf="warnings.length > 0">
<div class="uk-h6">Warnings list ({{warnings.length | number}} warning<span *ngIf="warnings.length > 1">s</span>)</div>
<ul uk-accordion class="uk-accordion">
<li *ngFor="let message of warnings" class="accordion-warning uk-padding-small">
<a class="uk-accordion-title">
<div class="uk-flex uk-flex-top">
<icon class="uk-text-warning" [flex]="true" name="warning" ratio="2"></icon>
<div class="uk-margin-left">
<div class="uk-text-warning uk-text-default">{{message.records.length | number}} Records - {{message.description}}</div>
<div class="uk-text-normal uk-text-xsmall uk-margin-small-top uk-flex">
<div class="uk-text-italic uk-text-meta">Try:</div>
<div class="uk-margin-small-left">
<div class="uk-text-truncate">
1. Lorem ipsum
</div>
<div class="uk-text-truncate">
2. Lorem ipsum
</div>
</div>
</div>
</div>
</div>
</a>
<div class="uk-accordion-content">
<hr>
<div *ngFor="let record of message.records" class="uk-margin-medium-left">
<a [href]="record" target="_blank" class="uk-margin-left">{{record}}</a>
</div>
</div>
</li>
</ul>
</div>
</div>
</modal-alert>
<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="errors.length == 0 && !internal_error">No errors available</div>
<div *ngIf="internal_error" [class.uk-margin-medium-bottom]="errors.length > 0">
<div class="uk-h6">Internal error</div>
<div class="uk-alert uk-alert-danger">{{internal_error}}</div>
</div>
<div *ngIf="errors.length > 0">
<div class="uk-h6">Errors list ({{errors.length | number}} error<span *ngIf="errors.length > 1">s</span>)</div>
<ul uk-accordion class="uk-accordion">
<li *ngFor="let message of errors" class="accordion-danger uk-padding-small">
<a class="uk-accordion-title">
<div class="uk-flex uk-flex-top">
<icon class="uk-text-danger" [flex]="true" name="cancel" ratio="2"></icon>
<div class="uk-margin-left">
<div class="uk-text-danger uk-text-default">{{message.description}}</div>
<div class="uk-text-normal uk-text-small uk-margin-small-top">{{message.records.length | number}} Records</div>
</div>
</div>
</a>
<div class="uk-accordion-content">
<hr>
<div *ngFor="let record of message.records" class="uk-margin-medium-left">
<a [href]="record" target="_blank" class="uk-margin-left">{{record}}</a>
</div>
</div>
</li>
</ul>
</div>
<!-- <div *ngIf="currentRule.internal_error">Internal error: {{currentRule.internal_error}}</div>-->
</div>
</modal-alert>
</div>
</div>
</div>
<div *ngIf="jobResult" id="analysis-right-sidebar" class="uk-width-1-3 uk-width-1-4@l uk-padding-remove uk-text-small">
<div class="uk-sticky" uk-sticky="bottom: 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="gavel" ratio="1.2" class="uk-text-background"></icon>
<div class="uk-margin-left">
<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>
</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-top">
<icon name="today" ratio="1.2" class="uk-text-background"></icon>
<div class="uk-margin-left">
<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>
<div *ngIf="jobResult.endDate" class="uk-text-primary uk-text-bold uk-text-default">{{jobResult.endDate | date: 'yyyy-MM-dd, HH:mm:ss'}}</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="schedule" ratio="1.2" class="uk-text-background"></icon>
<div class="uk-margin-left">
<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>
<span *ngIf="jobDuration.years && (jobDuration.months || jobDuration.days || jobDuration.hours || jobDuration.minutes || jobDuration.seconds)"> & </span>
<span *ngIf="jobDuration.months">{{jobDuration.months}} months</span>
<span *ngIf="jobDuration.months && (jobDuration.days || jobDuration.hours || jobDuration.minutes || jobDuration.seconds)"> & </span>
<span *ngIf="jobDuration.days">{{jobDuration.days}} days</span>
<span *ngIf="jobDuration.days && (jobDuration.hours || jobDuration.minutes || jobDuration.seconds)"> & </span>
<span *ngIf="jobDuration.hours">{{jobDuration.hours}} hours</span>
<span *ngIf="jobDuration.hours && (jobDuration.minutes || jobDuration.seconds)"> & </span>
<span *ngIf="jobDuration.minutes">{{jobDuration.minutes}} minutes</span>
<span *ngIf="jobDuration.minutes && jobDuration.seconds"> & </span>
<span *ngIf="jobDuration.seconds">{{jobDuration.seconds}} seconds</span>
</div>
</div>
</div>
</div>
</div>
<!-- <hr class="uk-margin-medium-top uk-margin-medium-bottom">-->
<!-- <div class="uk-flex uk-flex-middle">-->
<!-- <div class="uk-progress-circle" [attr.percentage]="jobResult.score" [style]="'&#45;&#45;percentage: '+jobResult.score"></div>-->
<!-- <div class="uk-margin-left uk-width-expand">-->
<!-- <div>Validation Score</div>-->
<!-- <div class="uk-text-left uk-width-1-1">-->
<!-- <a class="uk-display-inline-block uk-width-auto uk-button uk-button-text uk-text-normal uk-text-xsmall" target="_blank" [href]="">-->
<!-- <div class="uk-text-truncate">Guidelines Compliance</div>-->
<!-- </a>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,56 @@
@import "~src/assets/openaire-theme/less/_import-variables";
@accordion-warning-border: @warning-border-color;
@accordion-danger-border: @danger-border-color;
@analysis-portal-color: @global-primary-background;
@analysis-right-sidebar-min-width: 360px;
@analysis-right-sidebar-background: @default-color;
@analysis-right-sidebar-border-width: 2px;
@analysis-right-sidebar-border: fade(@analysis-portal-color, @global-opacity);
@analysis-header-height: @global-header-height;
@analysis-sidebar-height: calc(100vh - @analysis-header-height);
#page_content {
background-color: #fff;
}
.accordion-warning {
border: 1px solid @accordion-warning-border;
}
.accordion-danger {
border: 1px solid @accordion-danger-border;
}
.analysis {
#analysis-center-content {
min-height: @analysis-sidebar-height;
}
#analysis-right-sidebar {
min-width: @analysis-right-sidebar-min-width;
background: @analysis-right-sidebar-background;
border-left: @analysis-right-sidebar-border-width solid @analysis-right-sidebar-border;
& > .uk-sticky {
height: @analysis-sidebar-height;
}
}
& #analysis_icon .start {
stop-color: #1F88BE;
}
& #analysis_icon .end {
stop-color: darkblue;
}
//& .analysis-progress:extend(.uk-progress all) {
// height: 19px;
// &::-webkit-progress-value,&::-moz-progress-bar {
// background-color: @global-muted-color;
// }
//}
}

View File

@ -0,0 +1,178 @@
import {Component, OnInit, ViewChild} from '@angular/core';
import {UntypedFormGroup} from "@angular/forms";
import {OaipmhValidatorService} from "../../../services/oaipmh-validator.service";
import {Subscriber} from "rxjs";
import {ActivatedRoute} from "@angular/router";
import {RulePerJob, Status} from "../../entities/RulePerJob";
import {Breadcrumb} from "../../../shared/utils/breadcrumbs/breadcrumbs.component";
import {Issue} from "../../entities/Issue";
import {JobResult} from "../../entities/JobResult";
export class Duration {
years: number;
months: number;
days: number;
hours: number;
minutes: number;
seconds: number;
}
@Component({
selector: 'app-oaipmh-analysis',
templateUrl: './oaipmh-analysis.component.html',
styleUrls: ['./oaipmh-analysis.component.less']
})
export class OaipmhAnalysisComponent implements OnInit {
public form: UntypedFormGroup;
public jobResult: JobResult = null;
public jobDuration: Duration = null;
public validationResult: Map<string, { "analysisResult": RulePerJob[], "successfulAnalysisResult": RulePerJob[], "warningAnalysisResult": RulePerJob[], "failedAnalysisResult": RulePerJob[] }>;
// public analysisResult: RulePerJob[] = [];
// public successfulAnalysisResult: RulePerJob[] = [];
// public warningAnalysisResult: RulePerJob[] = [];
// public failedAnalysisResult: RulePerJob[] = [];
public warningsModalOpen: boolean = false;
public errorsModalOpen: boolean = false;
public warnings: Issue[];
public errors: Issue[];
public internal_error: string;
@ViewChild('warningsModal') warningsModal;
@ViewChild('errorsModal') errorsModal;
public offset: number;
public jobId: string = "";
public breadcrumbs: Breadcrumb[] = [{name: 'home', route: '/'}, {name: 'Validator\'s History', route: '/oaipmh-history'}, {name: 'Result for ...'}];
subscriptions = [];
constructor(private route: ActivatedRoute, private validator: OaipmhValidatorService) {
}
ngOnInit(): void {
this.subscriptions.push(this.route.queryParams.subscribe(params => {
this.jobId = params['jobId'];
this.validationResult = new Map<string, {"analysisResult": RulePerJob[], "successfulAnalysisResult": RulePerJob[], "warningAnalysisResult": RulePerJob[], "failedAnalysisResult": RulePerJob[] }>;
// this.analysisResult = [];
// this.successfulAnalysisResult = [];
// this.warningAnalysisResult = [];
// this.failedAnalysisResult = [];
this.jobResult = null;
if(this.jobId) {
this.getAnalysis();
this.getJobResult();
}
}));
}
ngAfterViewInit() {
if (typeof document !== 'undefined') {
if (document.getElementById("main-menu")) {
this.offset = Number.parseInt(getComputedStyle(document.documentElement).getPropertyValue('--header-height'));
} else {
this.offset = 0;
}
}
}
ngOnDestroy() {
this.subscriptions.forEach(subscription => {
if (subscription instanceof Subscriber) {
subscription.unsubscribe();
}
});
}
public getAnalysis() {
this.validator.getAnalysis(this.jobId).subscribe(
(result: RulePerJob[]) => {
console.log(result);
// this.analysisResult = result;
for(let rulePerJob of result) {
if(!this.validationResult.has(rulePerJob.guidelines)) {
this.validationResult.set(rulePerJob.guidelines, {analysisResult: [], successfulAnalysisResult: [], warningAnalysisResult: [], failedAnalysisResult: []});
}
this.validationResult.get(rulePerJob.guidelines).analysisResult.push(rulePerJob);
if(rulePerJob.rule_status == Status.FAILURE || rulePerJob.rule_status == Status.ERROR) {
this.validationResult.get(rulePerJob.guidelines).failedAnalysisResult.push(rulePerJob);
} else {
if(rulePerJob.has_warnings || rulePerJob.has_errors || rulePerJob.internal_error) {
this.validationResult.get(rulePerJob.guidelines).warningAnalysisResult.push(rulePerJob);
} else {
this.validationResult.get(rulePerJob.guidelines).successfulAnalysisResult.push(rulePerJob);
}
}
}
}
);
}
public openWarningsModal(rule: RulePerJob) {
this.warningsModalOpen = true;
this.warningsModal.cancelButton = false;
this.warningsModal.okButton = false;
this.warningsModal.alertTitle = rule.rule_name;
this.warningsModal.open();
}
public openErrorsModal(rule: RulePerJob) {
this.errorsModalOpen = true;
this.errorsModal.cancelButton = false;
this.errorsModal.okButton = false;
this.errorsModal.alertTitle = rule.rule_name;
this.errorsModal.open();
}
getWarnings(rule: RulePerJob) {
this.warnings = [];
this.openWarningsModal(rule);
this.warningsModalOpen = true;
this.validator.getWarnings(this.jobId, rule.rule_name).subscribe(
result => {
this.warnings = result;
console.log(result);
// this.result = result;
}
);
}
getErrors(rule: RulePerJob) {
this.internal_error = rule.internal_error;
this.errors = [];
this.openErrorsModal(rule);
this.errorsModalOpen = true;
if(rule.has_errors) {
this.validator.getErrors(this.jobId, rule.rule_name).subscribe(
result => {
this.errors = result;
console.log(result);
}
);
}
}
public getJobResult() {
this.validator.getJobResult(this.jobId).subscribe(
(result: JobResult) => {
this.jobResult = result;
let startDate = new Date(this.jobResult.startDate);
let endDate = this.jobResult.endDate ? new Date(this.jobResult.endDate) : new Date();
this.jobDuration = new Duration();
this.jobDuration.years = endDate.getFullYear() - startDate.getFullYear();
this.jobDuration.months = endDate.getMonth() - startDate.getMonth();
this.jobDuration.days = endDate.getDate() - startDate.getDate();
this.jobDuration.hours = endDate.getHours() - startDate.getHours();
this.jobDuration.minutes = endDate.getMinutes() - startDate.getMinutes();
this.jobDuration.seconds = endDate.getSeconds() - startDate.getSeconds();
console.log(this.jobDuration);
}
);
}
protected readonly Status = Status;
}

View File

@ -0,0 +1,189 @@
<div id="page_content">
<div id="page_content_header" class="uk-blur-background" offset="65">
<div class="uk-container uk-container-large">
<div class="uk-section uk-section-small">
<breadcrumbs [breadcrumbs]="breadcrumbs"></breadcrumbs>
<div class="uk-margin-large-top">
<h1 class="uk-h5">Validator's History</h1>
<div *ngIf="!result" class="uk-alert uk-alert-primary">
No validated metadata record yet
</div>
<div *ngIf="result" class="uk-margin-large-top">
<table class="uk-table uk-table-middle uk-table-divider uk-table-striped">
<thead>
<tr>
<th class="uk-width-medium">Base URL</th>
<!-- <th class="uk-text-center uk-width-xsmall">Validation Score</th>-->
<!-- <th *ngIf="result.fairScore" class="uk-text-center">Fair Score</th>-->
<th class="uk-text-center uk-width-xsmall">Started</th>
<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>
</tr>
</thead>
<tbody>
<tr>
<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>-->
<td class="uk-text-center">{{result.startDate | date:'yyyy-MM-dd, HH:mm:ss'}}</td>
<td class="uk-text-center">{{result.guidelines}}</td>
<!-- <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>
<ng-container *ngIf="result.progress != Progress.COMPLETED"
class="uk-label" [ngClass]="result.progress == Progress.IN_PROGRESS ? 'uk-label-warning' : 'uk-label-danger'">
{{result.progress}}
</ng-container>
</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>
<!-- <ng-container *ngIf="validationAnalysis">Hide Results</ng-container>-->
</a>
</td>
<!-- SUCCESS, FAILURE, IN_PROGRESS, COMPLETED, STOPPED-->
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<!--<div id="page_content">-->
<!-- <div id="page_content_header" class="uk-blur-background" offset="65">-->
<!-- <div class="uk-container uk-container-large">-->
<!-- <div class="uk-section" [formGroup]="form">-->
<!-- <ng-container *ngIf="!viewResults">-->
<!-- <h6>1. Select guidelines (*)</h6>-->
<!-- <div *ngFor="let option of options">-->
<!-- <input [id]="option.value" type="radio" [value]="option.value" name="guidelines" formControlName="guidelines">-->
<!-- <label [for]="option.value"> {{option.label}}</label>-->
<!-- </div>-->
<!-- <h6>2. Paste metadata record (*)</h6>-->
<!-- <div input class="uk-width-1-1 uk-margin-top" [formInput]="form.get('xml')" placeholder="Paste your xml here" type="textarea" [rows]="15"></div>-->
<!-- &lt;!&ndash; <ngx-dropzone #drop class="" (change)="fileChangeEvent($event, true)"&ndash;&gt;-->
<!-- &lt;!&ndash; [multiple]="false" [accept]="xml">&ndash;&gt;-->
<!-- &lt;!&ndash; <ngx-dropzone-preview *ngIf="form.value.value && form.value.value.name" class="file-preview"&ndash;&gt;-->
<!-- &lt;!&ndash; [removable]="true" (removed)="onRemove()">&ndash;&gt;-->
<!-- &lt;!&ndash; <ngx-dropzone-label class="file-label">{{ form.value.value.name }}</ngx-dropzone-label>&ndash;&gt;-->
<!-- &lt;!&ndash; </ngx-dropzone-preview>&ndash;&gt;-->
<!-- &lt;!&ndash; </ngx-dropzone>&ndash;&gt;-->
<!-- &lt;!&ndash; <button *ngIf="!form.value.value || filesToUpload" mat-button (click)="drop.showFileSelector()" type="button" class="attach-file-btn"&ndash;&gt;-->
<!-- &lt;!&ndash; [disabled]="!!form.value.value">&ndash;&gt;-->
<!-- &lt;!&ndash; <mat-icon class="mr-2">upload</mat-icon>&ndash;&gt;-->
<!-- &lt;!&ndash; <mat-label>{{ (form.get('data').value.label | translate)}}</mat-label>&ndash;&gt;-->
<!-- &lt;!&ndash; </button>&ndash;&gt;-->
<!-- <div class="uk-margin-medium-top">-->
<!-- <button class="uk-button uk-flex uk-flex-middle uk-flex-wrap"-->
<!-- [class.uk-button-primary]="form.valid" [class.uk-disabled]="!form.valid"-->
<!-- (click)="validate(); viewResults=true">-->
<!-- <span class="uk-margin-small-left">Start Validation</span>-->
<!-- </button>-->
<!-- </div>-->
<!-- </ng-container>-->
<!-- <div *ngIf="viewResults">-->
<!-- <button class="uk-button uk-button-link uk-flex uk-flex-middle uk-margin-bottom" (click)="viewResults = false">-->
<!-- <icon name="west" [flex]="true"></icon>-->
<!-- <span class="uk-margin-small-left">Back</span>-->
<!-- </button>-->
<!-- <div *ngIf="!result" class="uk-alert uk-alert-primary">-->
<!-- No validated metadata record yet-->
<!-- </div>-->
<!-- <div *ngIf="result">-->
<!-- <h6>Validator's History</h6>-->
<!-- <table class="uk-table uk-table-striped">-->
<!-- <thead>-->
<!-- <tr class="uk-child-width-1-4">-->
<!-- <th *ngIf="result.validationScore">Validation Score</th>-->
<!-- <th *ngIf="result.fairScore" class="uk-text-center">Fair Score</th>-->
<!-- <th class="uk-text-center">Guidelines</th>-->
<!-- <th class="uk-text-center">Actions</th>-->
<!-- </tr>-->
<!-- </thead>-->
<!-- <tbody>-->
<!-- <tr>-->
<!-- <td *ngIf="result.validationScore" [class.uk-text-center]="!result.validationScore">{{result.validationScore ? result.validationScore : '-'}}</td>-->
<!-- <td *ngIf="result.fairScore" class="uk-text-center">{{result.fairScore ? result.fairScore : '-'}}</td>-->
<!-- <td class="uk-text-center">{{currentGuideline?.label}}</td>-->
<!-- <td class="uk-flex uk-flex-center">-->
<!-- <a class="uk-button-link uk-flex uk-flex-middle uk-flex-center" (click)="validationAnalysis=!validationAnalysis">-->
<!-- <icon name="visibility" flex="true" class="uk-margin-small-right"></icon>-->
<!-- <ng-container *ngIf="!validationAnalysis">View Results</ng-container>-->
<!-- <ng-container *ngIf="validationAnalysis">Hide Results</ng-container>-->
<!-- </a>-->
<!-- </td>-->
<!-- </tr>-->
<!-- </tbody>-->
<!-- </table>-->
<!-- </div>-->
<!-- <div *ngIf="result && validationAnalysis" class="uk-margin-large-top">-->
<!-- <ul uk-tab>-->
<!-- <li *ngIf="result.validationScore"><a>Validation Result Analysis</a></li>-->
<!-- <li *ngIf="result.fairScore"><a>Fair Validation Result Analysis</a></li>-->
<!-- </ul>-->
<!-- <ul class="uk-switcher uk-margin">-->
<!-- <li *ngIf="result.validationScore"><ng-container *ngTemplateOutlet="rules_analysis; context: {rules: result.rules}"></ng-container></li>-->
<!-- <li *ngIf="result.fairScore"><ng-container *ngTemplateOutlet="rules_analysis; context: {rules: result.fairRules}"></ng-container></li>-->
<!-- </ul>-->
<!--&lt;!&ndash; <h6>Validation Result Analysis</h6>&ndash;&gt;-->
<!-- </div>-->
<!-- <modal-alert #warningsModal large="true" (alertOutput)="warningsModalOpen=false">-->
<!-- <div *ngIf="warningsModalOpen" class="uk-modal-body uk-height-min-medium uk-width-expand">-->
<!-- <div *ngIf="currentRule.warnings?.length > 0" class="uk-margin-bottom">-->
<!-- <span class="uk-text-warning">Warnings</span>-->
<!-- <div *ngFor="let message of currentRule.warnings">{{message}}</div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </modal-alert>-->
<!-- <modal-alert #errorsModal large="true" (alertOutput)="errorsModalOpen=false">-->
<!-- <div *ngIf="errorsModalOpen" class="uk-modal-body uk-height-min-medium uk-width-expand">-->
<!-- <div *ngIf="currentRule.errors?.length > 0" class="uk-margin-bottom">-->
<!-- <span class="uk-text-danger">Errors</span>-->
<!-- <div *ngFor="let message of currentRule.errors">{{message}}</div>-->
<!-- </div>-->
<!-- <div *ngIf="currentRule.internalError">Internal error: {{currentRule.internalError}}</div>-->
<!-- </div>-->
<!-- </modal-alert>-->
<!--&lt;!&ndash; <div class="uk-margin-top uk-section-muted uk-padding-small">&ndash;&gt;-->
<!--&lt;!&ndash; Comments&ndash;&gt;-->
<!--&lt;!&ndash; <ul class="uk-list uk-list-bullet">&ndash;&gt;-->
<!--&lt;!&ndash; <li>returns success, not warning status</li>&ndash;&gt;-->
<!--&lt;!&ndash; <li>what is the score?</li>&ndash;&gt;-->
<!--&lt;!&ndash; <li>do we have rule weight?</li>&ndash;&gt;-->
<!--&lt;!&ndash; <li>no description or user friendly name provided</li>&ndash;&gt;-->
<!--&lt;!&ndash; <li>errors/ warning not user friendly</li>&ndash;&gt;-->
<!--&lt;!&ndash; <li>can return errors & warnings</li>&ndash;&gt;-->
<!--&lt;!&ndash; <li>on errors, only the 1st one is returned (this is what comments in code say)</li>&ndash;&gt;-->
<!--&lt;!&ndash; </ul>&ndash;&gt;-->
<!--&lt;!&ndash; </div>&ndash;&gt;-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!--</div>-->

View File

@ -0,0 +1,4 @@
.uk-table-middle th {
vertical-align: middle !important;
display: table-cell;
}

View File

@ -0,0 +1,53 @@
import {Component, OnInit, ViewChild} from '@angular/core';
import {OaipmhValidatorService} from "../../../services/oaipmh-validator.service";
import {Status} from "../../entities/RuleInfo";
import {JobResult, Progress} from "../../entities/JobResult";
import {ActivatedRoute} from "@angular/router";
import {Breadcrumb} from "../../../shared/utils/breadcrumbs/breadcrumbs.component";
import {Subscriber} from "rxjs";
@Component({
selector: 'app-oaipmh-history',
templateUrl: './oaipmh-history.component.html',
styleUrls: ['./oaipmh-history.component.less']
})
export class OaipmhHistoryComponent implements OnInit {
public result: JobResult;
public jobId: string = "";
public breadcrumbs: Breadcrumb[] = [{name: 'home', route: '/'}, {name: 'Validator\'s History'}];
protected readonly Progress = Progress;
subscriptions = [];
constructor(private route: ActivatedRoute, private validator: OaipmhValidatorService) {}
ngOnInit(): void {
this.subscriptions.push(this.route.queryParams.subscribe(params => {
this.jobId = params['jobId'];
this.result = null;
if(!this.jobId) {
this.jobId = "825";
}
this.getJobResult();
}));
}
ngOnDestroy() {
this.subscriptions.forEach(subscription => {
if (subscription instanceof Subscriber) {
subscription.unsubscribe();
}
});
}
public getJobResult() {
this.validator.getJobResult(this.jobId).subscribe(
(result: JobResult) => {
this.result = result;
}
);
}
protected readonly Status = Status;
}

View File

@ -0,0 +1 @@
under development...

View File

@ -0,0 +1,15 @@
import {Component, OnInit, ViewChild} from '@angular/core';
import {OaipmhValidatorService} from "../../../services/oaipmh-validator.service";
@Component({
selector: 'app-oaipmh-validator',
templateUrl: './oaipmh-validator.component.html',
styleUrls: ['./oaipmh-validator.component.less']
})
export class OaipmhValidatorComponent implements OnInit {
constructor(private validator: OaipmhValidatorService) {}
ngOnInit() {
console.log("under development...");
}
}

View File

@ -1,6 +1,6 @@
import {Component, OnInit, ViewChild} from '@angular/core';
import {UntypedFormBuilder, UntypedFormGroup, Validators} from "@angular/forms";
import {SingleRecordValidatorService} from "./single-record-validator.service";
import {SingleRecordValidatorService} from "../../services/single-record-validator.service";
import {Option} from "../../shared/utils/input/input.component";
import {RuleInfo, Status} from "../entities/RuleInfo";
import {XmlValidationResponse} from "../entities/XmlValidationResponse";

View File

@ -0,0 +1,31 @@
import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {environment} from "../../environments/environment";
@Injectable({
providedIn: "root"
})
export class OaipmhValidatorService {
constructor(private http: HttpClient) {}
getAnalysis(jobId: string) {
let url: string = environment.validatorAPI + "reports/getResultsByJobId?jobId="+jobId;
return this.http.get(url);
}
getWarnings(jobId: string, ruleName: string) {
let url: string = environment.validatorAPI + "reports/getWarningsReport?jobId="+jobId+"&ruleName="+ruleName;
return this.http.get<any>(url);
}
getErrors(jobId: string, ruleName: string) {
let url:string = environment.validatorAPI + "reports/getErrorsReport?jobId="+jobId+"&ruleName="+ruleName;
return this.http.get<any>(url);
}
getJobResult(jobId: string) {
let url: string = environment.validatorAPI + "reports/getJobResult?jobId="+jobId;
return this.http.get(url);
}
}

View File

@ -1,7 +1,7 @@
import {Injectable} from "@angular/core";
import {HttpClient, HttpHeaders} from "@angular/common/http";
import {Observable} from "rxjs";
import {environment} from "../../../environments/environment";
import {environment} from "../../environments/environment";
@Injectable({
providedIn: "root"

View File

@ -95,7 +95,20 @@
<nav uk-navbar="" class="uk-navbar">
<div class="uk-navbar-left">
<a class="uk-logo uk-navbar-item uk-text-center uk-text-bold uk-margin-medium-left"
href="./home"><img src="http://catalogue.openaire.eu/images/logos/Metadata_validator_logo.svg" alt="Metadata Validator"></a>
href="./"><img src="http://catalogue.openaire.eu/images/logos/Metadata_validator_logo.svg" alt="Metadata Validator"></a>
</div>
<div class="uk-navbar-center">
<ul class="uk-navbar-nav">
<li class="uk-parent">
<a routerLink="/single-record-validate">Singe Record</a>
</li>
<li class="uk-parent">
<a routerLink="/oaipmh-history" [queryParams]="{jobId: 825}">History [demo]</a>
</li>
<li class="uk-parent">
<a routerLink="/oaipmh-analysis" [queryParams]="{jobId: 825}">Analysis [demo]</a>
</li>
</ul>
</div>
</nav>
</div>

View File

@ -0,0 +1,24 @@
import {Component, Input} from "@angular/core";
export interface Breadcrumb {
name: string;
route?: string;
keepFormat?: boolean
}
@Component({
selector: 'breadcrumbs',
template: `
<ul class="uk-breadcrumb uk-margin-remove-bottom" [ngClass]="addClass" [class.uk-light]="light">
<li *ngFor="let breadcrumb of breadcrumbs">
<a [class.uk-text-capitalize]="!breadcrumb.keepFormat" *ngIf="breadcrumb.route" [routerLink]="breadcrumb.route">{{breadcrumb.name}}</a>
<span [class.uk-text-capitalize]="!breadcrumb.keepFormat" *ngIf="!breadcrumb.route">{{breadcrumb.name}}</span>
</li>
</ul>`
})
export class BreadcrumbsComponent {
@Input() public light: boolean = false;
@Input() public breadcrumbs: Breadcrumb[] = [];
@Input() public addClass = "";
}

View File

@ -0,0 +1,11 @@
import {NgModule} from "@angular/core";
import {CommonModule} from "@angular/common";
import {BreadcrumbsComponent} from "./breadcrumbs.component";
import {RouterModule} from "@angular/router";
@NgModule({
imports: [CommonModule, RouterModule],
declarations: [BreadcrumbsComponent],
exports: [BreadcrumbsComponent]
})
export class BreadcrumbsModule {}

@ -1 +1 @@
Subproject commit 9141b4c9c3363ceb8a690a361d483268eb7eabe4
Subproject commit 2fd57843f85125e54adfb95b35776755037ea359

@ -1 +1 @@
Subproject commit 143c2719ec972c8456817c7c1dd17f96593eeddf
Subproject commit 2fffe0fa672adcf5577461d10c30e34b00308c85

View File

@ -2,6 +2,9 @@
@aggregator-secondary-background: #5ABDF9;
@global-primary-gradient: linear-gradient(101deg, @aggregator-primary-background 0%, @aggregator-secondary-background 100%);
@progress-height: 20px;
@progress-bar-background: @global-muted-color;
//.sidebar_main_swipe #sidebar_main #sidebar_content {
// width: 280px;
// position: fixed;

View File

@ -4,7 +4,7 @@
export const environment = {
production: false,
validatorAPI: "http://duffy.di.uoa.gr:19580/uoa-validator-api/"
validatorAPI: "http://rudie.di.uoa.gr:9200/"
};
/*