Merge pull request #4832 from okriuchykhin/ok_SCI_7638

Improve loading speed of inventory table [SCI-7638]
This commit is contained in:
Alex Kriuchykhin 2023-01-18 11:39:52 +01:00 committed by GitHub
commit 1ac86b8188
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 143 additions and 78 deletions

View file

@ -412,10 +412,10 @@ var RepositoryDatatable = (function(global) {
// Adjust columns width in table header // Adjust columns width in table header
function adjustTableHeader() { function adjustTableHeader() {
TABLE.columns.adjust(); TABLE.columns.adjust();
$('.dropdown-menu').parent() // $('.dropdown-menu').parent()
.on('shown.bs.dropdown hidden.bs.dropdown', function() { // .on('shown.bs.dropdown hidden.bs.dropdown', function() {
TABLE.columns.adjust(); // TABLE.columns.adjust();
}); // });
} }
function checkSnapshottingStatus() { function checkSnapshottingStatus() {
@ -664,9 +664,9 @@ var RepositoryDatatable = (function(global) {
initActiveRemindersFilter(); initActiveRemindersFilter();
renderFiltersDropdown(); renderFiltersDropdown();
setTimeout(function() { // setTimeout(function() {
adjustTableHeader(); // adjustTableHeader();
}, 500); // }, 500);
} }
}); });
@ -699,11 +699,11 @@ var RepositoryDatatable = (function(global) {
}) })
initRowSelection(); initRowSelection();
$(window).resize(() => { // $(window).resize(() => {
setTimeout(() => { // setTimeout(() => {
adjustTableHeader(); // adjustTableHeader();
}, 500); // }, 500);
}); // });
return TABLE; return TABLE;
} }
@ -822,7 +822,7 @@ var RepositoryDatatable = (function(global) {
}); });
changeToEditMode(); changeToEditMode();
adjustTableHeader(); // adjustTableHeader();
}) })
.on('click', '#deleteRepositoryRecords', function() { .on('click', '#deleteRepositoryRecords', function() {
$('#deleteRepositoryRecord').modal('show'); $('#deleteRepositoryRecord').modal('show');
@ -909,9 +909,9 @@ var RepositoryDatatable = (function(global) {
document.documentElement.style.setProperty('--repository-sidebar-margin', '363px'); document.documentElement.style.setProperty('--repository-sidebar-margin', '363px');
}); });
$('#wrapper').on('sideBar::hidden sideBar::shown', function() { // $('#wrapper').on('sideBar::hidden sideBar::shown', function() {
adjustTableHeader(); // adjustTableHeader();
}); // });
} }
function renderFiltersDropdown() { function renderFiltersDropdown() {

View file

@ -28,11 +28,14 @@ module InputSanitizeHelper
base64_encoded_imgs = options.fetch(:base64_encoded_imgs, false) base64_encoded_imgs = options.fetch(:base64_encoded_imgs, false)
text = sanitize_input(text, tags) text = sanitize_input(text, tags)
text = simple_format(text, {}, format_opt) if simple_f text = simple_format(text, {}, format_opt) if simple_f
if text =~ SmartAnnotations::TagToHtml::USER_REGEX || text =~ SmartAnnotations::TagToHtml::REGEX
text = smart_annotation_parser(text, team, base64_encoded_imgs, preview_repository)
end
auto_link( auto_link(
custom_link_open_new_tab(smart_annotation_parser(text, team, base64_encoded_imgs, preview_repository)), text,
html: { target: '_blank' },
link: :urls, link: :urls,
sanitize: false, sanitize: false
html: { target: '_blank' }
).html_safe ).html_safe
end end
end end

View file

@ -6,22 +6,22 @@ module RepositoryDatatableHelper
def prepare_row_columns(repository_rows, repository, columns_mappings, team, options = {}) def prepare_row_columns(repository_rows, repository, columns_mappings, team, options = {})
has_stock_management = repository.has_stock_management? has_stock_management = repository.has_stock_management?
reminders_enabled = Repository.reminders_enabled? reminders_enabled = Repository.reminders_enabled?
reminder_row_ids = reminders_enabled ? repository_reminder_row_ids(repository_rows, repository) : [] repository_rows = reminders_enabled ? with_reminders_status(repository_rows, repository) : repository_rows
repository_rows.map do |record| repository_rows.map do |record|
default_cells = public_send("#{repository.class.name.underscore}_default_columns", record) row = public_send("#{repository.class.name.underscore}_default_columns", record)
row = { row.merge!(
DT_RowId: record.id, DT_RowId: record.id,
DT_RowAttr: { 'data-state': row_style(record) }, DT_RowAttr: { 'data-state': row_style(record) },
recordInfoUrl: Rails.application.routes.url_helpers.repository_repository_row_path(repository, record), recordInfoUrl: Rails.application.routes.url_helpers.repository_repository_row_path(repository, record),
hasActiveReminders: reminder_row_ids.include?(record.id), hasActiveReminders: record.has_active_stock_reminders || record.has_active_datetime_reminders,
rowRemindersUrl: rowRemindersUrl:
Rails.application.routes.url_helpers Rails.application.routes.url_helpers
.active_reminder_repository_cells_repository_repository_row_url( .active_reminder_repository_cells_repository_repository_row_url(
repository, repository,
record record
) )
}.merge(default_cells) )
if has_stock_management if has_stock_management
row['manageStockUrl'] = if record.has_stock? row['manageStockUrl'] = if record.has_stock?
@ -100,7 +100,7 @@ module RepositoryDatatableHelper
def prepare_simple_view_row_columns(repository_rows, repository, my_module, options = {}) def prepare_simple_view_row_columns(repository_rows, repository, my_module, options = {})
has_stock_management = repository.has_stock_management? has_stock_management = repository.has_stock_management?
reminders_enabled = Repository.reminders_enabled? reminders_enabled = Repository.reminders_enabled?
reminder_row_ids = reminders_enabled ? repository_reminder_row_ids(repository_rows, repository) : [] repository_rows = reminders_enabled ? with_reminders_status(repository_rows, repository) : repository_rows
repository_rows.map do |record| repository_rows.map do |record|
row = { row = {
@ -108,7 +108,7 @@ module RepositoryDatatableHelper
DT_RowAttr: { 'data-state': row_style(record) }, DT_RowAttr: { 'data-state': row_style(record) },
'0': escape_input(record.name), '0': escape_input(record.name),
recordInfoUrl: Rails.application.routes.url_helpers.repository_repository_row_path(record.repository, record), recordInfoUrl: Rails.application.routes.url_helpers.repository_repository_row_path(record.repository, record),
hasActiveReminders: reminder_row_ids.include?(record.id), hasActiveReminders: record.has_active_stock_reminders || record.has_active_datetime_reminders,
rowRemindersUrl: rowRemindersUrl:
Rails.application.routes.url_helpers Rails.application.routes.url_helpers
.active_reminder_repository_cells_repository_repository_row_url( .active_reminder_repository_cells_repository_repository_row_url(
@ -275,14 +275,33 @@ module RepositoryDatatableHelper
'' ''
end end
def repository_reminder_row_ids(repository_rows, repository) def with_reminders_status(repository_rows, repository)
# don't load reminders for archived repositories # don't load reminders for archived repositories
return [] if repository_rows.blank? || repository.archived? return repository_rows if repository.archived?
# don't load reminders for snapshots # don't load reminders for snapshots
return [] if repository.is_a?(RepositorySnapshot) return repository_rows if repository.is_a?(RepositorySnapshot)
repository_rows.active.with_active_reminders(current_user).to_a.pluck(:id).uniq repository_cells = RepositoryCell.joins(
"INNER JOIN repository_columns ON repository_columns.id = repository_cells.repository_column_id " \
"AND repository_columns.repository_id = #{repository.id}"
)
repository_rows
.joins(
"LEFT OUTER JOIN (#{RepositoryCell.stock_reminder_repository_cells_scope(repository_cells, current_user)
.select(:id, :repository_row_id).to_sql}) " \
"AS repository_cells_with_active_stock_reminders " \
"ON repository_cells_with_active_stock_reminders.repository_row_id = repository_rows.id"
)
.joins(
"LEFT OUTER JOIN (#{RepositoryCell.date_time_reminder_repository_cells_scope(repository_cells, current_user)
.select(:id, :repository_row_id).to_sql}) " \
"AS repository_cells_with_active_datetime_reminders " \
"ON repository_cells_with_active_datetime_reminders.repository_row_id = repository_rows.id"
)
.select('COUNT(repository_cells_with_active_stock_reminders.id) > 0 AS has_active_stock_reminders')
.select('COUNT(repository_cells_with_active_datetime_reminders.id) > 0 AS has_active_datetime_reminders')
end end
def stock_consumption_permitted?(repository, my_module) def stock_consumption_permitted?(repository, my_module)

View file

@ -27,5 +27,38 @@ module ReminderRepositoryCellJoinable
'(repository_date_time_values.id IS NOT NULL OR repository_stock_values.id IS NOT NULL)' '(repository_date_time_values.id IS NOT NULL OR repository_stock_values.id IS NOT NULL)'
) )
end end
def self.stock_reminder_repository_cells_scope(relation, user)
relation.joins( # stock reminders
'LEFT OUTER JOIN "repository_stock_values" ON ' \
'"repository_cells"."value_type" = \'RepositoryStockValue\' AND ' \
'"repository_stock_values"."id" = "repository_cells"."value_id" AND ' \
'(repository_stock_values.amount <= repository_stock_values.low_stock_threshold OR ' \
'repository_stock_values.amount <= 0)'
).joins(
'LEFT OUTER JOIN "hidden_repository_cell_reminders" ON ' \
'"repository_cells"."id" = "hidden_repository_cell_reminders"."repository_cell_id" AND ' \
'"hidden_repository_cell_reminders"."user_id" = ' + user.id.to_s
).where(
'hidden_repository_cell_reminders.id IS NULL AND repository_stock_values.id IS NOT NULL'
)
end
def self.date_time_reminder_repository_cells_scope(relation, user)
relation.joins( # datetime reminders
'LEFT OUTER JOIN "repository_date_time_values" ON ' \
'"repository_date_time_values"."id" = "repository_cells"."value_id" AND ' \
'"repository_cells"."value_type" = \'RepositoryDateTimeValueBase\' ' \
'AND repository_columns.metadata ->> \'reminder_delta\' <> \'\' AND ' \
'(repository_date_time_values.data - NOW()) <= ' \
'(repository_columns.metadata ->> \'reminder_delta\')::int * interval \'1 sec\''
).joins(
'LEFT OUTER JOIN "hidden_repository_cell_reminders" ON ' \
'"repository_cells"."id" = "hidden_repository_cell_reminders"."repository_cell_id" AND ' \
'"hidden_repository_cell_reminders"."user_id" = ' + user.id.to_s
).where(
'hidden_repository_cell_reminders.id IS NULL AND repository_date_time_values.id IS NOT NULL'
)
end
end end
end end

