Commit 05d4e92e authored by Andrei Popescu's avatar Andrei Popescu
Browse files

fixes to better handle anyOf and nested enumeration (items.enum)

parent 79dbd5e5
Pipeline #56203 passed with stage
in 31 seconds
......@@ -4,4 +4,5 @@ node_modules
steps.txt
shacl.ttl
turtleTranslation.ttl
.json-schema-spec.txt
\ No newline at end of file
.json-schema-spec.txt
.paths.txt
\ No newline at end of file
{
"$schema": "http://json-schema.org/schema#",
"modelTags": "",
"$id": "https://smart-data-models.github.io/dataModel.Battery/Battery/schema.json",
"$schemaVersion": "0.0.2",
"title": "Smart Data Models - Battery",
"description": "Represent a physical battery with its hardware specifications",
"type": "object",
"allOf": [
{
"$ref": "https://smart-data-models.github.io/data-models/common-schema.json#/definitions/GSMA-Commons"
},
{
"$ref": "https://smart-data-models.github.io/data-models/common-schema.json#/definitions/Location-Commons"
},
{
"properties": {
"type": {
"type": "string",
"enum": [
"Battery"
],
"description": "Property. NGSI Entity type. It has to be Battery"
},
"refDevice": {
"anyOf": [
{
"type": "string",
"minLength": 1,
"maxLength": 256,
"pattern": "^[\\w\\-\\.\\{\\}\\$\\+\\*\\[\\]`|~^@!,:\\\\]+$",
"description": "Property. Identifier format of any NGSI entity"
},
{
"type": "string",
"format": "uri",
"description": "Property. Identifier format of any NGSI entity"
}
],
"description": "Relationship. Model:'http://schema.org/URL'. Device providing the measured data about the battery"
},
"status": {
"type": "array",
"description": "Property. Model:'http://schema.org/Text'. Current operational status of the item",
"minItems": 1,
"uniqueItems": true,
"items": {
"type": "string",
"enum": [
"outOfService",
"withIncidence",
"working"
]
}
},
"cycleLife": {
"type": "integer",
"description": "Property. Model:'http://schema.org/Number'. Numeric value of the load/unload operation cycles for the item'"
},
"autonomyTime": {
"type": "string",
"description": "Property. Model:'http://schema.org/Number'. Autonomy of operations of the item without further charge.",
"pattern": "^(-?)P(?=\\d|T\\d)(?:(\\d+)Y)?(?:(\\d+)M)?(?:(\\d+)([DW]))?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+(?:\\.\\d+)?)S)?)?$"
},
"rechargeTime": {
"type": "string",
"description": "Property. Model:'http://schema.org/Number'. Time for the full charge of the battery.",
"pattern": "^(-?)P(?=\\d|T\\d)(?:(\\d+)Y)?(?:(\\d+)M)?(?:(\\d+)([DW]))?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+(?:\\.\\d+)?)S)?)?$"
},
"acPowerInput": {
"type": "number",
"description": "Property. Model:'http://schema.org/Number'. Numeric value in volts for the alternate current charge. Units:'volts'"
},
"acPowerOutput": {
"type": "number",
"description": "Property. Model:'http://schema.org/Number'. Numeric value in volts for the alternate output. Units:'volts'"
},
"dcPowerInput": {
"type": "number",
"description": "Property. Model:'http://schema.org/Number'. Numeric value in volts for the continuous current charge. Units:'volts'"
},
"dcPowerOutput": {
"type": "number",
"description": "Property. Model:'http://schema.org/Number'. Numeric value in volts for the continuous current charge. Units:'volts'"
}
}
}
],
"required": [
"id",
"type"
]
}
\ No newline at end of file
......@@ -23,11 +23,13 @@ gbfs:user_types rdf:type rdf:Property;
rdfs:range gbfs:User_types.
gbfs:User_types rdf:type rdfs:Class.
gbfs:user_types rdfs:label "Array of member and nonmember value(s) indicating that this set of rental hours applies to either members or non-members only."@en.
gbfs:User_types owl:oneOf (<member> <nonmember>).
gbfs:undefined owl:oneOf (<member> <nonmember>), (<member> <nonmember>).
gbfs:days rdf:type rdf:Property;
rdfs:range gbfs:Days.
gbfs:Days rdf:type rdfs:Class.
gbfs:days rdfs:label "An array of abbreviations (first 3 letters) of English names of the days of the week for which this object applies."@en.
gbfs:Days owl:oneOf (<sun> <mon> <tue> <wed> <thu> <fri> <sat>).
gbfs:undefined owl:oneOf (<sun> <mon> <tue> <wed> <thu> <fri> <sat>), (<sun> <mon> <tue> <wed> <thu> <fri> <sat>).
gbfs:start_time rdfs:range xsd:string;
rdfs:label "Start time for the hours of operation of the system."@en.
......
......@@ -25,6 +25,7 @@ gbfs:rental_methods rdf:type rdf:Property;
rdfs:range gbfs:Rental_methods.
gbfs:Rental_methods rdf:type rdfs:Class.
gbfs:rental_methods rdfs:label "Payment methods accepted at this station."@en.
gbfs:Rental_methods owl:oneOf (<key> schema:CreditCard <paypass> <applepay> <androidpay> <transitcard> <accountnumber> foaf:phone).
gbfs:undefined owl:oneOf (<key> schema:CreditCard <paypass> <applepay> <androidpay> <transitcard> <accountnumber> foaf:phone).
gbfs:enum rdf:type rdf:Property;
rdfs:range gbfs:Enum.
......
......@@ -33,6 +33,7 @@ gbfs:return_type rdf:type rdf:Property;
rdfs:range gbfs:Return_type.
gbfs:Return_type rdf:type rdfs:Class.
gbfs:return_type rdfs:label "The conditions for returning the vehicle at the end of the trip added in v2.3-RC."@en.
gbfs:Return_type owl:oneOf (<free_floating> <roundtrip_station> <any_station>).
gbfs:undefined owl:oneOf (<free_floating> <roundtrip_station> <any_station>), (<free_floating> <roundtrip_station> <any_station>).
gbfs:vehicle_assets rdf:type rdf:Property;
rdfs:range gbfs:Vehicle_assets.
......
{
"prefix": "sdm",
"jsonObjects": {
"sdm:Battery": "allOf",
"sdm:Type": "type",
"sdm:RefDevice": "refDevice",
"sdm:Status": "status",
"sdm:CycleLife": "cycleLife",
"sdm:AutonomyTime": "autonomyTime",
"sdm:RechargeTime": "rechargeTime",
"sdm:AcPowerInput": "acPowerInput",
"sdm:AcPowerOutput": "acPowerOutput",
"sdm:DcPowerInput": "dcPowerInput",
"sdm:DcPowerOutput": "dcPowerOutput"
},
"sources": {
"./SmartDataModels/battery.json": "sdm:Battery"
},
"shaclTargets": {
"sdm:Battery": ""
},
"shaclRoot": "",
"prefixes": {
"sdm": "https://smart-data-models.github.io/dataModel.Energy/ThreePhaseAcMeasurement/terms",
"schema": "http://schema.org/#",
"ebucore": "http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
"foaf": "http://xmlns.com/foaf/0.1/",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"dcterms": "http://purl.org/dc/terms/",
"vs": "http://www.w3.org/2003/06/sw-vocab-status/ns#",
"geo": "http://www.w3.org/2003/01/geo/wgs84_pos#",
"vann": "http://purl.org/vocab/vann/",
"owl": "http://www.w3.org/2002/07/owl#",
"jsonsc": "https://www.w3.org/2019/wot/json-schema#",
"airs": "https://raw.githubusercontent.com/airs-linked-data/lov/latest/src/airs_vocabulary.ttl#",
"vso": "http://purl.org/vso/ns#",
"dbpedia-owl": "http://dbpedia.org/ontology/"
},
"terms": {
"description":"dcterms:description",
"type": "rdf:type",
"last_updated": "dcterms:modified" ,
"url": "schema:url",
"summary": "ebucore:summary",
"name": "foaf:name",
"short_name": "rdfs:label",
"lat": "geo:lat",
"lon": "geo:long",
"cross_street": "airs:locatedAtCrossStreet",
"post_code": "dbpedia-owl:postalCode",
"capacity": "dbpedia-owl:capacity",
"creditcard": "schema:CreditCard",
"phone": "foaf:phone",
"car": "schema:car"
},
"creators": {
"creator1": "https://pietercolpaert.be/#me",
"creator2": "https://www.linkedin.com/in/andrei-popescu/"
}
}
\ No newline at end of file
......@@ -25,10 +25,12 @@ var JsonProcessor = /** @class */ (function () {
this.mainObject = mainObj;
this.mainJsonObject = this.getJsonObject(this.mainObject);
this.prefix = this.config.prefix;
// Set path (TODO: set from confi.json)
// SMD
//this.path = this.jsonSchema[this.mainJsonObject];
//this.properties = this.path[2].properties; // Path to the properties of the main object
// Set paths (TODO: set from confi.json)
// SMD: ElectricalMeasurment and Battery schemas
/*
this.path = this.jsonSchema[this.mainJsonObject];
this.properties = this.path[2].properties; // Path to the properties of the main object
*/
// GBFS
this.path = this.jsonSchema.properties.data.properties[this.mainJsonObject];
this.properties = this.path.items.properties; // Path to the properties of the main object
......@@ -66,13 +68,11 @@ var JsonProcessor = /** @class */ (function () {
*/
JsonProcessor.callJsonTraverseRecursive = function () {
var depth = 0;
// For each propery of the main object, call the recursive function jsonTraverseRecursive, which will
// recursively traverse each property up to depth = 1.
for (var prop in this.properties) {
//console.log("prop",prop);
//console.log("mainobj", this.mainObject);
//this.mainJsonObject = JsonProcessor.getJsonObject(this.prefix+':'+ RDFTools.capitalizeFirstLetter(prop));
this.mainJsonObject = JsonProcessor.getJsonObject(this.mainObject);
//console.log("mainjson",this.mainJsonObject);
this.jsonTraverseRecursive(depth, this.path, this.mainJsonObject, prop);
var mainJsonObject = JsonProcessor.getJsonObject(this.prefix + ':' + rdfTools_1.RDFTools.capitalizeFirstLetter(prop));
this.jsonTraverseRecursive(depth, this.path, mainJsonObject, prop);
}
;
return;
......@@ -94,9 +94,9 @@ var JsonProcessor = /** @class */ (function () {
var subItems;
var propDescription;
var directEnum;
var nestedEnum;
var oneOf;
console.log("MainJsonObject: ", mainJsonObject);
console.log("Depth", depth);
var anyOf;
if (depth == 0) {
// SMD
/*
......@@ -105,6 +105,10 @@ var JsonProcessor = /** @class */ (function () {
subItems = path[2].properties[prop].items;
propDescription = path[2].properties[prop].description;
directEnum = path[2].properties[prop].enum;
anyOf = path[2].properties[prop].anyOf;
if( subItems != undefined){
nestedEnum = subItems.enum;
}
*/
// GBFS
propType = path.items.properties[prop].type;
......@@ -113,10 +117,12 @@ var JsonProcessor = /** @class */ (function () {
propDescription = path.items.properties[prop].description;
directEnum = path.items.properties[prop]["enum"];
oneOf = path.items.properties[prop].oneOf;
if (subItems != undefined) {
nestedEnum = subItems["enum"];
}
}
if (depth == 1) {
console.log("depth = 1");
// SMD
// SMD: Electrical measurment and Battery
/*
tmpPath = path[2].properties[mainJsonObject]; // adapt the path at depth 1 for the currently mainObject
if(tmpPath.properties != undefined){
......@@ -138,21 +144,17 @@ var JsonProcessor = /** @class */ (function () {
propDescription = tmpPath.description;
directEnum = tmpPath["enum"];
oneOf = tmpPath.oneOf;
console.log("path", tmpPath);
console.log("directEnum", directEnum);
}
else {
propType = tmpPath.type;
subItems = tmpPath.items;
propDescription = tmpPath.description;
directEnum = subItems["enum"];
directEnum = subItems["enum"]; // look at station_information.json
oneOf = tmpPath.oneOf;
console.log("path", tmpPath);
console.log("directEnum", directEnum);
}
}
// Base cases
if (depth > 2) {
if (depth > 3) {
return;
}
if (propType == 'number') {
......@@ -249,6 +251,12 @@ var JsonProcessor = /** @class */ (function () {
this.writer.addQuad(quad_5);
return;
}
// first need to write sdm:RefDevice to file
if (anyOf != undefined) {
var quad_6 = JsonProcessor.getEnumerationQuad(anyOf, prop);
this.writer.addQuad(quad_6);
return;
}
// Recursive step
if (propType == 'object' || propType == 'array') {
var newClassName = void 0;
......@@ -271,8 +279,12 @@ var JsonProcessor = /** @class */ (function () {
}
}
if (directEnum != undefined) {
var quad_6 = JsonProcessor.getEnumerationQuad(directEnum, newClassName);
this.writer.addQuad(quad_6);
var quad_7 = JsonProcessor.getEnumerationQuad(directEnum, newClassName);
this.writer.addQuad(quad_7);
}
if (nestedEnum != undefined) {
var quad_8 = JsonProcessor.getEnumerationQuad(nestedEnum, newClassName);
this.writer.addQuad(quad_8);
}
depth += 1;
//mainJsonObject = JsonProcessor.getJsonObject(this.prefix+':'+ RDFTools.capitalizeFirstLetter(prop));
......@@ -324,17 +336,24 @@ var JsonProcessor = /** @class */ (function () {
*/
JsonProcessor.getEnumerationQuad = function (directEnum, name) {
var oneOfValues = [];
for (var _i = 0, directEnum_1 = directEnum; _i < directEnum_1.length; _i++) {
var value = directEnum_1[_i];
//We get the values from the mapping, else we create new terms
if (this.termMap.get(value) != undefined) {
oneOfValues.push(namedNode(this.termMap.get(value)));
}
else {
oneOfValues.push(namedNode(value));
var subPropQuad;
if (Array.isArray(directEnum)) {
console.log("is array", directEnum);
for (var _i = 0, directEnum_1 = directEnum; _i < directEnum_1.length; _i++) {
var value = directEnum_1[_i];
//We get the values from the mapping, else we create new terms
if (this.termMap.get(value) != undefined) {
oneOfValues.push(namedNode(this.termMap.get(value)));
}
else {
oneOfValues.push(namedNode(value));
}
}
subPropQuad = rdfTools_1.RDFTools.node_node_list(this.prefix + ':' + name, 'owl:oneOf', this.writer.list(oneOfValues));
}
var subPropQuad = rdfTools_1.RDFTools.node_node_list(this.prefix + ':' + name, 'owl:oneOf', this.writer.list(oneOfValues));
// trial to manage different anyOf, but they all result in arrays.
//if (typeof directEnum === 'object' ){
//}
return subPropQuad;
};
JsonProcessor.getJsonObject = function (mainObject) {
......
import { RDFTools } from "./rdfTools";
import {ShaclTools} from './shaclTools';
import { NamedNode } from "n3/lib/N3DataFactory";
import { convertToObject } from "typescript";
const N3 = require('n3');
const { DataFactory } = N3;
const { namedNode, literal, defaultGraph, quad } = DataFactory;
......@@ -11,6 +12,8 @@ export class JsonProcessor {
static config = require('./configs/config-gbfs.json');
//static config = require('./configs/config-smartdatamodel.json');
//static config = require('./configs/config-battery.json');
static jsonSource: any;
static jsonSchema: any;
static mainObject: any;
......@@ -46,15 +49,18 @@ export class JsonProcessor {
this.mainObject = mainObj;
this.mainJsonObject = this.getJsonObject(this.mainObject);
this.prefix = this.config.prefix;
// Set path (TODO: set from confi.json)
// Set paths (TODO: set from confi.json)
// SMD: ElectricalMeasurment and Battery schemas
/*
this.path = this.jsonSchema[this.mainJsonObject];
this.properties = this.path[2].properties; // Path to the properties of the main object
*/
// SMD
//this.path = this.jsonSchema[this.mainJsonObject];
//this.properties = this.path[2].properties; // Path to the properties of the main object
// GBFS
this.path = this.jsonSchema.properties.data.properties[this.mainJsonObject];
this.properties = this.path.items.properties; // Path to the properties of the main object
this.writer.addQuad(RDFTools.node_node_node('https://w3id.org/sdm/terms/'+ this.mainJsonObject, 'rdf:type', 'foaf:Document'));
this.writer.addQuad(RDFTools.node_node_literal('https://w3id.org/sdm/terms/'+ this.mainJsonObject, 'rdfs:comment', this.jsonSchema.description));
......@@ -79,10 +85,8 @@ export class JsonProcessor {
}
this.shaclFileText = ""; // reset in case there are more schemas
this.shaclTargetClass = JsonProcessor.getShaclTarget(mainObj);
// Create a ShaclShape object and insert the first entries
this.shaclFileText = this.shaclFileText+ShaclTools.shapeShaclRoot(this.shaclRoot);
this.shaclFileText = this.shaclFileText+'sh:targetClass ' + this.shaclTargetClass+ '; \n';
}
......@@ -92,15 +96,11 @@ export class JsonProcessor {
*/
static callJsonTraverseRecursive(){
let depth = 0;
// For each propery of the main object, call the recursive function jsonTraverseRecursive, which will
// recursively traverse each property up to depth = 1.
for (let prop in this.properties){
//console.log("prop",prop);
//console.log("mainobj", this.mainObject);
//this.mainJsonObject = JsonProcessor.getJsonObject(this.prefix+':'+ RDFTools.capitalizeFirstLetter(prop));
this.mainJsonObject = JsonProcessor.getJsonObject(this.mainObject);
//console.log("mainjson",this.mainJsonObject);
this.jsonTraverseRecursive( depth, this.path, this.mainJsonObject, prop);
let mainJsonObject = JsonProcessor.getJsonObject(this.prefix+':'+ RDFTools.capitalizeFirstLetter(prop));
this.jsonTraverseRecursive( depth, this.path, mainJsonObject, prop);
};
return;
}
......@@ -122,38 +122,36 @@ export class JsonProcessor {
let subItems;
let propDescription;
let directEnum;
let nestedEnum;
let oneOf;
console.log("MainJsonObject: ", mainJsonObject);
console.log("Depth", depth);
let anyOf;
if (depth == 0){
// SMD
/*
propType = path[2].properties[prop].type;
subProperties = path[2].properties[prop].properties;
subItems = path[2].properties[prop].items;
propDescription = path[2].properties[prop].description;
directEnum = path[2].properties[prop].enum;
anyOf = path[2].properties[prop].anyOf;
if( subItems != undefined){
nestedEnum = subItems.enum;
}
*/
// GBFS
propType = path.items.properties[prop].type;
subProperties = path.items.properties[prop].properties;
subItems = path.items.properties[prop].items;
propDescription = path.items.properties[prop].description;
directEnum = path.items.properties[prop].enum;
oneOf = path.items.properties[prop].oneOf;
if( subItems != undefined){
nestedEnum = subItems.enum;
}
}
if (depth == 1){
console.log("depth = 1");
// SMD
// SMD: Electrical measurment and Battery
/*
tmpPath = path[2].properties[mainJsonObject]; // adapt the path at depth 1 for the currently mainObject
if(tmpPath.properties != undefined){
......@@ -168,7 +166,6 @@ export class JsonProcessor {
propDescription = tmpPath.description;
*/
// GBFS
tmpPath = path.items.properties[mainJsonObject];
if(tmpPath.items == undefined ){
......@@ -177,26 +174,18 @@ export class JsonProcessor {
propDescription = tmpPath.description;
directEnum = tmpPath.enum;
oneOf = tmpPath.oneOf;
console.log("path", tmpPath);
console.log("directEnum", directEnum);
}
else{
propType = tmpPath.type;
subItems = tmpPath.items;
propDescription = tmpPath.description;
directEnum = subItems.enum;
directEnum = subItems.enum; // look at station_information.json
oneOf = tmpPath.oneOf;
console.log("path", tmpPath);
console.log("directEnum", directEnum);
}
}
// Base cases
if(depth > 2){
if(depth > 3){
return;
}
......@@ -268,9 +257,6 @@ export class JsonProcessor {
return;
}
if (propType == 'boolean'){
if (this.termMap.has(prop) == false) {
this.termMap.set(prop, this.prefix+':'+prop);
......@@ -300,6 +286,15 @@ export class JsonProcessor {
this.writer.addQuad(quad);
return;
}
// first need to write sdm:RefDevice to file
if (anyOf != undefined){
let quad = JsonProcessor.getEnumerationQuad(anyOf, prop);
this.writer.addQuad(quad);
return;
}
// Recursive step
if(propType == 'object' || propType =='array'){
......@@ -333,6 +328,11 @@ export class JsonProcessor {
let quad = JsonProcessor.getEnumerationQuad(directEnum, newClassName);
this.writer.addQuad(quad);
}
if (nestedEnum != undefined){
let quad = JsonProcessor.getEnumerationQuad(nestedEnum, newClassName);
this.writer.addQuad(quad);
}
depth += 1;
......@@ -392,17 +392,24 @@ export class JsonProcessor {
*/
static getEnumerationQuad(directEnum, name){
let oneOfValues:NamedNode[] = [];
for (const value of directEnum){
//We get the values from the mapping, else we create new terms
if (this.termMap.get(value)!= undefined) {
oneOfValues.push(namedNode(this.termMap.get(value)));
}
else{
oneOfValues.push(namedNode(value));
let subPropQuad;
if (Array.isArray(directEnum)){
console.log("is array", directEnum);
for (const value of directEnum){
//We get the values from the mapping, else we create new terms
if (this.termMap.get(value)!= undefined) {
oneOfValues.push(namedNode(this.termMap.get(value)));
}
else{
oneOfValues.push(namedNode(value));
}
}
subPropQuad = RDFTools.node_node_list(this.prefix+':'+name, 'owl:oneOf', this.writer.list(oneOfValues));
}
let subPropQuad = RDFTools.node_node_list(this.prefix+':'+name, 'owl:oneOf', this.writer.list(oneOfValues));
// trial to manage different anyOf, but they all result in arrays.
//if (typeof directEnum === 'object' ){
//}
return subPropQuad;
}
......
......@@ -8,6 +8,7 @@ var jsonProcessor_1 = require("./jsonProcessor");
var schema_object = new Map();
var config = require('./configs/config-gbfs.json');
//const config = require('./configs/config-smartdatamodel.json');
//const config = require('./configs/config-battery.json');
for (var object in config.sources) {
schema_object.set(object, config.sources[object]);
}
......
......@@ -7,6 +7,9 @@ import {JsonProcessor} from './jsonProcessor';
let schema_object = new Map<string, string>();
const config = require('./configs/config-gbfs.json');
//const config = require('./configs/config-smartdatamodel.json');
//const config = require('./configs/config-battery.json');
for( let object in config.sources){
schema_object.set(object, config.sources[object]);
}
......
// GBFS
depth = 0