[main | DONE | CHANGED]: Added sepatare modules & routing modules for each component | Updated Single record validator to look the same as oai pmh validator & changed endpoint from "/validate" to "/validateRecord" | Deleted single-record-validator.component.less, oaipmh-validator.component.less, oaipmh-analysis.component.less | Created AnalysisComponent (<app-analysis>) to be commonly called by SingleRecordValidatorComponent and OaipmhAnalysisComponent & created SettingsComponent (<app-settings>) to be commonly called by SingleRecordValidatorComponent and OaipmhValidatorComponent.

This commit is contained in:
Konstantina Galouni 2024-07-25 18:41:59 +03:00
parent e08b72eec2
commit 39e1e7acd2
29 changed files with 1110 additions and 916 deletions

View File

@ -1,36 +1,35 @@
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 = [
{
path: '',
pathMatch: 'full',
redirectTo: 'single-record-validate'
redirectTo: 'oaipmh'
},
{
path: 'single-record-validate',
component: SingleRecordValidatorComponent
},
loadChildren: () => import('./pages/single-record-validator/single-record-validator.module').then(m => m.SingleRecordValidatorModule)},
{
path: 'oaipmh-analysis',
component: OaipmhAnalysisComponent
loadChildren: () => import('./pages/oaipmh-validator/validation-analysis/oaipmh-analysis.module').then(m => m.OaipmhAnalysisModule)
},
{
path: 'oaipmh-history',
component: OaipmhHistoryComponent
loadChildren: () => import('./pages/oaipmh-validator/validation-history/oaipmh-history.module').then(m => m.OaipmhHistoryModule)
},
{
path: 'oaipmh',
component: OaipmhValidatorComponent
loadChildren: () => import('./pages/oaipmh-validator/validation-settings/oaipmh-validator.module').then(m => m.OaipmhValidatorModule)
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
imports: [RouterModule.forRoot(routes,
{
onSameUrlNavigation: "reload"
}
)],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@ -5,39 +5,38 @@ import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { TopmenuComponent } from './shared/topmenu/topmenu.component';
import { SidebarComponent } from './shared/sidebar/sidebar.component';
import { SingleRecordValidatorComponent } from './pages/single-record-validator/single-record-validator.component';
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
import {HttpClientModule} from "@angular/common/http";
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 {InputModule} from "./openaire-library/sharedComponents/input/input.module";
import {AlertModalModule} from "./openaire-library/utils/modal/alertModal.module";
import {IconsModule} from "./openaire-library/utils/icons/icons.module";
import {BreadcrumbsModule} from "./openaire-library/utils/breadcrumbs/breadcrumbs.module";
import {CommonModule} from "@angular/common";
import {SingleRecordValidatorModule} from "./pages/single-record-validator/single-record-validator.module";
import {AnalysisModule} from "./pages/shared/analysis/analysis.module";
import {SettingsModule} from "./pages/shared/settings/settings.module";
import {OaipmhValidatorModule} from "./pages/oaipmh-validator/validation-settings/oaipmh-validator.module";
import {OaipmhHistoryModule} from "./pages/oaipmh-validator/validation-history/oaipmh-history.module";
import {OaipmhAnalysisModule} from "./pages/oaipmh-validator/validation-analysis/oaipmh-analysis.module";
@NgModule({
declarations: [
AppComponent,
TopmenuComponent,
SidebarComponent,
SingleRecordValidatorComponent,
OaipmhValidatorComponent,
OaipmhHistoryComponent,
OaipmhAnalysisComponent
],
imports: [
CommonModule,
BrowserModule,
AppRoutingModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule,
InputModule,
AlertModalModule,
IconsModule,
BreadcrumbsModule
SingleRecordValidatorModule,
OaipmhValidatorModule,
OaipmhHistoryModule,
OaipmhAnalysisModule,
AnalysisModule,
SettingsModule
],
providers: [],
exports: [],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@ -1,5 +1,6 @@
export class JobResult {
id: string;
xml?: string;
baseUrl: string;
set: string;
numberOfRecords: number;

View File

@ -0,0 +1,10 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { PreviousRouteRecorder } from "../../../openaire-library/utils/piwik/previousRouteRecorder.guard";
import { OaipmhAnalysisComponent } from "./oaipmh-analysis.component";
@NgModule({
imports: [
RouterModule.forChild([{ path: '', component: OaipmhAnalysisComponent, canDeactivate: [PreviousRouteRecorder] }])
]
})
export class OaipmhAnalysisRoutingModule { }

View File

@ -1,301 +1,4 @@
<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>
<span *ngIf="!ruleResult.ruleName">[No title available]</span>
<span *ngIf="ruleResult.ruleName">
<a *ngIf="ruleResult.link" class="custom-external uk-link-text" [href]="ruleResult.link" target="_blank">{{ruleResult.ruleName}}</a>
<span *ngIf="!ruleResult.link">{{ruleResult.ruleName}}</span>
</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>-->
<!-- <span>, </span>-->
<!-- <span uk-tooltip="Accessible">A</span>-->
<!-- <span>, </span>-->
<!-- <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>-->
<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-bolder uk-text-primary"
style="position:relative; top: -18px; mix-blend-mode: color-burn">
{{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 getKeys(validationResult)" 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 getValues(validationResult)">
<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">
<!-- {Base OAI-PMH url}?verb=GetRecord&metadataPrefix={the metadata prefix for each rule*}&identifier={oai identifier, which you have}-->
<!-- *-->
<a [href]="jobResult.baseUrl + '?verb=GetRecord&metadataPrefix=' + guidelinesLabelToPrefix.get(jobResult.guidelines) + '&identifier=' + 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.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]="jobResult.baseUrl + '?verb=GetRecord&metadataPrefix=' + guidelinesLabelToPrefix.get(jobResult.guidelines) + '&identifier=' + 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="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 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>
</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 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>
<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 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>
<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>
<app-analysis [breadcrumbs]="breadcrumbs" [single]="false" [jobResult]="jobResult"
[jobDuration]="jobDuration" [validationResult]="validationResult"
[(warnings)]="warnings" [(errors)]="errors" [(internal_error)]="internal_error"
(getWarningsClicked)="getWarnings($event)" (getErrorsClicked)="getErrors($event)"></app-analysis>

View File

@ -1,5 +1,4 @@
import {ChangeDetectorRef, Component, OnInit, ViewChild} from '@angular/core';
import {UntypedFormGroup} from "@angular/forms";
import {ChangeDetectorRef, Component, OnInit} from '@angular/core';
import {OaipmhValidatorService} from "../../../services/oaipmh-validator.service";
import {Subscriber} from "rxjs";
import {ActivatedRoute} from "@angular/router";
@ -7,71 +6,31 @@ import {RulePerJob, Status} from "../../entities/RulePerJob";
import {Issue} from "../../entities/Issue";
import {JobResult} from "../../entities/JobResult";
import {Breadcrumb} from "../../../openaire-library/utils/breadcrumbs/breadcrumbs.component";
export class Duration {
years: number;
months: number;
days: number;
hours: number;
minutes: number;
seconds: number;
}
import {Duration} from "../../shared/analysis/analysis.component";
@Component({
selector: 'app-oaipmh-analysis',
templateUrl: './oaipmh-analysis.component.html',
styleUrls: ['./oaipmh-analysis.component.less']
templateUrl: './oaipmh-analysis.component.html'
})
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 = [];
public guidelinesLabelToPrefix: Map<string, string> = new Map([
['OpenAIRE Guidelines for Literature Repositories Profile v4', 'oai_openaire'],
['OpenAIRE Guidelines for Literature Repositories Profile v3', 'oai_dc'],
['OpenAIRE Guidelines for Data Archives Profile v2', 'oai_datacite'],
['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 {
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();
@ -80,16 +39,6 @@ export class OaipmhAnalysisComponent implements OnInit {
}));
}
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) {
@ -98,7 +47,6 @@ export class OaipmhAnalysisComponent implements OnInit {
});
}
public getAnalysis() {
this.subscriptions.push(this.validator.getAnalysis(this.jobId).subscribe(
(result: RulePerJob[]) => {
@ -156,31 +104,11 @@ export class OaipmhAnalysisComponent implements OnInit {
));
}
public openWarningsModal(rule: RulePerJob) {
this.warningsModalOpen = true;
this.warningsModal.cancelButton = false;
this.warningsModal.okButton = false;
this.warningsModal.alertTitle = rule.ruleName;
this.warningsModal.open();
}
public openErrorsModal(rule: RulePerJob) {
this.errorsModalOpen = true;
this.errorsModal.cancelButton = false;
this.errorsModal.okButton = false;
this.errorsModal.alertTitle = rule.ruleName;
this.errorsModal.open();
}
getWarnings(rule: RulePerJob) {
this.warnings = [];
this.openWarningsModal(rule);
this.warningsModalOpen = true;
this.subscriptions.push(this.validator.getWarnings(this.jobId, rule.ruleName).subscribe(
result => {
this.warnings = result;
console.log(result);
// this.result = result;
}
));
}
@ -188,14 +116,10 @@ export class OaipmhAnalysisComponent implements OnInit {
getErrors(rule: RulePerJob) {
this.internal_error = rule.internal_error;
this.errors = [];
this.openErrorsModal(rule);
this.errorsModalOpen = true;
if(rule.has_errors) {
this.subscriptions.push(this.validator.getErrors(this.jobId, rule.ruleName).subscribe(
result => {
this.errors = result;
console.log(result);
}
));
}
@ -220,13 +144,5 @@ export class OaipmhAnalysisComponent implements OnInit {
));
}
public getKeys(map) {
return Array.from(map.keys());
}
public getValues(map) {
return Array.from(map.values());
}
protected readonly Status = Status;
}

View File

@ -0,0 +1,21 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {RouterModule} from '@angular/router';
import {OaipmhAnalysisRoutingModule} from "./oaipmh-analysis-routing.module";
import {OaipmhAnalysisComponent} from "./oaipmh-analysis.component";
import {AnalysisModule} from "../../shared/analysis/analysis.module";
@NgModule({
imports: [
CommonModule, RouterModule,
OaipmhAnalysisRoutingModule, AnalysisModule
],
declarations: [
OaipmhAnalysisComponent
],
providers: [],
exports: [
OaipmhAnalysisComponent
]
})
export class OaipmhAnalysisModule {}

View File

@ -0,0 +1,10 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { PreviousRouteRecorder } from "../../../openaire-library/utils/piwik/previousRouteRecorder.guard";
import { OaipmhHistoryComponent } from "./oaipmh-history.component";
@NgModule({
imports: [
RouterModule.forChild([{ path: '', component: OaipmhHistoryComponent, canDeactivate: [PreviousRouteRecorder] }])
]
})
export class OaipmhHistoryRoutingModule { }

View File

@ -0,0 +1,22 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {RouterModule} from '@angular/router';
import {OaipmhHistoryRoutingModule} from "./oaipmh-history-routing.module";
import {OaipmhHistoryComponent} from "./oaipmh-history.component";
import {BreadcrumbsModule} from "../../../openaire-library/utils/breadcrumbs/breadcrumbs.module";
@NgModule({
imports: [
CommonModule, RouterModule,
OaipmhHistoryRoutingModule,
BreadcrumbsModule
],
declarations: [
OaipmhHistoryComponent
],
providers: [],
exports: [
OaipmhHistoryComponent
]
})
export class OaipmhHistoryModule {}

View File

@ -0,0 +1,10 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { PreviousRouteRecorder } from "../../../openaire-library/utils/piwik/previousRouteRecorder.guard";
import { OaipmhValidatorComponent } from "./oaipmh-validator.component";
@NgModule({
imports: [
RouterModule.forChild([{ path: '', component: OaipmhValidatorComponent, canDeactivate: [PreviousRouteRecorder] }])
]
})
export class OaipmhValidatorRoutingModule { }

View File

@ -1,232 +1 @@
<div>
<!-- uk-blur-background-->
<div class="">
<div class="uk-grid uk-margin-remove-left" uk-grid>
<!-- <div id="landing-center-content" class="uk-width-expand uk-padding-remove uk-background-default">-->
<!-- <div class="uk-section uk-width-expand" [formGroup]="form">-->
<div id="center-content" class="uk-width-2-3 uk-width-3-4@l" [formGroup]="form">
<div class="uk-section uk-container uk-container-large">
<div class="uk-margin-large-bottom">
<h6><span class="uk-text-secondary uk-margin-right">1.</span> Select a data source (repository or journal)</h6>
<div input class="uk-width-xlarge uk-margin-xsmall-left uk-margin-medium-top" inputClass="flat x-small" [formInput]="form.get('url')"
placeholder="Give the URL from one of your registered repositories" type="URL" (valueChange)="getSets()"></div>
</div>
<div class="uk-margin-large-bottom">
<h6><span class="uk-text-secondary uk-margin-right">2.</span> Select guidelines</h6>
<div class="uk-margin-medium-top">
<div *ngFor="let option of options" class="uk-margin-xsmall-left uk-margin-small-bottom">
<input [id]="option.value" type="radio" [value]="option.value" name="guidelines" formControlName="guidelines">
<label [for]="option.value" class="uk-margin-xsmall-left">{{option.label}}</label>
</div>
</div>
</div>
<div class="uk-margin-large-bottom">
<h6><span class="uk-text-secondary uk-margin-right">3.</span> Define the number of records to validate</h6>
<!-- <div class="uk-disabled uk-text-muted uk-margin-xsmall-left uk-margin-medium-top uk-margin-small-bottom">-->
<!-- <input id="all" type="radio" name="recordsNum" value="-1" formControlName="recordsNum">-->
<!-- <label for="all" class="uk-margin-xsmall-left">All</label>-->
<!-- </div>-->
<div class="uk-flex uk-flex-middle uk-margin-xsmall-left">
<!-- <input #customRecordsNum id="custom" type="radio" name="recordsNum" [value]="recordsNum" formControlName="recordsNum">-->
<!-- <label for="custom" class="uk-margin-xsmall-left uk-margin-right">-->
<!-- Custom-->
<!-- </label>-->
<span class="uk-flex-inline uk-flex-middle">
<icon class="clickable" name="remove_circle_outline" flex="true" ratio="0.9" type="outlined"
[ngClass]="recordsNum <= 10 ? 'uk-disabled uk-text-muted' : ''" (click)="updateRecordsNum(false)"></icon>
<span class="uk-margin-small-left uk-margin-small-right">{{recordsNum}}</span>
<icon class="clickable" name="add_circle_outline" flex="true" ratio="0.9" type="outlined"
[ngClass]="recordsNum >= 100 ? 'uk-disabled uk-text-muted' : ''" (click)="updateRecordsNum(true)"></icon>
</span>
</div>
<div [disabled]="loadingSets || sets.length == 1" input type="select" placeholder="Select validation set" inputClass="flat x-small"
[options]="sets" [formInput]="form.get('set')" class="uk-width-large uk-margin-medium-top uk-margin-xsmall-left"></div>
</div>
<div class="uk-margin-medium-top uk-margin-xsmall-left">
<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();">
<span>Start Validation</span>
</button>
</div>
</div>
</div>
<!-- <div id="right-sidebar" class="uk-width-1-3 uk-width-1-4@l uk-padding-remove uk-background-primary">-->
<!-- <div class="uk-sticky" uk-sticky [attr.offset]="offset">-->
<!-- <div class="uk-overflow-auto uk-height-1-1">-->
<!-- <div class="uk-h5 uk-light uk-text-center">Help</div>-->
<!-- <div class="uk-card uk-card-default">-->
<!-- <div class="uk-card-body">test body</div>-->
<!-- <div class="uk-card-footer">Dismiss Help</div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<div id="right-sidebar" *ngIf="help"
class="uk-width-1-3 uk-width-1-4@l uk-padding-remove uk-text-small uk-visible@m uk-background-primary">
<div class="uk-sticky" uk-sticky="end: true" [attr.offset]="offset">
<div #right_sidebar class="uk-height-1-1">
<div class="uk-height-1-1">
<div #right_sidebar_header class="uk-section uk-section-small">
<div class="uk-h5 uk-light uk-text-center">Help</div>
</div>
<div class="uk-card uk-card-default" [style]="'height: ' + right_sidebar_card_height + 'px;'">
<div class="uk-overflow-auto uk-padding-remove-horizontal" [style]="'height: ' + right_sidebar_body_height + 'px;'">
<div class="uk-card-body">
<ul uk-accordion>
<li class="uk-open">
<a class="uk-accordion-title uk-text-small uk-padding-small uk-padding-remove-vertical" href="#">
<span class="accordion-title-num">
<svg height="27" width="27">
<circle cx="13" cy="13" r="13" stroke-width="1"></circle>
<text x="50%" y="50%" text-anchor="middle" dy=".2em" font-size="12" font-weight="200">
1
</text>
</svg>
</span>
<span class="uk-margin-small-left">Select Data source</span>
</a>
<div class="uk-accordion-content uk-text-small uk-padding-small uk-padding-remove-vertical">
<p>
Select from the list, or enter the base OAI URL of the repository/ journal.
The repository's/ journal's published sets are exposed in a later stage and you may select one for the compatibility tests.
</p>
</div>
</li>
<li>
<a class="uk-accordion-title uk-text-small uk-padding-small uk-padding-remove-vertical" href="#">
<span class="accordion-title-num">
<svg height="27" width="27">
<circle cx="13" cy="13" r="13" stroke-width="1"></circle>
<text x="50%" y="50%" text-anchor="middle" dy=".2em" font-size="12" font-weight="200">
2
</text>
</svg>
</span>
<span class="uk-margin-small-left">Select Guidelines</span>
</a>
<div class="uk-accordion-content uk-text-small uk-padding-small uk-padding-remove-vertical">
<p>You may apply two types of compatibility tests on your repository:</p>
<ul>
<li>Content: specific to the content/ format of the dc fields</li>
<li>Usage: specific to the implementation of OAI-PMH</li>
</ul>
<p>You may select from a predefined set of rules (OpenAIRE for Literature Repositories, OpenAIRE for Data Archives) or customize them according to your needs.</p>
</div>
</li>
<li>
<a class="uk-accordion-title uk-text-small uk-padding-small uk-padding-remove-vertical" href="#">
<span class="accordion-title-num">
<svg height="27" width="27">
<circle cx="13" cy="13" r="13" stroke-width="1"></circle>
<text x="50%" y="50%" text-anchor="middle" dy=".2em" font-size="12" font-weight="200">
3
</text>
</svg>
</span>
<span class="uk-margin-small-left">Select Parameters</span>
</a>
<div class="uk-accordion-content uk-text-small uk-padding-small uk-padding-remove-vertical">
<p>
...
</p>
</div>
</li>
</ul>
</div>
</div>
<a #right_sidebar_footer uk-sticky="end: true" class="uk-sticky uk-card-footer uk-button-link uk-text-center" (click)="help=false">Dismiss Help</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div *ngIf="!help" class="quick-contact uk-align-right uk-padding uk-padding-remove-vertical">
<button class="uk-button uk-box-shadow-default uk-box-shadow-default-hover uk-button-secondary uk-flex uk-flex-middle"
(click)="openHelp();">
<icon name="lightbulb" [flex]="true"></icon>
<span class="uk-margin-small-left">help</span>
</button>
</div>
</div>
</div>
<!--<div id="tm-main" class="uk-visible@m landing uk-section uk-padding-remove tm-middle">-->
<!-- <div class="tm-main">-->
<!-- <div class="publication">-->
<!-- <div class="uk-grid uk-margin-remove-left" uk-grid>-->
<!-- <div id="center-content" class="uk-width-expand uk-padding-remove uk-background-default">-->
<!-- <div class="uk-heading-2xlarge">-->
<!-- <div>test</div>-->
<!-- <div>test</div>-->
<!-- <div>test</div>-->
<!-- <div>test</div>-->
<!-- <div>test</div>-->
<!-- <div>test</div>-->
<!-- <div>test</div>-->
<!-- <div>test</div>-->
<!-- <div>test</div>-->
<!-- <div>test</div>-->
<!-- </div>-->
<!-- </div>-->
<!-- <ng-container >-->
<!-- <div id="right-sidebar" class="uk-width-1-3 uk-width-1-4@l uk-padding-remove uk-text-small uk-visible@m">-->
<!-- <div class="uk-sticky" uk-sticky="bottom: true" [attr.offset]="offset">-->
<!-- <div class="uk-overflow-auto uk-height-1-1">-->
<!-- test-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </ng-container>-->
<!-- </div>-->
<!-- </div>-->
<!--</div>-->
<!--<div id="tm-main" class="uk-visible@m landing uk-section uk-padding-remove tm-middle">-->
<!-- <div class="tm-main">-->
<!-- <div class="">-->
<!-- <div class="uk-grid uk-margin-remove-left" uk-grid>-->
<!-- <div id="center-content" class="uk-width-2-3 uk-width-3-4@l">-->
<!-- <div class="uk-heading-2xlarge">-->
<!-- <div>test</div>-->
<!-- <div>test</div>-->
<!-- <div>test</div>-->
<!-- <div>test</div>-->
<!-- <div>test</div>-->
<!-- <div>test</div>-->
<!-- <div>test</div>-->
<!-- <div>test</div>-->
<!-- <div>test</div>-->
<!-- <div>test</div>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div id="right-sidebar" class="uk-width-1-3 uk-width-1-4@l uk-padding-remove uk-background-primary">-->
<!-- <div class="uk-sticky" uk-sticky [attr.offset]="offset">-->
<!-- <div class="uk-overflow-auto uk-height-1-1">-->
<!-- <div class="uk-h5 uk-light uk-text-center">Help</div>-->
<!-- <div class="uk-card uk-card-default">-->
<!-- <div class="uk-card-body">test body</div>-->
<!-- <div class="uk-card-footer">Dismiss Help</div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!--</div>-->
<app-settings [single]="false" [sets]="sets" (getSetsClicked)="getSets($event)" (validationClicked)="validate($event)"></app-settings>

View File

@ -1,67 +1,29 @@
import {ChangeDetectorRef, Component, OnInit, ViewChild} from '@angular/core';
import {ChangeDetectorRef, Component, OnInit} from '@angular/core';
import {Router} from "@angular/router";
import {Subscriber} from "rxjs";
import {OaipmhValidatorService} from "../../../services/oaipmh-validator.service";
import {UntypedFormBuilder, UntypedFormGroup, Validators} from "@angular/forms";
import {Option} from "../../../openaire-library/sharedComponents/input/input.component";
import {StringUtils} from "../../../openaire-library/utils/string-utils.class";
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',
styleUrls: ['./oaipmh-validator.component.less']
templateUrl: './oaipmh-validator.component.html'
})
export class OaipmhValidatorComponent implements OnInit {
@ViewChild("right_sidebar") right_sidebar;
@ViewChild("right_sidebar_header") right_sidebar_header;
@ViewChild("right_sidebar_footer") right_sidebar_footer;
public options: Option[] = [
{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 & OpenAIRE FAIR Guidelines for Literature Profile', value: 'OpenAIRE Guidelines for Literature Repositories Profile v4'},
{label: 'OpenAIRE FAIR Guidelines for Data Repositories Profile', value: 'OpenAIRE FAIR Guidelines for Data Repositories Profile'}
];
@ViewChild('customRecordsNum') customRecordsNum;
public form: UntypedFormGroup;
public sets: Option[] = [{label: 'All sets', value: 'all'}];
public recordsNum: number = 10;
public loadingSets: boolean = false;
public offset: number = 0;
public help: boolean = false;
public right_sidebar_body_height: number = 0;
public right_sidebar_card_height: number = 0;
subscriptions = [];
constructor(private fb: UntypedFormBuilder, private router: Router,
constructor(private router: Router,
private cdr: ChangeDetectorRef,
private validator: OaipmhValidatorService) {
this.form = this.fb.group({
url: this.fb.control("", [Validators.required, StringUtils.urlValidator()]),//[Validators.required/*, Validators.email*/]),
guidelines: this.fb.control("", Validators.required),
recordsNum: this.fb.control(this.recordsNum, Validators.required),
set: this.fb.control('all', Validators.required)
});
}
ngOnInit() {}
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) {
@ -70,51 +32,27 @@ export class OaipmhValidatorComponent implements OnInit {
});
}
openHelp() {
this.help = true;
this.cdr.detectChanges();
if (typeof document !== 'undefined') {
console.log(this.right_sidebar.nativeElement.offsetHeight);
console.log(this.right_sidebar_header.nativeElement.offsetHeight);
console.log(this.right_sidebar_footer.nativeElement.offsetHeight);
this.right_sidebar_card_height = this.right_sidebar.nativeElement.offsetHeight - this.right_sidebar_header.nativeElement.offsetHeight + 1;
this.right_sidebar_body_height = this.right_sidebar_card_height - this.right_sidebar_footer.nativeElement.offsetHeight;
}
}
updateRecordsNum(increase: boolean = true) {
this.recordsNum = this.recordsNum + (increase ? 10 : -10);
this.form.get('recordsNum').setValue(this.recordsNum);
if(this.customRecordsNum) {
this.customRecordsNum.nativeElement.checked = true;
}
}
getSets() {
this.loadingSets = true;
getSets(form) {
let options: Option[] = [{label: 'All sets', value: 'all'}];
if(this.form.get('setName')) {
this.form.get('setName').setValue('all');
if(form.get('setName')) {
form.get('setName').setValue('all');
}
if(this.form.get('url') && this.form.get('url').value && StringUtils.isValidUrl(this.form.get('url').value)) {
this.subscriptions.push(this.validator.getSets(this.form.get('url').value).subscribe(sets => {
if(form.get('url') && form.get('url').value && StringUtils.isValidUrl(form.get('url').value)) {
this.subscriptions.push(this.validator.getSets(form.get('url').value).subscribe(sets => {
for(let set of sets) {
options.push({label: set['setName'], value: set['setSpec']});
}
this.sets = options;
this.loadingSets = false;
}, error => {
this.sets = options;
this.loadingSets = false;
}));
} else {
this.sets = options;
this.loadingSets = false;
}
}
validate() {
this.subscriptions.push(this.validator.validate(this.form.get('guidelines').value, this.form.get('url').value, this.form.get('recordsNum').value, this.form.get('set').value).subscribe(
validate(form) {
this.subscriptions.push(this.validator.validate(form.get('guidelines').value, form.get('url').value, form.get('recordsNum').value, form.get('set').value).subscribe(
(result: JobResult) => {
this.router.navigate(['/oaipmh-history'], {
queryParams: { 'jobId': result.id }

View File

@ -0,0 +1,21 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {RouterModule} from '@angular/router';
import {OaipmhValidatorRoutingModule} from "./oaipmh-validator-routing.module";
import {OaipmhValidatorComponent} from "./oaipmh-validator.component";
import {SettingsModule} from "../../shared/settings/settings.module";
@NgModule({
imports: [
CommonModule, RouterModule,
OaipmhValidatorRoutingModule, SettingsModule
],
declarations: [
OaipmhValidatorComponent
],
providers: [],
exports: [
OaipmhValidatorComponent
]
})
export class OaipmhValidatorModule {}

View File

@ -0,0 +1,299 @@
<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 *ngIf="!single" 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>
<span *ngIf="!ruleResult.ruleName">[No title available]</span>
<span *ngIf="ruleResult.ruleName">
<a *ngIf="ruleResult.link" class="custom-external uk-link-text" [href]="ruleResult.link" target="_blank">{{ruleResult.ruleName}}</a>
<span *ngIf="!ruleResult.link">{{ruleResult.ruleName}}</span>
</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 *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>-->
<td *ngIf="!single">
<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-bolder uk-text-primary"
style="position:relative; top: -18px; mix-blend-mode: color-burn">
{{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">
<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 getKeys(validationResult)" 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 getValues(validationResult)">
<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">
<div [class]="message.records ? 'uk-link uk-accordion-title' : 'uk-text-emphasis'">
<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-bold uk-text-default"><ng-container *ngIf="message.records">{{message.records.length | number}} Records - </ng-container>{{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>
</div>
<div *ngIf="message.records" class="uk-accordion-content">
<hr>
<div *ngFor="let record of message.records" class="uk-margin-medium-left">
<!-- {Base OAI-PMH url}?verb=GetRecord&metadataPrefix={the metadata prefix for each rule*}&identifier={oai identifier, which you have}-->
<!-- *-->
<a [href]="jobResult.baseUrl + '?verb=GetRecord&metadataPrefix=' + guidelinesLabelToPrefix.get(jobResult.guidelines) + '&identifier=' + 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">
<div [class]="message.records ? 'uk-link uk-accordion-title' : 'uk-text-emphasis'">
<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-bold uk-text-default"><ng-container *ngIf="message.records">{{message.records.length | number}} Records - </ng-container>{{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>
</div>
<div *ngIf="message.records" class="uk-accordion-content">
<hr>
<div *ngFor="let record of message.records" class="uk-margin-medium-left">
<a [href]="jobResult.baseUrl + '?verb=GetRecord&metadataPrefix=' + guidelinesLabelToPrefix.get(jobResult.guidelines) + '&identifier=' + 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="end: true" [attr.offset]="offset">
<div class="uk-overflow-auto uk-height-1-1 uk-padding">
<div *ngIf="jobResult.baseUrl" 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.xml" class="uk-card uk-card-default">
<div class="uk-card-body uk-card-small">
<div class="uk-flex uk-flex-middle">
<icon name="code" 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">Xml metadata record</div>
<div class="uk-text-primary uk-text-bold uk-text-default multi-line-ellipsis lines-3">
<p>{{jobResult.xml}}</p>
</div>
<a class="uk-link" (click)="openXmlModal()">View xml record</a>
</div>
</div>
</div>
<modal-alert #xmlModal large="true" (alertOutput)="xmlModalOpen=false" classTitle="uk-border-bottom">
<div *ngIf="xmlModalOpen" class="uk-modal-body uk-height-min-medium uk-width-expand">
<pre>{{jobResult.xml}}</pre>
</div>
</modal-alert>
</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 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>
</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 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>
<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 *ngIf="hasDuration" 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 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>
<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>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,132 @@
import {Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
import {JobResult} from "../../entities/JobResult";
import {RulePerJob, Status} from "../../entities/RulePerJob";
import {Issue} from "../../entities/Issue";
import {Breadcrumb} from "../../../openaire-library/utils/breadcrumbs/breadcrumbs.component";
export class Duration {
years: number;
months: number;
days: number;
hours: number;
minutes: number;
seconds: number;
}
@Component({
selector: 'app-analysis',
templateUrl: './analysis.component.html',
styleUrls: ['./analysis.component.less']
})
export class AnalysisComponent {
@Input() single: boolean = true;
@Input() jobResult: JobResult = null;
@Input() jobDuration: Duration = null;
@Input() validationResult: Map<string, { "analysisResult": RulePerJob[], "successfulAnalysisResult": RulePerJob[], "warningAnalysisResult": RulePerJob[], "failedAnalysisResult": RulePerJob[] }>;
@Input() breadcrumbs: Breadcrumb[];
@Input() warnings: Issue[];
@Input() errors: Issue[];
@Input() internal_error: string;
@Output() warningsChange = new EventEmitter();
@Output() errorsChange = new EventEmitter();
@Output() internal_errorChange = new EventEmitter();
@Output() getWarningsClicked = new EventEmitter();
@Output() getErrorsClicked = new EventEmitter();
public warningsModalOpen: boolean = false;
public errorsModalOpen: boolean = false;
public xmlModalOpen: boolean = false;
@ViewChild('warningsModal') warningsModal;
@ViewChild('errorsModal') errorsModal;
@ViewChild('xmlModal') xmlModal;
public offset: number;
public guidelinesLabelToPrefix: Map<string, string> = new Map([
['OpenAIRE Guidelines for Literature Repositories Profile v4', 'oai_openaire'],
['OpenAIRE Guidelines for Literature Repositories Profile v3', 'oai_dc'],
['OpenAIRE Guidelines for Data Archives Profile v2', 'oai_datacite'],
['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() {}
ngAfterViewInit() {
if (typeof document !== 'undefined') {
if (document.getElementById("main-menu")) {
this.offset = Number.parseInt(getComputedStyle(document.documentElement).getPropertyValue('--header-height'));
} else {
this.offset = 0;
}
}
}
public openWarningsModal(rule: RulePerJob) {
this.warningsModalOpen = true;
this.warningsModal.cancelButton = false;
this.warningsModal.okButton = false;
this.warningsModal.alertTitle = rule.ruleName;
this.warningsModal.open();
}
public openErrorsModal(rule: RulePerJob) {
this.errorsModalOpen = true;
this.errorsModal.cancelButton = false;
this.errorsModal.okButton = false;
this.errorsModal.alertTitle = rule.ruleName;
this.errorsModal.open();
}
public openXmlModal() {
this.xmlModalOpen = true;
this.xmlModal.cancelButton = false;
this.xmlModal.okButton = false;
this.xmlModal.alertTitle = "Xml metadata record";
this.xmlModal.open();
}
public getWarnings(rule: RulePerJob) {
this.warnings = [];
this.warningsChange.emit([]);
this.openWarningsModal(rule);
this.warningsModalOpen = true;
this.getWarningsClicked.emit(rule);
}
public getErrors(rule: RulePerJob) {
this.errors = [];
this.errorsChange.emit([]);
this.internal_error = "";
this.internal_errorChange.emit("");
this.openErrorsModal(rule);
this.errorsModalOpen = true;
this.getErrorsClicked.emit(rule);
}
public get hasDuration() {
if(this.jobDuration && (this.jobDuration.years || this.jobDuration.months || this.jobDuration.days || this.jobDuration.hours || this.jobDuration.minutes || this.jobDuration.seconds)) {
return true;
}
return false;
}
public getKeys(map) {
return Array.from(map.keys());
}
public getValues(map) {
return Array.from(map.values());
}
protected readonly Status = Status;
}

View File

@ -0,0 +1,25 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {RouterModule} from '@angular/router';
import {InputModule} from "../../../openaire-library/sharedComponents/input/input.module";
import {AnalysisComponent} from "./analysis.component";
import {BreadcrumbsModule} from "../../../openaire-library/utils/breadcrumbs/breadcrumbs.module";
import {AlertModalModule} from "../../../openaire-library/utils/modal/alertModal.module";
import {IconsModule} from "../../../openaire-library/utils/icons/icons.module";
@NgModule({
imports: [
CommonModule, FormsModule, RouterModule,
ReactiveFormsModule, InputModule, BreadcrumbsModule,
AlertModalModule, IconsModule,
],
declarations: [
AnalysisComponent
],
providers: [],
exports: [
AnalysisComponent
]
})
export class AnalysisModule {}

View File

@ -0,0 +1,195 @@
<div>
<div class="">
<div class="uk-grid uk-margin-remove-left" uk-grid>
<div id="center-content" class="uk-width-2-3 uk-width-3-4@l" [formGroup]="form">
<div class="uk-section uk-container uk-container-large">
<div class="uk-margin-large-bottom">
<ng-container *ngIf="!single">
<h6><span class="uk-text-secondary uk-margin-right">1.</span> Select a data source (repository or journal)</h6>
<div input class="uk-width-xlarge uk-margin-xsmall-left uk-margin-medium-top" inputClass="flat x-small" [formInput]="form.get('url')"
placeholder="Give the URL from one of your registered repositories" type="URL" (valueChange)="getSets()"></div>
</ng-container>
<ng-container *ngIf="single">
<h6><span class="uk-text-secondary uk-margin-right">1.</span> Metadata record</h6>
<div input class="uk-width-xxlarge uk-margin-xsmall-left uk-margin-medium-top" [formInput]="form.get('xml')"
placeholder="Paste your xml here" type="textarea" [rows]="15"></div>
</ng-container>
</div>
<div class="uk-margin-large-bottom">
<h6><span class="uk-text-secondary uk-margin-right">2.</span> Select guidelines</h6>
<div class="uk-margin-medium-top">
<div *ngFor="let option of options" class="uk-margin-xsmall-left uk-margin-small-bottom">
<input [id]="option.value" type="radio" [value]="option.value" name="guidelines" formControlName="guidelines">
<label [for]="option.value" class="uk-margin-xsmall-left">{{option.label}}</label>
</div>
</div>
</div>
<div *ngIf="!single" class="uk-margin-large-bottom">
<h6><span class="uk-text-secondary uk-margin-right">3.</span> Define the number of records to validate</h6>
<!-- <div class="uk-disabled uk-text-muted uk-margin-xsmall-left uk-margin-medium-top uk-margin-small-bottom">-->
<!-- <input id="all" type="radio" name="recordsNum" value="-1" formControlName="recordsNum">-->
<!-- <label for="all" class="uk-margin-xsmall-left">All</label>-->
<!-- </div>-->
<div class="uk-flex uk-flex-middle uk-margin-xsmall-left">
<!-- <input #customRecordsNum id="custom" type="radio" name="recordsNum" [value]="recordsNum" formControlName="recordsNum">-->
<!-- <label for="custom" class="uk-margin-xsmall-left uk-margin-right">-->
<!-- Custom-->
<!-- </label>-->
<span class="uk-flex-inline uk-flex-middle">
<icon class="clickable" name="remove_circle_outline" flex="true" ratio="0.9" type="outlined"
[ngClass]="recordsNum <= 10 ? 'uk-disabled uk-text-muted' : ''" (click)="updateRecordsNum(false)"></icon>
<span class="uk-margin-small-left uk-margin-small-right">{{recordsNum}}</span>
<icon class="clickable" name="add_circle_outline" flex="true" ratio="0.9" type="outlined"
[ngClass]="recordsNum >= 100 ? 'uk-disabled uk-text-muted' : ''" (click)="updateRecordsNum(true)"></icon>
</span>
</div>
<div [disabled]="!sets || sets.length <= 1" input type="select" placeholder="Select validation set" inputClass="flat x-small"
[options]="sets" [formInput]="form.get('set')" class="uk-width-large uk-margin-medium-top uk-margin-xsmall-left"></div>
</div>
<div class="uk-margin-medium-top uk-margin-xsmall-left">
<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();">
<span>Start Validation</span>
</button>
</div>
</div>
</div>
<div id="right-sidebar" *ngIf="help"
class="uk-width-1-3 uk-width-1-4@l uk-padding-remove uk-text-small uk-visible@m uk-background-primary">
<div class="uk-sticky" uk-sticky="end: true" [attr.offset]="offset">
<div #right_sidebar class="uk-height-1-1">
<div class="uk-height-1-1">
<div #right_sidebar_header class="uk-section uk-section-small">
<div class="uk-h5 uk-light uk-text-center">Help</div>
</div>
<div class="uk-card uk-card-default" [style]="'height: ' + right_sidebar_card_height + 'px;'">
<div class="uk-overflow-auto uk-padding-remove-horizontal" [style]="'height: ' + right_sidebar_body_height + 'px;'">
<div class="uk-card-body">
<ul uk-accordion>
<ng-container *ngIf="single">
<li class="uk-open">
<a class="uk-accordion-title uk-text-small uk-padding-small uk-padding-remove-vertical" href="#">
<span class="accordion-title-num">
<svg height="27" width="27">
<circle cx="13" cy="13" r="13" stroke-width="1"></circle>
<text x="50%" y="50%" text-anchor="middle" dy=".2em" font-size="12" font-weight="200">
1
</text>
</svg>
</span>
<span class="uk-margin-small-left">Select Guidelines</span>
</a>
<div class="uk-accordion-content uk-text-small uk-padding-small uk-padding-remove-vertical">
<p>You may apply two types of compatibility tests on your repository:</p>
<ul>
<li>Content: specific to the content/ format of the dc fields</li>
<li>Usage: specific to the implementation of OAI-PMH</li>
</ul>
<p>You may select from a predefined set of rules (OpenAIRE for Literature Repositories, OpenAIRE for Data Archives) or customize them according to your needs.</p>
</div>
</li>
<li>
<a class="uk-accordion-title uk-text-small uk-padding-small uk-padding-remove-vertical" href="#">
<span class="accordion-title-num">
<svg height="27" width="27">
<circle cx="13" cy="13" r="13" stroke-width="1"></circle>
<text x="50%" y="50%" text-anchor="middle" dy=".2em" font-size="12" font-weight="200">
2
</text>
</svg>
</span>
<span class="uk-margin-small-left">Paste xml metadata record</span>
</a>
<div class="uk-accordion-content uk-text-small uk-padding-small uk-padding-remove-vertical">
<p>Paste your xml metadata record, which will be validated according to the selected guidelines.</p>
</div>
</li>
</ng-container>
<ng-container *ngIf="!single">
<li class="uk-open">
<a class="uk-accordion-title uk-text-small uk-padding-small uk-padding-remove-vertical" href="#">
<span class="accordion-title-num">
<svg height="27" width="27">
<circle cx="13" cy="13" r="13" stroke-width="1"></circle>
<text x="50%" y="50%" text-anchor="middle" dy=".2em" font-size="12" font-weight="200">
1
</text>
</svg>
</span>
<span class="uk-margin-small-left">Select Data source</span>
</a>
<div class="uk-accordion-content uk-text-small uk-padding-small uk-padding-remove-vertical">
<p>
Select from the list, or enter the base OAI URL of the repository/ journal.
The repository's/ journal's published sets are exposed in a later stage and you may select one for the compatibility tests.
</p>
</div>
</li>
<li>
<a class="uk-accordion-title uk-text-small uk-padding-small uk-padding-remove-vertical" href="#">
<span class="accordion-title-num">
<svg height="27" width="27">
<circle cx="13" cy="13" r="13" stroke-width="1"></circle>
<text x="50%" y="50%" text-anchor="middle" dy=".2em" font-size="12" font-weight="200">
2
</text>
</svg>
</span>
<span class="uk-margin-small-left">Select Guidelines</span>
</a>
<div class="uk-accordion-content uk-text-small uk-padding-small uk-padding-remove-vertical">
<p>You may apply two types of compatibility tests on your repository:</p>
<ul>
<li>Content: specific to the content/ format of the dc fields</li>
<li>Usage: specific to the implementation of OAI-PMH</li>
</ul>
<p>You may select from a predefined set of rules (OpenAIRE for Literature Repositories, OpenAIRE for Data Archives) or customize them according to your needs.</p>
</div>
</li>
<li>
<a class="uk-accordion-title uk-text-small uk-padding-small uk-padding-remove-vertical" href="#">
<span class="accordion-title-num">
<svg height="27" width="27">
<circle cx="13" cy="13" r="13" stroke-width="1"></circle>
<text x="50%" y="50%" text-anchor="middle" dy=".2em" font-size="12" font-weight="200">
3
</text>
</svg>
</span>
<span class="uk-margin-small-left">Select Parameters</span>
</a>
<div class="uk-accordion-content uk-text-small uk-padding-small uk-padding-remove-vertical">
<p>
...
</p>
</div>
</li>
</ng-container>
</ul>
</div>
</div>
<a #right_sidebar_footer uk-sticky="end: true" class="uk-sticky uk-card-footer uk-button-link uk-text-center" (click)="help=false">Dismiss Help</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div *ngIf="!help" class="quick-contact uk-align-right uk-padding uk-padding-remove-vertical">
<button class="uk-button uk-box-shadow-default uk-box-shadow-default-hover uk-button-secondary uk-flex uk-flex-middle"
(click)="openHelp();">
<icon name="lightbulb" [flex]="true"></icon>
<span class="uk-margin-small-left">help</span>
</button>
</div>
</div>
</div>

View File

@ -0,0 +1,94 @@
import {ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
import {UntypedFormBuilder, UntypedFormGroup, Validators} from "@angular/forms";
import {Router} from "@angular/router";
import {Option} from "../../../openaire-library/sharedComponents/input/input.component";
import {StringUtils} from "../../../openaire-library/utils/string-utils.class";
@Component({
selector: 'app-settings',
templateUrl: './settings.component.html',
styleUrls: ['./settings.component.less']
})
export class SettingsComponent implements OnInit {
@Input() single: boolean = true;
@Input() sets: Option[] = [{label: 'All sets', value: 'all'}];
@Output() validationClicked = new EventEmitter();
@Output() getSetsClicked = new EventEmitter();
@ViewChild("right_sidebar") right_sidebar;
@ViewChild("right_sidebar_header") right_sidebar_header;
@ViewChild("right_sidebar_footer") right_sidebar_footer;
public right_sidebar_body_height: number = 0;
public right_sidebar_card_height: number = 0;
public offset: number = 0;
public help: boolean = false;
public form: UntypedFormGroup;
public recordsNum: number = 10;
@ViewChild('customRecordsNum') customRecordsNum;
public options: Option[] = [
{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 & OpenAIRE FAIR Guidelines for Literature Profile', value: 'OpenAIRE Guidelines for Literature Repositories Profile v4'},
{label: 'OpenAIRE FAIR Guidelines for Data Repositories Profile', value: 'OpenAIRE FAIR Guidelines for Data Repositories Profile'}
];
constructor(private fb: UntypedFormBuilder, private router: Router,
private cdr: ChangeDetectorRef) {
}
ngOnInit() {
if(this.single) {
this.form = this.fb.group({
guidelines: this.fb.control("", Validators.required),
xml: this.fb.control('', Validators.required)
});
} else {
this.form = this.fb.group({
url: this.fb.control("", [Validators.required, StringUtils.urlValidator()]),//[Validators.required/*, Validators.email*/]),
guidelines: this.fb.control("", Validators.required),
recordsNum: this.fb.control(this.recordsNum, Validators.required),
set: this.fb.control('all', Validators.required)
});
}
}
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() {}
openHelp() {
this.help = true;
this.cdr.detectChanges();
if (typeof document !== 'undefined') {
this.right_sidebar_card_height = this.right_sidebar.nativeElement.offsetHeight - this.right_sidebar_header.nativeElement.offsetHeight + 1;
this.right_sidebar_body_height = this.right_sidebar_card_height - this.right_sidebar_footer.nativeElement.offsetHeight;
}
}
updateRecordsNum(increase: boolean = true) {
this.recordsNum = this.recordsNum + (increase ? 10 : -10);
this.form.get('recordsNum').setValue(this.recordsNum);
if(this.customRecordsNum) {
this.customRecordsNum.nativeElement.checked = true;
}
}
getSets() {
this.getSetsClicked.emit(this.form);
}
validate() {
this.validationClicked.emit(this.form);
}
}

View File

@ -0,0 +1,24 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {RouterModule} from '@angular/router';
import {InputModule} from "../../../openaire-library/sharedComponents/input/input.module";
import {SettingsComponent} from "./settings.component";
import {BreadcrumbsModule} from "../../../openaire-library/utils/breadcrumbs/breadcrumbs.module";
import {IconsModule} from "../../../openaire-library/utils/icons/icons.module";
@NgModule({
imports: [
CommonModule, FormsModule, RouterModule,
ReactiveFormsModule, InputModule, BreadcrumbsModule,
IconsModule
],
declarations: [
SettingsComponent
],
providers: [],
exports: [
SettingsComponent
]
})
export class SettingsModule {}

View File

@ -0,0 +1,10 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import {PreviousRouteRecorder} from "../../openaire-library/utils/piwik/previousRouteRecorder.guard";
import { SingleRecordValidatorComponent } from "./single-record-validator.component";
@NgModule({
imports: [
RouterModule.forChild([{ path: '', component: SingleRecordValidatorComponent, canDeactivate: [PreviousRouteRecorder] }])
]
})
export class SingleRecordValidatorRoutingModule { }

View File

@ -1,154 +1,6 @@
<ng-template #rules_analysis let-rules=rules>
<table class="uk-table uk-table-striped">
<thead>
<tr class="uk-child-width-1-3">
<th>Rule Name</th>
<th class="uk-text-center">Score</th>
<th class="uk-text-center">Status</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let ruleResult of rules">
<td>
<div>{{ruleResult.name ? ruleResult.name : '-'}}</div>
<div *ngIf="ruleResult.description" class="uk-text-small uk-text-meta">{{ruleResult.description}}</div>
</td>
<td class="uk-text-center">{{(ruleResult.score != undefined && ruleResult.score != null) ? ruleResult.score : '-'}}</td>
<td class="uk-text-center">
<ng-container *ngIf="!ruleResult.status">-</ng-container>
<ng-container *ngIf="ruleResult.status">
<div *ngIf="(!ruleResult.errors || ruleResult.errors.length == 0) &&
(!ruleResult.warnings || ruleResult.warnings.length == 0) &&
(!ruleResult.internalError)" class="uk-text-success">{{ruleResult.status}}</div>
<a *ngIf="ruleResult.warnings?.length > 0 || ruleResult.errors?.length > 0 || ruleResult.internalError" (click)="openMessagesModal(ruleResult)">
<div *ngIf="ruleResult.warnings?.length > 0" class="uk-text-warning">warnings</div>
<div *ngIf="ruleResult.errors?.length > 0" class="uk-text-danger">errors</div>
<div *ngIf="ruleResult.internalError" class="uk-text-danger">error</div>
</a>
</ng-container>
</td>
</tr>
</tbody>
</table>
</ng-template>
<app-settings *ngIf="!viewResults" (validationClicked)="validate($event)"></app-settings>
<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>
<!-- <ngx-dropzone #drop class="" (change)="fileChangeEvent($event, true)"-->
<!-- [multiple]="false" [accept]="xml">-->
<!-- <ngx-dropzone-preview *ngIf="form.value.value && form.value.value.name" class="file-preview"-->
<!-- [removable]="true" (removed)="onRemove()">-->
<!-- <ngx-dropzone-label class="file-label">{{ form.value.value.name }}</ngx-dropzone-label>-->
<!-- </ngx-dropzone-preview>-->
<!-- </ngx-dropzone>-->
<!-- <button *ngIf="!form.value.value || filesToUpload" mat-button (click)="drop.showFileSelector()" type="button" class="attach-file-btn"-->
<!-- [disabled]="!!form.value.value">-->
<!-- <mat-icon class="mr-2">upload</mat-icon>-->
<!-- <mat-label>{{ (form.get('data').value.label | translate)}}</mat-label>-->
<!-- </button>-->
<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>
<!-- <h6>Validation Result Analysis</h6>-->
</div>
<modal-alert #modal large="true" (alertOutput)="modalOpen=false">
<div *ngIf="modalOpen" 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 *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>
<!-- <div class="uk-margin-top uk-section-muted uk-padding-small">-->
<!-- Comments-->
<!-- <ul class="uk-list uk-list-bullet">-->
<!-- <li>returns success, not warning status</li>-->
<!-- <li>what is the score?</li>-->
<!-- <li>do we have rule weight?</li>-->
<!-- <li>no description or user friendly name provided</li>-->
<!-- <li>errors/ warning not user friendly</li>-->
<!-- <li>can return errors & warnings</li>-->
<!-- <li>on errors, only the 1st one is returned (this is what comments in code say)</li>-->
<!-- </ul>-->
<!-- </div>-->
</div>
</div>
</div>
</div>
</div>
<app-analysis *ngIf="viewResults" [breadcrumbs]="breadcrumbs" [single]="true" [jobResult]="jobResult"
[jobDuration]="jobDuration" [validationResult]="validationResult"
[(warnings)]="warnings" [(errors)]="errors" [(internal_error)]="internal_error"
(getWarningsClicked)="getWarnings($event)" (getErrorsClicked)="getErrors($event)"></app-analysis>

View File

@ -1,42 +1,62 @@
import {Component, OnInit, ViewChild} from '@angular/core';
import {UntypedFormBuilder, UntypedFormGroup, Validators} from "@angular/forms";
import {ChangeDetectorRef, Component, OnInit} from '@angular/core';
import {UntypedFormBuilder, UntypedFormGroup} from "@angular/forms";
import {SingleRecordValidatorService} from "../../services/single-record-validator.service";
import {RuleInfo} from "../entities/RuleInfo";
import {XmlValidationResponse} from "../entities/XmlValidationResponse";
import {Option} from "../../openaire-library/sharedComponents/input/input.component";
import {RulePerJob, Status} from "../entities/RulePerJob";
import {JobResult, Progress} from "../entities/JobResult";
import {Breadcrumb} from "../../openaire-library/utils/breadcrumbs/breadcrumbs.component";
import {Issue} from "../entities/Issue";
import {Subscriber} from "rxjs";
import {NavigationEnd, Router} from "@angular/router";
import {Duration} from "../shared/analysis/analysis.component";
@Component({
selector: 'app-single-record-validator',
templateUrl: './single-record-validator.component.html',
styleUrls: ['./single-record-validator.component.less']
templateUrl: './single-record-validator.component.html'
})
export class SingleRecordValidatorComponent implements OnInit {
public options: Option[] = [
{label: 'Data Archive Guidelines V2 Profile & FAIR Data Guidelines Profile', value: 'dataArchiveGuidelinesV2Profile'},
{label: 'Literature Guidelines V3 Profile', value: 'literatureGuidelinesV3Profile'},
{label: 'Literature Guidelines V4 Profile', value: 'literatureGuidelinesV4Profile'},
{label: 'FAIR Data Guidelines Profile', value: 'fairDataGuidelinesProfile'}
{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 & OpenAIRE FAIR Guidelines for Literature Profile', value: 'OpenAIRE Guidelines for Literature Repositories Profile v4'},
{label: 'OpenAIRE FAIR Guidelines for Data Repositories Profile', value: 'OpenAIRE FAIR Guidelines for Data Repositories Profile'}
];
public breadcrumbs: Breadcrumb[] = [{name: 'Single Record Validation', route: '/single-record-validate'}, {name: 'Result for ...'}];
public form: UntypedFormGroup;
public result: XmlValidationResponse
public modalOpen: boolean = false;
public currentRule: RuleInfo;
@ViewChild('modal') modal;
public result: RulePerJob[];
public jobResult: JobResult = null;
public jobDuration: Duration = null;
public validationResult: Map<string, { "analysisResult": RulePerJob[], "successfulAnalysisResult": RulePerJob[], "warningAnalysisResult": RulePerJob[], "failedAnalysisResult": RulePerJob[] }>;
public viewResults: boolean = false;
public validationAnalysis: boolean = true;
subscriptions = [];
constructor(private fb: UntypedFormBuilder, private validator: SingleRecordValidatorService) {
this.form = this.fb.group({
guidelines: this.fb.control("", Validators.required),
xml: this.fb.control('', Validators.required)
});
}
public warningsPerRule: Map<string, Issue[]>;
public errorsPerRule: Map<string, Issue[]>;
public warnings: Issue[];
public errors: Issue[];
public internal_error: string;
ngOnInit(): void {}
constructor(private _router: Router, private fb: UntypedFormBuilder, private cdr: ChangeDetectorRef,
private validator: SingleRecordValidatorService) {}
ngOnInit(): void {
this.subscriptions.push(this._router.events.subscribe((event) => {
if (event instanceof NavigationEnd) {
// do some logic again when same url is clicked
this.viewResults = false;
this.result = [];
this.jobResult = null;
this.jobDuration = null;
this.validationResult = null;
}
}));
}
ngOnDestroy() {
this.subscriptions.forEach(subscription => {
@ -46,27 +66,106 @@ export class SingleRecordValidatorComponent implements OnInit {
});
}
public validate() {
this.subscriptions.push(this.validator.validateRecord(this.form.get('xml')?.getRawValue(), this.form.get('guidelines')?.getRawValue()).subscribe(
public validate(form) {
this.viewResults = true;
this.jobResult = new JobResult();
this.jobResult.startDate = new Date();
this.jobResult.progress = Progress.IN_PROGRESS;
this.jobResult.guidelines = form.get("guidelines").getRawValue();
this.jobResult.xml = form.get("xml").getRawValue();
this.subscriptions.push(this.validator.validateRecord(form.get('xml')?.getRawValue(), form.get('guidelines')?.getRawValue()).subscribe(
result => {
this.result = result;
this.jobResult.endDate = new Date();
this.jobResult.numberOfRecords = 1;
this.jobResult.recordsTested = 1;
this.jobResult.progress = Progress.COMPLETED;
this.jobDuration = new Duration();
const msBetweenDates = this.jobResult.endDate.getTime() - this.jobResult.startDate.getTime();
this.jobDuration.seconds = Math.ceil(msBetweenDates / 1000 % 60);
this.jobDuration.minutes = Math.floor(msBetweenDates / 1000 / 60 % 60);
this.jobDuration.hours = Math.floor(msBetweenDates / 1000 / 60 / 60 % 24);
this.jobDuration.days = Math.floor(msBetweenDates / 1000 / 60 / 60 / 24);
this.jobDuration.months = Math.floor(this.jobDuration.days / 31);
this.jobDuration.years = Math.floor(this.jobDuration.months / 12);
this.result = result.summaryResults;
let validationResult: Map<string, { "analysisResult": RulePerJob[], "successfulAnalysisResult": RulePerJob[], "warningAnalysisResult": RulePerJob[], "failedAnalysisResult": RulePerJob[] }> =
new Map<string, {"analysisResult": RulePerJob[], "successfulAnalysisResult": RulePerJob[], "warningAnalysisResult": RulePerJob[], "failedAnalysisResult": RulePerJob[] }>;
for(let rulePerJob of result.summaryResults) {
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(rulePerJob.issues) {
for(let issue of rulePerJob.issues) {
if(issue.issueType == "WARNING") {
if(this.warningsPerRule == null) {
this.warningsPerRule = new Map<string, Issue[]>();
}
if(!this.warningsPerRule.has(issue.ruleName)) {
this.warningsPerRule.set(issue.ruleName, new Array<Issue>());
}
this.warningsPerRule.get(issue.ruleName).push({description: issue.issueText, records: null});
} else if(issue.issueType == "ERROR") {
if(this.errorsPerRule == null) {
this.errorsPerRule = new Map<string, Issue[]>();
}
if(!this.errorsPerRule.has(issue.ruleName)) {
this.errorsPerRule.set(issue.ruleName, new Array<Issue>());
}
this.errorsPerRule.get(issue.ruleName).push({description: issue.issueText, records: null});
}
}
}
if(!validationResult.has(rulePerJob.guidelines)) {
validationResult.set(rulePerJob.guidelines, {analysisResult: [], successfulAnalysisResult: [], warningAnalysisResult: [], failedAnalysisResult: []});
}
validationResult.get(rulePerJob.guidelines).analysisResult.push(rulePerJob);
if(rulePerJob.rule_status == Status.FAILURE || rulePerJob.rule_status == Status.ERROR) {
validationResult.get(rulePerJob.guidelines).failedAnalysisResult.push(rulePerJob);
} else {
if(rulePerJob.has_warnings || rulePerJob.has_errors || rulePerJob.internal_error) {
validationResult.get(rulePerJob.guidelines).warningAnalysisResult.push(rulePerJob);
} else {
validationResult.get(rulePerJob.guidelines).successfulAnalysisResult.push(rulePerJob);
}
}
}
this.validationResult = validationResult;
// this.cdr.detectChanges();
}
));
}
public openMessagesModal(rule: RuleInfo) {
this.modalOpen = true;
this.currentRule = rule;
this.modal.cancelButton = false;
this.modal.okButton = false;
this.modal.alertTitle = "Warnings & Errors";
this.modal.open();
}
public get currentGuideline() {
return this.options.find(option => this.form.get("guidelines").getRawValue() == option.value);
}
// fileChangeEvent(fileInput: any, dropped: boolean = false) {
// if(this.form.value.value) {
// this.onRemove(false);
@ -127,4 +226,23 @@ export class SingleRecordValidatorComponent implements OnInit {
// this.form.get("value").patchValue(null);
// }
getWarnings(rule: RulePerJob) {
this.warnings = [];
if(this.warningsPerRule == null) {
this.warnings = [];
} else {
this.warnings = [...this.warningsPerRule.get(rule.ruleName)];
}
}
getErrors(rule: RulePerJob) {
this.internal_error = rule.internal_error;
this.errors = [];
if(this.errorsPerRule == null) {
this.errors = [];
} else {
this.errors = [...this.errorsPerRule.get(rule.ruleName)];
}
}
}

View File

@ -0,0 +1,25 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {RouterModule} from '@angular/router';
import {SingleRecordValidatorRoutingModule} from "./single-record-validator-routing.module";
import {SingleRecordValidatorComponent} from "./single-record-validator.component";
import {SingleRecordValidatorService} from "../../services/single-record-validator.service";
import {AnalysisModule} from "../shared/analysis/analysis.module";
import {SettingsModule} from "../shared/settings/settings.module";
@NgModule({
imports: [
CommonModule, RouterModule,
SingleRecordValidatorRoutingModule, AnalysisModule, SettingsModule
],
declarations: [
SingleRecordValidatorComponent
],
providers: [
SingleRecordValidatorService
],
exports: [
SingleRecordValidatorComponent
]
})
export class SingleRecordValidatorModule {}

View File

@ -6,6 +6,7 @@ import {map} from "rxjs";
@Injectable({
providedIn: "root"
})
export class OaipmhValidatorService {
constructor(private http: HttpClient) {}

View File

@ -11,7 +11,7 @@ export class SingleRecordValidatorService {
constructor(private http: HttpClient) {}
validateRecord(xml: string, guidelinesName: string): Observable<any> {
let url = environment.validatorAPI + "validate?guidelines="+guidelinesName;
let url = environment.validatorAPI + "validateRecord?guidelines="+guidelinesName;
let headers = new HttpHeaders({'Content-Type': 'application/json', 'accept': 'application/json'});
return this.http.post<any>(url, xml, {headers: headers});
}

View File

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