@igea/oac_backend 1.0.50 → 1.0.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +9 -4
- package/src/controllers/ontology.js +1 -1
- package/src/models/converter.js +122 -2
- package/src/models/vocabolaries/parser +1 -1
- package/src/ontology/schema_v2.shacl.ttl +10 -2
- package/test/models/converter.test.js +12 -2
- package/test/models/example-investigation-02.ttl +32 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@igea/oac_backend",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.52",
|
|
4
4
|
"description": "Backend service for the OAC project",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -24,22 +24,27 @@
|
|
|
24
24
|
"homepage": "https://github.com/chpigea/oac_backend#readme",
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@igea/oac_jwt_helpers": "1.0.10",
|
|
27
|
+
"@rdfjs/data-model": "1.1.2",
|
|
28
|
+
"@rdfjs/types": "1.1.0",
|
|
27
29
|
"axios": "1.10.0",
|
|
28
30
|
"cookie-parser": "1.4.7",
|
|
29
31
|
"crypto": "1.0.1",
|
|
30
32
|
"express": "5.1.0",
|
|
31
33
|
"express-rate-limit": "8.1.0",
|
|
32
|
-
"fast-xml-parser": "
|
|
34
|
+
"fast-xml-parser": "5.3.3",
|
|
33
35
|
"get-port": "7.1.0",
|
|
34
36
|
"knex": "3.1.0",
|
|
35
37
|
"libxmljs2": "0.37.0",
|
|
36
38
|
"multer": "2.0.2",
|
|
37
|
-
"n3": "1.
|
|
39
|
+
"n3": "1.17.2",
|
|
38
40
|
"nodemailer": "7.0.6",
|
|
39
41
|
"pg": "8.16.3",
|
|
42
|
+
"rdf-data-factory": "2.0.2",
|
|
43
|
+
"rdf-ext": "1.3.0",
|
|
40
44
|
"rdf-validate-shacl": "0.6.5",
|
|
41
45
|
"strip-bom": "5.0.0",
|
|
42
|
-
"tmp": "0.2.5"
|
|
46
|
+
"tmp": "0.2.5",
|
|
47
|
+
"xmlbuilder2": "4.0.3"
|
|
43
48
|
},
|
|
44
49
|
"devDependencies": {
|
|
45
50
|
"chai": "5.2.1",
|
|
@@ -228,7 +228,7 @@ router.post('/convert/:from/:to', (req, res) => {
|
|
|
228
228
|
console.log(`Requesting conversion from ${from} to ${to}`);
|
|
229
229
|
let conversionFunction = null
|
|
230
230
|
if(from === 'ttl' && to === 'xml'){
|
|
231
|
-
conversionFunction = Converter.
|
|
231
|
+
conversionFunction = Converter.turtle2RdfXmlCustom
|
|
232
232
|
}else if(from === 'xml' && to === 'ttl'){
|
|
233
233
|
conversionFunction = Converter.rdfXml2Turtle
|
|
234
234
|
}else if(from === to){
|
package/src/models/converter.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
|
-
|
|
3
|
-
const { DataFactory, Store } = require('n3');
|
|
2
|
+
|
|
3
|
+
const { Parser, DataFactory, Store } = require('n3');
|
|
4
4
|
const { exec } = require('child_process');
|
|
5
|
+
const { create } = require('xmlbuilder2');
|
|
6
|
+
const rdf = require('rdf-ext')
|
|
5
7
|
|
|
6
8
|
class Converter {
|
|
7
9
|
|
|
@@ -94,6 +96,124 @@ class Converter {
|
|
|
94
96
|
|
|
95
97
|
}
|
|
96
98
|
|
|
99
|
+
//------------------------------------------------------------------------------------
|
|
100
|
+
// Helper per estrarre QName/localName
|
|
101
|
+
|
|
102
|
+
static qnameLocal(uriObj) {
|
|
103
|
+
let uri = (typeof uriObj === 'string') ? uriObj : uriObj.value
|
|
104
|
+
if (uri.includes('#'))
|
|
105
|
+
return uri.split('#')[1];
|
|
106
|
+
return uri.split('/').pop();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Generatore XML ricorsivo
|
|
110
|
+
static buildNode(dataset, subject, parent, visited) {
|
|
111
|
+
if (visited.has(subject.value)) {
|
|
112
|
+
parent.ele('Reference').att('rdf:resource', subject.value);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
visited.add(subject.value);
|
|
116
|
+
|
|
117
|
+
// Determina tag principale dall'rdf:type
|
|
118
|
+
const types = [...dataset.match(subject, rdf.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'))];
|
|
119
|
+
const tagUri = types.length ? types[0].object.value : subject.value;
|
|
120
|
+
let tagName = Converter.qnameLocal(tagUri);
|
|
121
|
+
|
|
122
|
+
const tagIsNumber = (typeof tagName === 'string' && tagName.trim() !== '' && !isNaN(tagName))
|
|
123
|
+
if(tagIsNumber) tagName = "_" + tagName; // Prefix numerici per XML
|
|
124
|
+
|
|
125
|
+
//console.log("Building node: " + tagName + " for subject: " + subject.value)
|
|
126
|
+
|
|
127
|
+
const elem = parent.ele(tagName).att('rdf:about', subject.value);
|
|
128
|
+
|
|
129
|
+
// Itera predicati
|
|
130
|
+
const preds = new Set(dataset.match(subject).toArray().map(q => q.predicate.value));
|
|
131
|
+
for (let p of Array.from(preds).sort()) {
|
|
132
|
+
const predName = Converter.qnameLocal(p);
|
|
133
|
+
//console.log(">>> predName: " + predName)
|
|
134
|
+
const objects = dataset.match(subject, rdf.namedNode(p)).toArray().map(q => q.object);
|
|
135
|
+
|
|
136
|
+
for (let obj of objects) {
|
|
137
|
+
// Skip rdf:type as it's used for the tag name
|
|
138
|
+
if(predName === "type") continue;
|
|
139
|
+
|
|
140
|
+
const child = elem.ele(predName);
|
|
141
|
+
|
|
142
|
+
if (obj.termType === 'Literal') {
|
|
143
|
+
const lit = child.ele('rdfs:Literal').att('rdf:about', obj.value);
|
|
144
|
+
lit.ele('rdfs:label').txt(obj.value);
|
|
145
|
+
} else if (obj.termType === 'NamedNode' || obj.termType === 'BlankNode') {
|
|
146
|
+
|
|
147
|
+
Converter.buildNode(dataset, obj, child, visited);
|
|
148
|
+
|
|
149
|
+
} else {
|
|
150
|
+
child.txt(obj.value);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return elem;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
static turtle2RdfXmlCustom(turtle, outRdfXmlPath=null) {
|
|
159
|
+
|
|
160
|
+
return new Promise((resolve, reject) => {
|
|
161
|
+
const parser = new Parser();
|
|
162
|
+
const quads = parser.parse(turtle);
|
|
163
|
+
|
|
164
|
+
const dataset = rdf.dataset();
|
|
165
|
+
for (const quad of quads) {
|
|
166
|
+
dataset.add(quad);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const root = create({ version: '1.0', encoding: 'UTF-8' })
|
|
170
|
+
.ele('rdf:RDF', {
|
|
171
|
+
'xmlns:rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
|
|
172
|
+
'xmlns:rdfs': 'http://www.w3.org/2000/01/rdf-schema#'
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const visited = new Set();
|
|
176
|
+
|
|
177
|
+
const subjects = Array.from(
|
|
178
|
+
new Set(
|
|
179
|
+
dataset.toArray().map(q => q.subject.value)
|
|
180
|
+
)
|
|
181
|
+
).map(uri => rdf.namedNode(uri))
|
|
182
|
+
.sort((a, b) => a.value.localeCompare(b.value));
|
|
183
|
+
|
|
184
|
+
const objects = Array.from(
|
|
185
|
+
new Set(
|
|
186
|
+
dataset.toArray().map(q => q.object.value)
|
|
187
|
+
)
|
|
188
|
+
).map(uri => rdf.namedNode(uri))
|
|
189
|
+
.sort((a, b) => a.value.localeCompare(b.value));
|
|
190
|
+
|
|
191
|
+
for (let s of subjects) {
|
|
192
|
+
s["_isRoot"] = false;
|
|
193
|
+
if (objects.findIndex(o => o.value === s.value) == -1) {
|
|
194
|
+
s["_isRoot"] = true;
|
|
195
|
+
//console.log("Root subject: " + s.value);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
for (let s of subjects) {
|
|
200
|
+
if(s["_isRoot"])
|
|
201
|
+
Converter.buildNode(dataset, s, root, visited);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const xml = root.end({ prettyPrint: true });
|
|
205
|
+
|
|
206
|
+
if(outRdfXmlPath){
|
|
207
|
+
fs.writeFileSync(outRdfXmlPath, xml, 'utf8');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
resolve(xml)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
//------------------------------------------------------------------------------------
|
|
216
|
+
|
|
97
217
|
static async turtle2RdfXml(inTurtlePath, outRdfXmlPath) {
|
|
98
218
|
return new Promise((resolve, reject) => {
|
|
99
219
|
const command = `rapper -i turtle -o rdfxml "${inTurtlePath}" > "${outRdfXmlPath}"`;
|
|
@@ -96,7 +96,7 @@ class Parser{
|
|
|
96
96
|
resolve(queries);
|
|
97
97
|
}
|
|
98
98
|
let checkCompleted = function(index, query, status, message){
|
|
99
|
-
console.log(`Completed ${index+1}/${files.length}`);
|
|
99
|
+
//console.log(`Completed ${index+1}/${files.length}`);
|
|
100
100
|
files[index].status = status;
|
|
101
101
|
files[index].message = message;
|
|
102
102
|
if(status) queries[index]=query;
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
#@prefix skos: <http://www.w3.org/2004/02/skos/core> .
|
|
13
13
|
#@prefix pref: <http://indagine/> .
|
|
14
14
|
|
|
15
|
+
@prefix core: <http://data.sparna.fr/ontologies/sparnatural-config-core#> .
|
|
16
|
+
@prefix dash: <http://datashapes.org/dash#> .
|
|
17
|
+
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
|
|
18
|
+
@prefix skosxl: <http://www.w3.org/2008/05/skos-xl#> .
|
|
19
|
+
@prefix volipi: <http://data.sparna.fr/ontologies/volipi#> .
|
|
20
|
+
|
|
15
21
|
|
|
16
22
|
ex:E7ActivityShape # INDAGINE
|
|
17
23
|
a sh:NodeShape, rdfs:Class ;
|
|
@@ -136,11 +142,13 @@ ex:E41Appellation02Shape # DENOMINAZIONE BENE
|
|
|
136
142
|
ex:E55Type02Shape # TIPO QUESITO DIAGNOSTICO
|
|
137
143
|
a sh:NodeShape, rdfs:Class ;
|
|
138
144
|
sh:targetClass crm:E55_Type ;
|
|
145
|
+
volipi:iconName "fa-solid fa-car" ;
|
|
139
146
|
sh:property [
|
|
140
147
|
sh:path ex:P2hastype02 ;
|
|
141
|
-
|
|
148
|
+
#dash:searchWidget core:TreeProperty ;
|
|
149
|
+
sh:class owl:Class ; # vocabolario QUESITO DIAGNOSTICO (8) id="quesito-diagnostico"
|
|
142
150
|
owl:imports <OAC_EXPOSED_PROTOCOL://OAC_EXPOSED_HOST:OAC_EXPOSED_PORT/backend/fuseki/get-vocabolary-terms/quesito-diagnostico> ;
|
|
143
|
-
|
|
151
|
+
sh:name "Tipo" ;
|
|
144
152
|
sh:maxCount 1 ;
|
|
145
153
|
sh:minCount 1 ;
|
|
146
154
|
] ;
|
|
@@ -27,13 +27,23 @@ describe('Converter', () => {
|
|
|
27
27
|
});
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
it('should convert a turtle string to sparql UPSERT', (done) => {
|
|
30
|
+
it('should convert a turtle string to sparql UPSERT', (done) => {
|
|
31
31
|
let inputTurtle = fs.readFileSync(__dirname + '/example-investigation-01.ttl', 'utf8');
|
|
32
32
|
let sparql = converter.turtle2Sparql(inputTurtle)
|
|
33
33
|
expect(sparql).to.be.a('string');
|
|
34
34
|
expect(sparql.length).to.be.greaterThan(0);
|
|
35
|
-
console.log(sparql);
|
|
35
|
+
//console.log(sparql);
|
|
36
36
|
done();
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
+
it('should convert a turtle string to Custom RDF/Xml', (done) => {
|
|
40
|
+
let inputTurtle = fs.readFileSync(__dirname + '/example-investigation-02.ttl', 'utf8');
|
|
41
|
+
converter.turtle2RdfXmlCustom(inputTurtle).then((sparql) => {
|
|
42
|
+
expect(sparql).to.be.a('string');
|
|
43
|
+
expect(sparql.length).to.be.greaterThan(0);
|
|
44
|
+
console.log(sparql);
|
|
45
|
+
done();
|
|
46
|
+
})
|
|
47
|
+
});
|
|
48
|
+
|
|
39
49
|
})
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
@prefix sh: <http://www.w3.org/ns/shacl#>.
|
|
2
|
+
@prefix ex: <http://example.org/shapes/>.
|
|
3
|
+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
|
|
4
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
|
|
5
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
|
|
6
|
+
@prefix owl: <http://www.w3.org/2002/07/owl#>.
|
|
7
|
+
@prefix crm: <http://www.cidoc-crm.org/cidoc-crm/>.
|
|
8
|
+
@prefix basecpm: <http://ontome.net/ns/cpm/>.
|
|
9
|
+
@prefix base: <http://www.ics.forth.gr/isl/CRMinf/>.
|
|
10
|
+
@prefix cpm: <http://ontome.net/ns/cpm/>.
|
|
11
|
+
@prefix crmsci: <http://www.cidoc-crm.org/extensions/crmsci/>.
|
|
12
|
+
@prefix pref: <http://diagnostica/>.
|
|
13
|
+
@prefix skos: <http://www.w3.org/2004/02/skos/core#>.
|
|
14
|
+
|
|
15
|
+
pref:3cc24c8c-ce06-40ef-bdce-9da2c4c5394b ex:P48haspreferredidentifier01 <http://indagine/3>;
|
|
16
|
+
a crm:E42_Identifier.
|
|
17
|
+
pref:1adba9d5-c83b-41a8-b97f-c6e5b168658a a crm:E7_Activity;
|
|
18
|
+
crm:P48_has_preferred_identifier pref:3cc24c8c-ce06-40ef-bdce-9da2c4c5394b;
|
|
19
|
+
crm:P17_was_motivated_by pref:f30ead0e-edcc-4f6f-868e-8bc6f9d13813;
|
|
20
|
+
crm:P14_carried_out_by pref:57701c6c-ece4-472b-bc24-b75c1c254568;
|
|
21
|
+
<http://purl.org/dc/terms/conformsTo> ex:E7ActivityShape.
|
|
22
|
+
pref:40a75ae6-daa4-4ae1-bd41-2e07175f6f75 a crm:E55_Type;
|
|
23
|
+
ex:P2hastype02 <http://diagnostica/vocabularies/quesito-diagnostico/tecnologia-di-produzione>.
|
|
24
|
+
pref:f30ead0e-edcc-4f6f-868e-8bc6f9d13813 a base:I12_Adopted_Belief;
|
|
25
|
+
crm:P2_has_type pref:40a75ae6-daa4-4ae1-bd41-2e07175f6f75.
|
|
26
|
+
pref:95e957ad-55b5-45d3-bf62-6a292959c4f9 a crm:E41_Appellation;
|
|
27
|
+
ex:ente_richiedente "Università";
|
|
28
|
+
ex:schedatore "Christian".
|
|
29
|
+
pref:956f0c5a-6fc5-4a79-b399-6c6288fb000f a crm:E55_Type;
|
|
30
|
+
crm:P1_is_defined_by pref:95e957ad-55b5-45d3-bf62-6a292959c4f9.
|
|
31
|
+
pref:57701c6c-ece4-472b-bc24-b75c1c254568 a crm:E29_Actor;
|
|
32
|
+
crm:P2_has_type pref:956f0c5a-6fc5-4a79-b399-6c6288fb000f.
|