hqmf-parser 1.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 (49) hide show
  1. data/Gemfile +23 -0
  2. data/README.md +903 -0
  3. data/Rakefile +19 -0
  4. data/VERSION +1 -0
  5. data/lib/hqmf-generator/hqmf-generator.rb +308 -0
  6. data/lib/hqmf-model/attribute.rb +35 -0
  7. data/lib/hqmf-model/data_criteria.rb +322 -0
  8. data/lib/hqmf-model/document.rb +172 -0
  9. data/lib/hqmf-model/population_criteria.rb +90 -0
  10. data/lib/hqmf-model/precondition.rb +85 -0
  11. data/lib/hqmf-model/types.rb +318 -0
  12. data/lib/hqmf-model/utilities.rb +52 -0
  13. data/lib/hqmf-parser.rb +54 -0
  14. data/lib/hqmf-parser/1.0/attribute.rb +68 -0
  15. data/lib/hqmf-parser/1.0/comparison.rb +34 -0
  16. data/lib/hqmf-parser/1.0/data_criteria.rb +105 -0
  17. data/lib/hqmf-parser/1.0/document.rb +209 -0
  18. data/lib/hqmf-parser/1.0/expression.rb +52 -0
  19. data/lib/hqmf-parser/1.0/population_criteria.rb +79 -0
  20. data/lib/hqmf-parser/1.0/precondition.rb +89 -0
  21. data/lib/hqmf-parser/1.0/range.rb +65 -0
  22. data/lib/hqmf-parser/1.0/restriction.rb +157 -0
  23. data/lib/hqmf-parser/1.0/utilities.rb +41 -0
  24. data/lib/hqmf-parser/2.0/data_criteria.rb +319 -0
  25. data/lib/hqmf-parser/2.0/document.rb +165 -0
  26. data/lib/hqmf-parser/2.0/population_criteria.rb +53 -0
  27. data/lib/hqmf-parser/2.0/precondition.rb +44 -0
  28. data/lib/hqmf-parser/2.0/types.rb +223 -0
  29. data/lib/hqmf-parser/2.0/utilities.rb +30 -0
  30. data/lib/hqmf-parser/converter/pass1/data_criteria_converter.rb +254 -0
  31. data/lib/hqmf-parser/converter/pass1/document_converter.rb +183 -0
  32. data/lib/hqmf-parser/converter/pass1/population_criteria_converter.rb +135 -0
  33. data/lib/hqmf-parser/converter/pass1/precondition_converter.rb +164 -0
  34. data/lib/hqmf-parser/converter/pass1/precondition_extractor.rb +159 -0
  35. data/lib/hqmf-parser/converter/pass1/simple_data_criteria.rb +35 -0
  36. data/lib/hqmf-parser/converter/pass1/simple_operator.rb +89 -0
  37. data/lib/hqmf-parser/converter/pass1/simple_population_criteria.rb +10 -0
  38. data/lib/hqmf-parser/converter/pass1/simple_precondition.rb +63 -0
  39. data/lib/hqmf-parser/converter/pass1/simple_restriction.rb +64 -0
  40. data/lib/hqmf-parser/converter/pass2/comparison_converter.rb +91 -0
  41. data/lib/hqmf-parser/converter/pass2/operator_converter.rb +169 -0
  42. data/lib/hqmf-parser/converter/pass3/specific_occurrence_converter.rb +86 -0
  43. data/lib/hqmf-parser/converter/pass3/specific_occurrence_converter_bak.rb +70 -0
  44. data/lib/hqmf-parser/parser.rb +22 -0
  45. data/lib/hqmf-parser/value_sets/value_set_parser.rb +206 -0
  46. data/lib/tasks/coverme.rake +8 -0
  47. data/lib/tasks/hqmf.rake +141 -0
  48. data/lib/tasks/value_sets.rake +23 -0
  49. metadata +159 -0
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ require_relative File.join('lib', 'hqmf-parser')
5
+
6
+ # Pull in any rake task defined in lib/tasks
7
+ Dir['lib/tasks/*.rake'].sort.each do |ext|
8
+ load ext
9
+ end
10
+
11
+ $LOAD_PATH << File.expand_path("../test",__FILE__)
12
+ desc "Run basic tests"
13
+ Rake::TestTask.new("test_unit") { |t|
14
+ t.pattern = 'test/unit/**/*_test.rb'
15
+ t.verbose = false
16
+ t.warning = false
17
+ }
18
+
19
+ task :default => [:test_unit,'cover_me:report']
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,308 @@
1
+ module HQMF2
2
+ module Generator
3
+
4
+ def self.render_template(name, params)
5
+ template_path = File.expand_path(File.join('..', "#{name}.xml.erb"), __FILE__)
6
+ template_str = File.read(template_path)
7
+ template = ERB.new(template_str, nil, '-', "_templ#{TemplateCounter.instance.new_id}")
8
+ context = ErbContext.new(params)
9
+ template.result(context.get_binding)
10
+ end
11
+
12
+ # Class to serialize HQMF::Document as HQMF V2 XML
13
+ class ModelProcessor
14
+ # Convert the supplied model instance to XML
15
+ # @param [HQMF::Document] doc the model instance
16
+ # @return [String] the serialized XML as a String
17
+ def self.to_hqmf(doc)
18
+ HQMF2::Generator.render_template('document', {'doc' => doc})
19
+ end
20
+ end
21
+
22
+ # Utility class used to supply a binding to Erb. Contains utility functions used
23
+ # by the erb templates that are used to generate the HQMF document.
24
+ class ErbContext < OpenStruct
25
+
26
+ def initialize(vars)
27
+ super(vars)
28
+ end
29
+
30
+ # Get a binding that contains all the instance variables
31
+ # @return [Binding]
32
+ def get_binding
33
+ binding
34
+ end
35
+
36
+ def xml_for_reference_id(id)
37
+ reference = HQMF::Reference.new(id)
38
+ xml_for_reference(reference)
39
+ end
40
+
41
+ def xml_for_reference(reference)
42
+ HQMF2::Generator.render_template('reference', {'doc' => doc, 'reference' => reference})
43
+ end
44
+
45
+ def xml_for_attribute(attribute)
46
+ HQMF2::Generator.render_template('attribute', {'attribute' => attribute})
47
+ end
48
+
49
+ def xml_for_fields(criteria)
50
+ fields = []
51
+ if criteria.field_values
52
+ criteria.field_values.each_pair do |key, value|
53
+ details = HQMF::DataCriteria::FIELDS[key]
54
+ details[:code_system_name] = HealthDataStandards::Util::CodeSystemHelper.code_system_for(details[:code_system])
55
+ fields << HQMF2::Generator.render_template('field', {'details' => details, 'value' => value})
56
+ end
57
+ end
58
+ if criteria.specific_occurrence
59
+ fields << HQMF2::Generator.render_template('specific_occurrence', {'source_criteria_id' => criteria.source_data_criteria, 'type' => criteria.specific_occurrence_const, 'id' => criteria.specific_occurrence})
60
+ elsif criteria.source_data_criteria
61
+ fields << HQMF2::Generator.render_template('source', {'source_criteria_id' => criteria.source_data_criteria})
62
+ end
63
+ fields.join
64
+ end
65
+
66
+ def xml_for_value(value, element_name='value', include_type=true)
67
+ HQMF2::Generator.render_template('value', {'doc' => doc, 'value' => value, 'name' => element_name, 'include_type' => include_type})
68
+ end
69
+
70
+ def xml_for_code(criteria, element_name='code', include_type=true)
71
+ HQMF2::Generator.render_template('code', {'doc' => doc, 'criteria' => criteria, 'name' => element_name, 'include_type' => include_type})
72
+ end
73
+
74
+ def xml_for_derivation(data_criteria)
75
+ xml = ''
76
+ if data_criteria.derivation_operator
77
+ xml = HQMF2::Generator.render_template('derivation', {'doc' => doc, 'criteria' => data_criteria})
78
+ end
79
+ xml
80
+ end
81
+
82
+ def xml_for_effective_time(data_criteria)
83
+ xml = ''
84
+ if data_criteria.effective_time
85
+ xml = HQMF2::Generator.render_template('effective_time', {'doc' => doc, 'effective_time' => data_criteria.effective_time})
86
+ end
87
+ xml
88
+ end
89
+
90
+ def xml_for_reason(data_criteria)
91
+ xml = ''
92
+ if data_criteria.negation && data_criteria.negation_code_list_id
93
+ xml = HQMF2::Generator.render_template('reason', {'doc' => doc, 'code_list_id' => data_criteria.negation_code_list_id})
94
+ end
95
+ xml
96
+ end
97
+
98
+ def xml_for_template(data_criteria, is_source_data_criteria)
99
+ xml = ''
100
+ templates = []
101
+ # Add a template ID if one is defined for this data criteria
102
+ template_id = HQMF::DataCriteria.template_id_for_definition(data_criteria.definition, data_criteria.status, data_criteria.negation)
103
+ if template_id
104
+ templates << {:id => template_id, :title => HQMF::DataCriteria.title_for_template_id(template_id)}
105
+ end
106
+ # Add our own template id if this is a source data criteria from HQMF V1. Source
107
+ # data criteria are the 'raw' HQMF V1 data criteria before any restrictions are applied
108
+ # they are only used for negating specific occurrences
109
+ if is_source_data_criteria
110
+ templates << {:id => HQMF::DataCriteria::SOURCE_DATA_CRITERIA_TEMPLATE_ID, :title => HQMF::DataCriteria::SOURCE_DATA_CRITERIA_TEMPLATE_TITLE}
111
+ end
112
+ if templates.length > 0
113
+ xml = HQMF2::Generator.render_template('template_id', {'templates' => templates})
114
+ end
115
+ xml
116
+ end
117
+
118
+ def xml_for_description(data_criteria)
119
+ xml = ''
120
+ if data_criteria.description
121
+ xml = HQMF2::Generator.render_template('description', {'text' => data_criteria.description})
122
+ end
123
+ xml
124
+ end
125
+
126
+ def xml_for_subsets(data_criteria)
127
+ subsets_xml = []
128
+ if data_criteria.subset_operators
129
+ subsets_xml = data_criteria.subset_operators.collect do |operator|
130
+ HQMF2::Generator.render_template('subset', {'doc' => doc, 'subset' => operator, 'criteria' => data_criteria})
131
+ end
132
+ end
133
+ subsets_xml.join()
134
+ end
135
+
136
+ def xml_for_precondition(precondition)
137
+ HQMF2::Generator.render_template('precondition', {'doc' => doc, 'precondition' => precondition})
138
+ end
139
+
140
+ def xml_for_data_criteria(data_criteria, is_source_data_criteria)
141
+ HQMF2::Generator.render_template(data_criteria_template_name(data_criteria), {'doc' => doc, 'criteria' => data_criteria, 'is_source_data_criteria' => is_source_data_criteria})
142
+ end
143
+
144
+ def xml_for_population_criteria(population, criteria_id)
145
+ xml = ''
146
+ population_criteria = doc.population_criteria(population[criteria_id])
147
+ if population_criteria
148
+ xml = HQMF2::Generator.render_template('population_criteria', {'doc' => doc, 'population' => population, 'criteria_id' => criteria_id, 'population_criteria' => population_criteria})
149
+ end
150
+ xml
151
+ end
152
+
153
+ def xml_for_temporal_references(criteria)
154
+ refs = []
155
+ if criteria.temporal_references
156
+ refs = criteria.temporal_references.collect do |reference|
157
+ HQMF2::Generator.render_template('temporal_relationship', {'doc' => doc, 'relationship' => reference})
158
+ end
159
+ end
160
+ refs.join
161
+ end
162
+
163
+ def oid_for_name(code_system_name)
164
+ HealthDataStandards::Util::CodeSystemHelper.oid_for_code_system(code_system_name)
165
+ end
166
+
167
+ def reference_element_name(id)
168
+ referenced_criteria = doc.data_criteria(id)
169
+ element_name_prefix(referenced_criteria)
170
+ end
171
+
172
+ def reference_type_name(id)
173
+ referenced_criteria = doc.data_criteria(id)
174
+ type = nil
175
+ if referenced_criteria
176
+ type = referenced_criteria.type
177
+ elsif id=="MeasurePeriod"
178
+ type = :observation
179
+ end
180
+ if !type
181
+ raise "No data criteria with ID: #{id}"
182
+ end
183
+ case type
184
+ when :encounters
185
+ 'ENC'
186
+ when :procedures
187
+ 'PROC'
188
+ when :medications, :allMedications
189
+ 'SBADM'
190
+ when :medication_supply
191
+ 'SPLY'
192
+ else
193
+ 'OBS'
194
+ end
195
+ end
196
+
197
+ def code_for_characteristic(characteristic)
198
+ case characteristic
199
+ when :birthtime
200
+ '21112-8'
201
+ when :age
202
+ '424144002'
203
+ when :gender
204
+ '263495000'
205
+ when :languages
206
+ '102902016'
207
+ when :maritalStatus
208
+ '125680007'
209
+ when :race
210
+ '103579009'
211
+ else
212
+ raise "Unknown demographic code [#{characteristic}]"
213
+ end
214
+ end
215
+
216
+ def oid_for_characteristic(characteristic)
217
+ case characteristic
218
+ when :birthtime
219
+ '2.16.840.1.113883.6.1'
220
+ else
221
+ '2.16.840.1.113883.6.96'
222
+ end
223
+ end
224
+
225
+ def data_criteria_template_name(data_criteria)
226
+ case data_criteria.definition
227
+ when 'diagnosis', 'diagnosis_family_history'
228
+ 'condition_criteria'
229
+ when 'encounter'
230
+ 'encounter_criteria'
231
+ when 'procedure', 'risk_category_assessment', 'physical_exam', 'communication_from_patient_to_provider', 'communication_from_provider_to_provider', 'device', 'diagnostic_study', 'intervention'
232
+ if data_criteria.value.nil?
233
+ 'procedure_criteria'
234
+ else
235
+ 'observation_criteria'
236
+ end
237
+ when 'medication'
238
+ case data_criteria.status
239
+ when 'dispensed', 'ordered'
240
+ 'supply_criteria'
241
+ else # active or administered
242
+ 'substance_criteria'
243
+ end
244
+ when 'patient_characteristic', 'patient_characteristic_birthdate', 'patient_characteristic_clinical_trial_participant', 'patient_characteristic_expired', 'patient_characteristic_gender', 'patient_characteristic_age', 'patient_characteristic_languages', 'patient_characteristic_marital_status', 'patient_characteristic_race'
245
+ 'characteristic_criteria'
246
+ when 'variable'
247
+ 'variable_criteria'
248
+ else
249
+ 'observation_criteria'
250
+ end
251
+ end
252
+
253
+ def section_name(data_criteria)
254
+ data_criteria.definition.to_s
255
+ end
256
+
257
+ def element_name_prefix(data_criteria)
258
+ type = data_criteria ? data_criteria.type : :observation
259
+ case type
260
+ when :encounters
261
+ 'encounter'
262
+ when :procedures
263
+ 'procedure'
264
+ when :medications, :allMedications
265
+ 'substanceAdministration'
266
+ when :medication_supply
267
+ 'supply'
268
+ else
269
+ 'observation'
270
+ end
271
+ end
272
+
273
+ def population_element_prefix(population_criteria_code)
274
+ case population_criteria_code
275
+ when HQMF::PopulationCriteria::IPP
276
+ 'patientPopulation'
277
+ when HQMF::PopulationCriteria::DENOM
278
+ 'denominator'
279
+ when HQMF::PopulationCriteria::NUMER
280
+ 'numerator'
281
+ when HQMF::PopulationCriteria::EXCEP
282
+ 'denominatorException'
283
+ when HQMF::PopulationCriteria::DENEX
284
+ 'denominatorExclusion'
285
+ else
286
+ raise "Unknown population criteria type #{population_criteria_code}"
287
+ end
288
+ end
289
+ end
290
+
291
+ # Simple class to issue monotonically increasing integer identifiers
292
+ class Counter
293
+ def initialize
294
+ @count = 0
295
+ end
296
+
297
+ def new_id
298
+ @count+=1
299
+ end
300
+ end
301
+
302
+ # Singleton to keep a count of template identifiers
303
+ class TemplateCounter < Counter
304
+ include Singleton
305
+ end
306
+
307
+ end
308
+ end
@@ -0,0 +1,35 @@
1
+ module HQMF
2
+
3
+ class Attribute
4
+ include HQMF::Conversion::Utilities
5
+ attr_reader :id,:code,:value,:unit,:name
6
+ # @param [String] id
7
+ # @param [String] code
8
+ # @param [String] value
9
+ # @param [String] unit
10
+ # @param [String] name
11
+ def initialize(id,code,value,unit,name)
12
+ @id = id
13
+ @code = code
14
+ @value = value
15
+ @unit = unit
16
+ @name = name
17
+ end
18
+
19
+ def self.from_json(json)
20
+ id = json["id"] if json["id"]
21
+ code = json["code"] if json["code"]
22
+ value = json["value"] if json["value"]
23
+ unit = json["unit"] if json["unit"]
24
+ name = json["name"] if json["name"]
25
+
26
+ HQMF::Attribute.new(id,code,value,unit,name)
27
+ end
28
+
29
+ def to_json
30
+ json = build_hash(self, [:id,:code,:value,:unit,:name])
31
+ json
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,322 @@
1
+ module HQMF
2
+ # Represents a data criteria specification
3
+ class DataCriteria
4
+
5
+ include HQMF::Conversion::Utilities
6
+
7
+ SOURCE_DATA_CRITERIA_TEMPLATE_ID = '2.16.840.1.113883.3.100.1.1'
8
+ SOURCE_DATA_CRITERIA_TEMPLATE_TITLE = 'Source data criteria'
9
+
10
+ XPRODUCT = 'XPRODUCT'
11
+ UNION = 'UNION'
12
+
13
+ FIELDS = {'SEVERITY'=>{title:'Severity',coded_entry_method: :severity, code: 'SEV', code_system:'2.16.840.1.113883.5.4', template_id: '2.16.840.1.113883.3.560.1.1021.2'},
14
+ 'ORDINAL'=>{title:'Ordinal',coded_entry_method: :ordinal, code: '117363000', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1012.2'},
15
+ 'REASON'=>{title:'Reason',coded_entry_method: :reason, code: '410666004', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1017.2'},
16
+ 'SOURCE'=>{title:'Source',coded_entry_method: :source, code: '260753009', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.2001.2'},
17
+ 'CUMULATIVE_MEDICATION_DURATION'=>{title:'Cumulative Medication Duration',coded_entry_method: :cumulative_medication_duration, code: '363819003', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1001.3'},
18
+ 'FACILITY_LOCATION'=>{title:'Facility Location',coded_entry_method: :facility_location, code: 'SDLOC'},
19
+ 'FACILITY_LOCATION_ARRIVAL_DATETIME'=>{title:'Facility Location Arrival Date/Time',coded_entry_method: :facility_location_arrival, code: 'SDLOC_ARRIVAL'},
20
+ 'FACILITY_LOCATION_DEPARTURE_DATETIME'=>{title:'Facility Location Departure Date/Time',coded_entry_method: :facility_location_departure, code: 'SDLOC_DEPARTURE'},
21
+ 'DISCHARGE_DATETIME'=>{title:'Discharge Date/Time',coded_entry_method: :discharge_datetime, code: '442864001', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1025.1'},
22
+ 'DISCHARGE_STATUS'=>{title:'Discharge Status',coded_entry_method: :discharge_status, code: '309039003', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1003.2'},
23
+ 'ADMISSION_DATETIME'=>{title:'Admission Date/Time',coded_entry_method: :admission_datetime, code: '399423000', code_system:'2.16.840.1.113883.6.96'},
24
+ 'LENGTH_OF_STAY'=>{title:'Length of Stay',coded_entry_method: :length_of_stay, code: '183797002', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1029.3'},
25
+ 'DOSE'=>{title:'Dose',coded_entry_method: :dose, code: '398232005', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1004.1'},
26
+ 'ROUTE'=>{title:'Route',coded_entry_method: :route, code: '263513008', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1020.2'},
27
+ 'START_DATETIME'=>{title:'Start Date/Time',coded_entry_method: :start_datetime, code: '398201009', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1027.1'},
28
+ 'FREQUENCY'=>{title:'Frequency',coded_entry_method: :frequency, code: '260864003', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1006.1'},
29
+ 'ANATOMICAL_STRUCTURE'=>{title:'Anatomical Structure',coded_entry_method: :anatomical_structure, code: '91723000', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1000.2'},
30
+ 'STOP_DATETIME'=>{title:'Stop Date/Time',coded_entry_method: :stop_datetime, code: '397898000', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1026.1'},
31
+ 'INCISION_DATETIME'=>{title:'Incision Date/Time',coded_entry_method: :incision_datetime, code: '34896006', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1007.1'},
32
+ 'REMOVAL_DATETIME'=>{title:'Removal Date/Time',coded_entry_method: :removal_datetime, code: '118292001', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1032.1'}
33
+ }
34
+
35
+ VALUE_FIELDS = {'SEV' => 'SEVERITY',
36
+ '117363000' => 'ORDINAL',
37
+ '410666004' => 'REASON',
38
+ '260753009' => 'SOURCE',
39
+ '363819003' => 'CUMULATIVE_MEDICATION_DURATION',
40
+ 'SDLOC' => 'FACILITY_LOCATION',
41
+ '442864001' => 'DISCHARGE_DATETIME',
42
+ '309039003' => 'DISCHARGE_STATUS',
43
+ '399423000' => 'ADMISSION_DATETIME',
44
+ '183797002' => 'LENGTH_OF_STAY',
45
+ '398232005' => 'DOSE',
46
+ '263513008' => 'ROUTE',
47
+ '398201009' => 'START_DATETIME',
48
+ '260864003' =>'FREQUENCY',
49
+ '91723000' => 'ANATOMICAL_STRUCTURE',
50
+ '397898000' => 'STOP_DATETIME',
51
+ '34896006' => 'INCISION_DATETIME',
52
+ '118292001' =>'REMOVAL_DATETIME'
53
+ }
54
+
55
+
56
+ attr_reader :title, :description, :code_list_id, :children_criteria, :derivation_operator , :specific_occurrence, :specific_occurrence_const, :source_data_criteria
57
+ attr_accessor :id, :value, :field_values, :effective_time, :status, :temporal_references, :subset_operators, :definition, :inline_code_list, :negation_code_list_id, :negation, :display_name
58
+
59
+ # Create a new data criteria instance
60
+ # @param [String] id
61
+ # @param [String] title
62
+ # @param [String] display_name
63
+ # @param [String] description
64
+ # @param [String] code_list_id
65
+ # @param [String] negation_code_list_id
66
+ # @param [List<String>] children_criteria (ids of children data criteria)
67
+ # @param [String] derivation_operator
68
+ # @param [String] definition
69
+ # @param [String] status
70
+ # @param [Value|Range|Coded] value
71
+ # @param [Hash<String,Value|Range|Coded>] field_values
72
+ # @param [Range] effective_time
73
+ # @param [Hash<String,[String]>] inline_code_list
74
+ # @param [boolean] negation
75
+ # @param [String] negation_code_list_id
76
+ # @param [List<TemporalReference>] temporal_references
77
+ # @param [List<SubsetOperator>] subset_operators
78
+ # @param [String] specific_occurrence
79
+ # @param [String] specific_occurrence_const
80
+ # @param [String] source_data_criteria (id for the source data criteria, important for specific occurrences)
81
+ def initialize(id, title, display_name, description, code_list_id, children_criteria, derivation_operator, definition, status, value, field_values, effective_time, inline_code_list, negation, negation_code_list_id, temporal_references, subset_operators, specific_occurrence, specific_occurrence_const, source_data_criteria=nil)
82
+
83
+ status = normalize_status(definition, status)
84
+ @settings = HQMF::DataCriteria.get_settings_for_definition(definition, status)
85
+
86
+ @id = id
87
+ @title = title
88
+ @description = description
89
+ @code_list_id = code_list_id
90
+ @negation_code_list_id = negation_code_list_id
91
+ @children_criteria = children_criteria
92
+ @derivation_operator = derivation_operator
93
+ @definition = definition
94
+ @status = status
95
+ @value = value
96
+ @field_values = field_values
97
+ @effective_time = effective_time
98
+ @inline_code_list = inline_code_list
99
+ @negation = negation
100
+ @negation_code_list_id = negation_code_list_id
101
+ @temporal_references = temporal_references
102
+ @subset_operators = subset_operators
103
+ @specific_occurrence = specific_occurrence
104
+ @specific_occurrence_const = specific_occurrence_const
105
+ @source_data_criteria = source_data_criteria || id
106
+ end
107
+
108
+ # create a new data criteria given a category and sub_category. A sub category can either be a status or a sub category
109
+ def self.create_from_category(id, title, description, code_list_id, category, sub_category=nil, negation=false, negation_code_list_id=nil)
110
+ settings = HQMF::DataCriteria.get_settings_for_definition(category, sub_category)
111
+ HQMF::DataCriteria.new(id, title, nil, description, code_list_id, nil, nil, settings['definition'], settings['status'], nil, nil, nil, nil, negation, negation_code_list_id, nil, nil, nil,nil)
112
+ end
113
+
114
+ def standard_category
115
+ @settings['standard_category']
116
+ end
117
+ def qds_data_type
118
+ @settings['qds_data_type']
119
+ end
120
+ def type
121
+ @settings['category'].to_sym
122
+ end
123
+ def property
124
+ @settings['property'].to_sym unless @settings['property'].nil?
125
+ end
126
+ def patient_api_function
127
+ @settings['patient_api_function'].to_sym unless @settings['patient_api_function'].empty?
128
+ end
129
+ def hard_status
130
+ @settings['hard_status']
131
+ end
132
+
133
+ def definition=(definition)
134
+ @definition = definition
135
+ @settings = HQMF::DataCriteria.get_settings_for_definition(@definition, @status)
136
+ end
137
+ def status=(status)
138
+ @status = status
139
+ @settings = HQMF::DataCriteria.get_settings_for_definition(@definition, @status)
140
+ end
141
+
142
+ # Create a new data criteria instance from a JSON hash keyed with symbols
143
+ def self.from_json(id, json)
144
+ title = json["title"] if json["title"]
145
+ display_name = json["display_name"] if json["display_name"]
146
+ description = json["description"] if json["description"]
147
+ code_list_id = json["code_list_id"] if json["code_list_id"]
148
+ children_criteria = json["children_criteria"] if json["children_criteria"]
149
+ derivation_operator = json["derivation_operator"] if json["derivation_operator"]
150
+ definition = json["definition"] if json["definition"]
151
+ status = json["status"] if json["status"]
152
+ value = convert_value(json["value"]) if json["value"]
153
+ field_values = json["field_values"].inject({}){|memo,(k,v)| memo[k.to_s] = convert_value(v); memo} if json["field_values"]
154
+ effective_time = HQMF::Range.from_json(json["effective_time"]) if json["effective_time"]
155
+ inline_code_list = json["inline_code_list"].inject({}){|memo,(k,v)| memo[k.to_s] = v; memo} if json["inline_code_list"]
156
+ negation = json["negation"] || false
157
+ negation_code_list_id = json['negation_code_list_id'] if json['negation_code_list_id']
158
+ temporal_references = json["temporal_references"].map {|reference| HQMF::TemporalReference.from_json(reference)} if json["temporal_references"]
159
+ subset_operators = json["subset_operators"].map {|operator| HQMF::SubsetOperator.from_json(operator)} if json["subset_operators"]
160
+ specific_occurrence = json['specific_occurrence'] if json['specific_occurrence']
161
+ specific_occurrence_const = json['specific_occurrence_const'] if json['specific_occurrence_const']
162
+ source_data_criteria = json['source_data_criteria'] if json['source_data_criteria']
163
+
164
+ HQMF::DataCriteria.new(id, title, display_name, description, code_list_id, children_criteria, derivation_operator, definition, status, value, field_values,
165
+ effective_time, inline_code_list, negation, negation_code_list_id, temporal_references, subset_operators,specific_occurrence,specific_occurrence_const,source_data_criteria)
166
+ end
167
+
168
+ def to_json
169
+ json = base_json
170
+ {self.id.to_s.to_sym => json}
171
+ end
172
+
173
+ def base_json
174
+ x = nil
175
+ json = build_hash(self, [:title,:display_name,:description,:standard_category,:qds_data_type,:code_list_id,:children_criteria, :derivation_operator, :property, :type, :definition, :status, :hard_status, :negation, :negation_code_list_id,:specific_occurrence,:specific_occurrence_const,:source_data_criteria])
176
+ json[:children_criteria] = @children_criteria unless @children_criteria.nil? || @children_criteria.empty?
177
+ json[:value] = ((@value.is_a? String) ? @value : @value.to_json) if @value
178
+ json[:field_values] = @field_values.inject({}) {|memo,(k,v)| memo[k] = v.to_json; memo} if @field_values
179
+ json[:effective_time] = @effective_time.to_json if @effective_time
180
+ json[:inline_code_list] = @inline_code_list if @inline_code_list
181
+ json[:temporal_references] = x if x = json_array(@temporal_references)
182
+ json[:subset_operators] = x if x = json_array(@subset_operators)
183
+ json
184
+ end
185
+
186
+ def has_temporal(temporal_reference)
187
+ @temporal_references.reduce(false) {|found, item| found ||= item == temporal_reference }
188
+ end
189
+ def has_subset(subset_operator)
190
+ @subset_operators.reduce(false) {|found, item| found ||= item == subset_operator }
191
+ end
192
+
193
+ def self.statuses_by_definition
194
+ settings_file = File.expand_path('../data_criteria.json', __FILE__)
195
+ settings_map = JSON.parse(File.read(settings_file))
196
+ all_defs = (settings_map.map {|key, value| {category: value['category'],definition:value['definition'],status:(value['status'].empty? ? nil : value['status']), sub_category: value['sub_category'],title:value['title']} unless value['not_supported']}).compact
197
+ by_categories = {}
198
+ all_defs.each do |definition|
199
+ by_categories[definition[:category]]||={}
200
+ status = definition[:status]
201
+ def_key = definition[:definition]
202
+ if status.nil? and definition[:sub_category] and !definition[:sub_category].empty?
203
+ status = definition[:sub_category]
204
+ def_key = def_key.gsub("_#{status}",'')
205
+ end
206
+ by_categories[definition[:category]][def_key]||={category:def_key,statuses:[]}
207
+ by_categories[definition[:category]][def_key][:statuses] << status unless status.nil?
208
+ end
209
+ status_by_category = {}
210
+ by_categories.each {|key, value| status_by_category[key] = value.values}
211
+ status_by_category.delete('derived')
212
+ status_by_category.delete('variable')
213
+ status_by_category.delete('measurement_period')
214
+ status_by_category.values.flatten
215
+ end
216
+
217
+ def referenced_data_criteria(document)
218
+ referenced = []
219
+ if (@children_criteria)
220
+ @children_criteria.each do |id|
221
+ dc = document.data_criteria(id)
222
+ referenced << id
223
+ referenced.concat(dc.referenced_data_criteria(document))
224
+ end
225
+ end
226
+ if (@temporal_references)
227
+ @temporal_references.each do |tr|
228
+ id = tr.reference.id
229
+ if (id != HQMF::Document::MEASURE_PERIOD_ID)
230
+ dc = document.data_criteria(id)
231
+ referenced << id
232
+ referenced.concat(dc.referenced_data_criteria(document))
233
+ end
234
+ end
235
+ end
236
+ referenced
237
+ end
238
+
239
+ def self.get_settings_for_definition(definition, status)
240
+ settings_file = File.expand_path('../data_criteria.json', __FILE__)
241
+ settings_map = JSON.parse(File.read(settings_file))
242
+ key = definition + ((status.nil? || status.empty?) ? '' : "_#{status}")
243
+ settings = settings_map[key]
244
+
245
+ raise "data criteria is not supported #{key}" if settings.nil? || settings["not_supported"]
246
+
247
+ settings
248
+ end
249
+
250
+ def self.definition_for_template_id(template_id)
251
+ get_template_id_map()[template_id]
252
+ end
253
+
254
+ def self.template_id_for_definition(definition, status, negation)
255
+ get_template_id_map().key({'definition' => definition, 'status' => status || '', 'negation' => negation})
256
+ end
257
+
258
+ def self.title_for_template_id(template_id)
259
+ value = get_template_id_map()[template_id]
260
+ if value
261
+ settings = self.get_settings_for_definition(value['definition'], value['status'])
262
+ if settings
263
+ settings['title']
264
+ else
265
+ 'Unknown data criteria'
266
+ end
267
+ else
268
+ 'Unknown template id'
269
+ end
270
+ end
271
+
272
+ def self.get_template_id_map
273
+ @@template_id_map ||= read_template_id_map
274
+ @@template_id_map
275
+ end
276
+
277
+ private
278
+
279
+ def self.read_template_id_map
280
+ HealthDataStandards::Util::HQMFTemplateHelper.template_id_map
281
+ end
282
+
283
+ def normalize_status(definition, status)
284
+ return status if status.nil?
285
+ case status.downcase
286
+ when 'completed', 'complete'
287
+ case definition
288
+ when 'diagnosis'
289
+ 'active'
290
+ else
291
+ 'performed'
292
+ end
293
+ when 'order'
294
+ 'ordered'
295
+ else
296
+ status.downcase
297
+ end
298
+ end
299
+
300
+ def self.convert_value(json)
301
+ return nil unless json
302
+ value = nil
303
+ type = json["type"]
304
+ case type
305
+ when 'TS', 'PQ'
306
+ value = HQMF::Value.from_json(json)
307
+ when 'IVL_PQ'
308
+ value = HQMF::Range.from_json(json)
309
+ when 'CD'
310
+ value = HQMF::Coded.from_json(json)
311
+ when 'ANYNonNull'
312
+ value = HQMF::AnyValue.from_json(json)
313
+ else
314
+ raise "Unknown value type [#{type}]"
315
+ end
316
+ value
317
+ end
318
+
319
+
320
+ end
321
+
322
+ end