mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-12-25 01:03:18 +08:00
Initial implementation of protocol processing modal [SCI-8661] (#5683)
* Initial implementation of protocol processing modal [SCI-8661] * Prepare placeholder jobs and integrate notifications [SCI-8661]
This commit is contained in:
parent
940ca1f096
commit
4a8582ebc8
11 changed files with 231 additions and 48 deletions
|
@ -667,28 +667,33 @@ var ProtocolsIndex = (function() {
|
||||||
var importUrl = fileInput.attr('data-import-url');
|
var importUrl = fileInput.attr('data-import-url');
|
||||||
var teamId = fileInput.attr('data-team-id');
|
var teamId = fileInput.attr('data-team-id');
|
||||||
var type = fileInput.attr('data-type');
|
var type = fileInput.attr('data-type');
|
||||||
importProtocolFromFile(
|
|
||||||
ev.target.files[0],
|
|
||||||
importUrl,
|
|
||||||
{ team_id: teamId, type: type },
|
|
||||||
false,
|
|
||||||
function(datas) {
|
|
||||||
var nrSuccessful = 0;
|
|
||||||
_.each(datas, function(data) {
|
|
||||||
if (data.status === 'ok') {
|
|
||||||
nrSuccessful += 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
animateSpinner(null, false);
|
|
||||||
|
|
||||||
if (nrSuccessful) {
|
if(ev.target.files[0].name.split('.').pop() === 'eln') {
|
||||||
HelperModule.flashAlertMsg(I18n.t('protocols.index.import_results.message_ok_html', { count: nrSuccessful }), 'success');
|
importProtocolFromFile(
|
||||||
reloadTable();
|
ev.target.files[0],
|
||||||
} else {
|
importUrl,
|
||||||
HelperModule.flashAlertMsg(I18n.t('protocols.index.import_results.message_failed'), 'danger');
|
{ team_id: teamId, type: type },
|
||||||
|
false,
|
||||||
|
function(datas) {
|
||||||
|
var nrSuccessful = 0;
|
||||||
|
_.each(datas, function(data) {
|
||||||
|
if (data.status === 'ok') {
|
||||||
|
nrSuccessful += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
animateSpinner(null, false);
|
||||||
|
|
||||||
|
if (nrSuccessful) {
|
||||||
|
HelperModule.flashAlertMsg(I18n.t('protocols.index.import_results.message_ok_html', { count: nrSuccessful }), 'success');
|
||||||
|
reloadTable();
|
||||||
|
} else {
|
||||||
|
HelperModule.flashAlertMsg(I18n.t('protocols.index.import_results.message_failed'), 'danger');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
);
|
||||||
);
|
} else {
|
||||||
|
protocolFileImportModal.init(ev.target.files, reloadTable);
|
||||||
|
}
|
||||||
$(this).val('');
|
$(this).val('');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -591,7 +591,6 @@ class ProtocolsController < ApplicationController
|
||||||
message_items: {
|
message_items: {
|
||||||
protocol: protocol.id
|
protocol: protocol.id
|
||||||
})
|
})
|
||||||
generate_import_protocol_notification(current_user, protocol)
|
|
||||||
format.json do
|
format.json do
|
||||||
render json: { status: :ok }, status: :ok
|
render json: { status: :ok }, status: :ok
|
||||||
end
|
end
|
||||||
|
@ -918,6 +917,11 @@ class ProtocolsController < ApplicationController
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def import_docx
|
||||||
|
@job = Protocols::DocxImportJob.perform_later(params[:files], current_user)
|
||||||
|
render json: { job_id: @job.job_id }
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_importer
|
def set_importer
|
||||||
|
@ -929,30 +933,6 @@ class ProtocolsController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_import_protocol_notification(user, protocol)
|
|
||||||
protocol_download_link = "<a data-id='#{protocol.id}' " \
|
|
||||||
"data-turbolinks='false' " \
|
|
||||||
"href='#{Rails.application
|
|
||||||
.routes
|
|
||||||
.url_helpers
|
|
||||||
.export_protocols_path(protocol_ids: [protocol.id])}'>" \
|
|
||||||
"#{export_protocol_file_name([protocol])}</a>"
|
|
||||||
|
|
||||||
notification = Notification.create(
|
|
||||||
type_of: :deliver,
|
|
||||||
title: I18n.t('protocols.import_export.import_protocol_notification.title', link: protocol_download_link),
|
|
||||||
message: "#{I18n.t('protocols.import_export.import_protocol_notification.message')} <a data-id='#{protocol.id}' " \
|
|
||||||
"data-turbolinks='false' " \
|
|
||||||
"href='#{Rails.application
|
|
||||||
.routes
|
|
||||||
.url_helpers
|
|
||||||
.protocol_path(protocol)}'>" \
|
|
||||||
"#{protocol.name}</a>"
|
|
||||||
)
|
|
||||||
|
|
||||||
UserNotification.create(notification: notification, user: user)
|
|
||||||
end
|
|
||||||
|
|
||||||
def export_protocol_file_name(protocols)
|
def export_protocol_file_name(protocols)
|
||||||
protocol_name = get_protocol_name(protocols[0])
|
protocol_name = get_protocol_name(protocols[0])
|
||||||
|
|
||||||
|
|
18
app/javascript/packs/vue/protocol_file_import_modal.js
Normal file
18
app/javascript/packs/vue/protocol_file_import_modal.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import TurbolinksAdapter from 'vue-turbolinks';
|
||||||
|
import Vue from 'vue/dist/vue.esm';
|
||||||
|
import ProtocolFileImportModal from '../../vue/protocol_import/file_import_modal.vue';
|
||||||
|
|
||||||
|
|
||||||
|
Vue.use(TurbolinksAdapter);
|
||||||
|
Vue.prototype.i18n = window.I18n;
|
||||||
|
|
||||||
|
window.initProtocolFileImportModalComponent = () => {
|
||||||
|
new Vue({
|
||||||
|
el: '#protocolFileImportModal',
|
||||||
|
components: {
|
||||||
|
'protocol-file-import-modal': ProtocolFileImportModal
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
initProtocolFileImportModalComponent();
|
105
app/javascript/vue/protocol_import/file_import_modal.vue
Normal file
105
app/javascript/vue/protocol_import/file_import_modal.vue
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
<template>
|
||||||
|
<div ref="modal" class="modal fade" id="protcolFileImportModal" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
<h4 class="modal-title">{{ i18n.t(`protocols.import_modal.${state}.title`) }}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body text-xs" v-html="i18n.t(`protocols.import_modal.${state}.body_html`, { url: protocolUrl })">
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-default"
|
||||||
|
data-dismiss="modal">{{ i18n.t('protocols.import_modal.cancel') }}</button>
|
||||||
|
|
||||||
|
<button v-if="state === 'confirm'" type="button"
|
||||||
|
class="btn btn-primary"
|
||||||
|
@click.stop.prevent="confirm">{{ i18n.t('protocols.import_modal.import') }}</button>
|
||||||
|
<button v-else type="button"
|
||||||
|
class="btn btn-primary"
|
||||||
|
:disabled="state === 'in_progress'"
|
||||||
|
@click.stop.prevent="close">{{ i18n.t('protocols.import_modal.close') }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'ProtocolFileImportModal',
|
||||||
|
props: {
|
||||||
|
importUrl: { type: String, required: true}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
state: "confirm",
|
||||||
|
files: null,
|
||||||
|
jobPollInterval: null,
|
||||||
|
pollCount: 0,
|
||||||
|
jobId: null,
|
||||||
|
protocolUrl: null,
|
||||||
|
refreshCallback: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
window.protocolFileImportModal = this;
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open() {
|
||||||
|
$(this.$refs.modal).modal('show');
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
if (this.state === "done" && this.refreshCallback) {
|
||||||
|
this.refreshCallback();
|
||||||
|
}
|
||||||
|
$(this.$refs.modal).modal('hide');
|
||||||
|
},
|
||||||
|
init(files, refreshCallback) {
|
||||||
|
this.refreshCallback = refreshCallback;
|
||||||
|
this.pollCount = 0;
|
||||||
|
this.jobId = null;
|
||||||
|
this.files = files;
|
||||||
|
this.state = "confirm";
|
||||||
|
this.open();
|
||||||
|
},
|
||||||
|
confirm() {
|
||||||
|
$.post(this.importUrl, (data) => {
|
||||||
|
this.state = 'in_progress';
|
||||||
|
this.jobId = data.job_id
|
||||||
|
this.jobPollInterval = setInterval(this.fetchJobStatus, 1000);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchJobStatus() {
|
||||||
|
this.pollCount += 1;
|
||||||
|
|
||||||
|
if (this.pollCount > 4) {
|
||||||
|
this.state = 'not_yet_done';
|
||||||
|
clearInterval(this.jobPollInterval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.get(`/jobs/${this.jobId}/status`, (data) => {
|
||||||
|
let status = data.status;
|
||||||
|
|
||||||
|
console.log(data)
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case 'pending':
|
||||||
|
break;
|
||||||
|
case 'running':
|
||||||
|
break;
|
||||||
|
case 'done':
|
||||||
|
this.state = 'done';
|
||||||
|
clearInterval(this.jobPollInterval);
|
||||||
|
break;
|
||||||
|
case 'failed':
|
||||||
|
clearInterval(this.jobPollInterval);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
9
app/jobs/protocols/docx_import_job.rb
Normal file
9
app/jobs/protocols/docx_import_job.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Protocols
|
||||||
|
class DocxImportJob < ApplicationJob
|
||||||
|
def perform(files, user)
|
||||||
|
ProtocolImporters::DocxService.new(files, user).import!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
43
app/services/protocol_importers/docx_service.rb
Normal file
43
app/services/protocol_importers/docx_service.rb
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module ProtocolImporters
|
||||||
|
class DocxService
|
||||||
|
def initialize(files, user)
|
||||||
|
@files = files
|
||||||
|
@user = user
|
||||||
|
end
|
||||||
|
|
||||||
|
def import!
|
||||||
|
# TODO: Implement actual logic
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
@protocol = @user.current_team
|
||||||
|
.protocols
|
||||||
|
.create!(
|
||||||
|
name: "PARSED PROTOCOL #{rand * 10000}",
|
||||||
|
protocol_type: :in_repository_draft,
|
||||||
|
added_by: @user
|
||||||
|
)
|
||||||
|
create_notification!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_notification!
|
||||||
|
# TODO: Add proper protocol original file link
|
||||||
|
protocol_download_link = "<a data-id='#{@protocol.id}' " \
|
||||||
|
"data-turbolinks='false' " \
|
||||||
|
"href='#'>" \
|
||||||
|
"#{@protocol.name}</a>"
|
||||||
|
|
||||||
|
notification = Notification.create(
|
||||||
|
type_of: :deliver,
|
||||||
|
title: I18n.t('protocols.import_export.import_protocol_notification.title', link: protocol_download_link),
|
||||||
|
message: "#{I18n.t('protocols.import_export.import_protocol_notification.message')} " \
|
||||||
|
"<a data-id='#{@protocol.id}' data-turbolinks='false' " \
|
||||||
|
"href='#{Rails.application.routes.url_helpers.protocol_path(@protocol)}'>" \
|
||||||
|
"#{@protocol.name}</a>"
|
||||||
|
)
|
||||||
|
|
||||||
|
UserNotification.create(notification: notification, user: @user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -34,6 +34,11 @@
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
<div id="protocolFileImportModal">
|
||||||
|
<protocol-file-import-modal import-url="<%= import_docx_protocols_path %>" />
|
||||||
|
</div>
|
||||||
|
<%= javascript_include_tag 'vue_protocol_file_import_modal' %>
|
||||||
|
|
||||||
<div id="protocolsio-preview-modal-target"></div>
|
<div id="protocolsio-preview-modal-target"></div>
|
||||||
<%= render partial: "protocols/index/general_toolbar.html.erb" %>
|
<%= render partial: "protocols/index/general_toolbar.html.erb" %>
|
||||||
<%= render partial: "protocols/index/protocol_filters.html.erb" %>
|
<%= render partial: "protocols/index/protocol_filters.html.erb" %>
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
<li>
|
<li>
|
||||||
<a class="btn-link-alt btn-secondary-link btn-open-file" <%= can_create_protocols_in_repository?(@current_team) ? 'data-action="import"' : 'disabled="disabled"' %>>
|
<a class="btn-link-alt btn-secondary-link btn-open-file" <%= can_create_protocols_in_repository?(@current_team) ? 'data-action="import"' : 'disabled="disabled"' %>>
|
||||||
<span><%= t("protocols.index.import_alt") %></span>
|
<span><%= t("protocols.index.import_alt") %></span>
|
||||||
<input type="file" value="" accept=".eln" data-role="import-file-input"
|
<input type="file" value="" accept=".eln,.docx" data-role="import-file-input"
|
||||||
data-team-id="<%= @current_team.id %>" data-import-url="<%= import_protocols_path %>">
|
data-team-id="<%= @current_team.id %>" data-import-url="<%= import_protocols_path %>">
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -2684,6 +2684,22 @@ en:
|
||||||
button: "Rearrange steps"
|
button: "Rearrange steps"
|
||||||
modal:
|
modal:
|
||||||
title: "Rearrange protocol steps"
|
title: "Rearrange protocol steps"
|
||||||
|
import_modal:
|
||||||
|
confirm:
|
||||||
|
title: "Import protocol"
|
||||||
|
body_html: "You will start importing selected .docx file into SciNote. The import time may vary according to the size of a file and internet connection."
|
||||||
|
in_progress:
|
||||||
|
title: "Importing in progress"
|
||||||
|
body_html: "The process of importing has started. Please wait..."
|
||||||
|
not_yet_done:
|
||||||
|
title: "Importing in progress"
|
||||||
|
body_html: "The import is taking longer than expected. You'll get a notice in <i class=\"sn-icon sn-icon-notifications\"></i> Notifications when your protocol template is ready. You can close the window and continue with your work."
|
||||||
|
done:
|
||||||
|
title: "Importing completed"
|
||||||
|
body_html: 'The protocol has been successfully imported. You can access the draft of protocol template <a href="%{url}">here.</a>'
|
||||||
|
import: "Import"
|
||||||
|
cancel: "Cancel"
|
||||||
|
close: "Close"
|
||||||
print:
|
print:
|
||||||
title: "Print protocol"
|
title: "Print protocol"
|
||||||
button: "Print"
|
button: "Print"
|
||||||
|
@ -2866,7 +2882,7 @@ en:
|
||||||
edit: "Edit"
|
edit: "Edit"
|
||||||
clone_btn: "Copy"
|
clone_btn: "Copy"
|
||||||
import: "Import"
|
import: "Import"
|
||||||
import_alt: "From local file"
|
import_alt: "From file (.docx, .eln)"
|
||||||
import_protocols_io: "From Protocols.io"
|
import_protocols_io: "From Protocols.io"
|
||||||
modal_import_json_upload: "Upload"
|
modal_import_json_upload: "Upload"
|
||||||
modal_import_json_title: "Import protocols.io file"
|
modal_import_json_title: "Import protocols.io file"
|
||||||
|
|
|
@ -654,6 +654,7 @@ Rails.application.routes.draw do
|
||||||
post 'restore', to: 'protocols#restore'
|
post 'restore', to: 'protocols#restore'
|
||||||
post 'clone', to: 'protocols#clone'
|
post 'clone', to: 'protocols#clone'
|
||||||
post 'import', to: 'protocols#import'
|
post 'import', to: 'protocols#import'
|
||||||
|
post 'import_docx', to: 'protocols#import_docx'
|
||||||
post 'protocolsio_import_create',
|
post 'protocolsio_import_create',
|
||||||
to: 'protocols#protocolsio_import_create'
|
to: 'protocols#protocolsio_import_create'
|
||||||
post 'protocolsio_import_save', to: 'protocols#protocolsio_import_save'
|
post 'protocolsio_import_save', to: 'protocols#protocolsio_import_save'
|
||||||
|
|
|
@ -34,7 +34,8 @@ const entryList = {
|
||||||
vue_repository_assign_items_to_task_modal: './app/javascript/packs/vue/assign_items_to_task_modal.js',
|
vue_repository_assign_items_to_task_modal: './app/javascript/packs/vue/assign_items_to_task_modal.js',
|
||||||
vue_navigation_top_menu: './app/javascript/packs/vue/navigation/top_menu.js',
|
vue_navigation_top_menu: './app/javascript/packs/vue/navigation/top_menu.js',
|
||||||
vue_navigation_navigator: './app/javascript/packs/vue/navigation/navigator.js',
|
vue_navigation_navigator: './app/javascript/packs/vue/navigation/navigator.js',
|
||||||
vue_components_action_toolbar: './app/javascript/packs/vue/action_toolbar.js'
|
vue_components_action_toolbar: './app/javascript/packs/vue/action_toolbar.js',
|
||||||
|
vue_protocol_file_import_modal: './app/javascript/packs/vue/protocol_file_import_modal.js'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Engine pack loading based on https://github.com/rails/webpacker/issues/348#issuecomment-635480949
|
// Engine pack loading based on https://github.com/rails/webpacker/issues/348#issuecomment-635480949
|
||||||
|
|
Loading…
Reference in a new issue