govau/performance-dashboard

View on GitHub
lib/organisation_importer.rb

Summary

Maintainability
C
1 day
Test Coverage
require 'json-schema'

class OrganisationImporter
  def initialize(organisation, data_json, definition_json, force_id = nil)
    @organisation = organisation
    @data_json = data_json
    @definition_json = definition_json
    @force_id = force_id
  end

  def import!
    JSON::Validator.validate! data_schema, data
    JSON::Validator.validate! definition_schema, definition

    toggle_auditing false

    ActiveRecord::Base.transaction do
      display_hero = definition['displayHero'].nil?
      display_kpis = definition['displayKPIs'].nil?

      if @force_id.present?
        if existing_dashboard = @organisation.dashboards.find_by(id: @force_id)
          existing_dashboard.widgets.each do |widget|
            if widget.data_table.present? 
              data_table = widget.data_table
              # widget.update_attribute :data_table, nil
              widget.datasets.destroy_all
              data_table.datasets.destroy_all
              data_table.widgets.update_all data_table_id: nil
              data_table.destroy
            end

            widget.destroy
          end

          users = existing_dashboard.users

          users.each do |user|
            user.dashboards.delete existing_dashboard # Remove association, restore later
          end

          existing_dashboard.delete
        end
      end

      users ||= []

      dashboard = Dashboard.new(
        :id => @force_id, # If nil, will be generated by DB
        :name => definition['name'],
        :description => definition['description'] || '',
        :target_users => definition['target_users'] || '',
        :notes => definition['notes'],
        :url => definition['url'],
        :display_hero => display_hero,
        :display_kpis => display_kpis,
        :organisation => @organisation
      )
      dashboard.published_at = Time.now
      dashboard.save!(:validate => false)

      datasets = data['datasets'].collect {|ds_hash|
        dataset = Dataset.create!(
          name:   ds_hash['name'],
          label:  ds_hash['label'] || ds_hash['name'],
          notes:  ds_hash['note'],
          units:  ds_hash['units'] || 'n')

        [ds_hash['id'], dataset]
      }.to_h

      definition['widgets'].each do |widget|
        res = definition['layout'].collect.with_index { |a, row|
          [row, a.index(widget['id'])] if a.index(widget['id'])
        }.compact.flatten

        # this widget does not to be specified in the layout section (will not render it).
        # if we let it go further, it will break the database validation
        next if res.empty?

        # puts widget['id']
        description = widget['definition'].present? ? widget['definition'] : widget['description']
        is_hero = widget['is_hero'].present? ? widget['is_hero'] : false

        options = {}
        options['displayRoundedData'] = widget['displayRoundedData'] unless widget['displayRoundedData'].nil?
        options['stacking'] = widget['stacking'] if widget['stacking'].present?

        widget_model = Widget.create!(
          :dashboard => dashboard,
          :name => widget['name'],
          :description => description,
          :type => widget['type'],
          :size => widget['size'],
          :units => widget['units'],
          :last_updated_at => widget['updated_at'],
          :is_hero => is_hero,
          :options => options,
          :row => res.first,
          :pos => res.last)

        if widget['datasets']
          widget['datasets'].each do |dataset_id|
            dataset = datasets[dataset_id]

            unless widget_model.data_table.present?
              data_table = dataset.widgets.where.not(data_table_id: nil).first&.data_table
              data_table ||= DataTable.create(dashboard: dashboard)
              widget_model.update_attribute :data_table, data_table
            end

            widget_model.data_table.datasets << datasets[dataset_id]
            widget_model.datasets << dataset
          end

          data['datasets'].select {|ds_hash|
            widget['datasets'].include? ds_hash['id']
          }.each do |ds_hash|
            dataset = datasets[ds_hash['id']]
            
            ds_hash['data']&.each do |dp|
              row_date = Date.strptime dp['label'], '%Y-%m'
              row = widget_model.data_table.data_rows.where(row_date: row_date).first_or_create!
              row.set_value_for dataset, dp['value']
              row.save
            end
          end
        end
      end

      unless @force_id.nil?
        # Restore primary key sequence so that subsequent inserts work OK
        fix_sequence = "SELECT setval('dashboards_id_seq', (SELECT MAX(id) from dashboards));"
        ActiveRecord::Base.connection.execute fix_sequence
      end

      users.each do |user|
        user.dashboards << dashboard # Restore permissions on datasets
      end
    end

    toggle_auditing true
  end

  private

  def data
    @data ||= JSON.parse @data_json
  end

  def data_schema
    @data_schema ||= JSON.parse File.read 'lib/schema/data.schema.json'
  end

  def definition
    @definition ||= JSON.parse @definition_json
  end

  def definition_schema
    @definition_schema ||= JSON.parse File.read 'lib/schema/definition.schema.json'
  end

  def toggle_auditing(bool)
    [ Dataset, Widget, DataTable, DataRow ].each do |clazz|
      clazz.auditing_enabled = bool
    end
  end
end