Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Sample::Query form object #866

Merged
merged 7 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 13 additions & 26 deletions app/components/samples/table_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -33,27 +33,15 @@
"w-4 h-4 mr-2.5 text-primary-600 bg-slate-100 border-slate-300 rounded focus:ring-primary-500 dark:focus:ring-primary-600 dark:ring-offset-slate-800 focus:ring-2 dark:bg-slate-700 dark:border-slate-600" %>
<% end %>

<% if column == :attachments_updated_at %>
<%= render Ransack::SortComponent.new(
ransack_obj: @q,
label: t(".#{column}"),
url: helpers.sorting_url(@q, :attachments_updated_at_nulls_last),
field: :attachments_updated_at_nulls_last,
data: {
turbo_action: "replace",
},
) %>
<% else %>
<%= render Ransack::SortComponent.new(
ransack_obj: @q,
label: t(".#{column}"),
url: helpers.sorting_url(@q, column),
field: column,
data: {
turbo_action: "replace",
},
) %>
<% end %>
<%= render SortComponent.new(
sort: @search_params[:sort],
label: t(".#{column}"),
url: sort_url(column),
field: column,
data: {
turbo_action: "replace",
},
) %>
<% end %>
<% end %>
<% @metadata_fields.each do |field| %>
Expand All @@ -62,11 +50,10 @@
scope: 'col',
classes: class_names('px-3 py-3')
) do %>
<%= render Ransack::SortComponent.new(
ransack_obj: @q,
<%= render SortComponent.new(
sort: @search_params[:sort],
label: field,
url:
helpers.sorting_url(@q, URI.encode_www_form_component("metadata_#{field}")),
url: sort_url("metadata_#{field}"),
field: "metadata_#{field}",
data: {
turbo_action: "replace",
Expand Down Expand Up @@ -127,7 +114,7 @@
) %>
</span>
<% end %>
<% elsif column == :project %>
<% elsif column == :project_id %>
<%= link_to sample.project.puid,
project_samples_path(sample.project),
data: {
Expand Down
25 changes: 20 additions & 5 deletions app/components/samples/table_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ module Samples
class TableComponent < Component
include Ransack::Helpers::FormHelper

# rubocop:disable Naming/MethodParameterName,Metrics/ParameterLists
# rubocop:disable Metrics/ParameterLists
def initialize(
samples,
namespace,
pagy,
q,
has_samples: true,
abilities: {},
metadata_fields: [],
Expand All @@ -24,7 +23,6 @@ def initialize(
@samples = samples
@namespace = namespace
@pagy = pagy
@q = q
@has_samples = has_samples
@abilities = abilities
@metadata_fields = metadata_fields
Expand All @@ -34,9 +32,11 @@ def initialize(
@renders_row_actions = @row_actions.select { |_key, value| value }.count.positive?
@system_arguments = system_arguments

@sort_key, @sort_direction = search_params[:sort].split

@columns = columns
end
# rubocop:enable Naming/MethodParameterName,Metrics/ParameterLists
# rubocop:enable Metrics/ParameterLists

def system_arguments
{ tag: 'div' }.deep_merge(@system_arguments).tap do |args|
Expand Down Expand Up @@ -77,11 +77,26 @@ def select_samples_url(**)
end
end

def sort_url(field)
sort_string = if field.to_s == @sort_key && @sort_direction == 'asc'
"#{field} desc"
else
"#{field} asc"
end

if @namespace.type == 'Group'
group_samples_url(@namespace, q: { sort: sort_string }, limit: @pagy.limit)
else
namespace_project_samples_url(@namespace.parent, @namespace.project, q: { sort: sort_string },
limit: @pagy.limit)
end
end

private

def columns
columns = %i[puid name]
columns << :project if @namespace.type == 'Group'
columns << :project_id if @namespace.type == 'Group'
columns += %i[created_at updated_at attachments_updated_at]
columns
end
Expand Down
4 changes: 4 additions & 0 deletions app/components/sort_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<%= render Viral::BaseComponent.new(tag: 'a', href: url, type: "button", classes: class_names('inline-flex items-center'), **system_arguments) do %>
<%= label %>
<span class="flex-grow-0 ml-2 flex items-center"><%= viral_icon(name: icon, classes: "w-4 h-4 inline-block") %></span>
<% end %>
20 changes: 20 additions & 0 deletions app/components/sort_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

# Component for sort in table headers
class SortComponent < Component
attr_reader :label, :field, :url, :system_arguments

def initialize(sort:, label:, url:, field:, **system_arguments)
@sort_key, @sort_direction = sort.split
@label = label
@url = url
@field = field
@system_arguments = system_arguments
end

def icon
return unless @sort_key.to_s == URI.encode_www_form_component(@field.to_s)

@sort_direction == 'asc' ? 'arrow_up' : 'arrow_down'
end
end
1 change: 1 addition & 0 deletions app/controllers/concerns/storable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def store(session_key, value)
# @param value [Hash] the value to merge with the existing value in the session.
def update_store(search_key, value)
session[search_key] = (session[search_key] || {}).merge(value)
session[search_key].reject! { |_, val| val.blank? || val == [''] }
get_store(search_key)
end

Expand Down
26 changes: 15 additions & 11 deletions app/controllers/groups/samples_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@
include Storable

before_action :group, :current_page
before_action :process_samples, only: %i[index search select]
include Sortable
before_action :query, only: %i[index search select]

def index
@timestamp = DateTime.current
@pagy, @samples = pagy_with_metadata_sort(@q.result)
@pagy, @samples = pagy(@query.results, limit: params[:limit] || 20)
@has_samples = authorized_samples.count.positive?
end

Expand All @@ -22,12 +21,12 @@

def select
authorize! @group, to: :sample_listing?
@selected_sample_ids = []
@sample_ids = []

respond_to do |format|
format.turbo_stream do
if params[:select].present?
@selected_sample_ids = @q.result.where(updated_at: ..params[:timestamp].to_datetime).select(:id).pluck(:id)
@sample_ids = @query.results.where(updated_at: ..params[:timestamp].to_datetime).select(:id).pluck(:id)
end
end
end
Expand All @@ -39,6 +38,10 @@
@group = Group.find_by_full_path(params[:group_id]) # rubocop:disable Rails/DynamicFindBy
end

def authorized_projects
authorized_scope(Project, type: :relation, as: :group_projects, scope_options: { group: @group })
end

def authorized_samples
authorized_scope(Sample, type: :relation, as: :namespace_samples,
scope_options: { namespace: @group }).includes(project: { namespace: [{ parent: :route },
Expand Down Expand Up @@ -71,20 +74,21 @@
fields_for_namespace(namespace: @group, show_fields: @search_params && @search_params[:metadata].to_i == 1)
end

def process_samples
def query
authorize! @group, to: :sample_listing?

@search_params = search_params
set_metadata_fields
query_parser = Irida::SearchSyntax::Ransack.new(text: :name_or_puid_cont, metadata_fields: @fields)
@parsed_params = query_parser.parse(@search_params.fetch(:name_or_puid_cont, nil))
@q = authorized_samples.ransack(@search_params.except(:name_or_puid_cont).merge(@parsed_params))

@query = Sample::Query.new(@search_params.except(:metadata).merge({ project_ids: authorized_projects.select(:id) }))

Check warning on line 83 in app/controllers/groups/samples_controller.rb

View workflow job for this annotation

GitHub Actions / runner / rubocop

[rubocop] reported by reviewdog 🐶 Line is too long. [122/120] Raw Output: app/controllers/groups/samples_controller.rb:83:121: C: Layout/LineLength: Line is too long. [122/120]
end

def search_params
updated_params = update_store(search_key, params[:q].present? ? params[:q].to_unsafe_h : {})

if updated_params[:metadata].to_i.zero? && updated_params[:s].present? && updated_params[:s].match?(/metadata_/)
updated_params[:s] = default_sort
if !updated_params.key?(:sort) ||
(updated_params[:metadata].to_i.zero? && updated_params[:sort]&.match?(/metadata_/))
updated_params[:sort] = 'updated_at desc'
update_store(search_key, updated_params)
end
updated_params
Expand Down
25 changes: 12 additions & 13 deletions app/controllers/projects/samples_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ class SamplesController < Projects::ApplicationController # rubocop:disable Metr

before_action :sample, only: %i[show edit update view_history_version]
before_action :current_page
before_action :process_samples, only: %i[index search select]
include Sortable
before_action :query, only: %i[index search select]

def index
@timestamp = DateTime.current
@pagy, @samples = pagy_with_metadata_sort(@q.result)
@pagy, @samples = pagy(@query.results, limit: params[:limit] || 20)
@has_samples = @project.samples.size.positive?
end

Expand Down Expand Up @@ -79,12 +78,12 @@ def update

def select
authorize! @project, to: :sample_listing?
@samples = []
@sample_ids = []

respond_to do |format|
format.turbo_stream do
if params[:select].present?
@samples = @q.result.where(updated_at: ..params[:timestamp].to_datetime).select(:id)
@sample_ids = @query.results.where(updated_at: ..params[:timestamp].to_datetime).select(:id).pluck(:id)
end
end
end
Expand Down Expand Up @@ -138,22 +137,22 @@ def search_key
:"#{controller_name}_#{@project.id}_search_params"
end

def process_samples
def query
authorize! @project, to: :sample_listing?

@search_params = search_params

set_metadata_fields
query_parser = Irida::SearchSyntax::Ransack.new(text: :name_or_puid_cont, metadata_fields: @fields)
@parsed_params = query_parser.parse(@search_params.fetch(:name_or_puid_cont, nil))
@q = load_samples.ransack(@search_params.except(:name_or_puid_cont).merge(@parsed_params))

@query = Sample::Query.new(@search_params.except(:metadata).merge({ project_ids: [@project.id] }))
end

def search_params
updated_params = update_store(search_key, params[:q].present? ? params[:q].to_unsafe_h : {})
updated_params = update_store(search_key,
params[:q].present? ? params[:q].to_unsafe_h : {}).with_indifferent_access

if updated_params[:metadata].to_i.zero? && updated_params[:s].present? && updated_params[:s].match?(/metadata_/)
updated_params[:s] = default_sort
if !updated_params.key?(:sort) ||
(updated_params[:metadata].to_i.zero? && updated_params[:sort]&.match?(/metadata_/))
updated_params[:sort] = 'updated_at desc'
update_store(search_key, updated_params)
end
updated_params
Expand Down
52 changes: 52 additions & 0 deletions app/models/sample/query.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

# model to represent sample search form
class Sample::Query # rubocop:disable Style/ClassAndModuleChildren
include ActiveModel::Model
include ActiveModel::Attributes

attribute :column, :string
attribute :direction, :string
attribute :name_or_puid_cont, :string
attribute :name_or_puid_in, default: -> { [] }
attribute :project_ids, default: -> { [] }
attribute :sort, :string, default: 'updated_at desc'

validates :direction, inclusion: { in: %w[asc desc] }
validates :project_ids, length: { minimum: 1 }

def initialize(...)
super
self.sort = sort
end

def sort=(value)
super
column, direction = sort.split
assign_attributes(column:, direction:)
end

def results
return Sample.none unless valid?

sort_samples.ransack(ransack_params).result
end

private

def ransack_params
{
name_or_puid_cont: name_or_puid_cont,
name_or_puid_in: name_or_puid_in
}.compact
end

def sort_samples(scope = Sample.where(project_id: project_ids))
if column.starts_with? 'metadata_'
field = column.gsub('metadata_', '')
scope.order(Sample.metadata_sort(field, direction))
else
scope.order("#{column} #{direction}")
end
end
end
3 changes: 1 addition & 2 deletions app/views/groups/samples/_table.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
samples,
group,
pagy,
@q,
has_samples: @has_samples,
abilities: {
select_samples:
allowed_to?(:submit_workflow?, group) ||
allowed_to?(:export_data?, group),
},
metadata_fields: @fields,
search_params: @parsed_params,
search_params: @search_params,
empty: {
title: t(:"groups.samples.table.no_samples"),
description: t(:"groups.samples.table.no_associated_samples"),
Expand Down
Loading
Loading