mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-09-05 04:34:37 +08:00
Implement V2 protocols exporter [SCI-6876] (#4142)
* Implement V2 protocols exporter [SCI-6876] * Implement new importer [SCI-6877]
This commit is contained in:
parent
ada26d8036
commit
5c051ef8c1
8 changed files with 633 additions and 125 deletions
|
@ -73,4 +73,4 @@ function generateElnTable(id, content) {
|
|||
}
|
||||
var html = html_s + tr + html_e;
|
||||
return $.parseHTML(html);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,10 @@ function importProtocolFromFile(
|
|||
element.find("[data-toggle='" + name + "']").hide();
|
||||
}
|
||||
|
||||
function showPartOfElement(element, name) {
|
||||
element.find("[data-toggle='" + name + "']").show();
|
||||
}
|
||||
|
||||
function newAssetElement(folder, stepGuid, fileRef, fileName, fileType) {
|
||||
var html = '<li>';
|
||||
var assetBytes;
|
||||
|
@ -214,20 +218,7 @@ function importProtocolFromFile(
|
|||
tableNodes = node.find('elnTables > elnTable');
|
||||
if (tableNodes.length > 0) {
|
||||
tableNodes.each(function() {
|
||||
var tableId = $(this).attr('id');
|
||||
var tableName = $(this).children('name').text();
|
||||
var tableContent = $(this).children('contents').text();
|
||||
|
||||
// Generate table element
|
||||
var tableEl = newPreviewElement(
|
||||
'table',
|
||||
{ name: tableName }
|
||||
);
|
||||
var elnTableEl = generateElnTable(tableId, tableContent);
|
||||
addChildToPreviewElement(tableEl, 'table', elnTableEl);
|
||||
|
||||
// Now, append table element to step
|
||||
addChildToPreviewElement(stepEl, 'tables', tableEl);
|
||||
addTablePreview(stepEl, this);
|
||||
});
|
||||
} else {
|
||||
hidePartOfElement(stepEl, 'tables');
|
||||
|
@ -237,41 +228,99 @@ function importProtocolFromFile(
|
|||
checklistNodes = node.find('checklists > checklist');
|
||||
if (checklistNodes.length > 0) {
|
||||
checklistNodes.each(function() {
|
||||
var checklistId = $(this).attr('id');
|
||||
var checklistName = $(this).children('name').text();
|
||||
|
||||
var checklistEl = newPreviewElement(
|
||||
'checklist',
|
||||
{ name: checklistName }
|
||||
);
|
||||
|
||||
// Iterate through checklist items
|
||||
$(this).find('items > item').each(function() {
|
||||
var itemId = $(this).attr('id');
|
||||
var itemText = $(this).children('text').text();
|
||||
|
||||
var itemEl = newPreviewElement(
|
||||
'checklist-item',
|
||||
{ text: itemText }
|
||||
);
|
||||
addChildToPreviewElement(checklistEl, 'checklist-items', itemEl);
|
||||
});
|
||||
|
||||
// Now, add checklist item to step
|
||||
addChildToPreviewElement(stepEl, 'checklists', checklistEl);
|
||||
addChecklistPreview(stepEl, this);
|
||||
});
|
||||
}
|
||||
|
||||
// Parse step elements
|
||||
$(this).find('stepElements > stepElement').each(function() {
|
||||
$element = $(this);
|
||||
switch ($(this).attr('type')) {
|
||||
case 'Checklist':
|
||||
addChecklistPreview(stepEl, $(this).find('checklist'));
|
||||
showPartOfElement(stepEl, 'checklists');
|
||||
break;
|
||||
case 'StepTable':
|
||||
addTablePreview(stepEl, $(this).find('elnTable'));
|
||||
showPartOfElement(stepEl, 'tables');
|
||||
break;
|
||||
case 'StepText':
|
||||
addStepTextPreview(stepEl, $(this).find('stepText'), protocolFolders[position], stepGuid);
|
||||
showPartOfElement(stepEl, 'step-texts');
|
||||
break;
|
||||
default:
|
||||
// nothing to do
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Append step element to preview container
|
||||
previewContainer.append(stepEl);
|
||||
});
|
||||
}
|
||||
|
||||
function addTablePreview(stepEl, tableNode) {
|
||||
var tableId = $(tableNode).attr('id');
|
||||
var tableName = $(tableNode).children('name').text();
|
||||
var tableContent = $(tableNode).children('contents').text();
|
||||
|
||||
// Generate table element
|
||||
var tableEl = newPreviewElement(
|
||||
'table',
|
||||
{ name: tableName }
|
||||
);
|
||||
var elnTableEl = generateElnTable(tableId, tableContent);
|
||||
addChildToPreviewElement(tableEl, 'table', elnTableEl);
|
||||
|
||||
// Now, append table element to step
|
||||
addChildToPreviewElement(stepEl, 'tables', tableEl);
|
||||
}
|
||||
|
||||
function addChecklistPreview(stepEl, checklistNode) {
|
||||
var checklistId = $(checklistNode).attr('id');
|
||||
var checklistName = $(checklistNode).children('name').text();
|
||||
|
||||
var checklistEl = newPreviewElement(
|
||||
'checklist',
|
||||
{ name: checklistName }
|
||||
);
|
||||
|
||||
// Iterate through checklist items
|
||||
$(checklistNode).find('items > item').each(function() {
|
||||
var itemId = $(this).attr('id');
|
||||
var itemText = $(this).children('text').text();
|
||||
|
||||
var itemEl = newPreviewElement(
|
||||
'checklist-item',
|
||||
{ text: itemText }
|
||||
);
|
||||
addChildToPreviewElement(checklistEl, 'checklist-items', itemEl);
|
||||
});
|
||||
|
||||
// Now, add checklist item to step
|
||||
addChildToPreviewElement(stepEl, 'checklists', checklistEl);
|
||||
}
|
||||
|
||||
function addStepTextPreview(stepEl, stepTextNode, folder, stepGuid) {
|
||||
var itemId = $(stepTextNode).attr('id');
|
||||
|
||||
var itemText = displayTinyMceAssetInDescription(stepTextNode, folder, stepGuid);
|
||||
|
||||
var textEl = newPreviewElement(
|
||||
'step-text',
|
||||
{ text: itemText }
|
||||
);
|
||||
|
||||
addChildToPreviewElement(stepEl, 'step-texts', textEl);
|
||||
}
|
||||
|
||||
// display tiny_mce_assets in step description
|
||||
function displayTinyMceAssetInDescription(node, folder, stepGuid) {
|
||||
var description;
|
||||
var description = node.children('description').html() || node.children('contents').html();
|
||||
|
||||
if (!description) return '';
|
||||
|
||||
if (node.children('descriptionAssets').length === 0) {
|
||||
description = node.children('description').html();
|
||||
return $('<div></div>').html(
|
||||
description.replace(/\[~tiny_mce_id:([0-9a-zA-Z]+)\]/,
|
||||
'<strong>Can\'t import image</strong>')
|
||||
|
@ -281,7 +330,7 @@ function importProtocolFromFile(
|
|||
.replace(' ]]>', '')
|
||||
).html();
|
||||
}
|
||||
description = node.children('description').html();
|
||||
|
||||
description = description.replace('<!--[CDATA[ ', '')
|
||||
.replace(' ]]-->', '')
|
||||
.replace(' ]]-->', '')
|
||||
|
@ -518,6 +567,76 @@ function importProtocolFromFile(
|
|||
return result;
|
||||
}
|
||||
|
||||
function stepTextJson(stepTextNode, folderIndex, stepGuid) {
|
||||
var json = {};
|
||||
|
||||
json.contents = $('<div></div>').html(
|
||||
stepTextNode.children('contents')
|
||||
.html()
|
||||
.replace('<!--[CDATA[', '')
|
||||
.replace(' ]]-->', '')
|
||||
.replace(']]>', '')
|
||||
).html();
|
||||
|
||||
json.descriptionAssets = prepareTinyMceAssets(stepTextNode, folderIndex, stepGuid);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
function stepTableJson(tableNode) {
|
||||
var json = {};
|
||||
var contents = tableNode.children('contents').text();
|
||||
json.id = tableNode.attr('id');
|
||||
json.name = tableNode.children('name').text();
|
||||
contents = hex2a(contents);
|
||||
contents = window.btoa(contents);
|
||||
json.contents = contents;
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
function checklistJson(checklistNode) {
|
||||
var json = {};
|
||||
var itemsJson = [];
|
||||
json.id = checklistNode.attr('id');
|
||||
json.name = checklistNode.children('name').text();
|
||||
|
||||
// Iterate through checklist items
|
||||
checklistNode.find('items > item').each(function() {
|
||||
var itemJson = {};
|
||||
itemJson.id = $(this).attr('id');
|
||||
itemJson.position = $(this).attr('position');
|
||||
itemJson.text = $(this).children('text').text();
|
||||
itemsJson.push(itemJson);
|
||||
});
|
||||
json.items = itemsJson;
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
function stepElementJson(stepElementNode, folderIndex, stepGuid) {
|
||||
var json = {
|
||||
type: stepElementNode.attr('type')
|
||||
};
|
||||
|
||||
switch (json.type) {
|
||||
case 'Checklist':
|
||||
json.checklist = checklistJson(stepElementNode.find('checklist'));
|
||||
break;
|
||||
case 'StepTable':
|
||||
json.elnTable = stepTableJson(stepElementNode.find('elnTable'));
|
||||
break;
|
||||
case 'StepText':
|
||||
json.stepText = stepTextJson(stepElementNode.find('stepText'), folderIndex, stepGuid);
|
||||
break;
|
||||
default:
|
||||
// nothing to do
|
||||
break;
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
function importSingleProtocol(index, replaceVals, resultCallback) {
|
||||
// Retrieve general protocol info
|
||||
var name;
|
||||
|
@ -551,6 +670,7 @@ function importProtocolFromFile(
|
|||
|
||||
// Allright, let's construct the huge,
|
||||
// messy-pot of a protocol JSON
|
||||
protocolJson.elnVersion = $(protocolXmls[index]).attr('version');
|
||||
protocolJson.name = name;
|
||||
protocolJson.description = protocolDescription;
|
||||
protocolJson.authors = authors;
|
||||
|
@ -571,14 +691,16 @@ function importProtocolFromFile(
|
|||
stepJson.position = $(this).attr('position');
|
||||
stepJson.name = $(this).children('name').text();
|
||||
// I know is crazy but is the only way I found to pass valid HTML
|
||||
stepJson.description = $('<div></div>').html($(this)
|
||||
.children('description')
|
||||
.html()
|
||||
.replace('<!--[CDATA[', '')
|
||||
.replace(' ]]-->', '')
|
||||
.replace(']]>', ''))
|
||||
.html();
|
||||
// Iterage throug tiny_mce_assets
|
||||
if ($(this).children('description').html()) {
|
||||
stepJson.description = $('<div></div>').html($(this)
|
||||
.children('description')
|
||||
.html()
|
||||
.replace('<!--[CDATA[', '')
|
||||
.replace(' ]]-->', '')
|
||||
.replace(']]>', ''))
|
||||
.html();
|
||||
}
|
||||
// Iterate through tiny_mce_assets
|
||||
stepJson.descriptionAssets = prepareTinyMceAssets(this, index, stepGuid);
|
||||
// Iterate through assets
|
||||
|
||||
|
@ -608,39 +730,23 @@ function importProtocolFromFile(
|
|||
|
||||
// Iterate through step tables
|
||||
$(this).find('elnTables > elnTable').each(function() {
|
||||
var stepTableJson = {};
|
||||
var contents = $(this).children('contents').text();
|
||||
stepTableJson.id = $(this).attr('id');
|
||||
stepTableJson.name = $(this).children('name').text();
|
||||
contents = hex2a(contents);
|
||||
contents = window.btoa(contents);
|
||||
stepTableJson.contents = contents;
|
||||
|
||||
stepTablesJson.push(stepTableJson);
|
||||
stepTablesJson.push(stepTableJson($(this)));
|
||||
});
|
||||
stepJson.tables = stepTablesJson;
|
||||
|
||||
// Iterate through checklists
|
||||
$(this).find('checklists > checklist').each(function() {
|
||||
var stepChecklistJson = {};
|
||||
var itemsJson = [];
|
||||
stepChecklistJson.id = $(this).attr('id');
|
||||
stepChecklistJson.name = $(this).children('name').text();
|
||||
|
||||
// Iterate through checklist items
|
||||
$(this).find('items > item').each(function() {
|
||||
var itemJson = {};
|
||||
itemJson.id = $(this).attr('id');
|
||||
itemJson.position = $(this).attr('position');
|
||||
itemJson.text = $(this).children('text').text();
|
||||
itemsJson.push(itemJson);
|
||||
});
|
||||
stepChecklistJson.items = itemsJson;
|
||||
|
||||
stepChecklistsJson.push(stepChecklistJson);
|
||||
stepChecklistsJson.push(checklistJson($(this)));
|
||||
});
|
||||
stepJson.checklists = stepChecklistsJson;
|
||||
|
||||
// Parse step elements
|
||||
stepJson.stepElements = [];
|
||||
|
||||
$(this).find('stepElements > stepElement').each(function() {
|
||||
stepJson.stepElements.push(stepElementJson($(this), index, stepGuid));
|
||||
});
|
||||
|
||||
stepsJson.push(stepJson);
|
||||
});
|
||||
protocolJson.steps = stepsJson;
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
|
||||
class ProtocolsController < ApplicationController
|
||||
include RenamingUtil
|
||||
include ProtocolsImporter
|
||||
include ProtocolsExporter
|
||||
include ActionView::Helpers::TextHelper
|
||||
include ActionView::Helpers::UrlHelper
|
||||
include ApplicationHelper
|
||||
|
@ -10,6 +8,7 @@ class ProtocolsController < ApplicationController
|
|||
include ProtocolsIoHelper
|
||||
include TeamsHelper
|
||||
include CommentHelper
|
||||
include ProtocolsExporterV2
|
||||
|
||||
before_action :check_create_permissions, only: %i(
|
||||
create
|
||||
|
@ -74,6 +73,8 @@ class ProtocolsController < ApplicationController
|
|||
before_action :check_protocolsio_import_permissions,
|
||||
only: %i(protocolsio_import_create protocolsio_import_save)
|
||||
|
||||
before_action :set_importer, only: %i(load_from_file import)
|
||||
|
||||
def index; end
|
||||
|
||||
def datatable
|
||||
|
@ -524,14 +525,14 @@ class ProtocolsController < ApplicationController
|
|||
if @protocol.can_destroy?
|
||||
transaction_error = false
|
||||
Protocol.transaction do
|
||||
begin
|
||||
import_into_existing(
|
||||
@protocol, @protocol_json, current_user, current_team
|
||||
)
|
||||
rescue Exception
|
||||
transaction_error = true
|
||||
raise ActiveRecord:: Rollback
|
||||
end
|
||||
@importer.import_into_existing(
|
||||
@protocol, @protocol_json
|
||||
)
|
||||
rescue StandardError => e
|
||||
transaction_error = true
|
||||
Rails.logger.error(e.message)
|
||||
Rails.logger.error(e.backtrace.join("\n"))
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
|
||||
if transaction_error
|
||||
|
@ -564,14 +565,12 @@ class ProtocolsController < ApplicationController
|
|||
respond_to do |format|
|
||||
transaction_error = false
|
||||
Protocol.transaction do
|
||||
begin
|
||||
protocol =
|
||||
import_new_protocol(@protocol_json, @team, @type, current_user)
|
||||
rescue StandardError => ex
|
||||
Rails.logger.error ex.message
|
||||
transaction_error = true
|
||||
raise ActiveRecord:: Rollback
|
||||
end
|
||||
protocol =
|
||||
@importer.import_new_protocol(@protocol_json, @type)
|
||||
rescue StandardError => e
|
||||
Rails.logger.error e.backtrace.join("\n")
|
||||
transaction_error = true
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
|
||||
p_name =
|
||||
|
@ -659,7 +658,7 @@ class ProtocolsController < ApplicationController
|
|||
# and some modals get closed and opened
|
||||
end
|
||||
rescue StandardError => e
|
||||
Rails.logger.error(e.message)
|
||||
Rails.logger.error(e.backtrace.join("\n"))
|
||||
@protocolsio_general_error = true
|
||||
respond_to do |format|
|
||||
format.js {}
|
||||
|
@ -697,8 +696,8 @@ class ProtocolsController < ApplicationController
|
|||
@protocolsio_general_error = false
|
||||
Protocol.transaction do
|
||||
begin
|
||||
protocol = import_new_protocol(
|
||||
@db_json, current_team, params[:type].to_sym, current_user
|
||||
protocol = @importer.import_new_protocol(
|
||||
@db_json, params[:type].to_sym
|
||||
)
|
||||
rescue Exception
|
||||
transaction_error = true
|
||||
|
@ -763,6 +762,10 @@ class ProtocolsController < ApplicationController
|
|||
end
|
||||
end
|
||||
ostream = step.tiny_mce_assets.save_to_eln(ostream, step_dir)
|
||||
|
||||
step.step_texts.each do |step_text|
|
||||
ostream = step_text.tiny_mce_assets.save_to_eln(ostream, step_dir)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -989,6 +992,15 @@ class ProtocolsController < ApplicationController
|
|||
|
||||
private
|
||||
|
||||
def set_importer
|
||||
case params.dig('protocol', 'elnVersion')
|
||||
when '1.0'
|
||||
@importer = ProtocolsImporter.new(current_user, current_team)
|
||||
when '1.1'
|
||||
@importer = ProtocolsImporterV2.new(current_user, current_team)
|
||||
end
|
||||
end
|
||||
|
||||
def valid_protocol_json(json)
|
||||
JSON.parse(json)
|
||||
return true
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
<li>
|
||||
<a data-action="load-from-file"
|
||||
class="btn-open-file"
|
||||
:data-import-url="protocol.attributes.urls.import_url"
|
||||
:class="{ disabled: !protocol.attributes.urls.import_url }">
|
||||
<span class="fas fa-download"></span>
|
||||
<span>{{ i18n.t("my_modules.protocol.options_dropdown.import") }}</span>
|
||||
|
|
136
app/utilities/protocols_exporter_v2.rb
Normal file
136
app/utilities/protocols_exporter_v2.rb
Normal file
|
@ -0,0 +1,136 @@
|
|||
require 'zip'
|
||||
|
||||
module ProtocolsExporterV2
|
||||
include ProtocolsExporter
|
||||
|
||||
private
|
||||
|
||||
def generate_envelope_xml(protocols)
|
||||
envelope_xml = "<envelope xmlns=\"http://www.scinote.net\" " \
|
||||
"version=\"1.1\">\n"
|
||||
protocols.each do |protocol|
|
||||
protocol_name = get_protocol_name(protocol)
|
||||
envelope_xml << "<protocol id=\"#{protocol.id}\" " \
|
||||
"guid=\"#{get_guid(protocol.id)}\">#{protocol_name}" \
|
||||
"</protocol>\n"
|
||||
end
|
||||
envelope_xml << "</envelope>\n"
|
||||
envelope_xml
|
||||
end
|
||||
|
||||
def generate_protocol_xml(protocol)
|
||||
protocol_name = get_protocol_name(protocol)
|
||||
|
||||
protocol_xml =
|
||||
"<eln xmlns=\"http://www.scinote.net\" version=\"1.1\">\n" \
|
||||
"<protocol id=\"#{protocol.id}\" guid=\"#{get_guid(protocol.id)}\">\n" \
|
||||
"<name>#{protocol_name}</name>\n" \
|
||||
"<authors>#{protocol.authors}</authors>\n" \
|
||||
"<description>" \
|
||||
"<!--[CDATA[ #{Nokogiri::HTML::DocumentFragment.parse(protocol.description)} ]]-->" \
|
||||
"</description>\n"
|
||||
|
||||
if tiny_mce_asset_present?(protocol) && protocol.description
|
||||
protocol_xml << get_tiny_mce_assets(protocol.description)
|
||||
end
|
||||
|
||||
protocol_xml << "<created_at>#{protocol.created_at.as_json}</created_at>\n"
|
||||
protocol_xml << "<updated_at>#{protocol.updated_at.as_json}</updated_at>\n"
|
||||
|
||||
# Steps
|
||||
if protocol.steps.any?
|
||||
protocol_xml <<
|
||||
"<steps>\n" \
|
||||
"#{protocol.steps.order(:position).map { |s| step_xml(s) }.join}" \
|
||||
"</steps>\n"
|
||||
end
|
||||
|
||||
protocol_xml << "</protocol>\n"
|
||||
protocol_xml << '</eln>'
|
||||
protocol_xml
|
||||
end
|
||||
|
||||
def step_xml(step)
|
||||
step_guid = get_guid(step.id)
|
||||
xml = "<step id=\"#{step.id}\" guid=\"#{step_guid}\" position=\"#{step.position}\">\n" \
|
||||
"<name>#{step.name}</name>\n"
|
||||
|
||||
# Assets
|
||||
xml << "<assets>\n#{step.assets.map { |a| asset_xml(a) }.join}</assets>\n" if step.assets.any?
|
||||
|
||||
if step.step_orderable_elements.any?
|
||||
xml << "<stepElements>\n"
|
||||
step.step_orderable_elements.find_each do |step_orderable_element|
|
||||
element = step_orderable_element.orderable
|
||||
element_guid = get_guid(element.id)
|
||||
xml << "<stepElement type=\"#{step_orderable_element.orderable_type}\" guid=\"#{element_guid}\" " \
|
||||
"position=\"#{step_orderable_element.position}\">"
|
||||
|
||||
case element
|
||||
when StepText
|
||||
xml << step_text_xml(element)
|
||||
when StepTable
|
||||
xml << table_xml(element.table)
|
||||
when Checklist
|
||||
xml << checklist_xml(element)
|
||||
end
|
||||
|
||||
xml << "</stepElement>\n"
|
||||
end
|
||||
xml << "</stepElements>\n"
|
||||
end
|
||||
|
||||
xml << '</step>'
|
||||
|
||||
xml
|
||||
end
|
||||
|
||||
def step_text_xml(step_text)
|
||||
xml = "<stepText id=\"#{step_text.id}\" guid=\"#{get_guid(step_text.id)}\">\n" \
|
||||
"<contents>\n" \
|
||||
"<!--[CDATA[ #{Nokogiri::HTML::DocumentFragment.parse(step_text.text)} ]]-->"\
|
||||
"</contents>\n"
|
||||
|
||||
xml << get_tiny_mce_assets(step_text.text)
|
||||
|
||||
xml << "</stepText>\n"
|
||||
end
|
||||
|
||||
def table_xml(table)
|
||||
"<elnTable id=\"#{table.id}\" guid=\"#{get_guid(table.id)}\">\n" \
|
||||
"<name>#{table.name}</name>\n" \
|
||||
"<contents>#{table.contents.unpack1('H*')}</contents>\n" \
|
||||
"</elnTable>\n"
|
||||
end
|
||||
|
||||
def checklist_xml(checklist)
|
||||
xml = "<checklist id=\"#{checklist.id}\" guid=\"#{get_guid(checklist.id)}\">\n" \
|
||||
"<name>#{checklist.name}</name>\n"
|
||||
|
||||
if checklist.checklist_items.any?
|
||||
xml << "<items>\n#{checklist.checklist_items.map { |ci| checklist_item_xml(ci) }.join}</items>\n"
|
||||
end
|
||||
|
||||
xml << "</checklist>\n"
|
||||
|
||||
xml
|
||||
end
|
||||
|
||||
def checklist_item_xml(checklist_item)
|
||||
"<item id=\"#{checklist_item.id}\" " \
|
||||
"guid=\"#{get_guid(checklist_item.id)}\" " \
|
||||
"position=\"#{checklist_item.position}\">\n" \
|
||||
"<text>#{checklist_item.text}</text>\n" \
|
||||
"</item>\n" \
|
||||
end
|
||||
|
||||
def asset_xml(asset)
|
||||
asset_guid = get_guid(asset.id)
|
||||
"#{asset_guid}#{File.extname(asset.file_name)}" \
|
||||
"<asset id=\"#{asset.id}\" guid=\"#{asset_guid}\" fileRef=\"#{asset_file_name}\">\n" \
|
||||
"<fileName>#{asset.file_name}</fileName>\n" \
|
||||
"<fileType>#{asset.content_type}</fileType>\n" \
|
||||
"<fileMetadata><!--[CDATA[ #{asset.file.metadata.to_json} ]]--></fileMetadata>\n" \
|
||||
"</asset>\n"
|
||||
end
|
||||
end
|
|
@ -1,17 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ProtocolsImporter
|
||||
class ProtocolsImporter
|
||||
include RenamingUtil
|
||||
|
||||
def import_new_protocol(protocol_json, team, type, user)
|
||||
def initialize(user, team)
|
||||
@user = user
|
||||
@team = team
|
||||
end
|
||||
|
||||
def import_new_protocol(protocol_json, type)
|
||||
remove_empty_inputs(protocol_json)
|
||||
protocol = Protocol.new(
|
||||
name: protocol_json['name'],
|
||||
authors: protocol_json['authors'],
|
||||
protocol_type: (type == :public ? :in_repository_public : :in_repository_private),
|
||||
published_on: (type == :public ? Time.now : nil),
|
||||
added_by: user,
|
||||
team: team
|
||||
added_by: @user,
|
||||
team: @team
|
||||
)
|
||||
|
||||
# Try to rename record
|
||||
|
@ -21,19 +26,19 @@ module ProtocolsImporter
|
|||
protocol.save!
|
||||
|
||||
# Protocol is saved, populate it
|
||||
populate_protocol(protocol, protocol_json, user, team)
|
||||
populate_protocol(protocol, protocol_json)
|
||||
|
||||
protocol
|
||||
end
|
||||
|
||||
def import_into_existing(protocol, protocol_json, user, team)
|
||||
def import_into_existing(protocol, protocol_json)
|
||||
# Firstly, destroy existing protocol's contents
|
||||
protocol.tiny_mce_assets.destroy_all
|
||||
protocol.destroy_contents
|
||||
protocol.reload
|
||||
|
||||
# Alright, now populate the protocol
|
||||
populate_protocol(protocol, protocol_json, user, team)
|
||||
populate_protocol(protocol, protocol_json)
|
||||
protocol.reload
|
||||
|
||||
# Unlink the protocol
|
||||
|
@ -43,9 +48,20 @@ module ProtocolsImporter
|
|||
|
||||
private
|
||||
|
||||
def populate_protocol(protocol, protocol_json, user, team)
|
||||
def create_in_step!(step, new_orderable)
|
||||
ActiveRecord::Base.transaction do
|
||||
new_orderable.save!
|
||||
|
||||
step.step_orderable_elements.create!(
|
||||
position: step.step_orderable_elements.length,
|
||||
orderable: new_orderable
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def populate_protocol(protocol, protocol_json)
|
||||
protocol.reload
|
||||
protocol.description = populate_rte(protocol_json, protocol, team)
|
||||
protocol.description = populate_rte(protocol_json, protocol)
|
||||
protocol.save!
|
||||
asset_ids = []
|
||||
step_pos = 0
|
||||
|
@ -55,22 +71,27 @@ module ProtocolsImporter
|
|||
name: step_json['name'],
|
||||
position: step_pos,
|
||||
completed: false,
|
||||
user: user,
|
||||
last_modified_by: user,
|
||||
user: @user,
|
||||
last_modified_by: @user,
|
||||
protocol: protocol
|
||||
)
|
||||
# need step id to link image to step
|
||||
step.description = populate_rte(step_json, step, team)
|
||||
step_description_text = step.step_texts.build(text: populate_rte(step_json, step))
|
||||
create_in_step!(step, step_description_text)
|
||||
|
||||
step.save!
|
||||
step_pos += 1
|
||||
|
||||
step_json['checklists']&.values&.each do |checklist_json|
|
||||
checklist = Checklist.create!(
|
||||
checklist = Checklist.new(
|
||||
name: checklist_json['name'],
|
||||
step: step,
|
||||
created_by: user,
|
||||
last_modified_by: user
|
||||
created_by: @user,
|
||||
last_modified_by: @user
|
||||
)
|
||||
|
||||
create_in_step!(step, checklist)
|
||||
|
||||
next unless checklist_json['items']
|
||||
|
||||
item_pos = 0
|
||||
|
@ -79,8 +100,8 @@ module ProtocolsImporter
|
|||
text: item_json['text'],
|
||||
checked: false,
|
||||
position: item_pos,
|
||||
created_by: user,
|
||||
last_modified_by: user,
|
||||
created_by: @user,
|
||||
last_modified_by: @user,
|
||||
checklist: checklist
|
||||
)
|
||||
item_pos += 1
|
||||
|
@ -88,26 +109,27 @@ module ProtocolsImporter
|
|||
end
|
||||
|
||||
step_json['tables']&.values&.each do |table_json|
|
||||
table = Table.create!(
|
||||
name: table_json['name'],
|
||||
contents: Base64.decode64(table_json['contents']),
|
||||
created_by: user,
|
||||
last_modified_by: user,
|
||||
team: team
|
||||
)
|
||||
StepTable.create!(
|
||||
step_table = StepTable.new(
|
||||
step: step,
|
||||
table: table
|
||||
table: Table.new(
|
||||
name: table_json['name'],
|
||||
contents: Base64.decode64(table_json['contents']),
|
||||
created_by: @user,
|
||||
last_modified_by: @user,
|
||||
team: @team
|
||||
)
|
||||
)
|
||||
|
||||
create_in_step!(step, step_table)
|
||||
end
|
||||
|
||||
next unless step_json['assets']
|
||||
|
||||
step_json['assets']&.values&.each do |asset_json|
|
||||
asset = Asset.new(
|
||||
created_by: user,
|
||||
last_modified_by: user,
|
||||
team: team
|
||||
created_by: @user,
|
||||
last_modified_by: @user,
|
||||
team: @team
|
||||
)
|
||||
|
||||
# Decode the file bytes
|
||||
|
@ -143,14 +165,14 @@ module ProtocolsImporter
|
|||
end
|
||||
|
||||
# create tiny_mce assets and change the inport tokens
|
||||
def populate_rte(object_json, object, team)
|
||||
def populate_rte(object_json, object)
|
||||
return populate_rte_legacy(object_json) unless object_json['descriptionAssets']
|
||||
|
||||
description = TinyMceAsset.update_old_tinymce(object_json['description'], nil, true)
|
||||
object_json['descriptionAssets'].values.each do |tiny_mce_img_json|
|
||||
tiny_mce_img = TinyMceAsset.new(
|
||||
object: object,
|
||||
team_id: team.id,
|
||||
team_id: @team.id,
|
||||
saved: true
|
||||
)
|
||||
tiny_mce_img.save!
|
||||
|
|
224
app/utilities/protocols_importer_v2.rb
Normal file
224
app/utilities/protocols_importer_v2.rb
Normal file
|
@ -0,0 +1,224 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProtocolsImporterV2
|
||||
include RenamingUtil
|
||||
|
||||
def initialize(user, team)
|
||||
@user = user
|
||||
@team = team
|
||||
end
|
||||
|
||||
def import_new_protocol(protocol_json, type)
|
||||
remove_empty_inputs(protocol_json)
|
||||
protocol = Protocol.new(
|
||||
name: protocol_json['name'],
|
||||
authors: protocol_json['authors'],
|
||||
protocol_type: (type == :public ? :in_repository_public : :in_repository_private),
|
||||
published_on: (type == :public ? Time.now : nil),
|
||||
added_by: @user,
|
||||
team: @team
|
||||
)
|
||||
|
||||
# Try to rename record
|
||||
rename_record(protocol, :name) if protocol.invalid?
|
||||
|
||||
# Okay, now save the protocol
|
||||
protocol.save!
|
||||
|
||||
# Protocol is saved, populate it
|
||||
populate_protocol(protocol, protocol_json)
|
||||
|
||||
protocol
|
||||
end
|
||||
|
||||
def import_into_existing(protocol, protocol_json)
|
||||
# Firstly, destroy existing protocol's contents
|
||||
protocol.tiny_mce_assets.destroy_all
|
||||
protocol.destroy_contents
|
||||
protocol.reload
|
||||
|
||||
# Alright, now populate the protocol
|
||||
populate_protocol(protocol, protocol_json)
|
||||
protocol.reload
|
||||
|
||||
# Unlink the protocol
|
||||
protocol.unlink
|
||||
protocol
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_in_step!(step, new_orderable)
|
||||
ActiveRecord::Base.transaction do
|
||||
new_orderable.save!
|
||||
step.step_orderable_elements.create!(
|
||||
position: step.step_orderable_elements.length,
|
||||
orderable: new_orderable
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def populate_protocol(protocol, protocol_json)
|
||||
protocol.reload
|
||||
protocol.description = populate_rte(protocol_json, protocol)
|
||||
protocol.save!
|
||||
asset_ids = []
|
||||
step_pos = 0
|
||||
# Check if protocol has steps
|
||||
protocol_json['steps']&.values&.each do |step_json|
|
||||
step = Step.create!(
|
||||
name: step_json['name'],
|
||||
position: step_pos,
|
||||
completed: false,
|
||||
user: @user,
|
||||
last_modified_by: @user,
|
||||
protocol: protocol
|
||||
)
|
||||
|
||||
step.save!
|
||||
step_pos += 1
|
||||
|
||||
step_json['stepElements']&.values&.each do |element_params|
|
||||
case element_params['type']
|
||||
when 'StepText'
|
||||
create_step_text(step, element_params['stepText'])
|
||||
when 'StepTable'
|
||||
create_step_table(step, element_params['elnTable'])
|
||||
when 'Checklist'
|
||||
create_checklist(step, element_params['checklist'])
|
||||
end
|
||||
end
|
||||
|
||||
next unless step_json['assets']
|
||||
|
||||
create_assets(step_json)
|
||||
end
|
||||
|
||||
# Post process assets
|
||||
asset_ids.each do |asset_id|
|
||||
Asset.find(asset_id).post_process_file(protocol.team)
|
||||
end
|
||||
end
|
||||
|
||||
def create_assets(step_json)
|
||||
step_json['assets']&.values&.each do |asset_json|
|
||||
asset = Asset.new(
|
||||
created_by: @user,
|
||||
last_modified_by: @user,
|
||||
team: @team
|
||||
)
|
||||
|
||||
# Decode the file bytes
|
||||
asset.file.attach(io: StringIO.new(Base64.decode64(asset_json['bytes'])),
|
||||
filename: asset_json['fileName'],
|
||||
content_type: asset_json['fileType'],
|
||||
metadata: JSON.parse(asset_json['fileMetadata'] || '{}'))
|
||||
asset.save!
|
||||
asset_ids << asset.id
|
||||
|
||||
StepAsset.create!(
|
||||
step: step,
|
||||
asset: asset
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def create_step_text(step, params)
|
||||
step_text = StepText.create!(
|
||||
step: step
|
||||
)
|
||||
|
||||
step_text.update!(text: populate_rte(params, step_text))
|
||||
|
||||
create_in_step!(step, step_text)
|
||||
end
|
||||
|
||||
def create_step_table(step, params)
|
||||
step_table = StepTable.new(
|
||||
step: step,
|
||||
table: Table.new(
|
||||
name: params['name'],
|
||||
contents: Base64.decode64(params['contents']),
|
||||
created_by: @user,
|
||||
last_modified_by: @user,
|
||||
team: @team
|
||||
)
|
||||
)
|
||||
|
||||
create_in_step!(step, step_table)
|
||||
end
|
||||
|
||||
def create_checklist(step, params)
|
||||
checklist = Checklist.new(
|
||||
name: params['name'],
|
||||
step: step,
|
||||
created_by: @user,
|
||||
last_modified_by: @user
|
||||
)
|
||||
|
||||
create_in_step!(step, checklist)
|
||||
|
||||
return unless params['items']
|
||||
|
||||
item_pos = 0
|
||||
|
||||
params['items'].each_value do |item_json|
|
||||
ChecklistItem.create!(
|
||||
text: item_json['text'],
|
||||
checked: false,
|
||||
position: item_pos,
|
||||
created_by: @user,
|
||||
last_modified_by: @user,
|
||||
checklist: checklist
|
||||
)
|
||||
item_pos += 1
|
||||
end
|
||||
end
|
||||
|
||||
def remove_empty_inputs(obj)
|
||||
obj.each_key do |key|
|
||||
case obj[key]
|
||||
when ''
|
||||
obj[key] = nil
|
||||
when Hash
|
||||
# Recursive call
|
||||
remove_empty_inputs(obj[key])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# create tiny_mce assets and change the import tokens
|
||||
def populate_rte(params, object)
|
||||
description = params['description'] || params['contents']
|
||||
|
||||
return description unless params['descriptionAssets']
|
||||
|
||||
params['descriptionAssets'].each_value do |tiny_mce_img_json|
|
||||
tiny_mce_img = TinyMceAsset.new(
|
||||
object: object,
|
||||
team_id: @team.id,
|
||||
saved: true
|
||||
)
|
||||
tiny_mce_img.save!
|
||||
|
||||
# Decode the file bytes
|
||||
file = StringIO.new(Base64.decode64(tiny_mce_img_json['bytes']))
|
||||
to_blob = ActiveStorage::Blob.create_and_upload!(
|
||||
io: file,
|
||||
filename: tiny_mce_img_json['fileName'],
|
||||
content_type: tiny_mce_img_json['fileType'],
|
||||
metadata: JSON.parse(tiny_mce_img_json['fileMetadata'] || '{}')
|
||||
)
|
||||
tiny_mce_img.image.attach(to_blob)
|
||||
description.gsub!(
|
||||
"data-mce-token=\"#{tiny_mce_img_json['tokenId']}\"",
|
||||
"data-mce-token=\"#{Base62.encode(tiny_mce_img.id)}\""
|
||||
) || description.gsub!(
|
||||
"data-mce-token=\"#{Base62.encode(tiny_mce_img_json['tokenId'].to_i)}\"",
|
||||
"data-mce-token=\"#{Base62.encode(tiny_mce_img.id)}\""
|
||||
)
|
||||
description.gsub!(' ]]-->', '')
|
||||
end
|
||||
description
|
||||
end
|
||||
end
|
|
@ -17,6 +17,9 @@
|
|||
<div data-val="description" class="ql-editor"></div>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div data-hold="step-texts"></div>
|
||||
</div>
|
||||
<div data-toggle="tables" class="col-xs-12">
|
||||
<div data-hold="tables"></div>
|
||||
</div>
|
||||
|
@ -55,3 +58,7 @@
|
|||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-template="step-text" style="display: none;">
|
||||
<div data-val="text"></div>
|
||||
</div>
|
||||
|
|
Loading…
Add table
Reference in a new issue