@igea/oac_backend 1.0.49 → 1.0.51
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/fuseki.js +8 -4
- package/src/controllers/ontology.js +1 -1
- package/src/models/converter.js +93 -2
- package/src/models/fuseki.js +12 -10
- 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.51",
|
|
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",
|
|
@@ -239,10 +239,13 @@ router.post('/search/by-prefix', (req, res) => {
|
|
|
239
239
|
const bindings = response.data.results.bindings;
|
|
240
240
|
let results = []
|
|
241
241
|
bindings.forEach(result => {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
242
|
+
if(result.node){
|
|
243
|
+
var label = result.label ? result.label.value : "";
|
|
244
|
+
results.push({
|
|
245
|
+
instance: result.node.value,
|
|
246
|
+
label
|
|
247
|
+
});
|
|
248
|
+
}
|
|
246
249
|
});
|
|
247
250
|
res.json({
|
|
248
251
|
success: true,
|
|
@@ -250,6 +253,7 @@ router.post('/search/by-prefix', (req, res) => {
|
|
|
250
253
|
message: null
|
|
251
254
|
});
|
|
252
255
|
}).catch(err => {
|
|
256
|
+
console.log(err)
|
|
253
257
|
res.status(500).json({
|
|
254
258
|
success: false,
|
|
255
259
|
data: null,
|
|
@@ -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,95 @@ 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
|
+
const tagName = Converter.qnameLocal(tagUri);
|
|
121
|
+
|
|
122
|
+
const elem = parent.ele(tagName).att('rdf:about', subject.value);
|
|
123
|
+
|
|
124
|
+
// Itera predicati
|
|
125
|
+
const preds = new Set(dataset.match(subject).map(q => q.predicate.value));
|
|
126
|
+
for (let p of Array.from(preds).sort()) {
|
|
127
|
+
const predName = Converter.qnameLocal(p);
|
|
128
|
+
console.log("predName: " + predName)
|
|
129
|
+
const objects = dataset.match(subject, rdf.namedNode(p)).map(q => q.object);
|
|
130
|
+
|
|
131
|
+
for (let obj of objects) {
|
|
132
|
+
const child = elem.ele(predName);
|
|
133
|
+
|
|
134
|
+
if (obj.termType === 'Literal') {
|
|
135
|
+
const lit = child.ele('rdfs:Literal').att('rdf:about', obj.value);
|
|
136
|
+
lit.ele('rdfs:label').txt(obj.value);
|
|
137
|
+
} else if (obj.termType === 'NamedNode' || obj.termType === 'BlankNode') {
|
|
138
|
+
Converter.buildNode(dataset, obj, child, visited);
|
|
139
|
+
} else {
|
|
140
|
+
child.txt(obj.value);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return elem;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
static turtle2RdfXmlCustom(turtle, outRdfXmlPath=null) {
|
|
149
|
+
|
|
150
|
+
return new Promise((resolve, reject) => {
|
|
151
|
+
const parser = new Parser();
|
|
152
|
+
const quads = parser.parse(turtle);
|
|
153
|
+
|
|
154
|
+
const dataset = rdf.dataset();
|
|
155
|
+
for (const quad of quads) {
|
|
156
|
+
dataset.add(quad);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const root = create({ version: '1.0', encoding: 'UTF-8' })
|
|
160
|
+
.ele('rdf:RDF', {
|
|
161
|
+
'xmlns:rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
|
|
162
|
+
'xmlns:rdfs': 'http://www.w3.org/2000/01/rdf-schema#'
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const visited = new Set();
|
|
166
|
+
const subjects = Array.from(new Set(dataset.map(q => q.subject.value)))
|
|
167
|
+
.map(uri => rdf.namedNode(uri))
|
|
168
|
+
.sort((a, b) => a.value.localeCompare(b.value));
|
|
169
|
+
|
|
170
|
+
console.log(subjects)
|
|
171
|
+
for (let s of subjects) {
|
|
172
|
+
Converter.buildNode(dataset, s, root, visited);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const xml = root.end({ prettyPrint: true });
|
|
176
|
+
|
|
177
|
+
if(outRdfXmlPath){
|
|
178
|
+
fs.writeFileSync(outRdfXmlPath, xml, 'utf8');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
resolve(xml)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
//------------------------------------------------------------------------------------
|
|
187
|
+
|
|
97
188
|
static async turtle2RdfXml(inTurtlePath, outRdfXmlPath) {
|
|
98
189
|
return new Promise((resolve, reject) => {
|
|
99
190
|
const command = `rapper -i turtle -o rdfxml "${inTurtlePath}" > "${outRdfXmlPath}"`;
|
package/src/models/fuseki.js
CHANGED
|
@@ -90,20 +90,22 @@ class Fuseki {
|
|
|
90
90
|
if(limit < 1) limit = 50;
|
|
91
91
|
return `
|
|
92
92
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
93
|
-
SELECT
|
|
93
|
+
SELECT ?node ?label
|
|
94
94
|
WHERE {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
95
|
+
{
|
|
96
|
+
?s ?p ?node .
|
|
97
|
+
FILTER (isIRI(?node) && STRSTARTS(STR(?node), "${prefix}"))
|
|
98
|
+
}
|
|
99
|
+
UNION
|
|
100
|
+
{
|
|
101
|
+
?node ?p ?o.
|
|
102
|
+
FILTER (isIRI(?node) && STRSTARTS(STR(?node), "${prefix}"))
|
|
103
|
+
}
|
|
104
|
+
OPTIONAL { ?node rdfs:label ?label . }
|
|
103
105
|
}
|
|
104
106
|
ORDER BY ?label
|
|
105
107
|
LIMIT ${limit}
|
|
106
|
-
OFFSET ${offset}`
|
|
108
|
+
OFFSET ${offset}`
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
static getQuerySearchInvestigation(id, prefix='http://indagine/') {
|
|
@@ -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.
|