hqmf-parser 1.0.6 → 1.1.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.
data/Gemfile CHANGED
@@ -6,8 +6,7 @@ gem 'pry-nav'
6
6
  gem 'nokogiri'
7
7
  gem 'rubyzip'
8
8
 
9
- #gem "health-data-standards", :git => 'http://github.com/projectcypress/health-data-standards.git', :branch => 'develop'
10
- gem "health-data-standards", '~> 2.1.4'
9
+ gem "health-data-standards", '~> 2.2.0'
11
10
  gem "bson_ext"
12
11
 
13
12
  # below are gems required for excel spreadsheet processing
@@ -4,7 +4,7 @@ module HQMF2
4
4
  def self.render_template(name, params)
5
5
  template_path = File.expand_path(File.join('..', "#{name}.xml.erb"), __FILE__)
6
6
  template_str = File.read(template_path)
7
- template = ERB.new(template_str, nil, '-', "_templ#{TemplateCounter.instance.new_id}")
7
+ template = ERB.new(template_str, nil, '-', "_templ#{HQMF::Counter.instance.next}")
8
8
  context = ErbContext.new(params)
9
9
  template.result(context.get_binding)
10
10
  end
@@ -288,21 +288,5 @@ module HQMF2
288
288
  end
289
289
  end
290
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
291
  end
308
292
  end
@@ -180,7 +180,7 @@
180
180
  "definition":"encounter",
181
181
  "status":"active",
182
182
  "sub_category":"",
183
- "hard_status":false,
183
+ "hard_status":true,
184
184
  "patient_api_function":"encounters",
185
185
  "standard_category":"encounter",
186
186
  "qds_data_type":"encounter",
@@ -202,7 +202,7 @@
202
202
  "definition":"encounter",
203
203
  "status":"ordered",
204
204
  "sub_category":"",
205
- "hard_status":false,
205
+ "hard_status":true,
206
206
  "patient_api_function":"encounters",
207
207
  "standard_category":"encounter",
208
208
  "qds_data_type":"encounter",
@@ -213,7 +213,7 @@
213
213
  "definition":"encounter",
214
214
  "status":"recommended",
215
215
  "sub_category":"",
216
- "hard_status":false,
216
+ "hard_status":true,
217
217
  "patient_api_function":"encounters",
218
218
  "standard_category":"encounter",
219
219
  "qds_data_type":"encounter",
@@ -246,7 +246,7 @@
246
246
  "definition":"procedure",
247
247
  "status":"ordered",
248
248
  "sub_category":"",
249
- "hard_status":false,
249
+ "hard_status":true,
250
250
  "patient_api_function":"allProcedures",
251
251
  "standard_category":"procedure",
252
252
  "qds_data_type":"procedure_performed",
@@ -290,7 +290,7 @@
290
290
  "definition":"procedure",
291
291
  "status":"recommended",
292
292
  "sub_category":"",
293
- "hard_status":false,
293
+ "hard_status":true,
294
294
  "patient_api_function":"allProcedures",
295
295
  "standard_category":"procedure",
296
296
  "qds_data_type":"procedure_performed",
