nci-cidc-api-modules 1.2.54__py3-none-any.whl → 1.2.56__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.
Files changed (90) hide show
  1. boot.py +8 -0
  2. cidc_api/__init__.py +1 -1
  3. cidc_api/models/dataset.py +80 -0
  4. cidc_api/models/db/stage1/additional_treatment_orm.py +8 -1
  5. cidc_api/models/db/stage1/adverse_event_orm.py +8 -1
  6. cidc_api/models/db/stage1/baseline_clinical_assessment_orm.py +8 -1
  7. cidc_api/models/db/stage1/comorbidity_orm.py +8 -1
  8. cidc_api/models/db/stage1/consent_group_orm.py +6 -6
  9. cidc_api/models/db/stage1/demographic_orm.py +8 -1
  10. cidc_api/models/db/stage1/disease_orm.py +8 -1
  11. cidc_api/models/db/stage1/exposure_orm.py +8 -1
  12. cidc_api/models/db/stage1/gvhd_diagnosis_acute_orm.py +8 -3
  13. cidc_api/models/db/stage1/gvhd_diagnosis_chronic_orm.py +8 -3
  14. cidc_api/models/db/stage1/gvhd_organ_acute_orm.py +8 -1
  15. cidc_api/models/db/stage1/gvhd_organ_chronic_orm.py +8 -1
  16. cidc_api/models/db/stage1/medical_history_orm.py +8 -1
  17. cidc_api/models/db/stage1/other_malignancy_orm.py +8 -1
  18. cidc_api/models/db/stage1/participant_orm.py +4 -5
  19. cidc_api/models/db/stage1/prior_treatment_orm.py +8 -2
  20. cidc_api/models/db/stage1/radiotherapy_dose_orm.py +8 -1
  21. cidc_api/models/db/stage1/response_by_system_orm.py +8 -1
  22. cidc_api/models/db/stage1/response_orm.py +9 -2
  23. cidc_api/models/db/stage1/specimen_orm.py +9 -25
  24. cidc_api/models/db/stage1/stem_cell_transplant_orm.py +8 -1
  25. cidc_api/models/db/stage1/surgery_orm.py +8 -1
  26. cidc_api/models/db/stage1/therapy_agent_dose_orm.py +8 -1
  27. cidc_api/models/db/stage1/treatment_orm.py +8 -1
  28. cidc_api/models/db/stage1/trial_orm.py +1 -2
  29. cidc_api/models/db/stage2/additional_treatment_orm.py +8 -1
  30. cidc_api/models/db/stage2/administrative_person_orm.py +8 -1
  31. cidc_api/models/db/stage2/administrative_role_assignment_orm.py +1 -0
  32. cidc_api/models/db/stage2/adverse_event_orm.py +8 -1
  33. cidc_api/models/db/stage2/arm_orm.py +5 -4
  34. cidc_api/models/db/stage2/baseline_clinical_assessment_orm.py +8 -1
  35. cidc_api/models/db/stage2/cohort_orm.py +5 -4
  36. cidc_api/models/db/stage2/comorbidity_orm.py +8 -1
  37. cidc_api/models/db/stage2/consent_group_orm.py +5 -5
  38. cidc_api/models/db/stage2/contact_orm.py +10 -1
  39. cidc_api/models/db/stage2/demographic_orm.py +8 -1
  40. cidc_api/models/db/stage2/disease_orm.py +8 -1
  41. cidc_api/models/db/stage2/exposure_orm.py +8 -1
  42. cidc_api/models/db/stage2/file_orm.py +4 -4
  43. cidc_api/models/db/stage2/gvhd_diagnosis_acute_orm.py +8 -3
  44. cidc_api/models/db/stage2/gvhd_diagnosis_chronic_orm.py +8 -3
  45. cidc_api/models/db/stage2/gvhd_organ_acute_orm.py +8 -1
  46. cidc_api/models/db/stage2/gvhd_organ_chronic_orm.py +8 -1
  47. cidc_api/models/db/stage2/institution_orm.py +4 -4
  48. cidc_api/models/db/stage2/medical_history_orm.py +8 -1
  49. cidc_api/models/db/stage2/other_clinical_endpoint_orm.py +9 -1
  50. cidc_api/models/db/stage2/other_malignancy_orm.py +8 -1
  51. cidc_api/models/db/stage2/participant_orm.py +6 -6
  52. cidc_api/models/db/stage2/prior_treatment_orm.py +9 -3
  53. cidc_api/models/db/stage2/publication_orm.py +5 -4
  54. cidc_api/models/db/stage2/radiotherapy_dose_orm.py +9 -2
  55. cidc_api/models/db/stage2/response_by_system_orm.py +8 -1
  56. cidc_api/models/db/stage2/response_orm.py +9 -2
  57. cidc_api/models/db/stage2/shipment_orm.py +5 -5
  58. cidc_api/models/db/stage2/shipment_specimen_orm.py +8 -2
  59. cidc_api/models/db/stage2/specimen_orm.py +9 -75
  60. cidc_api/models/db/stage2/stem_cell_transplant_orm.py +8 -1
  61. cidc_api/models/db/stage2/surgery_orm.py +8 -1
  62. cidc_api/models/db/stage2/therapy_agent_dose_orm.py +8 -1
  63. cidc_api/models/db/stage2/treatment_orm.py +8 -1
  64. cidc_api/models/db/stage2/trial_orm.py +1 -2
  65. cidc_api/models/pydantic/stage1/response_by_system.py +62 -0
  66. cidc_api/models/pydantic/stage1/trial.py +1 -1
  67. cidc_api/models/pydantic/stage2/adverse_event.py +52 -25
  68. cidc_api/models/pydantic/stage2/comorbidity.py +15 -8
  69. cidc_api/models/pydantic/stage2/demographic.py +45 -28
  70. cidc_api/models/pydantic/stage2/disease.py +100 -58
  71. cidc_api/models/pydantic/stage2/exposure.py +14 -8
  72. cidc_api/models/pydantic/stage2/medical_history.py +15 -8
  73. cidc_api/models/pydantic/stage2/other_malignancy.py +17 -11
  74. cidc_api/models/pydantic/stage2/participant.py +27 -15
  75. cidc_api/models/pydantic/stage2/prior_treatment.py +2 -0
  76. cidc_api/models/pydantic/stage2/radiotherapy_dose.py +27 -14
  77. cidc_api/models/pydantic/stage2/response.py +42 -23
  78. cidc_api/models/pydantic/stage2/response_by_system.py +138 -30
  79. cidc_api/models/pydantic/stage2/specimen.py +2 -185
  80. cidc_api/models/pydantic/stage2/surgery.py +15 -7
  81. cidc_api/models/pydantic/stage2/therapy_agent_dose.py +27 -14
  82. cidc_api/models/pydantic/stage2/treatment.py +30 -16
  83. cidc_api/telemetry.py +13 -13
  84. {nci_cidc_api_modules-1.2.54.dist-info → nci_cidc_api_modules-1.2.56.dist-info}/METADATA +1 -1
  85. nci_cidc_api_modules-1.2.56.dist-info/RECORD +163 -0
  86. cidc_api/models/data.py +0 -28
  87. nci_cidc_api_modules-1.2.54.dist-info/RECORD +0 -163
  88. {nci_cidc_api_modules-1.2.54.dist-info → nci_cidc_api_modules-1.2.56.dist-info}/WHEEL +0 -0
  89. {nci_cidc_api_modules-1.2.54.dist-info → nci_cidc_api_modules-1.2.56.dist-info}/licenses/LICENSE +0 -0
  90. {nci_cidc_api_modules-1.2.54.dist-info → nci_cidc_api_modules-1.2.56.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,9 @@
1
- from typing import Self
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
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,11 +13,12 @@ from cidc_api.models.types import (
13
13
  SystemOrganClass,
14
14
  AttributionCause,
15
15
  AttributionLikelihood,
16
- YNU,
17
16
  YN,
17
+ YNU,
18
18
  )
19
19
 
20
20
 
21
+ @forced_validators
21
22
  class AdverseEvent(Base):
22
23
  __data_category__ = "adverse_event"
23
24
  __cardinality__ = "many"
@@ -76,26 +77,52 @@ class AdverseEvent(Base):
76
77
  # The individual therapy (therapy agent, radiotherapy, surgery, stem cell transplant) in the treatment that is attributed to the adverse event.
77
78
  individual_therapy: str | None = None
78
79
 
79
- @model_validator(mode="after")
80
- def validate_term_and_code_cr(self) -> Self:
81
- if not self.event_term and not self.event_code:
82
- raise ValueError("Please provide event_term or event_code or both")
83
- return self
84
-
85
- @model_validator(mode="after")
86
- def validate_event_other_specify_cr(self) -> Self:
87
- if (
88
- self.severity_grade_system == "CTCAE"
89
- and is_ctcae_other_term(self.event_term)
90
- and not self.event_other_specify
91
- ):
92
- raise ValueError(
93
- 'If severity_grade_system is "CTCAE" and the event_code or event_term are of type "Other, specify", please provide event_other_specify'
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"
94
115
  )
95
- return self
96
116
 
97
- @model_validator(mode="after")
98
- def validate_system_organ_class_cr(self) -> Self:
99
- if self.event_other_specify and not self.system_organ_class:
100
- raise ValueError("If event_other_specify is provided, please provide system_organ_class.")
101
- return self
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 Self
1
+ from typing import Any
2
2
 
3
- from pydantic import model_validator
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
- @model_validator(mode="after")
31
- def validate_code_or_term_or_other_cr(self) -> Self:
32
- if not self.comorbidity_code and not self.comorbidity_term and not self.comorbidity_other:
33
- raise ValueError(
34
- 'Please provide at least one of "comorbidity_code", "comorbidity_term" or "comorbidity_other".'
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 Self, Annotated, List
1
+ from typing import Annotated, List
2
2
 
3
- from pydantic import PositiveInt, NonNegativeFloat, PositiveFloat, model_validator, field_validator, BeforeValidator
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,
@@ -17,6 +19,7 @@ from cidc_api.models.types import (
17
19
  )
18
20
 
19
21
 
22
+ @forced_validators
20
23
  class Demographic(Base):
21
24
  __data_category__ = "demographic"
22
25
  __cardinality__ = "one"
@@ -91,34 +94,48 @@ class Demographic(Base):
91
94
  # CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=2681552%20and%20ver_nr=1
92
95
  highest_level_of_education: Education | None = None
93
96
 
94
- @model_validator(mode="after")
95
- def validate_age_at_enrollment_cr(self) -> Self:
96
- if self.age_90_or_over == "Yes":
97
- if self.age_at_enrollment or self.age_at_enrollment_units:
98
- raise ValueError(
99
- 'If "age_90_or_over" is "Yes" then "age_at_enrollment" and "age_at_enrollment_units" must be blank.'
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",
100
109
  )
101
- elif self.age_90_or_over == "No":
102
- if not self.age_at_enrollment or not self.age_at_enrollment_units:
103
- raise ValueError(
104
- '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",
105
115
  )
106
- return self
107
116
 
108
- @model_validator(mode="after")
109
- def validate_age_at_enrollment_value(self) -> Self:
110
- if self.age_90_or_over == "No":
111
- age_in_years = (
112
- self.age_at_enrollment if self.age_at_enrollment_units == "Years" else self.age_at_enrollment / 365.25
113
- )
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
114
126
  if age_in_years >= 90:
115
- raise ValueError('"age_at_enrollment" cannot represent a value greater than 90 years of age.')
116
- return self
117
-
118
- @model_validator(mode="after")
119
- def validate_body_surface_area_units_cr(self) -> Self:
120
- if self.body_surface_area and not self.body_surface_area_units:
121
- raise ValueError(
122
- 'If "body_surface_area" is provided then "body_surface_area_units_other" must also be provided.'
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.'
123
141
  )
124
- return self
@@ -1,6 +1,9 @@
1
- from pydantic import NonPositiveInt, model_validator, BeforeValidator
2
- from typing import List, Self, Annotated, get_args
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
- @model_validator(mode="after")
96
- def validate_code_or_term_or_description_cr(self) -> Self:
97
- if not self.morphological_code and not self.morphological_term and not self.cancer_type_description:
98
- raise ValueError(
99
- 'Please provide at least one of "morphological_code", "morphological_term" or "cancer_type_description".'
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
- @model_validator(mode="after")
104
- def validate_cancer_stage_system_version(self) -> Self:
105
- msg = f"{self.cancer_stage_system_version} is not applicable to {self.cancer_stage_system}"
106
- if self.cancer_stage_system == "AJCC" and self.cancer_stage_system_version not in get_args(
107
- CancerStageSystemVersionAJCC
108
- ):
109
- raise ValueError(msg)
110
- elif self.cancer_stage_system == "RISS" and self.cancer_stage_system_version not in get_args(
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 ValueError(msg)
114
- elif self.cancer_stage_system == "FIGO" and self.cancer_stage_system_version not in get_args(
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 ValueError(msg)
118
- return self
119
-
120
- @model_validator(mode="after")
121
- def validate_cancer_stage_system_version_cr(self) -> Self:
122
- if self.cancer_stage_system != "Not Applicable" and not self.cancer_stage_system_version:
123
- raise ValueError(
124
- f'Please provide cancer_stage_system_version when cancer_stage_system is "{self.cancer_stage_system}"'
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
- return self
127
-
128
- @model_validator(mode="after")
129
- def validate_cancer_stage_cr(self) -> Self:
130
- if self.cancer_stage_system != "Not Applicable" and not self.cancer_stage:
131
- raise ValueError(f'Please provide cancer_stage when cancer_stage_system is "{self.cancer_stage_system}"')
132
- return self
133
-
134
- @model_validator(mode="after")
135
- def validate_t_category_cr(self) -> Self:
136
- if self.cancer_stage_system == "AJCC" and not self.t_category:
137
- raise ValueError(f'Please provide t_category when cancer_stage_system is "{self.cancer_stage_system}"')
138
- return self
139
-
140
- @model_validator(mode="after")
141
- def validate_n_category_cr(self) -> Self:
142
- if self.cancer_stage_system == "AJCC" and not self.n_category:
143
- raise ValueError(f'Please provide n_category when cancer_stage_system is "{self.cancer_stage_system}"')
144
- return self
145
-
146
- @model_validator(mode="after")
147
- def validate_m_category_cr(self) -> Self:
148
- if self.cancer_stage_system == "AJCC" and not self.m_category:
149
- raise ValueError(f'Please provide m_category when cancer_stage_system is "{self.cancer_stage_system}"')
150
- return self
151
-
152
- @model_validator(mode="after")
153
- def validate_extramedullary_organ_cr(self) -> Self:
154
- if self.solely_extramedullary_disease in ["No", "Unknown"] and self.extramedullary_organ:
155
- raise ValueError(
156
- "If solely_extramedullary_disease indicates no disease, please leave extramedullary_organ blank."
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 typing import Self
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
- @model_validator(mode="after")
29
- def validate_exposure_type_cr(self) -> Self:
30
- if self.carcinogen_exposure in ["No", "Unknown"] and self.exposure_type:
31
- raise ValueError("If carcinogen_exposure indicates non exposure, please leave exposure_type blank.")
32
- return self
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
+ )
@@ -1,11 +1,12 @@
1
- from typing import Self
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
- @model_validator(mode="after")
33
- def validate_pack_years_smoked_cr(self) -> Self:
34
- if self.tobacco_smoking_status in ["Never Smoker", "Unknown", "Not reported"] and self.pack_years_smoked:
35
- raise ValueError("If tobacco_smoking_status indicates non-smoker, please leave pack_years_smoked blank.")
36
- return self
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 typing import Self
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
- @model_validator(mode="after")
40
- def validate_code_or_term_or_description_cr(self) -> Self:
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 self.other_malignancy_morphological_code
43
- and not self.other_malignancy_morphological_term
44
- and not self.other_malignancy_description
48
+ not other_malignancy_morphological_code
49
+ and not other_malignancy_morphological_term
50
+ and not other_malignancy_description
45
51
  ):
46
- raise ValueError(
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 typing import Self
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
- @model_validator(mode="after")
42
- def off_study_reason_cr(self) -> Self:
43
- if self.off_study == "Yes" and not self.off_study_reason:
44
- raise ValueError('If "off_study" is "Yes" then "off_study_reason" is required.')
45
- return self
46
-
47
- @model_validator(mode="after")
48
- def off_study_reason_other_cr(self) -> Self:
49
- if self.off_study_reason == "Other" and not self.off_study_reason_other:
50
- raise ValueError('If "off_study_reason" is "Other" then "off_study_reason_other" is required.')
51
- return self
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