openehr 2.0.2 → 2.3.0
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.
- checksums.yaml +4 -4
- data/README.rdoc +19 -6
- data/lib/openehr/am/archetype/constraint_model.rb +1 -1
- data/lib/openehr/aql/engine/contains_resolver.rb +98 -7
- data/lib/openehr/aql/engine/dataset.rb +3 -1
- data/lib/openehr/aql/engine/predicate_evaluator.rb +70 -3
- data/lib/openehr/aql/engine.rb +45 -12
- data/lib/openehr/assumed_library_types.rb +20 -17
- data/lib/openehr/parser/adl_parser.rb +0 -4
- data/lib/openehr/parser/opt_parser.rb +12 -429
- data/lib/openehr/parser/xml_archetype_parser.rb +255 -0
- data/lib/openehr/parser/xml_constraint_parsing.rb +264 -0
- data/lib/openehr/parser/xml_domain_type_parsing.rb +128 -0
- data/lib/openehr/parser/xml_primitive_parsing.rb +145 -0
- data/lib/openehr/parser.rb +1 -0
- data/lib/openehr/rm/common/generic.rb +6 -9
- data/lib/openehr/rm/common/resource.rb +0 -1
- data/lib/openehr/rm/composition/content/entry.rb +4 -1
- data/lib/openehr/rm/data_structures/item_structure/representation.rb +12 -2
- data/lib/openehr/rm/data_structures/item_structure.rb +0 -1
- data/lib/openehr/rm/data_types/basic.rb +11 -21
- data/lib/openehr/rm/data_types/encapsulated.rb +18 -2
- data/lib/openehr/rm/data_types/quantity/date_time.rb +203 -28
- data/lib/openehr/rm/data_types/quantity.rb +66 -10
- data/lib/openehr/rm/data_types/time_specification.rb +101 -27
- data/lib/openehr/rm/data_types/uri.rb +0 -8
- data/lib/openehr/rm/ehr.rb +6 -6
- data/lib/openehr/rm/factory.rb +22 -0
- data/lib/openehr/rm/security.rb +3 -1
- data/lib/openehr/rm/type_name.rb +0 -1
- data/lib/openehr/serializer/adl_serializer.rb +239 -84
- data/lib/openehr/serializer/base.rb +2 -0
- data/lib/openehr/serializer/rm_json_serializer.rb +0 -1
- data/lib/openehr/serializer/xml_serializer.rb +351 -103
- data/lib/openehr/version.rb +1 -1
- metadata +9 -144
|
@@ -1,66 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative 'base'
|
|
2
4
|
|
|
3
5
|
module OpenEHR
|
|
4
6
|
module Serializer
|
|
5
7
|
class ADLSerializer < BaseSerializer
|
|
6
8
|
def header
|
|
7
|
-
hd = 'archetype'
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
hd << NL+INDENT
|
|
12
|
-
hd << '
|
|
13
|
-
hd << NL+'language'+NL+INDENT+'original_language = <['+
|
|
9
|
+
hd = +'archetype'
|
|
10
|
+
hd << archetype_meta_data_clause
|
|
11
|
+
hd << (NL+INDENT + "#{@archetype.archetype_id.value}"+NL)
|
|
12
|
+
hd << specialise_section
|
|
13
|
+
hd << (NL+'concept'+NL+ INDENT+"[#{@archetype.concept}]"+NL)
|
|
14
|
+
hd << (NL+'language'+NL+INDENT+'original_language = <['+
|
|
14
15
|
@archetype.original_language.terminology_id.value+'::'+
|
|
15
|
-
@archetype.original_language.code_string+']>'+NL
|
|
16
|
+
@archetype.original_language.code_string+']>'+NL)
|
|
17
|
+
hd << translations_block(@archetype.translations)
|
|
16
18
|
return hd
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
def description
|
|
20
|
-
desc = ''
|
|
22
|
+
desc = +''
|
|
21
23
|
if @archetype.description
|
|
22
24
|
ad = @archetype.description
|
|
23
|
-
desc << 'description' + NL
|
|
24
|
-
desc << INDENT + 'original_author = <' + NL
|
|
25
|
+
desc << ('description' + NL)
|
|
26
|
+
desc << (INDENT + 'original_author = <' + NL)
|
|
25
27
|
ad.original_author.each do |k,v|
|
|
26
|
-
desc << INDENT+INDENT+'["'+k+'"] = <"'+v+'">'+NL
|
|
28
|
+
desc << (INDENT+INDENT+'["'+k+'"] = <"'+v+'">'+NL)
|
|
27
29
|
end
|
|
28
|
-
desc << INDENT+'>'+NL
|
|
29
|
-
desc << INDENT+'lifecycle_state = <"'+ad.lifecycle_state+'">'+NL
|
|
30
|
-
desc << INDENT+'details = <'+NL
|
|
30
|
+
desc << (INDENT+'>'+NL)
|
|
31
|
+
desc << (INDENT+'lifecycle_state = <"'+ad.lifecycle_state+'">'+NL)
|
|
32
|
+
desc << (INDENT+'details = <'+NL)
|
|
31
33
|
ad.details.each do |lang,item|
|
|
32
|
-
desc <<
|
|
33
|
-
desc << INDENT*3+'language = <['+
|
|
34
|
-
item.language.terminology_id.value+'::'+
|
|
35
|
-
item.language.code_string+']>'+NL
|
|
36
|
-
desc << INDENT*3+'purpose = <"'+item.purpose+'">'+NL
|
|
37
|
-
if item.keywords then
|
|
38
|
-
desc << INDENT*3+'keywords = <'
|
|
39
|
-
item.keywords.each do |word|
|
|
40
|
-
desc << '"'+word+'",'
|
|
41
|
-
end
|
|
42
|
-
desc.chop! << '>'+NL
|
|
43
|
-
end
|
|
44
|
-
desc << INDENT*3+'use = <"'+item.use+'">'+NL if item.use
|
|
45
|
-
desc << INDENT*3+'misuse = <"'+item.misuse+'">'+NL if item.misuse
|
|
46
|
-
desc << INDENT*3+'copyright = <"'+item.copyright+'">'+NL if item.copyright
|
|
47
|
-
if item.original_resource_uri
|
|
48
|
-
desc << INDENT*3 + 'original_resource_uri = <'
|
|
49
|
-
item.original_resource_uri.each do |k,v|
|
|
50
|
-
desc << INDENT*4+'["'+k+'"] = <"'+v+'">'+NL
|
|
51
|
-
end
|
|
52
|
-
desc << INDENT*3+'>'+NL
|
|
53
|
-
end
|
|
54
|
-
if item.other_details
|
|
55
|
-
desc << INDENT*3 + 'other_details = <'
|
|
56
|
-
item.other_details.each do |k,v|
|
|
57
|
-
desc << INDENT*4+'["'+k+'"] = <"'+v+'">'+NL
|
|
58
|
-
end
|
|
59
|
-
desc << INDENT*3+'>'+NL
|
|
60
|
-
end
|
|
61
|
-
desc << INDENT*2+'>'+NL
|
|
34
|
+
desc << description_details_item(lang, item)
|
|
62
35
|
end
|
|
63
|
-
desc << INDENT+'>'+NL
|
|
36
|
+
desc << (INDENT+'>'+NL)
|
|
64
37
|
end
|
|
65
38
|
return desc
|
|
66
39
|
end
|
|
@@ -72,29 +45,18 @@ module OpenEHR
|
|
|
72
45
|
def ontology
|
|
73
46
|
ao = @archetype.ontology
|
|
74
47
|
ontology = 'ontology'+NL
|
|
48
|
+
ontology << primary_language_line(ao.primary_language)
|
|
75
49
|
ontology << string_list_line('languages_available', ao.languages_available)
|
|
76
50
|
ontology << string_list_line('terminologies_available', ao.terminologies_available)
|
|
77
|
-
ontology <<
|
|
78
|
-
ao.
|
|
79
|
-
ontology << INDENT*2 + "[\"#{lang}\"] = <" + NL
|
|
80
|
-
ontology << INDENT*3 + 'items = <' + NL
|
|
81
|
-
items.each do |code, item|
|
|
82
|
-
ontology << INDENT*4 + "[\"#{code}\"] = <" + NL
|
|
83
|
-
item.items.each do |name, desc|
|
|
84
|
-
ontology << INDENT*5 + "#{name} = <\"#{desc}\">" + NL
|
|
85
|
-
end
|
|
86
|
-
ontology << INDENT*4 + '>'+NL
|
|
87
|
-
end
|
|
88
|
-
ontology << INDENT*3 + '>' + NL
|
|
89
|
-
ontology << INDENT*2 + '>' + NL
|
|
90
|
-
end
|
|
91
|
-
ontology << INDENT + '>' + NL
|
|
51
|
+
ontology << term_definitions_block(ao.term_definitions)
|
|
52
|
+
ontology << term_definitions_block(ao.constraint_definitions, 'constraint_definitions') if ao.constraint_definitions
|
|
92
53
|
ontology << term_bindings_block(ao.term_bindings)
|
|
54
|
+
ontology << constraint_bindings_block(ao.constraint_bindings)
|
|
93
55
|
ontology
|
|
94
56
|
end
|
|
95
57
|
|
|
96
58
|
def merge
|
|
97
|
-
return header + NL + description + NL + definition + NL + ontology
|
|
59
|
+
return header + NL + description + NL + definition + NL + invariant_section + ontology
|
|
98
60
|
end
|
|
99
61
|
|
|
100
62
|
include OpenEHR::AM::Archetype::ConstraintModel
|
|
@@ -109,26 +71,154 @@ module OpenEHR
|
|
|
109
71
|
INDENT + "#{keyword} = <" + values.map { |v| "\"#{v}\"" }.join(', ') + '>' + NL
|
|
110
72
|
end
|
|
111
73
|
|
|
74
|
+
# archetype (adl_version = ...; uid = ...) - a ';'-separated
|
|
75
|
+
# metadata clause. Empty when neither is present, so callers that
|
|
76
|
+
# never set uid see byte-identical output to before uid support.
|
|
77
|
+
def archetype_meta_data_clause
|
|
78
|
+
items = []
|
|
79
|
+
items << "adl_version = #{@archetype.adl_version}" if @archetype.adl_version
|
|
80
|
+
items << "uid = #{@archetype.uid.value}" if @archetype.uid
|
|
81
|
+
return '' if items.empty?
|
|
82
|
+
|
|
83
|
+
" (#{items.join('; ')})"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def specialise_section
|
|
87
|
+
return '' if @archetype.parent_archetype_id.nil?
|
|
88
|
+
|
|
89
|
+
'specialise' + NL + INDENT + @archetype.parent_archetype_id.value + NL
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def translations_block(translations)
|
|
93
|
+
return '' if translations.nil? || translations.empty?
|
|
94
|
+
|
|
95
|
+
block = INDENT + 'translations = <' + NL
|
|
96
|
+
translations.each { |code, details| block << translation_item_block(code, details) }
|
|
97
|
+
block << (INDENT + '>' + NL)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def translation_item_block(code, details)
|
|
101
|
+
block = (INDENT*2) + "[\"#{code}\"] = <" + NL
|
|
102
|
+
block << ((INDENT*3) + "language = <[#{details.language.terminology_id.value}::#{details.language.code_string}]>" + NL)
|
|
103
|
+
block << keyed_map_block('author', details.author) if details.author
|
|
104
|
+
block << ((INDENT*3) + "accreditation = <\"#{details.accreditation}\">" + NL) if details.accreditation
|
|
105
|
+
block << keyed_map_block('other_details', details.other_details) if details.other_details
|
|
106
|
+
block << ((INDENT*2) + '>' + NL)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def invariant_section
|
|
110
|
+
invariants = @archetype.invariants
|
|
111
|
+
return '' if invariants.nil? || invariants.empty?
|
|
112
|
+
|
|
113
|
+
block = 'invariant' + NL
|
|
114
|
+
invariants.each { |assertion| block << (INDENT + assertion.string_expression + NL) }
|
|
115
|
+
block << NL
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def primary_language_line(primary_language)
|
|
119
|
+
return '' if primary_language.nil?
|
|
120
|
+
|
|
121
|
+
INDENT + "primary_language = <\"#{primary_language}\">" + NL
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def description_details_item(lang, item)
|
|
125
|
+
block = (INDENT*2)+'["'+lang+'"] = <'+NL
|
|
126
|
+
block << description_language_line(item.language)
|
|
127
|
+
block << ((INDENT*3)+'purpose = <"'+item.purpose+'">'+NL)
|
|
128
|
+
block << description_keywords_line(item) if item.keywords
|
|
129
|
+
block << optional_field_line('use', item.use)
|
|
130
|
+
block << optional_field_line('misuse', item.misuse)
|
|
131
|
+
block << optional_field_line('copyright', item.copyright)
|
|
132
|
+
block << keyed_map_block('original_resource_uri', item.original_resource_uri) if item.original_resource_uri
|
|
133
|
+
block << keyed_map_block('other_details', item.other_details) if item.other_details
|
|
134
|
+
block << ((INDENT*2)+'>'+NL)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def description_language_line(language)
|
|
138
|
+
(INDENT*3)+'language = <['+
|
|
139
|
+
language.terminology_id.value+'::'+
|
|
140
|
+
language.code_string+']>'+NL
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def optional_field_line(keyword, value)
|
|
144
|
+
return '' if value.nil?
|
|
145
|
+
|
|
146
|
+
(INDENT*3)+"#{keyword} = <\"#{value}\">"+NL
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def description_keywords_line(item)
|
|
150
|
+
block = (INDENT*3)+'keywords = <'
|
|
151
|
+
item.keywords.each do |word|
|
|
152
|
+
block << ('"'+word+'",')
|
|
153
|
+
end
|
|
154
|
+
block.chop! << ('>'+NL)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def keyed_map_block(keyword, hash)
|
|
158
|
+
block = (INDENT*3) + "#{keyword} = <"
|
|
159
|
+
hash.each do |k,v|
|
|
160
|
+
block << ((INDENT*4)+'["'+k+'"] = <"'+v+'">'+NL)
|
|
161
|
+
end
|
|
162
|
+
block << ((INDENT*3)+'>'+NL)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def term_definitions_block(term_definitions, keyword = 'term_definitions')
|
|
166
|
+
block = INDENT + "#{keyword} = <" + NL
|
|
167
|
+
term_definitions.each do |lang, items|
|
|
168
|
+
block << ((INDENT*2) + "[\"#{lang}\"] = <" + NL)
|
|
169
|
+
block << ((INDENT*3) + 'items = <' + NL)
|
|
170
|
+
items.each do |code, item|
|
|
171
|
+
block << ((INDENT*4) + "[\"#{code}\"] = <" + NL)
|
|
172
|
+
item.items.each do |name, desc|
|
|
173
|
+
block << ((INDENT*5) + "#{name} = <\"#{desc}\">" + NL)
|
|
174
|
+
end
|
|
175
|
+
block << ((INDENT*4) + '>'+NL)
|
|
176
|
+
end
|
|
177
|
+
block << ((INDENT*3) + '>' + NL)
|
|
178
|
+
block << ((INDENT*2) + '>' + NL)
|
|
179
|
+
end
|
|
180
|
+
block << (INDENT + '>' + NL)
|
|
181
|
+
end
|
|
182
|
+
|
|
112
183
|
def term_bindings_block(term_bindings)
|
|
113
184
|
return '' if term_bindings.nil? || term_bindings.empty?
|
|
114
185
|
|
|
115
186
|
block = INDENT + 'term_bindings = <' + NL
|
|
116
187
|
term_bindings.each do |terminology, codes|
|
|
117
|
-
block << INDENT*2 + "[\"#{terminology}\"] = <" + NL
|
|
118
|
-
block << INDENT*3 + 'items = <' + NL
|
|
188
|
+
block << ((INDENT*2) + "[\"#{terminology}\"] = <" + NL)
|
|
189
|
+
block << ((INDENT*3) + 'items = <' + NL)
|
|
119
190
|
codes.each do |code, bindings|
|
|
120
191
|
code_phrase = Array(bindings).first
|
|
121
|
-
block << INDENT*4 + "[\"#{code}\"] = <[#{code_phrase.terminology_id.value}::#{code_phrase.code_string}]>" + NL
|
|
192
|
+
block << ((INDENT*4) + "[\"#{code}\"] = <[#{code_phrase.terminology_id.value}::#{code_phrase.code_string}]>" + NL)
|
|
122
193
|
end
|
|
123
|
-
block << INDENT*3 + '>' + NL
|
|
124
|
-
block << INDENT*2 + '>' + NL
|
|
194
|
+
block << ((INDENT*3) + '>' + NL)
|
|
195
|
+
block << ((INDENT*2) + '>' + NL)
|
|
125
196
|
end
|
|
126
|
-
block << INDENT + '>' + NL
|
|
197
|
+
block << (INDENT + '>' + NL)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# constraint_bindings mirrors term_bindings' 3-level nesting
|
|
201
|
+
# (terminology -> items -> code), but each value is a bare/unquoted
|
|
202
|
+
# URI (a DvUri) rather than a qualified term code reference.
|
|
203
|
+
def constraint_bindings_block(constraint_bindings)
|
|
204
|
+
return '' if constraint_bindings.nil? || constraint_bindings.empty?
|
|
205
|
+
|
|
206
|
+
block = INDENT + 'constraint_bindings = <' + NL
|
|
207
|
+
constraint_bindings.each do |terminology, codes|
|
|
208
|
+
block << ((INDENT*2) + "[\"#{terminology}\"] = <" + NL)
|
|
209
|
+
block << ((INDENT*3) + 'items = <' + NL)
|
|
210
|
+
codes.each do |code, uri|
|
|
211
|
+
block << ((INDENT*4) + "[\"#{code}\"] = <#{uri.value}>" + NL)
|
|
212
|
+
end
|
|
213
|
+
block << ((INDENT*3) + '>' + NL)
|
|
214
|
+
block << ((INDENT*2) + '>' + NL)
|
|
215
|
+
end
|
|
216
|
+
block << (INDENT + '>' + NL)
|
|
127
217
|
end
|
|
128
218
|
|
|
129
219
|
# Recursive cADL emitter for a single C_OBJECT node, dispatching
|
|
130
|
-
# on its concrete class.
|
|
131
|
-
#
|
|
220
|
+
# on its concrete class. Domain types with no ADL 1.4 grammar rule
|
|
221
|
+
# (C_DV_SCALE, C_DV_STATE - both RM 1.1.0+ additions) deliberately
|
|
132
222
|
# raise rather than emit a guess at their syntax.
|
|
133
223
|
def emit_c_object(node, depth)
|
|
134
224
|
case node
|
|
@@ -140,6 +230,14 @@ module OpenEHR
|
|
|
140
230
|
emit_constraint_ref(node, depth)
|
|
141
231
|
when CComplexObject
|
|
142
232
|
emit_complex_object(node, depth)
|
|
233
|
+
when OpenEHRProfile::DataTypes::Quantity::CDvScale
|
|
234
|
+
raise ArgumentError,
|
|
235
|
+
'ADLSerializer cannot emit C_DV_SCALE in cADL - the ADL 1.4 grammar has no C_DV_SCALE rule ' \
|
|
236
|
+
'(RM 1.1.0 addition); use the XML serializer instead'
|
|
237
|
+
when OpenEHRProfile::DataTypes::Basic::CDvState
|
|
238
|
+
raise ArgumentError,
|
|
239
|
+
'ADLSerializer cannot emit C_DV_STATE in cADL - the ADL 1.4 grammar has no C_DV_STATE rule; ' \
|
|
240
|
+
'use the XML serializer instead'
|
|
143
241
|
else
|
|
144
242
|
raise ArgumentError, "ADLSerializer cannot emit a #{node.class} node"
|
|
145
243
|
end
|
|
@@ -151,12 +249,12 @@ module OpenEHR
|
|
|
151
249
|
head + ' matches {*}' + NL
|
|
152
250
|
else
|
|
153
251
|
body = node.attributes.map { |a| emit_c_attribute(a, depth + 1) }.join
|
|
154
|
-
head + ' matches {' + NL + body + INDENT*depth + '}' + NL
|
|
252
|
+
head + ' matches {' + NL + body + (INDENT*depth) + '}' + NL
|
|
155
253
|
end
|
|
156
254
|
end
|
|
157
255
|
|
|
158
256
|
def complex_object_head(node, depth)
|
|
159
|
-
head = INDENT*depth + node.rm_type_name
|
|
257
|
+
head = (INDENT*depth) + node.rm_type_name
|
|
160
258
|
head += "[#{node.node_id}]" if node.node_id
|
|
161
259
|
head += " occurrences matches {#{interval_literal(node.occurrences)}}" if show_interval?(node.occurrences)
|
|
162
260
|
head
|
|
@@ -174,15 +272,15 @@ module OpenEHR
|
|
|
174
272
|
head = attribute_head(attribute, depth)
|
|
175
273
|
children = attribute.children || []
|
|
176
274
|
if children.size == 1 && inline?(children.first)
|
|
177
|
-
head + " matches {#{inline_body(children.first)}}" + NL
|
|
275
|
+
head + " matches {#{inline_body(children.first, depth)}}" + NL
|
|
178
276
|
else
|
|
179
277
|
body = children.map { |c| emit_c_object(c, depth + 1) }.join
|
|
180
|
-
head + ' matches {' + NL + body + INDENT*depth + '}' + NL
|
|
278
|
+
head + ' matches {' + NL + body + (INDENT*depth) + '}' + NL
|
|
181
279
|
end
|
|
182
280
|
end
|
|
183
281
|
|
|
184
282
|
def attribute_head(attribute, depth)
|
|
185
|
-
head = INDENT*depth + attribute.rm_attribute_name
|
|
283
|
+
head = (INDENT*depth) + attribute.rm_attribute_name
|
|
186
284
|
head += " existence matches {#{interval_literal(attribute.existence)}}" if show_interval?(attribute.existence)
|
|
187
285
|
head += " cardinality matches {#{cardinality_literal(attribute.cardinality)}}" if attribute.is_a?(CMultipleAttribute)
|
|
188
286
|
head
|
|
@@ -191,10 +289,11 @@ module OpenEHR
|
|
|
191
289
|
def inline?(node)
|
|
192
290
|
node.is_a?(CPrimitiveObject) || node.is_a?(ConstraintRef) ||
|
|
193
291
|
node.is_a?(OpenEHRProfile::DataTypes::Quantity::CDvOrdinal) ||
|
|
194
|
-
node.is_a?(OpenEHRProfile::DataTypes::Text::CCodePhrase)
|
|
292
|
+
node.is_a?(OpenEHRProfile::DataTypes::Text::CCodePhrase) ||
|
|
293
|
+
node.is_a?(OpenEHRProfile::DataTypes::Quantity::CDvQuantity)
|
|
195
294
|
end
|
|
196
295
|
|
|
197
|
-
def inline_body(node)
|
|
296
|
+
def inline_body(node, depth)
|
|
198
297
|
case node
|
|
199
298
|
when CPrimitiveObject
|
|
200
299
|
primitive_body(node.item)
|
|
@@ -204,34 +303,81 @@ module OpenEHR
|
|
|
204
303
|
ordinal_body(node)
|
|
205
304
|
when OpenEHRProfile::DataTypes::Text::CCodePhrase
|
|
206
305
|
code_phrase_body(node)
|
|
306
|
+
when OpenEHRProfile::DataTypes::Quantity::CDvQuantity
|
|
307
|
+
cdv_quantity_body(node, depth)
|
|
207
308
|
end
|
|
208
309
|
end
|
|
209
310
|
|
|
311
|
+
# cADL syntax for C_DV_QUANTITY is a dADL-style block, not a short
|
|
312
|
+
# inline literal like the other inline? types above - see the
|
|
313
|
+
# adl_grammar.tt c_dv_quantity rule and CDvQuantityItems#value
|
|
314
|
+
# (adl_helper.rb) for the exact shape this mirrors: property is a
|
|
315
|
+
# qualified term code reference, list is an array of CQuantityItem
|
|
316
|
+
# keyed by 1-based position (the key itself isn't stored on
|
|
317
|
+
# CQuantityItem, so round-tripping only needs *a* stable key, not
|
|
318
|
+
# the original one), and assumed_value is a real DV_QUANTITY (plain
|
|
319
|
+
# magnitude/precision, not a range).
|
|
320
|
+
def cdv_quantity_body(node, depth)
|
|
321
|
+
return 'C_DV_QUANTITY < >' if node.any_allowed?
|
|
322
|
+
|
|
323
|
+
body = 'C_DV_QUANTITY <' + NL
|
|
324
|
+
body << quantity_property_line(node.property, depth + 1) if node.property
|
|
325
|
+
body << quantity_list_block(node.list, depth + 1) if node.list
|
|
326
|
+
body << assumed_quantity_block(node.assumed_value, depth + 1) if node.assumed_value
|
|
327
|
+
body << ((INDENT*depth) + '>')
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def quantity_property_line(property, depth)
|
|
331
|
+
(INDENT*depth) + "property = <[#{property.terminology_id.value}::#{property.code_string}]>" + NL
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def quantity_list_block(list, depth)
|
|
335
|
+
block = (INDENT*depth) + 'list = <' + NL
|
|
336
|
+
list.each_with_index { |item, index| block << quantity_item_block(item, index + 1, depth + 1) }
|
|
337
|
+
block << ((INDENT*depth) + '>' + NL)
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def quantity_item_block(item, key, depth)
|
|
341
|
+
block = (INDENT*depth) + "[\"#{key}\"] = <" + NL
|
|
342
|
+
block << ((INDENT*(depth+1)) + "units = <\"#{item.units}\">" + NL)
|
|
343
|
+
block << ((INDENT*(depth+1)) + "magnitude = <|#{range_literal(item.magnitude)}|>" + NL) if item.magnitude
|
|
344
|
+
block << ((INDENT*(depth+1)) + "precision = <|#{range_literal(item.precision)}|>" + NL) if item.precision
|
|
345
|
+
block << ((INDENT*depth) + '>' + NL)
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def assumed_quantity_block(assumed_value, depth)
|
|
349
|
+
block = (INDENT*depth) + 'assumed_value = <' + NL
|
|
350
|
+
block << ((INDENT*(depth+1)) + "units = <\"#{assumed_value.units}\">" + NL)
|
|
351
|
+
block << ((INDENT*(depth+1)) + "magnitude = <#{assumed_value.magnitude}>" + NL)
|
|
352
|
+
block << ((INDENT*(depth+1)) + "precision = <#{assumed_value.precision}>" + NL) unless assumed_value.precision.nil?
|
|
353
|
+
block << ((INDENT*depth) + '>' + NL)
|
|
354
|
+
end
|
|
355
|
+
|
|
210
356
|
def emit_archetype_slot(node, depth)
|
|
211
|
-
head = INDENT*depth + 'allow_archetype ' + node.rm_type_name
|
|
357
|
+
head = (INDENT*depth) + 'allow_archetype ' + node.rm_type_name
|
|
212
358
|
head += "[#{node.node_id}]" if node.node_id
|
|
213
359
|
head += " occurrences matches {#{interval_literal(node.occurrences)}}" if show_interval?(node.occurrences)
|
|
214
360
|
lines = [head + ' matches {' + NL]
|
|
215
361
|
lines << assertion_block(node.includes, 'include', depth + 1)
|
|
216
362
|
lines << assertion_block(node.excludes, 'exclude', depth + 1)
|
|
217
|
-
lines << INDENT*depth + '}' + NL
|
|
363
|
+
lines << ((INDENT*depth) + '}' + NL)
|
|
218
364
|
lines.join
|
|
219
365
|
end
|
|
220
366
|
|
|
221
367
|
def assertion_block(assertions, keyword, depth)
|
|
222
368
|
return '' if assertions.nil?
|
|
223
369
|
|
|
224
|
-
lines = [INDENT*depth + keyword + NL]
|
|
225
|
-
assertions.each { |a| lines << INDENT*(depth+1) + a.string_expression + NL }
|
|
370
|
+
lines = [(INDENT*depth) + keyword + NL]
|
|
371
|
+
assertions.each { |a| lines << ((INDENT*(depth+1)) + a.string_expression + NL) }
|
|
226
372
|
lines.join
|
|
227
373
|
end
|
|
228
374
|
|
|
229
375
|
def emit_internal_ref(node, depth)
|
|
230
|
-
INDENT*depth + "use_node #{node.rm_type_name} #{node.target_path}" + NL
|
|
376
|
+
(INDENT*depth) + "use_node #{node.rm_type_name} #{node.target_path}" + NL
|
|
231
377
|
end
|
|
232
378
|
|
|
233
379
|
def emit_constraint_ref(node, depth)
|
|
234
|
-
INDENT*depth + "[#{node.reference}]" + NL
|
|
380
|
+
(INDENT*depth) + "[#{node.reference}]" + NL
|
|
235
381
|
end
|
|
236
382
|
|
|
237
383
|
def primitive_body(item)
|
|
@@ -301,10 +447,19 @@ module OpenEHR
|
|
|
301
447
|
def ordinal_body(node)
|
|
302
448
|
return 'C_DV_ORDINAL < >' if node.any_allowed?
|
|
303
449
|
|
|
304
|
-
body = node.list.map { |o| "#{o.value}|[#{o.symbol
|
|
450
|
+
body = node.list.map { |o| "#{o.value}|[#{ordinal_symbol_reference(o.symbol)}]" }.join(', ')
|
|
305
451
|
with_assumed_value(body, node)
|
|
306
452
|
end
|
|
307
453
|
|
|
454
|
+
# The c_ordinal grammar rule builds symbol as a DV_CODED_TEXT
|
|
455
|
+
# (code lives at symbol.defining_code, not symbol itself) - a bare
|
|
456
|
+
# CODE_PHRASE-like symbol is also accepted, matching the dual
|
|
457
|
+
# shape CDvOrdinal#valid_value? already tolerates.
|
|
458
|
+
def ordinal_symbol_reference(symbol)
|
|
459
|
+
code_phrase = symbol.respond_to?(:defining_code) ? symbol.defining_code : symbol
|
|
460
|
+
"#{code_phrase.terminology_id.value}::#{code_phrase.code_string}"
|
|
461
|
+
end
|
|
462
|
+
|
|
308
463
|
def code_phrase_body(node)
|
|
309
464
|
return '*' if node.any_allowed?
|
|
310
465
|
|