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 +1 -2
- data/lib/hqmf-generator/hqmf-generator.rb +1 -17
- data/lib/hqmf-model/data_criteria.json +31 -31
- data/lib/hqmf-model/data_criteria.rb +44 -25
- data/lib/hqmf-model/document.rb +4 -0
- data/lib/hqmf-model/population_criteria.rb +3 -2
- data/lib/hqmf-model/precondition.rb +6 -0
- data/lib/hqmf-model/types.rb +1 -1
- data/lib/hqmf-parser.rb +2 -0
- data/lib/hqmf-parser/1.0/data_criteria.rb +6 -19
- data/lib/hqmf-parser/1.0/document.rb +8 -14
- data/lib/hqmf-parser/1.0/expression.rb +6 -0
- data/lib/hqmf-parser/1.0/observation.rb +61 -0
- data/lib/hqmf-parser/1.0/restriction.rb +5 -2
- data/lib/hqmf-parser/converter/pass1/data_criteria_converter.rb +13 -17
- data/lib/hqmf-parser/converter/pass1/document_converter.rb +2 -2
- data/lib/hqmf-parser/converter/pass1/population_criteria_converter.rb +15 -9
- data/lib/hqmf-parser/converter/pass1/precondition_converter.rb +12 -3
- data/lib/hqmf-parser/converter/pass1/precondition_extractor.rb +34 -5
- data/lib/hqmf-parser/converter/pass1/simple_precondition.rb +1 -13
- data/lib/hqmf-parser/converter/pass2/operator_converter.rb +2 -69
- data/lib/hqmf-parser/parser.rb +15 -2
- data/lib/util/counter.rb +20 -0
- metadata +4 -2
data/Gemfile
CHANGED
@@ -6,8 +6,7 @@ gem 'pry-nav'
|
|
6
6
|
gem 'nokogiri'
|
7
7
|
gem 'rubyzip'
|
8
8
|
|
9
|
-
|
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#{
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":"
|
335
|
-
"status":"",
|
334
|
+
"definition":"diagnosis",
|
335
|
+
"status":"family_history",
|
336
336
|
"sub_category":"family_history",
|
337
|
-
"hard_status":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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: :
|
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: :
|
19
|
-
'FACILITY_LOCATION_ARRIVAL_DATETIME'=>{title:'Facility Location Arrival Date/Time',coded_entry_method: :
|
20
|
-
'FACILITY_LOCATION_DEPARTURE_DATETIME'=>{title:'Facility Location Departure Date/Time',coded_entry_method: :
|
21
|
-
'DISCHARGE_DATETIME'=>{title:'Discharge Date/Time',coded_entry_method: :
|
22
|
-
'DISCHARGE_STATUS'=>{title:'Discharge Status',coded_entry_method: :
|
23
|
-
'ADMISSION_DATETIME'=>{title:'Admission Date/Time',coded_entry_method: :
|
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: :
|
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: :
|
31
|
-
'INCISION_DATETIME'=>{title:'Incision Date/Time',coded_entry_method: :
|
32
|
-
'REMOVAL_DATETIME'=>{title:'Removal Date/Time',coded_entry_method: :
|
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
|
-
|
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
|
data/lib/hqmf-model/document.rb
CHANGED
@@ -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
|
data/lib/hqmf-model/types.rb
CHANGED
@@ -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
|
data/lib/hqmf-parser.rb
CHANGED
@@ -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
|
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
|
-
|
25
|
-
@occurrence_key =
|
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 <<
|
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
|
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}_#{
|
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
|
@@ -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
|
-
|
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}_#{
|
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 =
|
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
|
-
|
178
|
-
description = "#{definition.titleize}#{
|
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
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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 =
|
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,
|
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,
|
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
|
data/lib/hqmf-parser/parser.rb
CHANGED
@@ -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
|
data/lib/util/counter.rb
ADDED
@@ -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
|
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-
|
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
|