@@ -331,10 +331,10 @@
331
331
  "diagnosis_family_history":{
332
332
  "title":"diagnosis, family history",
333
333
  "category":"conditions",
334
- "definition":"diagnosis_family_history",
335
- "status":"",
334
+ "definition":"diagnosis",
335
+ "status":"family_history",
336
336
  "sub_category":"family_history",
337
- "hard_status":false,
337
+ "hard_status":true,
338
338
  "patient_api_function":"allProblems",
339
339
  "standard_category":"diagnosis_condition_problem",
340
340
  "qds_data_type":"diagnosis_active",
@@ -422,7 +422,7 @@
422
422
  "definition":"diagnostic_study",
423
423
  "status":"ordered",
424
424
  "sub_category":"",
425
- "hard_status":false,
425
+ "hard_status":true,
426
426
  "patient_api_function":"allProcedures",
427
427
  "standard_category":"diagnostic_study",
428
428
  "qds_data_type":"diagnostic_study_performed",
@@ -444,7 +444,7 @@
444
444
  "definition":"medication",
445
445
  "status":"dispensed",
446
446
  "sub_category":"",
447
- "hard_status":false,
447
+ "hard_status":true,
448
448
  "patient_api_function":"allMedications",
449
449
  "standard_category":"medication",
450
450
  "qds_data_type":"medication_dispensed",
@@ -455,7 +455,7 @@
455
455
  "definition":"medication",
456
456
  "status":"ordered",
457
457
  "sub_category":"",
458
- "hard_status":false,
458
+ "hard_status":true,
459
459
  "patient_api_function":"allMedications",
460
460
  "standard_category":"medication",
461
461
  "qds_data_type":"medication_order",
@@ -477,7 +477,7 @@
477
477
  "definition":"medication",
478
478
  "status":"administered",
479
479
  "sub_category":"",
480
- "hard_status":false,
480
+ "hard_status":true,
481
481
  "patient_api_function":"allMedications",
482
482
  "standard_category":"medication",
483
483
  "qds_data_type":"medication_administered",
@@ -486,9 +486,9 @@
486
486
  "title":"medication, discharge",
487
487
  "category":"medications",
488
488
  "definition":"medication_discharge",
489
- "status":"",
489
+ "status":"discharge",
490
490
  "sub_category":"discharge",
491
- "hard_status":false,
491
+ "hard_status":true,
492
492
  "patient_api_function":"allMedications",
493
493
  "standard_category":"medication",
494
494
  "qds_data_type":"medication_discharge",
@@ -543,7 +543,7 @@
543
543
  "definition":"physical_exam",
544
544
  "status":"ordered",
545
545
  "sub_category":"",
546
- "hard_status":false,
546
+ "hard_status":true,
547
547
  "patient_api_function":"procedureResults",
548
548
  "standard_category":"physical_exam",
549
549
  "qds_data_type":"physical_exam",
@@ -565,7 +565,7 @@
565
565
  "definition":"physical_exam",
566
566
  "status":"recommended",
567
567
  "sub_category":"",
568
- "hard_status":false,
568
+ "hard_status":true,
569
569
  "patient_api_function":"procedureResults",
570
570
  "standard_category":"physical_exam",
571
571
  "qds_data_type":"physical_exam",
@@ -620,7 +620,7 @@
620
620
  "definition":"laboratory_test",
621
621
  "status":"ordered",
622
622
  "sub_category":"",
623
- "hard_status":false,
623
+ "hard_status":true,
624
624
  "patient_api_function":"laboratoryTests",
625
625
  "standard_category":"laboratory_test",
626
626
  "qds_data_type":"laboratory_test",
@@ -631,7 +631,7 @@
631
631
  "definition":"laboratory_test",
632
632
  "status":"recommended",
633
633
  "sub_category":"",
634
- "hard_status":false,
634
+ "hard_status":true,
635
635
  "patient_api_function":"laboratoryTests",
636
636
  "standard_category":"laboratory_test",
637
637
  "qds_data_type":"laboratory_test",
@@ -697,7 +697,7 @@
697
697
  "definition":"device",
698
698
  "status":"applied",
699
699
  "sub_category":"",
700
- "hard_status":false,
700
+ "hard_status":true,
701
701
  "patient_api_function":"allDevices",
702
702
  "standard_category":"device",
703
703
  "qds_data_type":"device_applied",
@@ -741,7 +741,7 @@
741
741
  "definition":"device",
742
742
  "status":"ordered",
743
743
  "sub_category":"",
744
- "hard_status":false,
744
+ "hard_status":true,
745
745
  "patient_api_function":"allDevices",
746
746
  "standard_category":"device",
747
747
  "qds_data_type":"device_applied",
@@ -752,7 +752,7 @@
752
752
  "definition":"device",
753
753
  "status":"recommended",
754
754
  "sub_category":"",
755
- "hard_status":false,
755
+ "hard_status":true,
756
756
  "patient_api_function":"allDevices",
757
757
  "standard_category":"device",
758
758
  "qds_data_type":"device_applied",
@@ -774,7 +774,7 @@
774
774
  "definition":"substance",
775
775
  "status":"administered",
776
776
  "sub_category":"",
777
- "hard_status":false,
777
+ "hard_status":true,
778
778
  "patient_api_function":"allMedications",
779
779
  "standard_category":"medication",
780
780
  "qds_data_type":"medication_administered",
@@ -785,7 +785,7 @@
785
785
  "definition":"substance",
786
786
  "status":"ordered",
787
787
  "sub_category":"",
788
- "hard_status":false,
788
+ "hard_status":true,
789
789
  "patient_api_function":"allMedications",
790
790
  "standard_category":"medication",
791
791
  "qds_data_type":"medication_order",
@@ -829,7 +829,7 @@
829
829
  "definition":"substance",
830
830
  "status":"recommended",
831
831
  "sub_category":"",
832
- "hard_status":false,
832
+ "hard_status":true,
833
833
  "patient_api_function":"allMedications",
834
834
  "standard_category":"medication",
835
835
  "qds_data_type":"medication_order",
@@ -873,7 +873,7 @@
873
873
  "definition":"intervention",
874
874
  "status":"ordered",
875
875
  "sub_category":"",
876
- "hard_status":false,
876
+ "hard_status":true,
877
877
  "patient_api_function":"allProcedures",
878
878
  "standard_category":"procedure",
879
879
  "qds_data_type":"procedure_performed",
@@ -906,7 +906,7 @@
906
906
  "definition":"intervention",
907
907
  "status":"recommended",
908
908
  "sub_category":"",
909
- "hard_status":false,
909
+ "hard_status":true,
910
910
  "patient_api_function":"allProcedures",
911
911
  "standard_category":"procedure",
912
912
  "qds_data_type":"procedure_performed",
@@ -939,7 +939,7 @@
939
939
  "definition":"symptom",
940
940
  "status":"assessed",
941
941
  "sub_category":"",
942
- "hard_status":false,
942
+ "hard_status":true,
943
943
  "patient_api_function":"allProblems",
944
944
  "standard_category":"symptom",
945
945
  "qds_data_type":"symptom",
@@ -994,7 +994,7 @@
994
994
  "definition":"functional_status",
995
995
  "status":"ordered",
996
996
  "sub_category":"",
997
- "hard_status":false,
997
+ "hard_status":true,
998
998
  "patient_api_function":"procedureResults",
999
999
  "standard_category":"procedure",
1000
1000
  "qds_data_type":"procedure_result",
@@ -1005,7 +1005,7 @@
1005
1005
  "definition":"functional_status",
1006
1006
  "status":"recommended",
1007
1007
  "sub_category":"",
1008
- "hard_status":false,
1008
+ "hard_status":true,
1009
1009
  "patient_api_function":"procedureResults",
1010
1010
  "standard_category":"procedure",
1011
1011
  "qds_data_type":"procedure_result",
@@ -1105,7 +1105,7 @@
1105
1105
  "status":"",
1106
1106
  "sub_category":"from",
1107
1107
  "hard_status":false,
1108
- "patient_api_function":"",
1108
+ "patient_api_function":"encounters",
1109
1109
  "standard_category":"",
1110
1110
  "qds_data_type":"",
1111
1111
  "not_supported":false},
@@ -1116,7 +1116,7 @@
1116
1116
  "status":"",
1117
1117
  "sub_category":"to",
1118
1118
  "hard_status":false,
1119
- "patient_api_function":"",
1119
+ "patient_api_function":"encounters",
1120
1120
  "standard_category":"",
1121
1121
  "qds_data_type":"",
1122
1122
  "not_supported":false}
@@ -10,26 +10,28 @@ module HQMF
10
10
  XPRODUCT = 'XPRODUCT'
11
11
  UNION = 'UNION'
