Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions adapters/inesdata/sources/Ontology-Hub
Submodule Ontology-Hub added at 1969d3

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"ngx-json-viewer": "~3.2.1",
"ngx-schema-form": "^2.11.0",
"rxjs": "~7.8.1",
"rdflib": "2.2.6",
"z-schema": "^6.0.1",
"zone.js": "~0.14.2"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,69 @@
<input [(ngModel)]="inesDataStoreAddress.folder" matInput>
</mat-form-field>
<app-uploader-file (filesChange)="setFiles($event)"></app-uploader-file>

<fieldset>
<legend>Ontology Information</legend>

<mat-form-field appearance="fill" style="width:100%">
<mat-label>URI Ontology</mat-label>

<mat-select [(ngModel)]="ontologyUri" (selectionChange)="fillVersions($event.value)">
<div style="padding: 8px 16px; position: sticky; top: 0; background: white; z-index: 1;">
<input matInput placeholder="Buscar..."
(keyup)="onSearchOntology($event)"
(keydown)="$event.stopPropagation()">
</div>
<mat-option *ngFor="let ontology of filteredOntologies" [value]="ontology.uri">
{{ontology.titles[0]?.value}}
</mat-option>
</mat-select>
</mat-form-field>

<div *ngIf="ontologyUri">

<mat-form-field appearance="fill" style="width:100%">
<mat-label>Versions</mat-label>

<mat-select [(ngModel)]="ontologyVersion" required>
<mat-option *ngFor="let version of ontologyVersions" [value]="version.issued">
{{version.name}}
</mat-option>
</mat-select>
</mat-form-field>

<mat-form-field appearance="fill" style="width:100%">
<mat-label>Shacles</mat-label>

<mat-select [(ngModel)]="ontologyShacle" required (selectionChange)="fileRequiresValidation()">
<mat-option *ngFor="let shacl of ontologyShacles" [value]="shacl">
{{shacl}}
</mat-option>
<mat-option value="newShacleFile">
Upload a new shacle file
</mat-option>
</mat-select>
</mat-form-field>

<div *ngIf="ontologyShacle === 'newShacleFile'">
<mat-label>Shacl File*</mat-label>
<app-uploader-file #shaclsFile (filesChange)="setShaclsFiles($event, shaclsFile)" accept="text/plain"></app-uploader-file>
<div *ngIf="showUploadFileButton">
<button mat-raised-button color="accent" (click)="uploadShacleFile()">
<mat-icon>upload</mat-icon>
Upload file
</button>
</div>
</div>
</div>
<div *ngIf="showTestFileButton">
<button mat-raised-button color="accent" (click)="runOntologyTest()">
<mat-icon>check_circle</mat-icon>
Validate Ontology
</button>
</div>

</fieldset>
</div>
</div>
</mat-tab>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Component, Inject, OnInit, QueryList, ViewChildren } from '@angular/cor
import { HttpDataAddress, DataAddress } from '@think-it-labs/edc-connector-client';
import { JsonDoc } from "../../../shared/models/json-doc";
import { StorageType } from "../../../shared/models/storage-type";
import { MatDialog } from '@angular/material/dialog';
import { AmazonS3DataAddress } from "../../../shared/models/amazon-s3-data-address";
import { Vocabulary } from "../../../shared/models/vocabulary";
import { NotificationService } from 'src/app/shared/services/notification.service';
Expand All @@ -20,6 +21,10 @@ import { BehaviorSubject } from 'rxjs';
import { Router } from '@angular/router';
import { MatSlideToggleChange } from '@angular/material/slide-toggle';
import { NgModel } from '@angular/forms';
import { SemanticFileValidatorService } from 'src/app/shared/services/semantic-file-validator.service';
import { Ontology, OntologyVersion } from 'src/app/shared/models/ontology';
import { OntologyService } from 'src/app/shared/services/ontology.service';
import { ConfirmationDialogComponent, ConfirmDialogModel } from 'src/app/shared/components/confirmation-dialog/confirmation-dialog.component';


