cqm-models 1.0.3 → 1.1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.eslintrc.json +10 -2
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -4
- data/.rubocop.yml +3 -0
- data/.travis.yml +31 -1
- data/README.md +59 -2
- data/app/assets/javascripts/AdverseEvent.js +16 -7
- data/app/assets/javascripts/AllDataElements.js +6 -6
- data/app/assets/javascripts/AllergyIntolerance.js +16 -7
- data/app/assets/javascripts/AssessmentOrder.js +15 -6
- data/app/assets/javascripts/AssessmentPerformed.js +16 -7
- data/app/assets/javascripts/AssessmentRecommended.js +16 -7
- data/app/assets/javascripts/CareGoal.js +16 -7
- data/app/assets/javascripts/CommunicationPerformed.js +15 -6
- data/app/assets/javascripts/DeviceApplied.js +16 -7
- data/app/assets/javascripts/DeviceOrder.js +16 -7
- data/app/assets/javascripts/DeviceRecommended.js +16 -7
- data/app/assets/javascripts/Diagnosis.js +16 -7
- data/app/assets/javascripts/DiagnosticStudyOrder.js +16 -7
- data/app/assets/javascripts/DiagnosticStudyPerformed.js +16 -7
- data/app/assets/javascripts/DiagnosticStudyRecommended.js +16 -7
- data/app/assets/javascripts/EncounterOrder.js +16 -7
- data/app/assets/javascripts/EncounterPerformed.js +16 -7
- data/app/assets/javascripts/EncounterRecommended.js +16 -7
- data/app/assets/javascripts/FamilyHistory.js +16 -7
- data/app/assets/javascripts/Id.js +7 -3
- data/app/assets/javascripts/ImmunizationAdministered.js +16 -7
- data/app/assets/javascripts/ImmunizationOrder.js +16 -7
- data/app/assets/javascripts/IndividualResult.js +7 -2
- data/app/assets/javascripts/InterventionOrder.js +16 -7
- data/app/assets/javascripts/InterventionPerformed.js +16 -7
- data/app/assets/javascripts/InterventionRecommended.js +16 -7
- data/app/assets/javascripts/LaboratoryTestOrder.js +16 -7
- data/app/assets/javascripts/LaboratoryTestPerformed.js +16 -7
- data/app/assets/javascripts/LaboratoryTestRecommended.js +16 -7
- data/app/assets/javascripts/MedicationActive.js +16 -7
- data/app/assets/javascripts/MedicationAdministered.js +16 -7
- data/app/assets/javascripts/MedicationDischarge.js +16 -7
- data/app/assets/javascripts/MedicationDispensed.js +16 -7
- data/app/assets/javascripts/MedicationOrder.js +16 -7
- data/app/assets/javascripts/Participation.js +15 -6
- data/app/assets/javascripts/PatientCareExperience.js +16 -7
- data/app/assets/javascripts/PatientCharacteristic.js +16 -7
- data/app/assets/javascripts/PatientCharacteristicBirthdate.js +16 -7
- data/app/assets/javascripts/PatientCharacteristicClinicalTrialParticipant.js +16 -7
- data/app/assets/javascripts/PatientCharacteristicEthnicity.js +16 -7
- data/app/assets/javascripts/PatientCharacteristicExpired.js +16 -7
- data/app/assets/javascripts/PatientCharacteristicPayer.js +16 -7
- data/app/assets/javascripts/PatientCharacteristicRace.js +16 -7
- data/app/assets/javascripts/PatientCharacteristicSex.js +16 -7
- data/app/assets/javascripts/PhysicalExamOrder.js +16 -7
- data/app/assets/javascripts/PhysicalExamPerformed.js +16 -7
- data/app/assets/javascripts/PhysicalExamRecommended.js +16 -7
- data/app/assets/javascripts/ProcedureOrder.js +16 -7
- data/app/assets/javascripts/ProcedurePerformed.js +16 -7
- data/app/assets/javascripts/ProcedureRecommended.js +16 -7
- data/app/assets/javascripts/ProviderCareExperience.js +16 -7
- data/app/assets/javascripts/ProviderCharacteristic.js +16 -7
- data/app/assets/javascripts/QDMPatient.js +262 -0
- data/app/assets/javascripts/Result.js +7 -2
- data/app/assets/javascripts/ResultComponent.js +14 -6
- data/app/assets/javascripts/SubstanceAdministered.js +16 -7
- data/app/assets/javascripts/SubstanceOrder.js +16 -7
- data/app/assets/javascripts/SubstanceRecommended.js +16 -7
- data/app/assets/javascripts/Symptom.js +16 -7
- data/app/assets/javascripts/attributes/Component.js +30 -0
- data/app/assets/javascripts/attributes/FacilityLocation.js +30 -0
- data/app/assets/javascripts/basetypes/Any.js +1 -1
- data/app/assets/javascripts/basetypes/Code.js +1 -1
- data/app/assets/javascripts/basetypes/DataElement.js +2 -1
- data/app/assets/javascripts/basetypes/DateTime.js +5 -1
- data/app/assets/javascripts/basetypes/Interval.js +1 -1
- data/app/assets/javascripts/basetypes/Quantity.js +1 -1
- data/app/assets/javascripts/basetypes/Ratio.js +1 -1
- data/app/assets/javascripts/browser.js +3 -0
- data/app/assets/javascripts/cqm/AllCQMModels.js +24 -0
- data/app/assets/javascripts/cqm/CQLLibrary.js +32 -0
- data/app/assets/javascripts/cqm/CQLStatementDependency.js +28 -0
- data/app/assets/javascripts/cqm/Concept.js +17 -0
- data/app/assets/javascripts/cqm/Measure.js +108 -0
- data/app/assets/javascripts/{MeasurePackage.js → cqm/MeasurePackage.js} +9 -3
- data/app/assets/javascripts/cqm/Patient.js +38 -0
- data/app/assets/javascripts/cqm/PopulationSet.js +88 -0
- data/app/assets/javascripts/cqm/Provider.js +44 -0
- data/app/assets/javascripts/cqm/ValueSet.js +26 -0
- data/app/assets/javascripts/index.js +18 -8
- data/app/models/{qdm/tacoma → cqm}/concept.rb +4 -4
- data/app/models/cqm/cql_library.rb +22 -0
- data/app/models/cqm/cql_statement_dependency.rb +24 -0
- data/app/models/cqm/measure.rb +105 -0
- data/app/models/{qdm/tacoma → cqm}/measure_package.rb +2 -2
- data/app/models/cqm/patient.rb +20 -0
- data/app/models/cqm/population_set.rb +86 -0
- data/app/models/cqm/provider.rb +110 -0
- data/app/models/cqm/valueset.rb +15 -0
- data/app/models/hqmfOid_to_datatype_map.json +55 -0
- data/app/models/model_finder.rb +12 -0
- data/app/models/models.rb +18 -6
- data/app/models/qdm/adverse_event.rb +3 -2
- data/app/models/qdm/allergy_intolerance.rb +2 -1
- data/app/models/qdm/assessment_order.rb +1 -0
- data/app/models/qdm/assessment_performed.rb +2 -1
- data/app/models/qdm/assessment_recommended.rb +2 -1
- data/app/models/qdm/attributes/attribute.rb +63 -0
- data/app/models/qdm/{component.rb → attributes/component.rb} +1 -1
- data/app/models/qdm/{facility_location.rb → attributes/facility_location.rb} +1 -1
- data/app/models/qdm/basetypes/data_element.rb +21 -6
- data/app/models/qdm/basetypes/interval.rb +2 -1
- data/app/models/qdm/care_goal.rb +2 -1
- data/app/models/qdm/communication_performed.rb +1 -0
- data/app/models/qdm/device_applied.rb +2 -1
- data/app/models/qdm/device_order.rb +2 -1
- data/app/models/qdm/device_recommended.rb +2 -1
- data/app/models/qdm/diagnosis.rb +2 -1
- data/app/models/qdm/diagnostic_study_order.rb +2 -1
- data/app/models/qdm/diagnostic_study_performed.rb +3 -2
- data/app/models/qdm/diagnostic_study_recommended.rb +2 -1
- data/app/models/qdm/encounter_order.rb +3 -2
- data/app/models/qdm/encounter_performed.rb +2 -1
- data/app/models/qdm/encounter_recommended.rb +3 -2
- data/app/models/qdm/family_history.rb +2 -1
- data/app/models/qdm/immunization_administered.rb +2 -1
- data/app/models/qdm/immunization_order.rb +2 -1
- data/app/models/qdm/intervention_order.rb +2 -1
- data/app/models/qdm/intervention_performed.rb +2 -1
- data/app/models/qdm/intervention_recommended.rb +2 -1
- data/app/models/qdm/laboratory_test_order.rb +2 -1
- data/app/models/qdm/laboratory_test_performed.rb +2 -1
- data/app/models/qdm/laboratory_test_recommended.rb +2 -1
- data/app/models/qdm/medication_active.rb +2 -1
- data/app/models/qdm/medication_administered.rb +2 -1
- data/app/models/qdm/medication_discharge.rb +2 -1
- data/app/models/qdm/medication_dispensed.rb +4 -3
- data/app/models/qdm/medication_order.rb +3 -2
- data/app/models/qdm/participation.rb +1 -0
- data/app/models/qdm/patient.rb +7 -4
- data/app/models/qdm/patient_care_experience.rb +2 -1
- data/app/models/qdm/patient_characteristic.rb +2 -1
- data/app/models/qdm/patient_characteristic_birthdate.rb +2 -1
- data/app/models/qdm/patient_characteristic_clinical_trial_participant.rb +2 -1
- data/app/models/qdm/patient_characteristic_ethnicity.rb +2 -1
- data/app/models/qdm/patient_characteristic_expired.rb +2 -1
- data/app/models/qdm/patient_characteristic_payer.rb +2 -1
- data/app/models/qdm/patient_characteristic_race.rb +2 -1
- data/app/models/qdm/patient_characteristic_sex.rb +2 -1
- data/app/models/qdm/physical_exam_order.rb +2 -1
- data/app/models/qdm/physical_exam_performed.rb +2 -1
- data/app/models/qdm/physical_exam_recommended.rb +2 -1
- data/app/models/qdm/procedure_order.rb +2 -1
- data/app/models/qdm/procedure_performed.rb +2 -1
- data/app/models/qdm/procedure_recommended.rb +2 -1
- data/app/models/qdm/provider_care_experience.rb +2 -1
- data/app/models/qdm/provider_characteristic.rb +2 -1
- data/app/models/qdm/substance_administered.rb +2 -1
- data/app/models/qdm/substance_order.rb +2 -1
- data/app/models/qdm/substance_recommended.rb +2 -1
- data/app/models/qdm/symptom.rb +2 -1
- data/bin/build_cql_execution.sh +8 -0
- data/bin/validate_browser.sh +17 -0
- data/bin/validate_generator.sh +12 -0
- data/cqm-models.gemspec +5 -2
- data/data/oids_qdm_5.4.json +321 -0
- data/data/{oids.json → oids_unversioned.json} +0 -0
- data/dist/browser.js +102937 -0
- data/dist/index.js +36637 -36540
- data/lib/generate_models.rb +89 -93
- data/lib/generate_patients.rb +125 -0
- data/lib/generate_types.rb +64 -0
- data/lib/generators/custom_mongo/model.rb.tt +20 -0
- data/lib/generators/custom_mongo/model_generator.rb +34 -0
- data/notice.md +9 -0
- data/package.json +12 -6
- data/templates/id_template.js.erb +8 -3
- data/templates/index_template.js.erb +18 -8
- data/templates/models_template.rb.erb +20 -4
- data/templates/mongoose_template.js.erb +23 -15
- data/templates/patient_extension.rb.erb +7 -4
- data/templates/patient_template.js.erb +88 -87
- data/yarn.lock +552 -347
- metadata +86 -19
- data/app/assets/javascripts/Component.js +0 -26
- data/app/assets/javascripts/Concept.js +0 -14
- data/app/assets/javascripts/FacilityLocation.js +0 -26
- data/app/assets/javascripts/Measure.js +0 -78
- data/app/assets/javascripts/Patient.js +0 -262
- data/app/assets/javascripts/ValueSet.js +0 -30
- data/app/models/qdm/tacoma/measure.rb +0 -174
- data/app/models/qdm/tacoma/valueset.rb +0 -68
data/lib/generate_models.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
#! /usr/bin/env ruby
|
2
|
-
|
3
2
|
require 'nokogiri'
|
4
3
|
require 'active_support/all'
|
5
4
|
require 'rails/generators'
|
6
5
|
require 'erb'
|
7
6
|
require 'json'
|
7
|
+
require_relative './generators/custom_mongo/model_generator'
|
8
8
|
|
9
9
|
###############################################################################
|
10
10
|
# Helpers
|
@@ -82,10 +82,15 @@ qdm_version = modelinfo.xpath('//ns4:modelInfo').first.attributes['version'].val
|
|
82
82
|
# Datatypes (keys are the datatype name, values are the datatype attributes)
|
83
83
|
datatypes = {}
|
84
84
|
|
85
|
+
# We will export a hqmfOid to datatype map
|
86
|
+
hqmfOid_to_datatype_map = {}
|
87
|
+
|
85
88
|
# Loop through each typeInfo node (each of these is a QDM datatype)
|
86
89
|
modelinfo.xpath('//ns4:typeInfo').each do |type|
|
87
90
|
# Grab the name of this QDM datatype
|
88
91
|
datatype_name = type.attributes['name'].value.split('.').last
|
92
|
+
# Reject irrelevant datatypes
|
93
|
+
next if datatype_name.include?('Negative') || datatype_name.include?('Positive') || datatype_name.include?('QDMBaseType')
|
89
94
|
|
90
95
|
# Grab the QDM attributes for this datatype
|
91
96
|
attributes = []
|
@@ -106,9 +111,27 @@ modelinfo.xpath('//ns4:typeInfo').each do |type|
|
|
106
111
|
attributes << { name: attribute_name, type: attribute_type }
|
107
112
|
end
|
108
113
|
|
109
|
-
#
|
110
|
-
|
111
|
-
|
114
|
+
# Add the label as qdmTitle
|
115
|
+
qdm_title = type['label']
|
116
|
+
if qdm_title.nil? # If there's no label, check if there is a "positive" profile
|
117
|
+
positive_profile = modelinfo.at_xpath("/ns4:modelInfo/ns4:typeInfo[@xsi:type='ns4:ProfileInfo'][@identifier='Positive#{datatype_name}']")
|
118
|
+
qdm_title = positive_profile['label'] unless positive_profile.nil?
|
119
|
+
end
|
120
|
+
attributes << { name: 'qdmTitle', type: 'System.String', default: qdm_title } unless qdm_title.nil?
|
121
|
+
|
122
|
+
# Add the extra info that is manually maintained in the "oids" file
|
123
|
+
extra_info = oids[datatype_name.underscore]
|
124
|
+
if extra_info.present?
|
125
|
+
attributes << { name: 'hqmfOid', type: 'System.String', default: extra_info['hqmf_oid'] } if extra_info['hqmf_oid'].present?
|
126
|
+
attributes << { name: 'qrdaOid', type: 'System.String', default: extra_info['qrda_oid'] } if extra_info['qrda_oid'].present?
|
127
|
+
attributes << { name: 'qdmCategory', type: 'System.String', default: extra_info['qdm_category'] } if extra_info['qdm_category'].present?
|
128
|
+
attributes << { name: 'qdmStatus', type: 'System.String', default: extra_info['qdm_status'] } if extra_info['qdm_status'].present?
|
129
|
+
hqmfOid_to_datatype_map[extra_info['hqmf_oid']] = datatype_name if extra_info['hqmf_oid'].present?
|
130
|
+
end
|
131
|
+
|
132
|
+
attributes << { name: 'qdmVersion', type: 'System.String', default: qdm_version }
|
133
|
+
|
134
|
+
datatypes[datatype_name] = { attributes: attributes }
|
112
135
|
end
|
113
136
|
|
114
137
|
###############################################################################
|
@@ -118,25 +141,25 @@ end
|
|
118
141
|
puts 'Generating Ruby models...'
|
119
142
|
|
120
143
|
# Do a quick sanity check on attribute types
|
121
|
-
datatypes.each do |datatype,
|
122
|
-
attributes.each do |attribute|
|
144
|
+
datatypes.each do |datatype, info|
|
145
|
+
info[:attributes].each do |attribute|
|
123
146
|
raise 'Unsupported type from modelinfo file for Ruby types: ' + attribute[:type] + 'from: ' + datatype if TYPE_LOOKUP_RB[attribute[:type]].blank?
|
124
147
|
raise 'Unsupported type from modelinfo file for JavaScript types: ' + attribute[:type] + 'from: ' + datatype if TYPE_LOOKUP_JS[attribute[:type]].blank?
|
125
148
|
end
|
126
149
|
end
|
127
150
|
|
128
151
|
# Create Ruby models
|
129
|
-
extra_fields_rb = [
|
130
|
-
'hqmfOid:String',
|
131
|
-
'qrdaOid:String',
|
132
|
-
'qdmCategory:String',
|
133
|
-
'qdmStatus:String',
|
134
|
-
'qdmVersion:String'
|
135
|
-
]
|
136
152
|
base_module = 'QDM::'
|
137
153
|
base_module = 'Test::QDM::' if IS_TEST
|
138
|
-
datatypes.each do |datatype,
|
139
|
-
|
154
|
+
datatypes.each do |datatype, info|
|
155
|
+
name = base_module + datatype
|
156
|
+
attributes = info[:attributes].map do |attribute|
|
157
|
+
attribute = attribute.dup
|
158
|
+
attribute[:type] = TYPE_LOOKUP_RB[attribute[:type]]
|
159
|
+
attribute
|
160
|
+
end
|
161
|
+
generator_args = [name, attributes]
|
162
|
+
Rails::Generators.invoke('custom_mongo:model', generator_args)
|
140
163
|
end
|
141
164
|
|
142
165
|
# Create require file (if not in test mode)
|
@@ -147,6 +170,12 @@ unless IS_TEST
|
|
147
170
|
File.open(file_path, 'w') { |file| file.puts renderer.result(binding) }
|
148
171
|
end
|
149
172
|
|
173
|
+
# Javascript PatientSchema for was renamed to QDMPatient since it just contains the QDM data
|
174
|
+
if datatypes['Patient']
|
175
|
+
datatypes['QDMPatient'] = datatypes['Patient']
|
176
|
+
datatypes.delete('Patient')
|
177
|
+
end
|
178
|
+
|
150
179
|
puts 'Generating JavaScript models...'
|
151
180
|
|
152
181
|
# Create JavaScript models
|
@@ -154,25 +183,22 @@ template = File.read('templates/mongoose_template.js.erb')
|
|
154
183
|
default_renderer = ERB.new(template, nil, '-')
|
155
184
|
file_path = 'app/assets/javascripts/'
|
156
185
|
file_path = 'tmp/' if IS_TEST
|
157
|
-
extra_fields_js = [
|
158
|
-
{ name: 'hqmfOid', type: 'System.String' },
|
159
|
-
{ name: 'qrdaOid', type: 'System.String' },
|
160
|
-
{ name: 'qdmCategory', type: 'System.String' },
|
161
|
-
{ name: 'qdmStatus', type: 'System.String' },
|
162
|
-
{ name: 'qdmVersion', type: 'System.String' },
|
163
|
-
{ name: '_type', type: 'System.String' }
|
164
|
-
]
|
165
186
|
datatype_custom_templates = {
|
166
|
-
|
187
|
+
QDMPatient: 'templates/patient_template.js.erb',
|
167
188
|
Id: 'templates/id_template.js.erb'
|
168
189
|
}
|
169
|
-
|
190
|
+
|
191
|
+
datatypes.each do |datatype, info|
|
170
192
|
renderer = default_renderer
|
171
193
|
if datatype_custom_templates.key?(datatype.to_sym)
|
172
194
|
puts "using custom template for #{datatype}"
|
173
195
|
renderer = ERB.new(File.read(datatype_custom_templates[datatype.to_sym]), nil, '-')
|
174
196
|
end
|
175
|
-
attrs_with_extras = attributes
|
197
|
+
attrs_with_extras = info[:attributes] # this field gets used in the template
|
198
|
+
# Custom datatypes don't need _type
|
199
|
+
unless datatype_custom_templates.key?(datatype.to_sym)
|
200
|
+
attrs_with_extras << { name: '_type', type: 'System.String', default: "QDM::#{datatype.underscore.camelize}" } # Add Class
|
201
|
+
end
|
176
202
|
puts ' ' + file_path + datatype + '.js'
|
177
203
|
File.open(file_path + datatype + '.js', 'w') { |file| file.puts renderer.result(binding) }
|
178
204
|
end
|
@@ -182,12 +208,18 @@ unless IS_TEST
|
|
182
208
|
indtemplate = File.read('templates/index_template.js.erb')
|
183
209
|
renderer = ERB.new(indtemplate, nil, '-')
|
184
210
|
file_path = 'app/assets/javascripts/index.js'
|
211
|
+
puts ' ' + file_path
|
185
212
|
File.open(file_path, 'w') { |file| file.puts renderer.result(binding) }
|
186
213
|
|
187
214
|
alltemplate = File.read('templates/all_data_elements_template.js.erb')
|
188
215
|
renderer = ERB.new(alltemplate, nil, '-')
|
189
216
|
file_path = 'app/assets/javascripts/AllDataElements.js'
|
217
|
+
puts ' ' + file_path
|
190
218
|
File.open(file_path, 'w') { |file| file.puts renderer.result(binding) }
|
219
|
+
contents = File.read(file_path)
|
220
|
+
contents.gsub!(%r{\/FacilityLocation.js}, '/attributes/FacilityLocation.js')
|
221
|
+
contents.gsub!(%r{\/Component.js}, '/attributes/Component.js')
|
222
|
+
File.open(file_path, 'w') { |file| file.puts contents }
|
191
223
|
end
|
192
224
|
|
193
225
|
###############################################################################
|
@@ -205,44 +237,21 @@ Dir.glob(ruby_models_path + '*.rb').each do |file_name|
|
|
205
237
|
# Cut out the 'Any' type placeholder (these attributes could point to anything).
|
206
238
|
contents.gsub!(/, type: Any/, '')
|
207
239
|
|
208
|
-
# Add QDM version
|
209
|
-
contents.gsub!(/field :qdmVersion, type: String/, "field :qdmVersion, type: String, default: '#{qdm_version}'")
|
210
|
-
|
211
240
|
# Add default to [] for facilityLocations
|
212
241
|
contents.gsub!(/field :facilityLocations, type: Array/, 'field :facilityLocations, type: Array, default: []')
|
213
242
|
|
214
|
-
#
|
215
|
-
|
216
|
-
if oids[dc_name].present? && oids[dc_name]['hqmf_oid'].present?
|
217
|
-
contents.gsub!(/ field :hqmfOid, type: String\n/, " field :hqmfOid, type: String, default: '#{oids[dc_name]['hqmf_oid']}'\n")
|
218
|
-
else
|
219
|
-
contents.gsub!(/ field :hqmfOid, type: String\n/, '') # Don't include this field
|
220
|
-
end
|
221
|
-
|
222
|
-
# Add QRDA oid (if it exists in the given QRDA oid mapping file)
|
223
|
-
if oids[dc_name].present? && oids[dc_name]['qrda_oid'].present?
|
224
|
-
contents.gsub!(/ field :qrdaOid, type: String\n/, " field :qrdaOid, type: String, default: '#{oids[dc_name]['qrda_oid']}'\n")
|
225
|
-
else
|
226
|
-
contents.gsub!(/ field :qrdaOid, type: String\n/, '') # Don't include this field
|
227
|
-
end
|
228
|
-
|
229
|
-
# Add category
|
230
|
-
if oids[dc_name].present? && oids[dc_name]['qdm_category'].present?
|
231
|
-
contents.gsub!(/ field :qdmCategory, type: String\n/, " field :qdmCategory, type: String, default: '#{oids[dc_name]['qdm_category']}'\n")
|
232
|
-
else
|
233
|
-
contents.gsub!(/ field :qdmCategory, type: String\n/, '') # Don't include this field
|
234
|
-
end
|
235
|
-
|
236
|
-
# Add status
|
237
|
-
if oids[dc_name].present? && oids[dc_name]['qdm_status'].present?
|
238
|
-
contents.gsub!(/ field :qdmStatus, type: String\n/, " field :qdmStatus, type: String, default: '#{oids[dc_name]['qdm_status']}'\n")
|
239
|
-
else
|
240
|
-
contents.gsub!(/ field :qdmStatus, type: String\n/, '') # Don't include this field
|
241
|
-
end
|
243
|
+
# Make facilityLocation of type QDM::FacilityLocation
|
244
|
+
contents.gsub!(/field :facilityLocation, type: Code/, 'field :facilityLocation, type: QDM::FacilityLocation')
|
242
245
|
|
243
246
|
# Make relatedTo embeds_many instead of field
|
244
247
|
contents.gsub!(/ field :relatedTo, type: Array\n/, " embeds_many :relatedTo, class_name: 'QDM::Id'\n")
|
245
248
|
|
249
|
+
# Make prescriberId embeds_many instead of field
|
250
|
+
contents.gsub!(/ field :prescriberId, type: Id\n/, " embeds_one :prescriberId, class_name: 'QDM::Id'\n")
|
251
|
+
|
252
|
+
# Make dispenserId embeds_many instead of field
|
253
|
+
contents.gsub!(/ field :dispenserId, type: Id\n/, " embeds_one :dispenserId, class_name: 'QDM::Id'\n")
|
254
|
+
|
246
255
|
File.open(file_name, 'w') { |file| file.puts contents }
|
247
256
|
end
|
248
257
|
|
@@ -255,41 +264,6 @@ files = Dir.glob(js_models_path + '*.js').each do |file_name|
|
|
255
264
|
# Replace 'Any' type placeholder (these attributes could point to anything).
|
256
265
|
contents.gsub!(/: Any/, ': Any')
|
257
266
|
|
258
|
-
# Add QDM version
|
259
|
-
contents.gsub!(/qdmVersion: String/, "qdmVersion: { type: String, default: '#{qdm_version}' }")
|
260
|
-
|
261
|
-
# Add HQMF oid (if it exists in the given HQMF oid mapping file)
|
262
|
-
dc_name = File.basename(file_name.underscore, '.*')
|
263
|
-
if oids[dc_name].present? && oids[dc_name]['hqmf_oid'].present?
|
264
|
-
contents.gsub!(/ hqmfOid: String,\n/, " hqmfOid: { type: String, default: '#{oids[dc_name]['hqmf_oid']}' },\n")
|
265
|
-
else
|
266
|
-
contents.gsub!(/ hqmfOid: String,\n/, '') # Don't include this field
|
267
|
-
end
|
268
|
-
|
269
|
-
# Add QRDA oid (if it exists in the given QRDA oid mapping file)
|
270
|
-
if oids[dc_name].present? && oids[dc_name]['qrda_oid'].present?
|
271
|
-
contents.gsub!(/ qrdaOid: String,\n/, " qrdaOid: { type: String, default: '#{oids[dc_name]['qrda_oid']}' },\n")
|
272
|
-
else
|
273
|
-
contents.gsub!(/ qrdaOid: String,\n/, '') # Don't include this field
|
274
|
-
end
|
275
|
-
|
276
|
-
# Add category
|
277
|
-
if oids[dc_name].present? && oids[dc_name]['qdm_category'].present?
|
278
|
-
contents.gsub!(/ qdmCategory: String,\n/, " qdmCategory: { type: String, default: '#{oids[dc_name]['qdm_category']}' },\n")
|
279
|
-
else
|
280
|
-
contents.gsub!(/ qdmCategory: String,\n/, '') # Don't include this field
|
281
|
-
end
|
282
|
-
|
283
|
-
# Add status
|
284
|
-
if oids[dc_name].present? && oids[dc_name]['qdm_status'].present?
|
285
|
-
contents.gsub!(/ qdmStatus: String,\n/, " qdmStatus: { type: String, default: '#{oids[dc_name]['qdm_status']}' },\n")
|
286
|
-
else
|
287
|
-
contents.gsub!(/ qdmStatus: String,\n/, '') # Don't include this field
|
288
|
-
end
|
289
|
-
|
290
|
-
# Add class
|
291
|
-
contents.gsub!(/ _type: String,\n/, " _type: { type: String, default: '#{dc_name.camelize}' },\n")
|
292
|
-
|
293
267
|
# Component, Facility, and Id types
|
294
268
|
contents.gsub!(/facilityLocations: \[\]/, 'facilityLocations: [FacilityLocationSchema]')
|
295
269
|
contents.gsub!(/facilityLocation: Code/, 'facilityLocation: FacilityLocationSchema')
|
@@ -314,6 +288,7 @@ Dir.glob(ruby_models_path + '*.rb').each do |file_name|
|
|
314
288
|
contents = File.read(file_name)
|
315
289
|
contents.gsub!('Qdm', 'QDM')
|
316
290
|
contents.gsub!('Code', 'QDM::Code')
|
291
|
+
contents.gsub!(' Id', ' QDM::Id')
|
317
292
|
contents.gsub!('Interval', 'QDM::Interval')
|
318
293
|
contents.gsub!('Quantity', 'QDM::Quantity')
|
319
294
|
File.open(file_name, 'w') { |file| file.puts contents }
|
@@ -337,7 +312,8 @@ end
|
|
337
312
|
Dir.glob(ruby_models_path + '*.rb').each do |file_name|
|
338
313
|
contents = ''
|
339
314
|
File.open(file_name).each_line.with_index do |line, index|
|
340
|
-
line.gsub!("\n", " < DataElement\n") if index.zero? && !file_name.include?('/patient.rb') && !file_name.include?('/id.rb')
|
315
|
+
line.gsub!("\n", " < DataElement\n") if index.zero? && !file_name.include?('/patient.rb') && !file_name.include?('/id.rb') && !file_name.include?('/component.rb') && !file_name.include?('/facility_location.rb')
|
316
|
+
line.gsub!("\n", " < Attribute\n") if index.zero? && (file_name.include?('/component.rb') || file_name.include?('/facility_location.rb'))
|
341
317
|
contents += "module QDM\n # #{file_name}\n #{line.gsub('QDM::', '')}" if index.zero?
|
342
318
|
contents += ' ' unless index.zero? || line.blank?
|
343
319
|
contents += line unless index.zero?
|
@@ -346,4 +322,24 @@ Dir.glob(ruby_models_path + '*.rb').each do |file_name|
|
|
346
322
|
File.open(file_name, 'w') { |file| file.puts contents }
|
347
323
|
end
|
348
324
|
|
325
|
+
puts 'Moving Attribute Schemas To Their Own Directory'
|
326
|
+
# Create ruby attributes directory if it doesn't exist, directory won't exist in test mode
|
327
|
+
if IS_TEST
|
328
|
+
Dir.mkdir(ruby_models_path + 'attributes')
|
329
|
+
Dir.mkdir(js_models_path + 'attributes')
|
330
|
+
end
|
331
|
+
if File.exist?(ruby_models_path + 'facility_location.rb')
|
332
|
+
File.rename ruby_models_path + 'facility_location.rb', ruby_models_path + 'attributes/facility_location.rb'
|
333
|
+
File.rename js_models_path + 'FacilityLocation.js', js_models_path + 'attributes/FacilityLocation.js'
|
334
|
+
end
|
335
|
+
if File.exist?(ruby_models_path + 'component.rb')
|
336
|
+
File.rename ruby_models_path + 'component.rb', ruby_models_path + 'attributes/component.rb'
|
337
|
+
File.rename js_models_path + 'Component.js', js_models_path + 'attributes/Component.js'
|
338
|
+
end
|
339
|
+
|
340
|
+
puts 'Create hqmfOid to datatype map as json file'
|
341
|
+
f = File.open('app/models/hqmfOid_to_datatype_map.json', 'w')
|
342
|
+
f.write(JSON.pretty_generate(hqmfOid_to_datatype_map))
|
343
|
+
f.close
|
344
|
+
|
349
345
|
puts 'Done.'
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require('nokogiri')
|
2
|
+
require_relative '../app/models/models'
|
3
|
+
require('generate_types')
|
4
|
+
|
5
|
+
module QDM
|
6
|
+
# PatientGeneration module can be used to generate patients with dataElements that are populated
|
7
|
+
# with every possible attribute that said type supports
|
8
|
+
module PatientGeneration
|
9
|
+
# Generates patient(s) with fully-loaded dataElements if new_patient_for_each_type is false then a
|
10
|
+
# single patient will be returned that has every data element on it
|
11
|
+
def self.generate_exhastive_data_element_patients(new_patient_for_each_type = true, model_info_file = 'qdm-modelinfo-5.4.xml')
|
12
|
+
datatypes = get_datatypes(File.join(File.dirname(__FILE__), "../modelinfo/#{model_info_file}"))
|
13
|
+
patients = []
|
14
|
+
cqm_patient = nil
|
15
|
+
qdm_patient = nil
|
16
|
+
datatypes.each do |type|
|
17
|
+
next if type.include? 'PatientCharacteristic'
|
18
|
+
# 1 Patient Per data element type containing negated and non-negated type (if negatable)
|
19
|
+
if cqm_patient.nil? || qdm_patient.nil? || new_patient_for_each_type
|
20
|
+
cqm_patient = QDM::BaseTypeGeneration.generate_cqm_patient(type)
|
21
|
+
qdm_patient = QDM::BaseTypeGeneration.generate_qdm_patient
|
22
|
+
# Add patient characteristics
|
23
|
+
sex = generate_loaded_datatype('QDM::PatientCharacteristicSex')
|
24
|
+
race = generate_loaded_datatype('QDM::PatientCharacteristicRace')
|
25
|
+
ethnicity = generate_loaded_datatype('QDM::PatientCharacteristicEthnicity')
|
26
|
+
birthdate = generate_loaded_datatype('QDM::PatientCharacteristicBirthdate')
|
27
|
+
qdm_patient.dataElements.push(sex)
|
28
|
+
qdm_patient.dataElements.push(race)
|
29
|
+
qdm_patient.dataElements.push(ethnicity)
|
30
|
+
qdm_patient.dataElements.push(birthdate)
|
31
|
+
cqm_patient.qdmPatient = qdm_patient
|
32
|
+
end
|
33
|
+
data_element = generate_loaded_datatype(type)
|
34
|
+
qdm_patient.dataElements.push(data_element)
|
35
|
+
# if type is negatable, add a negated version to the patient
|
36
|
+
if data_element.fields.keys.include? 'negationRationale'
|
37
|
+
negated_data_element = generate_loaded_datatype(type, true)
|
38
|
+
qdm_patient.dataElements.push(negated_data_element)
|
39
|
+
end
|
40
|
+
patients.push(cqm_patient)
|
41
|
+
end
|
42
|
+
patients
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.get_datatypes(modelinfo_file)
|
46
|
+
modelinfo = File.open(modelinfo_file) { |f| Nokogiri::XML(f) }
|
47
|
+
datatypes = []
|
48
|
+
# Loop through each typeInfo node (each of these is a QDM datatype)
|
49
|
+
modelinfo.xpath('//ns4:typeInfo').each do |type|
|
50
|
+
# Grab the name of this QDM datatype
|
51
|
+
datatype_name = type.attributes['name'].value.split('.').last
|
52
|
+
exclusion_array = %w[Component Id Patient ResultComponent FacilityLocation]
|
53
|
+
# Store datatype and its attributes (reject irrelevant datatypes)
|
54
|
+
next if datatype_name.include?('Negative') || datatype_name.include?('Positive') ||
|
55
|
+
datatype_name.include?('QDMBaseType') || (exclusion_array.include? datatype_name)
|
56
|
+
datatypes.push(datatype_name)
|
57
|
+
end
|
58
|
+
datatypes
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.generate_loaded_datatype(data_element_type, negate_data_element = false)
|
62
|
+
data_element = QDM.const_get(data_element_type).new
|
63
|
+
fields = data_element.typed_attributes.keys
|
64
|
+
fields.each do |field_name|
|
65
|
+
# Ignore these fields, they are used by mongo
|
66
|
+
next if %w[_id _type].include? field_name
|
67
|
+
if !data_element[field_name] || data_element[field_name] == []
|
68
|
+
populate_fields(field_name, data_element, negate_data_element)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
data_element
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.populate_fields(field_name, data_element, negate_data_element)
|
75
|
+
# There are certain fields that we want to populate manually
|
76
|
+
if field_name == 'description'
|
77
|
+
# Skip the setting of patient description for now
|
78
|
+
elsif field_name == 'codeListId'
|
79
|
+
# Skip the setting code list id. It is not used in a patient's data_element, only in the measure's
|
80
|
+
elsif field_name == 'negationRationale'
|
81
|
+
data_element[field_name] = QDM::BaseTypeGeneration.generate_code_field if negate_data_element
|
82
|
+
elsif field_name == 'components'
|
83
|
+
data_element[field_name] = [QDM::BaseTypeGeneration.generate_component]
|
84
|
+
elsif field_name == 'result'
|
85
|
+
# TODO: Result can be MANY Integer, Decimal, Code, Quantity or Ratio randomize this
|
86
|
+
data_element[field_name] = QDM::BaseTypeGeneration.generate_code_field
|
87
|
+
elsif %w[diagnoses dataElementCodes].include? field_name
|
88
|
+
# TODO: Populate dataElementCodes with codes specifically for data element type
|
89
|
+
data_element[field_name] = [QDM::BaseTypeGeneration.generate_code_field]
|
90
|
+
elsif field_name == 'facilityLocations'
|
91
|
+
# TODO: Randomize number of facility locations added
|
92
|
+
data_element[field_name] = [QDM::BaseTypeGeneration.generate_facility_location]
|
93
|
+
elsif field_name == 'facilityLocation'
|
94
|
+
# TODO: Randomize number of facility locations added
|
95
|
+
data_element[field_name] = QDM::BaseTypeGeneration.generate_facility_location
|
96
|
+
elsif field_name == 'targetOutcome'
|
97
|
+
# TODO: Randomize type of targetOutcome, use code for now
|
98
|
+
data_element[field_name] = QDM::BaseTypeGeneration.generate_code_field
|
99
|
+
else
|
100
|
+
# Fallback to populating fields by expected type
|
101
|
+
populate_field_by_type(field_name, data_element)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.populate_field_by_type(field_name, data_element)
|
106
|
+
field_type = data_element.fields[field_name].type
|
107
|
+
if field_type == QDM::Code
|
108
|
+
data_element[field_name] = QDM::BaseTypeGeneration.generate_code_field
|
109
|
+
elsif field_type == DateTime
|
110
|
+
data_element[field_name] = QDM::BaseTypeGeneration.generate_datetime
|
111
|
+
elsif field_type == QDM::Interval
|
112
|
+
data_element[field_name] = QDM::BaseTypeGeneration.generate_date_interval_field
|
113
|
+
elsif field_type == QDM::Quantity
|
114
|
+
data_element[field_name] = QDM::BaseTypeGeneration.generate_quantity
|
115
|
+
elsif field_type == QDM::Id
|
116
|
+
data_element[field_name] = QDM::BaseTypeGeneration.generate_qdm_id
|
117
|
+
elsif field_type == Integer
|
118
|
+
# TODO: randomize value
|
119
|
+
data_element[field_name] = 3
|
120
|
+
else
|
121
|
+
puts("Unknown Type in patient generator for field #{field_name}, type #{field_type} in #{data_element._type}")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require_relative '../app/models/models'
|
2
|
+
|
3
|
+
module QDM
|
4
|
+
# BaseTypeGeneration contains functions to randomly generate basetypes used by PatientGeneration
|
5
|
+
module BaseTypeGeneration
|
6
|
+
def self.generate_component
|
7
|
+
component = QDM::Component.new
|
8
|
+
component.code = generate_code_field
|
9
|
+
# TODO: Randomize the type of the result
|
10
|
+
component.result = generate_code_field
|
11
|
+
component
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.generate_facility_location
|
15
|
+
facility_location = QDM::FacilityLocation.new
|
16
|
+
facility_location.code = generate_code_field
|
17
|
+
facility_location.locationPeriod = generate_date_interval_field
|
18
|
+
facility_location
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.generate_code_field
|
22
|
+
# TODO: use code with all parameters, possibly randomize parameter values and optional information
|
23
|
+
QDM::Code.new('1234', 'SNOMEDCT')
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.generate_datetime
|
27
|
+
# TODO: Randomize dateTime
|
28
|
+
DateTime.new(2019)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.generate_quantity
|
32
|
+
# TODO: Randomize value and parameters for Quantity
|
33
|
+
QDM::Quantity.new(100, 'mg')
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.generate_date_interval_field
|
37
|
+
# TODO: Randomize dates in interval and open/closed parameters
|
38
|
+
QDM::Interval.new(DateTime.new(2018), DateTime.new(2019))
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.generate_cqm_patient(type)
|
42
|
+
cqm_patient = CQM::Patient.new
|
43
|
+
cqm_patient.givenNames = [type]
|
44
|
+
cqm_patient.familyName = "#{type} Test Patient"
|
45
|
+
cqm_patient
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.generate_qdm_patient
|
49
|
+
qdm_patient = QDM::Patient.new
|
50
|
+
qdm_patient.extendedData = {}
|
51
|
+
qdm_patient.extendedData['medical_record_number'] = '1'
|
52
|
+
qdm_patient.birthDatetime = generate_datetime
|
53
|
+
qdm_patient
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.generate_qdm_id
|
57
|
+
# TODO: randomize values
|
58
|
+
qdm_id = QDM::Id.new
|
59
|
+
qdm_id.value = 'TestValue'
|
60
|
+
qdm_id.namingSystem = 'TestNamingSystem'
|
61
|
+
qdm_id
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|