hqmf-parser 1.0.6 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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