12
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'}
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', field_type: :value},
14
+ 'ORDINAL' => {title:'Ordinal', coded_entry_method: :ordinality, code: '117363000', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1012.2', field_type: :value},
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', field_type: :value},
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', field_type: :value},
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', field_type: :value},
18
+ 'FACILITY_LOCATION' => {title:'Facility Location', coded_entry_method: :facility, code: 'SDLOC', field_type: :value},
19
+ 'FACILITY_LOCATION_ARRIVAL_DATETIME' => {title:'Facility Location Arrival Date/Time', coded_entry_method: :facility_arrival, code: 'SDLOC_ARRIVAL', field_type: :nested_timestamp},
20
+ 'FACILITY_LOCATION_DEPARTURE_DATETIME' => {title:'Facility Location Departure Date/Time', coded_entry_method: :facility_departure, code: 'SDLOC_DEPARTURE', field_type: :nested_timestamp},
21
+ 'DISCHARGE_DATETIME' => {title:'Discharge Date/Time', coded_entry_method: :discharge_time, code: '442864001', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1025.1', field_type: :timestamp},
22
+ 'DISCHARGE_STATUS' => {title:'Discharge Status', coded_entry_method: :discharge_disposition, code: '309039003', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1003.2', field_type: :value},
23
+ 'ADMISSION_DATETIME' => {title:'Admission Date/Time', coded_entry_method: :admit_time, code: '399423000', code_system:'2.16.840.1.113883.6.96', field_type: :timestamp},
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', field_type: :value},
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', field_type: :value},
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', field_type: :value},
27
+ 'START_DATETIME' => {title:'Start Date/Time', coded_entry_method: :start_date, code: '398201009', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1027.1', field_type: :timestamp},
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', field_type: :value},
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', field_type: :value},
30
+ 'STOP_DATETIME' => {title:'Stop Date/Time', coded_entry_method: :end_date, code: '397898000', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1026.1', field_type: :timestamp},
31
+ 'INCISION_DATETIME' => {title:'Incision Date/Time', coded_entry_method: :incision_time, code: '34896006', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1007.1', field_type: :timestamp},
32
+ 'REMOVAL_DATETIME' => {title:'Removal Date/Time', coded_entry_method: :removal_time, code: '118292001', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1032.1', field_type: :timestamp},
33
+ 'TRANSFER_TO' => {title:'Transfer To', coded_entry_method: :transfer_to, code: 'TRANSFER_TO', template_id: '2.16.840.1.113883.3.560.1.72', field_type: :value},
34
+ 'TRANSFER_FROM' => {title:'Transfer From', coded_entry_method: :transfer_from, code: 'TRANSFER_FROM', template_id: '2.16.840.1.113883.3.560.1.71', field_type: :value}
33
35
  }
34
36
 
35
37
  VALUE_FIELDS = {'SEV' => 'SEVERITY',
@@ -176,7 +178,7 @@ module HQMF
176
178
  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])
177
179
  json[:children_criteria] = @children_criteria unless @children_criteria.nil? || @children_criteria.empty?
178
180
  json[:value] = ((@value.is_a? String) ? @value : @value.to_json) if @value
179
- json[:field_values] = @field_values.inject({}) {|memo,(k,v)| memo[k] = v.to_json; memo} if @field_values
181
+ json[:field_values] = @field_values.inject({}) {|memo,(k,v)| memo[k] = (!v.nil? ? v.to_json : nil); memo} if @field_values
180
182
  json[:effective_time] = @effective_time.to_json if @effective_time
181
183
  json[:inline_code_list] = @inline_code_list if @inline_code_list
182
184
  json[:temporal_references] = x if x = json_array(@temporal_references)
@@ -236,6 +238,26 @@ module HQMF
236
238
  end
237
239
  referenced
238
240
  end
241
+
242
+ def all_code_set_oids
243
+
244
+ # root oid
245
+ referenced_oids = [code_list_id]
246
+
247
+ # value oid
248
+ referenced_oids << value.code_list_id if value != nil and value.type == 'CD'
249
+
250
+ # negation oid
251
+ referenced_oids << negation_code_list_id if negation_code_list_id != nil
252
+
253
+ # field oids
254
+ if field_values != nil
255
+ referenced_oids.concat (field_values.map {|key,field| field.code_list_id if field != nil and field.type == 'CD'})
256
+ end
257
+
258
+ referenced_oids
259
+
260
+ end
239
261
 
240
262
  def self.get_settings_for_definition(definition, status)
241
263
  settings_file = File.expand_path('../data_criteria.json', __FILE__)
@@ -299,9 +321,7 @@ module HQMF
299
321
  end
300
322
 
301
323
  def self.convert_value(json)
302
- # null check is a hack for right now to deal with criteria that are not currenty properly parsed
303
- return nil if json.nil? || json == "null"
304
- value = nil
324
+ return nil unless json.present?
305
325
  type = json["type"]
306
326
  case type
307
327
  when 'TS', 'PQ'
@@ -313,7 +333,6 @@ module HQMF
313
333
  when 'ANYNonNull'
314
334
  value = HQMF::AnyValue.from_json(json)
315
335
  else
316
-
317
336
  raise "Unknown value type [#{type}]"
318
337
  end
319
338
  value
@@ -127,6 +127,10 @@ module HQMF
127
127
  @data_criteria
128
128
  end
129
129
 
130
+ def all_code_set_oids
131
+ (@data_criteria.map {|d| d.all_code_set_oids }).flatten.compact.uniq
132
+ end
133
+
130
134
  # Get the source data criteria that are specific occurrences
131
135
  # @return [Array] an array of HQMF::DataCriteria describing the data elements used by the measure that are specific occurrences
132
136
  def specific_occurrence_source_data_criteria
@@ -13,8 +13,9 @@ module HQMF
13
13
  DENEXCEP = 'DENEXCEP'
14
14
  DENEX = 'DENEX'
15
15
  MSRPOPL = 'MSRPOPL'
16
+ OBSERV = 'OBSERV'
16
17
 
17
- ALL_POPULATION_CODES = [IPP, DENOM, NUMER, DENEXCEP, DENEX, MSRPOPL]
18
+ ALL_POPULATION_CODES = [IPP, DENOM, NUMER, DENEXCEP, DENEX, MSRPOPL, OBSERV]
18
19
 
19
20
  # Create a new population criteria
20
21
  # @param [String] id
@@ -63,7 +64,7 @@ module HQMF
63
64
  def conjunction_code
64
65
 
65
66
  case @type
66
- when IPP, DENOM, NUMER
67
+ when IPP, DENOM, NUMER, MSRPOPL
67
68
  HQMF::Precondition::ALL_TRUE
68
69
  when DENEXCEP, DENEX
69
70
  HQMF::Precondition::AT_LEAST_ONE_TRUE
@@ -14,6 +14,12 @@ module HQMF
14
14
  ALL_TRUE => AT_LEAST_ONE_FALSE,
15
15
  AT_LEAST_ONE_FALSE => ALL_TRUE
16
16
  }
17
+ INVERSIONS = {
18
+ AT_LEAST_ONE_TRUE => ALL_TRUE,
19
+ ALL_FALSE => AT_LEAST_ONE_FALSE,
20
+ ALL_TRUE => AT_LEAST_ONE_TRUE,
21
+ AT_LEAST_ONE_FALSE => ALL_FALSE
22
+ }
17
23
 
18
24
  attr_reader :id, :preconditions, :reference, :conjunction_code
19
25
  attr_accessor :negation
@@ -257,7 +257,7 @@ module HQMF
257
257
  class SubsetOperator
258
258
  include HQMF::Conversion::Utilities
259
259
 
260
- TYPES = ['COUNT', 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'FIFTH', 'RECENT', 'LAST', 'MIN', 'MAX', 'DATEDIFF']
260
+ TYPES = ['COUNT', 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'FIFTH', 'RECENT', 'LAST', 'MIN', 'MAX', 'DATEDIFF', 'TIMEDIFF', 'MEDIAN', 'MEAN']
261
261
 
262
262
  attr_accessor :type, :value
263
263
  # @param [String] type
@@ -6,6 +6,7 @@ require 'ostruct'
6
6
  require 'health-data-standards'
7
7
 
8
8
  # require_relative
9
+ require_relative 'util/counter.rb'
9
10
  require_relative 'hqmf-model/utilities.rb'
10
11
 
11
12
  require_relative 'hqmf-parser/1.0/utilities'
@@ -14,6 +15,7 @@ require_relative 'hqmf-parser/1.0/document'
14
15
  require_relative 'hqmf-parser/1.0/data_criteria'
15
16
  require_relative 'hqmf-parser/1.0/attribute'
16
17
  require_relative 'hqmf-parser/1.0/population_criteria'
18
+ require_relative 'hqmf-parser/1.0/observation'
17
19
  require_relative 'hqmf-parser/1.0/precondition'
18
20
  require_relative 'hqmf-parser/1.0/restriction'
19
21
  require_relative 'hqmf-parser/1.0/comparison'
@@ -4,11 +4,11 @@ module HQMF1
4
4
 
5
5
  include HQMF1::Utilities
6
6
 
7
- attr_accessor :code_list_id, :derived_from, :definition, :status, :negation, :specific_occurrence, :specific_occurrence_const
7
+ attr_accessor :code_list_id, :derived_from, :definition, :status, :negation, :specific_occurrence
8
8
 
9
9
  # Create a new instance based on the supplied HQMF entry
10
10
  # @param [Nokogiri::XML::Element] entry the parsed HQMF entry
11
- def initialize(entry)
11
+ def initialize(entry, occurrence_counters)
12
12
  @entry = entry
13
13
 
14
14
  template_map = HQMF::DataCriteria.get_template_id_map()
@@ -21,8 +21,8 @@ module HQMF1
21
21
  if derived_entry
22
22
  derived = derived_entry.at_xpath('cda:act/cda:id/@root') || derived_entry.at_xpath('cda:observation/cda:id/@root')
23
23
  @derived_from = derived.value
24
- @@occurrences[@derived_from] ||= Counter.new
25
- @occurrence_key = @@occurrences[@derived_from].next
24
+ occurrence_counters[@derived_from] ||= HQMF::InstanceCounter.new
25
+ @occurrence_key = occurrence_counters[@derived_from].next-1
26
26
  @specific_occurrence = "#{('A'..'ZZ').to_a[@occurrence_key]}"
27
27
  end
28
28
 
@@ -74,31 +74,18 @@ module HQMF1
74
74
  def const_name
75
75
  components = title.gsub(/\W/,' ').split.collect {|word| word.strip.upcase }
76
76
  if @derived_from
77
- components << @@id.next
78
- @specific_occurrence_const = (description.gsub(/\W/,' ').split.collect {|word| word.strip.upcase }).join '_'
77
+ components << HQMF::Counter.instance.next
79
78
  end
80
79
  components.join '_'
81
80
  end
82
81
 
83
82
  def to_json
84
- json = build_hash(self, [:id,:title,:code_list_id,:derived_from,:description, :definition, :status, :negation, :specific_occurrence,:specific_occurrence_const])
83
+ json = build_hash(self, [:id,:title,:code_list_id,:derived_from,:description, :definition, :status, :negation, :specific_occurrence])
85
84
  {
86
85
  self.const_name => json
87
86
  }
88
87
  end
89
88
 
90
- # Simple class to issue monotonically increasing integer identifiers
91
- class Counter
92
- def initialize
93
- @count = -1
94
- end
95
-
96
- def next
97
- @count+=1
98
- end
99
- end
100
- @@id = Counter.new
101
- @@occurrences = {}
102
89
 
103
90
  end
104
91
 
@@ -9,9 +9,11 @@ module HQMF1
9
9
  # Create a new HQMF1::Document instance by parsing the supplied contents
10
10
  # @param [String] hqmf_contents the contents of an HQMF v1.0 document
11
11
  def initialize(hqmf_contents)
12
+
12
13
  @doc = Document.parse(hqmf_contents)
14
+ occurrence_counters = {}
13
15
  @data_criteria = @doc.xpath('//cda:section[cda:code/@code="57025-9"]/cda:entry').collect do |entry|
14
- DataCriteria.new(entry)
16
+ DataCriteria.new(entry, occurrence_counters)
15
17
  end
16
18
 
17
19
  backfill_derived_code_lists
@@ -22,6 +24,10 @@ module HQMF1
22
24
  @population_criteria = @doc.xpath('//cda:section[cda:code/@code="57026-7"]/cda:entry').collect do |attr|
23
25
  PopulationCriteria.new(attr, self)
24
26
  end
27
+ observations = @doc.xpath('//cda:section[cda:code/@code="57027-5"]/cda:entry').collect do |attr|
28
+ Observation.new(attr, self)
29
+ end
30
+ @population_criteria.concat(observations)
25
31
 
26
32
  @stratification = @doc.xpath('//cda:section[cda:code/@code="69669-0"]/cda:entry').collect do |attr|
27
33
  PopulationCriteria.new(attr, self)
@@ -153,7 +159,7 @@ module HQMF1
153
159
  criteria_json = criteria.to_json
154
160
  # check if the key already exists... if it does redefine the key
155
161
  if (json[:data_criteria][criteria_json.keys.first])
156
- criteria_json = {"#{criteria_json.keys.first}_#{@@ids.next}" => criteria_json.values.first}
162
+ criteria_json = {"#{criteria_json.keys.first}_#{HQMF::Counter.instance.next}" => criteria_json.values.first}
157
163
  end
158
164
  json[:data_criteria].merge! criteria_json
159
165
  end
@@ -193,17 +199,5 @@ module HQMF1
193
199
  collection.find {|e| e.send(attribute)==value}
194
200
  end
195
201
 
196
- # Simple class to issue monotonically increasing integer identifiers
197
- class Counter
198
- def initialize
199
- @count = 0
200
- end
201
-
202
- def next
203
- @count+=1
204
- end
205
- end
206
- @@ids = Counter.new
207
-
208
202
  end
209
203
  end
@@ -33,6 +33,12 @@ module HQMF1
33
33
  "MAX"
34
34
  when /^DATEDIFF(.*)$/
35
35
  "DATEDIFF"
36
+ when /^TIMEDIFF(.*)$/
37
+ "TIMEDIFF"
38
+ when /^MEDIAN(.*)$/
39
+ "MEDIAN"
40
+ when /^AVG(.*)$/
41
+ "MEAN"
36
42
  else
37
43
  raise "unknown expression type: #{@text}"
38
44
  end
@@ -0,0 +1,61 @@
1
+ module HQMF1
2
+ # Represents an HQMF population criteria
3
+ class Observation
4
+
5
+ include HQMF1::Utilities
6
+
7
+ attr_reader :preconditions, :entry, :doc
8
+ attr_accessor :id, :hqmf_id, :stratification_id
9
+
10
+ # Create a new population criteria from the supplied HQMF entry
11
+ # @param [Nokogiri::XML::Element] the HQMF entry
12
+ def initialize(entry, doc)
13
+ @doc = doc
14
+ @entry = entry
15
+ @id = attr_val('cda:observation/cda:id/@root').upcase
16
+ @preconditions = [Precondition.new(@entry, nil, @doc)]
17
+ end
18
+
19
+ # Get the code for the population criteria
20
+ # @return [String] the code (e.g. IPP, DEMON, NUMER, DENEX, DENEXCEP)
21
+ def code
22
+ HQMF::PopulationCriteria::OBSERV
23
+ end
24
+
25
+ # Get the id for the population criteria, used elsewhere in the HQMF document to
26
+ # refer to this criteria
27
+ # @return [String] the id
28
+ def id
29
+ @id
30
+ end
31
+
32
+ def title
33
+ "Measure Observation"
34
+ end
35
+
36
+ def reference
37
+ nil
38
+ end
39
+
40
+ def to_json
41
+
42
+ json = {}
43
+ self.preconditions.compact.each do |precondition|
44
+ json[:preconditions] ||= []
45
+ json[:preconditions] << precondition.to_json
46
+ end
47
+ json[:preconditions].each {|p| p[:conjunction] ||= "AND"}
48
+
49
+ json[:id] = id
50
+ json[:title] = title
51
+ json[:code] = code
52
+ json[:hqmf_id] = hqmf_id if hqmf_id
53
+ json[:stratification_id] = stratification_id if stratification_id
54
+ json[:reference] = reference
55
+
56
+ {self.code => json}
57
+
58
+ end
59
+
60
+ end
61
+ end
@@ -4,7 +4,7 @@ module HQMF1
4
4
 
5
5
  include HQMF1::Utilities
6
6
 
7
- attr_reader :range, :comparison, :restrictions, :subset, :preconditions
7
+ attr_reader :range, :comparison, :restrictions, :subset, :preconditions, :expression
8
8
  attr_accessor :from_parent
9
9
 
10
10
  def initialize(entry, parent, doc)
@@ -28,7 +28,9 @@ module HQMF1
28
28
  @subset = local_subset
29
29
  end
30
30
 
31
- #@subset = attr_val('./cda:subsetCode/@code')
31
+ if @entry.at_xpath('./*/cda:derivationExpr')
32
+ @expression = Expression.new(@entry)
33
+ end
32
34
 
33
35
  comparison_def = @entry.at_xpath('./*/cda:sourceOf[@typeCode="COMP"]')
34
36
  if comparison_def
@@ -150,6 +152,7 @@ module HQMF1
150
152
  json[:comparison] = comparison.to_json if comparison
151
153
  json[:restrictions] = json_array(self.restrictions)
152
154
  json[:preconditions] = json_array(self.preconditions)
155
+ json[:expression] = self.expression.to_json if self.expression
153
156
  json
154
157
  end
155
158
 
@@ -53,7 +53,7 @@ module HQMF
53
53
  # make sure nobody else is going to delete the criteria we've grouped
54
54
  criteria_ids.each {|target| validate_not_deleted(target)}
55
55
 
56
- id = "#{parent_id}_#{section}_#{@@ids.next}"
56
+ id = "#{parent_id}_#{section}_#{HQMF::Counter.instance.next}"
57
57
  title = "#{id}"
58
58
  description = ""
59
59
  definition = 'derived'
@@ -170,12 +170,13 @@ module HQMF
170
170
  negation = criteria[:negation]
171
171
  negation_code_list_id = criteria[:negation_code_list_id]
172
172
  specific_occurrence = criteria[:specific_occurrence]
173
- specific_occurrence_const = criteria[:specific_occurrence_const]
173
+ specific_occurrence_const = nil
174
174
 
175
175
  # specific occurrences do not properly set the description, so we want to add the definition and status
176
176
  if (specific_occurrence)
177
- _status = ", #{status.titleize}" if status
178
- description = "#{definition.titleize}#{_status}: #{description}"
177
+ statusText = ", #{status.titleize}" if status
178
+ description = "#{definition.titleize}#{statusText}: #{description}"
179
+ specific_occurrence_const = (description.gsub(/\W/,' ').split.collect {|word| word.strip.upcase }).join '_'
179
180
  end
180
181
 
181
182
  value = nil # value is filled out by backfill_patient_characteristics for things like gender and by REFR restrictions
@@ -189,6 +190,14 @@ module HQMF
189
190
  inline_code_list = nil # inline code list is only used in HQMF V2, so we can just pass in nil
190
191
  display_name=nil
191
192
 
193
+ # transfers should be modeled as a field. The code_list_id of the transfer data criteria is cleared and the oid is added to a transfer field
194
+ # The definition of the data criteria is still transfer, but it is marked as an encounter using the patient api funciton.
195
+ if ['transfer_to', 'transfer_from'].include? definition
196
+ field_values ||= {}
197
+ field_values[definition.upcase] = HQMF::Coded.for_code_list(code_list_id, title)
198
+ code_list_id = nil
199
+ end
200
+
192
201
  HQMF::DataCriteria.new(id, title, display_name, description, code_list_id, children_criteria, derivation_operator, definition, status,
193
202
  value, field_values, effective_time, inline_code_list, negation, negation_code_list_id, temporal_references, subset_operators,specific_occurrence,specific_occurrence_const)
194
203
 
@@ -239,18 +248,5 @@ module HQMF
239
248
  key.to_s.downcase.gsub('_', ' ').split(' ').map {|w| w.capitalize }.join('')
240
249
  end
241
250
 
242
- # Simple class to issue monotonically increasing integer identifiers
243
- class Counter
244
- def initialize
245
- @count = 0
246
- end
247
-
248
- def next
249
- @count+=1
250
- end
251
- end
252
- @@ids = Counter.new
253
-
254
-
255
251
  end
256
252
  end
@@ -98,8 +98,8 @@ module HQMF
98
98
  data_criteria.definition = 'patient_characteristic_birthdate'
99
99
  end
100
100
  # this is looking for a gender characteristic that is set as a generic characteristic
101
- gender_key = (value_set.keys.select {|set| set.start_with? 'HL7'}).first
102
- if (gender_key and ['M','F'].include? value_set[gender_key].first)
101
+ gender_key = (value_set.keys.select {|set| set == 'Administrative Sex' || set == 'AdministrativeSex'}).first
102
+ if (gender_key and ['M','F'].include? value_set[gender_key].first)
103
103
  data_criteria.definition = 'patient_characteristic_gender'
104
104
  data_criteria.value = HQMF::Coded.new('CD','Gender',value_set[gender_key].first)
105
105
  end
@@ -26,18 +26,22 @@ module HQMF
26
26
  denoms = @population_criteria_by_id.select {|key, value| value.type == HQMF::PopulationCriteria::DENOM}
27
27
  nums = @population_criteria_by_id.select {|key, value| value.type == HQMF::PopulationCriteria::NUMER}
28
28
  msrpopls = @population_criteria_by_id.select {|key, value| value.type == HQMF::PopulationCriteria::MSRPOPL}
29
+ observs = @population_criteria_by_id.select {|key, value| value.type == HQMF::PopulationCriteria::OBSERV}
29
30
  excls = @population_criteria_by_id.select {|key, value| value.type == HQMF::PopulationCriteria::DENEX}
30
31
  denexcs = @population_criteria_by_id.select {|key, value| value.type == HQMF::PopulationCriteria::DENEXCEP}
31
32
 
32
- if (ipps.size<=1 and denoms.size<=1 and nums.size<=1 and excls.size<=1 and denexcs.size<=1 )
33
- @sub_measures <<
34
- {
35
- HQMF::PopulationCriteria::IPP => HQMF::PopulationCriteria::IPP,
36
- HQMF::PopulationCriteria::DENOM => HQMF::PopulationCriteria::DENOM,
37
- HQMF::PopulationCriteria::NUMER => HQMF::PopulationCriteria::NUMER,
38
- HQMF::PopulationCriteria::DENEXCEP => HQMF::PopulationCriteria::DENEXCEP,
39
- HQMF::PopulationCriteria::DENEX => HQMF::PopulationCriteria::DENEX
40
- }
33
+ if (ipps.size<=1 and denoms.size<=1 and nums.size<=1 and excls.size<=1 and denexcs.size<=1 and msrpopls.size<=1 and observs.size<=1 )
34
+ sub_measure = {}
35
+
36
+ sub_measure[HQMF::PopulationCriteria::IPP] = HQMF::PopulationCriteria::IPP if ipps.size > 0
37
+ sub_measure[HQMF::PopulationCriteria::DENOM] = HQMF::PopulationCriteria::DENOM if denoms.size > 0
38
+ sub_measure[HQMF::PopulationCriteria::NUMER] = HQMF::PopulationCriteria::NUMER if nums.size > 0
39
+ sub_measure[HQMF::PopulationCriteria::DENEXCEP] = HQMF::PopulationCriteria::DENEXCEP if denexcs.size > 0
40
+ sub_measure[HQMF::PopulationCriteria::DENEX] = HQMF::PopulationCriteria::DENEX if excls.size > 0
41
+ sub_measure[HQMF::PopulationCriteria::MSRPOPL] = HQMF::PopulationCriteria::MSRPOPL if msrpopls.size > 0
42
+ sub_measure[HQMF::PopulationCriteria::OBSERV] = HQMF::PopulationCriteria::OBSERV if observs.size > 0
43
+
44
+ @sub_measures << sub_measure
41
45
  else
42
46
 
43
47
  nums.each do |num_id, num|
@@ -51,6 +55,8 @@ module HQMF
51
55
 
52
56
  apply_to_submeasures(@sub_measures, HQMF::PopulationCriteria::DENEX, excls.values, HQMF::PopulationCriteria::IPP, get_unmatched_population_keys(ipps, excls))
53
57
  apply_to_submeasures(@sub_measures, HQMF::PopulationCriteria::DENEXCEP, denexcs.values, HQMF::PopulationCriteria::DENOM, get_unmatched_population_keys(denoms, denexcs))
58
+
59
+ apply_to_submeasures(@sub_measures, HQMF::PopulationCriteria::OBSERV, observs.values)
54
60
 
55
61
  keep = []
56
62
 
@@ -134,13 +134,22 @@ module HQMF
134
134
  end
135
135
 
136
136
  if (!sub_conditions.empty?)
137
- # if we have negated conditions, add them to a new precondition of the same conjunction that is negated
137
+ # if we have negated conditions, add them to a new precondition with an inverted conjunction on a negated precondition
138
+ # the reason we invert the conjunction is because we are turning
139
+ # AND: NOT X
140
+ # AND: NOT Y
141
+ # into
142
+ # NOT: X OR Y
143
+ # (i.e, demorgan's law)
138
144
  if (!negated_conditions.empty?)
139
- sub_conditions << HQMF::Converter::SimplePrecondition.new(nil,negated_conditions,nil,conjunction_code, true)
145
+ inverted_conjunction_code = HQMF::Precondition::INVERSIONS[conjunction_code]
146
+ sub_conditions << HQMF::Converter::SimplePrecondition.new(nil,negated_conditions,nil,inverted_conjunction_code, true)
140
147
  end
141
148
  joined << HQMF::Converter::SimplePrecondition.new(nil,sub_conditions,nil,conjunction_code, false)
142
149
  elsif (!negated_conditions.empty?)
143
- joined << HQMF::Converter::SimplePrecondition.new(nil,negated_conditions,nil,conjunction_code, true)
150
+ # invert conjunction based on demorgan's law
151
+ inverted_conjunction_code = HQMF::Precondition::INVERSIONS[conjunction_code]
152
+ joined << HQMF::Converter::SimplePrecondition.new(nil,negated_conditions,nil,inverted_conjunction_code, true)
144
153
  end
145
154
 
146
155
  end
@@ -28,8 +28,12 @@ module HQMF
28
28
  # restriction.comparison
29
29
  # restriction.restriction
30
30
  def self.extract_preconditions_from_restriction(restriction,data_criteria_converter)
31
-
32
- target_id = data_criteria_converter.v1_data_criteria_by_id[restriction[:target_id]].id if restriction[:target_id]
31
+ target_id=nil
32
+ if restriction[:target_id] and data_criteria_converter.v1_data_criteria_by_id[restriction[:target_id]]
33
+ target_id = data_criteria_converter.v1_data_criteria_by_id[restriction[:target_id]].id
34
+ elsif restriction[:target_id]
35
+ puts "\tPrecondition Data Criteria MISSING: #{restriction[:target_id]}"
36
+ end
33
37
  type = restriction[:type]
34
38
  if (restriction[:negation])
35
39
  inverted = HQMF::TemporalReference::INVERSION[type]
@@ -79,11 +83,37 @@ module HQMF
79
83
 
80
84
  # check restrictions
81
85
  restrictions = extract_preconditions_from_restrictions(restriction[:restrictions], data_criteria_converter) if restriction[:restrictions]
82
- HQMF::PreconditionConverter.apply_restrictions_to_comparisons(children, restrictions) unless restrictions.nil? or restrictions.empty?
86
+ if (children)
87
+ HQMF::PreconditionConverter.apply_restrictions_to_comparisons(children, restrictions) unless restrictions.nil? or restrictions.empty?
88
+ end
89
+
83
90
 
84
91
  container = nil
92
+ # check if there is an expression on the restriction
93
+ if (restriction[:expression])
94
+ # this is for things like TIMEDIFF
95
+ type = restriction[:expression][:type]
96
+ exp_operator = HQMF::Converter::SimpleOperator.new(HQMF::Converter::SimpleOperator.find_category(type), type, HQMF::Converter::SimpleOperator.parse_value(restriction[:expression][:value]))
97
+ preconditions = []
98
+
99
+ driv_preconditions = []
100
+ restrictions.each {|element| driv_preconditions << element if element.is_a? HQMF::Converter::SimpleRestriction and element.operator.type == 'DRIV'}
101
+
102
+ if driv_preconditions and !driv_preconditions.empty?
103
+ preconditions = driv_preconditions.map(&:preconditions).flatten
104
+ end
105
+
106
+ reference = nil
107
+ conjunction_code = nil
108
+
109
+ comparison_precondition = HQMF::Converter::SimplePrecondition.new(nil,[HQMF::Converter::SimpleRestriction.new(exp_operator, nil, preconditions)],reference,conjunction_code, false)
110
+ comparison_precondition.klass = HQMF::Converter::SimplePrecondition::COMPARISON
111
+
112
+ comparison_precondition.subset_comparison = true
113
+ container = HQMF::Converter::SimpleRestriction.new(operator, nil, [comparison_precondition])
114
+
85
115
  # check if there is a subset on the restriction
86
- if restriction[:subset]
116
+ elsif restriction[:subset]
87
117
  # if we have a subset, we want to create a Comparison Precondition for the subset and have it be the child of the operator on the restriction.
88
118
  # the reason for this is that we want the order of operations to be SBS the FIRST of a data criteria, rather than FIRST of SBS of a data criteria
89
119
 
@@ -91,7 +121,6 @@ module HQMF
91
121
  subset_operator = HQMF::Converter::SimpleOperator.new(HQMF::Converter::SimpleOperator.find_category(subset_type), subset_type, nil)
92
122
 
93
123
  reference = nil
94
- # conjunction_code = "operator"
95
124
  conjunction_code = nil
96
125
 
97
126
  restriction = HQMF::Converter::SimpleRestriction.new(subset_operator, target_id)
@@ -11,7 +11,7 @@ module HQMF
11
11
 
12
12
  def initialize(id, preconditions,reference,conjunction_code,negation)
13
13
  super(id, preconditions,reference,conjunction_code,negation)
14
- @id = @@ids.next if (@id.nil?)
14
+ @id = HQMF::Counter.instance.next if (@id.nil?)
15
15
  @klass = PRECONDITION
16
16
  end
17
17
 
@@ -44,18 +44,6 @@ module HQMF
44
44
  preconditions.delete_if {|precondition| precondition.restriction? and precondition.converted}
45
45
  end
46
46
 
47
- # Simple class to issue monotonically increasing integer identifiers
48
- class Counter
49
- def initialize
50
- @count = 0
51
- end
52
-
53
- def next
54
- @count+=1
55
- end
56
- end
57
- @@ids = Counter.new
58
-
59
47
  end
60
48
  end
61
49
 
@@ -37,7 +37,7 @@ module HQMF
37
37
  parent_id = "GROUP"
38
38
  if restriction.generated_data_criteria.nil?
39
39
  # we pass in restriction.preconditions here rather than children_criteria because we need to be able to create grouping data criteria for and and or preconditions in a tree
40
- group_criteria = data_criteria_converter.create_group_data_criteria(restriction.preconditions, "#{type}_CHILDREN", value, parent_id, @@ids.next, "grouping", "temporal")
40
+ group_criteria = data_criteria_converter.create_group_data_criteria(restriction.preconditions, "#{type}_CHILDREN", value, parent_id, HQMF::Counter.instance.next, "grouping", "temporal")
41
41
  # save the generated grouping criteria so that we can reference it from other locations
42
42
  restriction.generated_data_criteria = group_criteria
43
43
  else
@@ -67,69 +67,15 @@ module HQMF
67
67
  data_criteria = nil
68
68
  if (children_criteria.length == 1)
69
69
  data_criteria = children_criteria[0]
70
- # this block used to set the order of operations of values after subset operators.
71
- # if data_criteria and !data_criteria.value.nil?
72
- # subset_operator.value ||= data_criteria.value
73
- # data_criteria.value = nil
74
- # end
75
70
  data_criteria.subset_operators ||= []
76
71
  # add subset operator to data criteria
77
72
  data_criteria.subset_operators << subset_operator unless data_criteria.has_subset(subset_operator)
78
73
  else
79
74
  parent_id = "GROUP"
80
75
 
81
- # this block of code used to be used to pull the value off of a data criteria and shove it onto a subset operator to evaluate MIN: lab result(result > 10) as MIN > 10: lab result
82
- # the decision was made by NQF to not support this order of operations based on previous guidance to the measure developers
83
- #
84
- # unless subset_operator.value
85
- # # scalar comparisons are used for MIN>90 etc. The value is on a REFR restriction. We need to store it on the data criteria since the value is processed before the operator is created.
86
- # scalar_comparison = nil
87
- #
88
- # # check to see if we have different values referenced accross the children
89
- # # this happens on things like most recent blood pressure reading
90
- # multiple_differing_values = false
91
- # children_criteria.each do |criteria|
92
- # if scalar_comparison.nil?
93
- # scalar_comparison = criteria.value
94
- # else
95
- # if scalar_comparison != criteria.value
96
- # multiple_differing_values = true
97
- # end
98
- # end
99
- # end
100
- #
101
- # # if we have multiple differing values, we want to keep the preconditions from the restriction, and attach them to the precondition
102
- # # we can then apply the subset operator to all of the children individually
103
- # if (multiple_differing_values)
104
- # precondition.preconditions.delete(restriction)
105
- # precondition.preconditions.concat(restriction.preconditions)
106
- # children_criteria.each do |criteria|
107
- # subset_operator = HQMF::SubsetOperator.new(type, value)
108
- # subset_operator.value = criteria.value
109
- # criteria.value = nil
110
- # criteria.subset_operators ||= []
111
- # criteria.subset_operators << subset_operator unless criteria.has_subset(subset_operator)
112
- # end
113
- # restriction.converted=true
114
- # # we want to return since we have applied the subset to the children. We no longer want to create a grouping data critiera
115
- # return;
116
- # end
117
- #
118
- # # all the children have the same value, apply the value to the subset operator
119
- # children_criteria.each do |criteria|
120
- # if scalar_comparison.nil?
121
- # scalar_comparison = criteria.value
122
- # else
123
- # raise "multiple different scalar comparisons for a grouping data criteria" if scalar_comparison != criteria.value
124
- # end
125
- # criteria.value = nil
126
- # end
127
- # subset_operator.value ||= scalar_comparison
128
- # end
129
-
130
76
  if restriction.generated_data_criteria.nil?
131
77
  # we pass in restriction.preconditions here rather than children_criteria because we need to be able to create grouping data criteria for and and or preconditions in a tree
132
- data_criteria = data_criteria_converter.create_group_data_criteria(restriction.preconditions, type, value, parent_id, @@ids.next, "grouping", "summary")
78
+ data_criteria = data_criteria_converter.create_group_data_criteria(restriction.preconditions, type, value, parent_id, HQMF::Counter.instance.next, "grouping", "summary")
133
79
  # save the generated grouping criteria so that we can reference it from other locations
134
80
  restriction.generated_data_criteria = data_criteria
135
81
  else
@@ -152,18 +98,5 @@ module HQMF
152
98
  restriction.converted=true
153
99
  end
154
100
 
155
- # Simple class to issue monotonically increasing integer identifiers
156
- class Counter
157
- def initialize
158
- @count = 0
159
- end
160
-
161
- def next
162
- @count+=1
163
- end
164
- end
165
- @@ids = Counter.new
166
-
167
-
168
101
  end
169
102
  end
@@ -6,7 +6,7 @@ module HQMF
6
6
 
7
7
  def self.parse(hqmf_contents, version, codes = nil)
8
8
 
9
-
9
+ HQMF::Counter.instance.reset()
10
10
  case version
11
11
  when HQMF_VERSION_1
12
12
  puts("\tCodes not passed in, cannot backfill properties like gender") unless codes
@@ -15,8 +15,21 @@ module HQMF
15
15
  HQMF2::Document.new(hqmf_contents).to_model
16
16
  else
17
17
  raise "Unsupported HQMF version specified: #{version}"
18
- end
19
18
  end
19
+ end
20
20
 
21
+ def self.parse_id(hqmf_contents, version)
22
+ case version
23
+ when HQMF_VERSION_1
24
+ doc = HQMF1::Document.parse(hqmf_contents)
25
+ doc.at_xpath('//cda:id/@root').value.upcase
26
+ when HQMF_VERSION_2
27
+ doc = HQMF2::Document.parse(hqmf_contents)
28
+ doc.at_xpath('cda:QualityMeasureDocument/cda:id/@extension', HQMF2::Document::NAMESPACES).value.upcase
29
+ else
30
+ raise "Unsupported HQMF version specified: #{version}"
31
+ end
32
+ end
21
33
  end
34
+
22
35
  end
@@ -0,0 +1,20 @@
1
+ module HQMF
2
+ # Simple class to issue monotonically increasing integer identifiers
3
+ class InstanceCounter
4
+ def initialize
5
+ @count=0
6
+ end
7
+
8
+ def reset
9
+ @count=0
10
+ end
11
+
12
+ def next
13
+ @count+=1
14
+ end
15
+ end
16
+
17
+ class Counter < InstanceCounter
18
+ include Singleton
19
+ end
20
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hqmf-parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.6
4
+ version: 1.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2012-11-15 00:00:00.000000000 Z
14
+ date: 2012-12-19 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rubyzip
@@ -96,6 +96,7 @@ files:
96
96
  - lib/hqmf-parser/1.0/data_criteria.rb
97
97
  - lib/hqmf-parser/1.0/document.rb
98
98
  - lib/hqmf-parser/1.0/expression.rb
99
+ - lib/hqmf-parser/1.0/observation.rb
99
100
  - lib/hqmf-parser/1.0/population_criteria.rb
100
101
  - lib/hqmf-parser/1.0/precondition.rb
101
102
  - lib/hqmf-parser/1.0/range.rb
@@ -122,6 +123,7 @@ files:
122
123
  - lib/hqmf-parser/parser.rb
123
124
  - lib/hqmf-parser/value_sets/value_set_parser.rb
124
125
  - lib/hqmf-parser.rb
126
+ - lib/util/counter.rb
125
127
  - lib/tasks/coverme.rake
126
128
  - lib/tasks/hqmf.rake
127
129
  - lib/tasks/value_sets.rake