cqm-reports 4.0.1 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -4
  3. data/lib/html-export/qdm-patient/data_element/_data_element_other_fields.mustache +1 -1
  4. data/lib/html-export/qdm-patient/qdm_patient.rb +22 -5
  5. data/lib/qrda-export/catI-r5/qrda1_r5.rb +12 -0
  6. data/lib/qrda-export/catI-r5/qrda_header/_participant.mustache +3 -2
  7. data/lib/qrda-export/catI-r5/qrda_header/_record_target.mustache +9 -0
  8. data/lib/qrda-export/catI-r5/qrda_templates/encounter_performed.mustache +49 -56
  9. data/lib/qrda-export/catI-r5/qrda_templates/medication_discharge.mustache +1 -1
  10. data/lib/qrda-export/catI-r5/qrda_templates/medication_dispensed.mustache +1 -1
  11. data/lib/qrda-export/catI-r5/qrda_templates/medication_order.mustache +1 -1
  12. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_medication_details.mustache +1 -1
  13. data/lib/qrda-export/catIII/_header.mustache +2 -4
  14. data/lib/qrda-export/catIII/_measure_section.mustache +2 -2
  15. data/lib/qrda-export/catIII/qrda_header/_participant_ehr.mustache +4 -1
  16. data/lib/qrda-export/helper/aggregate_object_helper.rb +10 -10
  17. data/lib/qrda-export/helper/cat1_view_helper.rb +23 -5
  18. data/lib/qrda-export/helper/view_helper.rb +9 -1
  19. data/lib/qrda-import/base-importers/section_importer.rb +32 -4
  20. data/lib/qrda-import/data-element-importers/encounter_performed_importer.rb +12 -12
  21. data/lib/qrda-import/data-element-importers/medication_discharge_importer.rb +1 -1
  22. data/lib/qrda-import/data-element-importers/medication_dispensed_importer.rb +1 -1
  23. data/lib/qrda-import/data-element-importers/medication_order_importer.rb +1 -1
  24. data/lib/qrda-import/data-element-importers/substance_order_importer.rb +1 -1
  25. data/lib/qrda-import/data-element-importers/substance_recommended_importer.rb +1 -1
  26. data/lib/qrda-import/patient_importer.rb +22 -6
  27. data/lib/util/hqmf_template_helper.rb +4 -4
  28. metadata +5 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9916f810f725d5fff7cfbc0f961e588ad17cc2dc634b9d51a48517a92eb7c076
4
- data.tar.gz: f5080b36a062bb8197420ecfefcfcfb480ba3a8d99bbd63dabd9bc325e638043
3
+ metadata.gz: 253b7e13bd3b5c1755304e4ea37ddb94bbe59a4a94692c59c4bf9332ec56a6ee
4
+ data.tar.gz: 22e3f623d94257e68e0902ab7787e2d97305412d85619010b0ee5b52ac66d792
5
5
  SHA512:
6
- metadata.gz: 54c5b802bcbba1c16aa3701e4927daa32230081a54ae039540ce82ce7af79a34a2ae7c884bd8af0e40bf94e9f5016db0e8b98565d3d3b308350a2b2bd7c6345f
7
- data.tar.gz: e7e2e958018f24a2ebbb07faf8ad1f2762054e861f99c8fa89466682d46e1f461674391a160788fca9e13ea2022a086fac860feb17ad032040cb2dd39bc9b1cd
6
+ metadata.gz: fad5c31a8d282ed50d4d628bb0ceb3bef287a00a1acf26232766e12db2a080585825cbb65293bea9b5f8818c0fc9b72fb2205f5af34a37a42f8b05bc658a525e
7
+ data.tar.gz: 725ddfa8f662591803a2a578637ebff876597f62c67156b6fad8064fa7b5c7b10a9e82383cbbedfbc96e34b7b77afb7d06705065b5c509a10c071c12f0831fa2
data/Gemfile CHANGED
@@ -2,13 +2,11 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec :development_group => :test
4
4
 
5
- gem 'mongoid', '~> 6.4.2'
6
-
7
5
  gem 'protected_attributes_continued'
8
6
 
9
7
  group :development, :test do
10
8
  gem 'bundler-audit'
11
- gem 'rubocop', '~> 0.52.1', require: false
9
+ gem 'rubocop', '~> 0.93.0', require: false
12
10
  end
13
11
 
14
12
  group :development do
@@ -18,7 +16,6 @@ end
18
16
 
19
17
  group :test do
20
18
  gem 'cqm-models', '~> 4.0'
21
- gem 'factory_girl', '~> 4.1.0'
22
19
  gem 'tailor', '~> 1.1.2'
23
20
  gem 'cane', '~> 2.3.0'
24
21
  gem 'codecov'
@@ -49,7 +49,7 @@
49
49
  {{/daysSupplied}}
