lib/organisation_importer.rb
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