lib/generate_models.rb in cqm-models-1.0.3 vs lib/generate_models.rb in cqm-models-1.1.1.0

- old
+ new

@@ -1,12 +1,12 @@ #! /usr/bin/env ruby - require 'nokogiri' require 'active_support/all' require 'rails/generators' require 'erb' require 'json' +require_relative './generators/custom_mongo/model_generator' ############################################################################### # Helpers ############################################################################### @@ -80,14 +80,19 @@ qdm_version = modelinfo.xpath('//ns4:modelInfo').first.attributes['version'].value # Datatypes (keys are the datatype name, values are the datatype attributes) datatypes = {} +# We will export a hqmfOid to datatype map +hqmfOid_to_datatype_map = {} + # Loop through each typeInfo node (each of these is a QDM datatype) modelinfo.xpath('//ns4:typeInfo').each do |type| # Grab the name of this QDM datatype datatype_name = type.attributes['name'].value.split('.').last + # Reject irrelevant datatypes + next if datatype_name.include?('Negative') || datatype_name.include?('Positive') || datatype_name.include?('QDMBaseType') # Grab the QDM attributes for this datatype attributes = [] type.xpath('./ns4:element').each do |attribute| # Grab the name of this QDM datatype attribute @@ -104,92 +109,119 @@ # Store name and type attributes << { name: attribute_name, type: attribute_type } end - # Store datatype and its attributes (reject irrelevant datatypes) - next if datatype_name.include?('Negative') || datatype_name.include?('Positive') || datatype_name.include?('QDMBaseType') - datatypes[datatype_name] = attributes + # Add the label as qdmTitle + qdm_title = type['label'] + if qdm_title.nil? # If there's no label, check if there is a "positive" profile + positive_profile = modelinfo.at_xpath("/ns4:modelInfo/ns4:typeInfo[@xsi:type='ns4:ProfileInfo'][@identifier='Positive#{datatype_name}']") + qdm_title = positive_profile['label'] unless positive_profile.nil? + end + attributes << { name: 'qdmTitle', type: 'System.String', default: qdm_title } unless qdm_title.nil? + + # Add the extra info that is manually maintained in the "oids" file + extra_info = oids[datatype_name.underscore] + if extra_info.present? + attributes << { name: 'hqmfOid', type: 'System.String', default: extra_info['hqmf_oid'] } if extra_info['hqmf_oid'].present? + attributes << { name: 'qrdaOid', type: 'System.String', default: extra_info['qrda_oid'] } if extra_info['qrda_oid'].present? + attributes << { name: 'qdmCategory', type: 'System.String', default: extra_info['qdm_category'] } if extra_info['qdm_category'].present? + attributes << { name: 'qdmStatus', type: 'System.String', default: extra_info['qdm_status'] } if extra_info['qdm_status'].present? + hqmfOid_to_datatype_map[extra_info['hqmf_oid']] = datatype_name if extra_info['hqmf_oid'].present? + end + + attributes << { name: 'qdmVersion', type: 'System.String', default: qdm_version } + + datatypes[datatype_name] = { attributes: attributes } end ############################################################################### # Start of model generation ############################################################################### puts 'Generating Ruby models...' # Do a quick sanity check on attribute types -datatypes.each do |datatype, attributes| - attributes.each do |attribute| +datatypes.each do |datatype, info| + info[:attributes].each do |attribute| raise 'Unsupported type from modelinfo file for Ruby types: ' + attribute[:type] + 'from: ' + datatype if TYPE_LOOKUP_RB[attribute[:type]].blank? raise 'Unsupported type from modelinfo file for JavaScript types: ' + attribute[:type] + 'from: ' + datatype if TYPE_LOOKUP_JS[attribute[:type]].blank? end end # Create Ruby models -extra_fields_rb = [ - 'hqmfOid:String', - 'qrdaOid:String', - 'qdmCategory:String', - 'qdmStatus:String', - 'qdmVersion:String' -] base_module = 'QDM::' base_module = 'Test::QDM::' if IS_TEST -datatypes.each do |datatype, attributes| - Rails::Generators.invoke('mongoid:model', [base_module + datatype] + attributes.collect { |attribute| attribute[:name] + ':' + TYPE_LOOKUP_RB[attribute[:type]] } + extra_fields_rb) +datatypes.each do |datatype, info| + name = base_module + datatype + attributes = info[:attributes].map do |attribute| + attribute = attribute.dup + attribute[:type] = TYPE_LOOKUP_RB[attribute[:type]] + attribute + end + generator_args = [name, attributes] + Rails::Generators.invoke('custom_mongo:model', generator_args) end # Create require file (if not in test mode) unless IS_TEST model_template = File.read('templates/models_template.rb.erb') renderer = ERB.new(model_template, nil, '-') file_path = 'app/models/models.rb' File.open(file_path, 'w') { |file| file.puts renderer.result(binding) } end +# Javascript PatientSchema for was renamed to QDMPatient since it just contains the QDM data +if datatypes['Patient'] + datatypes['QDMPatient'] = datatypes['Patient'] + datatypes.delete('Patient') +end + puts 'Generating JavaScript models...' # Create JavaScript models template = File.read('templates/mongoose_template.js.erb') default_renderer = ERB.new(template, nil, '-') file_path = 'app/assets/javascripts/' file_path = 'tmp/' if IS_TEST -extra_fields_js = [ - { name: 'hqmfOid', type: 'System.String' }, - { name: 'qrdaOid', type: 'System.String' }, - { name: 'qdmCategory', type: 'System.String' }, - { name: 'qdmStatus', type: 'System.String' }, - { name: 'qdmVersion', type: 'System.String' }, - { name: '_type', type: 'System.String' } -] datatype_custom_templates = { - Patient: 'templates/patient_template.js.erb', + QDMPatient: 'templates/patient_template.js.erb', Id: 'templates/id_template.js.erb' } -datatypes.each do |datatype, attributes| + +datatypes.each do |datatype, info| renderer = default_renderer if datatype_custom_templates.key?(datatype.to_sym) puts "using custom template for #{datatype}" renderer = ERB.new(File.read(datatype_custom_templates[datatype.to_sym]), nil, '-') end - attrs_with_extras = attributes + extra_fields_js # this field gets used in the template + attrs_with_extras = info[:attributes] # this field gets used in the template + # Custom datatypes don't need _type + unless datatype_custom_templates.key?(datatype.to_sym) + attrs_with_extras << { name: '_type', type: 'System.String', default: "QDM::#{datatype.underscore.camelize}" } # Add Class + end puts ' ' + file_path + datatype + '.js' File.open(file_path + datatype + '.js', 'w') { |file| file.puts renderer.result(binding) } end # Create require file (if not in test mode) unless IS_TEST indtemplate = File.read('templates/index_template.js.erb') renderer = ERB.new(indtemplate, nil, '-') file_path = 'app/assets/javascripts/index.js' + puts ' ' + file_path File.open(file_path, 'w') { |file| file.puts renderer.result(binding) } alltemplate = File.read('templates/all_data_elements_template.js.erb') renderer = ERB.new(alltemplate, nil, '-') file_path = 'app/assets/javascripts/AllDataElements.js' + puts ' ' + file_path File.open(file_path, 'w') { |file| file.puts renderer.result(binding) } + contents = File.read(file_path) + contents.gsub!(%r{\/FacilityLocation.js}, '/attributes/FacilityLocation.js') + contents.gsub!(%r{\/Component.js}, '/attributes/Component.js') + File.open(file_path, 'w') { |file| file.puts contents } end ############################################################################### # Model post processing ############################################################################### @@ -203,48 +235,25 @@ contents = File.read(file_name) # Cut out the 'Any' type placeholder (these attributes could point to anything). contents.gsub!(/, type: Any/, '') - # Add QDM version - contents.gsub!(/field :qdmVersion, type: String/, "field :qdmVersion, type: String, default: '#{qdm_version}'") - # Add default to [] for facilityLocations contents.gsub!(/field :facilityLocations, type: Array/, 'field :facilityLocations, type: Array, default: []') - # Add HQMF oid (if it exists in the given HQMF oid mapping file) - dc_name = File.basename(file_name, '.*') - if oids[dc_name].present? && oids[dc_name]['hqmf_oid'].present? - contents.gsub!(/ field :hqmfOid, type: String\n/, " field :hqmfOid, type: String, default: '#{oids[dc_name]['hqmf_oid']}'\n") - else - contents.gsub!(/ field :hqmfOid, type: String\n/, '') # Don't include this field - end + # Make facilityLocation of type QDM::FacilityLocation + contents.gsub!(/field :facilityLocation, type: Code/, 'field :facilityLocation, type: QDM::FacilityLocation') - # Add QRDA oid (if it exists in the given QRDA oid mapping file) - if oids[dc_name].present? && oids[dc_name]['qrda_oid'].present? - contents.gsub!(/ field :qrdaOid, type: String\n/, " field :qrdaOid, type: String, default: '#{oids[dc_name]['qrda_oid']}'\n") - else - contents.gsub!(/ field :qrdaOid, type: String\n/, '') # Don't include this field - end - - # Add category - if oids[dc_name].present? && oids[dc_name]['qdm_category'].present? - contents.gsub!(/ field :qdmCategory, type: String\n/, " field :qdmCategory, type: String, default: '#{oids[dc_name]['qdm_category']}'\n") - else - contents.gsub!(/ field :qdmCategory, type: String\n/, '') # Don't include this field - end - - # Add status - if oids[dc_name].present? && oids[dc_name]['qdm_status'].present? - contents.gsub!(/ field :qdmStatus, type: String\n/, " field :qdmStatus, type: String, default: '#{oids[dc_name]['qdm_status']}'\n") - else - contents.gsub!(/ field :qdmStatus, type: String\n/, '') # Don't include this field - end - # Make relatedTo embeds_many instead of field contents.gsub!(/ field :relatedTo, type: Array\n/, " embeds_many :relatedTo, class_name: 'QDM::Id'\n") + # Make prescriberId embeds_many instead of field + contents.gsub!(/ field :prescriberId, type: Id\n/, " embeds_one :prescriberId, class_name: 'QDM::Id'\n") + + # Make dispenserId embeds_many instead of field + contents.gsub!(/ field :dispenserId, type: Id\n/, " embeds_one :dispenserId, class_name: 'QDM::Id'\n") + File.open(file_name, 'w') { |file| file.puts contents } end # JavaScript post processing js_models_path = 'app/assets/javascripts/' @@ -253,45 +262,10 @@ contents = File.read(file_name) # Replace 'Any' type placeholder (these attributes could point to anything). contents.gsub!(/: Any/, ': Any') - # Add QDM version - contents.gsub!(/qdmVersion: String/, "qdmVersion: { type: String, default: '#{qdm_version}' }") - - # Add HQMF oid (if it exists in the given HQMF oid mapping file) - dc_name = File.basename(file_name.underscore, '.*') - if oids[dc_name].present? && oids[dc_name]['hqmf_oid'].present? - contents.gsub!(/ hqmfOid: String,\n/, " hqmfOid: { type: String, default: '#{oids[dc_name]['hqmf_oid']}' },\n") - else - contents.gsub!(/ hqmfOid: String,\n/, '') # Don't include this field - end - - # Add QRDA oid (if it exists in the given QRDA oid mapping file) - if oids[dc_name].present? && oids[dc_name]['qrda_oid'].present? - contents.gsub!(/ qrdaOid: String,\n/, " qrdaOid: { type: String, default: '#{oids[dc_name]['qrda_oid']}' },\n") - else - contents.gsub!(/ qrdaOid: String,\n/, '') # Don't include this field - end - - # Add category - if oids[dc_name].present? && oids[dc_name]['qdm_category'].present? - contents.gsub!(/ qdmCategory: String,\n/, " qdmCategory: { type: String, default: '#{oids[dc_name]['qdm_category']}' },\n") - else - contents.gsub!(/ qdmCategory: String,\n/, '') # Don't include this field - end - - # Add status - if oids[dc_name].present? && oids[dc_name]['qdm_status'].present? - contents.gsub!(/ qdmStatus: String,\n/, " qdmStatus: { type: String, default: '#{oids[dc_name]['qdm_status']}' },\n") - else - contents.gsub!(/ qdmStatus: String,\n/, '') # Don't include this field - end - - # Add class - contents.gsub!(/ _type: String,\n/, " _type: { type: String, default: '#{dc_name.camelize}' },\n") - # Component, Facility, and Id types contents.gsub!(/facilityLocations: \[\]/, 'facilityLocations: [FacilityLocationSchema]') contents.gsub!(/facilityLocation: Code/, 'facilityLocation: FacilityLocationSchema') contents.gsub!(/components: \[\]/, 'components: [ComponentSchema]') contents.gsub!(/component: Code/, 'component: ComponentSchema') @@ -312,10 +286,11 @@ ruby_models_path = 'app/models/test/qdm/' if IS_TEST Dir.glob(ruby_models_path + '*.rb').each do |file_name| contents = File.read(file_name) contents.gsub!('Qdm', 'QDM') contents.gsub!('Code', 'QDM::Code') + contents.gsub!(' Id', ' QDM::Id') contents.gsub!('Interval', 'QDM::Interval') contents.gsub!('Quantity', 'QDM::Quantity') File.open(file_name, 'w') { |file| file.puts contents } end @@ -335,15 +310,36 @@ # Make sure Ruby datatypes models have the correct inheritance Dir.glob(ruby_models_path + '*.rb').each do |file_name| contents = '' File.open(file_name).each_line.with_index do |line, index| - line.gsub!("\n", " < DataElement\n") if index.zero? && !file_name.include?('/patient.rb') && !file_name.include?('/id.rb') + 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') + line.gsub!("\n", " < Attribute\n") if index.zero? && (file_name.include?('/component.rb') || file_name.include?('/facility_location.rb')) contents += "module QDM\n # #{file_name}\n #{line.gsub('QDM::', '')}" if index.zero? contents += ' ' unless index.zero? || line.blank? contents += line unless index.zero? end contents += 'end' File.open(file_name, 'w') { |file| file.puts contents } end + +puts 'Moving Attribute Schemas To Their Own Directory' +# Create ruby attributes directory if it doesn't exist, directory won't exist in test mode +if IS_TEST + Dir.mkdir(ruby_models_path + 'attributes') + Dir.mkdir(js_models_path + 'attributes') +end +if File.exist?(ruby_models_path + 'facility_location.rb') + File.rename ruby_models_path + 'facility_location.rb', ruby_models_path + 'attributes/facility_location.rb' + File.rename js_models_path + 'FacilityLocation.js', js_models_path + 'attributes/FacilityLocation.js' +end +if File.exist?(ruby_models_path + 'component.rb') + File.rename ruby_models_path + 'component.rb', ruby_models_path + 'attributes/component.rb' + File.rename js_models_path + 'Component.js', js_models_path + 'attributes/Component.js' +end + +puts 'Create hqmfOid to datatype map as json file' +f = File.open('app/models/hqmfOid_to_datatype_map.json', 'w') +f.write(JSON.pretty_generate(hqmfOid_to_datatype_map)) +f.close puts 'Done.'