@Component({
Expand All @@ -39,6 +44,11 @@ export class AssetCreateComponent implements OnInit {
]
};

//manage the validation button for semantic files
showTestFileButton = false;
showUploadFileButton = false;


// Dynamic forms from vocabylary variables
vocabularies: Vocabulary[];
selectedVocabularies: Vocabulary[]
Expand Down Expand Up @@ -75,6 +85,15 @@ export class AssetCreateComponent implements OnInit {
keywords: string = '';
format: string = '';
byteSize: string = '';
ontologyUri: string = '';
ontologyVersion: string = '';
ontologyShacle: any = '';

ontologies: Ontology[] = [];
filteredOntologies: Ontology[] = [];
ontologyVersions: OntologyVersion[] = [];
ontologyShacles: any[] = [];


// Storage information
amazonS3DataAddress: AmazonS3DataAddress = {
Expand All @@ -90,6 +109,10 @@ export class AssetCreateComponent implements OnInit {
type: 'InesDataStore'
};

shaclsInesDataStoreAddress: InesDataStoreAddress = {
type: 'InesDataStore'
};

assetType: any;
assetTypes = Object.entries(ASSET_TYPES);
defaultForms: JsonFormData[];
Expand All @@ -111,6 +134,14 @@ export class AssetCreateComponent implements OnInit {
this.selectedForms = []
this.selectedAssetTypeVocabularies = []

this.ontologyService.getOntologyLists().subscribe({
next: (res: Ontology[]) => {
this.ontologies = res;
this.filteredOntologies = res;
},
});


this.vocabularyService.requestVocabularies().subscribe({
next: (res: Vocabulary[]) => {
this.vocabularies = res;
Expand Down Expand Up @@ -151,7 +182,10 @@ export class AssetCreateComponent implements OnInit {
private notificationService: NotificationService,
@Inject('STORAGE_TYPES') public storageTypes: StorageType[],
private router: Router,
private loadingService: LoadingService) {
private loadingService: LoadingService,
private ontologyService: OntologyService,
private semanticFileValidator: SemanticFileValidatorService,
private dialog: MatDialog) {
}

async onSave() {
Expand Down Expand Up @@ -245,7 +279,9 @@ export class AssetCreateComponent implements OnInit {
properties["dcterms:description"] = this.description;
properties["dcat:byteSize"] = this.byteSize;
properties["dcterms:format"] = this.format;

properties["ontologyUri"] = this.ontologyUri;
properties["ontologyVersion"] = this.ontologyVersion;
properties["ontologyShacle"] = this.ontologyShacle;
this.addKeywords(properties);
}

Expand Down Expand Up @@ -290,6 +326,10 @@ export class AssetCreateComponent implements OnInit {
if (!this.id || !this.storageTypeId || !this.name || !this.version || !this.description || !this.keywords || !this.shortDescription || !this.assetType) {
return false;
} else {
if (this.ontologyUri && (!this.ontologyVersion || !this.ontologyShacle || (this.ontologyShacle === 'newShacleFile' && !this.shaclsInesDataStoreAddress.file))) {
return false;
}

if (this.storageTypeId === DATA_ADDRESS_TYPES.httpData && (!this.httpDataAddress.name || !this.httpDataAddress.baseUrl || !this.validateUrl())) {
return false;
}
Expand Down Expand Up @@ -349,12 +389,23 @@ export class AssetCreateComponent implements OnInit {

setFiles(event: File[]) {
if (event?.length > 0) {
this.inesDataStoreAddress.file = event[0]
this.inesDataStoreAddress.file = event[0];
this.fileRequiresValidation();
} else {
delete this.inesDataStoreAddress.file
}
}

async setShaclsFiles(event: File[], shaclFile:any) {
this.showUploadFileButton = false;
if (event?.length > 0) {
this.showUploadFileButton = true;
this.shaclsInesDataStoreAddress.file = event[0];
} else {
delete this.shaclsInesDataStoreAddress.file;
}
}

onSelectionChangeVocabulary() {
this.selectedForms = []

Expand Down Expand Up @@ -448,6 +499,152 @@ export class AssetCreateComponent implements OnInit {
}

onToggleChange(propertyName: string, event: MatSlideToggleChange): void {
this.httpDataAddress[propertyName] = event ? 'true' : 'false';
this.httpDataAddress[propertyName] = event.checked;
}


async fileRequiresValidation(){
let isSemanticFile = false;

if(!this.inesDataStoreAddress.file || this.ontologyShacle === 'newShacleFile' || this.ontologyShacle === ''){
return
}

await this.semanticFileValidator.isASemanticFile(this.inesDataStoreAddress.file).then(res => {
isSemanticFile = res
});

if(isSemanticFile && this.ontologyUri && this.ontologyVersion && this.ontologyShacle){
this.showTestFileButton = true;
}
}

onSearchOntology(event: any) {
const value = (event.target as HTMLInputElement).value.toLowerCase();
this.filteredOntologies = this.ontologies.filter(ontology =>
ontology.titles[0].value.toLowerCase().includes(value) ||
ontology.uri.toLowerCase().includes(value)
);
}

runOntologyTest() {
const selectedOntology = this.ontologies.find(o => o.uri === this.ontologyUri);
const prefix = selectedOntology?.prefix || '';

this.loadingService.showLoading('Validating semantic file...');
const ontologyUrl = this.ontologyService.buildUrl(prefix, 'ontology', this.ontologyVersion.split('T')[0]);
const shaclUrl = this.ontologyService.buildUrl(prefix, 'shacl', this.ontologyShacle);
const rdfFormat = this.resolveRdfFormat(this.inesDataStoreAddress.file);

this.assetService.testRdfAsset(ontologyUrl, shaclUrl, this.inesDataStoreAddress.file, rdfFormat).subscribe({
next: (res) => {
this.notificationService.showInfo('Validation completed successfully');
this.loadingService.hideLoading();
},
error: (err) => {
this.loadingService.hideLoading();

// Extraemos los mensajes del array de error si existe, si no el mensaje general
const detail = Array.isArray(err.error)
? err.error.map((e: any) => typeof e === 'string' ? e : e.message).join('\n')
: err.error?.message || err.message || "Unknown validation error";

const dialogData = new ConfirmDialogModel("Validation Failed", detail);
dialogData.confirmText = "Close";
dialogData.confirmColor = "warn";
dialogData.showCancel = false; // Esto oculta el botón "Cancel"

this.dialog.open(ConfirmationDialogComponent, { data: dialogData, width: '600px' });
}
});
}

private resolveRdfFormat(file: File): string {
const mimeType = (file?.type || '').toLowerCase();
switch (mimeType) {
case 'text/turtle':
return 'turtle';
case 'application/rdf+xml':
return 'rdfxml';
case 'application/ld+json':
return 'jsonld';
case 'application/n-triples':
return 'ntriples';
case 'text/n3':
return 'n3';
default:
break;
}

const fileName = (file?.name || '').toLowerCase();
if (fileName.endsWith('.n3')) return 'n3';
if (fileName.endsWith('.ttl')) return 'turtle';
if (fileName.endsWith('.rdf') || fileName.endsWith('.xml') || fileName.endsWith('.owl')) return 'rdfxml';
if (fileName.endsWith('.jsonld')) return 'jsonld';
if (fileName.endsWith('.nt')) return 'ntriples';

return 'turtle';
}

fillVersions(uri: string) {
const selectedOntology = this.ontologies.find(o => o.uri === uri);
this.ontologyVersions = selectedOntology?.versions || [];
this.ontologyShacles = selectedOntology?.artifacts?.shapes || [];

// Selección automática si solo hay una versión
if (this.ontologyVersions.length === 1) {
this.ontologyVersion = this.ontologyVersions[0].issued;
} else {
this.ontologyVersion = '';
}

// Selección automática si solo hay un Shacle
if (this.ontologyShacles.length === 1) {
this.ontologyShacle = this.ontologyShacles[0];
} else {
this.ontologyShacle = '';
}
}


uploadShacleFile() {
if (this.ontologyShacle === 'newShacleFile' && this.shaclsInesDataStoreAddress.file && this.ontologyUri) {
const selectedOntology = this.ontologies.find(o => o.uri === this.ontologyUri);
if (selectedOntology) {
this.loadingService.showLoading('Uploading Shacl file...');
this.ontologyService.postUploadShacl(
this.shaclsInesDataStoreAddress.file,
selectedOntology.prefix,
selectedOntology.uri
).subscribe({
next: () => {
this.ontologyService.getOntologyLists().subscribe({
next: (res: Ontology[]) => {
this.ontologies = res;
this.filteredOntologies = res;

const updatedOntology = this.ontologies.find(o => o.uri === this.ontologyUri);
this.ontologyShacles = updatedOntology?.artifacts?.shapes || [];
this.ontologyShacle = '';

this.loadingService.hideLoading();
this.notificationService.showInfo('Shacl file uploaded successfully');
this.showUploadFileButton = false;
delete this.shaclsInesDataStoreAddress.file;
},
error: () => {
this.loadingService.hideLoading();
this.notificationService.showError('Error refreshing ontology list');
}
});
},
error: (err) => {
this.loadingService.hideLoading();
this.notificationService.showError('Error uploading Shacl file');
}
});
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<a
mat-stroked-button
color="accent"
[href]="baseUrl"
[href]="baseUrl + '/edition'"
target="_blank"
rel="noopener noreferrer"
aria-label="Create ontology"
Expand Down Expand Up @@ -35,7 +35,7 @@
<mat-card-actions class="card-actions align-content-end text-align-end">
<mat-card-actions class="card-actions align-content-end text-align-end">
<a
[href]="baseUrl + '/vocabs/' + ontology.prefix"
[href]="baseUrl + '/dataset/vocabs/' + ontology.prefix"
target="_blank"
rel="noopener noreferrer"
color="info"
Expand Down
Loading