diff --git a/Gemfile b/Gemfile
index 710cbf891d..e68cc23127 100644
--- a/Gemfile
+++ b/Gemfile
@@ -47,8 +47,7 @@ gem 'aspector' # Aspect-oriented programming for Rails
gem 'auto_strip_attributes', '~> 2.1' # Removes unnecessary whitespaces AR
gem 'bcrypt', '~> 3.1.10'
# gem 'caracal'
-# gem 'caracal', git: 'https://github.com/scinote-eln/caracal.git', branch: 'rubyzip2' # Build docx report
-gem 'caracal_the_curve', '~> 1.4', '>= 1.4.6'
+gem 'caracal', git: 'https://github.com/scinote-eln/caracal.git', branch: 'custom-docx-reports' # Build docx report
gem 'caxlsx' # Build XLSX files
gem 'deface', '~> 1.9'
gem 'down', '~> 5.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 956bc482e4..4ed5dbebe6 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -14,6 +14,16 @@ GIT
docile (>= 1.1.0)
rails (>= 4)
+GIT
+ remote: https://github.com/scinote-eln/caracal.git
+ revision: 54c21353798569476a1eaa73b5fd3e275ac85419
+ branch: custom-docx-reports
+ specs:
+ caracal (1.4.2)
+ nokogiri (~> 1.6)
+ rubyzip (>= 2.3)
+ tilt (>= 1.4)
+
GIT
remote: https://github.com/scinote-eln/img2zpl
revision: 23d61cfc3e90ac4caa62dd08546fa0d7590a5140
@@ -210,10 +220,6 @@ GEM
capybara-email (3.0.2)
capybara (>= 2.4, < 4.0)
mail
- caracal_the_curve (1.4.6)
- nokogiri (~> 1.6)
- rubyzip (>= 1.1.0, < 3.0)
- tilt (>= 1.4)
case_transform (0.2)
activesupport
caxlsx (4.0.0)
@@ -790,7 +796,7 @@ DEPENDENCIES
canaid!
capybara
capybara-email
- caracal_the_curve (~> 1.4, >= 1.4.6)
+ caracal!
caxlsx
cssbundling-rails
cucumber-rails
diff --git a/app/assets/javascripts/reports/new.js b/app/assets/javascripts/reports/new.js
index ad280af9f7..bcc8ad5d39 100644
--- a/app/assets/javascripts/reports/new.js
+++ b/app/assets/javascripts/reports/new.js
@@ -968,6 +968,16 @@ function reportHandsonTableConverter() {
}
(function() {
+ function getSelectedRepositoryColumnValues(element, selectedAll = false) {
+ const values = [];
+ $(element).find('option').each((_, option) => {
+ if ($(option).attr('selected-value') || selectedAll) {
+ values.push(option.value);
+ }
+ });
+ return values;
+ }
+
function getReportData() {
var reportData = {};
@@ -982,7 +992,7 @@ function reportHandsonTableConverter() {
// Template values
reportData.template_values = {};
- $.each($('.report-template-values-container').find('.sci-input-field'), function(i, field) {
+ $.each($('.report-template-values-container').find('.sci-input-field').not('.report-template-value-dropdown'), (_, field) => {
if (field.value.length === 0) return;
reportData.template_values[field.name] = {
@@ -1046,12 +1056,24 @@ function reportHandsonTableConverter() {
reportData.report.settings.task[e.value] = e.checked;
});
reportData.report.settings.task.repositories = [];
- $.each($('.task-contents-container .repositories-contents .repositories-setting:checked'), function(i, e) {
- reportData.report.settings.task.repositories.push(parseInt(e.value, 10));
+ reportData.report.settings.task.excluded_repository_columns = {};
+
+ $.each($('.task-contents-container .repositories-contents .repositories-setting:checked'), (_, e) => {
+ const value = parseInt(e.value, 10);
+ const $repositoryColumn = $(e).parent().siblings('.repository-columns')[0];
+ const selectedValues = dropdownSelector.getValues($repositoryColumn);
+ const excludedValues = getSelectedRepositoryColumnValues($repositoryColumn, true)
+ .filter((item) => !selectedValues.includes(item))
+ .map((el) => parseInt(el, 10));
+ reportData.report.settings.task.repositories.push(value);
+ reportData.report.settings.task.excluded_repository_columns[value] = excludedValues;
});
reportData.report.settings.task.result_order = dropdownSelector.getValues('#taskResultsOrder');
+ reportData.report.settings.exclude_task_metadata = $('.exclude-task-metadata-setting')[0].checked;
+ reportData.report.settings.exclude_timestamps = $('.exclude-timestamps-setting')[0].checked;
+
return reportData;
}
@@ -1256,7 +1278,8 @@ function reportHandsonTableConverter() {
function reCheckContinueButton() {
if (dropdownSelector.getValues('#projectSelector').length > 0
&& dropdownSelector.getValues('#templateSelector').length > 0
- && dropdownSelector.getValues('#docxTemplateSelector').length > 0) {
+ && (dropdownSelector.getValues('#docxTemplateSelector').length > 0
+ || $('#docxTemplateSelector').closest('.hidden').length > 0)) {
$('.continue-button').attr('disabled', false);
} else {
$('.continue-button').attr('disabled', true);
@@ -1279,6 +1302,12 @@ function reportHandsonTableConverter() {
if (dropdownSelector.getValues('#projectSelector').length > 0) {
dropdownSelector.enableSelector('#templateSelector');
dropdownSelector.enableSelector('#docxTemplateSelector');
+ if ($('#templateSelector').data('defaultTemplate')) {
+ dropdownSelector.selectValues('#templateSelector', $('#templateSelector').data('defaultTemplate'));
+ }
+ if ($('#docxTemplateSelector').data('defaultTemplate')) {
+ dropdownSelector.selectValues('#docxTemplateSelector', $('#docxTemplateSelector').data('defaultTemplate'));
+ }
} else {
dropdownSelector.selectValues('#templateSelector', '');
dropdownSelector.disableSelector('#templateSelector');
@@ -1349,10 +1378,24 @@ function reportHandsonTableConverter() {
if (dropdownSelector.getValues('#docxTemplateSelector').length > 0) {
loadDocxTemplate();
}
+
+ $('.repository-columns').each((_, element) => {
+ const elementId = `#${$(element).attr('id')}`;
+ const elements = getSelectedRepositoryColumnValues(elementId);
+
+ dropdownSelector.init(elementId, {
+ selectAppearance: 'simple',
+ optionClass: 'checkbox-icon'
+ });
+
+ if (elements.length) {
+ dropdownSelector.selectValues(elementId, elements);
+ }
+ });
}
function loadTemplate() {
- let template = $('#templateSelector').val();
+ const template = dropdownSelector.getValues('#templateSelector');
let params = {
project_id: dropdownSelector.getValues('#projectSelector'),
template: template
@@ -1382,7 +1425,7 @@ function reportHandsonTableConverter() {
}
function loadDocxTemplate() {
- let template = $('#docxTemplateSelector').val();
+ const template = dropdownSelector.getValues('#docxTemplateSelector');
let params = {
project_id: dropdownSelector.getValues('#projectSelector'),
template: template
diff --git a/app/assets/javascripts/sitewide/dropdown_selector.js b/app/assets/javascripts/sitewide/dropdown_selector.js
index b78704270b..41902a5046 100644
--- a/app/assets/javascripts/sitewide/dropdown_selector.js
+++ b/app/assets/javascripts/sitewide/dropdown_selector.js
@@ -353,7 +353,7 @@ var dropdownSelector = (function() {
// If we setup Select All we draw it and add correspond logic
if (selectElement.data('select-all-button')) {
- $(`
${selectElement.data('select-all-button')}
`)
+ $(`${selectElement.data('select-all-button')}
`)
.appendTo(dropdownContainer.find('.dropdown-container'))
.click(() => {
// For AJAX dropdown we will use only "Deselect All"
diff --git a/app/assets/stylesheets/reports/new.scss b/app/assets/stylesheets/reports/new.scss
index 0355c40640..8b50742d8b 100644
--- a/app/assets/stylesheets/reports/new.scss
+++ b/app/assets/stylesheets/reports/new.scss
@@ -250,6 +250,25 @@
}
}
+ // scss-lint:disable ImportantRule
+ .dropdown-selector-container {
+ .dropdown-container {
+ left: auto !important;
+ margin: auto !important;
+ position: absolute !important;
+ }
+ }
+ // scss-lint:enable ImportantRule
+
+ .repositories-contents {
+ .dropdown-selector-container {
+ display: inline-flex;
+ flex-shrink: 0;
+ margin-left: auto;
+ width: 200px;
+ }
+ }
+
.project-selector-container {
background: $color-white;
box-shadow: $modal-shadow;
diff --git a/app/assets/stylesheets/shared/dropdown_selector.scss b/app/assets/stylesheets/shared/dropdown_selector.scss
index ef119838a8..21c9ff9e8c 100644
--- a/app/assets/stylesheets/shared/dropdown_selector.scss
+++ b/app/assets/stylesheets/shared/dropdown_selector.scss
@@ -175,6 +175,10 @@
top: 0;
width: 100%;
z-index: 5;
+
+ &:hover {
+ background: $color-concrete;
+ }
}
.dropdown-blank {
diff --git a/app/components/reports/repositories_input_component.rb b/app/components/reports/repositories_input_component.rb
index c7909d8c7d..f168cc3966 100644
--- a/app/components/reports/repositories_input_component.rb
+++ b/app/components/reports/repositories_input_component.rb
@@ -2,10 +2,9 @@
module Reports
class RepositoriesInputComponent < TemplateValueComponent
- def initialize(report:, name:, label:, placeholder: nil, editing: true, displayed_field: :name)
+ def initialize(report:, name:, label:, placeholder: nil, editing: true, displayed_field: :name, user: nil)
super(report: report, name: name, label: label, placeholder: placeholder, editing: editing)
-
- live_repositories = Repository.accessible_by_teams(report.team).sort_by { |r| r.name.downcase }
+ live_repositories = Repository.viewable_by_user(user, report.team).sort_by { |r| r.name.downcase }
snapshots_of_deleted = RepositorySnapshot.left_outer_joins(:original_repository)
.where(team: report.team)
.where.not(original_repository: live_repositories)
diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb
index 3618b9cfb6..3a0dcbc736 100644
--- a/app/controllers/reports_controller.rb
+++ b/app/controllers/reports_controller.rb
@@ -18,7 +18,8 @@ class ReportsController < ApplicationController
before_action :check_create_permissions, only: %i(new create)
before_action :check_manage_permissions, only: %i(edit update generate_pdf generate_docx)
before_action :switch_team_with_param, only: :index
- after_action :generate_pdf_report, only: %i(create update generate_pdf)
+ after_action :generate_pdf_report, only: %i(generate_pdf)
+ after_action :generate_report, only: %i(create update)
# Index showing all reports of a single project
def index
@@ -44,6 +45,8 @@ def new
def new_template_values
if Extends::REPORT_TEMPLATES.key?(params[:template]&.to_sym)
template = params[:template]
+ @type = :pdf
+ @template_name = Extends::REPORT_TEMPLATES[params[:template].to_sym]
else
return render_404
end
@@ -69,6 +72,7 @@ def new_template_values
else
render json: {
html: render_to_string(partial: 'reports/wizard/no_template_values',
+ locals: { type: @type, template: @template_name },
formats: :html)
}
end
@@ -77,6 +81,8 @@ def new_template_values
def new_docx_template_values
if Extends::DOCX_REPORT_TEMPLATES.key?(params[:template]&.to_sym)
template = params[:template]
+ @type = :docx
+ @template_name = Extends::DOCX_REPORT_TEMPLATES[params[:template].to_sym]
else
return render_404
end
@@ -102,6 +108,7 @@ def new_docx_template_values
else
render json: {
html: render_to_string(partial: 'reports/wizard/no_template_values',
+ locals: { type: @type, template: @template_name },
formats: :html)
}
end
@@ -363,6 +370,9 @@ def load_wizard_vars
.merge(MyModule.active)
.group(:id)
.select(:id, :name)
+ @default_template = Extends::REPORT_TEMPLATES.keys.first.to_s if Extends::REPORT_TEMPLATES.one?
+
+ @default_docx_template = Extends::DOCX_REPORT_TEMPLATES.keys.first.to_s if Extends::DOCX_REPORT_TEMPLATES.one? && custom_templates(Extends::DOCX_REPORT_TEMPLATES)
end
def check_project_read_permissions
@@ -430,6 +440,26 @@ def generate_pdf_report
Rails.logger.error e.message
end
+ def generate_docx_report
+ return unless @report.persisted?
+
+ @report.docx_processing!
+ log_activity(:generate_docx_report)
+
+ ensure_report_template!
+ Reports::DocxJob.perform_later(@report.id, user_id: current_user.id, root_url: root_url)
+ rescue ActiveRecord::ActiveRecordError => e
+ Rails.logger.error e.message
+ end
+
+ def generate_report
+ return unless @report.persisted?
+
+ generate_pdf_report
+
+ generate_docx_report if @report.settings['docx_template'].present? && custom_templates(Extends::DOCX_REPORT_TEMPLATES)
+ end
+
def ensure_report_template!
return if @report.settings['template'].present?
diff --git a/app/helpers/input_sanitize_helper.rb b/app/helpers/input_sanitize_helper.rb
index 73c9eaca28..55c3ff38e6 100644
--- a/app/helpers/input_sanitize_helper.rb
+++ b/app/helpers/input_sanitize_helper.rb
@@ -40,11 +40,12 @@ def custom_auto_link(text, options = {})
preview_repository = options.fetch(:preview_repository, false)
format_opt = wrapper_tag.merge(sanitize: false)
base64_encoded_imgs = options.fetch(:base64_encoded_imgs, false)
- text = simple_format(text, {}, format_opt) if simple_f
# allow base64 images when sanitizing if base64_encoded_imgs is true
sanitizer_config = Constants::INPUT_SANITIZE_CONFIG.deep_dup
+
text = sanitize_input(text, tags, sanitizer_config: sanitizer_config)
+ text = simple_format(text, {}, format_opt) if simple_f
text = smart_annotation_parser(text, team, base64_encoded_imgs, preview_repository) if text.match?(SmartAnnotations::TagToHtml::ALL_REGEX)
diff --git a/app/helpers/reports_helper.rb b/app/helpers/reports_helper.rb
index 2abd4fdc2c..47a49e6cf4 100644
--- a/app/helpers/reports_helper.rb
+++ b/app/helpers/reports_helper.rb
@@ -119,4 +119,8 @@ def permit_report_settings_structure(settings_definition)
end
end
end
+
+ def custom_templates(templates)
+ templates.any? { |template, _| template != :scinote_template }
+ end
end
diff --git a/app/models/my_module.rb b/app/models/my_module.rb
index 690427c269..5ded2c691d 100644
--- a/app/models/my_module.rb
+++ b/app/models/my_module.rb
@@ -391,17 +391,17 @@ def repository_json_hot(repository, order)
{ data: data, headers: headers }
end
- def repository_docx_json(repository)
- headers = [
- I18n.t('repositories.table.id'),
- I18n.t('repositories.table.row_name'),
- I18n.t('repositories.table.added_on'),
- I18n.t('repositories.table.added_by')
- ]
+ def repository_docx_json(repository, excluded_columns)
+ headers = Report.default_repository_columns.filter_map do |key, value|
+ value unless excluded_columns.include?(key.to_s.to_i)
+ end
+
custom_columns = []
return false unless repository
repository.repository_columns.order(:id).each do |column|
+ next if excluded_columns.include?(column.id)
+
if column.data_type == 'RepositoryStockValue'
if repository.has_stock_consumption?
headers.push(I18n.t('repositories.table.row_consumption'))
@@ -416,7 +416,7 @@ def repository_docx_json(repository)
records = repository.assigned_rows(self)
.select(:id, :name, :created_at, :created_by_id, :repository_id, :parent_id, :archived)
- { headers: headers, rows: records, custom_columns: custom_columns }
+ { headers: headers, rows: records, custom_columns: custom_columns, excluded_columns: excluded_columns }
end
def deep_clone(current_user)
diff --git a/app/models/report.rb b/app/models/report.rb
index 0d1fbc97da..61f092e33d 100644
--- a/app/models/report.rb
+++ b/app/models/report.rb
@@ -43,6 +43,8 @@ class Report < ApplicationRecord
DEFAULT_SETTINGS = {
all_tasks: true,
+ exclude_task_metadata: false,
+ exclude_timestamps: false,
task: {
protocol: {
description: true,
@@ -62,7 +64,8 @@ class Report < ApplicationRecord
result_comments: true,
result_order: 'new',
activities: true,
- repositories: []
+ repositories: [],
+ excluded_repository_columns: {}
}
}.freeze
@@ -124,4 +127,13 @@ def self.generate_whole_project_report(project, current_user, current_team)
ReportActions::ReportContent.new(report, content, {}, current_user).save_with_content
report
end
+
+ def self.default_repository_columns
+ {
+ '-1': I18n.t('repositories.table.id'),
+ '-2': I18n.t('repositories.table.row_name'),
+ '-3': I18n.t('repositories.table.added_on'),
+ '-4': I18n.t('repositories.table.added_by')
+ }
+ end
end
diff --git a/app/services/reports/docx.rb b/app/services/reports/docx.rb
index 46445f338c..72e6ce3398 100644
--- a/app/services/reports/docx.rb
+++ b/app/services/reports/docx.rb
@@ -30,7 +30,7 @@ def initialize(report, docx, options)
@link_style = {}
@color = {}
@scinote_url = options[:scinote_url][0..-2]
- @template = @settings[:docx_template] || 'scinote_template'
+ @template = @settings[:docx_template].presence || 'scinote_template'
extend "#{@template.camelize}Docx".constantize
end
diff --git a/app/services/reports/docx/draw_experiment.rb b/app/services/reports/docx/draw_experiment.rb
index e02d018a7a..ebe3ae7ca3 100644
--- a/app/services/reports/docx/draw_experiment.rb
+++ b/app/services/reports/docx/draw_experiment.rb
@@ -4,6 +4,7 @@ module Reports::Docx::DrawExperiment
def draw_experiment(subject)
color = @color
link_style = @link_style
+ settings = @settings
scinote_url = @scinote_url
experiment = subject.experiment
return unless can_read_experiment?(@user, experiment)
@@ -14,12 +15,15 @@ def draw_experiment(subject)
link_style
end
- @docx.p do
- text I18n.t('projects.reports.elements.experiment.user_time',
- code: experiment.code, timestamp: I18n.l(experiment.created_at, format: :full)), color: color[:gray]
- if experiment.archived?
- text ' | '
- text I18n.t('search.index.archived'), color: color[:gray]
+ if !settings['exclude_timestamps'] || experiment.archived?
+ @docx.p do
+ unless settings['exclude_timestamps']
+ text I18n.t('projects.reports.elements.experiment.user_time',
+ code: experiment.code,
+ timestamp: I18n.l(experiment.created_at, format: :full)), color: color[:gray]
+ text ' | ' if experiment.archived?
+ end
+ text I18n.t('search.index.archived'), color: color[:gray] if experiment.archived?
end
end
html = custom_auto_link(experiment.description, team: @report_team)
diff --git a/app/services/reports/docx/draw_my_module.rb b/app/services/reports/docx/draw_my_module.rb
index e85ccf7014..bd859baab4 100644
--- a/app/services/reports/docx/draw_my_module.rb
+++ b/app/services/reports/docx/draw_my_module.rb
@@ -4,6 +4,7 @@ module Reports::Docx::DrawMyModule
def draw_my_module(subject, without_results: false, without_repositories: false)
color = @color
link_style = @link_style
+ settings = @settings
scinote_url = @scinote_url
my_module = subject.my_module
tags = my_module.tags.order(:id)
@@ -15,45 +16,50 @@ def draw_my_module(subject, without_results: false, without_repositories: false)
link_style
end
- @docx.p do
- text I18n.t('projects.reports.elements.module.user_time', code: my_module.code,
- timestamp: I18n.l(my_module.created_at, format: :full)), color: color[:gray]
- if my_module.archived?
- text ' | '
- text I18n.t('search.index.archived'), color: color[:gray]
- end
- end
-
- if my_module.started_on.present?
+ if my_module.archived? || !settings['exclude_timestamps']
@docx.p do
- text I18n.t('projects.reports.elements.module.started_on',
- started_on: I18n.l(my_module.started_on, format: :full))
+ unless settings['exclude_timestamps']
+ text I18n.t('projects.reports.elements.module.user_time', code: my_module.code,
+ timestamp: I18n.l(my_module.created_at, format: :full)), color: color[:gray]
+ text ' | ' if my_module.archived?
+ end
+
+ text I18n.t('search.index.archived'), color: color[:gray] if my_module.archived?
end
end
- if my_module.due_date.present?
- @docx.p do
- text I18n.t('projects.reports.elements.module.due_date',
- due_date: I18n.l(my_module.due_date, format: :full))
+ unless settings['exclude_task_metadata']
+ if my_module.started_on.present?
+ @docx.p do
+ text I18n.t('projects.reports.elements.module.started_on',
+ started_on: I18n.l(my_module.started_on, format: :full))
+ end
end
- end
- status = my_module.my_module_status
- @docx.p do
- text I18n.t('projects.reports.elements.module.status')
- text ' '
- text "[#{status.name}]", color: (status.light_color? ? '000000' : status.color.delete('#'))
- if my_module.completed?
- text " #{I18n.t('my_modules.states.completed')} #{I18n.l(my_module.completed_on, format: :full)}"
+ if my_module.due_date.present?
+ @docx.p do
+ text I18n.t('projects.reports.elements.module.due_date',
+ due_date: I18n.l(my_module.due_date, format: :full))
+ end
end
- end
- if tags.present?
+ status = my_module.my_module_status
@docx.p do
- text I18n.t('projects.reports.elements.module.tags_header')
- tags.each do |tag|
- text ' '
- text "[#{tag.name}]", color: tag.color.delete('#')
+ text I18n.t('projects.reports.elements.module.status')
+ text ' '
+ text "[#{status.name}]", color: (status.light_color? ? '000000' : status.color.delete('#'))
+ if my_module.completed?
+ text " #{I18n.t('my_modules.states.completed')} #{I18n.l(my_module.completed_on, format: :full)}"
+ end
+ end
+
+ if tags.present?
+ @docx.p do
+ text I18n.t('projects.reports.elements.module.tags_header')
+ tags.each do |tag|
+ text ' '
+ text "[#{tag.name}]", color: tag.color.delete('#')
+ end
end
end
end
@@ -69,10 +75,13 @@ def draw_my_module(subject, without_results: false, without_repositories: false)
filter_steps_for_report(my_module.protocol.steps, @settings).order(:position).each do |step|
draw_step(step)
end
+ @docx.p
- draw_results(my_module) unless without_results
+ unless without_results
+ draw_results(my_module)
+ @docx.p
+ end
- @docx.p
subject.children.active.each do |child|
next if without_repositories && child.type_of == 'my_module_repository'
diff --git a/app/services/reports/docx/draw_my_module_protocol.rb b/app/services/reports/docx/draw_my_module_protocol.rb
index 2096569ffe..0f3b56dbea 100644
--- a/app/services/reports/docx/draw_my_module_protocol.rb
+++ b/app/services/reports/docx/draw_my_module_protocol.rb
@@ -12,13 +12,14 @@ def draw_my_module_protocol(my_module)
end
if @settings.dig('task', 'protocol', 'description') && protocol.description.present?
- @docx.p I18n.t('projects.reports.elements.module.protocol.user_time', code: protocol.original_code,
- timestamp: I18n.l(protocol.created_at, format: :full)), color: @color[:gray]
+ unless @settings['exclude_timestamps']
+ @docx.p I18n.t('projects.reports.elements.module.protocol.user_time', code: protocol.original_code,
+ timestamp: I18n.l(protocol.created_at, format: :full)), color: @color[:gray]
+ end
html = custom_auto_link(protocol.description, team: @report_team)
Reports::HtmlToWordConverter.new(@docx, { scinote_url: @scinote_url,
link_style: @link_style }).html_to_word_converter(html)
@docx.p
- @docx.p
end
end
end
diff --git a/app/services/reports/docx/draw_my_module_repository.rb b/app/services/reports/docx/draw_my_module_repository.rb
index b552211d18..11f1ed0c84 100644
--- a/app/services/reports/docx/draw_my_module_repository.rb
+++ b/app/services/reports/docx/draw_my_module_repository.rb
@@ -5,11 +5,12 @@ def draw_my_module_repository(subject)
my_module = subject.my_module
repository = subject.repository
repository = assigned_repository_or_snapshot(my_module, repository)
+ excluded_repository_columns = @settings.dig(:task, :excluded_repository_columns, repository.id.to_s) || {}
return unless repository && can_read_experiment?(@user, my_module.experiment) &&
(repository.is_a?(RepositorySnapshot) || can_read_repository?(@user, repository))
- repository_data = my_module.repository_docx_json(repository)
+ repository_data = my_module.repository_docx_json(repository, excluded_repository_columns)
return false unless repository_data[:rows].any? && can_read_repository?(@user, repository)
@@ -19,7 +20,12 @@ def draw_my_module_repository(subject)
@docx.p I18n.t('projects.reports.elements.module_repository.name',
repository: repository.name,
my_module: my_module.name), bold: true, size: Constants::REPORT_DOCX_STEP_ELEMENTS_TITLE_SIZE
- @docx.table table, border_size: Constants::REPORT_DOCX_TABLE_BORDER_SIZE
+
+ if table.present?
+ @docx.table table, border_size: Constants::REPORT_DOCX_TABLE_BORDER_SIZE
+ else
+ @docx.p I18n.t('projects.reports.elements.module_repository.no_columns'), italic: true
+ end
@docx.p
@docx.p
diff --git a/app/services/reports/docx/draw_project_header.rb b/app/services/reports/docx/draw_project_header.rb
index defa8e36ea..c0bbffeef4 100644
--- a/app/services/reports/docx/draw_project_header.rb
+++ b/app/services/reports/docx/draw_project_header.rb
@@ -15,10 +15,12 @@ def draw_project_header(subject)
link_style
end
- @docx.p do
- text I18n.t('projects.reports.elements.project_header.user_time', code: project.code,
- timestamp: I18n.l(project.created_at, format: :full)), color: color[:gray]
- br
+ unless @settings['exclude_timestamps']
+ @docx.p do
+ text I18n.t('projects.reports.elements.project_header.user_time', code: project.code,
+ timestamp: I18n.l(project.created_at, format: :full)), color: color[:gray]
+ br
+ end
end
end
end
diff --git a/app/services/reports/docx/draw_result_asset.rb b/app/services/reports/docx/draw_result_asset.rb
index 030de2591a..fc690977f9 100644
--- a/app/services/reports/docx/draw_result_asset.rb
+++ b/app/services/reports/docx/draw_result_asset.rb
@@ -25,11 +25,9 @@ def draw_result_asset(result, settings)
end
text " #{I18n.t('search.index.archived')} ", bold: true if result.archived?
text ' ' + I18n.t('projects.reports.elements.result_asset.file_name', file: asset.file_name)
- text ' ' + I18n.t('projects.reports.elements.result_asset.user_time',
- user: result.user.full_name, timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
-
- if settings.dig(:task, :file_results_previews) && ActiveStorageFileUtil.previewable_document?(asset&.file&.blob)
- text " #{I18n.t('projects.reports.elements.result_asset.full_preview_attached')}", color: color[:gray]
+ unless settings['exclude_timestamps']
+ text ' ' + I18n.t('projects.reports.elements.result_asset.user_time',
+ user: result.user.full_name, timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
end
end
diff --git a/app/services/reports/docx/draw_result_comments.rb b/app/services/reports/docx/draw_result_comments.rb
index 3fc68ef1f3..ebec9ad139 100644
--- a/app/services/reports/docx/draw_result_comments.rb
+++ b/app/services/reports/docx/draw_result_comments.rb
@@ -8,8 +8,10 @@ def draw_result_comments(result)
@docx.p
@docx.p I18n.t('projects.reports.elements.result_comments.name', result: result.name),
bold: true, size: Constants::REPORT_DOCX_STEP_ELEMENTS_TITLE_SIZE
- comments.each do |comment|
+ comments.find_each.with_index do |comment, index|
comment_ts = comment.created_at
+
+ @docx.p unless index.zero?
@docx.p I18n.t('projects.reports.elements.result_comments.comment_prefix',
user: comment.user.full_name,
date: I18n.l(comment_ts, format: :full_date),
@@ -17,7 +19,6 @@ def draw_result_comments(result)
html = custom_auto_link(comment.message, team: @report_team)
Reports::HtmlToWordConverter.new(@docx, { scinote_url: @scinote_url,
link_style: @link_style }).html_to_word_converter(html)
- @docx.p
end
end
end
diff --git a/app/services/reports/docx/draw_result_table.rb b/app/services/reports/docx/draw_result_table.rb
index 661d41a466..f919cc8a40 100644
--- a/app/services/reports/docx/draw_result_table.rb
+++ b/app/services/reports/docx/draw_result_table.rb
@@ -5,6 +5,7 @@ def draw_result_table(element)
result = element.result
table = element.orderable.table
timestamp = table.created_at
+ settings = @settings
color = @color
obj = self
table_data = JSON.parse(table.contents_utf_8)['data']
@@ -39,9 +40,11 @@ def draw_result_table(element)
end
@docx.p do
text I18n.t 'projects.reports.elements.result_table.table_name', name: table.name
- text ' '
- text I18n.t('projects.reports.elements.result_table.user_time',
- timestamp: I18n.l(timestamp, format: :full), user: result.user.full_name), color: color[:gray]
+ unless settings['exclude_timestamps']
+ text ' '
+ text I18n.t('projects.reports.elements.result_table.user_time',
+ timestamp: I18n.l(timestamp, format: :full), user: result.user.full_name), color: color[:gray]
+ end
end
end
end
diff --git a/app/services/reports/docx/draw_result_text.rb b/app/services/reports/docx/draw_result_text.rb
index 2ab606a7e6..baf9bc9f3d 100644
--- a/app/services/reports/docx/draw_result_text.rb
+++ b/app/services/reports/docx/draw_result_text.rb
@@ -4,12 +4,18 @@ module Reports::Docx::DrawResultText
def draw_result_text(element)
result_text = element.orderable
timestamp = element.created_at
+ settings = @settings
color = @color
- @docx.p do
- text result_text.name.presence || '', italic: true
- text ' '
- text I18n.t('projects.reports.elements.result_text.user_time',
- timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ if result_text.name.present? || !settings['exclude_timestamps']
+ @docx.p do
+ text result_text.name.to_s, italic: true
+ text ' ' if result_text.name.present?
+
+ unless settings['exclude_timestamps']
+ text I18n.t('projects.reports.elements.result_text.user_time',
+ timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ end
+ end
end
html = custom_auto_link(result_text.text, team: @report_team)
Reports::HtmlToWordConverter.new(@docx, { scinote_url: @scinote_url,
diff --git a/app/services/reports/docx/draw_results.rb b/app/services/reports/docx/draw_results.rb
index 0baedb9cf8..a708c262e0 100644
--- a/app/services/reports/docx/draw_results.rb
+++ b/app/services/reports/docx/draw_results.rb
@@ -1,19 +1,31 @@
# frozen_string_literal: true
module Reports::Docx::DrawResults
- def draw_results(my_module)
+ def draw_results(my_module, with_my_module_name: false)
color = @color
+ settings = @settings
+ scinote_url = @scinote_url
+ link_style = @link_style
return unless can_read_my_module?(@user, my_module)
if my_module.results.any? && (%w(file_results table_results text_results).any? { |k| @settings.dig('task', k) })
+ if with_my_module_name
+ @docx.h3 do
+ link my_module.name,
+ scinote_url + Rails.application.routes.url_helpers.protocols_my_module_path(my_module),
+ link_style
+ end
+ end
@docx.h4 I18n.t('Results')
order_results_for_report(my_module.results, @settings.dig('task', 'result_order')).each do |result|
@docx.p do
text result.name.presence || I18n.t('projects.reports.unnamed'), italic: true
text " #{I18n.t('search.index.archived')} ", bold: true if result.archived?
- text I18n.t('projects.reports.elements.result.user_time',
- timestamp: I18n.l(result.created_at, format: :full),
- user: result.user.full_name), color: color[:gray]
+ unless settings['exclude_timestamps']
+ text I18n.t('projects.reports.elements.result.user_time',
+ timestamp: I18n.l(result.created_at, format: :full),
+ user: result.user.full_name), color: color[:gray]
+ end
end
draw_result_asset(result, @settings) if @settings.dig('task', 'file_results')
result.result_orderable_elements.each do |element|
diff --git a/app/services/reports/docx/draw_step.rb b/app/services/reports/docx/draw_step.rb
index 6d4e27da6d..579a24c4e5 100644
--- a/app/services/reports/docx/draw_step.rb
+++ b/app/services/reports/docx/draw_step.rb
@@ -6,22 +6,30 @@ def draw_step(step)
step_type_str = step.completed ? 'completed' : 'uncompleted'
user = (step.completed? && step.last_modified_by) || step.user
timestamp = step.completed ? step.completed_on : step.created_at
+ settings = @settings
@docx.p
@docx.h4(
"#{I18n.t('projects.reports.elements.step.step_pos', pos: step.position_plus_one)} #{step.name}"
)
- @docx.p do
- if step.completed
- text I18n.t('protocols.steps.completed'), color: color[:green], bold: true
- else
- text I18n.t('protocols.steps.uncompleted'), color: color[:gray], bold: true
+
+ unless settings['exclude_task_metadata'] || settings['exclude_timestamps']
+ @docx.p do
+ unless settings['exclude_task_metadata']
+ if step.completed
+ text I18n.t('protocols.steps.completed'), color: color[:green], bold: true
+ else
+ text I18n.t('protocols.steps.uncompleted'), color: color[:gray], bold: true
+ end
+ end
+ unless settings['exclude_timestamps']
+ text ' | ' unless settings['exclude_task_metadata']
+ text I18n.t(
+ "projects.reports.elements.step.#{step_type_str}.user_time",
+ user: user.full_name,
+ timestamp: I18n.l(timestamp, format: :full)
+ ), color: color[:gray]
+ end
end
- text ' | '
- text I18n.t(
- "projects.reports.elements.step.#{step_type_str}.user_time",
- user: user.full_name,
- timestamp: I18n.l(timestamp, format: :full)
- ), color: color[:gray]
end
step.step_orderable_elements.order(:position).each do |element|
@@ -41,9 +49,6 @@ def draw_step(step)
end
draw_step_comments(step) if @settings.dig('task', 'protocol', 'step_comments')
-
- @docx.p
- @docx.p
end
def handle_step_table(table)
diff --git a/app/services/reports/docx/draw_step_asset.rb b/app/services/reports/docx/draw_step_asset.rb
index 8d83ca221b..5b6569919e 100644
--- a/app/services/reports/docx/draw_step_asset.rb
+++ b/app/services/reports/docx/draw_step_asset.rb
@@ -5,6 +5,7 @@ def draw_step_asset(asset)
timestamp = asset.created_at
asset_url = Rails.application.routes.url_helpers.asset_download_url(asset)
color = @color
+ settings = @settings
@docx.p
begin
@@ -22,9 +23,11 @@ def draw_step_asset(asset)
link I18n.t('projects.reports.elements.download'), asset_url do
italic true
end
- text ' '
- text I18n.t('projects.reports.elements.step_asset.user_time',
- timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ unless settings['exclude_timestamps']
+ text ' '
+ text I18n.t('projects.reports.elements.step_asset.user_time',
+ timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ end
end
end
end
diff --git a/app/services/reports/docx/draw_step_checklist.rb b/app/services/reports/docx/draw_step_checklist.rb
index e2662e60ec..5c510c9f98 100644
--- a/app/services/reports/docx/draw_step_checklist.rb
+++ b/app/services/reports/docx/draw_step_checklist.rb
@@ -4,6 +4,7 @@ module Reports::Docx::DrawStepChecklist
def draw_step_checklist(checklist)
team = @report_team
user = @user
+ settings = @settings
items = checklist.checklist_items
timestamp = checklist.created_at
@@ -15,9 +16,11 @@ def draw_step_checklist(checklist)
team,
I18n.t('projects.reports.elements.step_checklist.checklist_name', name: checklist.name)
).text, italic: true
- text ' '
- text I18n.t('projects.reports.elements.step_checklist.user_time',
- timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ unless settings['exclude_timestamps']
+ text ' '
+ text I18n.t('projects.reports.elements.step_checklist.user_time',
+ timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ end
end
if items.any?
@docx.ul do
diff --git a/app/services/reports/docx/draw_step_comments.rb b/app/services/reports/docx/draw_step_comments.rb
index 87d0abc5f6..f9733eaeb1 100644
--- a/app/services/reports/docx/draw_step_comments.rb
+++ b/app/services/reports/docx/draw_step_comments.rb
@@ -8,8 +8,10 @@ def draw_step_comments(step)
@docx.p
@docx.p I18n.t('projects.reports.elements.step_comments.name', step: step.name),
bold: true, size: Constants::REPORT_DOCX_STEP_ELEMENTS_TITLE_SIZE
- comments.each do |comment|
+ comments.find_each.with_index do |comment, index|
comment_ts = comment.created_at
+
+ @docx.p unless index.zero?
@docx.p I18n.t('projects.reports.elements.step_comments.comment_prefix',
user: comment.user.full_name,
date: I18n.l(comment_ts, format: :full_date),
@@ -17,7 +19,6 @@ def draw_step_comments(step)
html = custom_auto_link(comment.message, team: @report_team)
Reports::HtmlToWordConverter.new(@docx, { scinote_url: @scinote_url,
link_style: @link_style }).html_to_word_converter(html)
- @docx.p
end
end
end
diff --git a/app/services/reports/docx/draw_step_table.rb b/app/services/reports/docx/draw_step_table.rb
index a0826741af..3565c200e6 100644
--- a/app/services/reports/docx/draw_step_table.rb
+++ b/app/services/reports/docx/draw_step_table.rb
@@ -4,6 +4,7 @@ module Reports::Docx::DrawStepTable
def draw_step_table(table, table_type)
color = @color
timestamp = table.created_at
+ settings = @settings
obj = self
table_data = JSON.parse(table.contents_utf_8)['data']
table_data = obj.add_headers_to_table(table_data, table_type == 'step_well_plates_table')
@@ -38,9 +39,11 @@ def draw_step_table(table, table_type)
end
@docx.p do
text I18n.t("projects.reports.elements.#{table_type}.table_name", name: table.name), italic: true
- text ' '
- text I18n.t("projects.reports.elements.#{table_type}.user_time",
- timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ unless settings['exclude_timestamps']
+ text ' '
+ text I18n.t("projects.reports.elements.#{table_type}.user_time",
+ timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ end
end
end
end
diff --git a/app/services/reports/docx/draw_step_text.rb b/app/services/reports/docx/draw_step_text.rb
index 978386a947..5fc3646393 100644
--- a/app/services/reports/docx/draw_step_text.rb
+++ b/app/services/reports/docx/draw_step_text.rb
@@ -5,11 +5,18 @@ def draw_step_text(element)
step_text = element.orderable
timestamp = element.created_at
color = @color
- @docx.p do
- text step_text.name.presence || '', italic: true
- text ' '
- text I18n.t('projects.reports.elements.result_text.user_time',
- timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ settings = @settings
+
+ if step_text.name.present? || !settings['exclude_timestamps']
+ @docx.p do
+ text step_text.name.to_s, italic: true
+ text ' ' if step_text.name.present?
+
+ unless settings['exclude_timestamps']
+ text I18n.t('projects.reports.elements.result_text.user_time',
+ timestamp: I18n.l(timestamp, format: :full)), color: color[:gray]
+ end
+ end
end
if step_text.text.present?
html = custom_auto_link(step_text.text, team: @report_team)
diff --git a/app/services/reports/docx/repository_helper.rb b/app/services/reports/docx/repository_helper.rb
index 159ba8dbf7..d8166ca9bd 100644
--- a/app/services/reports/docx/repository_helper.rb
+++ b/app/services/reports/docx/repository_helper.rb
@@ -4,15 +4,20 @@ module Reports::Docx::RepositoryHelper
include InputSanitizeHelper
include ActionView::Helpers::NumberHelper
-
def prepare_row_columns_for_docx(repository_data, my_module = nil, repository = nil)
+ return if repository_data[:headers].blank?
+
result = [repository_data[:headers]]
+ excluded_columns = repository_data[:excluded_columns]
+
repository_data[:rows].each do |record|
row = []
- row.push(record.code)
- row.push(escape_input(record.archived ? "#{record.name} [#{I18n.t('general.archived')}]" : record.name))
- row.push(I18n.l(record.created_at, format: :full))
- row.push(escape_input(record.created_by.full_name))
+ row.push(record.code) unless excluded_columns.include?(-1)
+ unless excluded_columns.include?(-2)
+ row.push(escape_input(record.archived ? "#{record.name} [#{I18n.t('general.archived')}]" : record.name))
+ end
+ row.push(I18n.l(record.created_at, format: :full)) unless excluded_columns.include?(-3)
+ row.push(escape_input(record.created_by.full_name)) unless excluded_columns.include?(-4)
cell_values = {}
custom_cells = record.repository_cells
@@ -39,6 +44,8 @@ def prepare_row_columns_for_docx(repository_data, my_module = nil, repository =
end
repository_data[:custom_columns].each do |column_id|
+ next if excluded_columns.include?(column_id)
+
value = cell_values[column_id]
row.push(value)
end
diff --git a/app/services/reports/docx_renderer.rb b/app/services/reports/docx_renderer.rb
index 91872dc46e..dd027bc4bb 100644
--- a/app/services/reports/docx_renderer.rb
+++ b/app/services/reports/docx_renderer.rb
@@ -130,6 +130,13 @@ def self.render_table_element(docx, element, options = {})
row[:data].each do |cell|
docx_cell = Caracal::Core::Models::TableCellModel.new do |c|
cell.each do |content|
+ c.background content[:style][:background] if content.dig(:style, :background).present?
+ if content.dig(:style, :vertical_align).present? && content[:style][:vertical_align] != :middle
+ c.vertical_align content[:style][:vertical_align]
+ else
+ c.vertical_align :center
+ end
+
if content[:type] == 'p'
Reports::DocxRenderer.render_p_element(c, content, options.merge({ skip_br: true }))
elsif content[:type] == 'table'
diff --git a/app/services/reports/html_to_word_converter.rb b/app/services/reports/html_to_word_converter.rb
index f4e312f8e8..6d589a020e 100644
--- a/app/services/reports/html_to_word_converter.rb
+++ b/app/services/reports/html_to_word_converter.rb
@@ -208,7 +208,7 @@ def paragraph_styling(elem)
if style
style_keys.each do |key|
- style_el = style.value.split(';').select { |i| (i.include? key) }[0]
+ style_el = style.value.split(';').find { |i| i.strip.start_with?(key) }
next unless style_el
value = style_el.split(':')[1].strip if style_el
@@ -259,6 +259,29 @@ def image_styling(elem, dimension)
}
end
+ def table_cell_styling(elem)
+ style = elem.attributes['style']
+ result = {}
+ style_keys = %w(background-color vertical-align background)
+
+ if style
+ style_keys.each do |key|
+ style_el = style.value.split(';').find { |i| (i.include? key) }
+ next unless style_el
+
+ value = style_el.split(':')[1].strip if style_el
+
+ case key
+ when 'background-color', 'background'
+ result[:background] = normalized_hex_color(value)
+ when 'vertical-align'
+ result[:vertical_align] = value.to_sym
+ end
+ end
+ end
+ result
+ end
+
def tiny_mce_table_element(table_element)
# array of elements
rows = table_element.css('tbody').first.children.map do |row|
@@ -267,11 +290,13 @@ def tiny_mce_table_element(table_element)
cells = row.children.map do |cell|
next unless cell.name == 'td'
+ style = table_cell_styling(cell)
# Parse cell content
formated_cell = recursive_children(cell.children, [], true)
# Combine text elements to single paragraph
formated_cell = combine_docx_elements(formated_cell)
+ formated_cell.each { |element| element[:style] = style } if style.present?
formated_cell
end.reject(&:blank?)
{ type: 'tr', data: cells }
diff --git a/app/views/layouts/reports/template_values_editor.html.erb b/app/views/layouts/reports/template_values_editor.html.erb
index 225c32a4a1..841b024efb 100644
--- a/app/views/layouts/reports/template_values_editor.html.erb
+++ b/app/views/layouts/reports/template_values_editor.html.erb
@@ -1,6 +1,10 @@