cqm-reports 4.0.1 → 4.1.1

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