cqm-reports 3.0.1 → 3.1.4

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 (26) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +1 -2
  3. data/README.md +2 -1
  4. data/lib/cqm-reports.rb +0 -1
  5. data/lib/html-export/qdm-patient/data_element/_data_element_codes.mustache +1 -1
  6. data/lib/html-export/qdm-patient/qdm_patient.mustache +13 -11
  7. data/lib/html-export/qdm-patient/qdm_patient.rb +50 -2
  8. data/lib/qrda-export/catI-r5/qrda_header/_record_target.mustache +1 -1
  9. data/lib/qrda-export/catI-r5/qrda_templates/communication_performed.mustache +0 -1
  10. data/lib/qrda-export/catI-r5/qrda_templates/laboratory_test_performed.mustache +4 -0
  11. data/lib/qrda-export/catI-r5/qrda_templates/physical_exam_performed.mustache +4 -0
  12. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_reaction_observation.mustache +1 -1
  13. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_reason.mustache +0 -1
  14. data/lib/qrda-export/catIII-r2-1/_continuous_variable_value.mustache +5 -5
  15. data/lib/qrda-export/catIII-r2-1/_measure_data.mustache +0 -2
  16. data/lib/qrda-export/catIII-r2-1/_stratification.mustache +3 -5
  17. data/lib/qrda-export/catIII-r2-1/qrda3_r21.rb +2 -5
  18. data/lib/qrda-export/helper/aggregate_object_helper.rb +14 -18
  19. data/lib/qrda-export/helper/date_helper.rb +2 -2
  20. data/lib/qrda-export/helper/patient_view_helper.rb +39 -0
  21. data/lib/qrda-import/base-importers/demographics_importer.rb +8 -7
  22. data/lib/qrda-import/base-importers/section_importer.rb +56 -11
  23. data/lib/qrda-import/data-element-importers/encounter_performed_importer.rb +11 -1
  24. data/lib/qrda-import/patient_importer.rb +64 -60
  25. metadata +32 -14
  26. data/lib/qrda-import/entry_package.rb +0 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 4ab383c8a8cbc82410448739f891fc5c96cd0ae8
4
- data.tar.gz: 62734f201648d065ea3bec1903f34d1c7005443b
2
+ SHA256:
3
+ metadata.gz: 294e31e72ca88d7e9a85c08b1995728693cb2cf80a906273a8f1762ef5a1cc01
4
+ data.tar.gz: c969fcf65e9f7f8efabb02c636aad2bef9659d2221c0e72a94afe9ec993c8e16
5
5
  SHA512:
6
- metadata.gz: 941d46cc062b4cb2d70a7f11438363b3af6a0ba7a21491ed451c37f61778445ad8e2375b055a5db54c8b90f365b7d52cd9fa7ce262eea8b20727ae42f7043310
7
- data.tar.gz: 13c9d7aca7e2497f47e66919b54926f33bd6fd4d2714aadbde204d24072f23e95aa0fbad63458572e4c776c0dac9caa67fae2dc3efa063d7f340d618eea4cd3f
6
+ metadata.gz: aa75bf444859f1fdff85ae788fa277f0acb8f75e4d6e036717a0545f7af2a19800f446500101f36f9773bff9eb815b3cf4601bf1694d26af2468f04ffa3cc5cc
7
+ data.tar.gz: 78fe7570a996c1cd80517e74a856c2149a5cb351b933dccda3870f8b6781f707cd0c957b343421a0b54a5d8205c3bcfe96d535f4fe2fc53658c2cd0db8e2ba95
data/Gemfile CHANGED
@@ -17,7 +17,7 @@ group :development do
17
17
  end
18
18
 
19
19
  group :test do
20
- gem 'cqm-models', git: 'https://github.com/projecttacoma/cqm-models', branch: 'master'
20
+ gem 'cqm-models', '~> 3.0'
21
21
  gem 'factory_girl', '~> 4.1.0'
22
22
  gem 'tailor', '~> 1.1.2'
23
23
  gem 'cane', '~> 2.3.0'
@@ -27,6 +27,5 @@ group :test do
27
27
  gem 'minitest', '~> 5.3'
28
28
  gem 'minitest-reporters'
29
29
  gem 'awesome_print', :require => 'ap'
30
- gem 'cqm-validators', git: 'https://github.com/projecttacoma/cqm-validators', branch: 'master'
31
30
  gem 'nokogiri-diff'
32
31
  end
data/README.md CHANGED
@@ -11,6 +11,7 @@ Starting with version **2.0.0** released on 6/20/2019, cqm-reports versioning ha
11
11
  | X | QRDA Cat 1 | QRDA Cat 3 |
12
12
  | --- | --- | --- |
13
13
  | 2 | R1 STU5.1 | R1 STU2.1 |
14
+ | 3 | R1 STU5.2 | R1 STU2.1 |
14
15
 
15
16
  * **Y** indicates major changes (incompatible API changes)
16
17
 
