Skip to content

Commit fd8b51a

Browse files
committed
Fastq manager updates
Introduced 'ResponseDict' concept to separate Response Classes from Response Dictionaries, model_dump() -> Self isn't always a viable solution. This also allows us to have dynamic logic in our file objects, either with s3 details or without s3 details. * NTSM Evaluation fixes
1 parent fe13f58 commit fd8b51a

File tree

25 files changed

+963
-220
lines changed

25 files changed

+963
-220
lines changed

Diff for: lib/workload/stateful/stacks/fastq-manager-db/deploy/stack.ts

+3
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,14 @@ export class FastqManagerTable extends cdk.Stack {
196196
}
197197

198198
private add_buckets(props: FastqManagerTableStackProps) {
199+
// Add the ntsm bucket with event bridge enabled
199200
new s3.Bucket(this, 'ntsm_bucket', {
200201
bucketName: props.ntsmBucketName,
201202
removalPolicy: props.removalPolicy || RemovalPolicy.RETAIN_ON_UPDATE_OR_DELETE,
203+
eventBridgeEnabled: true, // So that the filemanager can listen to events
202204
});
203205

206+
// Add fastq manager cache bucket
204207
new s3.Bucket(this, 'fastq_manager_cache_bucket', {
205208
bucketName: props.fastqManagerCacheBucketName,
206209
removalPolicy: props.removalPolicy || RemovalPolicy.RETAIN_ON_UPDATE_OR_DELETE,

Diff for: lib/workload/stateless/stacks/fastq-manager/app/api/fastq_manager_api_tools/api/v1/routers/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,8 @@ def run_ntsm_eval(fastq_set_id_x: str, fastq_set_id_y: Optional[str] = None) ->
226226
else:
227227
env_var = RUN_NTSM_EVAL_X_Y_AWS_STEP_FUNCTION_ARN_ENV_VAR
228228
input_dict = {
229-
"fastqSetIdX": fastq_set_id_x,
230-
"fastqSetIdY": fastq_set_id_y
229+
"fastqSetIdA": fastq_set_id_x,
230+
"fastqSetIdB": fastq_set_id_y
231231
}
232232

233233
# Run qc stats through the AWS step function

Diff for: lib/workload/stateless/stacks/fastq-manager/app/api/fastq_manager_api_tools/api/v1/routers/fastq_list_row.py

+19-15
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,10 @@
5151

5252
# Model imports
5353
from ....models import BoolQueryEnum, FastqListRowDict, PresignedUrlModel, QueryPagination
54-
from ....models.fastq_list_row import FastqListRowResponse, FastqListRowData, FastqListRowCreate, \
55-
FastqListRowListResponse, FastqListRowQueryPaginatedResponse
54+
from ....models.fastq_list_row import (
55+
FastqListRowResponse, FastqListRowData, FastqListRowCreate,
56+
FastqListRowListResponse, FastqListRowQueryPaginatedResponse, FastqListRowResponseDict
57+
)
5658
from ....models.fastq_pair import FastqPairStorageObjectPatch, FastqPairStorageObjectData
5759
from ....models.fastq_set import FastqSetData
5860
from ....models.file_compression_info import FileCompressionInfoPatch, FileCompressionInfoData
@@ -334,7 +336,7 @@ async def list_fastq(
334336
"includeS3Details": include_s3_details,
335337
},
336338
**pagination
337-
)
339+
).items()
338340
)),
339341
)
340342

@@ -353,9 +355,11 @@ async def get_fastq(
353355
alias="includeS3Details",
354356
description="Include the s3 details such as s3 uri and storage class"
355357
),
356-
) -> FastqListRowResponse:
358+
) -> FastqListRowResponseDict:
357359
try:
358-
return FastqListRowData.get(fastq_id).to_dict(include_s3_details=include_s3_details)
360+
return FastqListRowData.get(fastq_id).to_dict(
361+
include_s3_details=include_s3_details
362+
)
359363
except DoesNotExist as e:
360364
raise HTTPException(status_code=404, detail=str(e))
361365

