Skip to content
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

Add Barcodes to Existing Kits #590

Open
wants to merge 39 commits into
base: master
Choose a base branch
from
Open

Conversation

cassidysymons
Copy link
Collaborator

@cassidysymons cassidysymons commented Dec 16, 2024

This PR addresses two related needs:

  1. It allows an admin to add barcodes to existing kits, either by providing barcodes or allowing the system to generate novel barcodes.
  2. It enhances the kit creation process by allowing the admin to create kits with a mix of admin-provided barcodes (e.g. matrix tubes with pre-determined barcodes) and system-generated barcodes.

The corresponding microsetta-admin PR is biocore/microsetta-admin#110

Copy link
Member

@wasade wasade left a comment

Choose a reason for hiding this comment

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

I'm following up by email

microsetta_private_api/admin/admin_impl.py Show resolved Hide resolved
microsetta_private_api/admin/admin_impl.py Outdated Show resolved Hide resolved
microsetta_private_api/admin/admin_impl.py Outdated Show resolved Hide resolved
microsetta_private_api/admin/admin_impl.py Outdated Show resolved Hide resolved
errors.append(f'Barcode {barcode} already exists')

# And verify that the number of barcodes and kit IDs are equal
if len(barcodes) != len(kit_ids):
Copy link
Member

Choose a reason for hiding this comment

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

This test should be performed before the checks against the database under the fail fast principle

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Given that this is an admin-facing tool with infrequent usage, I've opted to return a comprehensive set of errors so they can all be remedied at once, rather than simply returning the first error the API encounters. As such, the sequence of checks isn't important.

microsetta_private_api/admin/admin_impl.py Show resolved Hide resolved
message=error_str
), 422

diag = admin_repo.add_barcodes_to_kits(kit_ids, barcodes)
Copy link
Member

Choose a reason for hiding this comment

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

I think this should be in a try/except given that create_kits is above

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm not following why this would be in a try/except block. It's a completely separate function/API path from creating kits, and all of the reasonable checks have been performed by the time we attempt to add barcodes to kits.

Copy link
Member

Choose a reason for hiding this comment

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

The methods have criteria which would trigger a raise. It would be more useful to provide a error message to the admin than to trigger a 500

@cassidysymons
Copy link
Collaborator Author

Lint is failing as GitHub is changing (has changed) what "ubuntu-latest" means for workflow and python 3.7 isn't available in the new version (source: actions/runner-images#10636). I'll revisit updating our workflow files to use Ubuntu 22.04 on Monday. Not a long-term fix as support for that will expire in two years, I'll revisit the longer term implications in a couple of months.

Merge Master into Working Branch
@cassidysymons
Copy link
Collaborator Author

Workflow issue is resolved

Copy link
Member

@wasade wasade left a comment

Choose a reason for hiding this comment

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

I'm not sure if this is ready to re-review or not, but I cannot get to it later this week and next week carries a lot of uncertainty. Added some additional comments, but was unable to review the unit tests. The input structure on creation of kits feels surprising still even with documentation, but I may just be looking at this from a different angle -- I'm not sure if @AmandaBirmingham has time for a second opinion

# slot in the set of kits being created.
# len(barcodes) == number_of_samples && len(barcodes[x]) == number_of_kits
# This allows the creation of a set of kits with a mixture of
# system-generated and admin-provided barcodes.
Copy link
Member

Choose a reason for hiding this comment

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

In English, the length of barcodes is the maximum number of barcodes which may be in a kit, and the length of an element of barcodes is the number of kits which have the number of barcodes equal to the index in barcodes? If this is the case, then should the following defensive assertions be added?

if len(barcodes) != number_of_samples:
    return jsonify(code=422, message="Unexpected data organization"), 422
for row in barcodes:
    if len(row) != number_of_kits:
        return jsonify(code=422, message="Unexpected data organization"), 422

