-
Notifications
You must be signed in to change notification settings - Fork 8
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
Y24-190-3: Use SS v2 for TagLayoutTemplates #1835
Changes from 18 commits
86d5934
b73aa7b
dff8746
4fd7757
1d89cf7
4b82bbb
3306e6d
b751825
c4940a1
cc2593b
cbb3f58
3da27a2
7b0759f
201fb5b
7e895d3
d92ff39
ef03d4b
98fa7a1
ff89668
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -79,6 +79,10 @@ def convert_tag_plate_to_new_purpose | |
|
||
def create_labware! | ||
create_plate! do |plate_uuid| | ||
# TODO: {Y24-190} Work out a way to call the `create!` method on TagLayoutTemplate model in Sequencescape | ||
# using the V2 API. I think either we need to misuse the PATCH method with some kind of | ||
# attributes telling Sequencescape to run the `create!` method, or we need to create a new | ||
# endpoint associated with a TagLayoutTemplate that will run the `create!` method. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought you were planning to redesign it so that you could call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, but we don't have |
||
api | ||
.tag_layout_template | ||
.find(tag_plate.template_uuid) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# frozen_string_literal: true | ||
|
||
# tag layout template resource | ||
class Sequencescape::Api::V2::TagLayoutTemplate < Sequencescape::Api::V2::Base | ||
has_one :tag_group | ||
has_one :tag2_group, class_name: 'TagGroup' | ||
|
||
def dual_index? | ||
tag2_group.present? | ||
end | ||
|
||
# Performs the coercion of this instance so that it behaves appropriately given the direction | ||
# and walking algorithm information. | ||
def coerce | ||
extend("limber/tag_layout_template/in_#{direction.gsub(/\s+/, '_')}s".camelize.constantize) | ||
extend("limber/tag_layout_template/walk_#{walking_by.gsub(/\s+/, '_')}".camelize.constantize) | ||
rescue NameError => e | ||
Rails.logger.warn("Unrecognised layout options: #{e.message}") | ||
extend Unsupported | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this method and the one below are taken from the existing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm leaving removal of V1 until the end. It's still unclear how tightly tied they are into the system and I do still need it until we remove the TODO items. |
||
ensure | ||
self | ||
end | ||
|
||
# This returns an array of well location to pool pairs. The 'walker' is responsible for actually doing the walking | ||
# of the wells that are acceptable, and it calls back with the location of the well being processed. | ||
def group_wells(plate) | ||
well_to_pool = plate.wells.each_with_object({}) { |well, store| store[well.location] = well.pool_id } | ||
|
||
# We assume that if a well is unpooled then it is in the same pool as the previous pool. | ||
prior_pool = nil | ||
callback = | ||
lambda do |row_column| | ||
prior_pool = pool = well_to_pool[row_column] || prior_pool # or next | ||
well_empty = well_to_pool[row_column].nil? | ||
well = pool.nil? ? nil : row_column | ||
[well, pool, well_empty] # Triplet: [ A1, pool_id, well_empty ] | ||
end | ||
yield(callback) | ||
end | ||
private :group_wells | ||
|
||
def tag_ids | ||
tag_group.tags.map { |t| t['index'].to_i }.sort | ||
end | ||
private :tag_ids | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,13 +12,23 @@ def generate_oligo | |
factory :v2_tag, class: Sequencescape::Api::V2::Tag do | ||
skip_create | ||
|
||
sequence(:map_id) { |i| i } | ||
sequence(:oligo) { |_index| generate_oligo } | ||
tag_group { create :v2_tag_group } | ||
tag_group { create :v2_tag_group, v2_tags: [instance] } | ||
end | ||
|
||
factory :v2_tag_group, class: Sequencescape::Api::V2::Tag do | ||
factory :v2_tag_group, class: Sequencescape::Api::V2::TagGroup do | ||
skip_create | ||
|
||
transient { v2_tags { [] } } | ||
sequence(:name) { |index| "TagGroup#{index}" } | ||
tags { v2_tags.map { |t| { index: t.map_id, oligo: t.oligo } } } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. I wouldn't have done it this way as it's not how V1 works, but the V2 endpoint was pre-existing with this and if I change it, who knows what else might break. One of the biggest (and possibly least obvious) issues with APIs is that they're even harder to change than other aspects of systems. You're setting up a contract with people outside your team's scope for how they can interface with your application. Making decisions about API implementations should never be taken lightly. Deploying a bad decision has to be maintained until the next version of the API. |
||
|
||
factory :v2_tag_group_with_tags do | ||
transient do | ||
size { 96 } | ||
v2_tags { (1..size).map { |i| create(:v2_tag, map_id: i, tag_group: instance) } } | ||
end | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for writing this documentation.
Not quite sure what this sentence means. How will the record have the property if you haven't specified it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Magic. Basically the resources mostly look like this:
but even so, that's just a template for the client gem to know that it should load a resource from that endpoint and the attributes from the API will be available to you when you use this class. They are dynamically assigned.
The exception is for relationships. If the API supports a resource having associated other resources, you do need to specify
has_one
,has_many
etc in order for it to make those accessible via the client gem.You can also add custom logic to these, like I did for TagLayoutTemplate, so you can change the named algorithms into implementations of that algorithm on the Limber side.