@@ -369,7 +373,7 @@ async def get_fastq(
369373
Please use the fastqSet endpoint if registering multiple fastqs simultaneously to reduce race conditions
370374
""")
371375
)
372-
async def create_fastq(fastq_obj: FastqListRowCreate) -> FastqListRowResponse:
376+
async def create_fastq(fastq_obj: FastqListRowCreate) -> FastqListRowResponseDict:
373377
# First convert the CreateFastqListRow to a FastqListRow
374378
fastq_obj = FastqListRowData(**dict(fastq_obj.model_dump(by_alias=True)))
375379

@@ -411,7 +415,7 @@ async def get_fastq_list_row(fastq_id: str = Depends(sanitise_fqr_orcabus_id)) -
411415
description="Update the library associated with a Fastq List Row Object"
412416
)
413417
async def update_library(fastq_id: str = Depends(sanitise_fqr_orcabus_id),
414-
library_obj: LibraryPatch = Depends()) -> FastqListRowResponse:
418+
library_obj: LibraryPatch = Depends()) -> FastqListRowResponseDict:
415419
fastq_obj = FastqListRowData.get(fastq_id)
416420

417421
library_obj = LibraryData(**dict(library_obj.model_dump(by_alias=True)))
@@ -506,7 +510,7 @@ async def get_jobs(
506510
description="Add QC Stats to a Fastq List Row Object"
507511
)
508512
async def add_qc_stats(fastq_id: str = Depends(sanitise_fqr_orcabus_id),
509-
qc_obj: QcInformationPatch = Depends()) -> FastqListRowResponse:
513+
qc_obj: QcInformationPatch = Depends()) -> FastqListRowResponseDict:
510514
fastq_obj = FastqListRowData.get(fastq_id)
511515
fastq_obj.qc = QcInformationData(**dict(qc_obj.model_dump(by_alias=True)))
512516
fastq_obj.save()
@@ -527,7 +531,7 @@ async def add_qc_stats(fastq_id: str = Depends(sanitise_fqr_orcabus_id),
527531
description="Add Read Count Information to a Fastq List Row Object"
528532
)
529533
async def add_read_count(fastq_id: str = Depends(sanitise_fqr_orcabus_id),
530-
read_count_obj: ReadCountInfoPatch = Depends()) -> FastqListRowResponse:
534+
read_count_obj: ReadCountInfoPatch = Depends()) -> FastqListRowResponseDict:
531535
fastq = FastqListRowData.get(fastq_id)
532536

533537
# Get read count info
@@ -553,7 +557,7 @@ async def add_read_count(fastq_id: str = Depends(sanitise_fqr_orcabus_id),
553557
description="Add File Compression Information to a Fastq List Row Object"
554558
)
555559
async def add_file_compression(fastq_id: str = Depends(sanitise_fqr_orcabus_id),
556-
file_compression_obj: FileCompressionInfoPatch = Depends()) -> FastqListRowResponse:
560+
file_compression_obj: FileCompressionInfoPatch = Depends()) -> FastqListRowResponseDict:
557561
# Get fastq object
558562
fastq = FastqListRowData.get(fastq_id)
559563

@@ -591,7 +595,7 @@ async def add_file_compression(fastq_id: str = Depends(sanitise_fqr_orcabus_id),
591595
description="Add Ntsm Storage Object to a Fastq List Row Object"
592596
)
593597
async def add_ntsm_uri(fastq_id: str = Depends(sanitise_fqr_orcabus_id),
594-
ntsm: NtsmUriUpdate = Depends()) -> FastqListRowResponse:
598+
ntsm: NtsmUriUpdate = Depends()) -> FastqListRowResponseDict:
595599
fastq = FastqListRowData.get(fastq_id)
596600
fastq.ntsm = NtsmUriData(**dict(ntsm.model_dump())).ntsm
597601
fastq.save()
@@ -612,7 +616,7 @@ async def add_ntsm_uri(fastq_id: str = Depends(sanitise_fqr_orcabus_id),
612616
tags=["fastq validate"],
613617
description="Validate a Fastq List Row Object"
614618
)
615-
async def validate_fastq(fastq_id: str = Depends(sanitise_fqr_orcabus_id)) -> FastqListRowResponse:
619+
async def validate_fastq(fastq_id: str = Depends(sanitise_fqr_orcabus_id)) -> FastqListRowResponseDict:
616620
fastq = FastqListRowData.get(fastq_id)
617621
fastq.is_valid = True
618622
fastq.save()
@@ -632,7 +636,7 @@ async def validate_fastq(fastq_id: str = Depends(sanitise_fqr_orcabus_id)) -> Fa
632636
tags=["fastq validate"],
633637
description="Invalidate a Fastq List Row Object, this is useful if an instrument run has failed"
634638
)
635-
async def invalidate_fastq(fastq_id: str = Depends(sanitise_fqr_orcabus_id)) -> FastqListRowResponse:
639+
async def invalidate_fastq(fastq_id: str = Depends(sanitise_fqr_orcabus_id)) -> FastqListRowResponseDict:
636640
fastq_obj = FastqListRowData.get(fastq_id)
637641

638642
# Check if the fastq is part of a fastq set
@@ -659,7 +663,7 @@ async def invalidate_fastq(fastq_id: str = Depends(sanitise_fqr_orcabus_id)) ->
659663
description="Add Fastq Pair Storage Object to a Fastq List Row Object"
660664
)
661665
async def add_fastq_pair_storage_object(fastq_id: str = Depends(sanitise_fqr_orcabus_id),
662-
fastq_pair_storage_obj: FastqPairStorageObjectPatch = Depends()) -> FastqListRowResponse:
666+
fastq_pair_storage_obj: FastqPairStorageObjectPatch = Depends()) -> FastqListRowResponseDict:
663667
fastq = FastqListRowData.get(fastq_id)
664668
# Check that no fastqPairStorageObject exists for this fastq id
665669
try:
@@ -682,7 +686,7 @@ async def add_fastq_pair_storage_object(fastq_id: str = Depends(sanitise_fqr_orc
682686
tags=["fastq update"],
683687
description="Remove Fastq Pair Storage Object from a Fastq List Row Object"
684688
)
685-
async def remove_fastq_pair_storage_object(fastq_id: str = Depends(sanitise_fqr_orcabus_id)) -> FastqListRowResponse:
689+
async def remove_fastq_pair_storage_object(fastq_id: str = Depends(sanitise_fqr_orcabus_id)) -> FastqListRowResponseDict:
686690
fastq = FastqListRowData.get(fastq_id)
687691
# Check that the fastqPairStorageObject exists for this fastq id
688692
try:

Diff for: lib/workload/stateless/stacks/fastq-manager/app/api/fastq_manager_api_tools/api/v1/routers/fastq_set.py

+45-25
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
from ....models.fastq_list_row import FastqListRowData
4646
from ....models.fastq_set import (
4747
FastqSetData, FastqSetResponse, FastqSetListResponse, FastqSetCreate,
48-
FastqSetQueryPaginatedResponse
48+
FastqSetQueryPaginatedResponse, FastqSetResponseDict
4949
)
5050
from ....models.library import LibraryData
5151
from ....models.merge_fastq_sets import MergePatch
@@ -54,6 +54,8 @@
5454
is_orcabus_ulid,
5555
sanitise_fqs_orcabus_id,
5656
sanitise_fqr_orcabus_id,
57+
sanitise_fqs_orcabus_id_x,
58+
sanitise_fqs_orcabus_id_y
5759
)
5860

5961
router = APIRouter()
@@ -217,7 +219,7 @@ async def list_fastq_sets(
217219
lambda fqs_iter_: FastqSetData.from_response(**fqs_iter_.to_dict()),
218220
query_lists[0]
219221
)),
220-
include_s3_detail=include_s3_details
222+
include_s3_details=include_s3_details
221223
).model_dump(),
222224
query_pagination=pagination,
223225
params_response=dict(filter(
@@ -253,7 +255,7 @@ async def list_fastq_sets(
253255
query_lists[0]
254256
)
255257
)),
256-
include_s3_detail=include_s3_details
258+
include_s3_details=include_s3_details
257259
).model_dump(by_alias=True),
258260
query_pagination=pagination,
259261
params_response=dict(filter(
@@ -280,27 +282,38 @@ async def list_fastq_sets(
280282
Please use the fastqSet endpoint if registering multiple fastqs simultaneously to reduce race conditions
281283
""")
282284
)
283-
async def create_fastq(fastq_set_obj_create: FastqSetCreate) -> FastqSetResponse:
285+
async def create_fastq(fastq_set_obj_create: FastqSetCreate) -> FastqSetResponseDict:
284286
# Confirm that the library id matches those in the fastq objects
285287
if len(set(list(map(
286288
lambda fastq_obj: fastq_obj.library.orcabus_id,
287-
fastq_set_obj_create.fastq_set
289+
# Iterate over each object in the fastq set, 'get' object if of type 'string'
290+
# Otherwise parse the object itself
291+
list(map(
292+
lambda fastq_obj_iter_: (
293+
FastqListRowData.get(fastq_obj_iter_)
294+
if isinstance(fastq_obj_iter_, str)
295+
else fastq_obj_iter_
296+
),
297+
fastq_set_obj_create.fastq_set
298+
))
288299
)))) > 1:
289300
raise HTTPException(
290301
status_code=409,
291302
detail="Got multiple different library ids in the fastq objects"
292303
)
293304

