Skip to content

Commit

Permalink
Merge pull request #4367 from sanger/y24-313-scrna-pooling-v2
Browse files Browse the repository at this point in the history
Y24-313 scRNA pooling calculation
  • Loading branch information
andrewsparkes authored Sep 25, 2024
2 parents f3999e9 + 000caa1 commit ea6d267
Show file tree
Hide file tree
Showing 17 changed files with 221 additions and 10 deletions.
12 changes: 12 additions & 0 deletions app/controllers/api/v2/request_metadata_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: true

module Api
module V2
# Provides a JSON API controller for RequestMetadata
# See: http://jsonapi-resources.com/ for JSONAPI::Resource documentation
class RequestMetadataController < JSONAPI::ResourceController
# By default JSONAPI::ResourceController provides most the standard
# behaviour, and in many cases this file may be left empty.
end
end
end
9 changes: 9 additions & 0 deletions app/models/pbmc_pooling_customer_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

# A class for customer requests that need the extra metadata fields used for PBMC pooling calculations
class PbmcPoolingCustomerRequest < CustomerRequest
has_metadata as: Request do
custom_attribute(:number_of_samples_per_pool, integer: true, required: false, default: nil)
custom_attribute(:cells_per_chip_well, integer: true, required: false, default: nil)
end
end
48 changes: 48 additions & 0 deletions app/resources/api/v2/request_metadata_resource.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# frozen_string_literal: true

module Api
module V2
# @todo This documentation does not yet include a detailed description of what this resource represents.
# @todo This documentation does not yet include detailed descriptions for relationships, attributes and filters.
# @todo This documentation does not yet include any example usage of the API via cURL or similar.
#
# @note Access this resource via the `/api/v2/requests_metadata/` endpoint.
#
# Provides a JSON:API representation of {Request::Metadata}.
#
# For more information about JSON:API see the [JSON:API Specifications](https://jsonapi.org/format/)
# or look at the [JSONAPI::Resources](http://jsonapi-resources.com/) package for Sequencescape's implementation
# of the JSON:API standard.
class RequestMetadataResource < BaseResource
# NB. request_metadata has been added to config/initializers/inflections.rb to make this class name
# work otherwise it expects RequestMetadatumResource

# Sets add_model_hint true by default, this allows updates from Limber, otherwise get a
# 500 error as it looks for resource Api::V2::MetadatumResource
model_name 'Request::Metadata'

# Associations:
has_one :request

###
# Attributes
###

# @!attribute [r] number_of_samples_per_pool
# @return [Int] the number_of_samples_per_pool.
attribute :number_of_samples_per_pool, readonly: true

# @!attribute [r] cells_per_chip_well
# @return [Int] the cells_per_chip_well.
attribute :cells_per_chip_well, readonly: true

# Filters

# Custom methods
# These shouldn't be used for business logic, and a more about
# I/O and isolating implementation details.

# Class method overrides
end
end
end
1 change: 1 addition & 0 deletions app/resources/api/v2/request_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class RequestResource < BaseResource
has_one :primer_panel
has_one :pre_capture_pool
has_many :poly_metadata, as: :metadatable, class_name: 'PolyMetadatum'
has_one :request_metadata, class_name: 'RequestMetadata', foreign_key_on: :related

# Attributes
attribute :uuid, readonly: true
Expand Down
3 changes: 3 additions & 0 deletions app/resources/api/v2/sample_metadata_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ module V2
# or look at the [JSONAPI::Resources](http://jsonapi-resources.com/) package for Sequencescape's implementation
# of the JSON:API standard.
class SampleMetadataResource < BaseResource
# NB. sample_metadata has been added to config/initializers/inflections.rb to make this class name
# work otherwise it expects SampleMetadatumResource

# Set add_model_hint true to allow updates from Limber, otherwise get a
# 500 error as it looks for resource Api::V2::MetadatumResource
model_name 'Sample::Metadata', add_model_hint: true
Expand Down
31 changes: 31 additions & 0 deletions app/uat_actions/uat_actions/tube_submission.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,26 @@ class UatActions::TubeSubmission < UatActions
include_blank: 'Using default library type...'
}

form_field :number_of_samples_per_pool,
:number_field,
label: 'Number of samples per pool',
help:
'Optional field to set the number_of_samples_per_pool field on the ' \
'submission request. Leave blank if not required.',
options: {
minimum: 0
}

form_field :cells_per_chip_well,
:number_field,
label: 'Cells per Chip Well',
help:
'Optional field to set the cells_per_chip_well field on the ' \
'submission request. Leave blank if not required.',
options: {
minimum: 0
}

