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,8 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from datetime import datetime
|
|
3
2
|
from typing import List
|
|
3
|
+
from datetime import datetime
|
|
4
4
|
|
|
5
|
-
from sqlalchemy import ForeignKey
|
|
6
5
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
7
6
|
from sqlalchemy.types import JSON
|
|
8
7
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from pydantic import NonNegativeInt
|
|
2
2
|
|
|
3
3
|
from cidc_api.models.pydantic.base import Base
|
|
4
|
-
from cidc_api.
|
|
4
|
+
from cidc_api.code_systems.ctcae import is_ctcae_other_term
|
|
5
5
|
from cidc_api.models.pydantic.base import forced_validator, forced_validators
|
|
6
6
|
from cidc_api.models.errors import ValueLocError
|
|
7
7
|
from cidc_api.models.types import (
|
|
@@ -13,6 +13,7 @@ from cidc_api.models.types import (
|
|
|
13
13
|
SystemOrganClass,
|
|
14
14
|
AttributionCause,
|
|
15
15
|
AttributionLikelihood,
|
|
16
|
+
YN,
|
|
16
17
|
YNU,
|
|
17
18
|
)
|
|
18
19
|
|
|
@@ -53,7 +54,7 @@ class AdverseEvent(Base):
|
|
|
53
54
|
system_organ_class: SystemOrganClass | None = None
|
|
54
55
|
|
|
55
56
|
# Indicator to identify whether a participant exited the study prematurely due to the adverse event being described.
|
|
56
|
-
discontinuation_due_to_event:
|
|
57
|
+
discontinuation_due_to_event: YN
|
|
57
58
|
|
|
58
59
|
# Days from enrollment date to date of onset of the adverse event.
|
|
59
60
|
days_to_onset_of_event: NonNegativeInt
|
|
@@ -15,6 +15,7 @@ from cidc_api.models.types import (
|
|
|
15
15
|
Occupation,
|
|
16
16
|
Education,
|
|
17
17
|
AgeAtEnrollmentUnits,
|
|
18
|
+
YN,
|
|
18
19
|
)
|
|
19
20
|
|
|
20
21
|
|
|
@@ -39,7 +40,7 @@ class Demographic(Base):
|
|
|
39
40
|
|
|
40
41
|
# Indicates whether the participant is 90 years old or over. (for PHI purposes)
|
|
41
42
|
# CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=15354920%20and%20ver_nr=1
|
|
42
|
-
age_90_or_over:
|
|
43
|
+
age_90_or_over: YN
|
|
43
44
|
|
|
44
45
|
# A textual description of a person's sex at birth. e.g. "Male"
|
|
45
46
|
# CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=7572817%20and%20ver_nr=3
|
|
@@ -100,13 +101,13 @@ class Demographic(Base):
|
|
|
100
101
|
age_90_or_over = data.get("age_90_or_over", None)
|
|
101
102
|
age_at_enrollment = int(data.get("age_at_enrollment", None) or 0)
|
|
102
103
|
|
|
103
|
-
if age_90_or_over ==
|
|
104
|
+
if age_90_or_over == "Yes":
|
|
104
105
|
if age_at_enrollment or age_at_enrollment_units:
|
|
105
106
|
raise ValueLocError(
|
|
106
107
|
'If "age_90_or_over" is "Yes" then "age_at_enrollment" and "age_at_enrollment_units" must be blank.',
|
|
107
108
|
loc="age_at_enrollment",
|
|
108
109
|
)
|
|
109
|
-
elif age_90_or_over ==
|
|
110
|
+
elif age_90_or_over == "No":
|
|
110
111
|
if not age_at_enrollment or not age_at_enrollment_units:
|
|
111
112
|
raise ValueLocError(
|
|
112
113
|
'If "age_90_or_over" is "No" then "age_at_enrollment" and "age_at_enrollment_units" are required.',
|
|
@@ -120,7 +121,7 @@ class Demographic(Base):
|
|
|
120
121
|
age_90_or_over = data.get("age_90_or_over", None)
|
|
121
122
|
age_at_enrollment = int(data.get("age_at_enrollment", None) or 0)
|
|
122
123
|
|
|
123
|
-
if
|
|
124
|
+
if age_90_or_over == "No":
|
|
124
125
|
age_in_years = age_at_enrollment if age_at_enrollment_units == "Years" else age_at_enrollment / 365.25
|
|
125
126
|
if age_in_years >= 90:
|
|
126
127
|
raise ValueLocError(
|
|
@@ -4,6 +4,7 @@ from cidc_api.models.pydantic.base import forced_validator, forced_validators
|
|
|
4
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,
|
|
@@ -38,7 +39,7 @@ class RadiotherapyDose(Base):
|
|
|
38
39
|
|
|
39
40
|
# Indicates whether the record represents the total dose for a radiotherapy treatment course (which may be either
|
|
40
41
|
# a multi-fractionated or a single-fraction dose).
|
|
41
|
-
is_total_dose:
|
|
42
|
+
is_total_dose: YN
|
|
42
43
|
|
|
43
44
|
# The number of fractions a participant received to deliver the radiation dose.
|
|
44
45
|
number_of_fractions: NonNegativeInt | None = None
|
|
@@ -3,7 +3,7 @@ from cidc_api.models.pydantic.base import forced_validator, forced_validators
|
|
|
3
3
|
|
|
4
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
9
|
@forced_validators
|
|
@@ -39,10 +39,10 @@ class Response(Base):
|
|
|
39
39
|
cause_of_death: CauseOfDeath | None = None
|
|
40
40
|
|
|
41
41
|
# Indicates whether participant was evaluable for toxicity (adverse events, DLT, etc.) overall.
|
|
42
|
-
evaluable_for_toxicity:
|
|
42
|
+
evaluable_for_toxicity: YN
|
|
43
43
|
|
|
44
44
|
# Indicates whether participant was evaluable for efficacy (for example, response, PFS, OS, etc.) overall.
|
|
45
|
-
evaluable_for_efficacy:
|
|
45
|
+
evaluable_for_efficacy: YN
|
|
46
46
|
|
|
47
47
|
# Days from enrollment date to the last time the patient's vital status was verified.
|
|
48
48
|
days_to_last_vital_status: NonNegativeInt | None = None
|
|
@@ -6,7 +6,7 @@ from cidc_api.models.pydantic.base import forced_validator, forced_validators
|
|
|
6
6
|
from cidc_api.models.errors import ValueLocError
|
|
7
7
|
from cidc_api.models.pydantic.base import Base
|
|
8
8
|
from cidc_api.models.pydantic.stage1.response import Response
|
|
9
|
-
from cidc_api.models.types import ResponseSystem, ResponseSystemVersion, BestOverallResponse, YNUNA
|
|
9
|
+
from cidc_api.models.types import ResponseSystem, ResponseSystemVersion, BestOverallResponse, YNUNA, YN
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
negative_response_values = [
|
|
@@ -52,7 +52,7 @@ class ResponseBySystem(Base):
|
|
|
52
52
|
duration_of_stable_disease: NonNegativeInt | None = None
|
|
53
53
|
|
|
54
54
|
# Indicates whether a patient achieved a durable clinical benefit.
|
|
55
|
-
durable_clinical_benefit:
|
|
55
|
+
durable_clinical_benefit: YN | None = None
|
|
56
56
|
|
|
57
57
|
# Number of days between enrollment date and the date of first response to trial treatment.
|
|
58
58
|
days_to_first_response: PositiveInt | None = None
|
|
@@ -3,7 +3,7 @@ from pydantic import BeforeValidator
|
|
|
3
3
|
from typing import List, Annotated
|
|
4
4
|
|
|
5
5
|
from cidc_api.models.pydantic.base import Base
|
|
6
|
-
from cidc_api.models.types import
|
|
6
|
+
from cidc_api.models.types import TrialOrganization, TrialFundingAgency, AssayType, AgeGroup, PrimaryPurposeType
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class Trial(Base):
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from pydantic import NonNegativeInt, model_validator
|
|
1
|
+
from pydantic import NonNegativeInt
|
|
4
2
|
|
|
5
3
|
from cidc_api.models.pydantic.base import Base
|
|
6
|
-
from cidc_api.
|
|
4
|
+
from cidc_api.code_systems.ctcae import is_ctcae_other_term
|
|
5
|
+
from cidc_api.models.pydantic.base import forced_validator, forced_validators
|
|
6
|
+
from cidc_api.models.errors import ValueLocError
|
|
7
7
|
from cidc_api.models.types import (
|
|
8
8
|
CTCAEEventTerm,
|
|
9
9
|
CTCAEEventCode,
|
|
@@ -13,10 +13,12 @@ from cidc_api.models.types import (
|
|
|
13
13
|
SystemOrganClass,
|
|
14
14
|
AttributionCause,
|
|
15
15
|
AttributionLikelihood,
|
|
16
|
+
YN,
|
|
16
17
|
YNU,
|
|
17
18
|
)
|
|
18
19
|
|
|
19
20
|
|
|
21
|
+
@forced_validators
|
|
20
22
|
class AdverseEvent(Base):
|
|
21
23
|
__data_category__ = "adverse_event"
|
|
22
24
|
__cardinality__ = "many"
|
|
@@ -52,7 +54,7 @@ class AdverseEvent(Base):
|
|
|
52
54
|
system_organ_class: SystemOrganClass | None = None
|
|
53
55
|
|
|
54
56
|
# Indicator to identify whether a participant exited the study prematurely due to the adverse event being described.
|
|
55
|
-
discontinuation_due_to_event:
|
|
57
|
+
discontinuation_due_to_event: YN
|
|
56
58
|
|
|
57
59
|
# Days from enrollment date to date of onset of the adverse event.
|
|
58
60
|
days_to_onset_of_event: NonNegativeInt
|
|
@@ -75,26 +77,52 @@ class AdverseEvent(Base):
|
|
|
75
77
|
# The individual therapy (therapy agent, radiotherapy, surgery, stem cell transplant) in the treatment that is attributed to the adverse event.
|
|
76
78
|
individual_therapy: str | None = None
|
|
77
79
|
|
|
78
|
-
@
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
80
|
+
@forced_validator
|
|
81
|
+
@classmethod
|
|
82
|
+
def validate_term_and_code_cr(cls, data, info) -> None:
|
|
83
|
+
event_code = data.get("event_code", None)
|
|
84
|
+
event_term = data.get("event_term", None)
|
|
85
|
+
|
|
86
|
+
if not event_term and not event_code:
|
|
87
|
+
raise ValueLocError(
|
|
88
|
+
"Please provide event_term or event_code or both",
|
|
89
|
+
loc="event_term,event_code",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
@forced_validator
|
|
93
|
+
@classmethod
|
|
94
|
+
def validate_event_other_specify_cr(cls, data, info) -> None:
|
|
95
|
+
event_other_specify = data.get("event_other_specify", None)
|
|
96
|
+
severity_grade_system = data.get("severity_grade_system", None)
|
|
97
|
+
event_term = data.get("event_term", None)
|
|
98
|
+
|
|
99
|
+
if severity_grade_system == "CTCAE" and is_ctcae_other_term(event_term) and not event_other_specify:
|
|
100
|
+
raise ValueLocError(
|
|
101
|
+
'If severity_grade_system is "CTCAE" and the event_code or event_term are of type '
|
|
102
|
+
'"Other, specify", please provide event_other_specify',
|
|
103
|
+
loc="event_other_specify",
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
@forced_validator
|
|
107
|
+
@classmethod
|
|
108
|
+
def validate_system_organ_class_cr(cls, data, info) -> None:
|
|
109
|
+
event_other_specify = data.get("event_other_specify", None)
|
|
110
|
+
system_organ_class = data.get("system_organ_class", None)
|
|
111
|
+
|
|
112
|
+
if event_other_specify and not system_organ_class:
|
|
113
|
+
raise ValueLocError(
|
|
114
|
+
"If event_other_specify is provided, please provide system_organ_class.", loc="system_organ_class"
|
|
93
115
|
)
|
|
94
|
-
return self
|
|
95
116
|
|
|
96
|
-
@
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
117
|
+
@forced_validator
|
|
118
|
+
@classmethod
|
|
119
|
+
def validate_days_to_resolution_of_event_chronology(cls, data, info) -> None:
|
|
120
|
+
days_to_onset_of_event = data.get("days_to_onset_of_event", None)
|
|
121
|
+
days_to_resolution_of_event = data.get("days_to_resolution_of_event", None)
|
|
122
|
+
|
|
123
|
+
if days_to_resolution_of_event is not None and days_to_onset_of_event is not None:
|
|
124
|
+
if int(days_to_resolution_of_event) < int(days_to_onset_of_event):
|
|
125
|
+
raise ValueLocError(
|
|
126
|
+
'Violate "days_to_onset_of_event" <= "days_to_resolution_of_event"',
|
|
127
|
+
loc="days_to_resolution_of_event",
|
|
128
|
+
)
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Any
|
|
2
2
|
|
|
3
|
-
from pydantic import
|
|
3
|
+
from cidc_api.models.pydantic.base import forced_validator, forced_validators
|
|
4
4
|
|
|
5
|
+
from cidc_api.models.errors import ValueLocError
|
|
5
6
|
from cidc_api.models.pydantic.base import Base
|
|
6
7
|
from cidc_api.models.types import ICD10CMCode, ICD10CMTerm
|
|
7
8
|
|
|
8
9
|
|
|
10
|
+
@forced_validators
|
|
9
11
|
class Comorbidity(Base):
|
|
10
12
|
__data_category__ = "comorbidity"
|
|
11
13
|
__cardinality__ = "many"
|
|
@@ -27,10 +29,15 @@ class Comorbidity(Base):
|
|
|
27
29
|
# A descriptive string that names or briefly describes the comorbidity.
|
|
28
30
|
comorbidity_other: str | None = None
|
|
29
31
|
|
|
30
|
-
@
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
@forced_validator
|
|
33
|
+
@classmethod
|
|
34
|
+
def validate_code_or_term_or_other_cr(cls, data, info) -> None:
|
|
35
|
+
comorbidity_term = data.get("comorbidity_term", None)
|
|
36
|
+
comorbidity_other = data.get("comorbidity_other", None)
|
|
37
|
+
comorbidity_code = data.get("comorbidity_code", None)
|
|
38
|
+
|
|
39
|
+
if not comorbidity_code and not comorbidity_term and not comorbidity_other:
|
|
40
|
+
raise ValueLocError(
|
|
41
|
+
'Please provide at least one of "comorbidity_code", "comorbidity_term" or "comorbidity_other".',
|
|
42
|
+
loc="comorbidity_code,comorbidity_term,comorbidity_other",
|
|
35
43
|
)
|
|
36
|
-
return self
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Annotated, List
|
|
2
2
|
|
|
3
|
-
from pydantic import PositiveInt, NonNegativeFloat, PositiveFloat,
|
|
3
|
+
from pydantic import PositiveInt, NonNegativeFloat, PositiveFloat, BeforeValidator
|
|
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
8
|
from cidc_api.models.types import (
|
|
7
9
|
Sex,
|
|
@@ -13,9 +15,11 @@ from cidc_api.models.types import (
|
|
|
13
15
|
Occupation,
|
|
14
16
|
Education,
|
|
15
17
|
AgeAtEnrollmentUnits,
|
|
18
|
+
YN,
|
|
16
19
|
)
|
|
17
20
|
|
|
18
21
|
|
|
22
|
+
@forced_validators
|
|
19
23
|
class Demographic(Base):
|
|
20
24
|
__data_category__ = "demographic"
|
|
21
25
|
__cardinality__ = "one"
|
|
@@ -36,7 +40,7 @@ class Demographic(Base):
|
|
|
36
40
|
|
|
37
41
|
# Indicates whether the participant is 90 years old or over. (for PHI purposes)
|
|
38
42
|
# CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=15354920%20and%20ver_nr=1
|
|
39
|
-
age_90_or_over:
|
|
43
|
+
age_90_or_over: YN
|
|
40
44
|
|
|
41
45
|
# A textual description of a person's sex at birth. e.g. "Male"
|
|
42
46
|
# CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=7572817%20and%20ver_nr=3
|
|
@@ -90,34 +94,48 @@ class Demographic(Base):
|
|
|
90
94
|
# CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=2681552%20and%20ver_nr=1
|
|
91
95
|
highest_level_of_education: Education | None = None
|
|
92
96
|
|
|
93
|
-
@
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
@forced_validator
|
|
98
|
+
@classmethod
|
|
99
|
+
def validate_age_at_enrollment_cr(cls, data, info) -> None:
|
|
100
|
+
age_at_enrollment_units = data.get("age_at_enrollment_units", None)
|
|
101
|
+
age_90_or_over = data.get("age_90_or_over", None)
|
|
102
|
+
age_at_enrollment = int(data.get("age_at_enrollment", None) or 0)
|
|
103
|
+
|
|
104
|
+
if age_90_or_over == "Yes":
|
|
105
|
+
if age_at_enrollment or age_at_enrollment_units:
|
|
106
|
+
raise ValueLocError(
|
|
107
|
+
'If "age_90_or_over" is "Yes" then "age_at_enrollment" and "age_at_enrollment_units" must be blank.',
|
|
108
|
+
loc="age_at_enrollment",
|
|
99
109
|
)
|
|
100
|
-
|
|
101
|
-
if not
|
|
102
|
-
raise
|
|
103
|
-
'If "age_90_or_over" is "No" then "age_at_enrollment" and "age_at_enrollment_units" are required.'
|
|
110
|
+
elif age_90_or_over == "No":
|
|
111
|
+
if not age_at_enrollment or not age_at_enrollment_units:
|
|
112
|
+
raise ValueLocError(
|
|
113
|
+
'If "age_90_or_over" is "No" then "age_at_enrollment" and "age_at_enrollment_units" are required.',
|
|
114
|
+
loc="age_at_enrollment",
|
|
104
115
|
)
|
|
105
|
-
return self
|
|
106
116
|
|
|
107
|
-
@
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
117
|
+
@forced_validator
|
|
118
|
+
@classmethod
|
|
119
|
+
def validate_age_at_enrollment_value(cls, data, info) -> None:
|
|
120
|
+
age_at_enrollment_units = data.get("age_at_enrollment_units", None)
|
|
121
|
+
age_90_or_over = data.get("age_90_or_over", None)
|
|
122
|
+
age_at_enrollment = int(data.get("age_at_enrollment", None) or 0)
|
|
123
|
+
|
|
124
|
+
if age_90_or_over == "No":
|
|
125
|
+
age_in_years = age_at_enrollment if age_at_enrollment_units == "Years" else age_at_enrollment / 365.25
|
|
113
126
|
if age_in_years >= 90:
|
|
114
|
-
raise
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
127
|
+
raise ValueLocError(
|
|
128
|
+
'"age_at_enrollment" cannot represent a value greater than 90 years of age.',
|
|
129
|
+
loc="age_at_enrollment",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
@forced_validator
|
|
133
|
+
@classmethod
|
|
134
|
+
def validate_body_surface_area_units_cr(cls, data, info) -> None:
|
|
135
|
+
body_surface_area = data.get("body_surface_area", None)
|
|
136
|
+
body_surface_area_units = data.get("body_surface_area_units", None)
|
|
137
|
+
|
|
138
|
+
if body_surface_area and not body_surface_area_units:
|
|
139
|
+
raise ValueLocError(
|
|
140
|
+
'If "body_surface_area" is provided then "body_surface_area_units" must also be provided.'
|
|
122
141
|
)
|
|
123
|
-
return self
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
from pydantic import NonPositiveInt,
|
|
2
|
-
from
|
|
1
|
+
from pydantic import NonPositiveInt, BeforeValidator
|
|
2
|
+
from cidc_api.models.pydantic.base import forced_validator, forced_validators
|
|
3
3
|
|
|
4
|
+
from typing import List, Self, Annotated, get_args, Any
|
|
5
|
+
|
|
6
|
+
from cidc_api.models.errors import ValueLocError
|
|
4
7
|
from cidc_api.models.pydantic.base import Base
|
|
5
8
|
from cidc_api.models.types import (
|
|
6
9
|
TumorGrade,
|
|
@@ -20,6 +23,7 @@ from cidc_api.models.types import (
|
|
|
20
23
|
)
|
|
21
24
|
|
|
22
25
|
|
|
26
|
+
@forced_validators
|
|
23
27
|
class Disease(Base):
|
|
24
28
|
__data_category__ = "disease"
|
|
25
29
|
__cardinality__ = "many"
|
|
@@ -92,67 +96,105 @@ class Disease(Base):
|
|
|
92
96
|
|
|
93
97
|
extramedullary_organ: Annotated[List[UberonAnatomicalTerm] | None, BeforeValidator(Base.split_list)] = []
|
|
94
98
|
|
|
95
|
-
@
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
@forced_validator
|
|
100
|
+
@classmethod
|
|
101
|
+
def validate_code_or_term_or_description_cr(cls, data, info) -> None:
|
|
102
|
+
morphological_term = data.get("morphological_term", None)
|
|
103
|
+
cancer_type_description = data.get("cancer_type_description", None)
|
|
104
|
+
morphological_code = data.get("morphological_code", None)
|
|
105
|
+
|
|
106
|
+
if not morphological_code and not morphological_term and not cancer_type_description:
|
|
107
|
+
raise ValueLocError(
|
|
108
|
+
'Please provide at least one of "morphological_code", "morphological_term" or "cancer_type_description".',
|
|
109
|
+
loc="morphological_code",
|
|
100
110
|
)
|
|
101
|
-
return self
|
|
102
111
|
|
|
103
|
-
@
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
112
|
+
@forced_validator
|
|
113
|
+
@classmethod
|
|
114
|
+
def validate_cancer_stage_system_version(cls, data, info) -> None:
|
|
115
|
+
cancer_stage_system_version = data.get("cancer_stage_system_version", None)
|
|
116
|
+
cancer_stage_system = data.get("cancer_stage_system", None)
|
|
117
|
+
|
|
118
|
+
msg = f"{cancer_stage_system_version} is not applicable to {cancer_stage_system}"
|
|
119
|
+
if cancer_stage_system == "AJCC" and cancer_stage_system_version not in get_args(CancerStageSystemVersionAJCC):
|
|
120
|
+
raise ValueLocError(msg, loc="cancer_stage_system")
|
|
121
|
+
elif cancer_stage_system == "RISS" and cancer_stage_system_version not in get_args(
|
|
111
122
|
CancerStageSystemVersionRISS
|
|
112
123
|
):
|
|
113
|
-
raise
|
|
114
|
-
elif
|
|
124
|
+
raise ValueLocError(msg, loc="cancer_stage_system")
|
|
125
|
+
elif cancer_stage_system == "FIGO" and cancer_stage_system_version not in get_args(
|
|
115
126
|
CancerStageSystemVersionFIGO
|
|
116
127
|
):
|
|
117
|
-
raise
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
@
|
|
121
|
-
def validate_cancer_stage_system_version_cr(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
128
|
+
raise ValueLocError(msg, loc="cancer_stage_system")
|
|
129
|
+
|
|
130
|
+
@forced_validator
|
|
131
|
+
@classmethod
|
|
132
|
+
def validate_cancer_stage_system_version_cr(cls, data, info) -> None:
|
|
133
|
+
cancer_stage_system = data.get("cancer_stage_system", None)
|
|
134
|
+
cancer_stage_system_version = data.get("cancer_stage_system_version", None)
|
|
135
|
+
|
|
136
|
+
if cancer_stage_system != "Not Applicable" and not cancer_stage_system_version:
|
|
137
|
+
raise ValueLocError(
|
|
138
|
+
f'Please provide cancer_stage_system_version when cancer_stage_system is "{cancer_stage_system}"',
|
|
139
|
+
loc="cancer_stage_system_version",
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
@forced_validator
|
|
143
|
+
@classmethod
|
|
144
|
+
def validate_cancer_stage_cr(cls, data, info) -> None:
|
|
145
|
+
cancer_stage_system = data.get("cancer_stage_system", None)
|
|
146
|
+
cancer_stage = data.get("cancer_stage", None)
|
|
147
|
+
|
|
148
|
+
if cancer_stage_system != "Not Applicable" and not cancer_stage:
|
|
149
|
+
raise ValueLocError(
|
|
150
|
+
f'Please provide cancer_stage when cancer_stage_system is "{cancer_stage_system}"',
|
|
151
|
+
loc="cancer_stage",
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
@forced_validator
|
|
155
|
+
@classmethod
|
|
156
|
+
def validate_t_category_cr(cls, data, info) -> None:
|
|
157
|
+
cancer_stage_system = data.get("cancer_stage_system", None)
|
|
158
|
+
t_category = data.get("t_category", None)
|
|
159
|
+
|
|
160
|
+
if cancer_stage_system == "AJCC" and not t_category:
|
|
161
|
+
raise ValueLocError(
|
|
162
|
+
f'Please provide t_category when cancer_stage_system is "{cancer_stage_system}"',
|
|
163
|
+
loc="t_category",
|
|
125
164
|
)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
@
|
|
129
|
-
def
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
@
|
|
141
|
-
def
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
@
|
|
153
|
-
def validate_extramedullary_organ_cr(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
165
|
+
|
|
166
|
+
@forced_validator
|
|
167
|
+
@classmethod
|
|
168
|
+
def validate_n_category_cr(cls, data, info) -> None:
|
|
169
|
+
cancer_stage_system = data.get("cancer_stage_system", None)
|
|
170
|
+
n_category = data.get("n_category", None)
|
|
171
|
+
|
|
172
|
+
if cancer_stage_system == "AJCC" and not n_category:
|
|
173
|
+
raise ValueLocError(
|
|
174
|
+
f'Please provide n_category when cancer_stage_system is "{cancer_stage_system}"',
|
|
175
|
+
loc="n_category",
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
@forced_validator
|
|
179
|
+
@classmethod
|
|
180
|
+
def validate_m_category_cr(cls, data, info) -> None:
|
|
181
|
+
cancer_stage_system = data.get("cancer_stage_system", None)
|
|
182
|
+
m_category = data.get("m_category", None)
|
|
183
|
+
|
|
184
|
+
if cancer_stage_system == "AJCC" and not m_category:
|
|
185
|
+
raise ValueLocError(
|
|
186
|
+
f'Please provide m_category when cancer_stage_system is "{cancer_stage_system}"',
|
|
187
|
+
loc="m_category",
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
@forced_validator
|
|
191
|
+
@classmethod
|
|
192
|
+
def validate_extramedullary_organ_cr(cls, data, info) -> None:
|
|
193
|
+
solely_extramedullary_disease = data.get("solely_extramedullary_disease", None)
|
|
194
|
+
extramedullary_organ = data.get("extramedullary_organ", None)
|
|
195
|
+
|
|
196
|
+
if solely_extramedullary_disease in ["No", "Unknown"] and extramedullary_organ:
|
|
197
|
+
raise ValueLocError(
|
|
198
|
+
"If solely_extramedullary_disease indicates no disease, please leave extramedullary_organ blank.",
|
|
199
|
+
loc="extramedullary_organ",
|
|
157
200
|
)
|
|
158
|
-
return self
|
|
@@ -1,11 +1,11 @@
|
|
|
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, ExposureType
|
|
7
6
|
|
|
8
7
|
|
|
8
|
+
@forced_validators
|
|
9
9
|
class Exposure(Base):
|
|
10
10
|
__data_category__ = "exposure"
|
|
11
11
|
__cardinality__ = "many"
|
|
@@ -25,8 +25,14 @@ class Exposure(Base):
|
|
|
25
25
|
# CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=15753203%20and%20ver_nr=1
|
|
26
26
|
exposure_type: ExposureType | None = None
|
|
27
27
|
|
|
28
|
-
@
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
@forced_validator
|
|
29
|
+
@classmethod
|
|
30
|
+
def validate_exposure_type_cr(cls, data, info) -> None:
|
|
31
|
+
carcinogen_exposure = data.get("carcinogen_exposure", None)
|
|
32
|
+
exposure_type = data.get("exposure_type", None)
|
|
33
|
+
|
|
34
|
+
if carcinogen_exposure in ["No", "Unknown"] and exposure_type:
|
|
35
|
+
raise ValueLocError(
|
|
36
|
+
"If carcinogen_exposure indicates non exposure, please leave exposure_type blank.",
|
|
37
|
+
loc="exposure_type",
|
|
38
|
+
)
|