Skip to content

Make RNAFUSION samples be always tumour#4537

Draft
diitaz93 wants to merge 7 commits intomasterfrom
tumor-validation-rnafusion
Draft

Make RNAFUSION samples be always tumour#4537
diitaz93 wants to merge 7 commits intomasterfrom
tumor-validation-rnafusion

Conversation

@diitaz93
Copy link
Contributor

@diitaz93 diitaz93 commented Aug 1, 2025

Description

Closes #4528
Closes #4530
This PR adds validation to RNAFUSION samples so that they are always tumour

Added

  • Boolean tumour field in the RNAFusionSample defaulting to True
  • Field validator over "tumour" that verifies that it is always true
  • Overriding of __setattr__ method of the RNAFusionSample so that it raises an error if tumour is set to False
  • Overriding of model_copy method of the RNAFusionSample so that it raises an error if tumour is set to False when updating
  • Tests for above scenarios

How to prepare for test

  • Ssh to relevant server (depending on type of change)
  • Use stage: us
  • Paxa the environment: paxa
  • Install on stage (example for Hasta):
    bash /home/proj/production/servers/resources/hasta.scilifelab.se/update-tool-stage.sh -e S_cg -t cg -b tumor-validation-rnafusion -a

How to test

  • Submit an RNAFUSION order using the order form with samples without specifying tumour value -> verify that is persisted as True in StatusDB
Screenshot 2025-08-01 at 13 53 16
  • Submit an RNAFUSION order through the order portal using an existing normal (non-tumour) sample -> verify that it raises an error with a meaningful message

  • Submit an RNAFUSION order using the order form specifying false as tumour status -> verify that it raises an error with a meaningful message (not very meaningful message, is there something we can do about this? @islean )

Screenshot 2025-08-01 at 13 57 39

Review

  • Tests executed by
  • "Merge and deploy" approved by
    Thanks for filling in who performed the code review and the test!

This version is a

  • PATCH - when you make backwards compatible bug fixes or documentation/instructions

Implementation Plan

  • Deployed to stage:
  • Deployed to production:

@diitaz93 diitaz93 marked this pull request as ready for review August 1, 2025 09:14
@diitaz93 diitaz93 requested a review from a team as a code owner August 1, 2025 09:14
Copy link
Contributor

@mogmi mogmi left a comment

Choose a reason for hiding this comment

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

Looks good!

@clingen-sthlm clingen-sthlm temporarily deployed to stage August 1, 2025 11:11 Inactive
@clingen-sthlm clingen-sthlm temporarily deployed to stage August 1, 2025 11:12 Inactive
@clingen-sthlm clingen-sthlm temporarily deployed to stage August 1, 2025 11:46 Inactive
@sonarqubecloud
Copy link

sonarqubecloud bot commented Aug 1, 2025

Comment on lines +24 to +33
def default_and_validate_tumour(cls, v) -> bool:
"""Ensure that the tumour field is always set to True.
If it is not provided, it defaults to True.
If it is provided and not True, raise a ValueError.
"""
if v is None:
return True
if v is not True:
raise ValueError("Can't perform RNAFUSION analysis on non-tumour samples")
return v
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we might want this to fail even if is None

Comment on lines +41 to +49
def __setattr__(self, name, value):
if name == "tumour" and hasattr(self, name):
raise ValueError(TUMOUR_ERROR_MESSAGE)
super().__setattr__(name, value)

def model_copy(self, *args, update=None, **kwargs):
if update and "tumour" in update and update["tumour"] is not True:
raise ValueError(TUMOUR_ERROR_MESSAGE)
return super().model_copy(*args, update=update, **kwargs)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this really needed? The RNAFusionSample is like a DTO which is never modified

if not errors.is_empty:
LOG.error(errors.get_error_message())
raise OrderValidationError(message="Order contained errors")
raise OrderValidationError(message=errors.get_error_message())
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this too crazy @islean? This will improve the error message

Copy link
Member

Choose a reason for hiding this comment

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

If we do that change, we should remove the logging of the error above though? Now we will just have the same thing twice.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The thing is, this message is the one the customer sees, not the logging. So during the tests, I got "Order contained errors" in the order portal, which is very confusing if you don't know what is wrong with your order

Copy link
Member

Choose a reason for hiding this comment

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

Oh, but this is not something that the customer can address either way, since the error concerns a field which is not editable for them. So I would say it is not an issue that the "Order contained errors" is nondescript.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is editable in the order form

Copy link
Contributor Author

Choose a reason for hiding this comment

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

they also could pick up an existing non-tumour sample

Copy link
Member

Choose a reason for hiding this comment

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

How can they edit the tumour status? Or you mean that they can do it in the Excel form? Will we even persist that?

Copy link
Member

Choose a reason for hiding this comment

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

Might be worth to inform about existing samples, although I feel that would be better as a filter in the endpoint for fetching old samples

Comment on lines +92 to +102
def test_set_tumour_to_false_fails_rnafusion_sample(rnafusion_order_to_submit: dict):
"""Test that setting tumour to False for a RNAFUSION sample raises an error."""
# GIVEN a parsed RNAFUSION order
order, _ = ModelValidator.validate(order=rnafusion_order_to_submit, model=RNAFusionOrder)

# WHEN setting the tumour field to False for a RNAFUSION sample

# THEN it should raise a ValueError saying that RNAFUSION samples must be tumour samples
with pytest.raises(ValueError) as exc_info:
order.cases[0].samples[0].tumour = False
assert str(exc_info.value) == "RNAFUSION samples must always be tumour samples"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we remove the overriden functions in RNAFusionSample, this test might not be needed anymore

@diitaz93 diitaz93 requested a review from islean August 1, 2025 13:35
@diitaz93 diitaz93 marked this pull request as draft August 1, 2025 13:35
class ExistingRNAFusionSample(ExistingSample):
"""Model with special tumour validation for existing RNAFUSION samples."""

tumour: bool = True
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 we want this to be an internal attribute which customers can not access at all. Then we can type hint it as a literal True. Or we simply remove it from the model. Leaving it open for modification just makes it necessary to add all these validators. If we call it _tumour, customers can not modify it: https://docs.pydantic.dev/latest/concepts/models/#private-model-attributes

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That is a great suggestion, I will try to implement it 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The problem is we want an error to be shown to the customer if they try to use non-tumour samples

Copy link
Member

Choose a reason for hiding this comment

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

Then we need to design how it should look. Given that the tumour status is not rendered for new samples it should likely be in the sample_errors or something of the case?

Copy link
Member

Choose a reason for hiding this comment

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

Just noticed that this is the existing sample. We should fetch tumour status from statusDB for existing samples IMO

Copy link
Member

Choose a reason for hiding this comment

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

Just remembered - we could add a warning that we have overruled the tumour status, similar to what was done for the capture kit here

source: str
subject_id: str = Field(pattern=NAME_PATTERN, min_length=1, max_length=128)
tissue_block_size: TissueBlockEnum | None = None
tumour: bool = True
Copy link
Member

Choose a reason for hiding this comment

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

Same thing here

Copy link
Member

Choose a reason for hiding this comment

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

This model is for new samples which currently do not have a tumour field in the order portal. We should either align them or make the field private

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.

Set rnafusion samples as tumour automatically when ordering Add order validation to prevent ordering RNAfusion analysis of normal samples

4 participants