lib/generate_models.rb in cqm-models-2.0.1 vs lib/generate_models.rb in cqm-models-3.0.0

- old
+ new

@@ -1,53 +1,63 @@ -#! /usr/bin/env ruby +# TODO: May need to add Entity casting to Any if entities need to be handled +# TODO: HQMF OIDS - Specifically RelatedPerson + +# ! /usr/bin/env ruby require 'nokogiri' require 'active_support/all' require 'rails/generators' require 'erb' require 'json' require_relative './generators/custom_mongo/model_generator' +require_relative './generator_helpers' ############################################################################### # Helpers ############################################################################### # Lookups for modelinfo 'element types' to Ruby+Mongoid types. TYPE_LOOKUP_RB = { 'System.DateTime': 'DateTime', + 'System.Date': 'Date', 'System.Integer': 'Integer', 'System.Quantity': 'Quantity', 'System.Code': 'Code', - 'QDM.Id': 'Id', + 'QDM.Identifier': 'Identifier', 'System.Any': 'Any', 'interval<System.DateTime>': 'Interval', 'interval<System.Quantity>': 'Interval', 'list<QDM.Component>': 'Array', 'System.String': 'String', 'list<QDM.Id>': 'Array', 'list<QDM.ResultComponent>': 'Array', 'list<QDM.FacilityLocation>': 'Array', + 'list<QDM.DiagnosisComponent>': 'Array', + 'list<System.String>': 'Array', 'list<System.Code>': 'Array', 'System.Decimal': 'Float', 'System.Time': 'Time', 'System.Concept': 'Any' }.stringify_keys! # Lookups for modelinfo 'element types' to JavaScript+Mongoose types. TYPE_LOOKUP_JS = { 'System.DateTime': 'DateTime', + 'System.Date': 'QDMDate', 'System.Integer': 'Number', 'System.Quantity': 'Quantity', 'System.Code': 'Code', 'System.Any': 'Any', - 'QDM.Id': 'IdSchema', + 'QDM.Identifier': 'IdentifierSchema', 'interval<System.DateTime>': 'Interval', 'interval<System.Quantity>': 'Interval', 'list<QDM.Component>': '[]', 'System.String': 'String', 'list<QDM.Id>': '[]', 'list<QDM.ResultComponent>': '[]', 'list<QDM.FacilityLocation>': '[]', + 'list<QDM.DiagnosisComponent>': '[]', + 'list<System.String>': '[]', 'list<System.Code>': '[Code]', 'System.Decimal': 'Number', 'System.Time': 'DateTime', 'System.Concept': '{}' }.stringify_keys! @@ -127,11 +137,14 @@ 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 } + # Add the qdmVersion attribute unless the base type is one that will provide it + unless ['QDM.Entity', 'QDM.Component'].include? type['baseType'] + attributes << { name: 'qdmVersion', type: 'System.String', default: qdm_version } + end datatypes[datatype_name] = { attributes: attributes } end ############################################################################### @@ -183,22 +196,22 @@ default_renderer = ERB.new(template, nil, '-') file_path = 'app/assets/javascripts/' file_path = 'tmp/' if IS_TEST datatype_custom_templates = { QDMPatient: 'templates/patient_template.js.erb', - Id: 'templates/id_template.js.erb' + Identifier: 'templates/identifier_template.js.erb' } 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 = info[:attributes] # this field gets used in the template - # Custom datatypes don't need _type - unless datatype_custom_templates.key?(datatype.to_sym) + # QDMPatients don't need _type + unless datatype.to_s == 'QDMPatient' 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 @@ -217,10 +230,18 @@ 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') + contents.gsub!(%r{\/CarePartner.js}, '/attributes/CarePartner.js') + contents.gsub!(%r{\/DiagnosisComponent.js}, '/attributes/DiagnosisComponent.js') + contents.gsub!(%r{\/Entity.js}, '/attributes/Entity.js') + contents.gsub!(%r{\/Organization.js}, '/attributes/Organization.js') + contents.gsub!(%r{\/PatientEntity.js}, '/attributes/PatientEntity.js') + contents.gsub!(%r{\/Practitioner.js}, '/attributes/Practitioner.js') + contents.gsub!(%r{\/ResultComponent.js}, '/attributes/ResultComponent.js') + contents.gsub!(%r{\/Identifier.js}, '/attributes/Identifier.js') File.open(file_path, 'w') { |file| file.puts contents } end ############################################################################### # Model post processing @@ -241,81 +262,99 @@ contents.gsub!(/field :facilityLocations, type: Array/, 'field :facilityLocations, type: Array, default: []') # Make facilityLocation of type QDM::FacilityLocation contents.gsub!(/field :facilityLocation, type: Code/, 'field :facilityLocation, type: QDM::FacilityLocation') - # Make relatedTo embeds_many instead of field - contents.gsub!(/ field :relatedTo, type: Array\n/, " embeds_many :relatedTo, class_name: 'QDM::Id'\n") + # Make Entity subclasses of type QDM::Entity + contents.gsub!(/field :participant/, "embeds_one :participant, class_name: 'QDM::Entity'") + contents.gsub!(/field :sender/, "embeds_one :sender, class_name: 'QDM::Entity'") + contents.gsub!(/field :recipient/, "embeds_one :recipient, class_name: 'QDM::Entity'") + contents.gsub!(/field :recorder/, "embeds_one :recorder, class_name: 'QDM::Entity'") + contents.gsub!(/field :performer/, "embeds_one :performer, class_name: 'QDM::Entity'") + contents.gsub!(/field :requester/, "embeds_one :requester, class_name: 'QDM::Entity'") + contents.gsub!(/field :prescriber/, "embeds_one :prescriber, class_name: 'QDM::Entity'") + contents.gsub!(/field :dispenser/, "embeds_one :dispenser, class_name: 'QDM::Entity'") + contents.gsub!(/field :identifier, type: Identifier/, "embeds_one :identifier, class_name: 'QDM::Identifier'") - # 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/' js_models_path = 'tmp/' if IS_TEST -files = Dir.glob(js_models_path + '*.js').each do |file_name| +Dir.glob(js_models_path + '*.js').each do |file_name| contents = File.read(file_name) # Replace 'Any' type placeholder (these attributes could point to anything). contents.gsub!(/: Any/, ': Any') - # Component, Facility, and Id types + # Component, Facility, Diagnoses contents.gsub!(/facilityLocations: \[\]/, 'facilityLocations: [FacilityLocationSchema]') contents.gsub!(/facilityLocation: Code/, 'facilityLocation: FacilityLocationSchema') contents.gsub!(/components: \[\]/, 'components: [ComponentSchema]') contents.gsub!(/component: Code/, 'component: ComponentSchema') - contents.gsub!(/relatedTo: \[\]/, 'relatedTo: [IdSchema]') + contents.gsub!(/diagnoses: \[\]/, 'diagnoses: [DiagnosisComponentSchema]') + contents.gsub!(/sender: Any/, 'sender: AnyEntity') + contents.gsub!(/recipient: Any/, 'recipient: AnyEntity') + contents.gsub!(/participant: Any/, 'participant: AnyEntity') + contents.gsub!(/recorder: Any/, 'recorder: AnyEntity') + contents.gsub!(/performer: Any/, 'performer: AnyEntity') + contents.gsub!(/requester: Any/, 'requester: AnyEntity') + contents.gsub!(/prescriber: Any/, 'prescriber: AnyEntity') + contents.gsub!(/dispenser: Any/, 'dispenser: AnyEntity') + contents.gsub!(/relatedTo: \[\]/, 'relatedTo: [String]') File.open(file_name, 'w') { |file| file.puts contents } end # Inject Ruby Patient model extensions -template = File.read('templates/patient_extension.rb.erb') -renderer = ERB.new(template, nil, '-') -rb_patient = File.read(ruby_models_path + 'patient.rb') -rb_patient.gsub!(/end/, renderer.result(binding)) -File.open(ruby_models_path + 'patient.rb', 'w') { |file| file.write(rb_patient) } +GeneratorHelpers.inject_extension('templates/patient_extension.rb.erb', ruby_models_path + 'patient.rb') +# Inject Ruby Entity model extensions +if datatypes['Entity'] + GeneratorHelpers.inject_extension('templates/entity_extension.rb.erb', ruby_models_path + 'entity.rb') +end # Make sure Ruby models are in the correct module ruby_models_path = 'app/models/qdm/' 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!('Date\n', 'QDM::Date\n') # \n so DateTime does not get overwritten + contents.gsub!(' Identifier', ' QDM::Identifier') contents.gsub!('Interval', 'QDM::Interval') contents.gsub!('Quantity', 'QDM::Quantity') File.open(file_name, 'w') { |file| file.puts contents } end +types_not_inherited_by_data_element = ['/patient.rb', '/identifier.rb', '/component.rb', '/facility_location.rb', '/entity.rb', '/organization.rb', '/patient_entity.rb', '/practitioner.rb', '/care_partner.rb', '/diagnosis_component.rb', '/result_component.rb'] +types_inherited_by_attribute = ['/component', '/facility_location', '/entity', '/diagnosis_component', '/identifier'] +types_inherited_by_entity = ['/patient_entity', '/care_partner', '/practitioner', '/organization'] +types_inherited_by_component = ['/result_component'] + # Set embedded in for datatypes Dir.glob(ruby_models_path + '*.rb').each do |file_name| contents = File.read(file_name) - # TODO: Might be able to make this list by finding baseType="System.Any" in model info file instead of hard-coding. - if File.basename(file_name) == 'id.rb' + if ['entity.rb', 'organization.rb', 'patient_entity.rb', 'practitioner.rb', 'care_partner.rb'].any? { |sub_string| sub_string.include?(File.basename(file_name)) } contents.gsub!(/ include Mongoid::Document\n/, " include Mongoid::Document\n embedded_in :data_element\n") - else - not_embedded_in_patient_files = ['patient.rb', 'component.rb', 'facility_location.rb'] - next if not_embedded_in_patient_files.include?(File.basename(file_name)) - contents.gsub!(/ include Mongoid::Document\n/, " include Mongoid::Document\n embedded_in :patient\n") + File.open(file_name, 'w') { |file| file.puts contents } end + # TODO: Might be able to make this list by finding baseType="System.Any" in model info file instead of hard-coding. + next if types_not_inherited_by_data_element.any? { |sub_string| sub_string.include?(File.basename(file_name)) } + contents.gsub!(/ include Mongoid::Document\n/, " include Mongoid::Document\n embedded_in :patient\n") File.open(file_name, 'w') { |file| file.puts contents } end # 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') && !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')) + line.gsub!("\n", " < DataElement\n") if index.zero? && types_not_inherited_by_data_element.none? { |sub_string| file_name.include?(sub_string) } + line.gsub!("\n", " < Attribute\n") if index.zero? && types_inherited_by_attribute.any? { |sub_string| file_name.include?(sub_string) } + line.gsub!("\n", " < Entity\n") if index.zero? && types_inherited_by_entity.any? { |sub_string| file_name.include?(sub_string) } + line.gsub!("\n", " < Component\n") if index.zero? && types_inherited_by_component.any? { |sub_string| file_name.include?(sub_string) } 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' @@ -326,16 +365,16 @@ # 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' + +attribute_list = types_inherited_by_attribute + types_inherited_by_component + types_inherited_by_entity +existing_types_moving_to_attributes = attribute_list.select { |sub_string| File.exist?(ruby_models_path + sub_string + '.rb') } +existing_types_moving_to_attributes.each do |type| + modified_name = type[1..-1] + File.rename ruby_models_path + modified_name + '.rb', ruby_models_path + 'attributes/' + modified_name + '.rb' + File.rename js_models_path + modified_name.camelize + '.js', js_models_path + 'attributes/' + modified_name.camelize + '.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))