validates :submission_template, presence: { message: 'could not be found' }

# Returns a default copy of the UatAction which will be used to fill in the form
Expand Down Expand Up @@ -74,12 +94,21 @@ def perform
# Fills the report with the information from the submission
#
# @return [Void]
# rubocop:disable Metrics/AbcSize
def fill_report(order)
report['tube_barcodes'] = assets.map(&:human_barcode)
report['submission_id'] = order.submission.id
report['library_type'] = order.request_options[:library_type] if order.request_options[:library_type].present?
report['number_of_samples_per_pool'] = order.request_options[:number_of_samples_per_pool] if order.request_options[
:number_of_samples_per_pool
].present?
report['cells_per_chip_well'] = order.request_options[:cells_per_chip_well] if order.request_options[
:cells_per_chip_well
].present?
end

# rubocop:enable Metrics/AbcSize

# Returns the submisssion template to use for the submission
#
# @return [SubmissionTemplate] The submission template to use
Expand Down Expand Up @@ -131,6 +160,8 @@ def default_request_options
def custom_request_options
options = {}
options[:library_type] = library_type_name if library_type_name.present?
options[:number_of_samples_per_pool] = number_of_samples_per_pool.presence
options[:cells_per_chip_well] = cells_per_chip_well.presence
options
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ limber_scrna_core_cdna_prep_gem_x_5p:
name: scRNA Core cDNA Prep GEM-X 5p
asset_type: SampleTube
order: 1
request_class_name: CustomerRequest
request_class_name: PbmcPoolingCustomerRequest
for_multiplexing: false
billable: true
product_line_name: Short Read
Expand Down
4 changes: 3 additions & 1 deletion config/initializers/inflections.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
# inflect.uncountable %w( fish sheep )
# end

ActiveSupport::Inflector.inflections(:en) { |inflect| inflect.uncountable %w[health sample_metadata labware] }
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.uncountable %w[health sample_metadata request_metadata labware]
end

# These inflection rules are supported but not enabled by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect|
Expand Down
6 changes: 6 additions & 0 deletions config/locales/metadata/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ en:
requested_flowcell_type:
label: Flowcell type

number_of_samples_per_pool:
label: Number of samples per pool

cells_per_chip_well:
label: Cells per chip well

library_creation_request:
<<: *REQUEST
sequencing_request:
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
jsonapi_resources :qcables
jsonapi_resources :racked_tubes
jsonapi_resources :receptacles
jsonapi_resources :request_metadata
jsonapi_resources :request_types
jsonapi_resources :requests
jsonapi_resources :samples
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddscRnaFieldsToRequestMetadata < ActiveRecord::Migration[6.1]
def change
add_column :request_metadata, :number_of_samples_per_pool, :integer, null: true
add_column :request_metadata, :cells_per_chip_well, :integer, null: true
end
end
4 changes: 3 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2024_08_13_130010) do
ActiveRecord::Schema.define(version: 2024_09_17_133813) do

create_table "aliquot_indices", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", options: "ENGINE=InnoDB ROW_FORMAT=DYNAMIC", force: :cascade do |t|
t.integer "aliquot_id", null: false
Expand Down Expand Up @@ -1174,6 +1174,8 @@
t.string "data_type"
t.integer "primer_panel_id"
t.string "requested_flowcell_type"
t.integer "number_of_samples_per_pool"
t.integer "cells_per_chip_well"
t.index ["request_id"], name: "index_request_metadata_on_request_id"
end

Expand Down
2 changes: 1 addition & 1 deletion spec/bulk_submission_excel/download_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
let(:columns) { configuration.columns.all.dup }
let(:ranges) { configuration.ranges.dup }
let(:assets) { create(:plate_with_untagged_wells).wells }
let(:submission_template) { create :libray_and_sequencing_template }
let(:submission_template) { create :library_and_sequencing_template }

after { File.delete(test_file) if File.exist?(test_file) }

Expand Down
4 changes: 4 additions & 0 deletions spec/factories/request_type_factories.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
request_class { CustomerRequest }
end

factory :pbmc_pooling_customer_request_type do
request_class { PbmcPoolingCustomerRequest }
end

factory :cherrypick_request_type do
request_class { CherrypickRequest }
asset_type { 'Well' }
Expand Down
10 changes: 7 additions & 3 deletions spec/factories/submission_factories.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,21 @@
transient { request_types { [create(:library_request_type)] } }
end

factory :libray_and_sequencing_template do
factory :library_and_sequencing_template do
transient { request_types { [create(:library_request_type), create(:sequencing_request_type)] } }
end

