nci-cidc-api-modules 1.2.54__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.
Files changed (90) hide show
  1. boot.py +8 -0
  2. cidc_api/__init__.py +1 -1
  3. cidc_api/config/db.py +0 -2
  4. cidc_api/models/dataset.py +80 -0
  5. cidc_api/models/db/stage1/additional_treatment_orm.py +8 -1
  6. cidc_api/models/db/stage1/adverse_event_orm.py +8 -1
  7. cidc_api/models/db/stage1/baseline_clinical_assessment_orm.py +8 -1
  8. cidc_api/models/db/stage1/comorbidity_orm.py +8 -1
  9. cidc_api/models/db/stage1/consent_group_orm.py +6 -6
  10. cidc_api/models/db/stage1/demographic_orm.py +8 -1
  11. cidc_api/models/db/stage1/disease_orm.py +8 -1
  12. cidc_api/models/db/stage1/exposure_orm.py +8 -1
  13. cidc_api/models/db/stage1/gvhd_diagnosis_acute_orm.py +8 -3
  14. cidc_api/models/db/stage1/gvhd_diagnosis_chronic_orm.py +8 -3
  15. cidc_api/models/db/stage1/gvhd_organ_acute_orm.py +8 -1
  16. cidc_api/models/db/stage1/gvhd_organ_chronic_orm.py +8 -1
  17. cidc_api/models/db/stage1/medical_history_orm.py +8 -1
  18. cidc_api/models/db/stage1/other_malignancy_orm.py +8 -1
  19. cidc_api/models/db/stage1/participant_orm.py +4 -5
  20. cidc_api/models/db/stage1/prior_treatment_orm.py +8 -2
  21. cidc_api/models/db/stage1/radiotherapy_dose_orm.py +8 -1
  22. cidc_api/models/db/stage1/response_by_system_orm.py +8 -1
  23. cidc_api/models/db/stage1/response_orm.py +9 -2
  24. cidc_api/models/db/stage1/specimen_orm.py +9 -25
  25. cidc_api/models/db/stage1/stem_cell_transplant_orm.py +8 -1
  26. cidc_api/models/db/stage1/surgery_orm.py +8 -1
  27. cidc_api/models/db/stage1/therapy_agent_dose_orm.py +8 -1
  28. cidc_api/models/db/stage1/treatment_orm.py +8 -1
  29. cidc_api/models/db/stage1/trial_orm.py +1 -2
  30. cidc_api/models/db/stage2/additional_treatment_orm.py +8 -1
  31. cidc_api/models/db/stage2/administrative_person_orm.py +8 -1
  32. cidc_api/models/db/stage2/administrative_role_assignment_orm.py +1 -0
  33. cidc_api/models/db/stage2/adverse_event_orm.py +8 -1
  34. cidc_api/models/db/stage2/arm_orm.py +5 -4
  35. cidc_api/models/db/stage2/baseline_clinical_assessment_orm.py +8 -1
  36. cidc_api/models/db/stage2/cohort_orm.py +5 -4
  37. cidc_api/models/db/stage2/comorbidity_orm.py +8 -1
  38. cidc_api/models/db/stage2/consent_group_orm.py +5 -5
  39. cidc_api/models/db/stage2/contact_orm.py +10 -1
  40. cidc_api/models/db/stage2/demographic_orm.py +8 -1
  41. cidc_api/models/db/stage2/disease_orm.py +8 -1
  42. cidc_api/models/db/stage2/exposure_orm.py +8 -1
  43. cidc_api/models/db/stage2/file_orm.py +4 -4
  44. cidc_api/models/db/stage2/gvhd_diagnosis_acute_orm.py +8 -3
  45. cidc_api/models/db/stage2/gvhd_diagnosis_chronic_orm.py +8 -3
  46. cidc_api/models/db/stage2/gvhd_organ_acute_orm.py +8 -1
  47. cidc_api/models/db/stage2/gvhd_organ_chronic_orm.py +8 -1
  48. cidc_api/models/db/stage2/institution_orm.py +4 -4
  49. cidc_api/models/db/stage2/medical_history_orm.py +8 -1
  50. cidc_api/models/db/stage2/other_clinical_endpoint_orm.py +9 -1
  51. cidc_api/models/db/stage2/other_malignancy_orm.py +8 -1
  52. cidc_api/models/db/stage2/participant_orm.py +6 -6
  53. cidc_api/models/db/stage2/prior_treatment_orm.py +9 -3
  54. cidc_api/models/db/stage2/publication_orm.py +5 -4
  55. cidc_api/models/db/stage2/radiotherapy_dose_orm.py +9 -2
  56. cidc_api/models/db/stage2/response_by_system_orm.py +8 -1
  57. cidc_api/models/db/stage2/response_orm.py +9 -2
  58. cidc_api/models/db/stage2/shipment_orm.py +5 -5
  59. cidc_api/models/db/stage2/shipment_specimen_orm.py +8 -2
  60. cidc_api/models/db/stage2/specimen_orm.py +9 -75
  61. cidc_api/models/db/stage2/stem_cell_transplant_orm.py +8 -1
  62. cidc_api/models/db/stage2/surgery_orm.py +8 -1
  63. cidc_api/models/db/stage2/therapy_agent_dose_orm.py +8 -1
  64. cidc_api/models/db/stage2/treatment_orm.py +8 -1
  65. cidc_api/models/db/stage2/trial_orm.py +1 -2
  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.55.dist-info}/METADATA +1 -1
  85. nci_cidc_api_modules-1.2.55.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.55.dist-info}/WHEEL +0 -0
  89. {nci_cidc_api_modules-1.2.54.dist-info → nci_cidc_api_modules-1.2.55.dist-info}/licenses/LICENSE +0 -0
  90. {nci_cidc_api_modules-1.2.54.dist-info → nci_cidc_api_modules-1.2.55.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,11 @@
1
1
  from typing import Self
2
2
 
3
- from pydantic import PositiveInt, model_validator, NonNegativeInt
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
8
+ from cidc_api.models.pydantic.stage1.response import Response
6
9
  from cidc_api.models.types import ResponseSystem, ResponseSystemVersion, BestOverallResponse, YNUNA, YN
7
10
 
8
11
 
@@ -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
@@ -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
- @model_validator(mode="after")
70
- def validate_response_duration_cr(self) -> Self:
71
- if self.best_overall_response in negative_response_values and self.response_duration:
72
- raise ValueError(
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
- @model_validator(mode="after")
79
- def validate_days_to_first_response_cr(self) -> Self:
80
- if self.best_overall_response in negative_response_values and self.days_to_first_response:
81
- raise ValueError(
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
- @model_validator(mode="after")
88
- def validate_days_to_best_response_cr(self) -> Self:
89
- if self.best_overall_response in negative_response_values and self.days_to_best_response:
90
- raise ValueError(
91
- "If best_overall_response does not indicate a positive response, \
92
- please leave days_to_best_response blank."
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
- return self
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 validate_days_to_disease_progression_cr(self) -> Self:
98
- if self.progression in ["No", "Unknown", "Not Applicable"] and self.days_to_disease_progression:
99
- raise ValueError(
100
- "If progression does not indicate confirmed progression of the disease, \
101
- please leave days_to_disease_progress blank."
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 validate_progression_free_survival_cr(self) -> Self:
107
- if self.progression_free_survival_event in ["Unknown", "Not Applicable"] and self.progression_free_survival:
108
- raise ValueError(
109
- "If progression_free_survival_event is not known, \
110
- please leave progression_free_survival blank."
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
@@ -1,32 +1,11 @@
1
1
  from datetime import datetime
2
2
 
3
3
  from cidc_api.models.pydantic.base import Base
4
- from cidc_api.models.types import (
5
- UberonAnatomicalTerm,
6
- ICDO3MorphologicalCode,
7
- SpecimenType,
8
- SpecimenDescription,
9
- TumorType,
10
- CollectionProcedure,
11
- FixationStabilizationType,
12
- PrimaryContainerType,
13
- VolumeUnits,
14
- ProcessedType,
15
- ConcentrationUnits,
16
- DerivativeType,
17
- PBMCRestingPeriodUsed,
18
- MaterialUnits,
19
- MaterialStorageCondition,
20
- QCCondition,
21
- ReplacementRequested,
22
- ResidualUse,
23
- DiagnosisVerification,
24
- AssayType,
25
- )
4
+ from cidc_api.models.types import UberonAnatomicalTerm
26
5
 
27
6
 
28
7
  class Specimen(Base):
29
- # __data_category__ = "specimen"
8
+ __data_category__ = "specimen"
30
9
  __cardinality__ = "many"
31
10
 
32
11
  # The unique internal identifier for the specimen record
@@ -40,173 +19,11 @@ class Specimen(Base):
40
19
  # Formatted as CTTTPPPSS.AA for trial code TTT, participant PPP, sample SS, and aliquot AA.
41
20
  cimac_id: str
42
21
 
43
- # The external identifier for the pathology report
44
- surgical_pathology_report_id: str | None = None
45
-
46
- # The external identifier for the clinical report
47
- clinical_report_id: str | None = None
48
-
49
- # The unique identifier for the specimen from which this specimen was derived
50
- parent_specimen_id: str | None = None
51
-
52
- # The unique identifier for the specimen after undergoing processing
53
- processed_specimen_id: str | None = None
54
-
55
- # The location within the body from which a specimen was originally obtained as captured in the Uberon anatomical term.
56
- # CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=12083894%20and%20ver_nr=1
57
- organ_site_of_collection: UberonAnatomicalTerm | None = None
58
-
59
- # ICD-O-3 code for histology and behavior. e.g. 9665/3"
60
- # CDE: TBD
61
- histology_behavior: ICDO3MorphologicalCode | None = None
62
-
63
- # Histology description. e.g. Hodgkin lymphoma, nod. scler., grade 1",
64
- histology_behavior_description: str | None = None
65
-
66
22
  # Categorical description of timepoint at which the sample was taken.
67
23
  # CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=5899851%20and%20ver_nr=1
68
24
  # Note: CIDC doesn't conform to this CDE's PVs
69
25
  collection_event_name: str
70
26
 
71
- # The type of the specimen
72
- specimen_type: SpecimenType | None = None
73
-
74
- # The type of the specimen, if not captured by specimen_type
75
- specimen_type_other: str | None = None
76
-
77
- # A general description of the specimen
78
- specimen_description: SpecimenDescription | None = None
79
-
80
- # The type of the tumor present in the specimen
81
- tumor_type: TumorType | None = None
82
-
83
- # Description of the procedure used to collect the specimen from the participant
84
- collection_procedure: CollectionProcedure | None = None
85
-
86
- # Description of the procedure used to collect the specimen from the participant, if not captured by collection_procedure
87
- collection_procedure_other: str | None = None
88
-
89
- # The biopsy core number from which the sample was taken.
90
- core_number: str | None = None
91
-
92
- # Type of specimen fixation or stabilization that was employed by the site directly after collection.
93
- fixation_stabilization_type: FixationStabilizationType | None = None
94
-
95
- # The type of container in which the specimen was shipped
96
- primary_container_type: PrimaryContainerType | None = None
97
-
98
- # The type of container in which the specimen was shipped, if not captured by primary_container_type
99
- primary_container_type_other: str | None = None
100
-
101
- # Volume of the specimen
102
- volume: float | None = None
103
-
104
- # The unit of measure of the volume of the specimen
105
- volume_units: VolumeUnits | None = None
106
-
107
- # The type of processing that was performed on the collected specimen by the biobank
108
- processed_type: ProcessedType | None = None
109
-
110
- # The volume of the specimen after being processed by the biobank
111
- processed_volume: float | None = None
112
-
113
- # The unit of measure of the volume of the specimen after being processed by the biobank
114
- processed_volume_units: VolumeUnits | None = None
115
-
116
- # The concentration of the sample after being processed by the biobank
117
- processed_concentration: float | None = None
118
-
119
- # The unit of measure of the concentration of the sample after being processed by the biobank
120
- processed_concentration_units: ConcentrationUnits | None = None
121
-
122
- # The quantity of the sample after being processed by the biobank
123
- processed_quantity: float | None = None
124
-
125
- # The type of the sample derivative
126
- derivative_type: DerivativeType | None = None
127
-
128
- # The volume of the sample derivative
129
- derivative_volume: float | None = None
130
-
131
- # The unit of measure of the volume of the sample derivative
132
- derivative_volume_units: VolumeUnits | None = None
133
-
134
- # The concentration of the sample derivative
135
- derivative_concentration: float | None = None
136
-
137
- # The unit of measure of the concentration of the sample derivative
138
- derivative_concentration_units: ConcentrationUnits | None = None
139
-
140
- # Score the percentage of tumor (including tumor bed) tissue area of the slide (e.g. vs non-malignant or normal tissue) (0-100)
141
- tumor_tissue_total_area_percentage: float | None = None
142
-
143
- # Score the percentage of viable tumor cells comprising the tumor bed area
144
- viable_tumor_area_percentage: float | None = None
145
-
146
- # Score the evaluation of stromal elements (this indicates the % area of tumor bed occupied by non-tumor cells,
147
- # including inflammatory cells [lymphocytes, histiocytes, etc], endothelial cells, fibroblasts, etc)
148
- viable_stroma_area_percentage: float | None = None
149
-
150
- # Score the percentage area of necrosis
151
- necrosis_area_percentage: float | None = None
152
-
153
- # Score the percentage area of Fibrosis
154
- fibrosis_area_percentage: float | None = None
155
-
156
- # Provides a DNA Integrity Number as an indication of extraction quality (values of 1-10)
157
- din: float | None = None
158
-
159
- # Provides an absorbance percentage ratio indicating purity of DNA (values of 0 to 2)
160
- a260_a280: float | None = None
161
-
162
- # Provides an absorbance percentage ratio indicating presence of contaminants (values of 0 to 3)
163
- a260_a230: float | None = None
164
-
165
- # Receiving site determines the percent recovered cells that are viable after thawing.
166
- pbmc_viability: float | None = None
167
-
168
- # Receiving site determines number for PBMCs per vial recovered upon receipt.
169
- pbmc_recovery: float | None = None
170
-
171
- # Receiving site indicates if a resting period was used after PBMC recovery.
172
- pbmc_resting_period_used: PBMCRestingPeriodUsed | None = None
173
-
174
- # Receiving site indicates how much material was used for assay purposes.
175
- material_used: float | None = None
176
-
177
- # Unit of measure for the amount of material used
178
- material_used_units: MaterialUnits | None = None
179
-
180
- # Receiving site indicates how much material remains after assay use.
181
- material_remaining: float | None = None
182
-
183
- # Unit of measure for the amount of material remaining.
184
- material_remaining_units: MaterialUnits | None = None
185
-
186
- # Storage condition of the material once it was received.
187
- material_storage_condition: MaterialStorageCondition | None = None
188
-
189
- # Final status of sample after QC and pathology review.
190
- qc_condition: QCCondition | None = None
191
-
192
- # Indication if sample replacement is/was requested.
193
- replacement_requested: ReplacementRequested | None = None
194
-
195
- # Indication if sample was sent to another location or returned back to biorepository.
196
- residual_use: ResidualUse | None = None
197
-
198
- # Additional comments on sample testing
199
- comments: str | None = None
200
-
201
- # Indicates whether the local pathology review was consistent with the diagnostic pathology report.
202
- diagnosis_verification: DiagnosisVerification | None = None
203
-
204
- # The assay that this sample is expected to be used as input for.
205
- intended_assay: AssayType | None = None
206
-
207
- # The datetime that CIDC ingested the sample/manifest
208
- date_ingested: datetime | None = None
209
-
210
27
  # Days from enrollment date to date specimen was collected.
211
28
  days_to_specimen_collection: int
212
29
 
@@ -1,11 +1,13 @@
1
- from typing import Self
1
+ from pydantic import NonNegativeInt
2
+ from cidc_api.models.pydantic.base import forced_validator, forced_validators
2
3
 
3
- from pydantic import NonNegativeInt, model_validator
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 SurgicalProcedure, UberonAnatomicalTerm, YNU
7
8
 
8
9
 
10
+ @forced_validators
9
11
  class Surgery(Base):
10
12
  __data_category__ = "surgery"
11
13
  __cardinality__ = "many"
@@ -42,8 +44,14 @@ class Surgery(Base):
42
44
  # CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=13362284%20and%20ver_nr=1
43
45
  extent_of_residual_disease: str | None = None
44
46
 
45
- @model_validator(mode="after")
46
- def validate_procedure_other_cr(self) -> Self:
47
- if self.procedure == "Other, specify" and not self.procedure_other:
48
- raise ValueError('If procedure is "Other, specify", please provide procedure_other.')
49
- return self
47
+ @forced_validator
48
+ @classmethod
49
+ def validate_procedure_other_cr(cls, data, info) -> None:
50
+ procedure = data.get("procedure", None)
51
+ procedure_other = data.get("procedure_other", None)
52
+
53
+ if procedure == "Other, specify" and not procedure_other:
54
+ raise ValueLocError(
55
+ 'If procedure is "Other, specify", please provide procedure_other.',
56
+ loc="procedure_other",
57
+ )
@@ -1,11 +1,12 @@
1
- from typing import Self
2
-
3
- from pydantic import NonNegativeInt, NonNegativeFloat, PositiveFloat, model_validator
1
+ from pydantic import NonNegativeInt, NonNegativeFloat, 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 YNU, TherapyAgentDoseUnits
7
7
 
8
8
 
9
+ @forced_validators
9
10
  class TherapyAgentDose(Base):
10
11
  __data_category__ = "therapy_agent_dose"
11
12
  __cardinality__ = "many"
@@ -54,14 +55,26 @@ class TherapyAgentDose(Base):
54
55
  # Description of the dose changes, misses, or delays.
55
56
  changes_delays_description: str | None = None
56
57
 
57
- @model_validator(mode="after")
58
- def validate_changes_delays_description_cr(self) -> Self:
59
- if self.dose_changes_delays == "Yes" and not self.changes_delays_description:
60
- raise ValueError('If dose_changes_delays is "Yes", please provide changes_delays_description.')
61
- return self
62
-
63
- @model_validator(mode="after")
64
- def validate_planned_dose_units_cr(self) -> Self:
65
- if self.planned_dose and not self.planned_dose_units:
66
- raise ValueError("If planned_dose is provided, please provide planned_dose_units.")
67
- return self
58
+ @forced_validator
59
+ @classmethod
60
+ def validate_changes_delays_description_cr(cls, data, info) -> None:
61
+ dose_changes_delays = data.get("dose_changes_delays", None)
62
+ changes_delays_description = data.get("changes_delays_description", None)
63
+
64
+ if dose_changes_delays == "Yes" and not changes_delays_description:
65
+ raise ValueLocError(
66
+ 'If dose_changes_delays is "Yes", please provide changes_delays_description.',
67
+ loc="changes_delays_description",
68
+ )
69
+
70
+ @forced_validator
71
+ @classmethod
72
+ def validate_planned_dose_units_cr(cls, data, info) -> None:
73
+ planned_dose = data.get("planned_dose", None)
74
+ planned_dose_units = data.get("planned_dose_units", None)
75
+
76
+ if planned_dose and not planned_dose_units:
77
+ raise ValueLocError(
78
+ "If planned_dose is provided, please provide planned_dose_units.",
79
+ loc="planned_dose_units",
80
+ )
@@ -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, OffTreatmentReason
7
6
 
8
7
 
8
+ @forced_validators
9
9
  class Treatment(Base):
10
10
  __data_category__ = "treatment"
11
11
  __cardinality__ = "many"
@@ -17,10 +17,10 @@ class Treatment(Base):
17
17
  participant_id: str | None = None
18
18
 
19
19
  # The unique internal identifier for the associated Arm record
20
- arm_id: int | None = None
20
+ arm: str | None = None
21
21
 
22
22
  # The unique internal identifier for the associated Cohort record
23
- cohort_id: int | None = None
23
+ cohort: str | None = None
24
24
 
25
25
  # A unique identifier used to describe a distinct, specific intervention or
26
26
  # treatment that a group or subgroup of participants in a clinical trial receives.
@@ -37,14 +37,28 @@ class Treatment(Base):
37
37
  # If "Other" is selected for "off_treatment_reason", provide a description of the reason.
38
38
  off_treatment_reason_other: str | None = None
39
39
 
40
- @model_validator(mode="after")
41
- def validate_off_treatment_reason_cr(self) -> Self:
42
- if self.off_treatment == "Yes" and not self.off_treatment_reason:
43
- raise ValueError('If off_treatment is "Yes", please provide off_treatment_reason.')
44
- return self
45
-
46
- @model_validator(mode="after")
47
- def validate_off_treatment_reason_other_cr(self) -> Self:
48
- if self.off_treatment_reason == "Other" and not self.off_treatment_reason_other:
49
- raise ValueError('If off_treatment_reason is "Other", please provide off_treatment_reason_other.')
50
- return self
40
+ @forced_validator
41
+ @classmethod
42
+ def validate_off_treatment_reason_cr(cls, data, info) -> None:
43
+ off_treatment = data.get("off_treatment", None)
44
+ off_treatment_reason = data.get("off_treatment_reason", None)
45
+
46
+ if off_treatment == "Yes" and not off_treatment_reason:
47
+ raise ValueLocError(
48
+ 'If off_treatment is "Yes", please provide off_treatment_reason.',
49
+ loc="off_treatment_reason",
50
+ )
51
+ return off_treatment_reason
52
+
53
+ @forced_validator
54
+ @classmethod
55
+ def validate_off_treatment_reason_other_cr(cls, data, info) -> None:
56
+ off_treatment_reason = data.get("off_treatment_reason", None)
57
+ off_treatment_reason_other = data.get("off_treatment_reason_other", None)
58
+
59
+ if off_treatment_reason == "Other" and not off_treatment_reason_other:
60
+ raise ValueLocError(
61
+ 'If off_treatment_reason is "Other", please provide off_treatment_reason_other.',
62
+ loc="off_treatment_reason_other",
63
+ )
64
+ return off_treatment_reason_other
cidc_api/telemetry.py CHANGED
@@ -43,19 +43,19 @@ resource = Resource(attributes={"service.name": f"CIDC-{ENV}"})
43
43
  provider = TracerProvider(resource=resource)
44
44
 
45
45
 
46
- if ENV == "dev" and not TESTING:
47
- from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
48
-
49
- COLLECTOR_ENDPOINT = "127.0.0.1"
50
- COLLECTOR_GRPC_PORT = 6004
51
-
52
- # send spans to local exporter
53
- # 1. download latest version from https://github.com/open-telemetry/opentelemetry-collector-releases/releases (otelcol-contrib_0.140.1_darwin_arm64)
54
- # 2. start exporter from otel folder with `./otelcol-contrib --config=config.yaml`
55
- # 3. download and start Jeager (all-in-one image) - https://www.jaegertracing.io/download/
56
- exporter = OTLPSpanExporter(endpoint=f"http://{COLLECTOR_ENDPOINT}:{COLLECTOR_GRPC_PORT}", insecure=True)
57
- processor = BatchSpanProcessor(exporter)
58
- provider.add_span_processor(processor)
46
+ # if ENV == "dev" and not TESTING:
47
+ # from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
48
+
49
+ # COLLECTOR_ENDPOINT = "127.0.0.1"
50
+ # COLLECTOR_GRPC_PORT = 6004
51
+
52
+ # # send spans to local exporter
53
+ # # 1. download latest version from https://github.com/open-telemetry/opentelemetry-collector-releases/releases (otelcol-contrib_0.140.1_darwin_arm64)
54
+ # # 2. start exporter from otel folder with `./otelcol-contrib --config=config.yaml`
55
+ # # 3. download and start Jeager (all-in-one image) - https://www.jaegertracing.io/download/
56
+ # exporter = OTLPSpanExporter(endpoint=f"http://{COLLECTOR_ENDPOINT}:{COLLECTOR_GRPC_PORT}", insecure=True)
57
+ # processor = BatchSpanProcessor(exporter)
58
+ # provider.add_span_processor(processor)
59
59
 
60
60
  if ENV == "dev-int":
61
61
  from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nci_cidc_api_modules
3
- Version: 1.2.54
3
+ Version: 1.2.55
4
4
  Summary: SQLAlchemy data models and configuration tools used in the NCI CIDC API
5
5
  Home-page: https://github.com/NCI-CIDC/cidc-api-gae
6
6
  License: MIT license