Implemented protocol export through Zip::OutputStream [SCI-742]

This commit is contained in:
Oleksii Kriuchykhin 2016-12-12 15:33:21 +01:00
parent 1abd7fc574
commit 14a6705b55
5 changed files with 221 additions and 660 deletions

View file

@ -1,5 +1,4 @@
//= require protocols/import_export/import
//= require protocols/import_export/export
//= require comments
//= require datatables
@ -17,7 +16,6 @@ function init() {
initLoadFromRepository();
initRefreshStatusBar();
initImport();
initExport();
Comments.bindNewElement();
Comments.initialize();
initTutorial();
@ -510,11 +508,4 @@ function initImport() {
});
}
function initExport() {
var exportBtn = $("[data-action='export']");
exportBtn.on("click", function() {
exportProtocols([ $(this).attr("data-id") ]);
});
}
init();

View file

@ -1,372 +0,0 @@
//= require jszip.min.js
function exportProtocols(protocol_ids) {
/*********************************************/
/* INNER FUNCTIONS */
/*********************************************/
// Custom StringBuilder 'object'
function StringBuilder() {
this.tokens = [];
this.add = function(token) {
this.tokens.push(token);
return this;
};
this.nl = function() {
this.tokens.push('\n');
return this;
};
this.build = function() {
var str = this.tokens.join("");
this.tokens = null;
return str;
};
}
function getGuid(id) {
var str1 = "00000000-0000-";
var str2 = id.toString();
for (var i = str2.length; i < 19; i++) {
str2 = "0" + str2;
}
str2 = "4" + str2;
var str2n = str2.slice(0, 4) + "-" + str2.slice(4, 8) + "-" + str2.slice(8);
return str1 + str2n;
}
// Escape null in String
function esn(val) {
if (val === null) {
return "";
} else {
return String(val);
}
}
function extractFileExtension(fileName) {
var tokens = fileName.split(".");
if (tokens.length <= 1) {
return "";
} else {
return "." + tokens[tokens.length - 1];
}
}
function generateEnvelopeXsd() {
var sb = new StringBuilder();
sb.add('<?xml version="1.0" encoding="UTF-8"?>').nl();
sb.add('<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">').nl();
sb.add('<xs:element name="envelope">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:sequence>').nl();
sb.add('<xs:element name="protocol" maxOccurs="unbounded">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:simpleContent>').nl();
sb.add('<xs:extension base="xs:string">').nl();
sb.add('<xs:attribute name="id" type="xs:int" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="guid" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('</xs:extension>').nl();
sb.add('</xs:simpleContent>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:sequence>').nl();
sb.add('<xs:attribute name="xmlns" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="version" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:schema>');
return sb.build();
}
function generateElnXsd() {
var sb = new StringBuilder();
sb.add('<?xml version="1.0" encoding="UTF-8"?>').nl();
sb.add('<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">').nl();
sb.add('<xs:element name="eln">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:all>').nl();
sb.add('<xs:element name="protocol">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:all>').nl();
sb.add('<xs:element name="name" type="xs:string"></xs:element>').nl();
sb.add('<xs:element name="authors" type="xs:string" minOccurs="0"></xs:element>').nl();
sb.add('<xs:element name="description" type="xs:string" minOccurs="0"></xs:element>').nl();
sb.add('<xs:element name="created_at" type="xs:string"></xs:element>').nl();
sb.add('<xs:element name="updated_at" type="xs:string"></xs:element>').nl();
sb.add('<xs:element name="steps" minOccurs="0">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:sequence>').nl();
sb.add('<xs:element name="step" maxOccurs="unbounded">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:all>').nl();
sb.add('<xs:element name="name" type="xs:string"></xs:element>').nl();
sb.add('<xs:element name="description" type="xs:string" minOccurs="0"></xs:element>').nl();
sb.add('<xs:element name="checklists" minOccurs="0">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:sequence>').nl();
sb.add('<xs:element name="checklist" maxOccurs="unbounded">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:all>').nl();
sb.add('<xs:element name="name" type="xs:string"></xs:element>').nl();
sb.add('<xs:element name="items" minOccurs="0">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:sequence>').nl();
sb.add('<xs:element name="item" maxOccurs="unbounded">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:all>').nl();
sb.add('<xs:element name="text" type="xs:string"></xs:element>').nl();
sb.add('</xs:all>').nl();
sb.add('<xs:attribute name="id" type="xs:int" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="guid" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="position" type="xs:int" use="required"></xs:attribute>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:sequence>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:all>').nl();
sb.add('<xs:attribute name="id" type="xs:int" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="guid" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:sequence>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('<xs:element name="assets" minOccurs="0">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:sequence>').nl();
sb.add('<xs:element name="asset" maxOccurs="unbounded">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:all>').nl();
sb.add('<xs:element name="fileName" type="xs:string"></xs:element>').nl();
sb.add('<xs:element name="fileType" type="xs:string"></xs:element>').nl();
sb.add('</xs:all>').nl();
sb.add('<xs:attribute name="id" type="xs:int" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="guid" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="fileRef" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:sequence>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('<xs:element name="elnTables" minOccurs="0">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:sequence>').nl();
sb.add('<xs:element name="elnTable" maxOccurs="unbounded">').nl();
sb.add('<xs:complexType>').nl();
sb.add('<xs:all>').nl();
sb.add('<xs:element name="contents" type="xs:string"></xs:element>').nl();
sb.add('</xs:all>').nl();
sb.add('<xs:attribute name="id" type="xs:int" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="guid" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:sequence>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:all>').nl();
sb.add('<xs:attribute name="id" type="xs:int" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="guid" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="position" type="xs:int" use="required"></xs:attribute>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:sequence>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:all>').nl();
sb.add('<xs:attribute name="id" type="xs:int" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="guid" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:all>').nl();
sb.add('<xs:attribute name="xmlns" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('<xs:attribute name="version" type="xs:string" use="required"></xs:attribute>').nl();
sb.add('</xs:complexType>').nl();
sb.add('</xs:element>').nl();
sb.add('</xs:schema>');
return sb.build();
}
/*********************************************/
/* ACTUAL FUNCTION CODE */
/*********************************************/
if (protocol_ids.length > 0) {
animateSpinner();
$.ajax({
type: "GET",
url: "/protocols/export",
dataType: "json",
data: { protocol_ids: protocol_ids},
success: function (data) {
var zip = new JSZip();
// Envelope code
var esb = new StringBuilder();
esb.add('<envelope xmlns="http://www.scinote.net" version="1.0">').nl();
_.each(data.protocols, function(protocol) {
var protocolGuid = getGuid(protocol.id);
// Create folder for this protocol
var protocolFolder = zip.folder(protocolGuid);
var protocolXml = [];
var psb = new StringBuilder();
// Header
psb.add('<eln xmlns="http://www.scinote.net" version="1.0">').nl();
// Protocol
psb.add('<protocol id="' + protocol.id + '" guid="' + protocolGuid + '">').nl();
psb.add('<name>' + esn(protocol.name) + '</name>').nl();
psb.add('<authors>' + esn(protocol.authors) + '</authors>').nl();
psb.add('<description>' + esn(protocol.description) + '</description>').nl();
psb.add('<created_at>' + esn(protocol.created_at) + '</created_at>').nl();
psb.add('<updated_at>' + esn(protocol.updated_at) + '</updated_at>').nl();
// Steps
if (protocol.steps.length > 0) {
psb.add('<steps>').nl();
_.each(protocol.steps, function(step) {
var stepGuid = getGuid(step.id);
var ssb = new StringBuilder();
ssb.add('<step id="' + step.id + '" guid="' + stepGuid + '" position="' + step.position + '">').nl();
ssb.add('<name>' + esn(step.name) + '</name>').nl();
ssb.add('<description>' + esn(step.description) + '</description>').nl();
// Assets
if (step.assets.length > 0) {
ssb.add('<assets>').nl();
_.each(step.assets, function(asset) {
var assetGuid = getGuid(asset.id);
// Generate the asset file inside ZIP
var assetFileName = assetGuid + extractFileExtension(asset.fileName);
var decodedData = window.atob(asset.bytes);
zip.folder(protocolGuid).folder(stepGuid).file(assetFileName, decodedData, { binary: true });
var asb = new StringBuilder();
asb.add('<asset id="' + asset.id + '" guid="' + assetGuid + '" fileRef="' + assetFileName + '">').nl();
asb.add('<fileName>' + esn(asset.fileName) + '</fileName>').nl();
asb.add('<fileType>' + esn(asset.fileType) + '</fileType>').nl();
asb.add('</asset>').nl();
ssb.add(asb.build());
});
ssb.add('</assets>').nl();
}
// Tables
if (step.tables.length > 0) {
ssb.add('<elnTables>').nl();
_.each(step.tables, function(table) {
var tsb = new StringBuilder();
tsb.add('<elnTable id="' + table.id + '" guid="' + getGuid(table.id) + '">').nl();
tsb.add('<name>' + esn(table.name) + '</name>').nl();
tsb.add('<contents>' + esn(table.contents) + '</contents>').nl();
tsb.add('</elnTable>');
ssb.add(tsb.build()).nl();
});
ssb.add('</elnTables>').nl();
}
// Checklists
if (step.checklists.length > 0) {
ssb.add('<checklists>').nl();
_.each(step.checklists, function(checklist) {
var csb = new StringBuilder();
csb.add('<checklist id="' + checklist.id + '" guid="' + getGuid(checklist.id) + '">').nl();
csb.add('<name>' + esn(checklist.name) + '</name>').nl();
if (checklist.items.length > 0) {
csb.add('<items>').nl();
_.each(checklist.items, function(item) {
var isb = new StringBuilder();
isb.add('<item id="' + item.id + '" guid="' + getGuid(item.id) + '" position="' + item.position + '">').nl();
isb.add('<text>' + esn(item.text) + '</text>').nl();
isb.add('</item>');
csb.add(isb.build()).nl();
});
csb.add('</items>').nl();
}
csb.add('</checklist>');
ssb.add(csb.build()).nl();
});
ssb.add('</checklists>').nl();
}
psb.add(ssb.build());
psb.add('</step>').nl();
});
psb.add('</steps>').nl();
}
psb.add('</protocol>').nl();
psb.add('</eln>');
// Okay, we have a generated XML for
// individual protocol, save it to protocol folder
protocolFolder.file("eln.xml", psb.build());
// Add protocol to the envelope
esb.add('<protocol id="' + protocol.id + '" guid="' + protocolGuid + '">' + esn(protocol.name) + '</protocol>').nl();
});
esb.add('</envelope>').nl();
// Save envelope to root directory in ZIP
zip.file("scinote.xml", esb.build());
// Add the XSD schemes into the ZIP
zip.file("scinote.xsd", generateEnvelopeXsd());
zip.file("eln.xsd", generateElnXsd());
// NOW, DOWNLOAD THE ZIP
var blob = zip.generate({ type: "blob" });
var fileName = "protocol.eln";
if (data.protocols.length === 1) {
// Try to construct an OS-safe file name
if (data.protocols[0].name.length && data.protocols[0].name.length > 0) {
var escapedName = data.protocols[0].name.replace(/[^0-9a-zA-Z-.,_]/gi, '_').toLowerCase().substring(0, 250);
if (escapedName.length > 0) {
fileName = escapedName + ".eln";
}
}
} else if (data.protocols.length > 1) {
fileName = "protocols.eln";
}
var link = document.createElement("a");
if (link.download !== undefined) {
// Browsers that support HTML5 download attribute
link.setAttribute("href", window.URL.createObjectURL(blob));
link.setAttribute("download", fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} else {
alert("Please use latest version of Chrome, Firefox, or Opera browser for the export!");
}
animateSpinner(null, false);
},
error: function() {
alert("Ajax error!");
}
});
}
}

View file

@ -560,38 +560,61 @@ class ProtocolsController < ApplicationController
end
def export
#respond_to do |format|
# format.json {
# render json: {
# protocols: export_protocols(@protocols)
# }, status: :ok
# }
#end
# Make a zip output stream and send it to the client
respond_to do |format|
format.html
format.zip do
Dir.mktmpdir do |tmp_dir|
end
format.html do
z_output_stream = Zip::OutputStream.write_buffer do |ostream|
ostream.put_next_entry('eln.xml')
ostream.print(protocol_xml)
ostream.put_next_entry("#{arch_dir}/scinote.xml")
ostream.print(envelope_xml)
ostream.put_next_entry("#{arch_dir}/scinote.xsd")
ostream.put_next_entry('scinote.xml')
ostream.print(generate_envelope_xml(@protocols))
ostream.put_next_entry('scinote.xsd')
ostream.print(generate_envelope_xsd)
ostream.put_next_entry("#{arch_dir}/eln.xsd")
ostream.put_next_entry('eln.xsd')
ostream.print(generate_eln_xsd)
# Create folder and xml file for each protocol and populate it
@protocols.each do |protocol|
protocol_dir = get_guid(protocol.id).to_s
ostream.put_next_entry("#{protocol_dir}/eln.xml")
ostream.print(generate_protocol_xml(protocol))
# Add assets to protocol folder
if protocol.steps.count > 0
protocol.steps.order(:id).each do |step|
step_guid = get_guid(step.id)
step_dir = "#{protocol_dir}/#{step_guid}"
if step.assets.count > 0
step.assets.order(:id).each do |asset|
asset_guid = get_guid(asset.id)
asset_file_name = asset_guid.to_s +
File.extname(asset.file_file_name).to_s
ostream.put_next_entry("#{step_dir}/#{asset_file_name}")
input_file = asset.open
ostream.print(input_file.read)
input_file.close
end
end
end
end
end
end
z_output_stream.rewind
send_data compressed_filestream.read, filename: "protocol.eln"
protocol_name = get_protocol_name(@protocols[0])
# Now generate filename of the archive and send file to user
if @protocols.count == 1
# Try to construct an OS-safe file name
file_name = 'protocol.eln'
unless protocol_name.nil?
escaped_name = protocol_name.gsub(/[^0-9a-zA-Z-.,_]/i, '_')
.downcase[0..250]
file_name = escaped_name + '.eln' unless escaped_name.empty?
end
elsif @protocols.length > 1
file_name = 'protocols.eln'
end
send_data(z_output_stream.read, filename: file_name)
end
end
end
def unlink_modal

View file

@ -1,152 +1,6 @@
require 'tmpdir'
require 'zip'
module ProtocolsExporter
def export_protocols(protocols)
#protocols_json = []
#protocols.each do |protocol|
# protocols_json << export_protocol(protocol)
#end
#return protocols_json
# create temporary working dir, will be deleted in the end
Dir.mktmpdir do |tmp_dir|
# create dir for archive
arch_dir = "#{tmp_dir}/protocols"
Dir.mkdir(arch_dir)
# create xml envelope document
envelope_xml = "<envelope xmlns=\"http://www.scinote.net\" version=\"1.0\">\n"
protocols.each do |protocol|
protocol_guid = get_guid(protocol.id)
# Create a folder for this protocol
protocol_dir = "#{arch_dir}/#{protocol_guid}"
Dir.mkdir(protocol_dir)
# Protocol
protocol_xml = "<eln xmlns=\"http://www.scinote.net\" version=\"1.0\">\n"
protocol_xml << "<protocol id=\"#{protocol.id}\" guid=\"#{protocol_guid}\">\n"
protocol_xml << "<name>#{protocol.name}</name>\n"
protocol_xml << "<authors>#{protocol.authors}</authors>\n"
protocol_xml << "<description>#{protocol.description}</description>\n"
protocol_xml << "<created_at>#{protocol.created_at}</created_at>\n"
protocol_xml << "<updated_at>#{protocol.updated_at}</updated_at>\n"
# Steps
if protocol.steps
protocol_xml << "<steps>\n"
protocol.steps.each do |step|
step_guid = get_guid(step.id)
step_xml = "<step id=\"#{step.id}\" guid=\"#{step_guid}\" position=\"#{step.position}\">\n"
step_xml << "<name>#{step.name}</name>\n"
step_xml << "<description>#{step.description}</description>\n"
# Assets
if step.assets
p step.id
p "assets"
step_xml << '<assets>'
step.assets.each do |asset|
asset_guid = get_guid(asset.id)
# create dir in protocol dir for assets
step_dir = "#{protocol_dir}/#{step_guid}"
p step_dir
Dir.mkdir(step_dir)
# create file for asset inside step dir
asset_file_name = "#{step_dir}/#{asset_guid}#{File.extname(asset.file_file_name)}"
output_file = File.open(asset_file_name, 'wb')
input_file = asset.open
p 'opened'
# p input_file
# puts `ls -la /tmp`
#output_file.write(input_file.read)
while buffer = input_file.read(4096)
output_file.write(buffer)
end
input_file.close
p output_file.size
asset_xml = "<asset id=\"#{asset.id}\" guid=\"#{asset_guid}\" fileRef=\"#{asset_file_name}\">\n"
asset_xml << "<fileName>#{asset.file_file_name}</fileName>\n"
asset_xml << "<fileType>#{asset.file_content_type}</fileType>\n"
asset_xml << "</asset>\n"
step_xml << asset_xml
end
step_xml << "</assets>\n"
end
# Tables
if step.tables
step_xml << "<elnTables>\n"
step.tables.each do |table|
table_xml = "<elnTable id=\"#{table.id}\" guid=\"#{get_guid(table.id)}\">\n"
table_xml << "<contents>#{table.contents.unpack('H*')[0]}</contents>\n"
table_xml << "</elnTable>\n"
step_xml << table_xml
end
step_xml << "</elnTables>\n"
end
# Checklists
if step.checklists
step_xml << "<checklists>\n"
step.checklists.each do |checklist|
checklist_xml = "<checklist id=\"#{checklist.id}\" guid=\"#{get_guid(checklist.id)}\">\n"
checklist_xml << "<name>#{checklist.name}</name>\n"
if checklist.items
checklist_xml << "<items>\n"
checklist.items.each do |item|
item_xml = "<item id=\"#{item.id}\" guid=\"#{get_guid(item.id)}\" position=\"#{item.position}\">\n"
item_xml << "<text>#{item.text}</text>\n"
item_xml << "</item>\n"
checklist_xml << item_xml
end
checklist_xml << "</items>\n"
end
checklist_xml << "</checklist>\n"
step_xml << checklist_xml
end
step_xml << "</checklists>\n"
end
protocol_xml << step_xml
protocol_xml << "</step>\n"
end
protocol_xml << "</steps>\n"
end
protocol_xml << "</protocol>\n"
protocol_xml << '</eln>'
p protocol_xml
# Okay, we have a generated XML for
# individual protocol, save it to protocol folder
output_file = File.open("#{protocol_dir}/eln.xml", 'w')
output_file.write(protocol_xml)
output_file.close
# Add protocol to the envelope
envelope_xml << "<protocol id=\"#{protocol.id}\" guid=\"#{protocol_guid}\">#{protocol.name}</protocol>\n"
end
envelope_xml << "</envelope>\n"
# Save envelope to root directory in archive
output_file = File.open("#{arch_dir}/scinote.xml", 'w')
output_file.write(envelope_xml)
output_file.close
output_file = File.open("#{arch_dir}/scinote.xsd", 'w')
output_file.write(generate_envelope_xsd)
output_file.close
output_file = File.open("#{arch_dir}/eln.xsd", 'w')
output_file.write(generate_eln_xsd)
output_file.close
puts `ls -la #{arch_dir}`
# Now make zip output stream and send it to the client
zip_name = "#{tmp_dir}/protocol.eln"
Zip::File.open(zip_name, Zip::File::CREATE) do |zipfile|
zipfile.add("eln.xsd", "#{arch_dir}/eln.xsd")
zipfile.add("eln.xsd", "#{arch_dir}/scinote.xml")
zipfile.add("eln.xsd", "#{arch_dir}/scinote.xsd")
end
end
end
private
def get_guid(id)
@ -160,9 +14,120 @@ module ProtocolsExporter
str1 + str2n
end
def get_protocol_name(protocol)
## "Inject" module's name
if protocol.in_module? && protocol.name.blank?
protocol_name = protocol.my_module.name
else
protocol_name = protocol.name
end
protocol_name
end
def generate_envelope_xml(protocols)
envelope_xml = "<envelope xmlns=\"http://www.scinote.net\" " \
"version=\"1.0\">\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.0\">\n"
protocol_xml << "<protocol id=\"#{protocol.id}\" " \
"guid=\"#{get_guid(protocol.id)}\">\n"
protocol_xml << "<name>#{protocol_name}</name>\n"
protocol_xml << "<authors>#{protocol.authors}</authors>\n"
protocol_xml << "<description>#{protocol.description}</description>\n"
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.count > 0
protocol_xml << "<steps>\n"
protocol.steps.order(:id).each do |step|
step_guid = get_guid(step.id)
step_xml = "<step id=\"#{step.id}\" guid=\"#{step_guid}\" " \
"position=\"#{step.position}\">\n"
step_xml << "<name>#{step.name}</name>\n"
step_xml << "<description>#{step.description}</description>\n"
# Assets
if step.assets.count > 0
step_xml << "<assets>\n"
step.assets.order(:id).each do |asset|
asset_guid = get_guid(asset.id)
asset_file_name = "#{asset_guid}" \
"#{File.extname(asset.file_file_name)}"
asset_xml = "<asset id=\"#{asset.id}\" guid=\"#{asset_guid}\" " \
"fileRef=\"#{asset_file_name}\">\n"
asset_xml << "<fileName>#{asset.file_file_name}</fileName>\n"
asset_xml << "<fileType>#{asset.file_content_type}</fileType>\n"
asset_xml << "</asset>\n"
step_xml << asset_xml
end
step_xml << "</assets>\n"
end
# Tables
if step.tables.count > 0
step_xml << "<elnTables>\n"
step.tables.order(:id).each do |table|
table_xml = "<elnTable id=\"#{table.id}\" " \
"guid=\"#{get_guid(table.id)}\">\n"
table_xml << "<contents>#{table.contents.unpack('H*')[0]}" \
"</contents>\n"
table_xml << "</elnTable>\n"
step_xml << table_xml
end
step_xml << "</elnTables>\n"
end
# Checklists
if step.checklists.count > 0
step_xml << "<checklists>\n"
step.checklists.order(:id).each do |checklist|
checklist_xml = "<checklist id=\"#{checklist.id}\" " \
"guid=\"#{get_guid(checklist.id)}\">\n"
checklist_xml << "<name>#{checklist.name}</name>\n"
if checklist.items
checklist_xml << "<items>\n"
checklist.items.each do |item|
item_xml = "<item id=\"#{item.id}\" " \
"guid=\"#{get_guid(item.id)}\" " \
"position=\"#{item.position}\">\n"
item_xml << "<text>#{item.text}</text>\n"
item_xml << "</item>\n"
checklist_xml << item_xml
end
checklist_xml << "</items>\n"
end
checklist_xml << "</checklist>\n"
step_xml << checklist_xml
end
step_xml << "</checklists>\n"
end
protocol_xml << step_xml
protocol_xml << "</step>\n"
end
protocol_xml << "</steps>\n"
end
protocol_xml << "</protocol>\n"
protocol_xml << '</eln>'
protocol_xml
end
def generate_envelope_xsd
envelope_xsd = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
envelope_xsd << "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" elementFormDefault=\"qualified\" attributeFormDefault=\"unqualified\">\n"
envelope_xsd << "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"" \
" elementFormDefault=\"qualified\" " \
"attributeFormDefault=\"unqualified\">\n"
envelope_xsd << "<xs:element name=\"envelope\">\n"
envelope_xsd << "<xs:complexType>\n"
envelope_xsd << "<xs:sequence>\n"
@ -170,15 +135,19 @@ module ProtocolsExporter
envelope_xsd << "<xs:complexType>\n"
envelope_xsd << "<xs:simpleContent>\n"
envelope_xsd << "<xs:extension base=\"xs:string\">\n"
envelope_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" use=\"required\"></xs:attribute>\n"
envelope_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" use=\"required\"></xs:attribute>\n"
envelope_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
"use=\"required\"></xs:attribute>\n"
envelope_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
envelope_xsd << "</xs:extension>\n"
envelope_xsd << "</xs:simpleContent>\n"
envelope_xsd << "</xs:complexType>\n"
envelope_xsd << "</xs:element>\n"
envelope_xsd << "</xs:sequence>\n"
envelope_xsd << "<xs:attribute name=\"xmlns\" type=\"xs:string\" use=\"required\"></xs:attribute>\n"
envelope_xsd << "<xs:attribute name=\"version\" type=\"xs:string\" use=\"required\"></xs:attribute>\n"
envelope_xsd << "<xs:attribute name=\"xmlns\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
envelope_xsd << "<xs:attribute name=\"version\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
envelope_xsd << "</xs:complexType>\n"
envelope_xsd << "</xs:element>\n"
envelope_xsd << "</xs:schema>\n"
@ -187,7 +156,9 @@ module ProtocolsExporter
def generate_eln_xsd
eln_xsd = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
eln_xsd << "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" elementFormDefault=\"qualified\" attributeFormDefault=\"unqualified\">\n"
eln_xsd << "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" " \
"elementFormDefault=\"qualified\" " \
"attributeFormDefault=\"unqualified\">\n"
eln_xsd << "<xs:element name=\"eln\">\n"
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:all>\n"
@ -195,10 +166,14 @@ module ProtocolsExporter
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:all>\n"
eln_xsd << "<xs:element name=\"name\" type=\"xs:string\"></xs:element>\n"
eln_xsd << "<xs:element name=\"authors\" type=\"xs:string\" minOccurs=\"0\"></xs:element>\n"
eln_xsd << "<xs:element name=\"description\" type=\"xs:string\" minOccurs=\"0\"></xs:element>\n"
eln_xsd << "<xs:element name=\"created_at\" type=\"xs:string\"></xs:element>\n"
eln_xsd << "<xs:element name=\"updated_at\" type=\"xs:string\"></xs:element>\n"
eln_xsd << "<xs:element name=\"authors\" type=\"xs:string\" " \
"minOccurs=\"0\"></xs:element>\n"
eln_xsd << "<xs:element name=\"description\" type=\"xs:string\" " \
"minOccurs=\"0\"></xs:element>\n"
eln_xsd << "<xs:element name=\"created_at\" " \
"type=\"xs:string\"></xs:element>\n"
eln_xsd << "<xs:element name=\"updated_at\" " \
"type=\"xs:string\"></xs:element>\n"
eln_xsd << "<xs:element name=\"steps\" minOccurs=\"0\">\n"
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:sequence>\n"
@ -206,7 +181,8 @@ module ProtocolsExporter
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:all>\n"
eln_xsd << "<xs:element name=\"name\" type=\"xs:string\"></xs:element>\n"
eln_xsd << "<xs:element name=\"description\" type=\"xs:string\" minOccurs=\"0\"></xs:element>\n"
eln_xsd << "<xs:element name=\"description\" type=\"xs:string\" " \
"minOccurs=\"0\"></xs:element>\n"
eln_xsd << "<xs:element name=\"checklists\" minOccurs=\"0\">\n"
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:sequence>\n"
@ -222,17 +198,22 @@ module ProtocolsExporter
eln_xsd << "<xs:all>\n"
eln_xsd << "<xs:element name=\"text\" type=\"xs:string\"></xs:element>\n"
eln_xsd << "</xs:all>\n"
eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"position\" type=\"xs:int\" use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" use=\"required\">" \
"</xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"position\" type=\"xs:int\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:sequence>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:all>\n"
eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:sequence>\n"
@ -244,12 +225,17 @@ module ProtocolsExporter
eln_xsd << "<xs:element name=\"asset\" maxOccurs=\"unbounded\">\n"
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:all>\n"
eln_xsd << "<xs:element name=\"fileName\" type=\"xs:string\"></xs:element>\n"
eln_xsd << "<xs:element name=\"fileType\" type=\"xs:string\"></xs:element>\n"
eln_xsd << "<xs:element name=\"fileName\" " \
"type=\"xs:string\"></xs:element>\n"
eln_xsd << "<xs:element name=\"fileType\" " \
"type=\"xs:string\"></xs:element>\n"
eln_xsd << "</xs:all>\n"
eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"fileRef\" type=\"xs:string\" use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"fileRef\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:sequence>\n"
@ -261,111 +247,44 @@ module ProtocolsExporter
eln_xsd << "<xs:element name=\"elnTable\" maxOccurs=\"unbounded\">\n"
eln_xsd << "<xs:complexType>\n"
eln_xsd << "<xs:all>\n"
eln_xsd << "<xs:element name=\"contents\" type=\"xs:string\"></xs:element>\n"
eln_xsd << "<xs:element name=\"contents\" " \
"type=\"xs:string\"></xs:element>\n"
eln_xsd << "</xs:all>\n"
eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:sequence>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:all>\n"
eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"position\" type=\"xs:int\" use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"position\" type=\"xs:int\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:sequence>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:all>\n"
eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << "</xs:all>\n"
eln_xsd << "<xs:attribute name=\"xmlns\" type=\"xs:string\" use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"version\" type=\"xs:string\" use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"xmlns\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "<xs:attribute name=\"version\" type=\"xs:string\" " \
"use=\"required\"></xs:attribute>\n"
eln_xsd << "</xs:complexType>\n"
eln_xsd << "</xs:element>\n"
eln_xsd << '</xs:schema>'
end
def export_protocol(protocol)
protocol_json = protocol.as_json(only: [
:id,
:name,
:description,
:authors,
:created_at,
:updated_at
])
# "Inject" module's name
if protocol.in_module? && protocol.name.blank?
protocol_json["name"] = protocol.my_module.name
end
protocol_json["steps"] = []
protocol.steps.find_each do |step|
step_json = step.as_json(only: [
:id,
:name,
:description,
:position
])
step_json["tables"] = []
step.tables.find_each do |table|
table_json = table.as_json(only: [:id, :name])
table_json["contents"] = table.contents.unpack("H*")[0]
step_json["tables"] << table_json
end
step_json["assets"] = []
step.assets.find_each do |asset|
asset_json = asset.as_json(only: [
:id,
:file_file_name,
:file_content_type
])
asset_json["fileName"] = asset_json.delete("file_file_name")
asset_json["fileType"] = asset_json.delete("file_content_type")
# Retrieve file contents
file = asset.open
asset_json["bytes"] = Base64.encode64(file.read)
file.close
step_json["assets"] << asset_json
end
step_json["checklists"] = []
step.checklists.find_each do |checklist|
checklist_json = checklist.as_json(only: [
:id,
:name
])
checklist_json["items"] = []
checklist.checklist_items.find_each do |item|
item_json = item.as_json(only: [
:id,
:text,
:position
])
checklist_json["items"] << item_json
end
step_json["checklists"] << checklist_json
end
protocol_json["steps"] << step_json
end
return protocol_json
eln_xsd << "</xs:schema>\n"
end
end

View file

@ -24,7 +24,7 @@
</ul>
</div>
<% if can_export_protocol_from_module(@my_module) %>
<a href="#" class="btn btn-default" data-action="export" data-id="<%= @protocol.id %>"><span class="glyphicon glyphicon-export"></span><span class="hidden-xs-custom">&nbsp;<%= t("my_modules.protocols.buttons.export") %></span></a>
<%= link_to raw("<span class=\"glyphicon glyphicon-export\"></span><span class=\"hidden-xs-custom\">&nbsp;" + t('my_modules.protocols.buttons.export') + "</span>"), export_protocols_path(protocol_ids: @protocol.id), class: "btn btn-default" %>
<% else %>
<a href="#" class="btn btn-default disabled"><span class="glyphicon glyphicon-export"></span><span class="hidden-xs-custom">&nbsp;<%= t("my_modules.protocols.buttons.export") %></span></a>
<% end %>