diff --git a/tests/messages/test_output_traction_message.py b/tests/messages/test_output_traction_message.py index 8550e99e..4380fa42 100644 --- a/tests/messages/test_output_traction_message.py +++ b/tests/messages/test_output_traction_message.py @@ -82,56 +82,267 @@ def test_output_traction_message_can_generate_payload_for_plates(): assert instance.payload() == { "data": { + "type": "receptions", "attributes": { - "request_attributes": [ + "source": "tol-lab-share.tol", + "plates_attributes": [ { - "container": {"barcode": "1", "position": "A1", "type": "wells"}, - "request": { - "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", - "library_type": "library", - "cost_code": "S1234", - }, - "sample": { - "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", - "name": "test1", - "species": "test " "species", - "priority_level": None, - "sanger_sample_id": "sample1", - "public_name": "Public1", - "supplier_name": "supplier1", - "taxon_id": "9606", - "donor_id": "donor1", - "country_of_origin": "United Kingdom", - "accession_number": "AN1234", - "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), - }, + "barcode": "1", + "type": "plates", + "wells_attributes": [ + { + "position": "A1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "library", + "cost_code": "S1234", + }, + "sample": { + "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", + "name": "test1", + "species": "test " "species", + "priority_level": None, + "sanger_sample_id": "sample1", + "public_name": "Public1", + "supplier_name": "supplier1", + "taxon_id": "9606", + "donor_id": "donor1", + "country_of_origin": "United Kingdom", + "accession_number": "AN1234", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + { + "position": "B1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "library", + "cost_code": "S4567", + }, + "sample": { + "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", + "name": "test1", + "species": "test " "species", + "priority_level": None, + "sanger_sample_id": "sample2", + "public_name": "Public2", + "supplier_name": "supplier2", + "taxon_id": "9606", + "donor_id": "donor2", + "country_of_origin": "United Kingdom", + "accession_number": "AN1235", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + ], + }, + ], + "tubes_attributes": [], + }, + } + } + + +def test_output_traction_message_can_generate_payload_for_multiple_plates(): + my_date = datetime.now() + instance = OutputTractionMessage() + + # Mix the order of the requests to ensure the grouping for plates works. + # Well order is still maintained since the payload keeps the order of the requests. + + # First plate, first well (A1). + request = instance.create_request() + request.container_barcode = "123" + request.container_type = "wells" + request.container_location = "A1" + request.library_type = "library" + request.sample_name = "test1" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-1234-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public1" + request.cost_code = "S1234" + request.priority_level = None + request.sanger_sample_id = "sample1" + request.supplier_name = "supplier1" + request.taxon_id = "9606" + request.donor_id = "donor1" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1234" + request.date_of_sample_collection = my_date + + # Second plate, first well (B1). + request = instance.create_request() + request.container_barcode = "456" + request.container_type = "wells" + request.container_location = "B1" + request.library_type = "library" + request.sample_name = "test3" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-1236-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public3" + request.cost_code = "S1234" + request.priority_level = None + request.sanger_sample_id = "sample3" + request.supplier_name = "supplier1" + request.taxon_id = "9606" + request.donor_id = "donor3" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1236" + request.date_of_sample_collection = my_date + + # First plate, second well (B1). + request = instance.create_request() + request.container_barcode = "123" + request.container_type = "wells" + request.container_location = "B1" + request.library_type = "library" + request.sample_name = "test2" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-1235-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public2" + request.cost_code = "S4567" + request.priority_level = None + request.sanger_sample_id = "sample2" + request.supplier_name = "supplier2" + request.taxon_id = "9606" + request.donor_id = "donor2" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1235" + request.date_of_sample_collection = my_date + + # Second plate, second well (C1). + request = instance.create_request() + request.container_barcode = "456" + request.container_type = "wells" + request.container_location = "C1" + request.library_type = "library" + request.sample_name = "test4" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-1237-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public4" + request.cost_code = "S4567" + request.priority_level = None + request.sanger_sample_id = "sample4" + request.supplier_name = "supplier2" + request.taxon_id = "9606" + request.donor_id = "donor4" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1237" + request.date_of_sample_collection = my_date + + assert instance.payload() == { + "data": { + "type": "receptions", + "attributes": { + "source": "tol-lab-share.tol", + "plates_attributes": [ + { + "barcode": "123", + "type": "plates", + "wells_attributes": [ + { + "position": "A1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "library", + "cost_code": "S1234", + }, + "sample": { + "external_id": "8860a6b4-1234-451c-aba2-a3129c38c0fc", + "name": "test1", + "species": "test " "species", + "priority_level": None, + "sanger_sample_id": "sample1", + "public_name": "Public1", + "supplier_name": "supplier1", + "taxon_id": "9606", + "donor_id": "donor1", + "country_of_origin": "United Kingdom", + "accession_number": "AN1234", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + { + "position": "B1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "library", + "cost_code": "S4567", + }, + "sample": { + "external_id": "8860a6b4-1235-451c-aba2-a3129c38c0fc", + "name": "test2", + "species": "test " "species", + "priority_level": None, + "sanger_sample_id": "sample2", + "public_name": "Public2", + "supplier_name": "supplier2", + "taxon_id": "9606", + "donor_id": "donor2", + "country_of_origin": "United Kingdom", + "accession_number": "AN1235", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + ], }, { - "container": {"barcode": "1", "position": "B1", "type": "wells"}, - "request": { - "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", - "library_type": "library", - "cost_code": "S4567", - }, - "sample": { - "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", - "name": "test1", - "species": "test " "species", - "priority_level": None, - "sanger_sample_id": "sample2", - "public_name": "Public2", - "supplier_name": "supplier2", - "taxon_id": "9606", - "donor_id": "donor2", - "country_of_origin": "United Kingdom", - "accession_number": "AN1235", - "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), - }, + "barcode": "456", + "type": "plates", + "wells_attributes": [ + { + "position": "B1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "library", + "cost_code": "S1234", + }, + "sample": { + "external_id": "8860a6b4-1236-451c-aba2-a3129c38c0fc", + "name": "test3", + "species": "test " "species", + "priority_level": None, + "sanger_sample_id": "sample3", + "public_name": "Public3", + "supplier_name": "supplier1", + "taxon_id": "9606", + "donor_id": "donor3", + "country_of_origin": "United Kingdom", + "accession_number": "AN1236", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + { + "position": "C1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "library", + "cost_code": "S4567", + }, + "sample": { + "external_id": "8860a6b4-1237-451c-aba2-a3129c38c0fc", + "name": "test4", + "species": "test " "species", + "priority_level": None, + "sanger_sample_id": "sample4", + "public_name": "Public4", + "supplier_name": "supplier2", + "taxon_id": "9606", + "donor_id": "donor4", + "country_of_origin": "United Kingdom", + "accession_number": "AN1237", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + ], }, ], - "source": "tol-lab-share.tol", + "tubes_attributes": [], }, - "type": "receptions", } } @@ -182,21 +393,131 @@ def test_output_traction_message_can_generate_payload_for_ont_library_types(): assert instance.payload() == { "data": { + "type": "receptions", "attributes": { - "request_attributes": [ + "source": "tol-lab-share.tol", + "plates_attributes": [ { - "container": {"barcode": "1", "position": "A1", "type": "wells"}, + "barcode": "1", + "type": "plates", + "wells_attributes": [ + { + "position": "A1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "ONT_mylib", + "cost_code": "S1234", + "data_type": "basecalls", + }, + "sample": { + "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", + "name": "test1", + "species": "test " "species", + "priority_level": "Medium", + "sanger_sample_id": "sample1", + "public_name": "Public1", + "supplier_name": "supplier1", + "taxon_id": "9606", + "donor_id": "donor1", + "country_of_origin": "United Kingdom", + "accession_number": "AN1234", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + { + "position": "B1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "ONT_mylib", + "cost_code": "S4567", + "data_type": "basecalls", + }, + "sample": { + "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", + "name": "test1", + "species": "test " "species", + "priority_level": None, + "sanger_sample_id": "sample2", + "public_name": "Public2", + "supplier_name": "supplier2", + "taxon_id": "9606", + "donor_id": "donor2", + "country_of_origin": "United Kingdom", + "accession_number": "AN1235", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + ], + }, + ], + "tubes_attributes": [], + }, + } + } + + +def test_output_traction_message_can_generate_payload_for_tubes(): + my_date = datetime.now() + instance = OutputTractionMessage() + + request = instance.create_request() + request.container_barcode = "1" + request.container_type = "tubes" + request.library_type = "library" + request.sample_name = "test1" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-82e2-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public1" + request.cost_code = "S1234" + request.priority_level = "High" + request.sanger_sample_id = "sample1" + request.supplier_name = "supplier1" + request.taxon_id = "9606" + request.donor_id = "donor1" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1234" + request.date_of_sample_collection = my_date + + request = instance.create_request() + request.container_barcode = "1" + request.container_type = "tubes" + request.library_type = "library" + request.sample_name = "test1" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-82e2-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public2" + request.cost_code = "S4567" + request.priority_level = "Low" + request.sanger_sample_id = "sample2" + request.supplier_name = "supplier2" + request.taxon_id = "9606" + request.donor_id = "donor2" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1235" + request.date_of_sample_collection = my_date + + assert instance.payload() == { + "data": { + "type": "receptions", + "attributes": { + "source": "tol-lab-share.tol", + "plates_attributes": [], + "tubes_attributes": [ + { + "barcode": "1", + "type": "tubes", "request": { "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", - "library_type": "ONT_mylib", + "library_type": "library", "cost_code": "S1234", - "data_type": "basecalls", }, "sample": { "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", "name": "test1", "species": "test " "species", - "priority_level": "Medium", + "priority_level": "High", "sanger_sample_id": "sample1", "public_name": "Public1", "supplier_name": "supplier1", @@ -208,18 +529,18 @@ def test_output_traction_message_can_generate_payload_for_ont_library_types(): }, }, { - "container": {"barcode": "1", "position": "B1", "type": "wells"}, + "barcode": "1", + "type": "tubes", "request": { "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", - "library_type": "ONT_mylib", + "library_type": "library", "cost_code": "S4567", - "data_type": "basecalls", }, "sample": { "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", "name": "test1", "species": "test " "species", - "priority_level": None, + "priority_level": "Low", "sanger_sample_id": "sample2", "public_name": "Public2", "supplier_name": "supplier2", @@ -231,28 +552,27 @@ def test_output_traction_message_can_generate_payload_for_ont_library_types(): }, }, ], - "source": "tol-lab-share.tol", }, - "type": "receptions", } } -def test_output_traction_message_can_generate_payload_for_tubes(): +def test_output_traction_message_can_generate_payload_for_mix_of_plate_and_tubes(): my_date = datetime.now() instance = OutputTractionMessage() request = instance.create_request() - request.container_barcode = "1" - request.container_type = "tubes" + request.container_barcode = "123" + request.container_type = "wells" + request.container_location = "A1" request.library_type = "library" request.sample_name = "test1" request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" - request.sample_uuid = "8860a6b4-82e2-451c-aba2-a3129c38c0fc" + request.sample_uuid = "8860a6b4-1234-451c-aba2-a3129c38c0fc" request.species = "test species" request.public_name = "Public1" request.cost_code = "S1234" - request.priority_level = "High" + request.priority_level = None request.sanger_sample_id = "sample1" request.supplier_name = "supplier1" request.taxon_id = "9606" @@ -262,16 +582,17 @@ def test_output_traction_message_can_generate_payload_for_tubes(): request.date_of_sample_collection = my_date request = instance.create_request() - request.container_barcode = "1" - request.container_type = "tubes" + request.container_barcode = "123" + request.container_type = "wells" + request.container_location = "B1" request.library_type = "library" - request.sample_name = "test1" + request.sample_name = "test2" request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" - request.sample_uuid = "8860a6b4-82e2-451c-aba2-a3129c38c0fc" + request.sample_uuid = "8860a6b4-1235-451c-aba2-a3129c38c0fc" request.species = "test species" request.public_name = "Public2" request.cost_code = "S4567" - request.priority_level = "Low" + request.priority_level = None request.sanger_sample_id = "sample2" request.supplier_name = "supplier2" request.taxon_id = "9606" @@ -280,58 +601,150 @@ def test_output_traction_message_can_generate_payload_for_tubes(): request.accession_number = "AN1235" request.date_of_sample_collection = my_date + request = instance.create_request() + request.container_barcode = "987" + request.container_type = "tubes" + request.library_type = "library" + request.sample_name = "test3" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-1236-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public3" + request.cost_code = "S1234" + request.priority_level = "High" + request.sanger_sample_id = "sample3" + request.supplier_name = "supplier3" + request.taxon_id = "9606" + request.donor_id = "donor3" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1236" + request.date_of_sample_collection = my_date + + request = instance.create_request() + request.container_barcode = "876" + request.container_type = "tubes" + request.library_type = "library" + request.sample_name = "test4" + request.study_uuid = "dd490ee5-fd1d-456d-99fd-eb9d3861e014" + request.sample_uuid = "8860a6b4-1237-451c-aba2-a3129c38c0fc" + request.species = "test species" + request.public_name = "Public4" + request.cost_code = "S4567" + request.priority_level = "Low" + request.sanger_sample_id = "sample4" + request.supplier_name = "supplier4" + request.taxon_id = "9606" + request.donor_id = "donor4" + request.country_of_origin = "United Kingdom" + request.accession_number = "AN1237" + request.date_of_sample_collection = my_date + assert instance.payload() == { "data": { + "type": "receptions", "attributes": { - "request_attributes": [ + "source": "tol-lab-share.tol", + "plates_attributes": [ { - "container": {"barcode": "1", "type": "tubes"}, + "barcode": "123", + "type": "plates", + "wells_attributes": [ + { + "position": "A1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "library", + "cost_code": "S1234", + }, + "sample": { + "external_id": "8860a6b4-1234-451c-aba2-a3129c38c0fc", + "name": "test1", + "species": "test " "species", + "priority_level": None, + "sanger_sample_id": "sample1", + "public_name": "Public1", + "supplier_name": "supplier1", + "taxon_id": "9606", + "donor_id": "donor1", + "country_of_origin": "United Kingdom", + "accession_number": "AN1234", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + { + "position": "B1", + "request": { + "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", + "library_type": "library", + "cost_code": "S4567", + }, + "sample": { + "external_id": "8860a6b4-1235-451c-aba2-a3129c38c0fc", + "name": "test2", + "species": "test " "species", + "priority_level": None, + "sanger_sample_id": "sample2", + "public_name": "Public2", + "supplier_name": "supplier2", + "taxon_id": "9606", + "donor_id": "donor2", + "country_of_origin": "United Kingdom", + "accession_number": "AN1235", + "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), + }, + }, + ], + }, + ], + "tubes_attributes": [ + { + "barcode": "987", + "type": "tubes", "request": { "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", "library_type": "library", "cost_code": "S1234", }, "sample": { - "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", - "name": "test1", + "external_id": "8860a6b4-1236-451c-aba2-a3129c38c0fc", + "name": "test3", "species": "test " "species", "priority_level": "High", - "sanger_sample_id": "sample1", - "public_name": "Public1", - "supplier_name": "supplier1", + "sanger_sample_id": "sample3", + "public_name": "Public3", + "supplier_name": "supplier3", "taxon_id": "9606", - "donor_id": "donor1", + "donor_id": "donor3", "country_of_origin": "United Kingdom", - "accession_number": "AN1234", + "accession_number": "AN1236", "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), }, }, { - "container": {"barcode": "1", "type": "tubes"}, + "barcode": "876", + "type": "tubes", "request": { "external_study_id": "dd490ee5-fd1d-456d-99fd-eb9d3861e014", "library_type": "library", "cost_code": "S4567", }, "sample": { - "external_id": "8860a6b4-82e2-451c-aba2-a3129c38c0fc", - "name": "test1", + "external_id": "8860a6b4-1237-451c-aba2-a3129c38c0fc", + "name": "test4", "species": "test " "species", "priority_level": "Low", - "sanger_sample_id": "sample2", - "public_name": "Public2", - "supplier_name": "supplier2", + "sanger_sample_id": "sample4", + "public_name": "Public4", + "supplier_name": "supplier4", "taxon_id": "9606", - "donor_id": "donor2", + "donor_id": "donor4", "country_of_origin": "United Kingdom", - "accession_number": "AN1235", + "accession_number": "AN1237", "date_of_sample_collection": my_date.strftime("%Y-%m-%d"), }, }, ], - "source": "tol-lab-share.tol", }, - "type": "receptions", } } diff --git a/tol_lab_share/messages/output_traction_message.py b/tol_lab_share/messages/output_traction_message.py index 6c79fd5f..4e481167 100644 --- a/tol_lab_share/messages/output_traction_message.py +++ b/tol_lab_share/messages/output_traction_message.py @@ -1,5 +1,5 @@ -from __future__ import annotations from functools import singledispatchmethod +import itertools from typing import Callable, Any from json import dumps from datetime import datetime @@ -56,94 +56,111 @@ def validate(self) -> bool: and (self.species is not None) ) - def serializer(self) -> RequestSerializer: - """Returns a serializer instance to handle the generation of the message for this request. + +class Serializer: + """Class to manage the serialization of requests.""" + + @staticmethod + def request_payload(request: OutputTractionMessageRequest) -> dict[str, Any]: + """Generate the request payload for the given request. + The payload varies depending on whether the library type is ONT or not. + + Args: + request (OutputTractionMessageRequest): The request to generate the payload for. Returns: - RequestSerializer: instance for this request + dict[str, Any]: A dictionary containing the Traction "request" payload for the request. """ - return RequestSerializer(self) + request_payload = { + "library_type": request.library_type, + "external_study_id": request.study_uuid, + "cost_code": request.cost_code, + } + if request.library_type and ("ONT" in request.library_type): + request_payload["data_type"] = "basecalls" -class RequestSerializer: - """Class to manage the serialization to JSON of the request received as argument in the constructor.""" + return request_payload - def __init__(self, instance: OutputTractionMessageRequest): - """Constructor that sets the initial state of the instance. + @staticmethod + def sample_payload(request: OutputTractionMessageRequest) -> dict[str, Any]: + """Generate the sample payload for the given request. Args: - instance (OutputTractionMessageRequest): The request that we want to serialize. + request (OutputTractionMessageRequest): The request to generate the payload for. + + Returns: + dict[str, Any]: A dictionary containing the Traction "sample" payload for the request. """ - self.instance = instance + collection_date = None + if request.date_of_sample_collection is not None: + collection_date = request.date_of_sample_collection.strftime("%Y-%m-%d") - def is_ont_library_type(self) -> bool: - """Flag boolean method that identifies if the library type is ONT. + return { + "name": request.sample_name, + "external_id": request.sample_uuid, + "species": request.species, + "priority_level": request.priority_level, + "sanger_sample_id": request.sanger_sample_id, + "supplier_name": request.supplier_name, + "public_name": request.public_name, + "taxon_id": request.taxon_id, + "donor_id": request.donor_id, + "country_of_origin": request.country_of_origin, + "accession_number": request.accession_number, + "date_of_sample_collection": collection_date, + } - Returns: - bool: True if the library type is ONT; otherwise false. + +class PlateSerializer(Serializer): + """Class to manage the serialization of plate requests.""" + + def __init__(self, plate_requests: list[OutputTractionMessageRequest]): + """Constructor. + + Args: + plate_requests (list[OutputTractionMessageRequest]): The list of requests to serialize for a single plate. """ - return bool(self.instance.library_type and ("ONT" in self.instance.library_type)) + self._requests = plate_requests - def request_payload(self) -> dict[str, Any]: - """Generate the payload for the request in this message. + def well_attributes_payload(self) -> list[dict[str, Any]]: + """Generate the well attributes payload for each of the plate's requests. Returns: - dict[str, Any] A dictionary containing the Traction payload for the request. + list[dict[str, Any]]: A list containing the well attributes for all the request. """ - if self.is_ont_library_type(): - return { - "data_type": "basecalls", - "library_type": self.instance.library_type, - "external_study_id": self.instance.study_uuid, - "cost_code": self.instance.cost_code, - } - else: - return { - "library_type": self.instance.library_type, - "external_study_id": self.instance.study_uuid, - "cost_code": self.instance.cost_code, + return [ + { + "position": request.container_location, + "request": self.request_payload(request), + "sample": self.sample_payload(request), } + for request in self._requests + ] - def sample_payload(self) -> dict[str, Any]: - """Generate the payload for the sample in this request. + def payload(self) -> dict[str, Any]: + """Generate a payload with the information required by Traction. Returns: - dict[str, Any]: A dictionary containing the Traction payload for the sample. + dict[str, Any]: A dictionary containing the overall payload. """ - collection_date = None - if self.instance.date_of_sample_collection is not None: - collection_date = self.instance.date_of_sample_collection.strftime("%Y-%m-%d") - return { - "name": self.instance.sample_name, - "external_id": self.instance.sample_uuid, - "species": self.instance.species, - "priority_level": self.instance.priority_level, - "sanger_sample_id": self.instance.sanger_sample_id, - "supplier_name": self.instance.supplier_name, - "public_name": self.instance.public_name, - "taxon_id": self.instance.taxon_id, - "donor_id": self.instance.donor_id, - "country_of_origin": self.instance.country_of_origin, - "accession_number": self.instance.accession_number, - "date_of_sample_collection": collection_date, + "barcode": self._requests[0].container_barcode, + "type": "plates", + "wells_attributes": self.well_attributes_payload(), } - def container_payload(self) -> dict[str, Any]: - """Generate the payload for the container in this request. - The contents of the payload varies depending on the container type (tubes or wells). - Returns: - dict[str, Any]: A dictionary containing the Traction payload for the container. +class TubeSerializer(Serializer): + """Class to manage the serialization of tube requests.""" + + def __init__(self, tube_request: OutputTractionMessageRequest): + """Constructor. + + Args: + tube_request (OutputTractionMessageRequest): The request to serialize for a single tube. """ - if self.instance.container_type == "tubes": - return {"type": self.instance.container_type, "barcode": self.instance.container_barcode} - else: - return { - "type": self.instance.container_type, - "barcode": self.instance.container_barcode, - "position": self.instance.container_location, - } + self._request = tube_request def payload(self) -> dict[str, Any]: """Generate a payload with the information required by Traction. @@ -152,9 +169,10 @@ def payload(self) -> dict[str, Any]: dict[str, Any]: A dictionary containing the overall payload. """ return { - "request": self.request_payload(), - "sample": self.sample_payload(), - "container": self.container_payload(), + "barcode": self._request.container_barcode, + "type": "tubes", + "request": self.request_payload(self._request), + "sample": self.sample_payload(self._request), } @@ -196,13 +214,26 @@ def create_request(self) -> OutputTractionMessageRequest: self._requests.append(OutputTractionMessageRequest()) return self._requests[-1] - def request_attributes(self) -> list[dict[str, Any]]: - """Prepare a payload for all the requests. + def plates_attributes(self) -> list[dict[str, Any]]: + """Prepare a payload for all the request related to plates. + + Returns: + list[dict[str, Any]]: A list containing the payload for all the plates. + """ + # Group well requests by plate barcode + well_requests = [request for request in self._requests if request.container_type == "wells"] + well_requests.sort(key=lambda request: request.container_barcode or "") + plate_requests = itertools.groupby(well_requests, lambda request: request.container_barcode) + + return [PlateSerializer(list(requests)).payload() for _, requests in plate_requests] + + def tubes_attributes(self) -> list[dict[str, Any]]: + """Prepare a payload for all the requests related to tubes. Returns: - list[dict[str, Any]]: The payload for all the requests. + list[dict[str, Any]]: A list containing the payload for all the tubes. """ - return [request.serializer().payload() for request in self._requests] + return [TubeSerializer(request).payload() for request in self._requests if request.container_type == "tubes"] @property def errors(self) -> list[ErrorCode]: @@ -254,7 +285,8 @@ def payload(self) -> dict[str, Any]: "type": "receptions", "attributes": { "source": OUTPUT_TRACTION_MESSAGE_SOURCE, - "request_attributes": self.request_attributes(), + "plates_attributes": self.plates_attributes(), + "tubes_attributes": self.tubes_attributes(), }, } } diff --git a/tol_lab_share/messages/traction_qc_message.py b/tol_lab_share/messages/traction_qc_message.py index a42493af..6a8c0161 100644 --- a/tol_lab_share/messages/traction_qc_message.py +++ b/tol_lab_share/messages/traction_qc_message.py @@ -1,4 +1,3 @@ -from __future__ import annotations from functools import singledispatchmethod import logging from json import dumps @@ -52,14 +51,6 @@ def validate(self) -> bool: and (self.date_submitted_utc is not None) ) - def serializer(self) -> QcRequestSerializer: - """A serializer instance to handle the generation of the payload for this request. - - Returns: - RequestSerializer: The serializer for this request. - """ - return QcRequestSerializer(self) - class QcRequestSerializer: """Class to manage the serialization to JSON of the request received as argument in the constructor.""" @@ -133,7 +124,7 @@ def request_attributes(self) -> list[dict[str, Any]]: Returns: list[dict[str,Any]]: The payload for all the requests. """ - return [request.serializer().payload() for request in self._requests] + return [QcRequestSerializer(request).payload() for request in self._requests] @property def errors(self) -> list[ErrorCode]: