From 26883af386af74a18dc5d9be113affd31e048750 Mon Sep 17 00:00:00 2001 From: zmagod Date: Thu, 8 Mar 2018 17:36:10 +0100 Subject: [PATCH] fix assigned ordering --- app/controllers/my_modules_controller.rb | 47 +++-- app/controllers/repository_rows_controller.rb | 4 +- app/helpers/repository_datatable_helper.rb | 21 +- app/models/repository.rb | 2 - app/models/repository_cell.rb | 4 + .../views/datatables/search_repository.rb | 21 -- app/services/repository_datatable_service.rb | 186 ++++++++++++++---- app/views/repository_rows/index.json.jbuilder | 6 +- ...180306074931_create_search_repositories.rb | 5 - db/schema.rb | 63 +----- db/views/search_repositories_v01.sql | 28 --- 11 files changed, 200 insertions(+), 187 deletions(-) delete mode 100644 app/models/views/datatables/search_repository.rb delete mode 100644 db/migrate/20180306074931_create_search_repositories.rb delete mode 100644 db/views/search_repositories_v01.sql diff --git a/app/controllers/my_modules_controller.rb b/app/controllers/my_modules_controller.rb index 50b71be62..ecee8d96e 100644 --- a/app/controllers/my_modules_controller.rb +++ b/app/controllers/my_modules_controller.rb @@ -12,9 +12,11 @@ class MyModulesController < ApplicationController toggle_task_state samples_index archive complete_my_module repository repository_index assign_repository_records unassign_repository_records] - before_action :load_vars_nested, only: %I[new create] - before_action :load_repository, only: %I[assign_repository_records - unassign_repository_records] + before_action :load_vars_nested, only: %i(new create) + before_action :load_repository, only: %i(assign_repository_records + unassign_repository_records + repository_index) + before_action :load_columns_mappings, only: :repository_index before_action :check_manage_permissions, only: %i(update destroy description due_date) before_action :check_view_info_permissions, only: :show @@ -366,20 +368,18 @@ class MyModulesController < ApplicationController # AJAX actions def repository_index - @repository = Repository.find_by_id(params[:repository_id]) - if @repository.nil? || !can_read_team?(@repository.team) - render_403 - else - respond_to do |format| - format.html - format.json do - render json: ::RepositoryDatatable.new(view_context, - @repository, - @my_module, - current_user) - end - end - end + @draw = params[:draw].to_i + per_page = params[:length] == '-1' ? 100 : params[:length].to_i + page = (params[:start].to_i / per_page) + 1 + records = RepositoryDatatableService.new(@repository, + params, + @columns_mappings, + current_user, + @my_module) + @assigned_rows = records.assigned_rows + @repository_row_count = records.repository_rows.count + @repository_rows = records.repository_rows.page(page).per(per_page) + render 'repository_rows/index.json' end # Submit actions @@ -598,7 +598,18 @@ class MyModulesController < ApplicationController def load_repository @repository = Repository.find_by_id(params[:repository_id]) - render_404 unless @repository && can_read_team?(@repository.team) + render_404 unless @repository + render_403 unless can_read_team?(@repository.team) + end + + def load_columns_mappings + # Make mappings of custom columns, so we have same id for every column + i = 5 + @columns_mappings = {} + @repository.repository_columns.order(:id).each do |column| + @columns_mappings[column.id] = i.to_s + i += 1 + end end def check_manage_permissions diff --git a/app/controllers/repository_rows_controller.rb b/app/controllers/repository_rows_controller.rb index ddb8dbd37..3366acf13 100644 --- a/app/controllers/repository_rows_controller.rb +++ b/app/controllers/repository_rows_controller.rb @@ -17,7 +17,9 @@ class RepositoryRowsController < ApplicationController page = (params[:start].to_i / per_page) + 1 records = RepositoryDatatableService.new(@repository, params, - @columns_mappings) + @columns_mappings, + current_user) + @assigned_rows = records.assigned_rows @repository_row_count = records.repository_rows.count @repository_rows = records.repository_rows.page(page).per(per_page) end diff --git a/app/helpers/repository_datatable_helper.rb b/app/helpers/repository_datatable_helper.rb index c93bdf09d..7604e7123 100644 --- a/app/helpers/repository_datatable_helper.rb +++ b/app/helpers/repository_datatable_helper.rb @@ -1,11 +1,15 @@ module RepositoryDatatableHelper include InputSanitizeHelper - def prepare_row_columns(repository_rows, repository, columns_mappings, team) + def prepare_row_columns(repository_rows, + repository, + columns_mappings, + team, + assigned_rows) parsed_records = [] repository_rows.each do |record| row = { 'DT_RowId': record.id, - '1': assigned_row(record), + '1': assigned_row(record, assigned_rows), '2': escape_input(record.name), '3': I18n.l(record.created_at, format: :full), '4': escape_input(record.created_by.full_name), @@ -21,7 +25,6 @@ module RepositoryDatatableHelper } # Add custom columns - # byebug record.repository_cells.each do |cell| row[columns_mappings[cell.repository_column.id]] = custom_auto_link( @@ -36,11 +39,11 @@ module RepositoryDatatableHelper parsed_records end - def assigned_row(record) - # if @assigned_rows && @assigned_rows.include?(record) - # " " - # else - " " - # end + def assigned_row(record, assigned_rows) + if assigned_rows&.include?(record) + " " + else + " " + end end end diff --git a/app/models/repository.rb b/app/models/repository.rb index ed5775492..fce0a148d 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -12,8 +12,6 @@ class Repository < ApplicationRecord inverse_of: :repository, dependent: :destroy has_many :report_elements, inverse_of: :repository, dependent: :destroy has_many :repository_list_items, inverse_of: :repository, dependent: :destroy - has_many :repository_searchable_rows, - class_name: '::Views::Datatables::SearchRepository' auto_strip_attributes :name, nullify: false validates :name, diff --git a/app/models/repository_cell.rb b/app/models/repository_cell.rb index 44ed0f77b..13f07f371 100644 --- a/app/models/repository_cell.rb +++ b/app/models/repository_cell.rb @@ -8,6 +8,10 @@ class RepositoryCell < ActiveRecord::Base validates :repository_row, uniqueness: { scope: :repository_column } + belongs_to :repository_text_value, optional: true, foreign_key: :value_id + belongs_to :repository_date_value, optional: true, foreign_key: :value_id + belongs_to :repository_list_value, optional: true, foreign_key: :value_id + private def repository_column_data_type diff --git a/app/models/views/datatables/search_repository.rb b/app/models/views/datatables/search_repository.rb deleted file mode 100644 index 2ff6a6f84..000000000 --- a/app/models/views/datatables/search_repository.rb +++ /dev/null @@ -1,21 +0,0 @@ -module Views - module Datatables - class SearchRepository < ApplicationRecord - belongs_to :repository - # def self.records(repository, search_value) - # # binding.pry - # # # where('repository_rows.repository_id', repository.id).to_a - # # where(repository_id: repository.id) - # # .where() - # end - - private - - # this isn't strictly necessary, but it will prevent - # rails from calling save, which would fail anyway. - def readonly? - true - end - end - end -end diff --git a/app/services/repository_datatable_service.rb b/app/services/repository_datatable_service.rb index 61fe08d12..5f77a049c 100644 --- a/app/services/repository_datatable_service.rb +++ b/app/services/repository_datatable_service.rb @@ -1,73 +1,179 @@ class RepositoryDatatableService - attr_reader :repository_rows + attr_reader :repository_rows, :assigned_rows - def initialize(repository, params, mappings) + def initialize(repository, params, mappings, user, my_module = nil) @mappings = mappings @repository = repository - process_query(params) + @mappings = mappings + @user = user + @my_module = my_module + @params = params + process_query end private - def process_query(params) - contitions = build_conditions(params) - if contitions[:search_value].present? - @repository_rows = search(contitions[:search_value]) + def process_query + contitions = build_conditions(@params) + order_obj = contitions[:order_by_column] + search_value = contitions[:search_value] + if search_value.present? + @repository_rows = sort_rows(order_obj, search(search_value)) else - @repository_rows = fetch_records + @repository_rows = sort_rows(order_obj, fetch_records) end - # byebug end def fetch_records - RepositoryRow.preload(:repository_columns, - :created_by, - repository_cells: :value) - .joins(:created_by) - .where(repository: @repository) + repository_rows = RepositoryRow.preload(:repository_columns, + :created_by, + repository_cells: :value) + .joins(:created_by) + .where(repository: @repository) + if @my_module + @assigned_rows = @my_module.repository_rows + .preload( + :repository_columns, + :created_by, + repository_cells: :value + ) + .joins(:created_by) + .where(repository: @repository) + return @assigned_rows if @params[:assigned] == 'assigned' + else + @assigned_rows = repository_rows.joins( + 'INNER JOIN my_module_repository_rows ON + (repository_rows.id = my_module_repository_rows.repository_row_id)' + ) + end + repository_rows end def search(value) - # binding.pry - filtered_rows = @repository.repository_searchable_rows.where( - 'name ILIKE :value - OR to_char(created_at, :time) ILIKE :value - OR user_full_name ILIKE :value - OR text_value ILIKE :value - OR date_value ILIKE :value - OR list_value ILIKE :value', - value: "%#{value}%", - time: "DD.MM.YYYY HH24:MI" - ).pluck(:id) - fetch_records.where(id: filtered_rows) + includes_json = { + repository_cells: [:repository_text_value, + repository_list_value: :repository_list_item ] + } + RepositoryRow .left_outer_joins(:created_by) + .left_outer_joins(includes_json) + .where(repository: @repository) + .where_attributes_like( + ['repository_rows.name', + 'users.full_name', + 'repository_text_values.data', + 'repository_list_items.data'], + value + ) end def build_conditions(params) search_value = params[:search][:value] - order_by_column = { column: params[:order][:column].to_i, - dir: params[:order][:dir] } + order = params[:order].values.first + order_by_column = { column: order[:column].to_i, + dir: order[:dir] } { search_value: search_value, order_by_column: order_by_column } end def sortable_columns - sort_array = [ + array = [ 'assigned', - 'RepositoryRow.name', - 'RepositoryRow.created_at', - 'User.full_name' + 'repository_rows.name', + 'repository_rows.created_at', + 'users.full_name' ] - - sort_array.push(*repository_columns_sort_by) - @sortable_columns = sort_array - end - - def repository_columns_sort_by - array = [] @repository.repository_columns.count.times do - array << 'RepositoryCell.value' + array << 'repository_cell.value' end array end + def sort_rows(column_obj, records) + dir = %w[DESC ASC].find { |dir| dir == column_obj[:dir].upcase } || 'ASC' + column_index = column_obj[:column] + col_order = @repository.repository_table_states + .find_by_user_id(@user.id) + .state['ColReorder'] + column_id = col_order[column_index].to_i + + if sortable_columns[column_id - 1] == 'assigned' + return records if @my_module && @params[:assigned] == 'assigned' + if @my_module + # Depending on the sort, order nulls first or + # nulls last on repository_cells association + return records.joins( + "LEFT OUTER JOIN my_module_repository_rows ON + (repository_rows.id = my_module_repository_rows.repository_row_id + AND (my_module_repository_rows.my_module_id = #{@my_module.id} OR + my_module_repository_rows.id IS NULL))" + ).order( + "my_module_repository_rows.id NULLS #{sort_null_direction(dir)}" + ) + else + return sort_assigned_records(records, dir) + end + elsif sortable_columns[column_id - 1] == 'repository_cell.value' + id = @mappings.key(column_id.to_s) + type = RepositoryColumn.find_by_id(id) + return records unless type + return select_type(type.data_type, records, id, dir) + else + return records.order("#{sortable_columns[column_id - 1]} #{dir}") + end + end + + def sort_assigned_records(records, direction) + assigned = records.joins(:my_module_repository_rows).distinct.pluck(:id) + unassigned = records.where.not(id: assigned).pluck(:id) + if direction == 'ASC' + ids = assigned + unassigned + elsif direction == 'DESC' + ids = unassigned + assigned + end + + order_by_index = ActiveRecord::Base.send( + :sanitize_sql_array, + ["position((',' || repository_rows.id || ',') in ?)", + ids.join(',') + ','] + ) + records.order(order_by_index) + end + + def select_type(type, records, id, dir) + return filter_by_text_value( + records, id, dir) if type == 'RepositoryTextValue' + return filter_by_list_value( + records, id, dir) if type == 'RepositoryListValue' + end + + def sort_null_direction(val) + val == 'ASC' ? 'LAST' : 'FIRST' + end + + def filter_by_text_value(records, id, dir) + return records.joins( + "LEFT OUTER JOIN (SELECT repository_cells.repository_row_id, + repository_text_values.data AS value + FROM repository_cells + INNER JOIN repository_text_values + ON repository_text_values.id = repository_cells.value_id + WHERE repository_cells.repository_column_id = #{id}) AS values + ON values.repository_row_id = repository_rows.id" + ).order("values.value #{dir}") + end + + def filter_by_list_value(records, id, dir) + return records.joins( + "LEFT OUTER JOIN (SELECT repository_cells.repository_row_id, + repository_list_items.data AS value + FROM repository_cells + INNER JOIN repository_list_values + ON repository_list_values.id = repository_cells.value_id + INNER JOIN repository_list_items + ON repository_list_values.repository_list_item_id = + repository_list_items.id + WHERE repository_cells.repository_column_id = #{id}) AS values + ON values.repository_row_id = repository_rows.id" + ).order("values.value #{dir}") + end end diff --git a/app/views/repository_rows/index.json.jbuilder b/app/views/repository_rows/index.json.jbuilder index 23af2e9f3..c747bd696 100644 --- a/app/views/repository_rows/index.json.jbuilder +++ b/app/views/repository_rows/index.json.jbuilder @@ -2,5 +2,9 @@ json.draw @draw json.recordsTotal @repository_rows.total_count json.recordsFiltered @repository_row_count json.data do - json.array! prepare_row_columns(@repository_rows, @repository, @columns_mappings, @repository.team) + json.array! prepare_row_columns(@repository_rows, + @repository, + @columns_mappings, + @repository.team, + @assigned_rows) end diff --git a/db/migrate/20180306074931_create_search_repositories.rb b/db/migrate/20180306074931_create_search_repositories.rb deleted file mode 100644 index 9311752f9..000000000 --- a/db/migrate/20180306074931_create_search_repositories.rb +++ /dev/null @@ -1,5 +0,0 @@ -class CreateSearchRepositories < ActiveRecord::Migration[5.1] - def change - create_view :search_repositories - end -end diff --git a/db/schema.rb b/db/schema.rb index a3a437072..4fac94123 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180306074931) do +ActiveRecord::Schema.define(version: 20180207095200) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -964,65 +964,4 @@ ActiveRecord::Schema.define(version: 20180306074931) do JOIN user_teams ON ((teams.id = user_teams.team_id))); SQL - create_view "search_repositories", sql_definition: <<-SQL - SELECT DISTINCT repository_rows.id, - repository_rows.repository_id, - repository_rows.created_by_id, - repository_rows.last_modified_by_id, - repository_rows.name, - repository_rows.created_at, - repository_rows.updated_at, - users.full_name AS user_full_name, - "values".text_value, - "values".date_value, - "values".list_value - FROM ((repository_rows - JOIN ( SELECT users_1.id, - users_1.full_name, - users_1.initials, - users_1.email, - users_1.encrypted_password, - users_1.reset_password_token, - users_1.reset_password_sent_at, - users_1.remember_created_at, - users_1.sign_in_count, - users_1.current_sign_in_at, - users_1.last_sign_in_at, - users_1.current_sign_in_ip, - users_1.last_sign_in_ip, - users_1.created_at, - users_1.updated_at, - users_1.avatar_file_name, - users_1.avatar_content_type, - users_1.avatar_file_size, - users_1.avatar_updated_at, - users_1.confirmation_token, - users_1.confirmed_at, - users_1.confirmation_sent_at, - users_1.unconfirmed_email, - users_1.invitation_token, - users_1.invitation_created_at, - users_1.invitation_sent_at, - users_1.invitation_accepted_at, - users_1.invitation_limit, - users_1.invited_by_type, - users_1.invited_by_id, - users_1.invitations_count, - users_1.tutorial_status, - users_1.current_team_id, - users_1.authentication_token, - users_1.settings - FROM users users_1) users ON ((users.id = repository_rows.created_by_id))) - LEFT JOIN ( SELECT repository_cells.repository_row_id, - repository_text_values.data AS text_value, - to_char(repository_date_values.data, 'DD.MM.YYYY HH24:MI'::text) AS date_value, - ( SELECT repository_list_items.data - FROM repository_list_items - WHERE (repository_list_items.id = repository_list_values.repository_list_item_id)) AS list_value - FROM (((repository_cells - JOIN repository_text_values ON ((repository_text_values.id = repository_cells.value_id))) - FULL JOIN repository_date_values ON ((repository_date_values.id = repository_cells.value_id))) - FULL JOIN repository_list_values ON ((repository_list_values.id = repository_cells.value_id)))) "values" ON (("values".repository_row_id = repository_rows.id))); - SQL - end diff --git a/db/views/search_repositories_v01.sql b/db/views/search_repositories_v01.sql deleted file mode 100644 index e9112c506..000000000 --- a/db/views/search_repositories_v01.sql +++ /dev/null @@ -1,28 +0,0 @@ -SELECT DISTINCT - repository_rows.*, - users.full_name AS user_full_name, - values.text_value AS text_value, - values.date_value AS date_value, - values.list_value AS list_value - FROM repository_rows - INNER JOIN ( - SELECT users.* - FROM users - ) AS users - ON users.id = repository_rows.created_by_id - LEFT OUTER JOIN ( - SELECT repository_cells.repository_row_id, - repository_text_values.data AS text_value, - to_char(repository_date_values.data, 'DD.MM.YYYY HH24:MI') AS date_value, - ( SELECT repository_list_items.data - FROM repository_list_items - WHERE repository_list_items.id = repository_list_values.repository_list_item_id ) AS list_value - FROM repository_cells - INNER JOIN repository_text_values - ON repository_text_values.id = repository_cells.value_id - FULL OUTER JOIN repository_date_values - ON repository_date_values.id = repository_cells.value_id - FUll OUTER JOIN repository_list_values - ON repository_list_values.id = repository_cells.value_id - ) AS values - ON values.repository_row_id = repository_rows.id