@@ -24,7 +25,7 @@ Importing QRDA
24
25
  A QRDA document can be imported into a CQM::Patient (defined in [cqm-models](https://github.com/projecttacoma/cqm-models)) using the following commands.
25
26
 
26
27
  doc = Nokogiri::XML(file)
27
- patient = QRDA::Cat1::PatientImporter.instance.parse_cat1(doc)
28
+ patient, warnings = QRDA::Cat1::PatientImporter.instance.parse_cat1(doc)
28
29
 
29
30
  Exporting QRDA Category I
30
31
  ==========
data/lib/cqm-reports.rb CHANGED
@@ -19,7 +19,6 @@ require_relative 'html-export/qdm-patient/qdm_patient.rb'
19
19
  require_relative 'qrda-export/catI-r5/qrda1_r5.rb'
20
20
  require_relative 'qrda-export/catIII-r2-1/qrda3_r21.rb'
21
21
 
22
- require_relative 'qrda-import/entry_package.rb'
23
22
  require_relative 'qrda-import/cda_identifier.rb'
24
23
  require_relative 'qrda-import/narrative_reference_handler.rb'
25
24
  require_relative 'qrda-import/entry_finder.rb'
@@ -2,7 +2,7 @@
2
2
  <div class="div-table-body">
3
3
  {{#dataElementCodes}}
4
4
  <div class="div-head-row">
5
- <div class="div-table-head--no-border"><span class="criteria-heading">{{{code_system_name}}}:</span> {{code}}</div>
5
+ <div class="div-table-head--no-border"><span class="criteria-heading">{{{code_system_name}}}:</span> {{code}}{{{code_description}}}</div>
6
6
  </div>
7
7
  {{/dataElementCodes}}
8
8
  </div>
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
5
5
  {{#patient}}
6
- <title>Cypress Certification Patient Test Record: {{{given_name}}} {{familyName}}</title>
6
+ <title>Cypress Certification Patient Test Record: {{given_name}} {{familyName}}</title>
7
7
  {{/patient}}
8
8
  {{#include_style?}}
9
9
  {{> _header_css}}
@@ -11,36 +11,38 @@
11
11
  </head>
12
12
  <body>
13
13
  {{#patient}}
14
- <h1 class="h1center">Cypress Certification Patient Test Record: {{{given_name}}} {{familyName}}</h1>
14
+ <h1 class="h1center">Cypress Certification Patient Test Record: {{given_name}} {{familyName}}</h1>
15
15
  <div class="div-table">
16
16
  <div class="div-table-body">
17
17
  <div class="div-head-row patient_narr_tr panel panel-default patient-details">
18
18
  <div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Patient</span></div>
19
- <div class="div-table-head">{{{given_name}}} {{familyName}}</div>
19
+ <div class="div-table-head">{{given_name}} {{familyName}}</div>
20
20
  <div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Sex</span></div>
21
- <div class="div-table-head">{{{gender}}}</div>
21
+ <div class="div-table-head">{{{gender}}}{{#demographic_code_description}}gender{{/demographic_code_description}}</div>
22
22
  </div>
23
23
  <div class="div-head-row patient_narr_tr panel panel-default patient-details">
24
24
  <div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Date of birth</span></div>
25
25
  <div class="div-table-head">{{{birthdate}}}</div>
26
26
  <div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Date of expiration</span></div>
27
- <div class="div-table-head"></div>
27
+ <div class="div-table-head">{{{expiration}}}</div>
28
28
  </div>
29
29
  <div class="div-head-row patient_narr_tr panel panel-default patient-details">
30
30
  <div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Race</span></div>
31
- <div class="div-table-head">{{{race}}}</div>
31
+ <div class="div-table-head">{{{race}}}{{#demographic_code_description}}race{{/demographic_code_description}}</div>
32
32
  <div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Ethnicity</span></div>
33
- <div class="div-table-head">{{{ethnic_group}}}</div>
34
- </div>
33
+ <div class="div-table-head">{{{ethnic_group}}}{{#demographic_code_description}}ethnic_group{{/demographic_code_description}}</div>
34
+ </div>
35
35
  <div class="div-head-row patient_narr_tr panel panel-default patient-details">
36
36
  <div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Insurance Providers</span></div>
37
- <div class="div-table-head">{{{payer}}}</div>
37
+ <div class="div-table-head">{{{payer}}}{{#demographic_code_description}}payer{{/demographic_code_description}}</div>
38
38
  <div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Patient IDs</span></div>
39
39
  <div class="div-table-head">{{{mrn}}} Cypress</div>
40
40
  </div>
41
41
  <div class="div-head-row patient_narr_tr panel panel-default patient-details">
42
- <div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Contact info</span></div>
43
- <div class="div-table-head"></div>
42
+ <div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Address</span></div>
43
+ <div class="div-table-head">{{{patient_addresses}}}</div>
44
+ <div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Telecom</span></div>
45
+ <div class="div-table-head">{{{patient_telecoms}}}</div>
44
46
  </div>
45
47
  </div>
46
48
  </div>
@@ -9,6 +9,36 @@ class QdmPatient < Mustache
9
9
  @include_style = include_style
10
10
  @patient = patient
11
11
  @qdmPatient = patient.qdmPatient
12
+ @patient_addresses = patient['addresses']
13
+ @patient_telecoms = patient['telecoms']
14
+ end
15
+
16
+ def patient_addresses
17
+ @patient_addresses ||= [CQM::Address.new(
18
+ use: 'HP',
19
+ street: ['202 Burlington Rd.'],
20
+ city: 'Bedford',
21
+ state: 'MA',
22
+ zip: '01730',
23
+ country: 'US'
24
+ )]
25
+ address_str = ""
26
+ @patient_addresses.each do |address|
27
+ # create formatted address
28
+ address_str += "<address>"
29
+ address['street'].each { |street| address_str += "#{street}<br>" }
30
+ address_str += "#{address['city']}, #{address['state']} #{address['zip']}<br> #{address['country']} </address>"
31
+ end
32
+ address_str
33
+ end
34
+
35
+ def patient_telecoms
36
+ @patient_telecoms ||= [CQM::Telecom.new(
37
+ use: 'HP',
38
+ value: '555-555-2003'
39
+ )]
40
+ # create formatted telecoms
41
+ @patient_telecoms.map { |telecom| "(#{telecom['use']}) #{telecom['value']}" }.join("<br>")
12
42
  end
13
43
 
14
44
  def include_style?
@@ -25,7 +55,7 @@ class QdmPatient < Mustache
25
55
  end
26
56
 
27
57
  def unit_string
28
- return "#{self['value']} " unless self['unit']
58
+ return "#{self['value']} " if !self['unit'] || self['unit'] == '1'
29
59
  "#{self['value']} #{self['unit']}"
30
60
  end
31
61
 
@@ -61,13 +91,31 @@ class QdmPatient < Mustache
61
91
  end
62
92
 
63
93
  def code_for_element(element)
64
- "#{element['code']} (#{HQMF::Util::CodeSystemHelper.code_system_for(element['system'])})"
94
+ "#{element['code']} (#{HQMF::Util::CodeSystemHelper.code_system_for(element['system'])})#{code_description(element)}"
65
95
  end
66
96
 
67
97
  def code_system_name
68
98
  HQMF::Util::CodeSystemHelper.code_system_for(self['system'])
69
99
  end
70
100
 
101
+ def code_description(element = self)
102
+ has_descriptions = @patient.respond_to?(:code_description_hash) && !@patient.code_description_hash.empty?
103
+ # mongo keys cannot contain '.', so replace all '.', key example: '21112-8:2_16_840_1_113883_6_1'
104
+ return " - #{@patient.code_description_hash["#{element['code']}:#{element['system']}".tr('.', '_')]}" if has_descriptions
105
+ # no code description available
106
+ ""
107
+ end
108
+
109
+ def demographic_code_description(code)
110
+ # only have code, don't need code system
111
+ has_descriptions = code && @patient.respond_to?(:code_description_hash) && !@patient.code_description_hash.empty?
112
+ if has_descriptions
113
+ key = @patient.code_description_hash.keys.detect { |k| k.starts_with?("#{send(code)}:") }
114
+ return " - #{@patient.code_description_hash[key]}"
115
+ end
116
+ ""
117
+ end
118
+
71
119
  def result_string
72
120
  return unit_string if self['value']
73
121
  return code_code_system_string if self['code']
@@ -18,7 +18,7 @@
18
18
  <patient>
19
19
  {{#patient}}
20
20
  <name>
21
- <given>{{{given_name}}}</given>
21
+ <given>{{given_name}}</given>
22
22
  <family>{{familyName}}</family>
23
23
  </name>
24
24
  {{/patient}}
@@ -48,7 +48,6 @@
48
48
  <entryRelationship typeCode="REFR">
49
49
  <observation classCode="OBS" moodCode="EVN">
50
50
  <templateId root="2.16.840.1.113883.10.20.24.3.88" extension="2017-08-01"/>
51
- <id root="1.3.6.1.4.1.115" extension="{{object_id}}" />
52
51
  <code code="77301-0" codeSystem="2.16.840.1.113883.6.1" displayName="reason" codeSystemName="LOINC"/>
53
52
  <statusCode code="completed"/>
54
53
  <!-- QDM Attribute: Code -->
@@ -41,5 +41,9 @@
41
41
  <!-- QDM Attribute: Components -->
42
42
  {{> qrda_templates/template_partials/_component}}
43
43
  {{/components}}
44
+ {{#encounter_id}}
45
+ <!-- QDM Attribute: relatedTo -->
46
+ {{> qrda_templates/template_partials/_related_to}}
47
+ {{/encounter_id}}
44
48
  </observation>
45
49
  </entry>
@@ -48,5 +48,9 @@
48
48
  <!-- QDM Attribute: Components -->
49
49
  {{> qrda_templates/template_partials/_component}}
50
50
  {{/components}}
51
+ {{#encounter_id}}
52
+ <!-- QDM Attribute: relatedTo -->
53
+ {{> qrda_templates/template_partials/_related_to}}
54
+ {{/encounter_id}}
51
55
  </observation>
52
56
  </entry>
@@ -4,6 +4,6 @@
4
4
  <id root="4adc1020-7b14-11db-9fe1-0800200c9a64" />
5
5
  <code code="ASSERTION" codeSystem="2.16.840.1.113883.5.4" />
6
6
  <statusCode code="completed" />
7
- {{> qrda_templates/template_partials/_data_element_codes_as_values}}
7
+ <value xsi:type="CD" {{> _code}}/>
8
8
  </observation>
9
9
  </entryRelationship>
@@ -1,7 +1,6 @@
1
1
  <entryRelationship typeCode="RSON">
2
2
  <observation classCode="OBS" moodCode="EVN">
3
3
  <templateId root="2.16.840.1.113883.10.20.24.3.88" extension="2017-08-01"/>
4
- <id root="1.3.6.1.4.1.115" extension="{{random_id}}" />
5
4
  <code code="77301-0" codeSystem="2.16.840.1.113883.6.1" displayName="reason" codeSystemName="LOINC"/>
6
5
  {{#relevantPeriod}}
7
6
  {{{relevant_period}}}
@@ -2,16 +2,16 @@
2
2
  <observation classCode="OBS" moodCode="EVN">
3
3
  <templateId root="2.16.840.1.113883.10.20.27.3.2"/>
4
4
  <code nullFlavor="OTH">
5
- <originalText>Time Difference</originalText>
5
+ <originalText>Other</originalText>
6
6
  </code>
7
7
  <statusCode code="completed"/>
8
- <value xsi:type="PQ" value="{{value}}" unit="min"/>
9
- <methodCode code="MEDIAN" displayName="Median" codeSystem="2.16.840.1.113883.5.84" codeSystemName="ObservationMethod"/>
8
+ <value xsi:type="REAL" value="{{value}}"/>
9
+ <methodCode code="{{method}}" displayName="{{method}}" codeSystem="2.16.840.1.113883.5.84" codeSystemName="ObservationMethod"/>
10
10
  <reference typeCode="REFR">
11
11
  <!-- reference to the relevant measure observation in the eMeasure -->
12
12
  <externalObservation classCode="OBS" moodCode="EVN">
13
- <id root="{{id}}"/>
13
+ <id root="{{hqmf_id}}"/>
14
14
  </externalObservation>
15
15
  </reference>
16
16
  </observation>
17
- </entryRelationship>
17
+ </entryRelationship>
@@ -27,11 +27,9 @@
27
27
  {{> _supplemental_data}}
28
28
  {{/population_supplemental_data}}
29
29
  {{/supplemental_data}}
30
- {{#msrpopl?}}
31
30
  {{#population_observation}}
32
31
  {{> _continuous_variable_value}}
33
32
  {{/population_observation}}
34
- {{/msrpopl?}}
35
33
  <reference typeCode="REFR">
36
34
  <externalObservation classCode="OBS" moodCode="EVN">
37
35
  <id root="{{id}}"/>
@@ -15,11 +15,9 @@
15
15
  <methodCode code="COUNT" displayName="Count" codeSystem="2.16.840.1.113883.5.84" codeSystemName="ObservationMethod"/>
16
16
  </observation>
17
17
  </entryRelationship>
18
- {{#msrpopl?}}
19
- {{#stratification_observation}}
20
- {{> _continuous_variable_value}}
21
- {{/stratification_observation}}
22
- {{/msrpopl?}}
18
+ {{#stratification_observation}}
19
+ {{> _continuous_variable_value}}
20
+ {{/stratification_observation}}
23
21
  <reference typeCode="REFR">
24
22
  <externalObservation classCode="OBS" moodCode="EVN">
25
23
  <id root="{{id}}"/>
@@ -58,14 +58,11 @@ class Qrda3R21 < Mustache
58
58
  end
59
59
 
60
60
  def stratification_observation
61
- observation = @measure_result_hash[self['measure_id']].aggregate_count.populations.find {|p| p.type == "OBSERV"}
62
- stratification_observation = @measure_result_hash[self['measure_id']].aggregate_count.populations.find {|p| p.type == "OBSERV"}.stratifications.find {|s| s.id == self['id'] }
63
- stratification_observation.id = observation.id
64
- stratification_observation
61
+ self['observation']
65
62
  end
66
63
 
67
64
  def population_observation
68
- @measure_result_hash[self['measure_id']].aggregate_count.populations.find {|p| p.type == "OBSERV"}
65
+ self['observation']
69
66
  end
70
67
 
71
68
  def supplemental_template_ids
@@ -57,23 +57,24 @@ module Qrda
57
57
  end
58
58
  end
59
59
  class Population
60
- attr_accessor :type, :value, :id, :stratifications, :supplemental_data
60
+ attr_accessor :type, :value, :id, :stratifications, :supplemental_data, :observation
61
61
 
62
62
  def initialize
63
63
  @stratifications = []
64
64
  end
65
65
 
66
- def add_stratification(id,value)
67
- stratifications << Stratification.new(id,value) unless stratifications.find {|st| st.id == id}
66
+ def add_stratification(id,value,observation)
67
+ stratifications << Stratification.new(id,value,observation) unless stratifications.find {|st| st.id == id}
68
68
  end
69
69
 
70
70
  end
71
71
 
72
72
  class Stratification
73
- attr_accessor :id, :value
74
- def initialize(id,value)
73
+ attr_accessor :id, :value, :observation
74
+ def initialize(id,value, observation)
75
75
  @id = id
76
76
  @value = value
77
+ @observation = observation
77
78
  end
78
79
 
79
80
  end
@@ -107,15 +108,17 @@ module Qrda
107
108
  def add_entry(cache_entry, population_sets)
108
109
  population_set = population_sets.where(population_set_id: cache_entry.pop_set_hash[:population_set_id]).first
109
110
  entry_populations = []
110
- %w[IPP DENOM NUMER NUMEX DENEX DENEXCEP MSRPOPL MSRPOPLEX OBSERV].each do |pop_code|
111
- next unless population_set.populations[pop_code] || pop_code == 'OBSERV'
111
+ %w[IPP DENOM NUMER NUMEX DENEX DENEXCEP MSRPOPL MSRPOPLEX].each do |pop_code|
112
+ next unless population_set.populations[pop_code]
112
113
 
113
114
  population = create_population_from_population_set(pop_code, population_set, cache_entry)
114
115
  if cache_entry.pop_set_hash[:stratification_id]
115
116
  strat_id = population_set.stratifications.where(stratification_id: cache_entry.pop_set_hash[:stratification_id]).first&.hqmf_id
116
- population.add_stratification(strat_id,cache_entry[pop_code])
117
+ observation = cache_entry['observations'][pop_code] if cache_entry['observations'] && cache_entry['observations'][pop_code]
118
+ population.add_stratification(strat_id,cache_entry[pop_code], observation)
117
119
  else
118
120
  population.value = cache_entry[pop_code]
121
+ population.observation = cache_entry['observations'][pop_code] if cache_entry['observations'] && cache_entry['observations'][pop_code]
119
122
  population.supplemental_data = cache_entry.supplemental_data[pop_code]
120
123
  end
121
124
  entry_populations << population if population
@@ -128,19 +131,12 @@ module Qrda
128
131
  end
129
132
 
130
133
  def create_population_from_population_set(pop_code, population_set, cache_entry)
131
- population = if pop_code == 'OBSERV'
132
- populations.find { |pop| pop.id == population_set.observations&.first&.hqmf_id }
133
- elsif pop_code != 'STRAT'
134
- populations.find { |pop| pop.id == population_set.populations[pop_code]&.hqmf_id }
135
- end
134
+ population = populations.find { |pop| pop.id == population_set.populations[pop_code]&.hqmf_id } if pop_code != 'STRAT'
136
135
  return population unless population.nil? && !cache_entry.pop_set_hash[:stratification_id]
136
+
137
137
  population = Population.new
138
138
  population.type = pop_code
139
- population.id = if pop_code == 'OBSERV'
140
- population_set.observations&.first&.hqmf_id
141
- else
142
- population_set.populations[pop_code]&.hqmf_id
143
- end
139
+ population.id = population_set.populations[pop_code]&.hqmf_id
144
140
  populations << population
145
141
  population
146
142
  end
@@ -101,7 +101,7 @@ module Qrda
101
101
  end
102
102
 
103
103
  def relevant_date_period_or_null_flavor
104
- return relevant_period if self['relevantPeriod']
104
+ return relevant_period if self['relevantPeriod'] && (self['relevantPeriod']['low'] || self['relevantPeriod']['high'])
105
105
  return relevant_date_time_value if self['relevantDatetime']
106
106
  "<effectiveTime nullFlavor='UNK'/>"
107
107
  end
@@ -128,4 +128,4 @@ module Qrda
128
128
  end
129
129
  end
130
130
  end
131
- end
131
+ end
@@ -16,16 +16,19 @@ module Qrda
16
16
 
17
17
  def provider_npi
18
18
  return nil unless self['ids']
19
+
19
20
  self['ids'].map { |id| id if id['namingSystem'] == '2.16.840.1.113883.4.6' }.compact
20
21
  end
21
22
 
22
23
  def provider_tin
23
24
  return nil unless self['ids']
25
+
24
26
  self['ids'].map { |id| id if id['namingSystem'] == '2.16.840.1.113883.4.2' }.compact
25
27
  end
26
28
 
27
29
  def provider_ccn
28
30
  return nil unless self['ids']
31
+
29
32
  self['ids'].map { |id| id if id['namingSystem'] == '2.16.840.1.113883.4.336' }.compact
30
33
  end
31
34
 
@@ -40,6 +43,42 @@ module Qrda
40
43
  def given_name
41
44
  self['givenNames'].join(' ')
42
45
  end
46
+
47
+ def gender
48
+ gender_elements = @qdmPatient.dataElements.select { |de| de._type == "QDM::PatientCharacteristicSex" }
49
+ return if gender_elements.empty?
50
+ gender_elements.first.dataElementCodes.first['code']
51
+ end
52
+
53
+ def birthdate
54
+ birthdate_elements = @qdmPatient.dataElements.select { |de| de._type == "QDM::PatientCharacteristicBirthdate" }
55
+ return "None" if birthdate_elements.empty?
56
+ birthdate_elements.first['birthDatetime']
57
+ end
58
+
59
+ def expiration
60
+ expired_elements = @qdmPatient.dataElements.select { |de| de._type == "QDM::PatientCharacteristicExpired" }
61
+ return "None" if expired_elements.empty?
62
+ expired_elements.first['expiredDatetime']
63
+ end
64
+
65
+ def race
66
+ race_elements = @qdmPatient.dataElements.select { |de| de._type == "QDM::PatientCharacteristicRace" }
67
+ return if race_elements.empty?
68
+ race_elements.first.dataElementCodes.first['code']
69
+ end
70
+
71
+ def ethnic_group
72
+ ethnic_elements = @qdmPatient.dataElements.select { |de| de._type == "QDM::PatientCharacteristicEthnicity" }
73
+ return if ethnic_elements.empty?
74
+ ethnic_elements.first.dataElementCodes.first['code']
75
+ end
76
+
77
+ def payer
78
+ payer_elements = @qdmPatient.dataElements.select { |de| de._type == "QDM::PatientCharacteristicPayer" }
79
+ return if payer_elements.empty?
80
+ payer_elements.first.dataElementCodes.first['code']
81
+ end
43
82
  end
44
83
  end
45
84
  end
@@ -1,7 +1,7 @@
1
1
  module QRDA
2
2
  module Cat1
3
3
  module DemographicsImporter
4
- def get_demographics(patient, doc)
4
+ def get_demographics(patient, doc, codes)
5
5
  patient_role_element = doc.at_xpath('/cda:ClinicalDocument/cda:recordTarget/cda:patientRole')
6
6
  patient_element = patient_role_element.at_xpath('./cda:patient')
7
7
  patient.givenNames = [patient_element.at_xpath('cda:name/cda:given').text]
@@ -9,28 +9,29 @@ module QRDA
9
9
  patient.qdmPatient.birthDatetime = DateTime.parse(patient_element.at_xpath('cda:birthTime')['value'])
10
10
  pcbd = QDM::PatientCharacteristicBirthdate.new
11
11
  pcbd.birthDatetime = patient.qdmPatient.birthDatetime
12
- pcbd.dataElementCodes = [{ code: '21112-8', system: '2.16.840.1.113883.6.1' }]
12
+ pcbd.dataElementCodes = [QDM::Code.new('21112-8', '2.16.840.1.113883.6.1')]
13
+ codes.add("21112-8:2.16.840.1.113883.6.1")
13
14
  patient.qdmPatient.dataElements << pcbd
14
15
 
15
16
  pcs = QDM::PatientCharacteristicSex.new
16
17
  code_element = patient_element.at_xpath('cda:administrativeGenderCode')
17
- pcs.dataElementCodes = [code_if_present(code_element)]
18
+ pcs.dataElementCodes = [code_if_present(code_element, codes)]
18
19
  patient.qdmPatient.dataElements << pcs unless pcs.dataElementCodes.compact.blank?
19
20
 
20
21
  pcr = QDM::PatientCharacteristicRace.new
21
22
  code_element = patient_element.at_xpath('cda:raceCode')
22
- pcr.dataElementCodes = [code_if_present(code_element)]
23
+ pcr.dataElementCodes = [code_if_present(code_element, codes)]
23
24
  patient.qdmPatient.dataElements << pcr unless pcr.dataElementCodes.compact.blank?
24
25
 
25
26
  pce = QDM::PatientCharacteristicEthnicity.new
26
27
  code_element = patient_element.at_xpath('cda:ethnicGroupCode')
27
- pce.dataElementCodes = [code_if_present(code_element)]
28
+ pce.dataElementCodes = [code_if_present(code_element, codes)]
28
29
  patient.qdmPatient.dataElements << pce unless pce.dataElementCodes.compact.blank?
29
30
  end
30
31
 
31
- def code_if_present(code_element)
32
+ def code_if_present(code_element, codes)
32
33
  return unless code_element && code_element['code'] && code_element['codeSystem']
33
-
34
+ codes.add("#{code_element['code']}:#{code_element['codeSystem']}")
34
35
  QDM::Code.new(code_element['code'], code_element['codeSystem'])
35
36
  end
36
37
  end
@@ -1,7 +1,7 @@
1
1
  module QRDA
2
2
  module Cat1
3
3
  class SectionImporter
4
- attr_accessor :check_for_usable, :status_xpath, :code_xpath
4
+ attr_accessor :check_for_usable, :status_xpath, :code_xpath, :warnings, :codes, :codes_modifiers
5
5
 
6
6
  def initialize(entry_finder)
7
7
  @entry_finder = entry_finder
@@ -9,6 +9,9 @@ module QRDA
9
9
  @entry_id_map = {}
10
10
  @check_for_usable = true
11
11
  @entry_class = QDM::DataElement
12
+ @warnings = []
13
+ @codes = Set.new
14
+ @codes_modifiers = {}
12
15
  end
13
16
 
14
17
  # Traverses an HL7 CDA document passed in and creates an Array of Entry
@@ -41,7 +44,7 @@ module QRDA
41
44
  # This is the id found in the QRDA file
42
45
  entry_qrda_id = extract_id(entry_element, @id_xpath)
43
46
  # Create a hash to map all of entry.ids to the same QRDA ids. This will be used to merge QRDA entries
44
- # that represent the same event.
47
+ # that represent the same event.
45
48
  @entry_id_map["#{entry_qrda_id.value}_#{entry_qrda_id.namingSystem}"] ||= []
46
49
  @entry_id_map["#{entry_qrda_id.value}_#{entry_qrda_id.namingSystem}"] << entry.id
47
50
  entry.dataElementCodes = extract_codes(entry_element, @code_xpath)
@@ -80,6 +83,7 @@ module QRDA
80
83
 
81
84
  def code_if_present(code_element)
82
85
  return unless code_element && code_element['code'] && code_element['codeSystem']
86
+ @codes.add("#{code_element['code']}:#{code_element['codeSystem']}")
83
87
  QDM::Code.new(code_element['code'], code_element['codeSystem'])
84
88
  end
85
89
 
@@ -93,7 +97,9 @@ module QRDA
93
97
  end
94
98
 
95
99
  def extract_interval(parent_element, interval_xpath)
96
- return nil unless parent_element.at_xpath(interval_xpath)
100
+ # nil if the time interval does not exist
101
+ return nil unless time_interval_exists(parent_element, interval_xpath)
102
+
97
103
  if parent_element.at_xpath("#{interval_xpath}/@value")
98
104
  low_time = DateTime.parse(parent_element.at_xpath(interval_xpath)['value'])
99
105
  high_time = DateTime.parse(parent_element.at_xpath(interval_xpath)['value'])
@@ -112,9 +118,32 @@ module QRDA
112
118
  low_time = Time.parse(parent_element.at_xpath("#{interval_xpath}/cda:center")['value'])
113
119
  high_time = Time.parse(parent_element.at_xpath("#{interval_xpath}/cda:center")['value'])
114
120
  end
121
+ if low_time && high_time && low_time > high_time
122
+ # pass warning: current code continues as expected, but adds warning
123
+ id_attr = parent_element.at_xpath(".//cda:id")&.attributes
124
+ id_str = id_attr ? "and id: #{id_attr['root']&.value}(root), #{id_attr['extension']&.value}(extension)" : ""
125
+ qrda_type = @entry_class.to_s.split("::")[1]
126
+ @warnings << ValidationError.new(message: "Interval with low time after high time. Located in element with QRDA type: #{qrda_type} #{id_str}",
127
+ location: parent_element.path)
128
+ end
129
+ if low_time.nil? && high_time.nil?
130
+ id_attr = parent_element.at_xpath(".//cda:id")&.attributes
131
+ id_str = id_attr ? "and id: #{id_attr['root']&.value}(root), #{id_attr['extension']&.value}(extension)" : ""
132
+ qrda_type = @entry_class.to_s.split("::")[1]
133
+ @warnings << ValidationError.new(message: "Interval with nullFlavor low time and nullFlavor high time. Located in element with QRDA type: #{qrda_type} #{id_str}",
134
+ location: parent_element.path)
135
+ end
115
136
  QDM::Interval.new(low_time, high_time).shift_dates(0)
116
137
  end
117
138
 
139
+ def time_interval_exists(parent_element, interval_xpath)
140
+ # false if the time interval does not exist
141
+ return false unless parent_element.at_xpath(interval_xpath)
142
+ # false if the time element exists but has a null Flavor
143
+ return false if parent_element.at_xpath(interval_xpath)['nullFlavor']
144
+ true
145
+ end
146
+
118
147
  def extract_time(parent_element, datetime_xpath)
119
148
  DateTime.parse(parent_element.at_xpath(datetime_xpath)['value']) if parent_element.at_xpath("#{datetime_xpath}/@value")
120
149
  end
@@ -129,6 +158,7 @@ module QRDA
129
158
  # If a Direct Reference Code isn't found, return nil
130
159
  return nil unless key
131
160
  # If a Direct Reference Code is found, return that code
161
+ @codes.add("#{key}:#{value[:code_system]}")
132
162
  QDM::Code.new(key, value[:code_system])
133
163
  end
134
164
 
@@ -154,7 +184,7 @@ module QRDA
154
184
  parent_element.xpath(@result_xpath).each do |elem|
155
185
  result << extract_result_value(elem)
156
186
  end
157
- result.size > 1 ? result : result.first
187
+ result.size > 1 ? result : result.first
158
188
  end
159
189
 
160
190
  def extract_result_value(value_element)
@@ -167,6 +197,11 @@ module QRDA
167
197
  elsif value_element['code'].present?
168
198
  return code_if_present(value_element)
169
199
  elsif value_element.text.present?
200
+ id_attr = value_element.parent.at_xpath(".//cda:id")&.attributes
201
+ id_str = id_attr ? "and id: #{id_attr['root']&.value}(root), #{id_attr['extension']&.value}(extension)" : ""
202
+ qrda_type = @entry_class.to_s.split("::")[1]
203
+ @warnings << ValidationError.new(message: "Value with string type found. When possible, it's best practice to use a coded value or scalar. Located in element with QRDA type: #{qrda_type} #{id_str}",
204
+ location: value_element.path)
170
205
  return value_element.text
171
206
  end
172
207
  end
@@ -177,16 +212,16 @@ module QRDA
177
212
  negation_indicator = parent_element['negationInd']
178
213
  # Return and do not set reason attribute if the entry is negated
179
214
  return nil if negation_indicator.eql?('true')
180
-
181
- reason_element.blank? ? nil : code_if_present(reason_element.first)
215
+
216
+ reason_element.blank? ? nil : code_if_present(reason_element.first)
182
217
  end
183
218
 
184
219
  def extract_negation(parent_element, entry)
185
220
  negation_element = parent_element.xpath("./cda:entryRelationship[@typeCode='RSON']/cda:observation[cda:templateId/@root='2.16.840.1.113883.10.20.24.3.88']/cda:value")
186
221
  negation_indicator = parent_element['negationInd']
187
222
  # Return and do not set negationRationale attribute if the entry is not negated
188
- return unless negation_indicator.eql?('true')
189
-
223
+ return unless negation_indicator.eql?('true')
224
+
190
225
  entry.negationRationale = code_if_present(negation_element.first) unless negation_element.blank?
191
226
  extract_negated_code(parent_element, entry)
192
227
  end
@@ -194,8 +229,18 @@ module QRDA
194
229
  def extract_negated_code(parent_element, entry)
195
230
  code_elements = parent_element.xpath(@code_xpath)
196
231
  code_elements.each do |code_element|
197
- if code_element['nullFlavor'] == 'NA' && code_element['sdtc:valueSet']
198
- entry.dataElementCodes = [{ code: code_element['sdtc:valueSet'], system: '1.2.3.4.5.6.7.8.9.10' }]
232
+ if code_element['nullFlavor'] == 'NA'
233
+ if code_element['sdtc:valueSet']
234
+ entry.dataElementCodes = [{ code: code_element['sdtc:valueSet'], system: '1.2.3.4.5.6.7.8.9.10' }]
235
+ else
236
+ # negated code is nullFlavored with no valueset
237
+ entry.dataElementCodes = [{ code: "NA", system: '1.2.3.4.5.6.7.8.9.10' }]
238
+ id_attr = parent_element.at_xpath(".//cda:id")&.attributes
239
+ id_str = id_attr ? "and id: #{id_attr['root']&.value}(root), #{id_attr['extension']&.value}(extension)" : ""
240
+ qrda_type = @entry_class.to_s.split("::")[1]
241
+ @warnings << ValidationError.new(message: "Negated code element contains nullFlavor code but no valueset. Located in element with QRDA type: #{qrda_type} #{id_str}.",
242
+ location: parent_element.path)
243
+ end
199
244
  end
200
245
  end
201
246
  end
@@ -227,7 +272,7 @@ module QRDA
227
272
  participant_element = facility_location_element.at_xpath("./cda:participantRole[@classCode='SDLOC']/cda:code")
228
273
  facility_location.code = code_if_present(participant_element)
229
274
  facility_location.locationPeriod = extract_interval(facility_location_element, './cda:time')
230
- facility_locations << facility_location
275
+ facility_locations << facility_location if facility_location.code
231
276
  end
232
277
  facility_locations
233
278
  end
@@ -21,15 +21,25 @@ module QRDA
21
21
  encounter_performed.facilityLocations = extract_facility_locations(entry_element)
22
22
  encounter_performed.diagnoses = extract_diagnoses(entry_element)
23
23
  if encounter_performed&.relevantPeriod&.low && encounter_performed&.relevantPeriod&.high
24
- los = encounter_performed.relevantPeriod.high - encounter_performed.relevantPeriod.low
24
+ los = encounter_performed.relevantPeriod.high.to_date - encounter_performed.relevantPeriod.low.to_date
25
25
  encounter_performed.lengthOfStay = QDM::Quantity.new(los.to_i, 'd')
26
26
  end
27
27
  encounter_performed.participant = extract_entity(entry_element, "./cda:entryRelationship/cda:encounter//cda:participant[@typeCode='PRF']")
28
+ extract_modifier_code(encounter_performed, entry_element)
28
29
  encounter_performed
29
30
  end
30
31
 
31
32
  private
32
33
 
34
+ def extract_modifier_code(encounter_performed, entry_element)
35
+ code_element = entry_element.at_xpath(@code_xpath)
36
+ return unless code_element
37
+
38
+ qualifier_name = code_element.at_xpath('./cda:qualifier/cda:name')
39
+ qualifier_value = code_element.at_xpath('./cda:qualifier/cda:value')
40
+ codes_modifiers[encounter_performed.id] = { name: code_if_present(qualifier_name), value: code_if_present(qualifier_value), xpath_location: entry_element.path } if qualifier_value || qualifier_name
41
+ end
42
+
33
43
  def extract_diagnoses(parent_element)
34
44
  diagnosis_elements = parent_element.xpath(@diagnosis_xpath)
35
45
  diagnosis_list = []
@@ -12,70 +12,73 @@ module QRDA
12
12
  def initialize
13
13
  # This differs from other HDS patient importers in that sections can have multiple importers
14
14
  @data_element_importers = []
15
- @data_element_importers << generate_importer(AdverseEventImporter)
16
- @data_element_importers << generate_importer(AllergyIntoleranceImporter)
17
- @data_element_importers << generate_importer(AssessmentOrderImporter)
18
- @data_element_importers << generate_importer(AssessmentPerformedImporter)
19
- @data_element_importers << generate_importer(AssessmentRecommendedImporter)
20
- @data_element_importers << generate_importer(CommunicationPerformedImporter)
21
- @data_element_importers << generate_importer(DeviceAppliedImporter)
22
- @data_element_importers << generate_importer(DeviceOrderImporter)
23
- @data_element_importers << generate_importer(DeviceRecommendedImporter)
24
- @data_element_importers << generate_importer(DiagnosisImporter)
25
- @data_element_importers << generate_importer(DiagnosticStudyOrderImporter)
26
- @data_element_importers << generate_importer(DiagnosticStudyPerformedImporter)
27
- @data_element_importers << generate_importer(DiagnosticStudyRecommendedImporter)
28
- @data_element_importers << generate_importer(EncounterOrderImporter)
29
- @data_element_importers << generate_importer(EncounterPerformedImporter)
30
- @data_element_importers << generate_importer(EncounterRecommendedImporter)
31
- @data_element_importers << generate_importer(FamilyHistoryImporter)
32
- @data_element_importers << generate_importer(ImmunizationAdministeredImporter)
33
- @data_element_importers << generate_importer(ImmunizationOrderImporter)
34
- @data_element_importers << generate_importer(InterventionOrderImporter)
35
- @data_element_importers << generate_importer(InterventionPerformedImporter)
36
- @data_element_importers << generate_importer(InterventionRecommendedImporter)
37
- @data_element_importers << generate_importer(LaboratoryTestOrderImporter)
38
- @data_element_importers << generate_importer(LaboratoryTestPerformedImporter)
39
- @data_element_importers << generate_importer(LaboratoryTestRecommendedImporter)
40
- @data_element_importers << generate_importer(MedicationActiveImporter)
41
- @data_element_importers << generate_importer(MedicationAdministeredImporter)
42
- @data_element_importers << generate_importer(MedicationDischargeImporter)
43
- @data_element_importers << generate_importer(MedicationDispensedImporter)
44
- @data_element_importers << generate_importer(MedicationOrderImporter)
45
- @data_element_importers << generate_importer(PatientCareExperienceImporter)
46
- @data_element_importers << generate_importer(PatientCharacteristicClinicalTrialParticipantImporter)
47
- @data_element_importers << generate_importer(PatientCharacteristicExpiredImporter)
48
- @data_element_importers << generate_importer(PatientCharacteristicPayerImporter)
49
- @data_element_importers << generate_importer(PhysicalExamOrderImporter)
50
- @data_element_importers << generate_importer(PhysicalExamPerformedImporter)
51
- @data_element_importers << generate_importer(PhysicalExamRecommendedImporter)
52
- @data_element_importers << generate_importer(ProcedureOrderImporter)
53
- @data_element_importers << generate_importer(ProcedurePerformedImporter)
54
- @data_element_importers << generate_importer(ProcedureRecommendedImporter)
55
- @data_element_importers << generate_importer(ProgramParticipationImporter)
56
- @data_element_importers << generate_importer(ProviderCareExperienceImporter)
57
- @data_element_importers << generate_importer(RelatedPersonImporter)
58
- @data_element_importers << generate_importer(SubstanceAdministeredImporter)
59
- @data_element_importers << generate_importer(SubstanceOrderImporter)
60
- @data_element_importers << generate_importer(SubstanceRecommendedImporter)
61
- @data_element_importers << generate_importer(SymptomImporter)
15
+ @data_element_importers << AdverseEventImporter.new
16
+ @data_element_importers << AllergyIntoleranceImporter.new
17
+ @data_element_importers << AssessmentOrderImporter.new
18
+ @data_element_importers << AssessmentPerformedImporter.new
19
+ @data_element_importers << AssessmentRecommendedImporter.new
20
+ @data_element_importers << CommunicationPerformedImporter.new
21
+ @data_element_importers << DeviceAppliedImporter.new
22
+ @data_element_importers << DeviceOrderImporter.new
23
+ @data_element_importers << DeviceRecommendedImporter.new
24
+ @data_element_importers << DiagnosisImporter.new
25
+ @data_element_importers << DiagnosticStudyOrderImporter.new
26
+ @data_element_importers << DiagnosticStudyPerformedImporter.new
27
+ @data_element_importers << DiagnosticStudyRecommendedImporter.new
28
+ @data_element_importers << EncounterOrderImporter.new
29
+ @data_element_importers << EncounterPerformedImporter.new
30
+ @data_element_importers << EncounterRecommendedImporter.new
31
+ @data_element_importers << FamilyHistoryImporter.new
32
+ @data_element_importers << ImmunizationAdministeredImporter.new
33
+ @data_element_importers << ImmunizationOrderImporter.new
34
+ @data_element_importers << InterventionOrderImporter.new
35
+ @data_element_importers << InterventionPerformedImporter.new
36
+ @data_element_importers << InterventionRecommendedImporter.new
37
+ @data_element_importers << LaboratoryTestOrderImporter.new
38
+ @data_element_importers << LaboratoryTestPerformedImporter.new
39
+ @data_element_importers << LaboratoryTestRecommendedImporter.new
40
+ @data_element_importers << MedicationActiveImporter.new
41
+ @data_element_importers << MedicationAdministeredImporter.new
42
+ @data_element_importers << MedicationDischargeImporter.new
43
+ @data_element_importers << MedicationDispensedImporter.new
44
+ @data_element_importers << MedicationOrderImporter.new
45
+ @data_element_importers << PatientCareExperienceImporter.new
46
+ @data_element_importers << PatientCharacteristicClinicalTrialParticipantImporter.new
47
+ @data_element_importers << PatientCharacteristicExpiredImporter.new
48
+ @data_element_importers << PatientCharacteristicPayerImporter.new
49
+ @data_element_importers << PhysicalExamOrderImporter.new
50
+ @data_element_importers << PhysicalExamPerformedImporter.new
51
+ @data_element_importers << PhysicalExamRecommendedImporter.new
52
+ @data_element_importers << ProcedureOrderImporter.new
53
+ @data_element_importers << ProcedurePerformedImporter.new
54
+ @data_element_importers << ProcedureRecommendedImporter.new
55
+ @data_element_importers << ProgramParticipationImporter.new
56
+ @data_element_importers << ProviderCareExperienceImporter.new
57
+ @data_element_importers << RelatedPersonImporter.new
58
+ @data_element_importers << SubstanceAdministeredImporter.new
59
+ @data_element_importers << SubstanceOrderImporter.new
60
+ @data_element_importers << SubstanceRecommendedImporter.new
61
+ @data_element_importers << SymptomImporter.new
62
62
  end
63
63
 
64
64
  def parse_cat1(doc)
65
65
  patient = CQM::Patient.new
66
+ warnings = []
67
+ codes = Set.new
68
+ codes_modifiers = {}
66
69
  entry_id_map = {}
67
- import_data_elements(patient, doc, entry_id_map)
70
+ import_data_elements(patient, doc, entry_id_map, codes, codes_modifiers, warnings)
68
71
  normalize_references(patient, entry_id_map)
69
- get_demographics(patient, doc)
70
- patient
72
+ get_demographics(patient, doc, codes)
73
+ [patient, warnings, codes, codes_modifiers]
71
74
  end
72
75
 
73
- def import_data_elements(patient, doc, entry_id_map)
76
+ def import_data_elements(patient, doc, entry_id_map, codes = Set.new, codes_modifiers = {}, warnings = [])
74
77
  context = doc.xpath("/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component/cda:section[cda:templateId/@root = '2.16.840.1.113883.10.20.24.2.1']")
75
78
  nrh = NarrativeReferenceHandler.new
76
79
  nrh.build_id_map(doc)
77
- @data_element_importers.each do |entry_package|
78
- data_elements, id_map = entry_package.package_entries(context, nrh)
80
+ @data_element_importers.each do |importer|
81
+ data_elements, id_map = importer.create_entries(context, nrh)
79
82
  new_data_elements = []
80
83
 
81
84
  id_map.each_value do |elem_ids|
@@ -92,7 +95,7 @@ module QRDA
92
95
  unique_element_keys << key_elements_for_determining_encounter_uniqueness(data_element)
93
96
 
94
97
  # Loop through all other data elements with the same id
95
- elem_ids[1,elem_ids.length].each do |dup_id|
98
+ elem_ids[1,elem_ids.length].each do |dup_id|
96
99
  dup_element = data_elements.find { |de| de.id == dup_id }
97
100
  dup_element_keys = key_elements_for_determining_encounter_uniqueness(dup_element)
98
101
  # See if a previously selected data element shared all of the keys files
@@ -107,6 +110,13 @@ module QRDA
107
110
 
108
111
  patient.qdmPatient.dataElements << new_data_elements
109
112
  entry_id_map.merge!(id_map)
113
+ warnings.concat(importer.warnings)
114
+ codes.merge(importer.codes)
115
+ codes_modifiers.merge!(importer.codes_modifiers)
116
+ # reset warnings and codes after they're captured so that the importer can be re-used
117
+ importer.warnings = []
118
+ importer.codes_modifiers = {}
119
+ importer.codes = Set.new
110
120
  end
111
121
  end
112
122
 
@@ -136,12 +146,6 @@ module QRDA
136
146
  data_element.relatedTo = relations_to_add.map(&:to_s)
137
147
  end
138
148
  end
139
-
140
- private
141
-
142
- def generate_importer(importer_class)
143
- EntryPackage.new(importer_class.new)
144
- end
145
149
  end
146
150
  end
147
151
  end
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: 3.0.1
4
+ version: 3.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - The MITRE Corporation
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-09 00:00:00.000000000 Z
11
+ date: 2021-06-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cqm-models
@@ -16,14 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 3.0.0
19
+ version: '3.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 3.0.0
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: cqm-validators
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: mustache
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -70,16 +84,22 @@ dependencies:
70
84
  name: nokogiri
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
- - - "~>"
87
+ - - ">="
74
88
  - !ruby/object:Gem::Version
75
- version: '1.10'
89
+ version: 1.8.5
90
+ - - "<"
91
+ - !ruby/object:Gem::Version
92
+ version: 1.12.0
76
93
  type: :runtime
77
94
  prerelease: false
78
95
  version_requirements: !ruby/object:Gem::Requirement
79
96
  requirements:
80
- - - "~>"
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: 1.8.5
100
+ - - "<"
81
101
  - !ruby/object:Gem::Version
82
- version: '1.10'
102
+ version: 1.12.0
83
103
  - !ruby/object:Gem::Dependency
84
104
  name: uuid
85
105
  requirement: !ruby/object:Gem::Requirement
@@ -314,7 +334,6 @@ files:
314
334
  - lib/qrda-import/data-element-importers/substance_recommended_importer.rb
315
335
  - lib/qrda-import/data-element-importers/symptom_importer.rb
316
336
  - lib/qrda-import/entry_finder.rb
317
- - lib/qrda-import/entry_package.rb
318
337
  - lib/qrda-import/narrative_reference_handler.rb
319
338
  - lib/qrda-import/patient_importer.rb
320
339
  - lib/util/code_system_helper.rb
@@ -330,7 +349,7 @@ homepage: https://github.com/projecttacoma/cqm-reports
330
349
  licenses:
331
350
  - Apache-2.0
332
351
  metadata: {}
333
- post_install_message:
352
+ post_install_message:
334
353
  rdoc_options: []
335
354
  require_paths:
336
355
  - lib
@@ -345,9 +364,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
345
364
  - !ruby/object:Gem::Version
346
365
  version: '0'
347
366
  requirements: []
348
- rubyforge_project:
349
- rubygems_version: 2.6.14
350
- signing_key:
367
+ rubygems_version: 3.1.4
368
+ signing_key:
351
369
  specification_version: 4
352
370
  summary: A library for import and export of reports for use with electronic Clinical
353
371
  Quality Measures (eCQMs).
@@ -1,16 +0,0 @@
1
- module QRDA
2
- module Cat1
3
- class EntryPackage
4
-
5
- attr_accessor :importer_type
6
-
7
- def initialize(type)
8
- self.importer_type = type
9
- end
10
-
11
- def package_entries(doc, nrh)
12
- importer_type.create_entries(doc, nrh)
13
- end
14
- end
15
- end
16
- end