openehr 1.2.999999 → 2.0.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.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/README.rdoc +95 -14
  3. data/lib/openehr/am/archetype/constraint_model/primitive.rb +204 -6
  4. data/lib/openehr/am/archetype/constraint_model.rb +228 -4
  5. data/lib/openehr/am/archetype/ontology.rb +2 -2
  6. data/lib/openehr/am/archetype.rb +101 -2
  7. data/lib/openehr/am/openehr_profile/data_types/basic.rb +28 -0
  8. data/lib/openehr/am/openehr_profile/data_types/quantity.rb +88 -0
  9. data/lib/openehr/am/openehr_profile/data_types/text.rb +23 -0
  10. data/lib/openehr/am/template.rb +77 -24
  11. data/lib/openehr/aql/engine/binding.rb +13 -0
  12. data/lib/openehr/aql/engine/contains_resolver.rb +161 -0
  13. data/lib/openehr/aql/engine/dataset.rb +122 -0
  14. data/lib/openehr/aql/engine/path_evaluator.rb +137 -0
  15. data/lib/openehr/aql/engine/predicate_evaluator.rb +65 -0
  16. data/lib/openehr/aql/engine.rb +119 -0
  17. data/lib/openehr/aql/errors.rb +27 -0
  18. data/lib/openehr/aql/lexer.rb +295 -0
  19. data/lib/openehr/aql/model/containment.rb +45 -0
  20. data/lib/openehr/aql/model/from_clause.rb +35 -0
  21. data/lib/openehr/aql/model/function_call.rb +56 -0
  22. data/lib/openehr/aql/model/identified_path.rb +20 -0
  23. data/lib/openehr/aql/model/literal.rb +15 -0
  24. data/lib/openehr/aql/model/object_path.rb +34 -0
  25. data/lib/openehr/aql/model/order_by_and_limit.rb +50 -0
  26. data/lib/openehr/aql/model/predicate.rb +84 -0
  27. data/lib/openehr/aql/model/query.rb +24 -0
  28. data/lib/openehr/aql/model/select_clause.rb +29 -0
  29. data/lib/openehr/aql/model/where_clause.rb +105 -0
  30. data/lib/openehr/aql/model.rb +14 -0
  31. data/lib/openehr/aql/parser.rb +484 -0
  32. data/lib/openehr/aql/result_set.rb +54 -0
  33. data/lib/openehr/aql.rb +28 -0
  34. data/lib/openehr/assumed_library_types.rb +68 -17
  35. data/lib/openehr/parser/adl_grammar.tt +29 -13
  36. data/lib/openehr/parser/adl_parser.rb +18 -4
  37. data/lib/openehr/parser/archetype_validator.rb +126 -0
  38. data/lib/openehr/parser/exception.rb +13 -0
  39. data/lib/openehr/parser/opt_parser.rb +306 -27
  40. data/lib/openehr/parser.rb +2 -0
  41. data/lib/openehr/path.rb +195 -0
  42. data/lib/openehr/rm/common/archetyped.rb +117 -7
  43. data/lib/openehr/rm/common/change_control.rb +3 -8
  44. data/lib/openehr/rm/common/directory.rb +4 -0
  45. data/lib/openehr/rm/common/generic.rb +24 -10
  46. data/lib/openehr/rm/composition/content/entry.rb +24 -5
  47. data/lib/openehr/rm/composition/content/navigation.rb +2 -1
  48. data/lib/openehr/rm/composition.rb +19 -2
  49. data/lib/openehr/rm/data_structures/history.rb +3 -0
  50. data/lib/openehr/rm/data_structures/item_structure/representation.rb +13 -9
  51. data/lib/openehr/rm/data_structures/item_structure.rb +28 -15
  52. data/lib/openehr/rm/data_types/encapsulated.rb +23 -3
  53. data/lib/openehr/rm/data_types/quantity/date_time.rb +9 -1
  54. data/lib/openehr/rm/data_types/quantity.rb +66 -14
  55. data/lib/openehr/rm/data_types/text.rb +8 -0
  56. data/lib/openehr/rm/data_types/time_specification.rb +5 -4
  57. data/lib/openehr/rm/data_types/uri.rb +0 -1
  58. data/lib/openehr/rm/demographic.rb +4 -3
  59. data/lib/openehr/rm/ehr.rb +20 -1
  60. data/lib/openehr/rm/factory.rb +345 -36
  61. data/lib/openehr/rm/integration.rb +1 -0
  62. data/lib/openehr/rm/support/identification.rb +37 -8
  63. data/lib/openehr/rm/support/measurement.rb +32 -0
  64. data/lib/openehr/rm/type_name.rb +90 -0
  65. data/lib/openehr/rm.rb +3 -0
  66. data/lib/openehr/serializer/adl_serializer.rb +335 -0
  67. data/lib/openehr/serializer/base.rb +20 -0
  68. data/lib/openehr/serializer/opt_serializer.rb +49 -0
  69. data/lib/openehr/serializer/rm_json_serializer.rb +62 -0
  70. data/lib/openehr/serializer/xml_serializer.rb +260 -0
  71. data/lib/openehr/serializer.rb +5 -291
  72. data/lib/openehr/terminology_service.rb +44 -0
  73. data/lib/openehr/version.rb +1 -1
  74. data/lib/openehr.rb +5 -2
  75. metadata +36 -11
  76. data/lib/openehr/parser/validator.rb +0 -18
  77. data/lib/openehr/parser/xml_parser.rb +0 -13
  78. data/lib/openehr/rm/data_types/charset_extract.rb +0 -24
  79. data/lib/openehr/writer.rb +0 -12
