Skip to content

update manifest conditional format by asset type #4752

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

Closed
wants to merge 5 commits into from
Closed
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
81 changes: 81 additions & 0 deletions app/models/sample_manifest/column_conditional_format_updater.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# frozen_string_literal: true
# This class is responsible for updating the conditional formatting for columns in the manifest.
# The formatting is applied based on the asset type, specifically targeting columns related to library assets.
#
# Example Usage:
# conditional_updater = ConditionalFormatUpdater.new(columns: columns, asset_type: 'library')
# conditional_updater.update_by_asset_type
# SampleManifest::Generator
class SampleManifest::ColumnConditionalFormatUpdater
# Predefined conditional formats for required fields.
REQUIRED_COLUMNS_CONDITIONAL_FORMATS = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Messaged re an alternative/ additional way of doing this :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Messaged re an alternative/ additional way of doing this :)

The proposed solution is to modify the columns.yml config file. This will result in modifying the column format for all templates where the columns in question are present. Here, the column background is set to red, which may cause confusion, as the columns may be present without necessarily having the "not empty" constraint. For example, in the multiplex library manifest types, the library_type column is present but not required to be filled in.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is the code that makes library_type required for one manifest type but not the other?

I would prefer a different approach, as this adds a new place where columns are configured - it's all quite neat at the moment, in columns.yml.

Can't find the words to explain this properly, but I also don't think that 'by asset type' will turn out to be a common thing to control the requiredness.

Another thing we can do is, if we want a column to behave differently in one manifest type vs another, we make a new column definition for it - e.g. see collected_by_for_cardinal, collected_by_for_controls and collected_by_for_scrna_core, which all save to the 'collected_by' column, but have different configuration, to be used in different manifest types.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is the code that makes library_type required for one manifest type but not the other?

It is happening here:

 def update_column_formatting_by_asset_type
    if library_asset?
      update_columns_formatting(%w[library_type insert_size_from insert_size_to], REQUIRED_COLUMNS_CONDITIONAL_FORMATS)
    else
      @columns
    end
  end

I would prefer a different approach, as this adds a new place where columns are configured - it's all quite neat at the moment, in columns.yml.

Can't find the words to explain this properly, but I also don't think that 'by asset type' will turn out to be a common thing to control the requiredness.

Another thing we can do is, if we want a column to behave differently in one manifest type vs another, we make a new column definition for it - e.g. see collected_by_for_cardinal, collected_by_for_controls and collected_by_for_scrna_core, which all save to the 'collected_by' column, but have different configuration, to be used in different manifest types.

Thank you for pointing to right direct, this is seems to put a simpler approach!

SequencescapeExcel::ConditionalFormatting.new(
name: 'empty_mandatory_cell',
options: {
'type' => :cellIs,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is cellIs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not really sure actually, I basically copy past the object as they are defined in the conditional_format yml file

'formula' => 'FALSE',
'operator' => :equal,
'priority' => 1
},
style: {
'bg_color' => 'DC3545',
'type' => :dxf
}
),
SequencescapeExcel::ConditionalFormatting.new(
name: 'is_error',
options: {
'type' => :expression,
'priority' => 2
},
style: {
'bg_color' => 'FF0000',
'type' => :dxf
}
)
].freeze

# Initializes the conditional format updater with columns and asset type.
#
# @param columns [Array<Column>] The collection of columns to be updated.
# @param asset_type [String] The type of the asset (e.g., 'library', 'library_plate').
def initialize(columns:, asset_type:)
@columns = columns
@asset_type = asset_type
end

# Checks if the asset is a library asset.
# @return [Boolean] Returns true if the asset type is either 'library' or 'library_plate'.
def library_asset?
%w[library library_plate].include?(@asset_type)
end

# Updates the conditional formatting for columns based on the asset type.
# Currently, it applies conditional formatting for library assets only.
# If the asset type is not library, no formatting is applied.
#
# @return [Array<Column>] The updated columns with conditional formatting applied.
def update_column_formatting_by_asset_type
if library_asset?
update_columns_formatting(%w[library_type insert_size_from insert_size_to], REQUIRED_COLUMNS_CONDITIONAL_FORMATS)
else
@columns
end
end

# Updates the conditional formatting for the specified columns.
# It applies the given set of conditional formats to the matching columns.
#
# @param columns_to_update [Array<string>] The list of column names to update.
# @param conditional_formats [Array<SequencescapeExcel::ConditionalFormatting>] The conditional formats to apply.
# @return [Array<Column>] The updated columns with the new conditional formatting.
def update_columns_formatting(columns_to_update, conditional_formats)
columns_to_update
.filter_map { |rc| @columns.find_by(:name, rc) }
.each do |column|
column.conditional_formattings.reset!
conditional_formats.each { |format| column.conditional_formattings.add(format) }
end
@columns
end
end
18 changes: 17 additions & 1 deletion app/models/sample_manifest/generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,24 @@ def initialize(params, user, configuration)
@params = params
end