294305
# Confirm that the library id matches the library id in the fastq set
295-
if fastq_set_obj_create.library.orcabus_id != fastq_set_obj_create.fastq_set[0].library.orcabus_id:
296-
raise HTTPException(
297-
status_code=409,
298-
detail="Fastq set library id does not match those of the fastq objects"
299-
)
306+
first_fastq_obj = fastq_set_obj_create.fastq_set[0]
307+
if isinstance(first_fastq_obj, str):
308+
first_fastq_obj = FastqListRowData.get(first_fastq_obj)
300309

301310
# Confirm that all fastq sets are unique
302311
rgid_exts = list(set(list(map(
303-
lambda fastq_obj_iter_: FastqListRowData(**dict(fastq_obj_iter_.model_dump(by_alias=True))).rgid_ext,
312+
lambda fastq_obj_iter_: (
313+
FastqListRowData.get(fastq_obj_iter_).rgid_ext
314+
if isinstance(fastq_obj_iter_, str)
315+
else FastqListRowData(**dict(fastq_obj_iter_.model_dump(by_alias=True))).rgid_ext
316+
),
304317
fastq_set_obj_create.fastq_set
305318
))))
306319
if len(rgid_exts) != len(fastq_set_obj_create.fastq_set):
@@ -398,6 +411,13 @@ async def create_fastq(fastq_set_obj_create: FastqSetCreate) -> FastqSetResponse
398411
detail="Fastq set contains invalid fastqs"
399412
)
400413