@@ -39,9 +39,11 @@ module OpenEHR
39
39
 
40
40
  private
41
41
 
42
+ CHARSET_LIST_PATH = File.expand_path('charset.lst', __dir__)
43
+
42
44
  def charset_valid?(charset)
43
45
  result = false
44
- open('lib/openehr/rm/data_types/charset.lst') do |file|
46
+ File.open(CHARSET_LIST_PATH) do |file|
45
47
  while line = file.gets
46
48
  if charset == line.chomp
47
49
  result = true
@@ -57,7 +59,8 @@ module OpenEHR
57
59
  class DvMultimedia < DvEncapsulated
58
60
  attr_reader :media_type
59
61
  attr_accessor :uri, :data, :compression_algorithm,
60
- :integrity_check, :integrity_check_algorithm, :alternate_text
62
+ :integrity_check, :integrity_check_algorithm, :alternate_text,
63
+ :thumbnail
61
64
 
62
65
  def initialize(args = {})
63
66
  super(args)
@@ -68,14 +71,31 @@ module OpenEHR
68
71
  self.integrity_check = args[:integrity_check]
69
72
  self.integrity_check_algorithm = args[:integrity_check_algorithm]
70
73
  self.alternate_text = args[:alternate_text]
74
+ self.thumbnail = args[:thumbnail]
71
75
  end
72
76
 
73
77
  def media_type=(media_type)
74
- if media_type.code_string.nil?
78
+ if media_type.nil? || media_type.code_string.nil?
75
79
  raise ArgumentError, 'media_type should not be nil'
76
80
  end
77
81
  @media_type = media_type
78
82
  end
83
+
84
+ def is_external?
85
+ !@uri.nil?
86
+ end
87
+
88
+ def is_inline?
89
+ !@data.nil?
90
+ end
91
+
92
+ def is_compressed?
93
+ !@compression_algorithm.nil?
94
+ end
95
+
96
+ def has_integrity_check?
97
+ !@integrity_check_algorithm.nil?
98
+ end
79
99
  end
80
100
 
81
101
  class DvParsable < DvEncapsulated
@@ -224,6 +224,7 @@ module OpenEHR
224
224
  self.minutes = iso8601_duration.minutes
225
225
  self.seconds = iso8601_duration.seconds
226
226
  self.fractional_second = iso8601_duration.fractional_second
227
+ self.negative = iso8601_duration.negative?
227
228
  end
228
229
 
229
230
  def magnitude
@@ -244,7 +245,14 @@ module OpenEHR
244
245
  seconds += @seconds if @seconds
245
246
  seconds += @fractional_second if @fractional_second
246
247
  seconds += minutes * SECONDS_IN_MINUTE if minutes
247
- return seconds
248
+ negative? ? -seconds : seconds
249
+ end
250
+
251
+ # DV_AMOUNT.negative alias "-": a new DvDuration with the
252
+ # sign flipped, leaving self untouched.
253
+ def -@
254
+ text = value.start_with?('-') ? value[1..-1] : "-#{value}"
255
+ DvDuration.new(:value => text)
248
256
  end
249
257
  end
250
258
  end # of DateTime
@@ -24,7 +24,7 @@ module OpenEHR
24
24
  if @normal_range.nil? and @normal_status.nil?
25
25
  return false
26
26
  elsif !@normal_range.nil?
27
- return @normal_range.has(@value)
27
+ return @normal_range.has?(@value)
28
28
  elsif !@normal_status.nil?
29
29
  return @normal_status.code_string == 'N'
30
30
  end
@@ -143,6 +143,40 @@ module OpenEHR
143
143
  end
144
144
  end
145
145
 
146
+ # RM 1.1.0 (SPECRM-19): like DV_ORDINAL, but for scales/scores
147
+ # whose points are Real-valued rather than Integer-valued (e.g.
148
+ # the Borg CR10 breathlessness scale, which includes 0.5).
149
+ class DvScale < DvOrdered
150
+ attr_reader :value, :symbol
151
+
152
+ def initialize(args = {})
153
+ super(args)
154
+ self.symbol = args[:symbol]
155
+ end
156
+
157
+ def value=(value)
158
+ raise ArgumentError, 'value should not be nil' if value.nil?
159
+ raise ArgumentError, 'value should be a Real (Numeric)' unless value.is_a?(Numeric)
160
+ @value = value
161
+ end
162
+
163
+ def symbol=(symbol)
164
+ raise ArgumentError, 'symbol should not be nil' if symbol.nil?
165
+ @symbol = symbol
166
+ end
167
+
168
+ def <=>(other)
169
+ @value <=> other.value
170
+ end
171
+
172
+ def is_strictly_comparable_to?(others)
173
+ return false unless super(others)
174
+
175
+ others.symbol.defining_code.terminology_id.value ==
176
+ @symbol.defining_code.terminology_id.value
177
+ end
178
+ end
179
+
146
180
  class DvAbsoluteQuantity < DvQuantified
147
181
  attr_accessor :accuracy
148
182
 
@@ -207,14 +241,21 @@ module OpenEHR
207
241
  return result
208
242
  end
209
243
 
210
- def -(other)
211
- other.magnitude = - other.magnitude
212
- self+(other)
244
+ def -(other)
245
+ negated = other.dup
246
+ negated.magnitude = -other.magnitude
247
+ self + negated
248
+ end
249
+
250
+ def -@
251
+ negated = self.dup
252
+ negated.magnitude = -magnitude
253
+ negated
213
254
  end
214
255
 
215
256
  def set_accuracy(accuracy, accuracy_percent)
216
257
  if accuracy_percent
217
- raise ArgumentError, 'accuracy invalid' if accuracy < 0.0 || accuracy > 100.0
258
+ raise ArgumentError, 'accuracy invalid' unless self.class.valid_percentage(accuracy)
218
259
  else
219
260
  raise ArgumentError, 'accuracy invaild' if accuracy < 0.0 || accuracy > 1.0
220
261
  end
@@ -224,15 +265,26 @@ module OpenEHR
224
265
  def accuracy_is_percent?
225
266
  return @accuracy_percent
226
267
  end
268
+
269
+ def self.valid_percentage(number)
270
+ number >= 0.0 && number <= 100.0
271
+ end
227
272
  end
228
273
 
229
274
  class DvQuantity < DvAmount
230
275
  attr_reader :units, :precision
276
+ # RM 1.1.0 (SPECRM-65): units is UCUM by default; units_system
277
+ # names another units system when units isn't UCUM, and
278
+ # units_display_name gives a displayable form when the units
279
+ # code itself isn't one (e.g. UCUM 'Cel' vs '°C'). Both optional.
280
+ attr_accessor :units_system, :units_display_name
231
281
 
232
282
  def initialize(args = {})
233
283
  super(args)
234
284
  self.units = args[:units]
235
285
  self.precision = args[:precision]
286
+ self.units_system = args[:units_system]
287
+ self.units_display_name = args[:units_display_name]
236
288
  end
237
289
 
238
290
  def units=(units)
@@ -248,14 +300,9 @@ module OpenEHR
248
300
  end
249
301
 
250
302
  def is_strictly_comparable_to?(others)
251
- unless super(others)
252
- return false
253
- end
254
- if others.units == @units
255
- return true
256
- else
257
- return false
258
- end
303
+ return false unless super(others)
304
+
305
+ others.units == @units && others.units_system == @units_system
259
306
  end
260
307
 
261
308
  def is_integral?
@@ -268,7 +315,12 @@ module OpenEHR
268
315
  end
269
316
 
270
317
  class DvCount < DvAmount
271
-
318
+ def magnitude=(magnitude)
319
+ unless magnitude.is_a?(Integer)
320
+ raise ArgumentError, 'magnitude should be an Integer'
321
+ end
322
+ super
323
+ end
272
324
  end
273
325
 
274
326
  class ReferenceRange
@@ -59,10 +59,15 @@ module OpenEHR
59
59
 
60
60
  class CodePhrase
61
61
  attr_reader :terminology_id, :code_string
62
+ # RM 1.1.0: optional preferred term for code_string, typically
63
+ # used in integration mappings that need both a (non-preferred)
64
+ # actual term and a preferred term.
65
+ attr_accessor :preferred_term
62
66
 
63
67
  def initialize(args = {})
64
68
  self.code_string = args[:code_string]
65
69
  self.terminology_id = args[:terminology_id]
70
+ self.preferred_term = args[:preferred_term]
66
71
  end
67
72
 
68
73
  def terminology_id=(terminology_id)
@@ -152,6 +157,9 @@ module OpenEHR
152
157
  attr_reader :items
153
158
 
154
159
  def initialize(args ={})
160
+ warn '[DEPRECATED] DvParagraph (DV_PARAGRAPH) is deprecated as of RM 1.0.4; ' \
161
+ 'use DvText/DvCodedText with markdown or newlines instead. ' \
162
+ 'It remains legal for legacy data.'
155
163
  self.items = args[:items]
156
164
  end
157
165
 
@@ -17,16 +17,17 @@ module OpenEHR
17
17
  @value = value
18
18
  end
19
19
 
20
- def calender_alignment
21
- raise NotImlementedError, "calender_alignment must be implemented"
20
+ def calendar_alignment
21
+ raise NotImplementedError, "calendar_alignment must be implemented"
22
22
  end
23
+ alias calender_alignment calendar_alignment
23
24
 
24
25
  def event_alignment
25
- raise NotImlementedError, "event_alignment must be implemented"
26
+ raise NotImplementedError, "event_alignment must be implemented"
26
27
  end
27
28
 
28
29
  def institution_specified
29
- raise NotImlementedError, "institution_specified must be implemented"
30
+ raise NotImplementedError, "institution_specified must be implemented"
30
31
  end
31
32
  end
32
33
 
@@ -24,7 +24,6 @@ module URI
24
24
  true
25
25
  end
26
26
  end
27
- @@schemes['EHR'] = EHR
28
27
  end
29
28
 
30
29
  module OpenEHR
@@ -2,6 +2,7 @@
2
2
  # http://www.openehr.org/uml/release-1.0.1/Browsable/_9_5_76d0249_1118674798473_6021_0Report.html
3
3
  # Ticket refs #45
4
4
  require_relative 'common/archetyped'
5
+ require_relative 'common/change_control'
5
6
 
6
7
  module OpenEHR
7
8
  module RM
@@ -247,7 +248,7 @@ module OpenEHR
247
248
  end
248
249
 
249
250
  def source=(source)
250
- if source.nil? or source.id.value != @uid.value
251
+ if source.nil?
251
252
  raise ArgumentError, 'source is invalid'
252
253
  end
253
254
  @source = source
@@ -255,13 +256,13 @@ module OpenEHR
255
256
 
256
257
  def target=(target)
257
258
  if target.nil?
258
- raise ArgumentError, 'taraget is invalid'
259
+ raise ArgumentError, 'target is invalid'
259
260
  end
260
261
  @target = target
261
262
  end
262
263
  end
263
264
 
264
- class VersionedParty < OpenEHR::RM::Common::Archetyped::Locatable
265
+ class VersionedParty < OpenEHR::RM::Common::ChangeControl::VersionedObject
265
266
 
266
267
  end
267
268
  end # of Demographic
@@ -9,7 +9,8 @@ module OpenEHR
9
9
  module EHR
10
10
  class EHR
11
11
  attr_reader :system_id, :ehr_id, :time_created, :contributions,
12
- :ehr_access, :ehr_status, :compositions, :directory
12
+ :ehr_access, :ehr_status, :compositions, :directory,
13
+ :folders
13
14
 
14
15
  def initialize(args = { })
15
16
  self.system_id = args[:system_id]
@@ -20,6 +21,7 @@ module OpenEHR
20
21
  self.ehr_status = args[:ehr_status]
21
22
  self.compositions = args[:compositions]
22
23
  self.directory = args[:directory]
24
+ self.folders = args[:folders]
23
25
  end
24
26
 
25
27
  def system_id=(system_id)
@@ -89,6 +91,22 @@ module OpenEHR
89
91
  end
90
92
  @directory = directory
91
93
  end
94
+
95
+ # RM 1.1.0 (SPECRM-55): additional Folder hierarchies for this
96
+ # EHR. When set, directory is kept pointing at folders.item(1)
97
+ # (the Directory_in_folders invariant), for backward
98
+ # compatibility with pre-1.1.0 systems that only had directory.
99
+ def folders=(folders)
100
+ unless folders.nil?
101
+ folders.each do |f|
102
+ if f.type != 'VERSIONED_FOLDER'
103
+ raise ArgumentError, 'folder type should be VERSIONED_FOLDER'
104
+ end
105
+ end
106
+ @directory = folders.first
107
+ end
108
+ @folders = folders
109
+ end
92
110
  end
93
111
 
94
112
  class VersionedEHRAccess < OpenEHR::RM::Common::ChangeControl::VersionedObject
@@ -120,6 +138,7 @@ module OpenEHR
120
138
  class EHRStatus < OpenEHR::RM::Common::Archetyped::Locatable
121
139
  attr_reader :subject
122
140
  attr_accessor :is_modifiable, :is_queryable, :other_details
141
+ path_attribute :other_details
123
142
 
124
143
  def initialize(args = { })
125
144
  super(args)