@igea/oac_backend 1.0.45 → 1.0.47

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@igea/oac_backend",
3
- "version": "1.0.45",
3
+ "version": "1.0.47",
4
4
  "description": "Backend service for the OAC project",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -29,6 +29,7 @@
29
29
  "crypto": "1.0.1",
30
30
  "express": "5.1.0",
31
31
  "express-rate-limit": "8.1.0",
32
+ "fast-xml-parser": "^5.3.3",
32
33
  "get-port": "7.1.0",
33
34
  "knex": "3.1.0",
34
35
  "libxmljs2": "0.37.0",
@@ -71,32 +71,65 @@ router.post('/upload/vocabularies', upload.array('files'), (req, res) => {
71
71
 
72
72
  if(xmlFiles.length == 1) {
73
73
  let xmlFile = uploadedFiles[0];
74
- VocabParser.insertQuery(xmlFile.path).then(query => {
75
- console.log("Query to insert vocabularies: ", query);
76
-
74
+ if(!xmlFile.valid){
77
75
  deleteFiles(uploadedFiles)
78
-
79
-
80
- axios.post(fusekiUrlUpdate, query, {
81
- headers: {
82
- 'Content-Type': 'application/sparql-update',
83
- 'Accept': 'application/sparql-results+json'
76
+ return res.status(400).json({ message: 'Uploaded XML file is not valid' });
77
+ }
78
+ VocabParser.insertQuery(xmlFile.path).then(queries => {
79
+ //console.log("Query to insert vocabularies: ", query);
80
+ let results = Array(queries.length, null);
81
+ let checkCompleted = function(){
82
+ console.log(results)
83
+ deleteFiles(uploadedFiles)
84
+ let failed = results.filter(r => r.status === false);
85
+ console.log(failed);
86
+ if(failed.length > 0){
87
+ let message = `Error inserting vocabularies in ${failed.length} files.`;
88
+ return res.status(500).json({
89
+ message,
90
+ files: uploadedFiles,
91
+ results
92
+ });
93
+ }else{
94
+ res.json({
95
+ message: 'File correctly uploaded and vocabularies updated in the triple store',
96
+ files: uploadedFiles
97
+ });
84
98
  }
99
+ }
100
+ let fusekiCall = function(index){
101
+ return new Promise((resolve, reject) => {
102
+ let query = queries[index];
103
+ try{
104
+ axios.post(fusekiUrlUpdate, query, {
105
+ headers: {
106
+ 'Content-Type': 'application/sparql-update',
107
+ 'Accept': 'application/sparql-results+json'
108
+ }
109
+ })
110
+ .then(() => {
111
+ resolve({
112
+ index, success: true, message: 'Vocabulary inserted correctly'
113
+ });
114
+ }).catch(error => {
115
+ let msg = (error.response?.status + error.response?.data) || error.message
116
+ resolve({
117
+ index, success: false, message: `Error from SPARQL end-point: ${msg}`
118
+ });
119
+ });
120
+ }catch(e){
121
+ resolve({
122
+ index, success: false, message: `Error: ${e}`
123
+ });
124
+ }
125
+ })
126
+ }
127
+ setTimeout(async ()=>{
128
+ for(let index=0; index<queries.length; index++){
129
+ results[index] = await fusekiCall(index);
130
+ }
131
+ checkCompleted();
85
132
  })
86
- .then(response => {
87
- res.json({
88
- message: 'File correctly uploaded and vocabularies updated: ' + response.data,
89
- files: uploadedFiles
90
- });
91
- }).catch(error => {
92
- let message = (error.response?.status + error.response?.data) || error.message
93
- res.status(500).json({
94
- message: 'Error from SPARQL end-point: ' + message,
95
- files: uploadedFiles,
96
- query
97
- });
98
- });
99
-
100
133
  }).catch(err => {
101
134
  deleteFiles(uploadedFiles)
102
135
  console.error('Error transforming XML:', err);
@@ -108,7 +141,7 @@ router.post('/upload/vocabularies', upload.array('files'), (req, res) => {
108
141
 
109
142
  }else{
110
143
  deleteFiles(uploadedFiles);
111
- let message = 'Multiple XML files is not supported';
144
+ let message = 'Multiple XML files are not supported';
112
145
  if(xmlFiles.length == 0)
113
146
  message = 'No XML files uploaded';
114
147
  return res.status(400).json({ message });
@@ -5,7 +5,7 @@ const { exec } = require('child_process');
5
5
  const stripBom = require('strip-bom').default;
6
6
  const config = require('../../config');
7
7
  const VocabPrefix = config.fuseki.vocabularies.prefix || 'diagnostica';
8
-
8
+ const Splitter = require('./splitter');
9
9
 
10
10
  const transformMode = {
11
11
  default: 'default',
@@ -63,7 +63,7 @@ class Parser{
63
63
  let xsltPath = this.xsltPath[using] || this.xsltPath.default;
64
64
  try{
65
65
  let command = 'xsltproc --stringparam prefix ' + VocabPrefix + ' ';
66
- command += xsltPath + ' ' + xmlPath
66
+ command += xsltPath + ' "' + xmlPath + '"';
67
67
  exec(command, (err, stdout, stderr) => {
68
68
  if (err) {
69
69
  reject(err)
@@ -75,45 +75,73 @@ class Parser{
75
75
  }catch(e){
76
76
  reject(e)
77
77
  }
78
-
79
- /*
80
- let xmlData = this._getXmlData(xmlPath);
81
- const xml = xmlParser.xmlParse(xmlData);
82
- xslt.xsltProcess(xml, this.xslt)
83
- .then(result => {
84
- var terms = result.split('&#10;')
85
- resolve(terms.map(line => line.trim()).filter(line => line.length > 0));
86
- }).catch(err => {
87
- reject(err)
88
- });
89
- */
90
78
  });
91
79
  }
92
80
 
93
- insertQuery(xmlPath){
81
+ insertQuery(xmlPath, className='crm:E55_Type'){
94
82
  return new Promise((resolve, reject) => {
95
- this.transform(xmlPath, transformMode.forInsert).then(terms => {
96
- let query = `
97
- PREFIX crm: <http://www.cidoc-crm.org/cidoc-crm/>
98
- PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
83
+ const xmlFolder = path.dirname(xmlPath);
84
+ const xmlFile = path.basename(xmlPath);
85
+ const splitter = new Splitter(xmlFolder, xmlFile);
86
+ const files = splitter.splitFiles();
87
+ let queries = new Array(files.length);
88
+ let cleanTempFiles = function(queries){
89
+ for(let file of files){
90
+ try{
91
+ fs.unlinkSync(file.path);
92
+ }catch(e){
93
+ //console.log(`Error deleting temp file ${file.path}: ${e.message}`);
94
+ }
95
+ }
96
+ resolve(queries);
97
+ }
98
+ let checkCompleted = function(index, query, status, message){
99
+ console.log(`Completed ${index+1}/${files.length}`);
100
+ files[index].status = status;
101
+ files[index].message = message;
102
+ if(status) queries[index]=query;
103
+ let allDone = files.every(f => f.status !== null);
104
+ if(allDone){
105
+ cleanTempFiles(queries);
106
+ }
107
+ }
108
+ let transformFn = function(instance, index, className, xmlPath){
109
+ return function(){
110
+ instance.transform(xmlPath, transformMode.forInsert).then(terms => {
111
+ let query = `
112
+ PREFIX crm: <http://www.cidoc-crm.org/cidoc-crm/>
113
+ PREFIX basecpm: <http://ontome.net/ns/cpm/>
114
+ PREFIX j.0: <http://www.cidoc-crm.org/extensions/crmsci/>
115
+ PREFIX crmsci: <http://www.ics.forth.gr/isl/CRMsci>
116
+ PREFIX base: <http://www.ics.forth.gr/isl/CRMinf/>
117
+ PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
99
118
 
100
- INSERT {
101
- ?term a crm:E55_Type ;
102
- rdfs:label ?label ;
103
- crm:P127_has_broader_term ?broader .
119
+ INSERT {
120
+ ?term a ${className} ;
121
+ rdfs:label ?label ;
122
+ crm:P127_has_broader_term ?broader .
123
+ }
124
+ WHERE {
125
+ VALUES (?term ?label ?broader) {
126
+ ${terms.join(' \n')}
127
+ }
128
+ FILTER NOT EXISTS {
129
+ ?term rdfs:label ?label .
130
+ }
131
+ }`
132
+ checkCompleted(index, query, true, null);
133
+ }).catch(err => {
134
+ console.log(err);
135
+ checkCompleted(index, null, false, `${err}`);
136
+ });
104
137
  }
105
- WHERE {
106
- VALUES (?term ?label ?broader) {
107
- ${terms.join(' \n')}
108
- }
109
- FILTER NOT EXISTS {
110
- ?term rdfs:label ?label .
111
- }
112
- }`
113
- resolve(query);
114
- }).catch(err => {
115
- reject(err);
116
- });
138
+ }
139
+
140
+ for(let index=0; index<files.length; index++){
141
+ let fn = transformFn(this, index, files[index].class, files[index].path);
142
+ fn();
143
+ }
144
+
117
145
  })
118
146
  }
119
147
 
@@ -0,0 +1,69 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { XMLParser, XMLBuilder } = require('fast-xml-parser');
4
+
5
+ class Splitter {
6
+
7
+ constructor(xmlFolder, xmlName, skipWrite=false) {
8
+ this.xmlFolder = xmlFolder;
9
+ this.xmlName = xmlName;
10
+ this.skipWrite = skipWrite;
11
+ const xmlPath = path.join(this.xmlFolder, this.xmlName);
12
+ this.xml = fs.readFileSync(xmlPath, 'utf8');
13
+ // Parser
14
+ this.parser = new XMLParser({
15
+ ignoreAttributes: false,
16
+ attributeNamePrefix: '@_'
17
+ });
18
+ }
19
+
20
+ splitByClass() {
21
+ const data = this.parser.parse(this.xml);
22
+ const vocabularies = data.vocabularies.vocabulary;
23
+
24
+ // Normalizza a array
25
+ const vocabArray = Array.isArray(vocabularies)
26
+ ? vocabularies
27
+ : [vocabularies];
28
+
29
+ // Raggruppa per class
30
+ const grouped = {};
31
+ for (const vocab of vocabArray) {
32
+ const cls = vocab['@_class'] || 'NO_CLASS';
33
+ if (!grouped[cls]) grouped[cls] = [];
34
+ grouped[cls].push(vocab);
35
+ }
36
+
37
+ return grouped;
38
+ }
39
+
40
+ splitFiles() {
41
+ const grouped = this.splitByClass();
42
+ const builder = new XMLBuilder({
43
+ ignoreAttributes: false,
44
+ attributeNamePrefix: '@_',
45
+ format: true,
46
+ indentBy: ' '
47
+ });
48
+ let index = 0
49
+ let results = [];
50
+ for (const cls in grouped) {
51
+ const outObj = {
52
+ vocabularies: {
53
+ vocabulary: grouped[cls]
54
+ }
55
+ };
56
+ index++;
57
+ const xmlOut = builder.build(outObj);
58
+ const xmlOutPath = path.join(this.xmlFolder, `${this.xmlName}_split_${index}.xml`);
59
+ if(!this.skipWrite)
60
+ fs.writeFileSync(xmlOutPath, xmlOut, 'utf8');
61
+ results.push({ class: cls, path: xmlOutPath, status:null, message:null });
62
+ }
63
+ return results.sort((a, b) => a.class.localeCompare(b.class));
64
+ }
65
+
66
+ }
67
+
68
+
69
+ module.exports = Splitter;
@@ -52,7 +52,14 @@
52
52
  </xs:element>
53
53
  </xs:sequence>
54
54
 
55
- <xs:attribute name="id" type="xs:string" use="required"/>
55
+ <xs:attribute name="id" use="required">
56
+ <xs:simpleType>
57
+ <xs:restriction base="xs:string">
58
+ <xs:pattern value="[A-Za-z0-9_\-]+"/>
59
+ </xs:restriction>
60
+ </xs:simpleType>
61
+ </xs:attribute>
62
+
56
63
  </xs:complexType>
57
64
 
58
65
  </xs:schema>
@@ -43,7 +43,7 @@
43
43
  </xsl:if>
44
44
  </xsl:variable>
45
45
 
46
- <xsl:variable name="vocab-id" select="normalize-space(@id)"/>
46
+ <xsl:variable name="vocab-id" select="translate(normalize-space(@id), ' ', '_')"/>
47
47
  <xsl:apply-templates select="term">
48
48
  <xsl:with-param name="path" select="concat('http://', $prefix, $vocab-id)"/>
49
49
  <xsl:with-param name="type" select="concat($type, ' ')"/>
@@ -54,7 +54,7 @@
54
54
  <xsl:template match="term">
55
55
  <xsl:param name="path"/>
56
56
  <xsl:param name="type"/>
57
- <xsl:variable name="current-id" select="normalize-space(@id)"/>
57
+ <xsl:variable name="current-id" select="translate(normalize-space(@id), ' ', '_')"/>
58
58
  <xsl:variable name="new-path" select="concat($path, '/', $current-id)"/>
59
59
  <xsl:variable name="new-type" select="concat($type, ' ')"/>
60
60
 
@@ -76,7 +76,7 @@
76
76
  </xsl:when>
77
77
  <xsl:otherwise>
78
78
 
79
- <xsl:variable name="cid" select="normalize-space(@id)"/>
79
+ <xsl:variable name="cid" select="translate(normalize-space(@id), ' ', '_')"/>
80
80
  <xsl:variable name="npath" select="concat($path, '/', $current-id)"/>
81
81
 
82
82
  <xsl:for-each select="name">
@@ -0,0 +1,175 @@
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 j.0: <http://www.cidoc-crm.org/extensions/crmsci/> .
10
+ #@prefix crmsci: <http://www.ics.forth.gr/isl/CRMsci> .
11
+ @prefix base: <http://www.ics.forth.gr/isl/CRMinf/> .
12
+ #@prefix skos: <http://www.w3.org/2004/02/skos/core> .
13
+ #@prefix pref: <http://indagine/> .
14
+
15
+
16
+ ex:E7ActivityShape # INDAGINE
17
+ a sh:NodeShape, rdfs:Class ;
18
+ sh:targetClass crm:E7_Activity ;
19
+ sh:property [
20
+ sh:path crm:P48_has_preferred_identifier ;
21
+ sh:node ex:E42Identifier01Shape ;
22
+ sh:name "ID indagine" ;
23
+ sh:maxCount 1 ;
24
+ sh:minCount 1 ;
25
+ ] ;
26
+ sh:property [
27
+ sh:path crm:P17_was_motivated_by ;
28
+ sh:node ex:I12AdoptedBeliefShape ;
29
+ sh:name "Quesito diagnostico" ;
30
+ sh:maxCount 1 ;
31
+ sh:minCount 1 ;
32
+ ] ;
33
+ sh:property [
34
+ sh:path crm:P14_carried_out_by ;
35
+ sh:node ex:E29ActorShape ;
36
+ sh:name "Attori" ;
37
+ sh:maxCount 1 ;
38
+ ] ;
39
+ sh:property [
40
+ sh:path crm:P16_used_specific_object ;
41
+ sh:node ex:S13SampleShape ;
42
+ sh:name "Campione" ;
43
+ sh:maxCount 1 ;
44
+ ] .
45
+
46
+ ex:E29ActorShape # ATTORI
47
+ a sh:NodeShape, rdfs:Class ;
48
+ sh:targetClass crm:E29_Actor ;
49
+ sh:property [
50
+ sh:path crm:P2_has_type ;
51
+ sh:node ex:E55Type03Shape ;
52
+ sh:name "Attori coinvolti" ;
53
+ sh:maxCount 1 ;
54
+ ] .
55
+
56
+ ex:S13SampleShape # CAMPIONE
57
+ a sh:NodeShape, rdfs:Class ;
58
+ sh:targetClass crm:S13_Sample ;
59
+ sh:property [
60
+ sh:path crm:P2_has_type ;
61
+ sh:node ex:E55Type05Shape ;
62
+ sh:name "Tipo campione" ;
63
+ sh:maxCount 1 ;
64
+ sh:minCount 1 ;
65
+ ] ;
66
+ sh:property [
67
+ sh:path basecpm:Pc43i_uses ;
68
+ sh:node ex:CP2ArchitectureWorkShape ;
69
+ sh:name "Bene di riferimento" ;
70
+ sh:maxCount 1 ;
71
+ ] .
72
+
73
+ ex:CP2ArchitectureWorkShape # BENE DI RIFERIMENTO
74
+ a sh:NodeShape, rdfs:Class ;
75
+ sh:targetClass basecpm:CP2_Architecture_Work ;
76
+ sh:property [
77
+ sh:path crm:P1_is_identified_by ;
78
+ sh:node ex:E41Appellation02Shape ;
79
+ sh:name "Denominazione bene" ;
80
+ sh:maxCount 1 ;
81
+ sh:minCount 1 ;
82
+ ] .
83
+
84
+ ex:E42Identifier01Shape #ID INDAGINE
85
+ a sh:NodeShape, rdfs:Class ;
86
+ sh:targetClass crm:E42_Identifier ;
87
+ sh:property [
88
+ sh:path ex:P48haspreferredidentifier01 ;
89
+ #sh:datatype xsd:string ;
90
+ sh:name "ID" ;
91
+ sh:description "http://indagine/$SEQ1$" ;
92
+ sh:nodeKind sh:IRI ;
93
+ sh:maxCount 1 ;
94
+ sh:minCount 1 ;
95
+ ] .
96
+
97
+ ex:I12AdoptedBeliefShape # QUESITO DIAGNOSTICO
98
+ a sh:NodeShape, rdfs:Class ;
99
+ sh:targetClass base:I12_Adopted_Belief ;
100
+ sh:property [
101
+ sh:path crm:P2_has_type ;
102
+ sh:node ex:E55Type02Shape ;
103
+ sh:name "Tipo quesito diagnostico" ;
104
+ sh:maxCount 1 ;
105
+ sh:minCount 1 ;
106
+ ] .
107
+
108
+ ex:E41Appellation01Shape # ENTE E SCHEDATORE
109
+ a sh:NodeShape, rdfs:Class ;
110
+ sh:targetClass crm:E41_Appellation ;
111
+ sh:property [
112
+ sh:path ex:ente_richiedente ;
113
+ sh:datatype xsd:string ;
114
+ sh:name "Ente richiedente" ;
115
+ sh:maxCount 1 ;
116
+ ] ;
117
+ sh:property [
118
+ sh:path ex:schedatore ;
119
+ sh:datatype xsd:string ;
120
+ sh:name "Schedatore" ;
121
+ sh:maxCount 1 ;
122
+ sh:minCount 1 ;
123
+ ] .
124
+
125
+ ex:E41Appellation02Shape # DENOMINAZIONE BENE
126
+ a sh:NodeShape, rdfs:Class ;
127
+ sh:targetClass crm:E41_Appellation ;
128
+ sh:property [
129
+ sh:path ex:P1isidentifiedby ;
130
+ sh:datatype xsd:string ;
131
+ sh:name "Denominazione" ;
132
+ sh:maxCount 1 ;
133
+ sh:minCount 1 ;
134
+ ] .
135
+
136
+ ex:E55Type02Shape # TIPO QUESITO DIAGNOSTICO
137
+ a sh:NodeShape, rdfs:Class ;
138
+ sh:targetClass crm:E55_Type ;
139
+ sh:property [
140
+ sh:path ex:P2hastype02 ;
141
+ sh:class owl:Class ; # vocabolario QUESITO DIAGNOSTICO (8) id="quesito-diagnostico"
142
+ owl:imports <OAC_EXPOSED_PROTOCOL://OAC_EXPOSED_HOST:OAC_EXPOSED_PORT/backend/fuseki/get-vocabolary-terms/quesito-diagnostico> ;
143
+ sh:name "Tipo" ;
144
+ sh:maxCount 1 ;
145
+ sh:minCount 1 ;
146
+ ] ;
147
+ sh:property [
148
+ sh:path crm:P3_has_note ;
149
+ sh:datatype xsd:string ;
150
+ sh:name "Altro" ;
151
+ sh:maxCount 1 ;
152
+ ] .
153
+
154
+ ex:E55Type03Shape # TIPOLOGIA ATTORI COINVOLTI
155
+ a sh:NodeShape, rdfs:Class ;
156
+ sh:targetClass crm:E55_Type ;
157
+ sh:property [
158
+ sh:path crm:P1_is_defined_by ;
159
+ sh:node ex:E41Appellation01Shape ;
160
+ sh:name "Tipologia attori" ;
161
+ #sh:maxCount 1 ;
162
+ ] .
163
+
164
+
165
+ ex:E55Type05Shape # TIPO CAMPIONE
166
+ a sh:NodeShape, rdfs:Class ;
167
+ sh:targetClass crm:E55_Type ;
168
+ sh:property [
169
+ sh:path ex:P2hastype05 ;
170
+ sh:class owl:Class ; # vocabolario TIPO CAMPIONE (21) id="tipo-campione
171
+ owl:imports <OAC_EXPOSED_PROTOCOL://OAC_EXPOSED_HOST:OAC_EXPOSED_PORT/backend/fuseki/get-vocabolary-terms/tipo-campione> ;
172
+ sh:name "Tipo" ;
173
+ sh:maxCount 1 ;
174
+ sh:minCount 1 ;
175
+ ] .
@@ -0,0 +1,136 @@
1
+ @prefix sh: <http://www.w3.org/ns/shacl#> .
2
+ @prefix ex: <http://example.org/shapes/> .
3
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
4
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
5
+ @prefix crm: <http://www.cidoc-crm.org/cidoc-crm/> .
6
+ @prefix owl: <http://www.w3.org/2002/07/owl#> .
7
+ @prefix dcterms: <http://purl.org/dc/terms/> .
8
+
9
+ ex:E7ActivityShape
10
+ a sh:NodeShape ;
11
+ sh:targetClass crm:E7_Activity ;
12
+ #rdfs:label "Indagine" ;
13
+ #sh:description "Indagine" ;
14
+ #sh:group ex:NameGroup ;
15
+ sh:property [
16
+ sh:path crm:P48_has_preferred_identifier ;
17
+ sh:node ex:E42IdentifierShape ;
18
+ sh:name "ID indagine" ;
19
+ sh:maxCount 1 ;
20
+ sh:minCount 1 ;
21
+ ] ;
22
+ sh:property [
23
+ sh:path crm:P2_has_type ;
24
+ sh:node ex:E55Type01Shape ;
25
+ sh:name "Tipo indagine" ;
26
+ sh:maxCount 1 ;
27
+ sh:minCount 1 ;
28
+ ] ;
29
+ sh:property [
30
+ sh:path crm:P17_was_motivated_by ;
31
+ sh:node ex:I12AdoptedBeliefShape ;
32
+ sh:name "Quesito diagnostico" ;
33
+ sh:maxCount 1 ;
34
+ sh:minCount 1 ;
35
+ ] ;
36
+ sh:property [
37
+ sh:path crm:P14_was_carried_out_by ;
38
+ sh:node ex:E29ActorShape ;
39
+ sh:name "Attori" ;
40
+ #sh:maxCount 1 ;
41
+ ] ;
42
+ sh:property [
43
+ sh:path crm:P16_used_specific_object ;
44
+ sh:node ex:S13Sample ;
45
+ sh:name "Campione" ;
46
+ sh:maxCount 1 ;
47
+ ] ;
48
+ sh:property [
49
+ sh:path crm:xxx ;
50
+ sh:node ex:E7Activity1 ;
51
+ sh:name "Attività dignostica" ;
52
+ #sh:maxCount 1 ;
53
+ ] .
54
+
55
+ ex:E42IdentifierShape
56
+ a sh:NodeShape ;
57
+ sh:targetClass crm:E42_Identifier ;
58
+ sh:property [
59
+ sh:path ex:P48_has_preferred_identifier ;
60
+ sh:datatype xsd:string ;
61
+ sh:name "ID" ;
62
+ sh:nodeKind sh:IRI;
63
+ sh:description "http://diagnostica/indagine/$UUID$" ;
64
+ sh:maxCount 1 ;
65
+ sh:minCount 1 ;
66
+ ] .
67
+
68
+ ex:E55Type01Shape
69
+ a sh:NodeShape ;
70
+ sh:targetClass crm:E55_Type ;
71
+ sh:property [
72
+ sh:class owl:Class ;
73
+ sh:path crm:P127_has_broader_term ;
74
+ owl:imports <OAC_EXPOSED_PROTOCOL://OAC_EXPOSED_HOST:OAC_EXPOSED_PORT/backend/fuseki/get-vocabolary-terms/fase-di-analisi> ;
75
+ sh:name "Tipo" ;
76
+ sh:maxCount 1 ;
77
+ sh:minCount 1 ;
78
+ ] .
79
+
80
+ ex:I12AdoptedBeliefShape
81
+ a sh:NodeShape ;
82
+ sh:targetClass crm:I12_Adopted_Belief ;
83
+ sh:property [
84
+ sh:path crm:P2_has_type ;
85
+ sh:node ex:E55Type02Shape ;
86
+ sh:name "Tipo quesito diagnostico" ;
87
+ sh:maxCount 1 ;
88
+ sh:minCount 1 ;
89
+ ] .
90
+
91
+ ex:E55Type02Shape
92
+ a sh:NodeShape ;
93
+ sh:targetClass crm:E55_Type ;
94
+ sh:property [
95
+ sh:name "Tipo" ;
96
+ sh:maxCount 1 ;
97
+ sh:minCount 1 ;
98
+ #sh:class crm:E55_Type ;
99
+ sh:class owl:Class ;
100
+ sh:path crm:P127_has_broader_term ;
101
+ owl:imports <OAC_EXPOSED_PROTOCOL://OAC_EXPOSED_HOST:OAC_EXPOSED_PORT/backend/fuseki/get-vocabolary-terms/quesito-diagnostico> ;
102
+
103
+ #sh:class owl:Class ;
104
+ #sh:path dcterms:subject ;
105
+ #owl:imports <https://raw.githubusercontent.com/tibonto/DFG-Fachsystematik-Ontology/refs/heads/main/dfgfo.ttl> ;
106
+
107
+ ] ;
108
+ sh:property [
109
+ sh:path crm:P3_has_note ;
110
+ sh:datatype xsd:string ;
111
+ sh:name "Altro" ;
112
+ sh:maxCount 1 ;
113
+ ] .
114
+
115
+ ex:E29ActorShape
116
+ a sh:NodeShape ;
117
+ sh:targetClass crm:E29_Actor ;
118
+ sh:property [
119
+ sh:path ex:ente_richiedente ;
120
+ sh:datatype xsd:string ;
121
+
122
+ #sh:name "Ente richiedente" ;
123
+ #sh:maxCount 1 ;
124
+
125
+ sh:name "ID" ;
126
+ sh:nodeKind sh:IRI;
127
+ sh:description "http://diagnostica/actor/$SEQ3$" ;
128
+
129
+ ] ;
130
+ sh:property [
131
+ sh:path ex:schedatore ;
132
+ sh:datatype xsd:string ;
133
+ sh:name "Schedatore" ;
134
+ sh:maxCount 1 ;
135
+ sh:minCount 1 ;
136
+ ] .