Add flash message and notification for generated reports [SCI-5552]

This commit is contained in:
Oleksii Kriuchykhin 2021-04-14 15:45:51 +02:00
parent 6695c00d4f
commit 0a9a656313
24 changed files with 117 additions and 58 deletions

View file

@ -1,4 +1,4 @@
/* global I18n DataTableHelpers animateSpinner */
/* global I18n DataTableHelpers animateSpinner HelperModule */
(function() {
'use strict';
@ -113,6 +113,8 @@
$(row).addClass('report-row')
.attr('data-edit-path', data.edit)
.attr('data-status-path', data.status)
.attr('data-generate-pdf-path', data.generate_pdf)
.attr('data-generate-docx-path', data.generate_docx)
.attr('data-retry-count', 0)
.attr('data-id', data['0']);
if (data['3'].processing || data['4'].processing) {
@ -244,6 +246,38 @@
});
}
function initGeneratePDFReport() {
$('.generate-pdf').click(function(ev) {
ev.stopPropagation();
ev.preventDefault();
animateSpinner();
if (CHECKED_REPORTS.length === 1) {
let row = $(".report-row[data-id='" + CHECKED_REPORTS[0] + "']");
$.post(row.data('generate-pdf-path'), function(response) {
animateSpinner(null, false);
HelperModule.flashAlertMsg(response.message, 'success');
setTimeout(() => { checkProcessingStatus(row.data('id')); }, START_POLLING_INTERVAL);
});
}
});
}
function initGenerateDocxReport() {
$('.generate-docx').click(function(ev) {
ev.stopPropagation();
ev.preventDefault();
animateSpinner();
if (CHECKED_REPORTS.length === 1) {
let row = $(".report-row[data-id='" + CHECKED_REPORTS[0] + "']");
$.post(row.data('generate-docx-path'), function(response) {
animateSpinner(null, false);
HelperModule.flashAlertMsg(response.message, 'success');
setTimeout(() => { checkProcessingStatus(row.data('id')); }, START_POLLING_INTERVAL);
});
}
});
}
function initEditReport() {
$('#edit-report-btn').click(function(e) {
e.preventDefault();
@ -271,6 +305,8 @@
}
initDatatable();
initGeneratePDFReport();
initGenerateDocxReport();
initEditReport();
initDeleteReports();
}());

View file

@ -17,6 +17,8 @@ $brand-primary-light: #7094cb;
$brand-academy: #a52068;
$brand-academy-dark: #8c1b58;
$brand-accent: #a52068;
$brand-focus: #609fff;
$brand-focus-light: #dfecff;

View file

@ -19,8 +19,8 @@
}
#count-notifications {
background-color: $brand-primary;
border-radius: 5px;
background-color: $brand-accent;
border-radius: 8px;
color: $color-concrete;
display: none;
font-size: 11px;

View file

@ -1,15 +1,13 @@
class ReportsController < ApplicationController
include TeamsHelper
include ReportActions
# Ignore CSRF protection just for PDF generation (because it's
# used via target='_blank')
protect_from_forgery with: :exception, except: :generate
BEFORE_ACTION_METHODS = %i(
create
edit
update
generate
generate_pdf
generate_docx
save_modal
project_contents
experiment_contents_modal
@ -22,7 +20,7 @@ class ReportsController < ApplicationController
result_contents
).freeze
before_action :load_vars, only: %i(edit update document_preview generate status)
before_action :load_vars, only: %i(edit update document_preview generate_pdf generate_docx status)
before_action :load_vars_nested, only: BEFORE_ACTION_METHODS
before_action :load_visible_projects, only: %i(new edit)
before_action :load_available_repositories,
@ -188,21 +186,27 @@ class ReportsController < ApplicationController
end
end
# Generation action
def generate
# Generation actions
def generate_pdf
respond_to do |format|
format.pdf do
render pdf: 'report', header: { html: { template: 'reports/header.pdf.erb' }},
footer: { html: { template: 'reports/footer.pdf.erb',
locals: { current_time: I18n.l(Time.zone.now, format: :full) }}},
locals: { content: content },
template: 'reports/report.pdf.erb',
disable_javascript: true
format.json do
@report.update!(pdf_file_processing: true)
Reports::PdfJob.perform_later(@report, 'template_1', current_user)
render json: {
message: I18n.t('projects.reports.index.generation.accepted_message')
}
end
end
end
def generate_docx
respond_to do |format|
format.json do
@report.update!(docx_file_processing: true)
Reports::DocxJob.perform_now(@report, params[:data], current_user, current_team, root_url)
render json: {}, status: :accepted
Reports::DocxJob.perform_later(@report, current_user, current_team, root_url)
render json: {
message: I18n.t('projects.reports.index.generation.accepted_message')
}
end
end
end

View file

@ -61,7 +61,9 @@ class ReportDatatable < CustomDatatable
'7' => I18n.l(record.created_at, format: :full),
'8' => I18n.l(record.updated_at, format: :full),
'edit' => edit_project_report_path(record.project_id, record.id),
'status' => status_project_report_path(record.project_id, record.id)
'status' => status_project_report_path(record.project_id, record.id),
'generate_pdf' => generate_pdf_project_report_path(record.project_id, record.id),
'generate_docx' => generate_docx_project_report_path(record.project_id, record.id)
}
end
end

View file

@ -2,16 +2,27 @@
module Reports
class DocxJob < ApplicationJob
include InputSanitizeHelper
queue_as :reports
def perform(report, data, user, team, root_url)
def perform(report, user, team, root_url)
file = Tempfile.new(['report', '.docx'])
begin
docx = Caracal::Document.new(file.path)
Reports::Docx.new(data, docx, user: user, team: team, scinote_url: root_url).draw
Reports::Docx.new(report, docx, user: user, team: team, scinote_url: root_url).draw
docx.save
report.docx_file.attach(io: file, filename: 'report.docx')
report.update!(docx_file_processing: false)
report_path = Rails.application.routes.url_helpers.reports_path
notification = Notification.create(
type_of: :deliver,
title: I18n.t('projects.reports.index.generation.completed_notification_title'),
message: I18n.t('projects.reports.index.generation.completed_notification_message',
report_link: "<a href='#{report_path}'>#{sanitize_input(report.name)}</a>",
team_name: sanitize_input(report.team.name))
)
notification.create_user_notification(user)
ensure
file.close
file.unlink

View file

@ -16,8 +16,8 @@ class Reports::Docx
include "Reports::Docx::#{include_module}".constantize
end
def initialize(json, docx, options)
@json = JSON.parse(json)
def initialize(report, docx, options)
@report = report
@docx = docx
@user = options[:user]
@report_team = options[:team]
@ -29,8 +29,8 @@ class Reports::Docx
def draw
initial_document_load
@json.each do |subject|
public_send("draw_#{subject['type_of']}", subject)
@report.root_elements.each do |subject|
public_send("draw_#{subject.type_of}", subject)
end
@docx
end

View file

@ -5,7 +5,7 @@ module Reports::Docx::DrawExperiment
color = @color
link_style = @link_style
scinote_url = @scinote_url
experiment = Experiment.find_by(id: subject['id']['experiment_id'])
experiment = Experiment.find_by(id: subject.experiment_id)
return unless experiment && can_read_experiment?(@user, experiment)
@docx.h2 experiment.name, size: Constants::REPORT_DOCX_EXPERIMENT_TITLE_SIZE
@ -25,8 +25,8 @@ module Reports::Docx::DrawExperiment
Reports::HtmlToWordConverter.new(@docx, { scinote_url: scinote_url,
link_style: link_style }).html_to_word_converter(html)
@docx.p
subject['children'].each do |child|
public_send("draw_#{child['type_of']}", child, experiment)
subject.children.each do |child|
public_send("draw_#{child.type_of}", child, experiment)
end
end
end

View file

@ -5,7 +5,7 @@ module Reports::Docx::DrawMyModule
color = @color
link_style = @link_style
scinote_url = @scinote_url
my_module = experiment.my_modules.find_by(id: subject['id']['my_module_id'])
my_module = experiment.my_modules.find_by(id: subject.my_module_id)
tags = my_module.tags
return unless my_module
@ -73,8 +73,8 @@ module Reports::Docx::DrawMyModule
end
@docx.p
subject['children'].each do |child|
public_send("draw_#{child['type_of']}", child, my_module)
subject.children.each do |child|
public_send("draw_#{child.type_of}", child, my_module)
end
end
end

View file

@ -4,7 +4,7 @@ module Reports::Docx::DrawMyModuleActivity
def draw_my_module_activity(subject, my_module)
return unless my_module
activities = ActivitiesService.my_module_activities(my_module).order(created_at: subject['sort_order'])
activities = ActivitiesService.my_module_activities(my_module).order(created_at: subject.sort_order)
return false unless activities.any?

View file

@ -4,8 +4,7 @@ module Reports::Docx::DrawMyModuleRepository
def draw_my_module_repository(subject, my_module)
return unless my_module
repository_id = subject['id']['repository_id']
repository = ::RepositoryBase.find(repository_id)
repository = ::RepositoryBase.find(subject.repository_id)
repository_data = my_module.repository_docx_json(repository)
return false unless repository_data[:rows].any? && can_read_repository?(@user, repository)

View file

@ -2,7 +2,7 @@
module Reports::Docx::DrawProjectHeader
def draw_project_header(subject)
project = Project.find_by(id: subject['id']['project_id'])
project = Project.find_by(id: subject.project_id)
return unless project && can_read_project?(@user, project)
@docx.p I18n.t('projects.reports.elements.project_header.user_time',

View file

@ -2,7 +2,7 @@
module Reports::Docx::DrawResultAsset
def draw_result_asset(subject, my_module)
result = my_module.results.find_by(id: subject['id']['result_id'])
result = my_module.results.find_by(id: subject.result_id)
return unless result
asset = result.asset
@ -24,8 +24,8 @@ module Reports::Docx::DrawResultAsset
Reports::DocxRenderer.render_asset_image(@docx, asset) if asset.previewable? && !asset.list?
subject['children'].each do |child|
public_send("draw_#{child['type_of']}", child, result)
subject.children.each do |child|
public_send("draw_#{child.type_of}", child, result)
end
end
end

View file

@ -4,7 +4,7 @@ module Reports::Docx::DrawResultComments
def draw_result_comments(subject, result)
return unless result
comments = result.result_comments.order(created_at: subject['sort_order'])
comments = result.result_comments.order(created_at: subject.sort_order)
return if comments.count.zero?
@docx.p

View file

@ -2,7 +2,7 @@
module Reports::Docx::DrawResultTable
def draw_result_table(subject, my_module)
result = my_module.results.find_by(id: subject['id']['result_id'])
result = my_module.results.find_by(id: subject.result_id)
return unless result
table = result.table
@ -19,8 +19,8 @@ module Reports::Docx::DrawResultTable
timestamp: I18n.l(timestamp, format: :full), user: result.user.full_name), color: color[:gray]
end
@docx.table JSON.parse(table.contents_utf_8)['data'], border_size: Constants::REPORT_DOCX_TABLE_BORDER_SIZE
subject['children'].each do |child|
public_send("draw_#{child['type_of']}", child, result)
subject.children.each do |child|
public_send("draw_#{child.type_of}", child, result)
end
end
end

View file

@ -2,7 +2,7 @@
module Reports::Docx::DrawResultText
def draw_result_text(subject, my_module)
result = my_module.results.find_by(id: subject['id']['result_id'])
result = my_module.results.find_by(id: subject.result_id)
return unless result
result_text = result.result_text
@ -20,8 +20,8 @@ module Reports::Docx::DrawResultText
Reports::HtmlToWordConverter.new(@docx, { scinote_url: @scinote_url,
link_style: @link_style }).html_to_word_converter(html)
subject['children'].each do |child|
public_send("draw_#{child['type_of']}", child, result)
subject.children.each do |child|
public_send("draw_#{child.type_of}", child, result)
end
end
end

View file

@ -3,7 +3,7 @@
module Reports::Docx::DrawStep
def draw_step(subject, my_module)
color = @color
step = my_module.protocols.first.steps.find_by(id: subject['id']['step_id'])
step = my_module.protocols.first.steps.find_by(id: subject.step_id)
return unless step
step_type_str = step.completed ? 'completed' : 'uncompleted'
@ -33,8 +33,8 @@ module Reports::Docx::DrawStep
@docx.p I18n.t 'projects.reports.elements.step.no_description'
end
subject['children'].each do |child|
public_send("draw_#{child['type_of']}", child, step)
subject.children.each do |child|
public_send("draw_#{child.type_of}", child, step)
end
@docx.p
@docx.p

View file

@ -2,7 +2,7 @@
module Reports::Docx::DrawStepAsset
def draw_step_asset(subject, step)
asset = step.assets.find_by(id: subject['id']['asset_id'])
asset = step.assets.find_by(id: subject.asset_id)
return unless asset
timestamp = asset.created_at

View file

@ -4,7 +4,7 @@ module Reports::Docx::DrawStepChecklist
def draw_step_checklist(subject, step)
team = @report_team
user = @user
checklist = step.checklists.find_by(id: subject['id']['checklist_id'])
checklist = step.checklists.find_by(id: subject.checklist_id)
return unless checklist
items = checklist.checklist_items

View file

@ -2,7 +2,7 @@
module Reports::Docx::DrawStepTable
def draw_step_table(subject, step)
table = step.tables.find_by(id: subject['id']['table_id'])
table = step.tables.find_by(id: subject.table_id)
return unless table
color = @color

View file

@ -12,7 +12,7 @@
</button>
<ul id="reportMenuDropdown" class="dropdown-menu report-actions-menu" aria-labelledby="reportMenu">
<li>
<%= link_to '#', remote: true, id: 'updatePdf' do %>
<%= link_to '#', remote: true, id: 'updatePdf', class: 'generate-pdf' do %>
<i class="fas fa-file-pdf"></i>
<%= t("projects.reports.index.update_pdf") %>
<% end %>
@ -24,13 +24,13 @@
<% end %>
</li>
<li>
<%= link_to '#', remote: true, id: 'requestDocx' do %>
<%= link_to '#', remote: true, id: 'requestDocx', class: 'generate-docx' do %>
<i class="fas fa-file-word"></i>
<%= t("projects.reports.index.request_docx") %>
<% end %>
</li>
<li>
<%= link_to '#', remote: true, id: 'updateDocx' do %>
<%= link_to '#', remote: true, id: 'updateDocx', class: 'generate-docx' do %>
<i class="fas fa-file-word"></i>
<%= t("projects.reports.index.update_docx") %>
<% end %>

View file

@ -26,8 +26,8 @@
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
<li><%= link_to t("projects.reports.new.nav_pdf"), generate_project_report_path(@project, @report, format: :pdf), id: "get-report-pdf" %></li>
<li><%= link_to t("projects.reports.new.nav_docx"), generate_project_report_path(@project, @report, format: :json), id: "get-report-docx" %></li>
<li><%= link_to t("projects.reports.new.nav_pdf"), generate_pdf_project_report_path(@project, @report), id: "get-report-pdf" %></li>
<li><%= link_to t("projects.reports.new.nav_docx"), generate_docx_project_report_path(@project, @report), id: "get-report-docx" %></li>
</ul>
</div>

View file

@ -512,6 +512,10 @@ en:
error: "Error"
generating: "Generating"
generate: "Generate"
generation:
accepted_message: "Your report is succesfully added to the generator queue. We will notify you when it is done!"
completed_notification_title: "Your report .DOCX was generated successfully."
completed_notification_message: "Report: %{report_link} | Team: %{team_name}"
modal_delete:
head_title: "Delete report/s"
message: "Are you sure to delete selected report/s?"

View file

@ -250,7 +250,8 @@ Rails.application.routes.draw do
path: '/reports',
only: %i(edit update create) do
member do
post 'generate', to: 'reports#generate', format: %w(pdf json)
post 'generate_pdf', to: 'reports#generate_pdf'
post 'generate_docx', to: 'reports#generate_docx'
get 'status', to: 'reports#status', format: %w(json)
end