nci-cidc-api-modules 1.2.16__py3-none-any.whl → 1.2.25__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.
- cidc_api/config/db.py +1 -3
- cidc_api/models/__init__.py +2 -0
- cidc_api/models/data.py +15 -0
- cidc_api/models/db/base_orm.py +25 -0
- cidc_api/models/db/stage2/__init__.py +78 -0
- cidc_api/models/db/stage2/additional_treatment_orm.py +22 -0
- cidc_api/models/db/stage2/administrative_person_orm.py +25 -0
- cidc_api/models/db/stage2/administrative_role_assignment_orm.py +28 -0
- cidc_api/models/db/stage2/adverse_event_orm.py +47 -0
- cidc_api/models/db/stage2/arm_orm.py +23 -0
- cidc_api/models/db/stage2/baseline_clinical_assessment_orm.py +23 -0
- cidc_api/models/db/stage2/cohort_orm.py +23 -0
- cidc_api/models/db/stage2/comorbidity_orm.py +24 -0
- cidc_api/models/db/stage2/consent_group_orm.py +31 -0
- cidc_api/models/db/stage2/contact_orm.py +32 -0
- cidc_api/models/db/stage2/demographic_orm.py +44 -0
- cidc_api/models/db/stage2/disease_orm.py +53 -0
- cidc_api/models/db/stage2/exposure_orm.py +21 -0
- cidc_api/models/db/stage2/file_orm.py +38 -0
- cidc_api/models/db/stage2/gvhd_diagnosis_acute_orm.py +33 -0
- cidc_api/models/db/stage2/gvhd_diagnosis_chronic_orm.py +37 -0
- cidc_api/models/db/stage2/gvhd_organ_acute_orm.py +20 -0
- cidc_api/models/db/stage2/gvhd_organ_chronic_orm.py +20 -0
- cidc_api/models/db/stage2/institution_orm.py +33 -0
- cidc_api/models/db/stage2/medical_history_orm.py +29 -0
- cidc_api/models/db/stage2/other_clinical_endpoint_orm.py +28 -0
- cidc_api/models/db/stage2/other_malignancy_orm.py +30 -0
- cidc_api/models/db/stage2/participant_orm.py +79 -0
- cidc_api/models/db/stage2/prior_treatment_orm.py +28 -0
- cidc_api/models/db/stage2/publication_orm.py +31 -0
- cidc_api/models/db/stage2/radiotherapy_dose_orm.py +39 -0
- cidc_api/models/db/stage2/response_by_system_orm.py +28 -0
- cidc_api/models/db/stage2/response_orm.py +27 -0
- cidc_api/models/db/stage2/shipment_orm.py +47 -0
- cidc_api/models/db/stage2/shipment_specimen_orm.py +24 -0
- cidc_api/models/db/stage2/specimen_orm.py +100 -0
- cidc_api/models/db/stage2/stem_cell_transplant_orm.py +25 -0
- cidc_api/models/db/stage2/surgery_orm.py +27 -0
- cidc_api/models/db/stage2/therapy_agent_dose_orm.py +31 -0
- cidc_api/models/db/stage2/treatment_orm.py +39 -0
- cidc_api/models/db/stage2/trial_orm.py +60 -0
- cidc_api/models/files/facets.py +5 -0
- cidc_api/models/migrations.py +12 -39
- cidc_api/models/models.py +65 -14
- cidc_api/models/pydantic/stage2/__init__.py +78 -0
- cidc_api/models/pydantic/stage2/additional_treatment.py +23 -0
- cidc_api/models/pydantic/stage2/administrative_person.py +30 -0
- cidc_api/models/pydantic/stage2/administrative_role_assignment.py +16 -0
- cidc_api/models/pydantic/stage2/adverse_event.py +100 -0
- cidc_api/models/pydantic/stage2/arm.py +16 -0
- cidc_api/models/pydantic/stage2/base.py +30 -0
- cidc_api/models/pydantic/stage2/baseline_clinical_assessment.py +23 -0
- cidc_api/models/pydantic/stage2/cohort.py +16 -0
- cidc_api/models/pydantic/stage2/comorbidity.py +36 -0
- cidc_api/models/pydantic/stage2/consent_group.py +30 -0
- cidc_api/models/pydantic/stage2/contact.py +35 -0
- cidc_api/models/pydantic/stage2/demographic.py +114 -0
- cidc_api/models/pydantic/stage2/disease.py +144 -0
- cidc_api/models/pydantic/stage2/exposure.py +32 -0
- cidc_api/models/pydantic/stage2/file.py +44 -0
- cidc_api/models/pydantic/stage2/gvhd_diagnosis_acute.py +33 -0
- cidc_api/models/pydantic/stage2/gvhd_diagnosis_chronic.py +32 -0
- cidc_api/models/pydantic/stage2/gvhd_organ_acute.py +22 -0
- cidc_api/models/pydantic/stage2/gvhd_organ_chronic.py +23 -0
- cidc_api/models/pydantic/stage2/institution.py +10 -0
- cidc_api/models/pydantic/stage2/medical_history.py +36 -0
- cidc_api/models/pydantic/stage2/other_clinical_endpoint.py +32 -0
- cidc_api/models/pydantic/stage2/other_malignancy.py +45 -0
- cidc_api/models/pydantic/stage2/participant.py +47 -0
- cidc_api/models/pydantic/stage2/prior_treatment.py +52 -0
- cidc_api/models/pydantic/stage2/publication.py +37 -0
- cidc_api/models/pydantic/stage2/radiotherapy_dose.py +79 -0
- cidc_api/models/pydantic/stage2/response.py +71 -0
- cidc_api/models/pydantic/stage2/response_by_system.py +109 -0
- cidc_api/models/pydantic/stage2/shipment.py +48 -0
- cidc_api/models/pydantic/stage2/shipment_specimen.py +15 -0
- cidc_api/models/pydantic/stage2/specimen.py +211 -0
- cidc_api/models/pydantic/stage2/stem_cell_transplant.py +35 -0
- cidc_api/models/pydantic/stage2/surgery.py +49 -0
- cidc_api/models/pydantic/stage2/therapy_agent_dose.py +67 -0
- cidc_api/models/pydantic/stage2/treatment.py +50 -0
- cidc_api/models/pydantic/stage2/trial.py +85 -0
- cidc_api/models/types.py +1439 -0
- cidc_api/shared/file_handling.py +37 -2
- cidc_api/shared/utils.py +4 -1
- {nci_cidc_api_modules-1.2.16.dist-info → nci_cidc_api_modules-1.2.25.dist-info}/METADATA +6 -4
- nci_cidc_api_modules-1.2.25.dist-info/RECORD +104 -0
- nci_cidc_api_modules-1.2.16.dist-info/RECORD +0 -26
- {nci_cidc_api_modules-1.2.16.dist-info → nci_cidc_api_modules-1.2.25.dist-info}/WHEEL +0 -0
- {nci_cidc_api_modules-1.2.16.dist-info → nci_cidc_api_modules-1.2.25.dist-info}/licenses/LICENSE +0 -0
- {nci_cidc_api_modules-1.2.16.dist-info → nci_cidc_api_modules-1.2.25.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
|
|
3
|
+
from sqlalchemy import ForeignKey
|
|
4
|
+
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
5
|
+
|
|
6
|
+
from cidc_api.models.db.base_orm import BaseORM
|
|
7
|
+
from cidc_api.models.types import YNU, OffTreatmentReason
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TreatmentORM(BaseORM):
|
|
11
|
+
__tablename__ = "treatment"
|
|
12
|
+
__repr_attrs__ = ["treatment_id", "participant_id", "treatment_description"]
|
|
13
|
+
__table_args__ = {"schema": "stage2"}
|
|
14
|
+
|
|
15
|
+
treatment_id: Mapped[int] = mapped_column(primary_key=True)
|
|
16
|
+
participant_id: Mapped[int] = mapped_column(ForeignKey("stage2.participant.participant_id", ondelete="CASCADE"))
|
|
17
|
+
arm_id: Mapped[Optional[int]] = mapped_column(ForeignKey("stage2.arm.arm_id", ondelete="CASCADE"))
|
|
18
|
+
cohort_id: Mapped[Optional[int]] = mapped_column(ForeignKey("stage2.cohort.cohort_id", ondelete="CASCADE"))
|
|
19
|
+
|
|
20
|
+
treatment_description: Mapped[str]
|
|
21
|
+
off_treatment: Mapped[YNU]
|
|
22
|
+
off_treatment_reason: Mapped[Optional[OffTreatmentReason]]
|
|
23
|
+
off_treatment_reason_other: Mapped[Optional[str]]
|
|
24
|
+
|
|
25
|
+
participant: Mapped["ParticipantORM"] = relationship(back_populates="treatments", cascade="all, delete")
|
|
26
|
+
arm: Mapped[Optional["ArmORM"]] = relationship(cascade="all, delete")
|
|
27
|
+
cohort: Mapped[Optional["CohortORM"]] = relationship(cascade="all, delete")
|
|
28
|
+
adverse_events: Mapped[List["AdverseEventORM"]] = relationship(back_populates="treatment", cascade="all, delete")
|
|
29
|
+
|
|
30
|
+
therapy_agent_doses: Mapped[List["TherapyAgentDoseORM"]] = relationship(
|
|
31
|
+
back_populates="treatment", cascade="all, delete"
|
|
32
|
+
)
|
|
33
|
+
radiotherapy_doses: Mapped[List["RadiotherapyDoseORM"]] = relationship(
|
|
34
|
+
back_populates="treatment", cascade="all, delete"
|
|
35
|
+
)
|
|
36
|
+
surgeries: Mapped[List["SurgeryORM"]] = relationship(back_populates="treatment", cascade="all, delete")
|
|
37
|
+
stem_cell_transplants: Mapped[List["StemCellTransplantORM"]] = relationship(
|
|
38
|
+
back_populates="treatment", cascade="all, delete"
|
|
39
|
+
)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
|
|
4
|
+
from sqlalchemy import ForeignKey
|
|
5
|
+
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
6
|
+
from sqlalchemy.types import JSON
|
|
7
|
+
|
|
8
|
+
from cidc_api.models.db.base_orm import BaseORM
|
|
9
|
+
from cidc_api.models.types import TrialStatus, AssayType, TrialOrganization, TrialFundingAgency
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TrialORM(BaseORM):
|
|
13
|
+
__tablename__ = "trial"
|
|
14
|
+
__repr_attrs__ = ["trial_id", "version"]
|
|
15
|
+
__table_args__ = {"schema": "stage2"}
|
|
16
|
+
|
|
17
|
+
trial_id: Mapped[str] = mapped_column(primary_key=True)
|
|
18
|
+
version: Mapped[str] = mapped_column(primary_key=True)
|
|
19
|
+
|
|
20
|
+
nct_id: Mapped[Optional[str]]
|
|
21
|
+
nci_id: Mapped[Optional[str]]
|
|
22
|
+
trial_name: Mapped[Optional[str]]
|
|
23
|
+
trial_type: Mapped[Optional[str]]
|
|
24
|
+
trial_description: Mapped[Optional[str]]
|
|
25
|
+
trial_organization: Mapped[Optional[TrialOrganization]]
|
|
26
|
+
grant_or_affiliated_network: Mapped[Optional[TrialFundingAgency]]
|
|
27
|
+
trial_status: Mapped[TrialStatus]
|
|
28
|
+
biobank_institution_id: Mapped[Optional[int]]
|
|
29
|
+
justification: Mapped[Optional[str]]
|
|
30
|
+
dates_of_conduct_start: Mapped[datetime]
|
|
31
|
+
dates_of_conduct_end: Mapped[Optional[datetime]]
|
|
32
|
+
schema_file_id: Mapped[Optional[int]]
|
|
33
|
+
biomarker_plan: Mapped[Optional[str]]
|
|
34
|
+
data_sharing_plan: Mapped[Optional[str]]
|
|
35
|
+
expected_assays: Mapped[Optional[List[AssayType]]] = mapped_column(JSON, nullable=True)
|
|
36
|
+
is_liquid_tumor_trial: Mapped[bool]
|
|
37
|
+
dbgap_study_accession: Mapped[Optional[str]]
|
|
38
|
+
|
|
39
|
+
biobank: Mapped["InstitutionORM"] = relationship(back_populates="trial")
|
|
40
|
+
schema: Mapped[Optional["FileORM"]] = relationship(back_populates="trial", viewonly=True)
|
|
41
|
+
administrative_role_assignments: Mapped[List["AdministrativeRoleAssignmentORM"]] = relationship(
|
|
42
|
+
back_populates="trial", cascade="all, delete", passive_deletes=True
|
|
43
|
+
)
|
|
44
|
+
arms: Mapped[List["ArmORM"]] = relationship(back_populates="trial", cascade="all, delete", passive_deletes=True)
|
|
45
|
+
cohorts: Mapped[List["CohortORM"]] = relationship(
|
|
46
|
+
back_populates="trial", cascade="all, delete", passive_deletes=True
|
|
47
|
+
)
|
|
48
|
+
participants: Mapped[List["ParticipantORM"]] = relationship(
|
|
49
|
+
back_populates="trial", cascade="all, delete", passive_deletes=True
|
|
50
|
+
)
|
|
51
|
+
shipments: Mapped[List["ShipmentORM"]] = relationship(
|
|
52
|
+
back_populates="trial", cascade="all, delete", passive_deletes=True
|
|
53
|
+
)
|
|
54
|
+
files: Mapped[List["FileORM"]] = relationship(back_populates="trial", cascade="all, delete", passive_deletes=True)
|
|
55
|
+
publications: Mapped[List["PublicationORM"]] = relationship(
|
|
56
|
+
back_populates="trial", cascade="all, delete", passive_deletes=True
|
|
57
|
+
)
|
|
58
|
+
consent_groups: Mapped[List["ConsentGroupORM"]] = relationship(
|
|
59
|
+
back_populates="trial", cascade="all, delete", passive_deletes=True
|
|
60
|
+
)
|
cidc_api/models/files/facets.py
CHANGED
|
@@ -439,6 +439,11 @@ assay_facets: Facets = {
|
|
|
439
439
|
"Analysis files for all samples run on the Olink platform in the trial.",
|
|
440
440
|
),
|
|
441
441
|
},
|
|
442
|
+
"Olink HT": {
|
|
443
|
+
"Batch-Level Combined File": FacetConfig(["/olink_ht/batch_level_combined_file.parquet"]),
|
|
444
|
+
"Study-Level Combined File": FacetConfig(["/olink_ht/study_level_combined_file.parquet"]),
|
|
445
|
+
"Npx Run File": FacetConfig(["/olink_ht/npx_run_file.parquet"]),
|
|
446
|
+
},
|
|
442
447
|
"IHC": {
|
|
443
448
|
"Images": FacetConfig(["/ihc/ihc_image."]),
|
|
444
449
|
"Combined Markers": FacetConfig(["csv|ihc marker combined"]),
|
cidc_api/models/migrations.py
CHANGED
|
@@ -91,15 +91,11 @@ def migration_session():
|
|
|
91
91
|
session.close()
|
|
92
92
|
|
|
93
93
|
|
|
94
|
-
def run_metadata_migration(
|
|
95
|
-
metadata_migration: Callable[[dict], MigrationResult], use_upload_jobs_table: bool
|
|
96
|
-
):
|
|
94
|
+
def run_metadata_migration(metadata_migration: Callable[[dict], MigrationResult], use_upload_jobs_table: bool):
|
|
97
95
|
"""Migrate trial metadata, upload job patches, and downloadable files according to `metadata_migration`"""
|
|
98
96
|
with migration_session() as (session, task_queue):
|
|
99
97
|
try:
|
|
100
|
-
_run_metadata_migration(
|
|
101
|
-
metadata_migration, use_upload_jobs_table, task_queue, session
|
|
102
|
-
)
|
|
98
|
+
_run_metadata_migration(metadata_migration, use_upload_jobs_table, task_queue, session)
|
|
103
99
|
except:
|
|
104
100
|
traceback.print_exc()
|
|
105
101
|
raise
|
|
@@ -122,9 +118,7 @@ class ManifestUploads(CommonColumns):
|
|
|
122
118
|
__tablename__ = "manifest_uploads"
|
|
123
119
|
|
|
124
120
|
|
|
125
|
-
def _select_successful_assay_uploads(
|
|
126
|
-
use_upload_jobs_table: bool, session: Session
|
|
127
|
-
) -> List[UploadJobs]:
|
|
121
|
+
def _select_successful_assay_uploads(use_upload_jobs_table: bool, session: Session) -> List[UploadJobs]:
|
|
128
122
|
if use_upload_jobs_table:
|
|
129
123
|
return (
|
|
130
124
|
session.query(UploadJobs)
|
|
@@ -133,21 +127,12 @@ def _select_successful_assay_uploads(
|
|
|
133
127
|
.all()
|
|
134
128
|
)
|
|
135
129
|
|
|
136
|
-
return (
|
|
137
|
-
session.query(AssayUploads)
|
|
138
|
-
.filter_by(status=UploadJobStatus.MERGE_COMPLETED.value)
|
|
139
|
-
.with_for_update()
|
|
140
|
-
.all()
|
|
141
|
-
)
|
|
130
|
+
return session.query(AssayUploads).filter_by(status=UploadJobStatus.MERGE_COMPLETED.value).with_for_update().all()
|
|
142
131
|
|
|
143
132
|
|
|
144
|
-
def _select_manifest_uploads(
|
|
145
|
-
use_upload_jobs_table: bool, session: Session
|
|
146
|
-
) -> List[UploadJobs]:
|
|
133
|
+
def _select_manifest_uploads(use_upload_jobs_table: bool, session: Session) -> List[UploadJobs]:
|
|
147
134
|
if use_upload_jobs_table:
|
|
148
|
-
return (
|
|
149
|
-
session.query(UploadJobs).filter_by(multifile=False).with_for_update().all()
|
|
150
|
-
)
|
|
135
|
+
return session.query(UploadJobs).filter_by(multifile=False).with_for_update().all()
|
|
151
136
|
|
|
152
137
|
return session.query(ManifestUploads).with_for_update().all()
|
|
153
138
|
|
|
@@ -188,21 +173,15 @@ def _run_metadata_migration(
|
|
|
188
173
|
|
|
189
174
|
# Regenerate additional metadata from the migrated clinical trial
|
|
190
175
|
# metadata object.
|
|
191
|
-
print(
|
|
192
|
-
f"Regenerating additional metadata for artifact with uuid {artifact['upload_placeholder']}"
|
|
193
|
-
)
|
|
176
|
+
print(f"Regenerating additional metadata for artifact with uuid {artifact['upload_placeholder']}")
|
|
194
177
|
artifact_path = uuid_path_map[artifact["upload_placeholder"]]
|
|
195
|
-
df.additional_metadata = get_source(
|
|
196
|
-
migration.result, artifact_path, skip_last=True
|
|
197
|
-
)[1]
|
|
178
|
+
df.additional_metadata = get_source(migration.result, artifact_path, skip_last=True)[1]
|
|
198
179
|
|
|
199
180
|
# If the GCS URI has changed, rename the blob
|
|
200
181
|
# makes call to bucket.rename_blob
|
|
201
182
|
new_gcs_uri = artifact["object_url"]
|
|
202
183
|
if old_gcs_uri != new_gcs_uri:
|
|
203
|
-
print(
|
|
204
|
-
f"Encountered GCS data bucket artifact URI to update: {old_gcs_uri}"
|
|
205
|
-
)
|
|
184
|
+
print(f"Encountered GCS data bucket artifact URI to update: {old_gcs_uri}")
|
|
206
185
|
renamer = PieceOfWork(
|
|
207
186
|
partial(
|
|
208
187
|
rename_gcs_blob,
|
|
@@ -220,9 +199,7 @@ def _run_metadata_migration(
|
|
|
220
199
|
gcs_tasks.schedule(renamer)
|
|
221
200
|
|
|
222
201
|
# Migrate all assay upload successes
|
|
223
|
-
successful_assay_uploads = _select_successful_assay_uploads(
|
|
224
|
-
use_upload_jobs_table, session
|
|
225
|
-
)
|
|
202
|
+
successful_assay_uploads = _select_successful_assay_uploads(use_upload_jobs_table, session)
|
|
226
203
|
for upload in successful_assay_uploads:
|
|
227
204
|
print(f"Running metadata migration for assay upload: {upload.id}")
|
|
228
205
|
if use_upload_jobs_table:
|
|
@@ -248,9 +225,7 @@ def _run_metadata_migration(
|
|
|
248
225
|
if old_target_uri in migration.file_updates:
|
|
249
226
|
new_target_uri = migration.file_updates[old_target_uri]["object_url"]
|
|
250
227
|
if old_target_uri != new_target_uri:
|
|
251
|
-
print(
|
|
252
|
-
f"Encountered GCS upload bucket artifact URI to update: {old_upload_uri}"
|
|
253
|
-
)
|
|
228
|
+
print(f"Encountered GCS upload bucket artifact URI to update: {old_upload_uri}")
|
|
254
229
|
new_upload_uri = "/".join([new_target_uri, upload_timestamp])
|
|
255
230
|
renamer = PieceOfWork(
|
|
256
231
|
partial(
|
|
@@ -325,7 +300,5 @@ def republish_artifact_uploads():
|
|
|
325
300
|
with migration_session() as (session, _):
|
|
326
301
|
files = session.query(DownloadableFiles).all()
|
|
327
302
|
for f in files:
|
|
328
|
-
print(
|
|
329
|
-
f"Publishing to 'artifact_upload' topic for downloadable file with in bucket url {f.object_url}"
|
|
330
|
-
)
|
|
303
|
+
print(f"Publishing to 'artifact_upload' topic for downloadable file with in bucket url {f.object_url}")
|
|
331
304
|
publish_artifact_upload(f.object_url)
|
cidc_api/models/models.py
CHANGED
|
@@ -96,7 +96,7 @@ from sqlalchemy import (
|
|
|
96
96
|
String,
|
|
97
97
|
Table,
|
|
98
98
|
)
|
|
99
|
-
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
|
99
|
+
from sqlalchemy.dialects.postgresql import JSONB, UUID, CITEXT
|
|
100
100
|
from sqlalchemy.engine import ResultProxy
|
|
101
101
|
from sqlalchemy.exc import IntegrityError
|
|
102
102
|
from sqlalchemy.ext.hybrid import hybrid_property
|
|
@@ -381,7 +381,7 @@ class Users(CommonColumns):
|
|
|
381
381
|
last_n = Column(String)
|
|
382
382
|
organization = Column(Enum(*ORGS, name="orgs"))
|
|
383
383
|
approval_date = Column(DateTime)
|
|
384
|
-
role = Column(Enum(*ROLES, name="
|
|
384
|
+
role = Column(Enum(*ROLES, name="roles"))
|
|
385
385
|
disabled = Column(Boolean, default=False, server_default="false")
|
|
386
386
|
|
|
387
387
|
@validates("approval_date")
|
|
@@ -606,6 +606,22 @@ class Permissions(CommonColumns):
|
|
|
606
606
|
unique=True,
|
|
607
607
|
postgresql_where=file_group_id.isnot(None),
|
|
608
608
|
),
|
|
609
|
+
Index(
|
|
610
|
+
"unique_trial_id_upload_type_is_null_perms",
|
|
611
|
+
"granted_to_user",
|
|
612
|
+
"trial_id",
|
|
613
|
+
literal_column("(upload_type IS NULL)"),
|
|
614
|
+
unique=True,
|
|
615
|
+
postgresql_where="(upload_type IS NULL)",
|
|
616
|
+
),
|
|
617
|
+
Index(
|
|
618
|
+
"unique_upload_type_trial_id_is_null_perms",
|
|
619
|
+
"granted_to_user",
|
|
620
|
+
literal_column("(trial_id IS NULL)"),
|
|
621
|
+
"upload_type",
|
|
622
|
+
unique=True,
|
|
623
|
+
postgresql_where="(trial_id IS NULL)",
|
|
624
|
+
),
|
|
609
625
|
)
|
|
610
626
|
|
|
611
627
|
# Shorthand to make code related to trial- and upload-type-level permissions
|
|
@@ -2293,7 +2309,7 @@ class DownloadableFiles(CommonColumns):
|
|
|
2293
2309
|
additional_metadata = Column(JSONB, nullable=False)
|
|
2294
2310
|
# TODO rename upload_type, because we store manifests in there too.
|
|
2295
2311
|
# NOTE: this column actually has type CITEXT.
|
|
2296
|
-
upload_type = Column(
|
|
2312
|
+
upload_type = Column(CITEXT, nullable=False)
|
|
2297
2313
|
md5_hash = Column(String, nullable=True)
|
|
2298
2314
|
crc32c_hash = Column(String, nullable=True)
|
|
2299
2315
|
trial_id = Column(String, nullable=False)
|
|
@@ -2313,7 +2329,7 @@ class DownloadableFiles(CommonColumns):
|
|
|
2313
2329
|
# used instead of data_format.
|
|
2314
2330
|
# The columns are left as optional for short term backwards compatibility.
|
|
2315
2331
|
file_name = Column(String, nullable=True)
|
|
2316
|
-
data_format = Column(
|
|
2332
|
+
data_format = Column(CITEXT, nullable=True)
|
|
2317
2333
|
|
|
2318
2334
|
file_groups = relationship(
|
|
2319
2335
|
"FileGroups",
|
|
@@ -3261,11 +3277,11 @@ class PreprocessedFiles(CommonColumns):
|
|
|
3261
3277
|
),
|
|
3262
3278
|
)
|
|
3263
3279
|
|
|
3264
|
-
file_name = Column(String)
|
|
3265
|
-
object_url = Column(String)
|
|
3280
|
+
file_name = Column(String, nullable=False)
|
|
3281
|
+
object_url = Column(String, nullable=False)
|
|
3266
3282
|
job_id = Column(Integer)
|
|
3267
|
-
file_category = Column(String)
|
|
3268
|
-
uploader_email = Column(String)
|
|
3283
|
+
file_category = Column(String, nullable=False)
|
|
3284
|
+
uploader_email = Column(String, nullable=False)
|
|
3269
3285
|
status = Column(String)
|
|
3270
3286
|
version = Column(Integer)
|
|
3271
3287
|
released_version = Column(String)
|
|
@@ -3399,6 +3415,15 @@ class PreprocessedFiles(CommonColumns):
|
|
|
3399
3415
|
|
|
3400
3416
|
return query.filter(cls.job_id.is_(None))
|
|
3401
3417
|
|
|
3418
|
+
@with_default_session
|
|
3419
|
+
def category_description(self, session: Session):
|
|
3420
|
+
category = (
|
|
3421
|
+
session.query(JobFileCategories)
|
|
3422
|
+
.filter(JobFileCategories.job_id == self.job_id, JobFileCategories.category == self.file_category)
|
|
3423
|
+
.first()
|
|
3424
|
+
)
|
|
3425
|
+
return category.description if category else None
|
|
3426
|
+
|
|
3402
3427
|
|
|
3403
3428
|
INGESTION_JOB_STATUSES = [
|
|
3404
3429
|
"DRAFT",
|
|
@@ -3433,7 +3458,7 @@ class IngestionJobs(CommonColumns):
|
|
|
3433
3458
|
),
|
|
3434
3459
|
)
|
|
3435
3460
|
|
|
3436
|
-
status = Column("status", Enum(*INGESTION_JOB_STATUSES, name="
|
|
3461
|
+
status = Column("status", Enum(*INGESTION_JOB_STATUSES, name="ingestion_job_status"), nullable=False)
|
|
3437
3462
|
trial_id = Column(String, nullable=False)
|
|
3438
3463
|
version = Column(Integer, nullable=False)
|
|
3439
3464
|
pending = Column(Boolean, nullable=False, default=False)
|
|
@@ -3522,16 +3547,18 @@ class JobFileCategories(CommonColumns):
|
|
|
3522
3547
|
["ingestion_jobs.id"],
|
|
3523
3548
|
),
|
|
3524
3549
|
Index(
|
|
3525
|
-
"idx_categories_job_id"
|
|
3550
|
+
"idx_categories_job_id",
|
|
3551
|
+
"job_id",
|
|
3526
3552
|
"category",
|
|
3527
3553
|
unique=True,
|
|
3528
3554
|
),
|
|
3529
3555
|
)
|
|
3530
3556
|
|
|
3531
3557
|
category = Column(String)
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3558
|
+
description = Column(String)
|
|
3559
|
+
job_id = Column(Integer, nullable=False)
|
|
3560
|
+
type = Column(Enum("required", "optional", name="type"), nullable=False)
|
|
3561
|
+
is_custom = Column(Boolean, nullable=False, default=False, server_default="false")
|
|
3535
3562
|
|
|
3536
3563
|
@staticmethod
|
|
3537
3564
|
@with_default_session
|
|
@@ -3539,6 +3566,7 @@ class JobFileCategories(CommonColumns):
|
|
|
3539
3566
|
category: str,
|
|
3540
3567
|
job_id: int,
|
|
3541
3568
|
type: str,
|
|
3569
|
+
description: str = None,
|
|
3542
3570
|
is_custom: bool = False,
|
|
3543
3571
|
session: Session = None,
|
|
3544
3572
|
):
|
|
@@ -3546,6 +3574,7 @@ class JobFileCategories(CommonColumns):
|
|
|
3546
3574
|
category=category,
|
|
3547
3575
|
job_id=job_id,
|
|
3548
3576
|
type=type,
|
|
3577
|
+
description=description,
|
|
3549
3578
|
is_custom=is_custom,
|
|
3550
3579
|
)
|
|
3551
3580
|
new_category.insert(session=session)
|
|
@@ -3557,6 +3586,11 @@ class JobFileCategories(CommonColumns):
|
|
|
3557
3586
|
categories = session.query(cls).filter(cls.job_id == job_id, cls.type == type).all()
|
|
3558
3587
|
return [c.category for c in categories]
|
|
3559
3588
|
|
|
3589
|
+
@classmethod
|
|
3590
|
+
@with_default_session
|
|
3591
|
+
def full_categories_for_job(cls, job_id: int, session: Session = None):
|
|
3592
|
+
return session.query(cls).filter_by(job_id=job_id).all()
|
|
3593
|
+
|
|
3560
3594
|
|
|
3561
3595
|
class CategoryDataElements(CommonColumns):
|
|
3562
3596
|
__tablename__ = "category_data_elements"
|
|
@@ -3567,7 +3601,8 @@ class CategoryDataElements(CommonColumns):
|
|
|
3567
3601
|
ondelete="CASCADE",
|
|
3568
3602
|
),
|
|
3569
3603
|
Index(
|
|
3570
|
-
"idx_elements_category_id"
|
|
3604
|
+
"idx_elements_category_id",
|
|
3605
|
+
"category_id",
|
|
3571
3606
|
"name",
|
|
3572
3607
|
unique=True,
|
|
3573
3608
|
),
|
|
@@ -3579,6 +3614,22 @@ class CategoryDataElements(CommonColumns):
|
|
|
3579
3614
|
element_type = Column(String, nullable=False)
|
|
3580
3615
|
cardinality = Column(String, nullable=True)
|
|
3581
3616
|
|
|
3617
|
+
@classmethod
|
|
3618
|
+
@with_default_session
|
|
3619
|
+
def elements_for_category(cls, category_id: int, session: Session = None):
|
|
3620
|
+
return session.query(cls).filter_by(category_id=category_id).all()
|
|
3621
|
+
|
|
3622
|
+
@classmethod
|
|
3623
|
+
@with_default_session
|
|
3624
|
+
def elements_by_category_for_job(cls, job_id: int, session: Session = None):
|
|
3625
|
+
"""Fetch all CategoryDataElements for a job, along with the category name."""
|
|
3626
|
+
return (
|
|
3627
|
+
session.query(JobFileCategories.category, cls)
|
|
3628
|
+
.join(cls, cls.category_id == JobFileCategories.id)
|
|
3629
|
+
.filter(JobFileCategories.job_id == job_id)
|
|
3630
|
+
.all()
|
|
3631
|
+
)
|
|
3632
|
+
|
|
3582
3633
|
|
|
3583
3634
|
class FileValidationErrors(CommonColumns):
|
|
3584
3635
|
__tablename__ = "file_validation_errors"
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from .additional_treatment import AdditionalTreatment
|
|
2
|
+
from .administrative_person import AdministrativePerson
|
|
3
|
+
from .administrative_role_assignment import AdministrativeRoleAssignment
|
|
4
|
+
from .adverse_event import AdverseEvent
|
|
5
|
+
from .arm import Arm
|
|
6
|
+
from .baseline_clinical_assessment import BaselineClinicalAssessment
|
|
7
|
+
from .cohort import Cohort
|
|
8
|
+
from .comorbidity import Comorbidity
|
|
9
|
+
from .consent_group import ConsentGroup
|
|
10
|
+
from .contact import Contact
|
|
11
|
+
from .demographic import Demographic
|
|
12
|
+
from .disease import Disease
|
|
13
|
+
from .exposure import Exposure
|
|
14
|
+
from .file import File
|
|
15
|
+
from .gvhd_diagnosis_acute import GVHDDiagnosisAcute
|
|
16
|
+
from .gvhd_diagnosis_chronic import GVHDDiagnosisChronic
|
|
17
|
+
from .gvhd_organ_acute import GVHDOrganAcute
|
|
18
|
+
from .gvhd_organ_chronic import GVHDOrganChronic
|
|
19
|
+
from .institution import Institution
|
|
20
|
+
from .medical_history import MedicalHistory
|
|
21
|
+
from .other_clinical_endpoint import OtherClinicalEndpoint
|
|
22
|
+
from .other_malignancy import OtherMalignancy
|
|
23
|
+
from .participant import Participant
|
|
24
|
+
from .prior_treatment import PriorTreatment
|
|
25
|
+
from .publication import Publication
|
|
26
|
+
from .radiotherapy_dose import RadiotherapyDose
|
|
27
|
+
from .response import Response
|
|
28
|
+
from .response_by_system import ResponseBySystem
|
|
29
|
+
from .shipment import Shipment
|
|
30
|
+
from .shipment_specimen import ShipmentSpecimen
|
|
31
|
+
from .specimen import Specimen
|
|
32
|
+
from .stem_cell_transplant import StemCellTransplant
|
|
33
|
+
from .surgery import Surgery
|
|
34
|
+
from .therapy_agent_dose import TherapyAgentDose
|
|
35
|
+
from .treatment import Treatment
|
|
36
|
+
from .trial import Trial
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
"AdditionalTreatment",
|
|
41
|
+
"AdministrativePerson",
|
|
42
|
+
"AdministrativeRoleAssignment",
|
|
43
|
+
"AdverseEvent",
|
|
44
|
+
"Arm",
|
|
45
|
+
"BaselineClinicalAssessment",
|
|
46
|
+
"Cohort",
|
|
47
|
+
"Comorbidity",
|
|
48
|
+
"ConsentGroup",
|
|
49
|
+
"Contact",
|
|
50
|
+
"Demographic",
|
|
51
|
+
"Disease",
|
|
52
|
+
"Exposure",
|
|
53
|
+
"File",
|
|
54
|
+
"GVHDDiagnosisAcute",
|
|
55
|
+
"GVHDOrganAcute",
|
|
56
|
+
"GVHDDiagnosisChronic",
|
|
57
|
+
"GVHDOrganChronic",
|
|
58
|
+
"Institution",
|
|
59
|
+
"MedicalHistory",
|
|
60
|
+
"OtherClinicalEndpoint",
|
|
61
|
+
"OtherMalignancy",
|
|
62
|
+
"Participant",
|
|
63
|
+
"PriorTreatment",
|
|
64
|
+
"Publication",
|
|
65
|
+
"RadiotherapyDose",
|
|
66
|
+
"Response",
|
|
67
|
+
"ResponseBySystem",
|
|
68
|
+
"Shipment",
|
|
69
|
+
"ShipmentSpecimen",
|
|
70
|
+
"Specimen",
|
|
71
|
+
"StemCellTransplant",
|
|
72
|
+
"Surgery",
|
|
73
|
+
"TherapyAgentDose",
|
|
74
|
+
"Treatment",
|
|
75
|
+
"Trial",
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
all_models = [globals()[cls_name] for cls_name in __all__]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from pydantic import NonNegativeInt
|
|
2
|
+
from .base import Base
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class AdditionalTreatment(Base):
|
|
6
|
+
__data_category__ = "additional_treatment"
|
|
7
|
+
__cardinality__ = "many"
|
|
8
|
+
|
|
9
|
+
# The unique internal identifier for the AdditionalTreatment record
|
|
10
|
+
additional_treatment_id: int | None = None
|
|
11
|
+
|
|
12
|
+
# The unique internal identifier for the associated Participant record
|
|
13
|
+
participant_id: int | None = None
|
|
14
|
+
|
|
15
|
+
# Number of days from the enrollment date to the first recorded administration or occurrence of the treatment modality.
|
|
16
|
+
days_to_start: NonNegativeInt | None = None
|
|
17
|
+
|
|
18
|
+
# Number of days from the enrollment date to the last recorded administration or occurrence of the treatment modality.
|
|
19
|
+
days_to_end: NonNegativeInt | None = None
|
|
20
|
+
|
|
21
|
+
# Description of the prior treatment such as its full generic name if it is a type of therapy agent, radiotherapy procedure
|
|
22
|
+
# name and location, or surgical procedure name and location.
|
|
23
|
+
description: str
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from .base import Base
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AdministrativePerson(Base):
|
|
5
|
+
|
|
6
|
+
# The unique internal identifier for the administrative person
|
|
7
|
+
administrative_person_id: int | None = None
|
|
8
|
+
|
|
9
|
+
# The internal identifier for the Institution the administrative person belongs to
|
|
10
|
+
institution_id: int | None = None
|
|
11
|
+
|
|
12
|
+
# The word or group of words indicating a person's first (personal or given) name, e.g. "John"
|
|
13
|
+
# CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=2179589%20and%20ver_nr=2
|
|
14
|
+
first_name: str
|
|
15
|
+
|
|
16
|
+
# The word or group of worlds indicating a person's middle name, e.g. "Alan"
|
|
17
|
+
# CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=2179590%20and%20ver_nr=2
|
|
18
|
+
middle_name: str | None = None
|
|
19
|
+
|
|
20
|
+
# The means of identifying an individual by using a word or group of words indicating a person's last (family) name, e.g. "Smith"
|
|
21
|
+
# CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=2179591%20and%20ver_nr=2
|
|
22
|
+
last_name: str
|
|
23
|
+
|
|
24
|
+
# The string of characters that represents the electronic mail address of a person.
|
|
25
|
+
# CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=2517550%20and%20ver_nr=1
|
|
26
|
+
email: str | None = None
|
|
27
|
+
|
|
28
|
+
# The string of digits that represent a telephone number that can be used to contact the person.
|
|
29
|
+
# CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=2179593%20and%20ver_nr=3
|
|
30
|
+
phone_number: str | None = None
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from .base import Base
|
|
2
|
+
from cidc_api.models.types import AdministrativeRole
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class AdministrativeRoleAssignment(Base):
|
|
6
|
+
# The unique identifier for the associated trial
|
|
7
|
+
trial_id: int | None = None
|
|
8
|
+
|
|
9
|
+
# The version number of the trial dataset
|
|
10
|
+
version: str | None = None
|
|
11
|
+
|
|
12
|
+
# The unique identifier for the associated administrative person
|
|
13
|
+
administrative_person_id: int
|
|
14
|
+
|
|
15
|
+
# The role the administrative_person is performing for the associated trial
|
|
16
|
+
administrative_role: AdministrativeRole
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from typing import Self
|
|
2
|
+
|
|
3
|
+
from pydantic import NonNegativeInt, model_validator
|
|
4
|
+
|
|
5
|
+
from .base import Base
|
|
6
|
+
from cidc_api.reference.ctcae import is_ctcae_other_term
|
|
7
|
+
from cidc_api.models.types import (
|
|
8
|
+
CTCAEEventTerm,
|
|
9
|
+
CTCAEEventCode,
|
|
10
|
+
SeverityGradeSystem,
|
|
11
|
+
SeverityGradeSystemVersion,
|
|
12
|
+
SeverityGrade,
|
|
13
|
+
SystemOrganClass,
|
|
14
|
+
AttributionCause,
|
|
15
|
+
AttributionLikelihood,
|
|
16
|
+
YNU,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AdverseEvent(Base):
|
|
21
|
+
__data_category__ = "adverse_event"
|
|
22
|
+
__cardinality__ = "many"
|
|
23
|
+
|
|
24
|
+
# The unique internal identifier of the adverse event
|
|
25
|
+
adverse_event_id: int | None = None
|
|
26
|
+
|
|
27
|
+
# The unique internal identifier of the associated participant
|
|
28
|
+
participant_id: int | None = None
|
|
29
|
+
|
|
30
|
+
# The unique internal identifier of the attributed treatment, if any
|
|
31
|
+
treatment_id: int | None = None
|
|
32
|
+
|
|
33
|
+
# Text that represents the Common Terminology Criteria for Adverse Events low level term name for an adverse event.
|
|
34
|
+
event_term: CTCAEEventTerm | None = None
|
|
35
|
+
|
|
36
|
+
# A MedDRA code mapped to a CTCAE low level name for an adverse event.
|
|
37
|
+
event_code: CTCAEEventCode | None = None
|
|
38
|
+
|
|
39
|
+
# System used to define and report adverse event severity grade.
|
|
40
|
+
severity_grade_system: SeverityGradeSystem
|
|
41
|
+
|
|
42
|
+
# The version of the adverse event grading system.
|
|
43
|
+
severity_grade_system_version: SeverityGradeSystemVersion
|
|
44
|
+
|
|
45
|
+
# Numerical grade indicating the severity of an adverse event.
|
|
46
|
+
severity_grade: SeverityGrade
|
|
47
|
+
|
|
48
|
+
# A brief description that sufficiently details the event.
|
|
49
|
+
event_other_specify: str | None = None
|
|
50
|
+
|
|
51
|
+
# The highest level of the MedDRA hierarchy, distinguished by anatomical or physiological system, etiology (disease origin) or purpose.
|
|
52
|
+
system_organ_class: SystemOrganClass | None = None
|
|
53
|
+
|
|
54
|
+
# Indicator to identify whether a participant exited the study prematurely due to the adverse event being described.
|
|
55
|
+
discontinuation_due_to_event: bool
|
|
56
|
+
|
|
57
|
+
# Days from enrollment date to date of onset of the adverse event.
|
|
58
|
+
days_to_onset_of_event: NonNegativeInt
|
|
59
|
+
|
|
60
|
+
# Days from enrollment date to date of resolution of the adverse event.
|
|
61
|
+
days_to_resolution_of_event: NonNegativeInt | None = None
|
|
62
|
+
|
|
63
|
+
# Indicates whether the adverse event was a serious adverse event (SAE).
|
|
64
|
+
serious_adverse_event: YNU
|
|
65
|
+
|
|
66
|
+
# Indicates whether the adverse event was a dose-limiting toxicity (DLT).
|
|
67
|
+
dose_limiting_toxicity: YNU
|
|
68
|
+
|
|
69
|
+
# Indicates if the adverse was attributable to the protocol as a whole or to an individual treatment.
|
|
70
|
+
attribution_cause: AttributionCause
|
|
71
|
+
|
|
72
|
+
# The code that indicates whether the adverse event is related to the treatment/intervention.
|
|
73
|
+
attribution_likelihood: AttributionLikelihood
|
|
74
|
+
|
|
75
|
+
# The individual therapy (therapy agent, radiotherapy, surgery, stem cell transplant) in the treatment that is attributed to the adverse event.
|
|
76
|
+
individual_therapy: str | None = None
|
|
77
|
+
|
|
78
|
+
@model_validator(mode="after")
|
|
79
|
+
def validate_term_and_code_cr(self) -> Self:
|
|
80
|
+
if not self.event_term and not self.event_code:
|
|
81
|
+
raise ValueError("Please provide event_term or event_code or both")
|
|
82
|
+
return self
|
|
83
|
+
|
|
84
|
+
@model_validator(mode="after")
|
|
85
|
+
def validate_event_other_specify_cr(self) -> Self:
|
|
86
|
+
if (
|
|
87
|
+
self.severity_grade_system == "CTCAE"
|
|
88
|
+
and is_ctcae_other_term(self.event_term)
|
|
89
|
+
and not self.event_other_specify
|
|
90
|
+
):
|
|
91
|
+
raise ValueError(
|
|
92
|
+
'If severity_grade_system is "CTCAE" and the event_code or event_term are of type "Other, specify", please provide event_other_specify'
|
|
93
|
+
)
|
|
94
|
+
return self
|
|
95
|
+
|
|
96
|
+
@model_validator(mode="after")
|
|
97
|
+
def validate_system_organ_class_cr(self) -> Self:
|
|
98
|
+
if self.event_other_specify and not self.system_organ_class:
|
|
99
|
+
raise ValueError("If event_other_specify is provided, please provide system_organ_class.")
|
|
100
|
+
return self
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from .base import Base
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Arm(Base):
|
|
5
|
+
# The unique internal identifier for the arm
|
|
6
|
+
arm_id: int | None = None
|
|
7
|
+
|
|
8
|
+
# The unique identifier for the associated trial
|
|
9
|
+
trial_id: int | None = None
|
|
10
|
+
|
|
11
|
+
# The version number of the trial dataset
|
|
12
|
+
version: str | None = None
|
|
13
|
+
|
|
14
|
+
# The name of the arm, e.g. "Arm A1"
|
|
15
|
+
# CDE: https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=2001626%20and%20ver_nr=3
|
|
16
|
+
name: str
|