CLOSER-Cohorts/archivist

View on GitHub
lib/importers/xml/ddi/question.rb

Summary

Maintainability
B
6 hrs
Test Coverage
module Importers::XML::DDI
  class Question < DdiImporterBase
    RESPONSE_DOMAIN_XPATH = './CodeDomain|./NumericDomain|./TextDomain|./DateTimeDomain'
    STRUCTURED_RESPONSE_DOMAIN_XPATH = './StructuredMixedResponseDomain/ResponseDomainInMixed/CodeDomain|./StructuredMixedResponseDomain/ResponseDomainInMixed/NumericDomain|./StructuredMixedResponseDomain/ResponseDomainInMixed/TextDomain|./StructuredMixedResponseDomain/ResponseDomainInMixed/DateTimeDomain'

    def initialize(instrument)
      @instrument = instrument
    end

    def question_grid_node(node)
      question = ::QuestionGrid.new(
          {
              urn: node.at_xpath('./URN').content,            
              label: node.at_xpath('./QuestionGridName/String').content,
              literal: node.at_xpath('./QuestionText/LiteralText/Text')&.content.to_s
          }
      )
      
      question.horizontal_code_list = ::CodeList.find_by_identifier(
          'urn',
          extract_urn_identifier(node.at_xpath("./GridDimension[@rank='2']/CodeDomain/CodeListReference"))
      )
      question.vertical_code_list = ::CodeList.find_by_identifier(
          'urn',
          extract_urn_identifier(node.at_xpath("./GridDimension[@rank='1']/CodeDomain/CodeListReference"))
      )
      roster = node.at_xpath("./GridDimension[@rank='1']/Roster")
      unless roster.nil?
        question.roster_label = roster.at_xpath('./Label/Content').content
        question.roster_rows = roster.attribute('minimumRequired').value.nil? ? 0 : roster.attribute('minimumRequired').value.to_i
      end

      @instrument.question_grids << question

      corner = node.at_xpath("./GridDimension[@displayLabel='true']")
      question.corner_label = corner.attribute('rank').value.to_i == 1 ? 'V' : 'H' unless corner.nil?

      # Response domain work
      structured_mixed_grid_rd_node = node.at_xpath('./StructuredMixedGridResponseDomain')
      unless structured_mixed_grid_rd_node.nil?
        structured_mixed_grid_rd_node.xpath('./GridResponseDomain').each do |grd_node|
          response_domain = read_response_domain(grd_node.at_xpath(RESPONSE_DOMAIN_XPATH))
          next if response_domain.nil?

          rank_node = grd_node.xpath("./GridAttachment/CellCoordinatesAsDefined/SelectDimension[@rank='2']")
          ::RdsQs.create({
            question: question,
            response_domain: response_domain,
            code_id: rank_node.attribute('specificValue').value
          })
        end
      else
        rd_node = node.at_xpath(RESPONSE_DOMAIN_XPATH)
        unless rd_node.nil?
      response_domain = read_response_domain(rd_node)
      unless response_domain.nil?
          ::RdsQs.create(
        {
               question: question,
               response_domain: response_domain,
               rd_order: 1
        }
      )
      end
      end
    end
      question
    end

    def question_item_node(node)
      question = ::QuestionItem.new(
          {
              urn: node.at_xpath('./URN').content,
              label: node.at_xpath('./QuestionItemName/String').content,
              literal: node.at_xpath('./QuestionText/LiteralText/Text')&.content.to_s
          }
      )
      @instrument.question_items << question

      node.xpath(RESPONSE_DOMAIN_XPATH + '|' + STRUCTURED_RESPONSE_DOMAIN_XPATH).each_with_index do |rd, i|
        response_domain = read_response_domain(rd)
        next if response_domain.nil?
        ::RdsQs.create(
            {
                         question: question,
                         response_domain: response_domain,
                         rd_order: i + 1
            }
        )
      end
      return question
    end

    def read_response_domain(node)
      if node.name == 'CodeDomain'
      cl = ::CodeList.find_by_identifier(
        'urn',
        extract_urn_identifier(node.at_xpath('./CodeListReference'))
      )
      response_cardinality = node.at_xpath('./ResponseCardinality')
      if response_cardinality && cl.response_domain.nil?
        min_responses = response_cardinality['minimumResponses']
        max_responses = response_cardinality['maximumResponses']
        cl.create_response_domain_code(min_responses: min_responses, max_responses: max_responses)
      end
      response_domain = cl.response_domain
    else
      if node.name == 'NumericDomain'
      response_domain = read_rdn_node node
      elsif node.name == 'TextDomain'
      response_domain = read_rdt_node node
      elsif node.name == 'DateTimeDomain'
      response_domain = read_rdd_node node
      else
      Rails.logger.warn 'ResponseDomain not supported'
      return nil
      end
    end
    return response_domain
    end

    def read_rdd_node(node)
      label = node.at_xpath('Label/Content')&.content
      response_domain = @instrument.response_domain_datetimes.find_by_label label
      if response_domain.nil?
        response_domain = @instrument.response_domain_datetimes.create(
             {
                 label: label,
                 datetime_type: node.at_xpath('DateTypeCode').content,
                 format: node.at_xpath('DateFieldFormat')&.content
             }
        )
      end
      return response_domain
    end

    def read_rdn_node(node)
      label = node.at_xpath('./Label/Content')&.content
      response_domain = @instrument.response_domain_numerics.find_by_label label
      if response_domain.nil?
        response_domain = @instrument.response_domain_numerics.create(
            {
                label: label,
                numeric_type: node.at_xpath('NumericTypeCode').content,
                min: node.at_xpath('NumberRange/Low')&.content,
                max: node.at_xpath('NumberRange/High')&.content
            }
        )
      end
      return response_domain
    end

    def read_rdt_node(node)
      label = node.at_xpath('Label/Content')&.content
      if label.nil?
        if node['maxLength'].nil?
          label = 'MISSING LABEL'
        else
          label = 'max:' + node['maxLength']
        end
      end
      response_domain = @instrument.response_domain_texts.find_by_label label
      if response_domain.nil?
        response_domain = @instrument.response_domain_texts.create ({
            label: label,
            maxlen: node['maxLength']
        })
      end
      return response_domain
    end

    def XML_node(node)
      if node.name == 'QuestionItem'
        question = question_item_node node
      elsif node.name == 'QuestionGrid'
        question = question_grid_node node
      else
        Rails.logger.debug 'Question not recogonised: ' + node.name
        return
      end

      unless node.at_xpath('./InterviewerInstructionReference').nil?
        question.instruction_id = ::Instruction.find_by_identifier(
            'urn',
            extract_urn_identifier(node.at_xpath('./InterviewerInstructionReference'))
        ).try(:id)
      end
      begin
        question.save!
      rescue ActiveRecord::RecordInvalid
        raise Importers::XML::DDI::ValidationError.new(node.to_xml, question)
      end
      question.add_urn extract_urn_identifier node
    end

    def XML_scheme(scheme)
      scheme.xpath('./QuestionItem | ./QuestionGrid').each do |question_node|
        XML_node question_node
      end
    end
  end
end