50
50
  {{#diagnoses}}
51
51
  <div class="div-head-row">
52
- <div class="div-table-head--no-border"><span class="criteria-heading">Diagnosis{{{diagnosis_rank_string}}}:</span> {{{nested_code_string}}}</div>
52
+ <div class="div-table-head--no-border"><span class="criteria-heading">Diagnosis:</span> {{{nested_code_string}}} {{{diagnosis_string}}}</div>
53
53
  </div>
54
54
  {{/diagnoses}}
55
55
  {{#dischargeDisposition}}
@@ -10,6 +10,7 @@ class QdmPatient < Mustache
10
10
  @patient = patient
11
11
  @qdmPatient = patient.qdmPatient
12
12
  @patient_addresses = patient['addresses']
13
+ @patient_email = patient['email']
13
14
  @patient_telecoms = patient['telecoms']
14
15
  end
15
16
 
@@ -35,6 +36,7 @@ class QdmPatient < Mustache
35
36
  def patient_telecoms
36
37
  @patient_telecoms ||= [CQM::Telecom.new(use: 'HP', value: '555-555-2003')]
37
38
  # create formatted telecoms
39
+ @patient_telecoms << CQM::Telecom.new(use: 'HP', value: @patient_email) if @patient_email
38
40
  @patient_telecoms.map { |telecom| "(#{telecom['use']}) #{telecom['value']}" }.join("<br>")
39
41
  end
40
42
 
@@ -52,8 +54,14 @@ class QdmPatient < Mustache
52
54
  end
53
55
 
54
56
  def unit_string
55
- return "#{self['value']} " if !self['unit'] || self['unit'] == '1'
56
- "#{self['value']} #{self['unit']}"
57
+ return trimed_value(self['value']).to_s if !self['unit'] || self['unit'] == '1'
58
+ "#{trimed_value(self['value'])} #{self['unit']}"
59
+ end
60
+
61
+ def trimed_value(number)
62
+ i = number.to_i
63
+ f = number.to_f
64
+ i == f ? i : f
57
65
  end
58
66
 
59
67
  def entity_string
@@ -120,15 +128,24 @@ class QdmPatient < Mustache
120
128
  return unit_string if self['value']
121
129
  return code_code_system_string if self['code']
122
130
 
123
- self['result']
131
+ # Checks to see if the result is a DateTime value, String, or Numeric
132
+ begin
133
+ DateTime.parse(self['result'])
134
+ rescue ArgumentError, TypeError
135
+ # If the value is not numeric, just print out the result
136
+ self['result'].is_a?(Numeric) ? trimed_value(self['result']) : self['result']
137
+ end
124
138
  end
125
139
 
126
140
  def nested_code_string
127
141
  code_for_element(self['code'])
128
142
  end
129
143
 
130
- def diagnosis_rank_string
131
- " (rank: #{self['rank']})" if self['rank']
144
+ def diagnosis_string
145
+ dx_string = ''
146
+ dx_string += "</br>&nbsp; rank: #{self['rank']}" if self['rank']
147
+ dx_string += "</br>&nbsp; presentOnAdmissionIndicator: #{self['presentOnAdmissionIndicator']['code']}" if self['presentOnAdmissionIndicator']
148
+ dx_string
132
149
  end
133
150
 
134
151
  def end_time?
@@ -16,9 +16,12 @@ class Qrda1R5 < Mustache
16
16
  @provider = options[:provider]
17
17
  @patient_address_option = options[:patient_addresses]
18
18
  @patient_telecom_option = options[:patient_telecoms]
19
+ @patient_email_option = options[:patient_email]
19
20
  @performance_period_start = options[:start_time]
20
21
  @performance_period_end = options[:end_time]
21
22
  @submission_program = options[:submission_program]
23
+ @medicare_beneficiary_identifier = options[:medicare_beneficiary_identifier]
24
+ @hicn = options[:hicn]
22
25
  end
23
26
 
24
27
  def patient_addresses
@@ -41,6 +44,15 @@ class Qrda1R5 < Mustache
41
44
  JSON.parse(@patient_telecom_option.to_json)
42
45
  end
43
46
 
47
+ def patient_email
48
+ return unless @patient_email_option
49
+ telecom_email = [CQM::Telecom.new(
50
+ use: 'HP',
51
+ value: @patient_email_option
52
+ )]
53
+ JSON.parse(telecom_email.to_json)
54
+ end
55
+
44
56
  def patient_characteristic_payer
45
57
  JSON.parse(@qdmPatient.get_data_elements('patient_characteristic', 'payer').to_json)
46
58
  end
@@ -1,7 +1,8 @@
1
1
  <participant typeCode="DEV">
2
2
  <associatedEntity classCode="RGPR">
3
3
  <!-- CMS EHR Certification Number (formerly known as Office of the
4
- National Coordinator Certification Number) -->
5
- <id extension="123456789" root="2.16.840.1.113883.3.2074.1"/>
4
+ National Coordinator Certification Number). Note, this is a test
5
+ file, and the provided ID is not for a certified product set. -->
6
+ <id extension="0015CPV4ZTB4WBU" root="2.16.840.1.113883.3.2074.1"/>
6
7
  </associatedEntity>
7
8
  </participant>
@@ -1,6 +1,12 @@
1
1
  <recordTarget>
2
2
  <patientRole>
3
3
  <id extension="{{mrn}}" root="1.3.6.1.4.1.115" />
4
+ {{#medicare_beneficiary_identifier}}
5
+ <id extension="{{medicare_beneficiary_identifier}}" root="2.16.840.1.113883.4.927" />
6
+ {{/medicare_beneficiary_identifier}}
7
+ {{#hicn}}
8
+ <id extension="{{hicn}}" root="2.16.840.1.113883.4.572" />
9
+ {{/hicn}}
4
10
  {{#patient_addresses}}
5
11
  <addr use="{{use}}">
6
12
  {{#street}}
@@ -15,6 +21,9 @@
15
21
  {{#patient_telecoms}}
16
22
  <telecom use="{{use}}" value="tel:{{value}}"/>
17
23
  {{/patient_telecoms}}
24
+ {{#patient_email}}
25
+ <telecom use="{{use}}" value="mailto:{{value}}"/>
26
+ {{/patient_email}}
18
27
  <patient>
19
28
  {{#patient}}
20
29
  <name>
@@ -1,58 +1,51 @@
1
1
  <entry>
2
- <act classCode="ACT" moodCode="EVN">
3
- <!--Encounter performed Act -->
4
- <templateId extension="2021-08-01" root="2.16.840.1.113883.10.20.24.3.133"/>
5
- <code code="ENC" codeSystem="2.16.840.1.113883.5.6" codeSystemName="ActClass" displayName="Encounter"/>
6
- <entryRelationship typeCode="SUBJ">
7
- <encounter classCode="ENC" moodCode="EVN">
8
- <!-- Encounter activities template -->
9
- <templateId extension="2015-08-01" root="2.16.840.1.113883.10.20.22.4.49"/>
10
- <!-- Encounter performed template -->
11
- <templateId extension="2021-08-01" root="2.16.840.1.113883.10.20.24.3.23"/>
12
- <id extension="{{object_id}}" root="1.3.6.1.4.1.115"/>
13
- <!-- QDM Attribute: Code -->
14
- {{> _codes}}
15
- <text>{{description}}</text>
16
- <statusCode code="completed"/>
17
- {{#relevantPeriod}}
18
- <!-- QDM Attribute: Relevant Period -->
19
- {{{relevant_period}}}
20
- {{/relevantPeriod}}
21
- {{#dischargeDisposition}}
22
- <!-- QDM Attribute: Discharge Disposition -->
23
- <sdtc:dischargeDispositionCode {{> _code}}/>
24
- {{/dischargeDisposition}}
25
- {{#authorDatetime}}
26
- <!-- QDM Attribute: Author dateTime -->
27
- {{> qrda_templates/template_partials/_author}}
28
- {{/authorDatetime}}
29
- {{#facilityLocations}}
30
- <!-- QDM Attribute: Facility Locations -->
31
- {{> qrda_templates/template_partials/_facility_location}}
32
- {{/facilityLocations}}
33
- {{#admissionSource}}
34
- <!-- QDM Attribute: Admission Source -->
35
- {{> qrda_templates/template_partials/_admission_source}}
36
- {{/admissionSource}}
37
- {{#participant}}
38
- <!-- QDM Attribute: Participant -->
39
- <participant typeCode="PRF">
40
- {{> qrda_templates/template_partials/_entity}}
41
- </participant>
42
- {{/participant}}
43
- {{#clazz}}
44
- <!-- QDM Attribute: Class -->
45
- {{> qrda_templates/template_partials/_encounter_class}}
46
- {{/clazz}}
47
- {{#diagnoses}}
48
- <!-- QDM Attribute: Diagnoses -->
49
- {{> qrda_templates/template_partials/_encounter_diagnosis_qdm}}
50
- {{/diagnoses}}
51
- {{#relatedTo}}
52
- <!-- QDM Attribute: relatedTo -->
53
- {{> qrda_templates/template_partials/_related_to}}
54
- {{/relatedTo}}
55
- </encounter>
56
- </entryRelationship>
57
- </act>
2
+ <encounter classCode="ENC" moodCode="EVN">
3
+ <!-- Encounter activities template -->
4
+ <templateId extension="2015-08-01" root="2.16.840.1.113883.10.20.22.4.49"/>
5
+ <!-- Encounter performed template -->
6
+ <templateId extension="2021-08-01" root="2.16.840.1.113883.10.20.24.3.23"/>
7
+ <id extension="{{object_id}}" root="1.3.6.1.4.1.115"/>
8
+ <!-- QDM Attribute: Code -->
9
+ {{> _codes}}
10
+ <text>{{description}}</text>
11
+ <statusCode code="completed"/>
12
+ {{#relevantPeriod}}
13
+ <!-- QDM Attribute: Relevant Period -->
14
+ {{{relevant_period}}}
15
+ {{/relevantPeriod}}
16
+ {{#dischargeDisposition}}
17
+ <!-- QDM Attribute: Discharge Disposition -->
18
+ <sdtc:dischargeDispositionCode {{> _code}}/>
19
+ {{/dischargeDisposition}}
20
+ {{#authorDatetime}}
21
+ <!-- QDM Attribute: Author dateTime -->
22
+ {{> qrda_templates/template_partials/_author}}
23
+ {{/authorDatetime}}
24
+ {{#facilityLocations}}
25
+ <!-- QDM Attribute: Facility Locations -->
26
+ {{> qrda_templates/template_partials/_facility_location}}
27
+ {{/facilityLocations}}
28
+ {{#admissionSource}}
29
+ <!-- QDM Attribute: Admission Source -->
30
+ {{> qrda_templates/template_partials/_admission_source}}
31
+ {{/admissionSource}}
32
+ {{#participant}}
33
+ <!-- QDM Attribute: Participant -->
34
+ <participant typeCode="PRF">
35
+ {{> qrda_templates/template_partials/_entity}}
36
+ </participant>
37
+ {{/participant}}
38
+ {{#clazz}}
39
+ <!-- QDM Attribute: Class -->
40
+ {{> qrda_templates/template_partials/_encounter_class}}
41
+ {{/clazz}}
42
+ {{#diagnoses}}
43
+ <!-- QDM Attribute: Diagnoses -->
44
+ {{> qrda_templates/template_partials/_encounter_diagnosis_qdm}}
45
+ {{/diagnoses}}
46
+ {{#relatedTo}}
47
+ <!-- QDM Attribute: relatedTo -->
48
+ {{> qrda_templates/template_partials/_related_to}}
49
+ {{/relatedTo}}
50
+ </encounter>
58
51
  </entry>
@@ -61,7 +61,7 @@
61
61
  {{/daysSupplied}}
62
62
  {{#supply}}
63
63
  <!-- QDM Attribute: Supply -->
64
- <entryRelationship typeCode="REFR">
64
+ <entryRelationship typeCode="COMP">
65
65
  {{> qrda_templates/template_partials/_medication_supply_request}}
66
66
  </entryRelationship>
67
67
  {{/supply}}
@@ -69,7 +69,7 @@
69
69
  </entryRelationship>
70
70
  {{#supply}}
71
71
  <!-- QDM Attribute: Supply -->
72
- <entryRelationship typeCode="REFR">
72
+ <entryRelationship typeCode="COMP">
73
73
  {{> qrda_templates/template_partials/_medication_supply_request}}
74
74
  </entryRelationship>
75
75
  {{/supply}}
@@ -62,7 +62,7 @@
62
62
  {{/daysSupplied}}
63
63
  {{#supply}}
64
64
  <!-- QDM Attribute: Supply -->
65
- <entryRelationship typeCode="REFR">
65
+ <entryRelationship typeCode="COMP">
66
66
  {{> qrda_templates/template_partials/_medication_supply_request}}
67
67
  </entryRelationship>
68
68
  {{/supply}}
@@ -17,7 +17,7 @@
17
17
  {{^negated}}
18
18
  {{#refills}}
19
19
  <!-- QDM Attribute: Refills -->
20
- <repeatNumber value="{{.}}"/>
20
+ <repeatNumber value="{{{refills_as_repeat_number}}}"/>
21
21
  {{/refills}}
22
22
  {{/negated}}
23
23
  {{#route}}
@@ -3,10 +3,8 @@
3
3
  <typeId root="2.16.840.1.113883.1.3" extension="POCD_HD000040"/>
4
4
  <!-- US Realm Header Template Id -->
5
5
  <templateId root="2.16.840.1.113883.10.20.27.1.1" extension="2020-12-01"/>
6
- {{#ry2022_submission?}}
7
- <!-- QRDA Category III Report - CMS (V6) -->
8
- <templateId root="2.16.840.1.113883.10.20.27.1.2" extension="2021-07-01"/>
9
- {{/ry2022_submission?}}
6
+ <!-- QRDA Category III Report - CMS (V7) -->
7
+ <templateId root="2.16.840.1.113883.10.20.27.1.2" extension="2022-12-01"/>
10
8
  <!-- This is the globally unique identifier for this QRDA document -->
11
9
  <id root="{{random_id}}"/>
12
10
  <!-- QRDA III document type code -->
@@ -11,7 +11,7 @@
11
11
  <!-- QRDA Category III Measure Section template -->
12
12
  <templateId extension="2020-12-01" root="2.16.840.1.113883.10.20.27.2.1"/>
13
13
  <!-- QRDA Category III Measure Section - CMS (V4) -->
14
- <templateId extension="2019-05-01" root="2.16.840.1.113883.10.20.27.2.3"/>
14
+ <templateId extension="2022-05-01" root="2.16.840.1.113883.10.20.27.2.3"/>
15
15
  <code code="55186-1" codeSystem="2.16.840.1.113883.6.1"/>
16
16
  <title>Measure Section</title>
17
17
  <text>
@@ -41,7 +41,7 @@
41
41
  <templateId root="2.16.840.1.113883.10.20.24.3.98"/>
42
42
  <!-- SHALL 1..* (one for each referenced measure) Measure Reference and Results template -->
43
43
  <templateId extension="2020-12-01" root="2.16.840.1.113883.10.20.27.3.1"/>
44
- <templateId extension="2019-05-01" root="2.16.840.1.113883.10.20.27.3.17"/>
44
+ <templateId extension="2022-05-01" root="2.16.840.1.113883.10.20.27.3.17"/>
45
45
  <id extension="{{random_id}}" root="1.3.6.1.4.1.115"/>
46
46
  <statusCode code="completed"/>
47
47
  <!-- Containing isBranch external references -->
@@ -1,6 +1,9 @@
1
1
  <participant typeCode="DEV">
2
2
  <associatedEntity classCode="RGPR">
3
- <id root="2.16.840.1.113883.3.2074.1" extension="12345abcde67890"/>
3
+ <!-- CMS EHR Certification Number (formerly known as Office of the
4
+ National Coordinator Certification Number). Note, this is a test
5
+ file, and the provided ID is not for a certified product set. -->
6
+ <id root="2.16.840.1.113883.3.2074.1" extension="0015CPV4ZTB4WBU"/>
4
7
  <code code="129465004" displayName="medical record, device" codeSystem="2.16.840.1.113883.6.96" codeSystemName="SNOMED-CT"/>
5
8
  </associatedEntity>
6
9
  </participant>
@@ -3,23 +3,23 @@ module Qrda
3
3
  module Helper
4
4
  module PopulationSelectors
5
5
  def numerator
6
- populations.find {|pop| pop.type == 'NUMER'}
6
+ populations.find { |pop| pop.type == 'NUMER' }
7
7
  end
8
8
 
9
9
  def denominator
10
- populations.find {|pop| pop.type == 'DENOM'}
10
+ populations.find { |pop| pop.type == 'DENOM' }
11
11
  end
12
12
 
13
13
  def denominator_exceptions
14
- populations.find {|pop| pop.type == 'DENEXCEP'}
14
+ populations.find { |pop| pop.type == 'DENEXCEP' }
15
15
  end
16
16
 
17
17
  def denominator_exclusions
18
- populations.find {|pop| pop.type == 'DENEX'}
18
+ populations.find { |pop| pop.type == 'DENEX' }
19
19
  end
20
20
 
21
21
  def population_count(population_type, population_id)
22
- population = populations.find {|pop| pop.type == population_type && pop.id == population_id}
22
+ population = populations.find { |pop| pop.type == population_type && pop.id == population_id }
23
23
  if population
24
24
  population.value
25
25
  else
@@ -28,7 +28,7 @@ module Qrda
28
28
  end
29
29
 
30
30
  def population_id(population_type)
31
- populations.find {|pop| pop.type == population_type}.id
31
+ populations.find { |pop| pop.type == population_type }.id
32
32
  end
33
33
 
34
34
  def method_missing(method, *args, &block)
@@ -64,7 +64,7 @@ module Qrda
64
64
  end
65
65
 
66
66
  def add_stratification(id,value,observation)
67
- stratifications << Stratification.new(id,value,observation) unless stratifications.find {|st| st.id == id}
67
+ stratifications << Stratification.new(id,value,observation) unless stratifications.find { |st| st.id == id }
68
68
  end
69
69
 
70
70
  end
@@ -91,7 +91,7 @@ module Qrda
91
91
  end
92
92
 
93
93
  def is_cv?
94
- populations.any? {|pop| pop.type == 'MSRPOPL'}
94
+ populations.any? { |pop| pop.type == 'MSRPOPL' }
95
95
  end
96
96
 
97
97
  end
@@ -123,7 +123,7 @@ module Qrda
123
123
  end
124
124
  entry_populations << population if population
125
125
  end
126
- return if population_groups.find {|pg| pg.populations.collect(&:id).compact.sort == entry_populations.collect(&:id).compact.sort }
126
+ return if population_groups.find { |pg| pg.populations.collect(&:id).compact.sort == entry_populations.collect(&:id).compact.sort }
127
127
 
128
128
  pg = PopulationGroup.new
129
129
  pg.populations = entry_populations
@@ -142,7 +142,7 @@ module Qrda
142
142
  end
143
143
 
144
144
  def is_cv?
145
- populations.any? {|pop| pop.type == 'MSRPOPL'}
145
+ populations.any? { |pop| pop.type == 'MSRPOPL' }
146
146
  end
147
147
  end
148
148
  end
@@ -55,22 +55,30 @@ module Qrda
55
55
  self['value'].to_f
56
56
  end
57
57
 
58
+ def refills_as_repeat_number
59
+ self['refills'] + 1
60
+ end
61
+
58
62
  def dose_quantity_value
59
- return "<doseQuantity value=\"#{value_as_float}\" unit=\"#{self['unit']}\"/>" if self['unit']
63
+ return "<doseQuantity value=\"#{value_as_float}\" unit=\"#{self['unit']}\"/>" if self['unit'] && self['unit'] != ''
60
64
  "<doseQuantity value=\"#{value_as_float}\" />"
61
65
  end
62
66
 
63
67
  def result_value
64
68
  return "<value xsi:type=\"CD\" nullFlavor=\"UNK\"/>" unless self['result']
65
-
66
69
  result_string = if self['result'].is_a? Array
67
70
  result_value_as_string(self['result'][0])
68
71
  elsif self['result'].is_a? Hash
69
72
  result_value_as_string(self['result'])
70
73
  elsif self['result'].is_a? String
71
- "<value xsi:type=\"ST\">#{self['result']}</value>"
74
+ begin
75
+ DateTime.parse self['result']
76
+ "<value xsi:type=\"TS\" #{value_or_null_flavor(self['result'])}/>"
77
+ rescue StandardError
78
+ "<value xsi:type=\"ST\">#{self['result']}</value>"
79
+ end
72
80
  elsif !self['result'].nil?
73
- "<value xsi:type=\"PQ\" value=\"#{self['result']}\" unit=\"1\"/>"
81
+ integer_or_pq(self['result'])
74
82
  end
75
83
  result_string
76
84
  end
@@ -79,7 +87,17 @@ module Qrda
79
87
  return "<value xsi:type=\"CD\" nullFlavor=\"UNK\"/>" unless result
80
88
  oid = result['system'] || result['codeSystem']
81
89
  return "<value xsi:type=\"CD\" code=\"#{result['code']}\" codeSystem=\"#{oid}\" codeSystemName=\"#{HQMF::Util::CodeSystemHelper.code_system_for(oid)}\"/>" if result['code']
82
- return "<value xsi:type=\"PQ\" value=\"#{result['value']}\" unit=\"#{result['unit']}\"/>" if result['unit']
90
+ return integer_or_pq(result['value'], result['unit']) if result['unit'] && result['unit'] != ''
91
+ end
92
+
93
+ def integer_or_pq(number, unit = nil)
94
+ i = number.to_i
95
+ f = number.to_f
96
+ if i == f
97
+ unit ? "<value xsi:type=\"PQ\" value=\"#{i}\" unit=\"#{unit}\"/>" : "<value xsi:type=\"INT\" value=\"#{i}\"/>"
98
+ else
99
+ unit ? "<value xsi:type=\"PQ\" value=\"#{f}\" unit=\"#{unit}\"/>" : "<value xsi:type=\"REAL\" value=\"#{f}\"/>"
100
+ end
83
101
  end
84
102
 
85
103
  def authordatetime_or_dispenserid?
@@ -4,7 +4,7 @@ module Qrda
4
4
  module Helper
5
5
  module ViewHelper
6
6
  def measures
7
- @measures.only(:hqmf_id, :hqmf_set_id, :description).as_json
7
+ @measures.as_json
8
8
  end
9
9
 
10
10
  def random_id
@@ -22,6 +22,14 @@ module Qrda
22
22
  def submission_program
23
23
  @submission_program
24
24
  end
25
+
26
+ def medicare_beneficiary_identifier
27
+ @medicare_beneficiary_identifier
28
+ end
29
+
30
+ def hicn
31
+ @hicn
32
+ end
25
33
  end
26
34
  end
27
35
  end
@@ -47,8 +47,8 @@ module QRDA
47
47
  entry_qrda_id = extract_id(entry_element, @id_xpath)
48
48
  # Create a hash to map all of entry.ids to the same QRDA ids. This will be used to merge QRDA entries
49
49
  # that represent the same event.
50
- @entry_id_map["#{entry_qrda_id.value}_#{entry_qrda_id.namingSystem}"] ||= []
51
- @entry_id_map["#{entry_qrda_id.value}_#{entry_qrda_id.namingSystem}"] << entry.id
50
+ @entry_id_map["#{entry_qrda_id.value}***#{entry_qrda_id.namingSystem}"] ||= []
51
+ @entry_id_map["#{entry_qrda_id.value}***#{entry_qrda_id.namingSystem}"] << entry.id
52
52
  entry.dataElementCodes = extract_codes(entry_element, @code_xpath)
53
53
  extract_dates(entry_element, entry)
54
54
  if @result_xpath
@@ -77,7 +77,13 @@ module QRDA
77
77
  code_list << code_if_present(code_element)
78
78
  translations = code_element.xpath('cda:translation')
79
79
  translations.each do |translation|
80
- code_list << code_if_present(translation)
80
+ translation_code = code_if_present(translation)
81
+ next unless translation_code
82
+
83
+ code_list << translation_code
84
+ root_code_string = "#{code_list[0].system}:#{code_list[0].code}"
85
+ @warnings << ValidationError.new(message: "Translation code #{translation_code.system}:#{translation_code.code} may not be used for eCQM calculation by a receiving system. Ensure that the root code #{root_code_string} is from an eCQM valueset.",
86
+ location: coded_element.path)
81
87
  end
82
88
  end
83
89
  code_list.compact
@@ -193,7 +199,14 @@ module QRDA
193
199
  return unless value_element && !value_element['nullFlavor']
194
200
  value = value_element['value']
195
201
  if value.present?
196
- return value.strip.to_f if (value_element['unit'] == "1" || value_element['unit'].nil?)
202
+ if ['TS'].include? value_element.at_xpath("@xsi:type")&.value
203
+ begin
204
+ return DateTime.parse(value_element['value'])
205
+ rescue StandardError
206
+ return nil
207
+ end
208
+ end
209
+ return value.strip.to_f if unitless?(value_element)
197
210
 
198
211
  return QDM::Quantity.new(value.strip.to_f, value_element['unit'])
199
212
  elsif value_element['code'].present?
@@ -208,6 +221,12 @@ module QRDA
208
221
  end
209
222
  end
210
223
 
224
+ def unitless?(value_element)
225
+ return true if value_element['unit'].nil?
226
+ return true if value_element['unit'] == '1'
227
+ return true if value_element['unit'][0] == '{' && value_element['unit'][-1] == '}'
228
+ end
229
+
211
230
  def extract_reason(parent_element)
212
231
  return unless @reason_xpath
213
232
  reason_element = parent_element.xpath(@reason_xpath)
@@ -254,6 +273,15 @@ module QRDA
254
273
  QDM::Quantity.new(scalar_element['value'].to_f, scalar_element['unit'])
255
274
  end
256
275
 
276
+ def extract_refills(parent_element, repeat_number_xpath)
277
+ repeat_number_element = parent_element.at_xpath(repeat_number_xpath)
278
+ return unless repeat_number_element && repeat_number_element['value'].present?
279
+
280
+ # Refills is the Repeat Number - 1
281
+ repeat_number = repeat_number_element['value'].to_i
282
+ repeat_number.positive? ? repeat_number - 1 : 0
283
+ end
284
+
257
285
  def extract_components(parent_element)
258
286
  component_elements = parent_element.xpath(@components_xpath)
259
287
  components = []
@@ -1,18 +1,18 @@
1
1
  module QRDA
2
2
  module Cat1
3
3
  class EncounterPerformedImporter < SectionImporter
4
- def initialize(entry_finder = QRDA::Cat1::EntryFinder.new("./cda:entry/cda:act[cda:templateId/@root = '2.16.840.1.113883.10.20.24.3.133']"))
4
+ def initialize(entry_finder = QRDA::Cat1::EntryFinder.new("./cda:entry/cda:encounter[cda:templateId/@root = '2.16.840.1.113883.10.20.24.3.23']"))
5
5
  super(entry_finder)
6
- @id_xpath = './cda:entryRelationship/cda:encounter/cda:id'
7
- @code_xpath = "./cda:entryRelationship/cda:encounter[cda:templateId/@root = '2.16.840.1.113883.10.20.24.3.23']/cda:code"
8
- @relevant_period_xpath = "./cda:entryRelationship/cda:encounter[cda:templateId/@root = '2.16.840.1.113883.10.20.24.3.23']/cda:effectiveTime"
9
- @author_datetime_xpath = "./cda:entryRelationship/cda:encounter[cda:templateId/@root = '2.16.840.1.113883.10.20.24.3.23']/cda:author[cda:templateId/@root='2.16.840.1.113883.10.20.24.3.155']/cda:time"
10
- @admission_source_xpath = "./cda:entryRelationship/cda:encounter[cda:templateId/@root = '2.16.840.1.113883.10.20.24.3.23']/cda:participant/cda:participantRole[cda:templateId/@root='2.16.840.1.113883.10.20.24.3.151']/cda:code"
11
- @discharge_disposition_xpath = "./cda:entryRelationship/cda:encounter[cda:templateId/@root = '2.16.840.1.113883.10.20.24.3.23']/sdtc:dischargeDispositionCode"
12
- @facility_locations_xpath = "./cda:entryRelationship/cda:encounter/cda:participant[cda:templateId/@root = '2.16.840.1.113883.10.20.24.3.100']"
13
- @clazz_xpath = "./cda:entryRelationship/cda:encounter[cda:templateId/@root = '2.16.840.1.113883.10.20.24.3.23']/cda:entryRelationship/cda:act[cda:templateId/@root='2.16.840.1.113883.10.20.24.3.171']/cda:code"
14
- @diagnosis_xpath = "./cda:entryRelationship/cda:encounter[cda:templateId/@root = '2.16.840.1.113883.10.20.24.3.23']/cda:entryRelationship/cda:observation[cda:templateId/@root='2.16.840.1.113883.10.20.24.3.168']"
15
- @related_to_xpath = "./cda:entryRelationship/cda:encounter/sdtc:inFulfillmentOf1/sdtc:actReference"
6
+ @id_xpath = './cda:id'
7
+ @code_xpath = "./cda:code"
8
+ @relevant_period_xpath = "./cda:effectiveTime"
9
+ @author_datetime_xpath = "./cda:author[cda:templateId/@root='2.16.840.1.113883.10.20.24.3.155']/cda:time"
10
+ @admission_source_xpath = "./cda:participant/cda:participantRole[cda:templateId/@root='2.16.840.1.113883.10.20.24.3.151']/cda:code"
11
+ @discharge_disposition_xpath = "./sdtc:dischargeDispositionCode"
12
+ @facility_locations_xpath = "./cda:participant[cda:templateId/@root = '2.16.840.1.113883.10.20.24.3.100']"
13
+ @clazz_xpath = "./cda:entryRelationship/cda:act[cda:templateId/@root='2.16.840.1.113883.10.20.24.3.171']/cda:code"
14
+ @diagnosis_xpath = "./cda:entryRelationship/cda:observation[cda:templateId/@root='2.16.840.1.113883.10.20.24.3.168']"
15
+ @related_to_xpath = "./sdtc:inFulfillmentOf1/sdtc:actReference"
16
16
  @entry_class = QDM::EncounterPerformed
17
17
  end
18
18
 
@@ -27,7 +27,7 @@ module QRDA
27
27
  los = encounter_performed.relevantPeriod.high.to_date - encounter_performed.relevantPeriod.low.to_date
28
28
  encounter_performed.lengthOfStay = QDM::Quantity.new(los.to_i, 'd')
29
29
  end
30
- entity = extract_entity(entry_element, "./cda:entryRelationship/cda:encounter//cda:participant[@typeCode='PRF']")
30
+ entity = extract_entity(entry_element, "./cda:participant[@typeCode='PRF']")
31
31
  encounter_performed.participant.concat(entity) if entity
32
32
  extract_modifier_code(encounter_performed, entry_element)
33
33
  encounter_performed.relatedTo = extract_related_to(entry_element)
@@ -18,7 +18,7 @@ module QRDA
18
18
 
19
19
  def create_entry(entry_element, nrh = NarrativeReferenceHandler.new)
20
20
  medication_discharge = super
21
- medication_discharge.refills = extract_scalar(entry_element, @refills_xpath)&.value
21
+ medication_discharge.refills = extract_refills(entry_element, @refills_xpath)
22
22
  medication_discharge.dosage = extract_scalar(entry_element, @dosage_xpath)
23
23
  medication_discharge.supply = extract_scalar(entry_element, @supply_xpath)
24
24
  medication_discharge.frequency = frequency_as_coded_value(entry_element, @frequency_xpath)
@@ -19,7 +19,7 @@ module QRDA
19
19
 
20
20
  def create_entry(entry_element, nrh = NarrativeReferenceHandler.new)
21
21
  medication_dispensed = super
22
- medication_dispensed.refills = extract_scalar(entry_element, @refills_xpath)&.value
22
+ medication_dispensed.refills = extract_refills(entry_element, @refills_xpath)
23
23
  medication_dispensed.dosage = extract_scalar(entry_element, @dosage_xpath)
24
24
  medication_dispensed.supply = extract_scalar(entry_element, @supply_xpath)
25
25
  medication_dispensed.frequency = frequency_as_coded_value(entry_element, @frequency_xpath)
@@ -25,7 +25,7 @@ module QRDA
25
25
  medication_order.dosage = extract_scalar(entry_element, @dosage_xpath)
26
26
  medication_order.supply = extract_scalar(entry_element, @supply_xpath)
27
27
  medication_order.frequency = frequency_as_coded_value(entry_element, @frequency_xpath)
28
- medication_order.refills = extract_scalar(entry_element, @refills_xpath)&.value
28
+ medication_order.refills = extract_refills(entry_element, @refills_xpath)
29
29
  medication_order.route = code_if_present(entry_element.at_xpath(@route_xpath))
30
30
  medication_order.setting = code_if_present(entry_element.at_xpath(@setting_xpath))
31
31
  medication_order.daysSupplied = extract_scalar(entry_element, @days_supplied_xpath)&.value
@@ -21,7 +21,7 @@ module QRDA
21
21
  substance_order.dosage = extract_scalar(entry_element, @dosage_xpath)
22
22
  substance_order.supply = extract_scalar(entry_element, @supply_xpath)
23
23
  substance_order.frequency = frequency_as_coded_value(entry_element, @frequency_xpath)
24
- substance_order.refills = extract_scalar(entry_element, @refills_xpath)&.value
24
+ substance_order.refills = extract_refills(entry_element, @refills_xpath)
25
25
  substance_order.route = code_if_present(entry_element.at_xpath(@route_xpath))
26
26
  substance_order.reason = extract_reason(entry_element)
27
27
  substance_order
@@ -18,7 +18,7 @@ module QRDA
18
18
  substance_recommended = super
19
19
  substance_recommended.dosage = extract_scalar(entry_element, @dosage_xpath)
20
20
  substance_recommended.frequency = frequency_as_coded_value(entry_element, @frequency_xpath)
21
- substance_recommended.refills = extract_scalar(entry_element, @refills_xpath)&.value
21
+ substance_recommended.refills = extract_refills(entry_element, @refills_xpath)
22
22
  substance_recommended.route = code_if_present(entry_element.at_xpath(@route_xpath))
23
23
  substance_recommended.reason = extract_reason(entry_element)
24
24
  entity = extract_entity(entry_element, "./cda:participant[@typeCode='PRF']")
@@ -67,7 +67,7 @@ module QRDA
67
67
  codes_modifiers = {}
68
68
  entry_id_map = {}
69
69
  import_data_elements(patient, doc, entry_id_map, codes, codes_modifiers, warnings)
70
- normalize_references(patient, entry_id_map)
70
+ normalize_references(patient, entry_id_map, warnings)
71
71
  get_demographics(patient, doc, codes)
72
72
  [patient, warnings, codes, codes_modifiers]
73
73
  end
@@ -80,9 +80,19 @@ module QRDA
80
80
  data_elements, id_map = importer.create_entries(context, nrh)
81
81
  new_data_elements = []
82
82
 
83
- id_map.each_value do |elem_ids|
84
- elem_id = elem_ids.first
83
+ id_map.each_pair do |key, elem_ids|
84
+ split_id = key.split('***')
85
+ id_string = "#{split_id[1]}(root), #{split_id[0]}(extension)"
86
+ warnings << ValidationError.new(message: "Two or more entries share the Id: #{id_string}.") if elem_ids.length > 1
87
+ elem_id = elem_ids.last
85
88
  data_element = data_elements.find { |de| de.id == elem_id }
89
+
90
+ # If a data_element isn't returned, there was an issue parsing the template, provide a warning
91
+ if data_element.nil?
92
+ warnings << ValidationError.new(message: "Error parsing template with Id: #{id_string}.")
93
+ next
94
+ end
95
+
86
96
  # Keep the first element with a shared ID
87
97
  new_data_elements << data_element
88
98
 
@@ -94,7 +104,7 @@ module QRDA
94
104
  unique_element_keys << key_elements_for_determining_encounter_uniqueness(data_element)
95
105
 
96
106
  # Loop through all other data elements with the same id
97
- elem_ids[1,elem_ids.length].each do |dup_id|
107
+ elem_ids[0,elem_ids.length - 1].each do |dup_id|
98
108
  dup_element = data_elements.find { |de| de.id == dup_id }
99
109
  dup_element_keys = key_elements_for_determining_encounter_uniqueness(dup_element)
100
110
  # See if a previously selected data element shared all of the keys files
@@ -134,13 +144,19 @@ module QRDA
134
144
  record.deathdate = DateTime.parse(entry_elements.at_xpath("./cda:effectiveTime/cda:low")['value']).to_i
135
145
  end
136
146
 
137
- def normalize_references(patient, entry_id_map)
147
+ def normalize_references(patient, entry_id_map, warnings)
138
148
  patient.qdmPatient.dataElements.each do |data_element|
139
149
  next unless data_element.respond_to?(:relatedTo) && data_element.relatedTo
140
150
 
141
151
  relations_to_add = []
142
152
  data_element.relatedTo.each do |related_to|
143
- relations_to_add += entry_id_map["#{related_to['value']}_#{related_to['namingSystem']}"]
153
+ relation_to_add = entry_id_map["#{related_to['value']}***#{related_to['namingSystem']}"]
154
+ # Add the relation if it can be found, otherwise return a warning
155
+ relations_to_add += relation_to_add unless relation_to_add.nil?
156
+ if relation_to_add.nil?
157
+ id_warning_str = "Related To Id: #{related_to['namingSystem']}(root), #{related_to['value']}(extension) cannot be found in QRDA file."
158
+ warnings << ValidationError.new(message: id_warning_str)
159
+ end
144
160
  end
145
161
  data_element.relatedTo = relations_to_add.map(&:to_s)
146
162
  end
@@ -3,7 +3,7 @@ module HQMF
3
3
  # General helpers for working with codes and code systems
4
4
  class HQMFTemplateHelper
5
5
 
6
- def self.definition_for_template_id(template_id, version="r1")
6
+ def self.definition_for_template_id(template_id, version = "r1")
7
7
  template_id_map(version)[template_id]
8
8
  end
9
9
 
@@ -18,14 +18,14 @@ module HQMF
18
18
  @id_map[version]
19
19
  end
20
20
 
21
- def self.template_id_by_definition_and_status(definition, status, negation=false, version="r1")
21
+ def self.template_id_by_definition_and_status(definition, status, negation, version)
22
22
  case version
23
23
  when "r1"
24
- kv_pair = template_id_map(version).find {|k, v| v['definition'] == definition &&
24
+ kv_pair = template_id_map(version).find { |k, v| v['definition'] == definition &&
25
25
  v['status'] == status &&
26
26
  v['negation'] == negation}
27
27
  when "r2", "r2cql"
28
- kv_pair = template_id_map(version).find {|k, v| v['definition'] == definition &&
28
+ kv_pair = template_id_map(version).find { |k, v| v['definition'] == definition &&
29
29
  v['status'] == status}
30
30
  end
31
31
  if kv_pair
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cqm-reports
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.1
4
+ version: 4.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - The MITRE Corporation
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-09 00:00:00.000000000 Z
11
+ date: 2024-05-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cqm-models
@@ -86,20 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: 1.8.5
90
- - - "<"
91
- - !ruby/object:Gem::Version
92
- version: 1.14.0
89
+ version: 1.16.2
93
90
  type: :runtime
94
91
  prerelease: false
95
92
  version_requirements: !ruby/object:Gem::Requirement
96
93
  requirements:
97
94
  - - ">="
98
95
  - !ruby/object:Gem::Version
99
- version: 1.8.5
100
- - - "<"
101
- - !ruby/object:Gem::Version
102
- version: 1.14.0
96
+ version: 1.16.2
103
97
  - !ruby/object:Gem::Dependency
104
98
  name: uuid
105
99
  requirement: !ruby/object:Gem::Requirement
@@ -365,7 +359,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
365
359
  - !ruby/object:Gem::Version
366
360
  version: '0'
367
361
  requirements: []
368
- rubygems_version: 3.1.6
362
+ rubygems_version: 3.1.4
369
363
  signing_key:
370
364
  specification_version: 4
371
365
  summary: A library for import and export of reports for use with electronic Clinical