2023-03-01 22:04:53 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2017-06-30 21:20:27 +08:00
|
|
|
class ProtocolsDatatable < CustomDatatable
|
2016-07-21 19:11:15 +08:00
|
|
|
# Needed for sanitize_sql_like method
|
|
|
|
include ActiveRecord::Sanitization::ClassMethods
|
2017-01-04 22:04:12 +08:00
|
|
|
include InputSanitizeHelper
|
2022-12-06 22:54:50 +08:00
|
|
|
include Rails.application.routes.url_helpers
|
2023-05-16 16:29:00 +08:00
|
|
|
include Canaid::Helpers::PermissionsHelper
|
2016-07-21 19:11:15 +08:00
|
|
|
|
2023-03-01 22:04:53 +08:00
|
|
|
PREFIXED_ID_SQL = "('#{Protocol::ID_PREFIX}' || COALESCE(\"protocols\".\"parent_id\", \"protocols\".\"id\"))"
|
|
|
|
|
2016-07-21 19:11:15 +08:00
|
|
|
def_delegator :@view, :linked_children_protocol_path
|
2022-05-27 18:49:10 +08:00
|
|
|
def_delegator :@view, :protocol_path
|
2016-07-21 19:11:15 +08:00
|
|
|
|
2017-01-25 16:48:49 +08:00
|
|
|
def initialize(view, team, type, user)
|
2016-07-21 19:11:15 +08:00
|
|
|
super(view)
|
2017-01-25 16:48:49 +08:00
|
|
|
@team = team
|
2023-03-01 22:04:53 +08:00
|
|
|
@type = type # :active or :archived
|
2016-07-21 19:11:15 +08:00
|
|
|
@user = user
|
|
|
|
end
|
|
|
|
|
|
|
|
def sortable_columns
|
|
|
|
@sortable_columns ||= [
|
2023-03-01 22:04:53 +08:00
|
|
|
'name',
|
|
|
|
'adjusted_parent_id',
|
2022-12-09 19:44:41 +08:00
|
|
|
'nr_of_versions',
|
|
|
|
'protocol_keywords_str',
|
2023-03-07 16:58:36 +08:00
|
|
|
'nr_of_linked_tasks',
|
2022-12-09 19:44:41 +08:00
|
|
|
'nr_of_assigned_users',
|
|
|
|
'full_username_str',
|
2023-03-01 22:04:53 +08:00
|
|
|
'published_on',
|
|
|
|
'updated_at',
|
2023-03-01 18:38:11 +08:00
|
|
|
'archived_full_username_str',
|
2023-03-01 22:04:53 +08:00
|
|
|
'archived_on'
|
2016-07-21 19:11:15 +08:00
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
def searchable_columns
|
|
|
|
@searchable_columns ||= [
|
2023-03-01 22:04:53 +08:00
|
|
|
'Protocol.name',
|
2023-01-25 04:17:50 +08:00
|
|
|
'Protocol.archived_on',
|
|
|
|
'Protocol.published_on',
|
2023-03-01 22:04:53 +08:00
|
|
|
"Protocol.#{PREFIXED_ID_SQL}",
|
|
|
|
'Protocol.updated_at',
|
|
|
|
'ProtocolKeyword.name'
|
2016-07-21 19:11:15 +08:00
|
|
|
]
|
|
|
|
end
|
|
|
|
|
2023-03-01 22:04:53 +08:00
|
|
|
def as_json(_options = {})
|
2016-07-21 19:11:15 +08:00
|
|
|
{
|
2017-06-30 21:20:27 +08:00
|
|
|
draw: dt_params[:draw].to_i,
|
2023-03-07 16:42:18 +08:00
|
|
|
recordsTotal: get_raw_records_base.distinct.count,
|
|
|
|
recordsFiltered: records.present? ? records.first.filtered_count : 0,
|
2017-06-30 21:20:27 +08:00
|
|
|
data: data
|
2016-07-21 19:11:15 +08:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
# A hack that overrides the new_search_contition method default behavior of the ajax-datatables-rails gem
|
|
|
|
# now the method checks if the column is the created_at or updated_at and generate a custom SQL to parse
|
|
|
|
# it back to the caller method
|
|
|
|
def new_search_condition(column, value)
|
2022-11-22 17:55:41 +08:00
|
|
|
model, column = column.split('.', 2)
|
2016-07-21 19:11:15 +08:00
|
|
|
model = model.constantize
|
|
|
|
case column
|
2023-03-01 22:04:53 +08:00
|
|
|
when PREFIXED_ID_SQL
|
|
|
|
casted_column = ::Arel::Nodes::SqlLiteral.new(PREFIXED_ID_SQL)
|
2016-07-21 19:11:15 +08:00
|
|
|
when 'published_on'
|
|
|
|
casted_column = ::Arel::Nodes::NamedFunction.new('CAST',
|
|
|
|
[ Arel.sql("to_char( protocols.created_at, '#{ formated_date }' ) AS VARCHAR") ] )
|
|
|
|
when 'updated_at'
|
|
|
|
casted_column = ::Arel::Nodes::NamedFunction.new('CAST',
|
|
|
|
[ Arel.sql("to_char( protocols.updated_at, '#{ formated_date }' ) AS VARCHAR") ] )
|
|
|
|
else
|
|
|
|
casted_column = ::Arel::Nodes::NamedFunction.new('CAST',
|
|
|
|
[model.arel_table[column.to_sym].as(typecast)])
|
|
|
|
end
|
2023-03-07 16:42:18 +08:00
|
|
|
casted_column.matches("%#{ActiveRecord::Base.sanitize_sql_like(value)}%")
|
2016-07-21 19:11:15 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
# Returns json of current protocols (already paginated)
|
|
|
|
def data
|
2022-12-09 19:44:41 +08:00
|
|
|
records.map do |record|
|
2023-03-01 22:04:53 +08:00
|
|
|
parent = record.parent || record
|
2022-12-09 19:44:41 +08:00
|
|
|
{
|
|
|
|
DT_RowId: record.id,
|
2023-01-20 21:29:25 +08:00
|
|
|
DT_RowAttr: {
|
2023-03-07 16:52:31 +08:00
|
|
|
'data-permissions-url': permissions_protocol_path(parent),
|
|
|
|
'data-versions-url': versions_modal_protocol_path(parent)
|
2023-01-20 21:29:25 +08:00
|
|
|
},
|
2023-03-01 22:04:53 +08:00
|
|
|
'1': name_html(parent),
|
|
|
|
'2': parent.code,
|
2022-12-09 19:44:41 +08:00
|
|
|
'3': versions_html(record),
|
|
|
|
'4': keywords_html(record),
|
|
|
|
'5': modules_html(record),
|
2023-03-01 22:04:53 +08:00
|
|
|
'6': access_html(parent),
|
|
|
|
'7': published_by(record),
|
|
|
|
'8': published_timestamp(record),
|
|
|
|
'9': modified_timestamp(record),
|
2023-01-25 04:17:50 +08:00
|
|
|
'10': escape_input(record.archived_full_username_str),
|
|
|
|
'11': (I18n.l(record.archived_on, format: :full) if record.archived_on)
|
2016-07-21 19:11:15 +08:00
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-03-07 16:42:18 +08:00
|
|
|
def fetch_records
|
|
|
|
super.select('COUNT("protocols"."id") OVER() AS filtered_count')
|
|
|
|
end
|
|
|
|
|
2023-02-21 21:57:43 +08:00
|
|
|
def filter_protocols_records(records)
|
|
|
|
if params[:name_and_keywords].present?
|
|
|
|
records = records.where_attributes_like(['protocols.name', 'protocol_keywords.name'], params[:name_and_keywords])
|
|
|
|
end
|
|
|
|
|
|
|
|
if params[:published_on_from].present?
|
|
|
|
records = records.where('protocols.published_on > ?', params[:published_on_from])
|
|
|
|
end
|
|
|
|
records = records.where('protocols.published_on < ?', params[:published_on_to]) if params[:published_on_to].present?
|
|
|
|
records = records.where('protocols.updated_at > ?', params[:modified_on_from]) if params[:modified_on_from].present?
|
|
|
|
records = records.where('protocols.updated_at < ?', params[:modified_on_to]) if params[:modified_on_to].present?
|
|
|
|
records = records.where(protocols: { published_by_id: params[:published_by] }) if params[:published_by].present?
|
2023-03-06 18:50:37 +08:00
|
|
|
|
|
|
|
if params[:members].present?
|
2023-03-08 19:22:46 +08:00
|
|
|
records = records.where(all_user_assignments: { user_id: params[:members] })
|
2023-03-06 18:50:37 +08:00
|
|
|
end
|
2023-02-21 21:57:43 +08:00
|
|
|
|
|
|
|
if params[:archived_on_from].present?
|
|
|
|
records = records.where('protocols.archived_on > ?', params[:archived_on_from])
|
|
|
|
end
|
|
|
|
records = records.where('protocols.archived_on < ?', params[:archived_on_to]) if params[:archived_on_to].present?
|
|
|
|
|
|
|
|
records = records.where(protocols: { archived_by_id: params[:archived_by] }) if params[:archived_by].present?
|
|
|
|
|
|
|
|
if params[:has_draft].present?
|
2023-03-09 21:46:43 +08:00
|
|
|
records =
|
|
|
|
records
|
|
|
|
.joins("LEFT OUTER JOIN protocols protocol_drafts " \
|
|
|
|
"ON protocol_drafts.protocol_type = #{Protocol.protocol_types[:in_repository_draft]} " \
|
|
|
|
"AND (protocol_drafts.parent_id = protocols.id OR protocol_drafts.parent_id = protocols.parent_id)")
|
|
|
|
.where('protocols.protocol_type = ? OR protocol_drafts.id IS NOT NULL',
|
|
|
|
Protocol.protocol_types[:in_repository_draft])
|
2023-02-21 21:57:43 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
records
|
|
|
|
end
|
|
|
|
|
2016-07-21 19:11:15 +08:00
|
|
|
def get_raw_records_base
|
2023-03-17 17:43:50 +08:00
|
|
|
records = Protocol.latest_available_versions(@team)
|
2023-03-01 22:04:53 +08:00
|
|
|
|
2023-03-07 16:42:18 +08:00
|
|
|
records = @type == :archived ? records.archived : records.active
|
|
|
|
|
2023-05-09 22:23:29 +08:00
|
|
|
records.viewable_by_user(@user, @team)
|
2023-03-07 16:42:18 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
# Query database for records (this will be later paginated and filtered)
|
|
|
|
# after that "data" function will return json
|
|
|
|
def get_raw_records
|
2016-07-21 19:11:15 +08:00
|
|
|
records =
|
2023-03-07 16:42:18 +08:00
|
|
|
get_raw_records_base
|
2023-03-01 22:04:53 +08:00
|
|
|
.preload(:parent, :latest_published_version, :draft, :protocol_keywords, user_assignments: %i(user user_role))
|
|
|
|
.joins("LEFT OUTER JOIN protocols protocol_versions " \
|
|
|
|
"ON protocol_versions.protocol_type = #{Protocol.protocol_types[:in_repository_published_version]} " \
|
|
|
|
"AND protocol_versions.parent_id = protocols.parent_id")
|
2023-03-07 16:58:36 +08:00
|
|
|
.joins("LEFT OUTER JOIN protocols self_linked_task_protocols " \
|
|
|
|
"ON self_linked_task_protocols.protocol_type = #{Protocol.protocol_types[:linked]} " \
|
|
|
|
"AND self_linked_task_protocols.parent_id = protocols.id")
|
|
|
|
.joins("LEFT OUTER JOIN protocols parent_linked_task_protocols " \
|
|
|
|
"ON parent_linked_task_protocols.protocol_type = #{Protocol.protocol_types[:linked]} " \
|
|
|
|
"AND parent_linked_task_protocols.parent_id = protocols.parent_id")
|
|
|
|
.joins("LEFT OUTER JOIN protocols version_linked_task_protocols " \
|
|
|
|
"ON version_linked_task_protocols.protocol_type = #{Protocol.protocol_types[:linked]} " \
|
|
|
|
"AND version_linked_task_protocols.parent_id = protocol_versions.id " \
|
|
|
|
"AND version_linked_task_protocols.parent_id != protocols.id")
|
2023-03-01 22:04:53 +08:00
|
|
|
.joins('LEFT OUTER JOIN "protocol_protocol_keywords" ' \
|
2022-12-09 19:44:41 +08:00
|
|
|
'ON "protocol_protocol_keywords"."protocol_id" = "protocols"."id"')
|
2023-03-01 22:04:53 +08:00
|
|
|
.joins('LEFT OUTER JOIN "protocol_keywords" ' \
|
2022-12-09 19:44:41 +08:00
|
|
|
'ON "protocol_protocol_keywords"."protocol_keyword_id" = "protocol_keywords"."id"')
|
2023-03-07 16:42:18 +08:00
|
|
|
.joins('LEFT OUTER JOIN "users" "archived_users" ON "archived_users"."id" = "protocols"."archived_by_id"')
|
|
|
|
.joins('LEFT OUTER JOIN "users" ON "users"."id" = "protocols"."published_by_id"')
|
2023-03-21 17:59:31 +08:00
|
|
|
.joins('LEFT OUTER JOIN "user_assignments" "all_user_assignments" ' \
|
|
|
|
'ON "all_user_assignments"."assignable_type" = \'Protocol\' ' \
|
|
|
|
'AND "all_user_assignments"."assignable_id" = "protocols"."id"')
|
2023-03-07 16:42:18 +08:00
|
|
|
.group('"protocols"."id"')
|
2016-07-21 19:11:15 +08:00
|
|
|
|
2023-02-21 21:57:43 +08:00
|
|
|
records = filter_protocols_records(records)
|
2023-03-07 16:42:18 +08:00
|
|
|
records.select(
|
2022-12-09 19:44:41 +08:00
|
|
|
'"protocols".*',
|
2023-03-01 22:04:53 +08:00
|
|
|
'COALESCE("protocols"."parent_id", "protocols"."id") AS adjusted_parent_id',
|
2023-02-28 00:17:12 +08:00
|
|
|
'STRING_AGG(DISTINCT("protocol_keywords"."name"), \', \') AS "protocol_keywords_str"',
|
2023-03-07 16:42:18 +08:00
|
|
|
"CASE WHEN protocols.protocol_type = #{Protocol.protocol_types[:in_repository_draft]} " \
|
2023-03-07 16:58:36 +08:00
|
|
|
"THEN 0 ELSE COUNT(DISTINCT(\"protocol_versions\".\"id\")) + 1 " \
|
2023-03-01 22:04:53 +08:00
|
|
|
"END AS nr_of_versions",
|
2023-03-07 16:58:36 +08:00
|
|
|
'(COUNT(DISTINCT("self_linked_task_protocols"."id")) + ' \
|
|
|
|
'COUNT(DISTINCT("parent_linked_task_protocols"."id")) + ' \
|
|
|
|
'COUNT(DISTINCT("version_linked_task_protocols"."id"))) AS nr_of_linked_tasks',
|
2023-03-08 19:22:46 +08:00
|
|
|
'COUNT(DISTINCT("all_user_assignments"."id")) AS "nr_of_assigned_users"',
|
2023-01-25 04:17:50 +08:00
|
|
|
'MAX("users"."full_name") AS "full_username_str"', # "Hack" to get single username
|
|
|
|
'MAX("archived_users"."full_name") AS "archived_full_username_str"'
|
2016-07-21 19:11:15 +08:00
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Various helper methods
|
|
|
|
|
2016-12-13 17:39:18 +08:00
|
|
|
def name_html(record)
|
2023-03-01 22:04:53 +08:00
|
|
|
path =
|
|
|
|
if record.in_repository_published_original? && record.latest_published_version.present?
|
|
|
|
protocol_path(record.latest_published_version)
|
|
|
|
else
|
|
|
|
protocol_path(record)
|
|
|
|
end
|
2023-05-16 16:29:00 +08:00
|
|
|
|
|
|
|
if can_read_protocol_in_repository?(@user, record)
|
|
|
|
"<a href='#{path}'>#{escape_input(record.name)}</a>"
|
|
|
|
else
|
|
|
|
# team admin can only see recod name
|
|
|
|
"<span class='not-clickable-record'>#{escape_input(record.name)}</span>"
|
|
|
|
end
|
2016-12-13 17:39:18 +08:00
|
|
|
end
|
|
|
|
|
2016-07-21 19:11:15 +08:00
|
|
|
def keywords_html(record)
|
2023-03-01 22:04:53 +08:00
|
|
|
if record.protocol_keywords.blank?
|
2023-03-23 20:40:21 +08:00
|
|
|
I18n.t('protocols.no_keywords')
|
2016-07-21 19:11:15 +08:00
|
|
|
else
|
|
|
|
res = []
|
2023-03-01 22:04:53 +08:00
|
|
|
record.protocol_keywords.sort_by { |kw| kw.name.downcase }.each do |kw|
|
2023-03-20 22:36:49 +08:00
|
|
|
sanitized_kw = escape_input(kw.name)
|
2023-03-01 22:04:53 +08:00
|
|
|
res << "<a href='#' data-action='filter' data-param='#{sanitized_kw}'>#{sanitized_kw}</a>"
|
2016-07-21 19:11:15 +08:00
|
|
|
end
|
2017-01-26 22:52:46 +08:00
|
|
|
res.join(', ')
|
2016-07-21 19:11:15 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def modules_html(record)
|
2020-04-28 18:30:43 +08:00
|
|
|
"<a href='#' data-action='load-linked-children'" \
|
2023-03-07 16:58:36 +08:00
|
|
|
"data-url='#{linked_children_protocol_path(record.parent || record)}'>" \
|
|
|
|
"#{record.nr_of_linked_tasks}" \
|
2018-08-21 18:08:14 +08:00
|
|
|
"</a>"
|
2016-07-21 19:11:15 +08:00
|
|
|
end
|
|
|
|
|
2022-12-09 19:44:41 +08:00
|
|
|
def versions_html(record)
|
|
|
|
@view.controller
|
2023-05-16 16:29:00 +08:00
|
|
|
.render_to_string(partial: 'protocols/index/protocol_versions.html.erb',
|
|
|
|
locals: { protocol: record, readable: can_read_protocol_in_repository?(@user, record) })
|
2022-12-09 19:44:41 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def access_html(record)
|
|
|
|
@view.controller.render_to_string(partial: 'protocols/index/protocol_access.html.erb', locals: { protocol: record })
|
|
|
|
end
|
|
|
|
|
2023-03-01 22:04:53 +08:00
|
|
|
def published_by(record)
|
|
|
|
return '' if record.published_by.blank?
|
|
|
|
|
|
|
|
escape_input(record.published_by.full_name)
|
|
|
|
end
|
|
|
|
|
|
|
|
def published_timestamp(record)
|
|
|
|
return '' if record.published_on.blank?
|
|
|
|
|
|
|
|
I18n.l(record.published_on, format: :full)
|
|
|
|
end
|
|
|
|
|
|
|
|
def modified_timestamp(record)
|
|
|
|
I18n.l(record.updated_at, format: :full)
|
2016-07-21 19:11:15 +08:00
|
|
|
end
|
|
|
|
end
|