414+
# Ensure that the library id matches the library id in the fastq set
415+
if fastq_set_data_obj.library.orcabus_id != first_fastq_obj.library.orcabus_id:
416+
raise HTTPException(
417+
status_code=409,
418+
detail="Fastq set library id does not match those of the fastq objects"
419+
)
420+
401421
# Add the fastq_set_id to the fastq objects
402422
for fastq_obj in fastq_data_objs:
403423
fastq_obj.fastq_set_id = fastq_set_data_obj.id
@@ -430,18 +450,18 @@ async def create_fastq(fastq_set_obj_create: FastqSetCreate) -> FastqSetResponse
430450
async def get_fastq(
431451
fastq_set_id: str = Depends(sanitise_fqs_orcabus_id),
432452
# Include s3 uri - resolve the s3 uri if requested
433-
include_s3_detail: Optional[bool] = Query(
453+
include_s3_details: Optional[bool] = Query(
434454
default=False,
435-
alias="includeS3Detail",
455+
alias="includeS3Details",
436456
description="Include the s3 uris for the fastq objects"
437457
)
438-
) -> FastqSetResponse:
458+
) -> FastqSetResponseDict:
439459
try:
440460
return FastqSetListResponse(
441461
fastq_set_list=[
442462
FastqSetData.get(fastq_set_id)
443463
],
444-
include_s3_detail=include_s3_detail
464+
include_s3_details=include_s3_details
445465
).model_dump(by_alias=True)[0]
446466
except DoesNotExist as e:
447467
raise HTTPException(status_code=404, detail=str(e))
@@ -465,7 +485,7 @@ async def get_fastq_list_row(fastq_set_id: str = Depends(sanitise_fqs_orcabus_id
465485
tags=["fastqset update"],
466486
description="Link a fastq object to this fastq set"
467487
)
468-
async def link_fastq(fastq_set_id: str = Depends(sanitise_fqs_orcabus_id), fastq_id = Depends(sanitise_fqr_orcabus_id)) -> FastqSetResponse:
488+
async def link_fastq(fastq_set_id: str = Depends(sanitise_fqs_orcabus_id), fastq_id = Depends(sanitise_fqr_orcabus_id)) -> FastqSetResponseDict:
469489
fastq_set_obj = FastqSetData.get(fastq_set_id)
470490
fastq_obj = FastqListRowData.get(fastq_id)
471491

@@ -574,7 +594,7 @@ async def unlink_fastq(fastq_set_id: str = Depends(sanitise_fqs_orcabus_id), fas
574594
tags=["fastqset update"],
575595
description="Set this fastq set as the current fastq set for this library"
576596
)
577-
async def set_current_fastq_set(fastq_set_id: str = Depends(sanitise_fqs_orcabus_id)) -> FastqSetResponse:
597+
async def set_current_fastq_set(fastq_set_id: str = Depends(sanitise_fqs_orcabus_id)) -> FastqSetResponseDict:
578598
fastq_set_obj = FastqSetData.get(fastq_set_id)
579599

580600
# Check fastq set exists
@@ -620,7 +640,7 @@ async def set_current_fastq_set(fastq_set_id: str = Depends(sanitise_fqs_orcabus
620640
tags=["fastqset update"],
621641
description="Set current fastq set flag to false for this fastq set"
622642
)
623-
async def set_current_fastq_set(fastq_set_id: str = Depends(sanitise_fqs_orcabus_id)) -> FastqSetResponse:
643+
async def set_current_fastq_set(fastq_set_id: str = Depends(sanitise_fqs_orcabus_id)) -> FastqSetResponseDict:
624644
fastq_set_obj = FastqSetData.get(fastq_set_id)
625645

626646
# Check fastq set exists
@@ -653,7 +673,7 @@ async def set_current_fastq_set(fastq_set_id: str = Depends(sanitise_fqs_orcabus
653673
tags=["fastqset update"],
654674
description="Allow additional fastqs to this fastq set"
655675
)
656-
async def set_allow_additional_fastqs(fastq_set_id: str = Depends(sanitise_fqs_orcabus_id)) -> FastqSetResponse:
676+
async def set_allow_additional_fastqs(fastq_set_id: str = Depends(sanitise_fqs_orcabus_id)) -> FastqSetResponseDict:
657677
fastq_set_obj = FastqSetData.get(fastq_set_id)
658678

659679
# Check fastq set exists
@@ -702,7 +722,7 @@ async def set_allow_additional_fastqs(fastq_set_id: str = Depends(sanitise_fqs_o
702722
tags=["fastqset update"],
703723
description="Disallow additional fastqs to this fastq set"
704724
)
705-
async def set_allow_additional_fastqs_to_false(fastq_set_id: str = Depends(sanitise_fqs_orcabus_id)) -> FastqSetResponse:
725+
async def set_allow_additional_fastqs_to_false(fastq_set_id: str = Depends(sanitise_fqs_orcabus_id)) -> FastqSetResponseDict:
706726
fastq_set_obj = FastqSetData.get(fastq_set_id)
707727

708728
# Check fastq set exists
@@ -736,7 +756,7 @@ async def set_allow_additional_fastqs_to_false(fastq_set_id: str = Depends(sanit
736756
tags=["fastqset merge"],
737757
description="Merge multiple fastq sets into a single fastq set"
738758
)
739-
async def merge_fastq_sets(fastq_set_ids: MergePatch = Depends()) -> FastqSetResponse:
759+
async def merge_fastq_sets(fastq_set_ids: MergePatch = Depends()) -> FastqSetResponseDict:
740760
# Check that the fastq set ids are unique
741761
fastq_set_ids = fastq_set_ids.fastq_set_ids
742762
if len(list(set(fastq_set_ids))) != len(fastq_set_ids):
@@ -856,7 +876,7 @@ async def validate_ntsm_internal(fastq_set_id: str = Depends(sanitise_fqs_orcabu
856876

857877
# Get the fastq list rows
858878
fastq_list_rows = list(map(
859-
lambda fastq_obj_iter_: FastqListRowData.get(fastq_obj_iter_.id),
879+
lambda fastq_set_id_iter: FastqListRowData.get(fastq_set_id_iter),
860880
fastq_set_obj.fastq_set_ids
861881
))
862882

@@ -882,7 +902,7 @@ async def validate_ntsm_internal(fastq_set_id: str = Depends(sanitise_fqs_orcabu
882902
tags=["fastqset ntsm"],
883903
description="Validate all fastq list rows in the ntsm match, run all-by-all on the ntsms in the fastq set"
884904
)
885-
async def validate_ntsm_internal(fastq_set_id_x: str = Depends(sanitise_fqs_orcabus_id), fastq_set_id_y = Depends(sanitise_fqs_orcabus_id)) -> Dict:
905+
async def validate_ntsm_internal(fastq_set_id_x: str = Depends(sanitise_fqs_orcabus_id_x), fastq_set_id_y = Depends(sanitise_fqs_orcabus_id_y)) -> Dict:
886906
# Get the fastq set object
887907
fastq_set_obj_x = FastqSetData.get(fastq_set_id_x)
888908
fastq_set_obj_y = FastqSetData.get(fastq_set_id_y)
@@ -896,7 +916,7 @@ async def validate_ntsm_internal(fastq_set_id_x: str = Depends(sanitise_fqs_orca
896916

897917
# Get the fastq list rows
898918
fastq_list_rows_x = list(map(
899-
lambda fastq_obj_iter_: FastqListRowData.get(fastq_obj_iter_.id),
919+
lambda fastq_obj_iter_: FastqListRowData.get(fastq_obj_iter_),
900920
fastq_set_obj_x.fastq_set_ids
901921
))
902922

@@ -919,7 +939,7 @@ async def validate_ntsm_internal(fastq_set_id_x: str = Depends(sanitise_fqs_orca
919939

920940
# Get the fastq list rows
921941
fastq_list_rows_y = list(map(
922-
lambda fastq_obj_iter_: FastqListRowData.get(fastq_obj_iter_.id),
942+
lambda fastq_obj_iter_: FastqListRowData.get(fastq_obj_iter_),
923943
fastq_set_obj_y.fastq_set_ids
924944
))
925945

0 commit comments

Comments
 (0)