View file

@ -5,7 +5,7 @@ module RepositoryDatatable
include Rails.application.routes.url_helpers include Rails.application.routes.url_helpers
def value def value
asset = object.asset asset = value_object.asset
{ {
id: asset.id, id: asset.id,
url: rails_blob_path(asset.file, disposition: 'attachment'), url: rails_blob_path(asset.file, disposition: 'attachment'),

View file

@ -1,15 +1,24 @@
# frozen_string_literal: true # frozen_string_literal: true
module RepositoryDatatable module RepositoryDatatable
class RepositoryBaseValueSerializer < ActiveModel::Serializer class RepositoryBaseValueSerializer
attributes :value, :value_type attr_accessor :value_object, :value_type, :scope
def initialize(value, scope:)
@value_object = value
@value_type = value.class.name
@scope = scope
end
def serializable_hash
{
value: value,
value_type: @value_type
}
end
def value def value
raise NotImplementedError raise NotImplementedError
end end
def value_type
object.class.name
end
end end
end end

View file

@ -4,7 +4,7 @@ module RepositoryDatatable
class RepositoryChecklistValueSerializer < RepositoryBaseValueSerializer class RepositoryChecklistValueSerializer < RepositoryBaseValueSerializer
include InputSanitizeHelper include InputSanitizeHelper
def value def value
object.data.each { |i| i[:label] = escape_input(i[:label]) } value_object.data.each { |i| i[:label] = escape_input(i[:label]) }
end end
end end
end end

View file

@ -5,12 +5,12 @@ module RepositoryDatatable
def value def value
{ {
start_time: { start_time: {
formatted: I18n.l(object.start_time, format: :full_date), formatted: I18n.l(value_object.start_time, format: :full_date),
datetime: object.start_time.strftime('%Y/%m/%d %H:%M') datetime: value_object.start_time.strftime('%Y/%m/%d %H:%M')
}, },
end_time: { end_time: {
formatted: I18n.l(object.end_time, format: :full_date), formatted: I18n.l(value_object.end_time, format: :full_date),
datetime: object.end_time.strftime('%Y/%m/%d %H:%M') datetime: value_object.end_time.strftime('%Y/%m/%d %H:%M')
} }
} }
end end

View file

@ -5,16 +5,16 @@ module RepositoryDatatable
def value def value
{ {
start_time: { start_time: {
formatted: I18n.l(object.start_time, format: :full_with_comma), formatted: I18n.l(value_object.start_time, format: :full_with_comma),
date_formatted: I18n.l(object.start_time, format: :full_date), date_formatted: I18n.l(value_object.start_time, format: :full_date),
time_formatted: I18n.l(object.start_time, format: :time), time_formatted: I18n.l(value_object.start_time, format: :time),
datetime: object.start_time.strftime('%Y/%m/%d %H:%M') datetime: value_object.start_time.strftime('%Y/%m/%d %H:%M')
}, },
end_time: { end_time: {
formatted: I18n.l(object.end_time, format: :full_with_comma), formatted: I18n.l(value_object.end_time, format: :full_with_comma),
date_formatted: I18n.l(object.end_time, format: :full_date), date_formatted: I18n.l(value_object.end_time, format: :full_date),
time_formatted: I18n.l(object.end_time, format: :time), time_formatted: I18n.l(value_object.end_time, format: :time),
datetime: object.end_time.strftime('%Y/%m/%d %H:%M') datetime: value_object.end_time.strftime('%Y/%m/%d %H:%M')
} }
} }
end end

View file

@ -4,16 +4,16 @@ module RepositoryDatatable
class RepositoryDateTimeValueSerializer < RepositoryBaseValueSerializer class RepositoryDateTimeValueSerializer < RepositoryBaseValueSerializer
def value def value
data = { data = {
formatted: I18n.l(object.data, format: :full_with_comma), formatted: I18n.l(value_object.data, format: :full_with_comma),
date_formatted: I18n.l(object.data, format: :full_date), date_formatted: I18n.l(value_object.data, format: :full_date),
time_formatted: I18n.l(object.data, format: :time), time_formatted: I18n.l(value_object.data, format: :time),
datetime: object.data.strftime('%Y/%m/%d %H:%M') datetime: value_object.data.strftime('%Y/%m/%d %H:%M')
} }
if scope.dig(:options, :reminders_enabled) if scope.dig(:options, :reminders_enabled)
reminder_delta = scope[:column].metadata['reminder_delta'] reminder_delta = scope[:column].metadata['reminder_delta']
if !scope[:repository].is_a?(RepositorySnapshot) && reminder_delta if !scope[:repository].is_a?(RepositorySnapshot) && reminder_delta
data[:reminder] = reminder_delta.to_i + DateTime.now.to_i >= object.data.to_i data[:reminder] = reminder_delta.to_i + DateTime.now.to_i >= value_object.data.to_i
end end
end end

View file

@ -4,14 +4,14 @@ module RepositoryDatatable
class RepositoryDateValueSerializer < RepositoryBaseValueSerializer class RepositoryDateValueSerializer < RepositoryBaseValueSerializer
def value def value
data = { data = {
formatted: I18n.l(object.data, format: :full_date), formatted: I18n.l(value_object.data, format: :full_date),
datetime: object.data.strftime('%Y/%m/%d %H:%M') datetime: value_object.data.strftime('%Y/%m/%d %H:%M')
} }
if scope.dig(:options, :reminders_enabled) if scope.dig(:options, :reminders_enabled)
reminder_delta = scope[:column].metadata['reminder_delta'] reminder_delta = scope[:column].metadata['reminder_delta']
if !scope[:repository].is_a?(RepositorySnapshot) && reminder_delta if !scope[:repository].is_a?(RepositorySnapshot) && reminder_delta
data[:reminder] = DateTime.now + reminder_delta.to_i.seconds >= object.data data[:reminder] = DateTime.now + reminder_delta.to_i.seconds >= value_object.data
end end
end end

View file

@ -5,8 +5,8 @@ module RepositoryDatatable
include InputSanitizeHelper include InputSanitizeHelper
def value def value
{ {
id: (object.repository_list_item&.id || ''), id: (value_object.repository_list_item&.id || ''),
text: (escape_input(object.data) || '') text: (escape_input(value_object.data) || '')
} }
end end
end end

View file

@ -4,7 +4,7 @@ module RepositoryDatatable
class RepositoryNumberValueSerializer < RepositoryBaseValueSerializer class RepositoryNumberValueSerializer < RepositoryBaseValueSerializer
def value def value
decimals = scope[:column].metadata.fetch('decimals', Constants::REPOSITORY_NUMBER_TYPE_DEFAULT_DECIMALS).to_i decimals = scope[:column].metadata.fetch('decimals', Constants::REPOSITORY_NUMBER_TYPE_DEFAULT_DECIMALS).to_i
object.data.round(decimals).to_s('F').remove(/.0+$/) value_object.data.round(decimals).to_s('F').remove(/.0+$/)
end end
end end
end end

View file

@ -5,9 +5,9 @@ module RepositoryDatatable
include InputSanitizeHelper include InputSanitizeHelper
def value def value
{ {
id: object.repository_status_item.id, id: value_object.repository_status_item.id,
icon: object.repository_status_item.icon, icon: value_object.repository_status_item.icon,
status: escape_input(object.repository_status_item.status) status: escape_input(value_object.repository_status_item.status)
} }
end end
end end

View file

@ -4,8 +4,8 @@ module RepositoryDatatable
class RepositoryStockConsumptionValueSerializer < RepositoryBaseValueSerializer class RepositoryStockConsumptionValueSerializer < RepositoryBaseValueSerializer
def value def value
{ {
consumed_stock_formatted: object.formatted, consumed_stock_formatted: value_object.formatted,
consumed_stock: object.data consumed_stock: value_object.data
} }
end end
end end

View file

@ -6,9 +6,9 @@ module RepositoryDatatable
def value def value
{ {
stock_formatted: object.formatted, stock_formatted: value_object.formatted,
stock_amount: object.data, stock_amount: value_object.data,
low_stock_threshold: object.low_stock_threshold low_stock_threshold: value_object.low_stock_threshold
} }
end end
end end

View file

@ -9,8 +9,8 @@ module RepositoryDatatable
def value def value
@user = scope[:user] @user = scope[:user]
{ {
view: custom_auto_link(object.data, simple_format: true, team: scope[:team]), view: custom_auto_link(value_object.data, simple_format: true, team: scope[:team]),
edit: object.data edit: value_object.data
} }
end end
end end

View file

@ -5,12 +5,12 @@ module RepositoryDatatable
def value def value
{ {
start_time: { start_time: {
formatted: I18n.l(object.start_time, format: :time), formatted: I18n.l(value_object.start_time, format: :time),
datetime: object.start_time.strftime('%Y/%m/%d %H:%M') datetime: value_object.start_time.strftime('%Y/%m/%d %H:%M')
}, },
end_time: { end_time: {
formatted: I18n.l(object.end_time, format: :time), formatted: I18n.l(value_object.end_time, format: :time),
datetime: object.end_time.strftime('%Y/%m/%d %H:%M') datetime: value_object.end_time.strftime('%Y/%m/%d %H:%M')
} }
} }
end end

View file

@ -4,8 +4,8 @@ module RepositoryDatatable
class RepositoryTimeValueSerializer < RepositoryBaseValueSerializer class RepositoryTimeValueSerializer < RepositoryBaseValueSerializer
def value def value
{ {
formatted: I18n.l(object.data, format: :time), formatted: I18n.l(value_object.data, format: :time),
datetime: object.data.strftime('%Y/%m/%d %H:%M') datetime: value_object.data.strftime('%Y/%m/%d %H:%M')
} }
end end
end end

View file

@ -2,6 +2,8 @@
module SmartAnnotations module SmartAnnotations
class TagToHtml class TagToHtml
REGEX = /\[\#(.*?)~(prj|exp|tsk|rep_item)~([0-9a-zA-Z]+)\]/.freeze
USER_REGEX = /\[@(.*?)~([0-9a-zA-Z]+)\]/.freeze
attr_reader :html attr_reader :html
def initialize(user, team, text, preview_repository = false) def initialize(user, team, text, preview_repository = false)
@ -10,7 +12,6 @@ module SmartAnnotations
private private
REGEX = /\[\#(.*?)~(prj|exp|tsk|rep_item)~([0-9a-zA-Z]+)\]/
OBJECT_MAPPINGS = { prj: Project, OBJECT_MAPPINGS = { prj: Project,
exp: Experiment, exp: Experiment,
tsk: MyModule, tsk: MyModule,