I apologize if I'm missing something obvious but I'm still having a hard time understanding this data structure, I think as it implicit rather than explicit. I wonder whether decomposing this endpoint would help -- is there a use case for the endpoint to support BOTH system and admin provided barcodes? If there is, why not a list of dict where each element is a kit description, e.g. something like: [{'number_of_system_generated_barcodes': X, 'admin_provided_barcodes': [...]}, ]

Copy link
Member

Choose a reason for hiding this comment

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

Is it reasonable to interpret this structure as a 2D matrix, where the index position of a row is the barcode number (1st, 2nd, 3rd, etc) and the columns are the logical kits?

if len(barcode_list) != 0:
# If barcodes were provided for a given slot, ensure the
# quantity matches the number of kits
if len(barcode_list) != number_of_kits:
Copy link
Member

Choose a reason for hiding this comment

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

Following separation of concerns, the input data structure should be validated prior to its use. But I think deleting this, and using the suggested check in the above comment would do that unless I'm misunderstanding something about the structure

# quantity matches the number of kits
if len(barcode_list) != number_of_kits:
errors.append(
f'Incorrect number of barcodes given for Sample {x}'
Copy link
Member

Choose a reason for hiding this comment

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

How does a barcode differ from a sample?


# Iterate through sample slots and use provided barcodes
for barcode_list in barcodes:
if len(barcode_list) > 0:
Copy link
Member

Choose a reason for hiding this comment

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

This is always True though from the description of the structure since len(barcodes[x]) == number_of_kits?

errors = []
for barcode_list in barcodes:
x = 1
if len(barcode_list) != 0:
Copy link
Member

Choose a reason for hiding this comment

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

From the description, it looks like len(barcode_list) == 0 should raise. From the repo code, I'm less clear as that is a valid condition

Returns
-------
list of tuples
The pairings of newly added kit IDs/barcodes
Copy link
Member

Choose a reason for hiding this comment

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

These barcodes are not associated with box IDs. It looks like it may be necessary to insert the kit UUID (see

)

Copy link
Member

Choose a reason for hiding this comment

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

Will samples added to an existing kit be shipped via daklapack?

for prj_id in project_ids:
barcode_projects.append((barcode, prj_id))
if isinstance(new_barcodes, list) and \
all(isinstance(item, list) for item in new_barcodes):
Copy link
Member

Choose a reason for hiding this comment

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

The docstring should be revised

Comment on lines +1144 to +1151
for barcodes in new_barcodes:
for barcode in barcodes:
for prj_id in project_ids:
barcode_projects.append((barcode, prj_id))
else:
for barcode in new_barcodes:
for prj_id in project_ids:
barcode_projects.append((barcode, prj_id))
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
for barcodes in new_barcodes:
for barcode in barcodes:
for prj_id in project_ids:
barcode_projects.append((barcode, prj_id))
else:
for barcode in new_barcodes:
for prj_id in project_ids:
barcode_projects.append((barcode, prj_id))
flat_barcodes = [bc for barcodes in new_barcodes for bc in barcodes]
new_barcodes = flat_barcodes
for barcode in new_barcodes:
for prj_id in project_ids:
barcode_projects.append((barcode, prj_id))

address = None if address_dict is None else json.dumps(address_dict)
kit_details = [{KIT_BOX_ID_KEY: box_id,
KIT_ADDRESS_KEY: address,
KIT_OUTBOUND_KEY: outbound_fedex_code,
KIT_INBOUND_KEY: inbound_fedex_code}]
kit_name_and_barcode_tuples_list = \
[(kit_name, x) for x in barcodes_list]
kit_names = [kit_name]
Copy link
Member

Choose a reason for hiding this comment

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

Was this relocation consequential?

for barcode_list in barcodes:
if len(barcode_list) > 0:
# Admin provided barcodes for this sample slot, use them
kit_barcode_tuples = list(zip(kit_names, barcode_list))
Copy link
Member

Choose a reason for hiding this comment

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

...but create_kits cannot assume it is only ever called from the API endpoint where the validation is presently performed, therefore we do not have assurance that len(kit_names) == len(barcode_list) here. Can that check be added?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants