diff --git a/app/controllers/location_reports_controller.rb b/app/controllers/location_reports_controller.rb index 5f49df9c1e..96bfe28a0d 100644 --- a/app/controllers/location_reports_controller.rb +++ b/app/controllers/location_reports_controller.rb @@ -60,8 +60,7 @@ def location_report_params # rubocop:todo Metrics/MethodLength :study_id, :start_date, :end_date, - :barcodes, - :barcodes_text, + retention_instructions: [], faculty_sponsor_ids: [], plate_purpose_ids: [] ) diff --git a/app/models/labware.rb b/app/models/labware.rb index bbcfb32bf3..c08689f9f9 100644 --- a/app/models/labware.rb +++ b/app/models/labware.rb @@ -155,12 +155,21 @@ def receptacle_proxy } # Used for location report + def self.map_retention_instructions(values) + return if values.blank? + + values.filter_map { |v| Labware.retention_instructions[v] } + end + def self.search_for_labware(params) with_faculty_sponsor_ids(params[:faculty_sponsor_ids] || nil) .with_study_id(params[:study_id] || nil) .with_plate_purpose_ids(params[:plate_purpose_ids] || nil) .created_between(params[:start_date], params[:end_date]) .filter_by_barcode(params[:barcodes] || nil) + .with_retention_instructions( + map_retention_instructions(params[:retention_instructions]) + ) .distinct end @@ -186,6 +195,10 @@ def self.search_for_count_of_labware(params) scope :with_plate_purpose_ids, ->(plate_purpose_ids) { where(plate_purpose_id: plate_purpose_ids) if plate_purpose_ids.present? } + scope :with_retention_instructions, ->(retention_instructions) { + where(retention_instruction: retention_instructions) if retention_instructions.present? + } + scope :created_between, ->(start_date, end_date) do where(created_at: (start_date.midnight..(end_date || Time.current).end_of_day)) if start_date.present? diff --git a/app/models/location_report.rb b/app/models/location_report.rb index bb39e0f675..1404fe204c 100644 --- a/app/models/location_report.rb +++ b/app/models/location_report.rb @@ -9,6 +9,7 @@ class LocationReport < ApplicationRecord serialize :faculty_sponsor_ids, type: Array, coder: YAML serialize :plate_purpose_ids, type: Array, coder: YAML serialize :barcodes, type: Array, coder: YAML + serialize :retention_instructions, type: Array, coder: YAML self.per_page = 20 enum :report_type, { type_selection: 0, type_labwhere: 1 } @@ -49,7 +50,7 @@ def check_location_barcode end def check_any_select_field_present - attr_list = %i[faculty_sponsor_ids study_id start_date end_date plate_purpose_ids barcodes] + attr_list = %i[faculty_sponsor_ids study_id start_date end_date plate_purpose_ids barcodes retention_instructions] if attr_list.all? { |attr| send(attr).blank? } errors.add(:base, I18n.t('location_reports.errors.no_selection_fields_filled')) end @@ -119,7 +120,6 @@ def generate_report_rows # rubocop:todo Metrics/MethodLength end yield column_headers - labware_list.each do |cur_labware| if cur_labware.studies.present? cur_labware.studies.each { |cur_study| yield(generate_report_row(cur_labware, cur_study)) } @@ -175,13 +175,15 @@ def generate_study_cols_for_row(cur_study) cols << (cur_study.study_metadata.faculty_sponsor&.name || 'Unknown') end - def search_for_labware_by_selection - params = { faculty_sponsor_ids:, study_id:, start_date:, end_date:, plate_purpose_ids:, barcodes: } + def search_for_labware_by_selection # rubocop:todo Metrics/AbcSize + params = { faculty_sponsor_ids:, study_id:, start_date:, end_date:, plate_purpose_ids:, barcodes:, + retention_instructions: } count = Labware.search_for_count_of_labware(params) if count > configatron.fetch(:location_reports_fetch_count_max, 25000) errors.add(:base, I18n.t('location_reports.errors.too_many_labwares_found', count:)) return [] end + # Only plates and tubes are currently supported by this report Labware.search_for_labware(params).filter { |labware| labware.is_a?(Plate) || labware.is_a?(Tube) } end diff --git a/app/models/location_report/location_report_form.rb b/app/models/location_report/location_report_form.rb index e501629a42..c016e8d760 100644 --- a/app/models/location_report/location_report_form.rb +++ b/app/models/location_report/location_report_form.rb @@ -10,7 +10,8 @@ class LocationReport::LocationReportForm include ActiveModel::AttributeMethods # Attributes - attr_accessor :user, :report_type, :faculty_sponsor_ids, :study_id, :start_date, :end_date, :plate_purpose_ids + attr_accessor :user, :report_type, :faculty_sponsor_ids, :study_id, :start_date, :end_date, :retention_instructions, + :plate_purpose_ids attr_accessor :barcodes_text attr_reader :name, :location_barcode @@ -44,7 +45,8 @@ def location_report start_date: start_date&.to_datetime, end_date: end_date&.to_datetime, plate_purpose_ids: plate_purpose_ids, - barcodes: barcodes + barcodes: barcodes, + retention_instructions: retention_instructions ) end diff --git a/app/views/location_reports/_location_labware_selection_form.html.erb b/app/views/location_reports/_location_labware_selection_form.html.erb index 72f7c6fe80..7378ed111e 100644 --- a/app/views/location_reports/_location_labware_selection_form.html.erb +++ b/app/views/location_reports/_location_labware_selection_form.html.erb @@ -31,6 +31,11 @@ +
+ <%= f.label 'retention_instructions', 'Retention instructions' %> + <%= f.select :retention_instructions, options_for_select(retention_instruction_option_for_select, location_report_form.retention_instructions), { include_hidden: false }, { class: 'form-control select2', :multiple => true } %> +
+
<%= f.label 'plate_purpose_ids', 'Labware purposes (can select multiple)' %> <%= f.select :plate_purpose_ids, options_for_select(Purpose.alphabetical.pluck(:name, :id), location_report_form.plate_purpose_ids), { include_hidden: false }, { class: 'form-control select2', :multiple => true } %> diff --git a/db/migrate/20260325134057_add_retention_instructions_to_location_reports.rb b/db/migrate/20260325134057_add_retention_instructions_to_location_reports.rb new file mode 100644 index 0000000000..60ad428001 --- /dev/null +++ b/db/migrate/20260325134057_add_retention_instructions_to_location_reports.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true +class AddRetentionInstructionsToLocationReports < ActiveRecord::Migration[7.2] + def change + add_column :location_reports, :retention_instructions, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index cd96dd237f..e56c2035b5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -637,6 +637,7 @@ t.string "report_filename" t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false + t.string "retention_instructions" t.index ["study_id"], name: "index_location_reports_on_study_id" t.index ["user_id"], name: "index_location_reports_on_user_id" end diff --git a/spec/models/location_report_spec.rb b/spec/models/location_report_spec.rb index 4af5d8f946..cec00c5b81 100644 --- a/spec/models/location_report_spec.rb +++ b/spec/models/location_report_spec.rb @@ -44,18 +44,10 @@ let(:plt_1_created) { plate_1.created_at.strftime('%Y-%m-%d %H:%M:%S') } let(:plt_1_received_date) { plt_1_asset_audit.created_at.strftime('%Y-%m-%d %H:%M:%S') } - # add retention instruction metadata to plate 1 custom metadatum collection - let(:retention_key) { 'retention_instruction' } - let(:retention_value) { 'Long term storage' } - let(:plate_1_custom_metadatum_collection) { create(:custom_metadatum_collection, asset: plate_1, user: user) } - let(:plate_1_custom_metadatum) do - create( - :custom_metadatum, - custom_metadatum_collection: plate_1_custom_metadatum_collection, - key: retention_key, - value: retention_value - ) - end + # use the labware retention_instruction enum column directly + let(:retention_value_long_term) { 'long_term_storage' } + let(:retention_value_return_after_two_years) { 'return_to_customer_after_2_years' } + let(:plate_1_set_long_term_storage) { plate_1.update!(retention_instruction: :long_term_storage) } let(:plate_2) do create( @@ -82,16 +74,7 @@ let(:plt_3_created) { plate_3.created_at.strftime('%Y-%m-%d %H:%M:%S') } let(:plt_3_received_date) { 'Unknown' } - # add retention instruction metadata to plate 3 custom metadatum collection - let(:plate_3_custom_metadatum_collection) { create(:custom_metadatum_collection, asset: plate_3, user: user) } - let(:plate_3_custom_metadatum) do - create( - :custom_metadatum, - custom_metadatum_collection: plate_3_custom_metadatum_collection, - key: retention_key, - value: retention_value - ) - end + let(:plate_3_return_after_two_years) { plate_3.update!(retention_instruction: :return_to_customer_after_2_years) } let(:tube_1) do create( @@ -104,15 +87,7 @@ let(:tube_1_purpose) { tube_1.purpose.name } let(:tube_1_created) { tube_1.created_at.strftime('%Y-%m-%d %H:%M:%S') } let(:tube_1_received_date) { 'Unknown' } - let(:tube_1_custom_metadatum_collection) { create(:custom_metadatum_collection, asset: tube_1, user: user) } - let(:tube_1_custom_metadatum) do - create( - :custom_metadatum, - custom_metadatum_collection: tube_1_custom_metadatum_collection, - key: retention_key, - value: retention_value - ) - end + let(:tube_1_set_long_term_storage) { tube_1.update!(retention_instruction: :long_term_storage) } let(:tube_rack) { create(:tube_rack, size: 96) } @@ -145,7 +120,8 @@ start_date:, end_date:, plate_purpose_ids:, - barcodes: + barcodes:, + retention_instructions: ) end let(:report_type) { nil } @@ -157,6 +133,7 @@ let(:end_date) { nil } let(:plate_purpose_ids) { nil } let(:barcodes) { nil } + let(:retention_instructions) { nil } describe 'validations' do context 'when no report type is set' do @@ -244,7 +221,7 @@ let(:plt_1_line) do # rubocop:todo Layout/LineLength - "#{plate_1.machine_barcode},#{plate_1.human_barcode},#{plt_1_purpose},#{plt_1_created},#{plt_1_received_date},#{locn_prefix} - Shelf 1,LabWhere,#{retention_value},#{study_1.name},#{study_1.id},#{study_1_sponsor.name}" + "#{plate_1.machine_barcode},#{plate_1.human_barcode},#{plt_1_purpose},#{plt_1_created},#{plt_1_received_date},#{locn_prefix} - Shelf 1,LabWhere,#{retention_value_long_term},#{study_1.name},#{study_1.id},#{study_1_sponsor.name}" # rubocop:enable Layout/LineLength end let(:plt_2_line_1) do @@ -259,12 +236,12 @@ end let(:plt_3_line) do # rubocop:todo Layout/LineLength - "#{plate_3.machine_barcode},#{plate_3.human_barcode},#{plt_3_purpose},#{plt_3_created},#{plt_3_received_date},#{locn_prefix} - Shelf 3,LabWhere,#{retention_value},#{study_2.name},#{study_2.id},#{study_2_sponsor.name}" + "#{plate_3.machine_barcode},#{plate_3.human_barcode},#{plt_3_purpose},#{plt_3_created},#{plt_3_received_date},#{locn_prefix} - Shelf 3,LabWhere,#{retention_value_return_after_two_years},#{study_2.name},#{study_2.id},#{study_2_sponsor.name}" # rubocop:enable Layout/LineLength end let(:tube_1_line) do # rubocop:todo Layout/LineLength - "#{tube_1.machine_barcode},#{tube_1.human_barcode},#{tube_1_purpose},#{tube_1_created},#{tube_1_received_date},#{locn_prefix} - Shelf 4,LabWhere,#{retention_value},#{study_1.name},#{study_1.id},#{study_1_sponsor.name}" + "#{tube_1.machine_barcode},#{tube_1.human_barcode},#{tube_1_purpose},#{tube_1_created},#{tube_1_received_date},#{locn_prefix} - Shelf 4,LabWhere,#{retention_value_long_term},#{study_1.name},#{study_1.id},#{study_1_sponsor.name}" # rubocop:enable Layout/LineLength end @@ -280,9 +257,9 @@ stub_lwclient_labware_find_by_bc(lw_barcode:, lw_locn_name:, lw_locn_parentage:) end - plate_1_custom_metadatum - plate_3_custom_metadatum - tube_1_custom_metadatum + plate_1_set_long_term_storage + plate_3_return_after_two_years + tube_1_set_long_term_storage end context 'dates only' do @@ -473,6 +450,47 @@ it_behaves_like 'a successful report' end + + context 'with retention instructions' do + let(:start_date) { '2016-01-01 00:00:00' } + let(:end_date) { '2016-11-01 00:00:00' } + let(:retention_instructions) { %w[long_term_storage] } + # Only plate_1, tube_1 have retention instructions set longterm_storage, + # plate_3 is set to return_to_customer_after_2_years so should not be included + let(:expected_lines) do + [ + headers_line, + plt_1_line, + tube_1_line + ] + end + + it_behaves_like 'a successful report' + end + + context 'with multiple retention instruction filters' do + let(:start_date) { '2016-01-01 00:00:00' } + let(:end_date) { '2016-11-01 00:00:00' } + let(:retention_instructions) { %w[long_term_storage return_to_customer_after_2_years] } + let(:plate_3_return_after_two_years) do + plate_3.update!(retention_instruction: :return_to_customer_after_2_years) + end + let(:plt_3_line) do + # rubocop:todo Layout/LineLength + "#{plate_3.machine_barcode},#{plate_3.human_barcode},#{plt_3_purpose},#{plt_3_created},#{plt_3_received_date},#{locn_prefix} - Shelf 3,LabWhere,#{retention_value_return_after_two_years},#{study_2.name},#{study_2.id},#{study_2_sponsor.name}" + # rubocop:enable Layout/LineLength + end + let(:expected_lines) do + [ + headers_line, + plt_1_line, + plt_3_line, + tube_1_line + ] + end + + it_behaves_like 'a successful report' + end end context 'by labwhere location' do @@ -492,8 +510,8 @@ stub_lwclient_locn_children(location_barcode, []) stub_lwclient_locn_labwares(location_barcode, []) - plate_1_custom_metadatum - plate_3_custom_metadatum + plate_1_set_long_term_storage + plate_3_return_after_two_years end it_behaves_like 'a successful report' @@ -503,7 +521,7 @@ let(:location_barcode) { 'locn-1-at-lvl-1' } let(:plt_1_line) do # rubocop:todo Layout/LineLength - "#{plate_1.machine_barcode},#{plate_1.human_barcode},#{plt_1_purpose},#{plt_1_created},#{plt_1_received_date},#{locn_prefix} - Shelf 1,LabWhere,#{retention_value},#{study_1.name},#{study_1.id},#{study_1_sponsor.name}" + "#{plate_1.machine_barcode},#{plate_1.human_barcode},#{plt_1_purpose},#{plt_1_created},#{plt_1_received_date},#{locn_prefix} - Shelf 1,LabWhere,#{retention_value_long_term},#{study_1.name},#{study_1.id},#{study_1_sponsor.name}" # rubocop:enable Layout/LineLength end let(:expected_lines) { [headers_line, plt_1_line] } @@ -520,7 +538,7 @@ stub_lwclient_locn_labwares(location_barcode, [p1]) stub_lwclient_labware_find_by_bc(p1) - plate_1_custom_metadatum + plate_1_set_long_term_storage end it_behaves_like 'a successful report' @@ -530,7 +548,7 @@ let(:location_barcode) { 'locn-1-at-lvl-1' } let(:plt_1_line) do # rubocop:todo Layout/LineLength - "#{plate_1.machine_barcode},#{plate_1.human_barcode},#{plt_1_purpose},#{plt_1_created},#{plt_1_received_date},#{locn_prefix} - Shelf 1 - Box 1,LabWhere,#{retention_value},#{study_1.name},#{study_1.id},#{study_1_sponsor.name}" + "#{plate_1.machine_barcode},#{plate_1.human_barcode},#{plt_1_purpose},#{plt_1_created},#{plt_1_received_date},#{locn_prefix} - Shelf 1 - Box 1,LabWhere,#{retention_value_long_term},#{study_1.name},#{study_1.id},#{study_1_sponsor.name}" # rubocop:enable Layout/LineLength end let(:plt_2_line_1) do @@ -545,7 +563,7 @@ end let(:tube_1_line) do # rubocop:todo Layout/LineLength - "#{tube_1.machine_barcode},#{tube_1.human_barcode},#{tube_1_purpose},#{tube_1_created},#{tube_1_received_date},#{locn_prefix} - Shelf 1 - Box 1,LabWhere,#{retention_value},#{study_1.name},#{study_1.id},#{study_1_sponsor.name}" + "#{tube_1.machine_barcode},#{tube_1.human_barcode},#{tube_1_purpose},#{tube_1_created},#{tube_1_received_date},#{locn_prefix} - Shelf 1 - Box 1,LabWhere,#{retention_value_long_term},#{study_1.name},#{study_1.id},#{study_1_sponsor.name}" # rubocop:enable Layout/LineLength end let(:expected_lines) { [headers_line, plt_1_line, plt_2_line_1, plt_2_line_2, tube_1_line] } @@ -596,8 +614,8 @@ stub_lwclient_labware_find_by_bc(p2) stub_lwclient_labware_find_by_bc(t1) - plate_1_custom_metadatum - tube_1_custom_metadatum + plate_1_set_long_term_storage + tube_1_set_long_term_storage end it_behaves_like 'a successful report' @@ -607,7 +625,7 @@ let(:location_barcode) { 'locn-1-at-lvl-1' } let(:plt_1_line) do # rubocop:todo Layout/LineLength - "#{plate_1.machine_barcode},#{plate_1.human_barcode},#{plt_1_purpose},#{plt_1_created},#{plt_1_received_date},#{locn_prefix} - Shelf 1 - Box 1,LabWhere,#{retention_value},#{study_1.name},#{study_1.id},#{study_1_sponsor.name}" + "#{plate_1.machine_barcode},#{plate_1.human_barcode},#{plt_1_purpose},#{plt_1_created},#{plt_1_received_date},#{locn_prefix} - Shelf 1 - Box 1,LabWhere,#{retention_value_long_term},#{study_1.name},#{study_1.id},#{study_1_sponsor.name}" # rubocop:enable Layout/LineLength end let(:plt_2_line_1) do @@ -664,7 +682,7 @@ stub_lwclient_locn_labwares(locn_lvl2_b2[:locn_barcode], [p2]) stub_lwclient_labware_find_by_bc(p2) - plate_1_custom_metadatum + plate_1_set_long_term_storage end it_behaves_like 'a successful report' @@ -675,7 +693,7 @@ # rubocop:todo Layout/LineLength let(:plt_1_line) do - "#{plate_1.machine_barcode},#{plate_1.human_barcode},#{plt_1_purpose},#{plt_1_created},#{plt_1_received_date},#{locn_prefix} - Shelf 1,LabWhere,#{retention_value},#{study_1.name},#{study_1.id},#{study_1_sponsor.name}" + "#{plate_1.machine_barcode},#{plate_1.human_barcode},#{plt_1_purpose},#{plt_1_created},#{plt_1_received_date},#{locn_prefix} - Shelf 1,LabWhere,#{retention_value_long_term},#{study_1.name},#{study_1.id},#{study_1_sponsor.name}" end let(:plt_2_line_1) do "#{plate_2.machine_barcode},#{plate_2.human_barcode},#{plt_2_purpose},#{plt_2_created},#{plt_2_received_date},#{locn_prefix} - Shelf 1 - Tray 1,LabWhere,Unknown,#{study_1.name},#{study_1.id},#{study_1_sponsor.name}" @@ -684,10 +702,10 @@ "#{plate_2.machine_barcode},#{plate_2.human_barcode},#{plt_2_purpose},#{plt_2_created},#{plt_2_received_date},#{locn_prefix} - Shelf 1 - Tray 1,LabWhere,Unknown,#{study_2.name},#{study_2.id},#{study_2_sponsor.name}" end let(:plt_3_line) do - "#{plate_3.machine_barcode},#{plate_3.human_barcode},#{plt_3_purpose},#{plt_3_created},#{plt_3_received_date},#{locn_prefix} - Shelf 1 - Tray 1 - Box 1,LabWhere,#{retention_value},#{study_2.name},#{study_2.id},#{study_2_sponsor.name}" + "#{plate_3.machine_barcode},#{plate_3.human_barcode},#{plt_3_purpose},#{plt_3_created},#{plt_3_received_date},#{locn_prefix} - Shelf 1 - Tray 1 - Box 1,LabWhere,#{retention_value_return_after_two_years},#{study_2.name},#{study_2.id},#{study_2_sponsor.name}" end let(:tube_1_line) do - "#{tube_1.machine_barcode},#{tube_1.human_barcode},#{tube_1_purpose},#{tube_1_created},#{tube_1_received_date},#{locn_prefix} - Shelf 1 - Tray 1 - Box 1,LabWhere,#{retention_value},#{study_1.name},#{study_1.id},#{study_1_sponsor.name}" + "#{tube_1.machine_barcode},#{tube_1.human_barcode},#{tube_1_purpose},#{tube_1_created},#{tube_1_received_date},#{locn_prefix} - Shelf 1 - Tray 1 - Box 1,LabWhere,#{retention_value_long_term},#{study_1.name},#{study_1.id},#{study_1_sponsor.name}" end # rubocop:enable Layout/LineLength @@ -744,9 +762,9 @@ stub_lwclient_labware_find_by_bc(p3) stub_lwclient_labware_find_by_bc(t1) - plate_1_custom_metadatum - plate_3_custom_metadatum - tube_1_custom_metadatum + plate_1_set_long_term_storage + plate_3_return_after_two_years + tube_1_set_long_term_storage end it_behaves_like 'a successful report'