factory :heron_libray_and_sequencing_template do
factory :heron_library_and_sequencing_template do
transient { request_types { [create(:heron_request_type), create(:sequencing_request_type)] } }
end

factory :isc_libray_and_sequencing_template do
factory :isc_library_and_sequencing_template do
transient { request_types { [create(:isc_library_request_type), create(:sequencing_request_type)] } }
end

factory :pbmc_pooling_submission_template do
transient { request_types { [create(:pbmc_pooling_customer_request_type)] } }
end
end

factory :order do
Expand Down
6 changes: 3 additions & 3 deletions spec/features/generate_a_bulk_submission_template_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# We only use two wells of out partial plate in our submission. However we are generating 13
# to ensure we are only using the specified well. The other two will be ignored.
let!(:partial_plate) { create(:plate_with_untagged_wells, well_count: 13) }
let!(:submission_template) { create :libray_and_sequencing_template }
let!(:submission_template) { create :library_and_sequencing_template }

let(:iso_date) { Time.current.utc.strftime('%Y%m%d') }
let(:filename) { "#{plate.human_barcode}_to_#{partial_plate.human_barcode}_#{iso_date}_#{user.login}.xlsx" }
Expand Down Expand Up @@ -37,7 +37,7 @@
end

context 'with a primer panel submission' do
let!(:submission_template) { create :heron_libray_and_sequencing_template }
let!(:submission_template) { create :heron_library_and_sequencing_template }
let!(:primer_panel) { create :primer_panel }

it 'populates the primer panel column' do
Expand All @@ -58,7 +58,7 @@
end

context 'with a bait_library submission' do
let!(:submission_template) { create :isc_libray_and_sequencing_template }
let!(:submission_template) { create :isc_library_and_sequencing_template }
let!(:bait_library) { create :bait_library }

it 'populates the primer panel column' do
Expand Down
81 changes: 81 additions & 0 deletions spec/uat_actions/tube_submission_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# frozen_string_literal: true

require 'rails_helper'

describe UatActions::TubeSubmission do
context 'with valid options' do
let(:tube) { create :sample_tube, purpose: create(:sample_tube_purpose) }
let(:tube_barcode) { tube.barcodes.last.barcode }
let(:submission_template) { create :pbmc_pooling_submission_template }
let(:parameters) { { submission_template_name: submission_template.name, tube_barcodes: tube_barcode } }
let(:uat_action) { described_class.new(parameters) }
let(:report) do
# A report is a hash of key value pairs which get returned to the user.
# It should include information such as barcodes and identifiers
{ 'tube_barcodes' => [tube_barcode] }
end

it 'can be performed' do
expect(uat_action.perform).to be true
expect(uat_action.report['tube_barcodes']).to eq report['tube_barcodes']
expect(uat_action.report['submission_id']).to be_a Integer
end

context 'with optional library type supplied' do
let(:parameters) do
{
submission_template_name: submission_template.name,
tube_barcodes: tube_barcode,
library_type_name: 'Standard'
}
end

it 'can be performed' do
expect(uat_action.perform).to be true
expect(uat_action.report['tube_barcodes']).to eq report['tube_barcodes']
expect(uat_action.report['submission_id']).to be_a Integer
expect(uat_action.report['library_type']).to eq 'Standard'
end
end

context 'with optional number of samples per pool supplied' do
let(:num_samples) { 15 }
let(:parameters) do
{
submission_template_name: submission_template.name,
tube_barcodes: tube_barcode,
number_of_samples_per_pool: num_samples
}
end

it 'can be performed' do
expect(uat_action.perform).to be true
expect(uat_action.report['tube_barcodes']).to eq report['tube_barcodes']
expect(uat_action.report['submission_id']).to be_a Integer
expect(uat_action.report['number_of_samples_per_pool']).to eq num_samples
end
end

context 'with optional cells per chip well supplied' do
let(:num_cells) { 20_000 }
let(:parameters) do
{
submission_template_name: submission_template.name,
tube_barcodes: tube_barcode,
cells_per_chip_well: num_cells
}
end

it 'can be performed' do
expect(uat_action.perform).to be true
expect(uat_action.report['tube_barcodes']).to eq report['tube_barcodes']
expect(uat_action.report['submission_id']).to be_a Integer
expect(uat_action.report['cells_per_chip_well']).to eq num_cells
end
end
end

it 'returns a default' do
expect(described_class.default).to be_a described_class
end
end

0 comments on commit ea6d267

Please sign in to comment.