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,12 +1,19 @@
|
|
|
1
1
|
require 'nokogiri'
|
|
2
|
+
require_relative 'xml_constraint_parsing'
|
|
3
|
+
require_relative 'xml_primitive_parsing'
|
|
4
|
+
require_relative 'xml_domain_type_parsing'
|
|
2
5
|
|
|
3
6
|
module OpenEHR
|
|
4
7
|
module Parser
|
|
5
8
|
class OPTParser < ::OpenEHR::Parser::Base
|
|
9
|
+
include XMLConstraintParsing
|
|
10
|
+
include XMLPrimitiveParsing
|
|
11
|
+
include XMLDomainTypeParsing
|
|
12
|
+
|
|
6
13
|
TEMPLATE_LANGUAGE_CODE_PATH =
|
|
7
14
|
'/template/language/code_string'
|
|
8
15
|
TEMPLATE_LANGUAGE_TERM_ID_PATH =
|
|
9
|
-
'/template/language/terminology_id/value'
|
|
16
|
+
'/template/language/terminology_id/value'
|
|
10
17
|
TEMPLATE_ID_PATH = '/template/template_id/value'
|
|
11
18
|
UID_PATH = '/template/uid/value'
|
|
12
19
|
CONCEPT_PATH = '/template/concept'
|
|
@@ -31,18 +38,14 @@ module OpenEHR
|
|
|
31
38
|
'/template/description/other_details'
|
|
32
39
|
DEFINITION_PATH = '/template/definition'
|
|
33
40
|
OCCURRENCE_PATH = '/occurrences'
|
|
34
|
-
|
|
35
|
-
def initialize(filename)
|
|
36
|
-
super(filename)
|
|
37
|
-
end
|
|
38
41
|
|
|
39
42
|
def parse
|
|
40
43
|
@opt = Nokogiri::XML::Document.parse(File.open(@filename))
|
|
41
44
|
@opt.remove_namespaces!
|
|
42
|
-
|
|
45
|
+
|
|
43
46
|
uid = build_uid
|
|
44
47
|
defs = definition
|
|
45
|
-
|
|
48
|
+
|
|
46
49
|
# Create operational template with archetype-compatible parameters
|
|
47
50
|
OpenEHR::AM::Template::OperationalTemplate.new(
|
|
48
51
|
uid: uid,
|
|
@@ -123,7 +126,7 @@ module OpenEHR
|
|
|
123
126
|
# Create a basic ontology for the template using the main concept
|
|
124
127
|
concept_code = 'at0000'
|
|
125
128
|
original_lang = language
|
|
126
|
-
|
|
129
|
+
|
|
127
130
|
term_definitions = {
|
|
128
131
|
original_lang.code_string => [
|
|
129
132
|
OpenEHR::AM::Archetype::Terminology::ArchetypeTerm.new(
|
|
@@ -135,7 +138,7 @@ module OpenEHR
|
|
|
135
138
|
)
|
|
136
139
|
]
|
|
137
140
|
}
|
|
138
|
-
|
|
141
|
+
|
|
139
142
|
OpenEHR::AM::Archetype::Terminology::ArchetypeTerminology.new(
|
|
140
143
|
concept_code: concept_code,
|
|
141
144
|
original_language: original_lang,
|
|
@@ -164,418 +167,6 @@ module OpenEHR
|
|
|
164
167
|
{ language.code_string => term_items }
|
|
165
168
|
end
|
|
166
169
|
|
|
167
|
-
def c_archetype_root(xml, node = Node.new)
|
|
168
|
-
rm_type_name = text_on_path(xml, './rm_type_name')
|
|
169
|
-
id = text_on_path(xml, './node_id')
|
|
170
|
-
node.id = id unless id.nil? or id.empty?
|
|
171
|
-
occurrences = occurrences(xml.xpath('./occurrences'))
|
|
172
|
-
archetype_id = OpenEHR::RM::Support::Identification::ArchetypeID.new(value: text_on_path(xml, './archetype_id/value'))
|
|
173
|
-
if node.root? or node.id.nil?
|
|
174
|
-
node.path = "/"
|
|
175
|
-
# else
|
|
176
|
-
# node.path += "/" #"/[#{archetype_id.value}]"
|
|
177
|
-
end
|
|
178
|
-
component_terminologies(archetype_id, xml)
|
|
179
|
-
OpenEHR::AM::Archetype::ConstraintModel::CArchetypeRoot.new(rm_type_name: rm_type_name, node_id: node.id, path: node.path, occurrences: occurrences, archetype_id: archetype_id, attributes: attributes(xml.xpath('./attributes'), node))
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
def c_complex_object(xml, node)
|
|
183
|
-
rm_type_name = xml.xpath('./rm_type_name').text
|
|
184
|
-
node_id = xml.xpath('./node_id').text
|
|
185
|
-
unless node_id.nil? or node_id.empty?
|
|
186
|
-
node.id = node_id
|
|
187
|
-
node.path = "#{node.path}[#{node.id}]"
|
|
188
|
-
end
|
|
189
|
-
OpenEHR::AM::Archetype::ConstraintModel::CComplexObject.new(rm_type_name: rm_type_name, node_id: node.id, path: node.path, occurrences: occurrences(xml.xpath('./occurrences')), attributes: attributes(xml.xpath('./attributes'), node))
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def attributes(attributes_xml, node)
|
|
193
|
-
attributes_xml.map do |attr|
|
|
194
|
-
rm_attribute_name = attr.at('rm_attribute_name').text
|
|
195
|
-
if node.root?
|
|
196
|
-
path = "/#{rm_attribute_name}"
|
|
197
|
-
# elsif node.id
|
|
198
|
-
# path = "#{node.path}[#{node.id}]/#{rm_attribute_name}"
|
|
199
|
-
else
|
|
200
|
-
path = "#{node.path}/#{rm_attribute_name}"
|
|
201
|
-
end
|
|
202
|
-
child_node = Node.new(node)
|
|
203
|
-
child_node.path = path
|
|
204
|
-
child_node.id = node.id
|
|
205
|
-
send attr.attributes['type'].text.downcase, attr, child_node
|
|
206
|
-
end
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
def children(children_xml, node)
|
|
210
|
-
children_xml.map do |child|
|
|
211
|
-
send child.attributes['type'].text.downcase, child, node
|
|
212
|
-
end
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
def c_single_attribute(attr_xml, node)
|
|
216
|
-
rm_attribute_name = attr_xml.at('rm_attribute_name').text
|
|
217
|
-
existence = occurrences(attr_xml.at('existence'))
|
|
218
|
-
OpenEHR::AM::Archetype::ConstraintModel::CSingleAttribute.new(rm_attribute_name: rm_attribute_name, existence: existence, path: node.path, children: children(attr_xml.xpath('./children'), node))
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
def c_multiple_attribute(attr_xml, node)
|
|
222
|
-
rm_attribute_name = attr_xml.at('rm_attribute_name').text
|
|
223
|
-
existence = occurrences(attr_xml.at('existence'))
|
|
224
|
-
OpenEHR::AM::Archetype::ConstraintModel::CMultipleAttribute.new(rm_attribute_name: rm_attribute_name, existence: existence, path: node.path, cardinality: cardinality(attr_xml), children: children(attr_xml.xpath('./children'), node))
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
def c_code_phrase(attr_xml, node)
|
|
228
|
-
terminology_id_node = attr_xml.at('terminology_id/value')
|
|
229
|
-
terminology_id = terminology_id_node ? OpenEHR::RM::Support::Identification::TerminologyID.new(value: terminology_id_node.text.strip) : nil
|
|
230
|
-
|
|
231
|
-
code_list_nodes = attr_xml.xpath('code_list')
|
|
232
|
-
code_list = code_list_nodes.map { |code_node| code_node.text.strip }
|
|
233
|
-
code_list = [code_list.first] if code_list.size == 1 && code_list.first.empty?
|
|
234
|
-
|
|
235
|
-
occurrences_node = attr_xml.at('occurrences')
|
|
236
|
-
occurrences_obj = occurrences_node ? occurrences(occurrences_node) : nil
|
|
237
|
-
|
|
238
|
-
OpenEHR::AM::OpenEHRProfile::DataTypes::Text::CCodePhrase.new(
|
|
239
|
-
terminology_id: terminology_id,
|
|
240
|
-
code_list: code_list,
|
|
241
|
-
path: node.path,
|
|
242
|
-
occurrences: occurrences_obj,
|
|
243
|
-
rm_type_name: 'CODE_PHRASE'
|
|
244
|
-
)
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
def archetype_slot(attr_xml,node)
|
|
248
|
-
path = node.path
|
|
249
|
-
node.id = attr_xml.at('node_id').text
|
|
250
|
-
rm_type_name = attr_xml.at('rm_type_name').text
|
|
251
|
-
occurrences = occurrences(attr_xml.at('occurrences'))
|
|
252
|
-
includes_leaf = attr_xml.at('includes')
|
|
253
|
-
includes = assertions(includes_leaf.children, node) if includes_leaf
|
|
254
|
-
excludes_leaf = attr_xml.at('excludes')
|
|
255
|
-
excludes = assertions(excludes_leaf.children, node) if excludes_leaf
|
|
256
|
-
OpenEHR::AM::Archetype::ConstraintModel::ArchetypeSlot.new(path: path, node_id: node.id, rm_type_name: rm_type_name, occurrences: occurrences, includes: includes, excludes: excludes)
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
def occurrences(occurrence_xml)
|
|
260
|
-
return nil if occurrence_xml.nil?
|
|
261
|
-
|
|
262
|
-
lower_node = occurrence_xml.at('lower')
|
|
263
|
-
upper_node = occurrence_xml.at('upper')
|
|
264
|
-
lower_included_node = occurrence_xml.at('lower_included')
|
|
265
|
-
upper_included_node = occurrence_xml.at('upper_included')
|
|
266
|
-
lower_unbounded_node = occurrence_xml.at('lower_unbounded')
|
|
267
|
-
upper_unbounded_node = occurrence_xml.at('upper_unbounded')
|
|
268
|
-
|
|
269
|
-
lower = lower_node ? lower_node.text.to_i : nil
|
|
270
|
-
upper = upper_node ? upper_node.text.to_i : nil
|
|
271
|
-
lower_included = lower_included_node ? to_bool(lower_included_node.text) : (lower.nil? ? nil : true)
|
|
272
|
-
upper_included = upper_included_node ? to_bool(upper_included_node.text) : (upper.nil? ? nil : true)
|
|
273
|
-
lower_unbounded = lower_unbounded_node ? to_bool(lower_unbounded_node.text) : false
|
|
274
|
-
upper_unbounded = upper_unbounded_node ? to_bool(upper_unbounded_node.text) : false
|
|
275
|
-
|
|
276
|
-
# An occurrences element with none of its children present carries no
|
|
277
|
-
# constraint at all; Interval requires at least one bound, so treat
|
|
278
|
-
# this as "no occurrences data" rather than raising.
|
|
279
|
-
return nil if lower.nil? && upper.nil? && !lower_unbounded && !upper_unbounded
|
|
280
|
-
|
|
281
|
-
# Handle unbounded intervals properly
|
|
282
|
-
if upper_unbounded || upper.nil?
|
|
283
|
-
upper = nil
|
|
284
|
-
upper_included = nil
|
|
285
|
-
end
|
|
286
|
-
|
|
287
|
-
if lower_unbounded || lower.nil?
|
|
288
|
-
lower = nil
|
|
289
|
-
lower_included = nil
|
|
290
|
-
end
|
|
291
|
-
|
|
292
|
-
OpenEHR::AssumedLibraryTypes::Interval.new(
|
|
293
|
-
lower: lower,
|
|
294
|
-
upper: upper,
|
|
295
|
-
lower_included: lower_included,
|
|
296
|
-
upper_included: upper_included
|
|
297
|
-
)
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
def cardinality(xml)
|
|
301
|
-
return nil if xml.nil?
|
|
302
|
-
|
|
303
|
-
order_node = xml.at('is_ordered')
|
|
304
|
-
unique_node = xml.at('is_unique')
|
|
305
|
-
interval_node = xml.at('interval')
|
|
306
|
-
|
|
307
|
-
# No cardinality sub-elements at all means no cardinality data.
|
|
308
|
-
return nil if order_node.nil? && unique_node.nil? && interval_node.nil?
|
|
309
|
-
|
|
310
|
-
order = order_node ? to_bool(order_node.text) : false
|
|
311
|
-
unique = unique_node ? to_bool(unique_node.text) : false
|
|
312
|
-
interval = interval_node ? occurrences(interval_node) : nil
|
|
313
|
-
|
|
314
|
-
OpenEHR::AM::Archetype::ConstraintModel::Cardinality.new(
|
|
315
|
-
is_ordered: order,
|
|
316
|
-
is_unique: unique,
|
|
317
|
-
interval: interval
|
|
318
|
-
)
|
|
319
|
-
end
|
|
320
|
-
|
|
321
|
-
def archetype_internal_ref(attr_xml, node)
|
|
322
|
-
rm_type_name = attr_xml.at('rm_type_name').text
|
|
323
|
-
target_path = attr_xml.at('target_path').text
|
|
324
|
-
occurrences = occurrences(attr_xml.at('occurrences'))
|
|
325
|
-
OpenEHR::AM::Archetype::ConstraintModel::ArchetypeInternalRef.new(rm_type_name: rm_type_name, occurrences: occurrences, target_path: target_path)
|
|
326
|
-
end
|
|
327
|
-
|
|
328
|
-
# No .opt fixture in this gem's corpus uses a C_DV_STATE (state
|
|
329
|
-
# machine) constraint, so its actual OPT XML shape is unverified;
|
|
330
|
-
# raising a clear, documented error here is safer than guessing
|
|
331
|
-
# at element names and risking a silently wrong StateMachine.
|
|
332
|
-
def c_dv_state(_attr_xml, _node)
|
|
333
|
-
raise NotImplementedError, 'OPTParser does not yet support C_DV_STATE (state machine) constraints'
|
|
334
|
-
end
|
|
335
|
-
|
|
336
|
-
def constraint_ref(attr_xml, node)
|
|
337
|
-
rm_type_name = attr_xml.at('rm_type_name').text
|
|
338
|
-
reference = attr_xml.at('reference').text
|
|
339
|
-
occurrences = occurrences(attr_xml.at('occurrences'))
|
|
340
|
-
OpenEHR::AM::Archetype::ConstraintModel::ConstraintRef.new(rm_type_name: rm_type_name, occurrences: occurrences, reference: reference)
|
|
341
|
-
end
|
|
342
|
-
|
|
343
|
-
def assertions(attr_xml, node)
|
|
344
|
-
string_expression = attr_xml.at('string_expression')
|
|
345
|
-
string_expression = string_expression.nil? ? nil : string_expression.text
|
|
346
|
-
expression_leaf = attr_xml.at 'expression'
|
|
347
|
-
expression = send expression_leaf.attributes['type'].text.downcase, expression_leaf
|
|
348
|
-
[OpenEHR::AM::Archetype::Assertion::Assertion.new(expression: expression, string_expression: string_expression)]
|
|
349
|
-
end
|
|
350
|
-
|
|
351
|
-
def expr_binary_operator(attr_xml)
|
|
352
|
-
type = attr_xml.at('type').text
|
|
353
|
-
operator = OpenEHR::AM::Archetype::Assertion::OperatorKind.new(value: attr_xml.at('operator').text.to_i)
|
|
354
|
-
|
|
355
|
-
precedence_overridden = attr_xml.at('precedence_overridden').text == 'true' ? true : false
|
|
356
|
-
right_operand_leaf = attr_xml.at 'right_operand'
|
|
357
|
-
right_operand = send right_operand_leaf.attributes['type'].text.downcase, right_operand_leaf
|
|
358
|
-
left_operand_leaf = attr_xml.at 'left_operand'
|
|
359
|
-
left_operand = send left_operand_leaf.attributes['type'].text.downcase, left_operand_leaf
|
|
360
|
-
OpenEHR::AM::Archetype::Assertion::ExprBinaryOperator.new(type: type, operator: operator, precedence_overridden: precedence_overridden, right_operand: right_operand, left_operand: left_operand)
|
|
361
|
-
end
|
|
362
|
-
|
|
363
|
-
def expr_leaf(attr_xml)
|
|
364
|
-
type = attr_xml.at('type').text
|
|
365
|
-
item_leaf = attr_xml.at('item')
|
|
366
|
-
item = send type.downcase, item_leaf
|
|
367
|
-
reference_type = attr_xml.at('reference_type').text
|
|
368
|
-
OpenEHR::AM::Archetype::Assertion::ExprLeaf.new(type: type, item: item, reference_type: reference_type)
|
|
369
|
-
end
|
|
370
|
-
|
|
371
|
-
def c_primitive_object(attr_xml, node)
|
|
372
|
-
rm_type_name = attr_xml.at('rm_type_name').text
|
|
373
|
-
occurrences = occurrences(attr_xml.at('occurrences'))
|
|
374
|
-
item = send attr_xml.at('item')['type'].downcase, attr_xml.at('item')
|
|
375
|
-
OpenEHR::AM::Archetype::ConstraintModel::CPrimitiveObject.new(rm_type_name: rm_type_name, occurrences: occurrences, node_id: node.id, item: item)
|
|
376
|
-
end
|
|
377
|
-
|
|
378
|
-
def c_string(attr_xml)
|
|
379
|
-
if attr_xml.at('pattern')
|
|
380
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CString.new(pattern: attr_xml.at('pattern').text)
|
|
381
|
-
else
|
|
382
|
-
list = attr_xml.xpath('.//list').map do |str|
|
|
383
|
-
str.text
|
|
384
|
-
end
|
|
385
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CString.new(list: list)
|
|
386
|
-
end
|
|
387
|
-
end
|
|
388
|
-
|
|
389
|
-
def c_dv_quantity(attr_xml, node)
|
|
390
|
-
rm_type_name = attr_xml.at('rm_type_name').text
|
|
391
|
-
occurrences = occurrences(attr_xml.at('occurrences'))
|
|
392
|
-
property = property_code_phrase(attr_xml.at('property'))
|
|
393
|
-
list = attr_xml.xpath('.//list').map do |element|
|
|
394
|
-
units = element.at('units').text if element.at('units')
|
|
395
|
-
magnitude = occurrences(element.at('magnitude')) if element.at('magnitude')
|
|
396
|
-
precision = occurrences(element.at('precision')) if element.at('precision')
|
|
397
|
-
OpenEHR::AM::OpenEHRProfile::DataTypes::Quantity::CQuantityItem.new(magnitude: magnitude, precision: precision, units: units)
|
|
398
|
-
end
|
|
399
|
-
OpenEHR::AM::OpenEHRProfile::DataTypes::Quantity::CDvQuantity.new(rm_type_name: rm_type_name, occurrences: occurrences, list: list, property: property)
|
|
400
|
-
end
|
|
401
|
-
|
|
402
|
-
# The <property> element is optional in real templates; return nil rather
|
|
403
|
-
# than dereferencing missing terminology/code nodes.
|
|
404
|
-
def property_code_phrase(property_xml)
|
|
405
|
-
return nil if property_xml.nil?
|
|
406
|
-
terminology_node = property_xml.at('terminology_id/value')
|
|
407
|
-
code_node = property_xml.at('code_string')
|
|
408
|
-
return nil if terminology_node.nil? || code_node.nil?
|
|
409
|
-
terminology_id = OpenEHR::RM::Support::Identification::TerminologyID.new(value: terminology_node.text)
|
|
410
|
-
OpenEHR::RM::DataTypes::Text::CodePhrase.new(terminology_id: terminology_id, code_string: code_node.text)
|
|
411
|
-
end
|
|
412
|
-
|
|
413
|
-
def c_dv_ordinal(attr_xml, node)
|
|
414
|
-
rm_type_name = attr_xml.at('rm_type_name').text
|
|
415
|
-
occurrences = occurrences(attr_xml.at('occurrences'))
|
|
416
|
-
list = attr_xml.xpath('list').map { |element| dv_ordinal_item(element) }.compact
|
|
417
|
-
OpenEHR::AM::OpenEHRProfile::DataTypes::Quantity::CDvOrdinal.new(rm_type_name: rm_type_name, occurrences: occurrences, list: list)
|
|
418
|
-
end
|
|
419
|
-
|
|
420
|
-
# DV_ORDINAL.symbol is spec'd as DV_CODED_TEXT; the OPT XML only
|
|
421
|
-
# carries a defining_code (terminology_id + code_string), so the
|
|
422
|
-
# DvCodedText's own value is set to that same code_string (there
|
|
423
|
-
# is no separate display text in this element).
|
|
424
|
-
def dv_ordinal_item(element)
|
|
425
|
-
value_node = element.at('value')
|
|
426
|
-
return nil unless value_node && !value_node.text.empty?
|
|
427
|
-
|
|
428
|
-
code_phrase = property_code_phrase(element.at('symbol/defining_code'))
|
|
429
|
-
return nil if code_phrase.nil?
|
|
430
|
-
|
|
431
|
-
symbol = OpenEHR::RM::DataTypes::Text::DvCodedText.new(value: code_phrase.code_string, defining_code: code_phrase)
|
|
432
|
-
OpenEHR::RM::DataTypes::Quantity::DvOrdinal.new(value: value_node.text.to_i, symbol: symbol)
|
|
433
|
-
end
|
|
434
|
-
|
|
435
|
-
def c_dv_scale(attr_xml, node)
|
|
436
|
-
rm_type_name = attr_xml.at('rm_type_name').text
|
|
437
|
-
occurrences = occurrences(attr_xml.at('occurrences'))
|
|
438
|
-
list = attr_xml.xpath('list').map { |element| dv_scale_item(element) }.compact
|
|
439
|
-
OpenEHR::AM::OpenEHRProfile::DataTypes::Quantity::CDvScale.new(rm_type_name: rm_type_name, occurrences: occurrences, list: list)
|
|
440
|
-
end
|
|
441
|
-
|
|
442
|
-
# Same XML shape as C_DV_ORDINAL's list items, but DV_SCALE.value
|
|
443
|
-
# is Real rather than Integer.
|
|
444
|
-
def dv_scale_item(element)
|
|
445
|
-
value_node = element.at('value')
|
|
446
|
-
return nil unless value_node && !value_node.text.empty?
|
|
447
|
-
|
|
448
|
-
code_phrase = property_code_phrase(element.at('symbol/defining_code'))
|
|
449
|
-
return nil if code_phrase.nil?
|
|
450
|
-
|
|
451
|
-
symbol = OpenEHR::RM::DataTypes::Text::DvCodedText.new(value: code_phrase.code_string, defining_code: code_phrase)
|
|
452
|
-
OpenEHR::RM::DataTypes::Quantity::DvScale.new(value: value_node.text.to_f, symbol: symbol)
|
|
453
|
-
end
|
|
454
|
-
|
|
455
|
-
def c_date(xml)
|
|
456
|
-
pattern = xml.at('pattern')
|
|
457
|
-
range = xml.at('range')
|
|
458
|
-
if pattern
|
|
459
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CDate.new(pattern: pattern.text)
|
|
460
|
-
elsif range
|
|
461
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CDate.new(range: occurrences(range))
|
|
462
|
-
else
|
|
463
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CDate.new
|
|
464
|
-
end
|
|
465
|
-
end
|
|
466
|
-
|
|
467
|
-
def c_date_time(xml)
|
|
468
|
-
pattern = xml.at('pattern')
|
|
469
|
-
range = xml.at('range')
|
|
470
|
-
if pattern
|
|
471
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CDateTime.new(pattern: pattern.text)
|
|
472
|
-
elsif range
|
|
473
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CDateTime.new(range: occurrences(range))
|
|
474
|
-
else
|
|
475
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CDateTime.new
|
|
476
|
-
end
|
|
477
|
-
end
|
|
478
|
-
|
|
479
|
-
def c_integer(xml)
|
|
480
|
-
range = xml.at('range')
|
|
481
|
-
list = xml.xpath('list')
|
|
482
|
-
if range
|
|
483
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CInteger.new(range: occurrences(range))
|
|
484
|
-
elsif !list.empty?
|
|
485
|
-
list_values = list.map { |item| item.text.to_i }
|
|
486
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CInteger.new(list: list_values)
|
|
487
|
-
else
|
|
488
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CInteger.new
|
|
489
|
-
end
|
|
490
|
-
end
|
|
491
|
-
|
|
492
|
-
def c_real(xml)
|
|
493
|
-
range = xml.at('range')
|
|
494
|
-
list = xml.xpath('list')
|
|
495
|
-
if range
|
|
496
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CReal.new(range: occurrences(range))
|
|
497
|
-
elsif !list.empty?
|
|
498
|
-
list_values = list.map { |item| item.text.to_f }
|
|
499
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CReal.new(list: list_values)
|
|
500
|
-
else
|
|
501
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CReal.new
|
|
502
|
-
end
|
|
503
|
-
end
|
|
504
|
-
|
|
505
|
-
def c_duration(xml)
|
|
506
|
-
pattern = xml.at('pattern')
|
|
507
|
-
range_xml = xml.at('range')
|
|
508
|
-
list = xml.xpath('list')
|
|
509
|
-
if pattern
|
|
510
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CDuration.new(pattern: pattern.text)
|
|
511
|
-
elsif range_xml
|
|
512
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CDuration.new(range: duration_range(range_xml))
|
|
513
|
-
elsif !list.empty?
|
|
514
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CDuration.new(list: list.map(&:text))
|
|
515
|
-
else
|
|
516
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CDuration.new
|
|
517
|
-
end
|
|
518
|
-
end
|
|
519
|
-
|
|
520
|
-
# A C_DURATION range's bounds are ISO8601 duration strings (e.g.
|
|
521
|
-
# PT24H), not plain numbers, so occurrences() (built for numeric
|
|
522
|
-
# Interval bounds) doesn't apply here; wrap each bound as a
|
|
523
|
-
# DV_DURATION instead, matching what CDuration#valid_value?
|
|
524
|
-
# already expects its range bounds to be.
|
|
525
|
-
def duration_range(range_xml)
|
|
526
|
-
lower = duration_bound(range_xml.at('lower'), range_xml.at('lower_unbounded'))
|
|
527
|
-
upper = duration_bound(range_xml.at('upper'), range_xml.at('upper_unbounded'))
|
|
528
|
-
return nil if lower.nil? && upper.nil?
|
|
529
|
-
|
|
530
|
-
OpenEHR::AssumedLibraryTypes::Interval.new(
|
|
531
|
-
lower: lower, upper: upper,
|
|
532
|
-
lower_included: lower.nil? ? nil : bool_node(range_xml.at('lower_included'), true),
|
|
533
|
-
upper_included: upper.nil? ? nil : bool_node(range_xml.at('upper_included'), true))
|
|
534
|
-
end
|
|
535
|
-
|
|
536
|
-
def duration_bound(value_node, unbounded_node)
|
|
537
|
-
return nil if bool_node(unbounded_node, false)
|
|
538
|
-
return nil if value_node.nil? || value_node.text.empty?
|
|
539
|
-
|
|
540
|
-
OpenEHR::RM::DataTypes::Quantity::DateTime::DvDuration.new(value: value_node.text)
|
|
541
|
-
end
|
|
542
|
-
|
|
543
|
-
def bool_node(node, default)
|
|
544
|
-
node ? to_bool(node.text) : default
|
|
545
|
-
end
|
|
546
|
-
|
|
547
|
-
def c_time(xml)
|
|
548
|
-
pattern = xml.at('pattern')
|
|
549
|
-
range = xml.at('range')
|
|
550
|
-
if pattern
|
|
551
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CTime.new(pattern: pattern.text)
|
|
552
|
-
elsif range
|
|
553
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CTime.new(range: occurrences(range))
|
|
554
|
-
else
|
|
555
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CTime.new
|
|
556
|
-
end
|
|
557
|
-
end
|
|
558
|
-
|
|
559
|
-
def c_boolean(xml)
|
|
560
|
-
true_valid = xml.at('true_valid')
|
|
561
|
-
false_valid = xml.at('false_valid')
|
|
562
|
-
assumed_value = xml.at('assumed_value')
|
|
563
|
-
|
|
564
|
-
true_valid_value = true_valid ? to_bool(true_valid.text) : nil
|
|
565
|
-
false_valid_value = false_valid ? to_bool(false_valid.text) : nil
|
|
566
|
-
assumed_value_value = assumed_value ? to_bool(assumed_value.text) : nil
|
|
567
|
-
|
|
568
|
-
OpenEHR::AM::Archetype::ConstraintModel::Primitive::CBoolean.new(
|
|
569
|
-
true_valid: true_valid_value,
|
|
570
|
-
false_valid: false_valid_value,
|
|
571
|
-
assumed_value: assumed_value_value
|
|
572
|
-
)
|
|
573
|
-
end
|
|
574
|
-
|
|
575
|
-
def string(attr_xml)
|
|
576
|
-
attr_xml.text
|
|
577
|
-
end
|
|
578
|
-
|
|
579
170
|
def empty_then_nil(val)
|
|
580
171
|
if val.empty?
|
|
581
172
|
return nil
|
|
@@ -587,14 +178,6 @@ module OpenEHR
|
|
|
587
178
|
def text_on_path(xml, path)
|
|
588
179
|
xml.xpath(path).text
|
|
589
180
|
end
|
|
590
|
-
|
|
591
|
-
def to_bool(str)
|
|
592
|
-
return nil if str.nil?
|
|
593
|
-
str = str.text if str.respond_to?(:text)
|
|
594
|
-
return true if /true/i =~ str.to_s
|
|
595
|
-
return false if /false/i =~ str.to_s
|
|
596
|
-
nil
|
|
597
|
-
end
|
|
598
181
|
end
|
|
599
182
|
end
|
|
600
183
|
end
|