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'