nci-cidc-api-modules 1.2.53__py3-none-any.whl → 1.2.55__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- boot.py +8 -0
- cidc_api/__init__.py +1 -1
- cidc_api/config/db.py +0 -2
- cidc_api/models/code_systems.py +29 -0
- cidc_api/models/dataset.py +80 -0
- cidc_api/models/db/stage1/additional_treatment_orm.py +8 -1
- cidc_api/models/db/stage1/adverse_event_orm.py +10 -2
- cidc_api/models/db/stage1/baseline_clinical_assessment_orm.py +8 -1
- cidc_api/models/db/stage1/comorbidity_orm.py +8 -1
- cidc_api/models/db/stage1/consent_group_orm.py +6 -6
- cidc_api/models/db/stage1/demographic_orm.py +10 -2
- cidc_api/models/db/stage1/disease_orm.py +8 -1
- cidc_api/models/db/stage1/exposure_orm.py +8 -1
- cidc_api/models/db/stage1/gvhd_diagnosis_acute_orm.py +8 -3
- cidc_api/models/db/stage1/gvhd_diagnosis_chronic_orm.py +8 -3
- cidc_api/models/db/stage1/gvhd_organ_acute_orm.py +8 -1
- cidc_api/models/db/stage1/gvhd_organ_chronic_orm.py +8 -1
- cidc_api/models/db/stage1/medical_history_orm.py +8 -1
- cidc_api/models/db/stage1/other_malignancy_orm.py +8 -1
- cidc_api/models/db/stage1/participant_orm.py +4 -5
- cidc_api/models/db/stage1/prior_treatment_orm.py +8 -2
- cidc_api/models/db/stage1/radiotherapy_dose_orm.py +11 -3
- cidc_api/models/db/stage1/response_by_system_orm.py +10 -3
- cidc_api/models/db/stage1/response_orm.py +11 -4
- cidc_api/models/db/stage1/specimen_orm.py +9 -25
- cidc_api/models/db/stage1/stem_cell_transplant_orm.py +8 -1
- cidc_api/models/db/stage1/surgery_orm.py +8 -1
- cidc_api/models/db/stage1/therapy_agent_dose_orm.py +8 -1
- cidc_api/models/db/stage1/treatment_orm.py +8 -1
- cidc_api/models/db/stage1/trial_orm.py +1 -2
- cidc_api/models/db/stage2/additional_treatment_orm.py +8 -1
- cidc_api/models/db/stage2/administrative_person_orm.py +8 -1
- cidc_api/models/db/stage2/administrative_role_assignment_orm.py +1 -0
- cidc_api/models/db/stage2/adverse_event_orm.py +10 -2
- cidc_api/models/db/stage2/arm_orm.py +5 -4
- cidc_api/models/db/stage2/baseline_clinical_assessment_orm.py +8 -1
- cidc_api/models/db/stage2/cohort_orm.py +5 -4
- cidc_api/models/db/stage2/comorbidity_orm.py +8 -1
- cidc_api/models/db/stage2/consent_group_orm.py +5 -5
- cidc_api/models/db/stage2/contact_orm.py +10 -1
- cidc_api/models/db/stage2/demographic_orm.py +10 -2
- cidc_api/models/db/stage2/disease_orm.py +8 -1
- cidc_api/models/db/stage2/exposure_orm.py +8 -1
- cidc_api/models/db/stage2/file_orm.py +4 -4
- cidc_api/models/db/stage2/gvhd_diagnosis_acute_orm.py +8 -3
- cidc_api/models/db/stage2/gvhd_diagnosis_chronic_orm.py +8 -3
- cidc_api/models/db/stage2/gvhd_organ_acute_orm.py +8 -1
- cidc_api/models/db/stage2/gvhd_organ_chronic_orm.py +8 -1
- cidc_api/models/db/stage2/institution_orm.py +4 -4
- cidc_api/models/db/stage2/medical_history_orm.py +8 -1
- cidc_api/models/db/stage2/other_clinical_endpoint_orm.py +9 -1
- cidc_api/models/db/stage2/other_malignancy_orm.py +8 -1
- cidc_api/models/db/stage2/participant_orm.py +6 -6
- cidc_api/models/db/stage2/prior_treatment_orm.py +9 -3
- cidc_api/models/db/stage2/publication_orm.py +5 -4
- cidc_api/models/db/stage2/radiotherapy_dose_orm.py +11 -3
- cidc_api/models/db/stage2/response_by_system_orm.py +10 -3
- cidc_api/models/db/stage2/response_orm.py +11 -4
- cidc_api/models/db/stage2/shipment_orm.py +5 -5
- cidc_api/models/db/stage2/shipment_specimen_orm.py +8 -2
- cidc_api/models/db/stage2/specimen_orm.py +9 -75
- cidc_api/models/db/stage2/stem_cell_transplant_orm.py +8 -1
- cidc_api/models/db/stage2/surgery_orm.py +8 -1
- cidc_api/models/db/stage2/therapy_agent_dose_orm.py +8 -1
- cidc_api/models/db/stage2/treatment_orm.py +8 -1
- cidc_api/models/db/stage2/trial_orm.py +1 -2
- cidc_api/models/pydantic/stage1/adverse_event.py +3 -2
- cidc_api/models/pydantic/stage1/demographic.py +5 -4
- cidc_api/models/pydantic/stage1/radiotherapy_dose.py +2 -1
- cidc_api/models/pydantic/stage1/response.py +3 -3
- cidc_api/models/pydantic/stage1/response_by_system.py +2 -2
- cidc_api/models/pydantic/stage1/trial.py +1 -1
- cidc_api/models/pydantic/stage2/adverse_event.py +54 -26
- cidc_api/models/pydantic/stage2/comorbidity.py +15 -8
- cidc_api/models/pydantic/stage2/demographic.py +47 -29
- cidc_api/models/pydantic/stage2/disease.py +100 -58
- cidc_api/models/pydantic/stage2/exposure.py +14 -8
- cidc_api/models/pydantic/stage2/medical_history.py +15 -8
- cidc_api/models/pydantic/stage2/other_malignancy.py +17 -11
- cidc_api/models/pydantic/stage2/participant.py +27 -15
- cidc_api/models/pydantic/stage2/prior_treatment.py +2 -0
- cidc_api/models/pydantic/stage2/radiotherapy_dose.py +29 -15
- cidc_api/models/pydantic/stage2/response.py +44 -25
- cidc_api/models/pydantic/stage2/response_by_system.py +140 -32
- cidc_api/models/pydantic/stage2/specimen.py +2 -185
- cidc_api/models/pydantic/stage2/surgery.py +15 -7
- cidc_api/models/pydantic/stage2/therapy_agent_dose.py +27 -14
- cidc_api/models/pydantic/stage2/treatment.py +30 -16
- cidc_api/models/types.py +11 -5
- cidc_api/telemetry.py +13 -13
- {nci_cidc_api_modules-1.2.53.dist-info → nci_cidc_api_modules-1.2.55.dist-info}/METADATA +1 -1
- nci_cidc_api_modules-1.2.55.dist-info/RECORD +163 -0
- cidc_api/models/data.py +0 -28
- cidc_api/reference/ctcae.py +0 -34
- cidc_api/reference/gvhd.py +0 -6
- cidc_api/reference/icd10cm.py +0 -12
- cidc_api/reference/icdo3.py +0 -12
- cidc_api/reference/uberon.py +0 -5
- nci_cidc_api_modules-1.2.53.dist-info/RECORD +0 -167
- {nci_cidc_api_modules-1.2.53.dist-info → nci_cidc_api_modules-1.2.55.dist-info}/WHEEL +0 -0
- {nci_cidc_api_modules-1.2.53.dist-info → nci_cidc_api_modules-1.2.55.dist-info}/licenses/LICENSE +0 -0
- {nci_cidc_api_modules-1.2.53.dist-info → nci_cidc_api_modules-1.2.55.dist-info}/top_level.txt +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from pydantic import NonNegativeInt, PositiveFloat, model_validator
|
|
1
|
+
from pydantic import NonNegativeInt, PositiveFloat
|
|
2
|
+
from cidc_api.models.pydantic.base import forced_validator, forced_validators
|
|
4
3
|
|
|
4
|
+
from cidc_api.models.errors import ValueLocError
|
|
5
5
|
from cidc_api.models.pydantic.base import Base
|
|
6
6
|
from cidc_api.models.types import TobaccoSmokingStatus
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
@forced_validators
|
|
9
10
|
class MedicalHistory(Base):
|
|
10
11
|
__data_category__ = "medical_history"
|
|
11
12
|
__cardinality__ = "one"
|
|
@@ -29,8 +30,14 @@ class MedicalHistory(Base):
|
|
|
29
30
|
# CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=16089302%20and%20ver_nr=1
|
|
30
31
|
num_prior_systemic_therapies: NonNegativeInt | None = None
|
|
31
32
|
|
|
32
|
-
@
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
@forced_validator
|
|
34
|
+
@classmethod
|
|
35
|
+
def validate_pack_years_smoked_cr(cls, data, info) -> None:
|
|
36
|
+
tobacco_smoking_status = data.get("tobacco_smoking_status", None)
|
|
37
|
+
pack_years_smoked = data.get("pack_years_smoked", None)
|
|
38
|
+
|
|
39
|
+
if tobacco_smoking_status in ["Never Smoker", "Unknown", "Not reported"] and pack_years_smoked:
|
|
40
|
+
raise ValueLocError(
|
|
41
|
+
"If tobacco_smoking_status indicates non-smoker, please leave pack_years_smoked blank.",
|
|
42
|
+
loc="pack_years_smoked",
|
|
43
|
+
)
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from pydantic import NonPositiveInt, model_validator
|
|
1
|
+
from pydantic import NonPositiveInt
|
|
2
|
+
from cidc_api.models.pydantic.base import forced_validator, forced_validators
|
|
4
3
|
|
|
4
|
+
from cidc_api.models.errors import ValueLocError
|
|
5
5
|
from cidc_api.models.pydantic.base import Base
|
|
6
6
|
from cidc_api.models.types import UberonAnatomicalTerm, ICDO3MorphologicalCode, ICDO3MorphologicalTerm, MalignancyStatus
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
@forced_validators
|
|
9
10
|
class OtherMalignancy(Base):
|
|
10
11
|
__data_category__ = "other_malignancy"
|
|
11
12
|
__cardinality__ = "many"
|
|
@@ -36,14 +37,19 @@ class OtherMalignancy(Base):
|
|
|
36
37
|
# Indicates the participant’s current clinical state regarding the cancer diagnosis.
|
|
37
38
|
other_malignancy_status: MalignancyStatus | None = None
|
|
38
39
|
|
|
39
|
-
@
|
|
40
|
-
|
|
40
|
+
@forced_validator
|
|
41
|
+
@classmethod
|
|
42
|
+
def validate_code_or_term_or_description_cr(cls, data, info) -> None:
|
|
43
|
+
other_malignancy_morphological_term = data.get("other_malignancy_morphological_term", None)
|
|
44
|
+
other_malignancy_description = data.get("other_malignancy_description", None)
|
|
45
|
+
other_malignancy_morphological_code = data.get("other_malignancy_morphological_code", None)
|
|
46
|
+
|
|
41
47
|
if (
|
|
42
|
-
not
|
|
43
|
-
and not
|
|
44
|
-
and not
|
|
48
|
+
not other_malignancy_morphological_code
|
|
49
|
+
and not other_malignancy_morphological_term
|
|
50
|
+
and not other_malignancy_description
|
|
45
51
|
):
|
|
46
|
-
raise
|
|
47
|
-
'Please provide at least one of "morphological_code", "morphological_term" or "malignancy_description".'
|
|
52
|
+
raise ValueLocError(
|
|
53
|
+
'Please provide at least one of "morphological_code", "morphological_term" or "malignancy_description".',
|
|
54
|
+
loc="other_malignancy_morphological_code",
|
|
48
55
|
)
|
|
49
|
-
return self
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from pydantic import model_validator
|
|
1
|
+
from cidc_api.models.pydantic.base import forced_validator, forced_validators
|
|
4
2
|
|
|
3
|
+
from cidc_api.models.errors import ValueLocError
|
|
5
4
|
from cidc_api.models.pydantic.base import Base
|
|
6
5
|
from cidc_api.models.types import YNU
|
|
7
6
|
from cidc_api.models.types import OffStudyReason
|
|
8
7
|
|
|
9
8
|
|
|
9
|
+
@forced_validators
|
|
10
10
|
class Participant(Base):
|
|
11
11
|
__data_category__ = "participant"
|
|
12
12
|
__cardinality__ = "one"
|
|
@@ -16,7 +16,7 @@ class Participant(Base):
|
|
|
16
16
|
participant_id: str | None = None
|
|
17
17
|
|
|
18
18
|
# The participant identifier assigned by the clinical trial team overseeing the study
|
|
19
|
-
native_participant_id: str
|
|
19
|
+
native_participant_id: str | None = None
|
|
20
20
|
|
|
21
21
|
# The globally unique participant identifier assigned by the CIMAC network. e.g. C8P29A7
|
|
22
22
|
cimac_participant_id: str | None = None
|
|
@@ -38,14 +38,26 @@ class Participant(Base):
|
|
|
38
38
|
# Additional information if "Other" is selected for off_study_reason. e.g. "Transfer to another study"
|
|
39
39
|
off_study_reason_other: str | None = None
|
|
40
40
|
|
|
41
|
-
@
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
41
|
+
@forced_validator
|
|
42
|
+
@classmethod
|
|
43
|
+
def off_study_reason_cr(cls, data, info) -> None:
|
|
44
|
+
off_study = data.get("off_study", None)
|
|
45
|
+
off_study_reason = data.get("off_study_reason", None)
|
|
46
|
+
|
|
47
|
+
if off_study == "Yes" and not off_study_reason:
|
|
48
|
+
raise ValueLocError(
|
|
49
|
+
'If "off_study" is "Yes" then "off_study_reason" is required.',
|
|
50
|
+
loc="off_study_reason",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
@forced_validator
|
|
54
|
+
@classmethod
|
|
55
|
+
def off_study_reason_other_cr(cls, data, info) -> None:
|
|
56
|
+
off_study_reason_other = data.get("off_study_reason_other", None)
|
|
57
|
+
off_study_reason = data.get("off_study_reason", None)
|
|
58
|
+
|
|
59
|
+
if off_study_reason == "Other" and not off_study_reason_other:
|
|
60
|
+
raise ValueLocError(
|
|
61
|
+
'If "off_study_reason" is "Other" then "off_study_reason_other" is required.',
|
|
62
|
+
loc="off_study_reason_other",
|
|
63
|
+
)
|
|
@@ -24,6 +24,8 @@ class PriorTreatment(Base):
|
|
|
24
24
|
# the treatment modality.
|
|
25
25
|
prior_treatment_days_to_end: NonPositiveInt | None = None
|
|
26
26
|
|
|
27
|
+
# Specifies the category or kind of prior treatment modality a participant received.
|
|
28
|
+
|
|
27
29
|
# Description of the prior treatment such as its full generic name if it is a type of therapy agent,
|
|
28
30
|
# radiotherapy procedure name and location, or surgical procedure name and location.
|
|
29
31
|
prior_treatment_description: str
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from pydantic import NonNegativeInt, NonNegativeFloat, model_validator
|
|
1
|
+
from pydantic import NonNegativeInt, NonNegativeFloat
|
|
2
|
+
from cidc_api.models.pydantic.base import forced_validator, forced_validators
|
|
4
3
|
|
|
4
|
+
from cidc_api.models.errors import ValueLocError
|
|
5
5
|
from cidc_api.models.pydantic.base import Base
|
|
6
6
|
from cidc_api.models.types import (
|
|
7
|
+
YN,
|
|
7
8
|
YNU,
|
|
8
9
|
RadiotherapyProcedure,
|
|
9
10
|
UberonAnatomicalTerm,
|
|
@@ -12,6 +13,7 @@ from cidc_api.models.types import (
|
|
|
12
13
|
)
|
|
13
14
|
|
|
14
15
|
|
|
16
|
+
@forced_validators
|
|
15
17
|
class RadiotherapyDose(Base):
|
|
16
18
|
__data_category__ = "radiotherapy_dose"
|
|
17
19
|
__cardinality__ = "many"
|
|
@@ -37,7 +39,7 @@ class RadiotherapyDose(Base):
|
|
|
37
39
|
|
|
38
40
|
# Indicates whether the record represents the total dose for a radiotherapy treatment course (which may be either
|
|
39
41
|
# a multi-fractionated or a single-fraction dose).
|
|
40
|
-
is_total_dose:
|
|
42
|
+
is_total_dose: YN
|
|
41
43
|
|
|
42
44
|
# The number of fractions a participant received to deliver the radiation dose.
|
|
43
45
|
number_of_fractions: NonNegativeInt | None = None
|
|
@@ -66,14 +68,26 @@ class RadiotherapyDose(Base):
|
|
|
66
68
|
# CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=7063755%20and%20ver_nr=1
|
|
67
69
|
radiation_extent: RadiationExtent
|
|
68
70
|
|
|
69
|
-
@
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
@forced_validator
|
|
72
|
+
@classmethod
|
|
73
|
+
def validate_changes_delays_description_cr(cls, data, info) -> None:
|
|
74
|
+
dose_changes_delays = data.get("dose_changes_delays", None)
|
|
75
|
+
changes_delays_description = data.get("changes_delays_description", None)
|
|
76
|
+
|
|
77
|
+
if dose_changes_delays == "Yes" and not changes_delays_description:
|
|
78
|
+
raise ValueLocError(
|
|
79
|
+
'If dose_changes_delays is "Yes", please provide changes_delays_description.',
|
|
80
|
+
loc="changes_delays_description",
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
@forced_validator
|
|
84
|
+
@classmethod
|
|
85
|
+
def validate_planned_dose_units_cr(cls, data, info) -> None:
|
|
86
|
+
planned_dose = data.get("planned_dose", None)
|
|
87
|
+
planned_dose_units = data.get("planned_dose_units", None)
|
|
88
|
+
|
|
89
|
+
if planned_dose and not planned_dose_units:
|
|
90
|
+
raise ValueLocError(
|
|
91
|
+
"If planned_dose is provided, please provide planned_dose_units.",
|
|
92
|
+
loc="planned_dose_units",
|
|
93
|
+
)
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from pydantic import NonNegativeInt, model_validator
|
|
1
|
+
from pydantic import NonNegativeInt
|
|
2
|
+
from cidc_api.models.pydantic.base import forced_validator, forced_validators
|
|
4
3
|
|
|
4
|
+
from cidc_api.models.errors import ValueLocError
|
|
5
5
|
from cidc_api.models.pydantic.base import Base
|
|
6
|
-
from cidc_api.models.types import SurvivalStatus, YNUNA, CauseOfDeath
|
|
6
|
+
from cidc_api.models.types import SurvivalStatus, YNUNA, YN, CauseOfDeath
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
@forced_validators
|
|
9
10
|
class Response(Base):
|
|
10
11
|
__data_category__ = "response"
|
|
11
12
|
__cardinality__ = "one"
|
|
@@ -38,28 +39,46 @@ class Response(Base):
|
|
|
38
39
|
cause_of_death: CauseOfDeath | None = None
|
|
39
40
|
|
|
40
41
|
# Indicates whether participant was evaluable for toxicity (adverse events, DLT, etc.) overall.
|
|
41
|
-
evaluable_for_toxicity:
|
|
42
|
+
evaluable_for_toxicity: YN
|
|
42
43
|
|
|
43
44
|
# Indicates whether participant was evaluable for efficacy (for example, response, PFS, OS, etc.) overall.
|
|
44
|
-
evaluable_for_efficacy:
|
|
45
|
+
evaluable_for_efficacy: YN
|
|
45
46
|
|
|
46
47
|
# Days from enrollment date to the last time the patient's vital status was verified.
|
|
47
|
-
days_to_last_vital_status: NonNegativeInt | None = None
|
|
48
|
-
|
|
49
|
-
@
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
@
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
48
|
+
days_to_last_vital_status: NonNegativeInt | None = None
|
|
49
|
+
|
|
50
|
+
@forced_validator
|
|
51
|
+
@classmethod
|
|
52
|
+
def validate_cause_of_death_cr(cls, data, info) -> None:
|
|
53
|
+
survival_status = data.get("survival_status", None)
|
|
54
|
+
cause_of_death = data.get("cause_of_death", None)
|
|
55
|
+
|
|
56
|
+
if survival_status == "Dead" and not cause_of_death:
|
|
57
|
+
raise ValueLocError(
|
|
58
|
+
'If survival_status is "Dead" then cause_of_death is required.',
|
|
59
|
+
loc="cause_of_death",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
@forced_validator
|
|
63
|
+
@classmethod
|
|
64
|
+
def validate_cause_of_death_cr2(cls, data, info) -> None:
|
|
65
|
+
survival_status = data.get("survival_status", None)
|
|
66
|
+
cause_of_death = data.get("cause_of_death", None)
|
|
67
|
+
|
|
68
|
+
if survival_status == "Alive" and cause_of_death:
|
|
69
|
+
raise ValueLocError(
|
|
70
|
+
'If survival_status is "Alive", please leave cause_of_death blank.',
|
|
71
|
+
loc="cause_of_death",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
@forced_validator
|
|
75
|
+
@classmethod
|
|
76
|
+
def validate_days_to_death_cr(cls, data, info) -> None:
|
|
77
|
+
survival_status = data.get("survival_status", None)
|
|
78
|
+
days_to_death = data.get("days_to_death", None)
|
|
79
|
+
|
|
80
|
+
if survival_status in ["Alive", "Unknown"] and days_to_death:
|
|
81
|
+
raise ValueLocError(
|
|
82
|
+
"If survival_status does not indicate death, please leave days_to_death blank.",
|
|
83
|
+
loc="days_to_death",
|
|
84
|
+
)
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
from typing import Self
|
|
2
2
|
|
|
3
|
-
from pydantic import PositiveInt,
|
|
3
|
+
from pydantic import PositiveInt, NonNegativeInt, model_validator
|
|
4
|
+
from cidc_api.models.pydantic.base import forced_validator, forced_validators
|
|
4
5
|
|
|
6
|
+
from cidc_api.models.errors import ValueLocError
|
|
5
7
|
from cidc_api.models.pydantic.base import Base
|
|
6
|
-
from cidc_api.models.
|
|
8
|
+
from cidc_api.models.pydantic.stage1.response import Response
|
|
9
|
+
from cidc_api.models.types import ResponseSystem, ResponseSystemVersion, BestOverallResponse, YNUNA, YN
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
negative_response_values = [
|
|
@@ -17,6 +20,7 @@ negative_response_values = [
|
|
|
17
20
|
]
|
|
18
21
|
|
|
19
22
|
|
|
23
|
+
@forced_validators
|
|
20
24
|
class ResponseBySystem(Base):
|
|
21
25
|
__data_category__ = "response_by_system"
|
|
22
26
|
__cardinality__ = "many"
|
|
@@ -28,6 +32,9 @@ class ResponseBySystem(Base):
|
|
|
28
32
|
# CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=12220014%20and%20ver_nr=1
|
|
29
33
|
participant_id: str | None = None
|
|
30
34
|
|
|
35
|
+
# The linked parent response for the participant. Used for cross-model validation.
|
|
36
|
+
response: Response | None = None
|
|
37
|
+
|
|
31
38
|
# A standardized method used to evaluate and categorize the participant’s clinical response to treatment based on predefined criteria.
|
|
32
39
|
# CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=13381490%20and%20ver_nr=1
|
|
33
40
|
response_system: ResponseSystem
|
|
@@ -45,7 +52,7 @@ class ResponseBySystem(Base):
|
|
|
45
52
|
duration_of_stable_disease: NonNegativeInt | None = None
|
|
46
53
|
|
|
47
54
|
# Indicates whether a patient achieved a durable clinical benefit.
|
|
48
|
-
durable_clinical_benefit:
|
|
55
|
+
durable_clinical_benefit: YN | None = None
|
|
49
56
|
|
|
50
57
|
# Number of days between enrollment date and the date of first response to trial treatment.
|
|
51
58
|
days_to_first_response: PositiveInt | None = None
|
|
@@ -66,47 +73,148 @@ class ResponseBySystem(Base):
|
|
|
66
73
|
# CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=5143957%20and%20ver_nr=1
|
|
67
74
|
progression_free_survival: PositiveInt | None = None
|
|
68
75
|
|
|
69
|
-
@
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
@forced_validator
|
|
77
|
+
@classmethod
|
|
78
|
+
def validate_response_duration_cr(cls, data, info) -> None:
|
|
79
|
+
best_overall_response = data.get("best_overall_response", None)
|
|
80
|
+
response_duration = data.get("response_duration", None)
|
|
81
|
+
|
|
82
|
+
if best_overall_response in negative_response_values and response_duration:
|
|
83
|
+
raise ValueLocError(
|
|
73
84
|
"If best_overall_response does not indicate a positive response, "
|
|
74
|
-
"please leave response_duration blank."
|
|
85
|
+
"please leave response_duration blank.",
|
|
86
|
+
loc="response_duration",
|
|
75
87
|
)
|
|
76
|
-
return self
|
|
77
88
|
|
|
78
|
-
@
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
@forced_validator
|
|
90
|
+
@classmethod
|
|
91
|
+
def validate_days_to_first_response_cr(cls, data, info) -> None:
|
|
92
|
+
best_overall_response = data.get("best_overall_response", None)
|
|
93
|
+
days_to_first_response = data.get("days_to_first_response", None)
|
|
94
|
+
|
|
95
|
+
if best_overall_response in negative_response_values and days_to_first_response:
|
|
96
|
+
raise ValueLocError(
|
|
82
97
|
"If best_overall_response does not indicate a positive response, "
|
|
83
|
-
"please leave days_to_first_response blank."
|
|
98
|
+
"please leave days_to_first_response blank.",
|
|
99
|
+
loc="days_to_first_response",
|
|
84
100
|
)
|
|
85
|
-
return self
|
|
86
101
|
|
|
87
|
-
@
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
102
|
+
@forced_validator
|
|
103
|
+
@classmethod
|
|
104
|
+
def validate_days_to_best_response_cr(cls, data, info) -> None:
|
|
105
|
+
best_overall_response = data.get("best_overall_response", None)
|
|
106
|
+
days_to_best_response = data.get("days_to_best_response", None)
|
|
107
|
+
|
|
108
|
+
if best_overall_response in negative_response_values and days_to_best_response:
|
|
109
|
+
raise ValueLocError(
|
|
110
|
+
"If best_overall_response does not indicate a positive response, "
|
|
111
|
+
"please leave days_to_best_response blank.",
|
|
112
|
+
loc="days_to_best_response",
|
|
93
113
|
)
|
|
94
|
-
|
|
114
|
+
|
|
115
|
+
@forced_validator
|
|
116
|
+
@classmethod
|
|
117
|
+
def validate_days_to_disease_progression_cr(cls, data, info) -> None:
|
|
118
|
+
progression = data.get("progression", None)
|
|
119
|
+
days_to_disease_progression = data.get("days_to_disease_progression", None)
|
|
120
|
+
|
|
121
|
+
if progression in ["No", "Unknown", "Not Applicable"] and days_to_disease_progression:
|
|
122
|
+
raise ValueLocError(
|
|
123
|
+
"If progression does not indicate confirmed progression of the disease, "
|
|
124
|
+
"please leave days_to_disease_progression blank.",
|
|
125
|
+
loc="days_to_disease_progression",
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
@forced_validator
|
|
129
|
+
@classmethod
|
|
130
|
+
def validate_progression_free_survival_cr(cls, data, info) -> None:
|
|
131
|
+
progression_free_survival_event = data.get("progression_free_survival_event", None)
|
|
132
|
+
progression_free_survival = data.get("progression_free_survival", None)
|
|
133
|
+
|
|
134
|
+
if progression_free_survival_event in ["Unknown", "Not Applicable"] and progression_free_survival:
|
|
135
|
+
raise ValueLocError(
|
|
136
|
+
"If progression_free_survival_event is not known, " "please leave progression_free_survival blank.",
|
|
137
|
+
loc="progression_free_survival",
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
@forced_validator
|
|
141
|
+
@classmethod
|
|
142
|
+
def validate_days_to_best_response_chronology(cls, data, info) -> None:
|
|
143
|
+
days_to_first_response = data.get("days_to_first_response", None)
|
|
144
|
+
days_to_best_response = data.get("days_to_best_response", None)
|
|
145
|
+
|
|
146
|
+
if days_to_best_response is not None and days_to_first_response is not None:
|
|
147
|
+
if int(days_to_best_response) < int(days_to_first_response):
|
|
148
|
+
raise ValueLocError(
|
|
149
|
+
'Violate "days_to_best_response" >= days_to_first_response"',
|
|
150
|
+
loc="days_to_best_response",
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
@forced_validator
|
|
154
|
+
@classmethod
|
|
155
|
+
def validate_days_to_disease_progression_chronology(cls, data, info) -> None:
|
|
156
|
+
days_to_disease_progression = data.get("days_to_disease_progression", None)
|
|
157
|
+
days_to_first_response = data.get("days_to_first_response", None)
|
|
158
|
+
|
|
159
|
+
if days_to_first_response is not None and days_to_disease_progression is not None:
|
|
160
|
+
if int(days_to_first_response) >= int(days_to_disease_progression):
|
|
161
|
+
raise ValueLocError(
|
|
162
|
+
'Violate "days_to_first_response" < "days_to_disease_progression"',
|
|
163
|
+
loc="days_to_first_response",
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
@forced_validator
|
|
167
|
+
@classmethod
|
|
168
|
+
def validate_days_to_best_response_progression_chronology(cls, data, info) -> None:
|
|
169
|
+
days_to_disease_progression = data.get("days_to_disease_progression", None)
|
|
170
|
+
days_to_best_response = data.get("days_to_best_response", None)
|
|
171
|
+
|
|
172
|
+
if days_to_best_response is not None and days_to_disease_progression is not None:
|
|
173
|
+
if int(days_to_best_response) >= int(days_to_disease_progression):
|
|
174
|
+
raise ValueLocError(
|
|
175
|
+
'Violate "days_to_best_response" < "days_to_disease_progression"',
|
|
176
|
+
loc="days_to_best_response",
|
|
177
|
+
)
|
|
95
178
|
|
|
96
179
|
@model_validator(mode="after")
|
|
97
|
-
def
|
|
98
|
-
if
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
180
|
+
def validate_days_to_last_vital_status_chronology(self) -> Self:
|
|
181
|
+
if not self.response:
|
|
182
|
+
return self
|
|
183
|
+
|
|
184
|
+
if not self.response.days_to_last_vital_status:
|
|
185
|
+
return self
|
|
186
|
+
|
|
187
|
+
max_value = max(
|
|
188
|
+
self.response.days_to_last_vital_status or 0,
|
|
189
|
+
self.days_to_first_response or 0,
|
|
190
|
+
self.days_to_best_response or 0,
|
|
191
|
+
self.days_to_disease_progression or 0,
|
|
192
|
+
)
|
|
193
|
+
if (self.response.days_to_last_vital_status or 0) != max_value:
|
|
194
|
+
raise ValueLocError(
|
|
195
|
+
'"days_to_last_vital_status" is not the max of all events. Rule: days_to_last_vital_status '
|
|
196
|
+
">= max(days_to_first_response,days_to_best_response,days_to_disease_progression)",
|
|
197
|
+
loc="days_to_last_vital_status,days_to_first_response,days_to_best_response,days_to_disease_progression",
|
|
102
198
|
)
|
|
103
199
|
return self
|
|
104
200
|
|
|
105
201
|
@model_validator(mode="after")
|
|
106
|
-
def
|
|
107
|
-
if
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
202
|
+
def validate_days_to_death_chronology(self) -> Self:
|
|
203
|
+
if not self.response:
|
|
204
|
+
return self
|
|
205
|
+
if not self.response.days_to_death:
|
|
206
|
+
return self
|
|
207
|
+
|
|
208
|
+
max_value = max(
|
|
209
|
+
self.response.days_to_death or 0,
|
|
210
|
+
self.days_to_first_response or 0,
|
|
211
|
+
self.days_to_best_response or 0,
|
|
212
|
+
self.days_to_disease_progression or 0,
|
|
213
|
+
)
|
|
214
|
+
if (self.response.days_to_death or 0) != max_value:
|
|
215
|
+
raise ValueLocError(
|
|
216
|
+
'"days_to_death" is not the max of all events. Rule: days_to_death'
|
|
217
|
+
">= max(days_to_first_response,days_to_best_response,days_to_disease_progression)",
|
|
218
|
+
loc="days_to_death,days_to_first_response,days_to_best_response,days_to_disease_progression",
|
|
111
219
|
)
|
|
112
220
|
return self
|