# Retrieves and caches the columns for the current template configuration.
# This method first fetches the columns based on the provided template parameter.
# Then, it applies conditional formatting updates based on the asset type
# using the `SampleManifest::ColumnConditionalFormatUpdater` class.
#
# The conditional formatting is adjusted dynamically depending on whether
# the asset type requires specific formatting rules (e.g., for library assets).
#
# @return [Array<Column>] The columns associated with the specified template, with conditional formatting
# updated if any.

def columns
@columns ||= configuration.columns.find(params[:template])
return unless asset_type
SampleManifest::ColumnConditionalFormatUpdater.new(
columns: @columns,
asset_type: asset_type
).update_column_formatting_by_asset_type
end

def print_job_required?
Expand Down Expand Up @@ -105,7 +121,7 @@ def attributes
end

def asset_type
configuration.manifest_types.find_by(params[:template]).asset_type
configuration.manifest_types.find_by(params[:template])&.asset_type
end

# Retrieves the value of the rows_per_well attribute from the manifest_types.yml config.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# frozen_string_literal: true

require 'rails_helper'

RSpec.describe SampleManifest::ColumnConditionalFormatUpdater do
let(:columns) { instance_double(SequencescapeExcel::Column) }

describe '#library_asset?' do
subject { described_class.new(columns:, asset_type:).library_asset? }

context 'when asset type is a library' do
let(:asset_type) { 'library_plate' }

it { is_expected.to be true }
end

context 'when asset type is not a library' do
let(:asset_type) { 'plate' }

it { is_expected.to be false }
end
end

describe '#update_column_formatting_by_asset_type' do
before do
SampleManifestExcel.configure do |config|
config.folder = File.join('spec', 'data', 'sample_manifest_excel')
config.load!
end
end

context 'when asset type is a library' do
let(:columns) { SampleManifestExcel.configuration.columns.tube_library_with_tag_sequences }
let(:asset_type) { 'library' }
let(:updater) { described_class.new(columns:, asset_type:) }
let(:required_column) { columns.find_by(:name, 'library_type') }
let(:non_required_column) { columns.find_by(:name, 'reference_genome') }

# rubocop:disable RSpec/MultipleExpectations
it 'updates the conditional format for the required columns' do
init_required_column_format = required_column.conditional_formattings.find_by(:name, 'empty_cell')
expect(init_required_column_format).not_to be_nil
updater.update_column_formatting_by_asset_type
expect(required_column.conditional_formattings.find_by(:name, 'empty_cell')).to be_nil
expect(required_column.conditional_formattings.find_by(:name, 'empty_mandatory_cell')).not_to be_nil
end
# rubocop:enable RSpec/MultipleExpectations

it 'does not change the format for non-required columns' do
initial_format = non_required_column.conditional_formattings.dup
updater.update_column_formatting_by_asset_type
expect(non_required_column.conditional_formattings).to eq(initial_format)
end
end

context 'when asset type is not a library' do
let(:columns) { SampleManifestExcel.configuration.columns.tube_multiplexed_library }
let(:asset_type) { 'multiplexed_library' }
let(:updater) { described_class.new(columns:, asset_type:) }
let(:library_type_column) { columns.find_by(:name, 'library_type') }

it 'does not update the conditional format' do
initial_format = library_type_column.conditional_formattings.dup

updater.update_column_formatting_by_asset_type

expect(library_type_column.conditional_formattings).to eq(initial_format)
end
end
end
end
11 changes: 11 additions & 0 deletions spec/models/sample_manifest/generator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
{ template: template, study_id: study.id, supplier_id: supplier.id, count: '4' }.with_indifferent_access
end

let(:column_updater_spy) { instance_double(SampleManifest::ColumnConditionalFormatUpdater) }

after(:all) { SampleManifestExcel.reset! }

it 'model name is sample manifest' do
Expand Down Expand Up @@ -91,6 +93,15 @@
expect(generator.sample_manifest.asset_type).to eq(configuration.manifest_types.find_by('tube_full').asset_type)
end

it 'calls ColumnConditionalFormatUpdater during sample manifest generation' do
generator = described_class.new(attributes.merge(template: 'tube_full'), user, configuration)
allow(column_updater_spy).to receive(:update_column_formatting_by_asset_type)
allow(SampleManifest::ColumnConditionalFormatUpdater).to receive(:new).and_return(column_updater_spy)
generator.execute

expect(column_updater_spy).to have_received(:update_column_formatting_by_asset_type)
end

it 'prints labels if barcode printer is present' do
allow(LabelPrinter::PmbClient).to receive(:get_label_template_by_name).and_return('data' => [{ 'id' => 15 }])

Expand Down