nci-cidc-api-modules 1.2.29__py3-none-any.whl → 1.2.45__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 (148) hide show
  1. cidc_api/__init__.py +1 -0
  2. cidc_api/config/db.py +21 -1
  3. cidc_api/config/settings.py +1 -0
  4. cidc_api/models/__init__.py +0 -2
  5. cidc_api/models/data.py +17 -4
  6. cidc_api/models/db/stage1/__init__.py +56 -0
  7. cidc_api/models/db/stage1/additional_treatment_orm.py +22 -0
  8. cidc_api/models/db/stage1/adverse_event_orm.py +46 -0
  9. cidc_api/models/db/stage1/base_orm.py +7 -0
  10. cidc_api/models/db/stage1/baseline_clinical_assessment_orm.py +22 -0
  11. cidc_api/models/db/stage1/comorbidity_orm.py +23 -0
  12. cidc_api/models/db/stage1/consent_group_orm.py +32 -0
  13. cidc_api/models/db/stage1/demographic_orm.py +47 -0
  14. cidc_api/models/db/stage1/disease_orm.py +52 -0
  15. cidc_api/models/db/stage1/exposure_orm.py +22 -0
  16. cidc_api/models/db/stage1/gvhd_diagnosis_acute_orm.py +34 -0
  17. cidc_api/models/db/stage1/gvhd_diagnosis_chronic_orm.py +36 -0
  18. cidc_api/models/db/stage1/gvhd_organ_acute_orm.py +21 -0
  19. cidc_api/models/db/stage1/gvhd_organ_chronic_orm.py +21 -0
  20. cidc_api/models/db/stage1/medical_history_orm.py +30 -0
  21. cidc_api/models/db/stage1/other_malignancy_orm.py +29 -0
  22. cidc_api/models/db/stage1/participant_orm.py +77 -0
  23. cidc_api/models/db/stage1/prior_treatment_orm.py +29 -0
  24. cidc_api/models/db/stage1/radiotherapy_dose_orm.py +39 -0
  25. cidc_api/models/db/stage1/response_by_system_orm.py +30 -0
  26. cidc_api/models/db/stage1/response_orm.py +28 -0
  27. cidc_api/models/db/stage1/specimen_orm.py +46 -0
  28. cidc_api/models/db/stage1/stem_cell_transplant_orm.py +25 -0
  29. cidc_api/models/db/stage1/surgery_orm.py +27 -0
  30. cidc_api/models/db/stage1/therapy_agent_dose_orm.py +31 -0
  31. cidc_api/models/db/stage1/treatment_orm.py +38 -0
  32. cidc_api/models/db/stage1/trial_orm.py +35 -0
  33. cidc_api/models/db/stage2/additional_treatment_orm.py +8 -8
  34. cidc_api/models/db/stage2/administrative_person_orm.py +4 -4
  35. cidc_api/models/db/stage2/administrative_role_assignment_orm.py +4 -4
  36. cidc_api/models/db/stage2/adverse_event_orm.py +12 -13
  37. cidc_api/models/db/stage2/arm_orm.py +3 -3
  38. cidc_api/models/db/stage2/base_orm.py +7 -0
  39. cidc_api/models/db/stage2/baseline_clinical_assessment_orm.py +6 -7
  40. cidc_api/models/db/stage2/cohort_orm.py +3 -3
  41. cidc_api/models/db/stage2/comorbidity_orm.py +7 -8
  42. cidc_api/models/db/stage2/consent_group_orm.py +5 -4
  43. cidc_api/models/db/stage2/contact_orm.py +16 -20
  44. cidc_api/models/db/stage2/demographic_orm.py +11 -8
  45. cidc_api/models/db/stage2/disease_orm.py +13 -14
  46. cidc_api/models/db/stage2/exposure_orm.py +5 -4
  47. cidc_api/models/db/stage2/file_orm.py +6 -9
  48. cidc_api/models/db/stage2/gvhd_diagnosis_acute_orm.py +5 -4
  49. cidc_api/models/db/stage2/gvhd_diagnosis_chronic_orm.py +5 -6
  50. cidc_api/models/db/stage2/gvhd_organ_acute_orm.py +4 -3
  51. cidc_api/models/db/stage2/gvhd_organ_chronic_orm.py +4 -3
  52. cidc_api/models/db/stage2/institution_orm.py +7 -7
  53. cidc_api/models/db/stage2/medical_history_orm.py +10 -9
  54. cidc_api/models/db/stage2/other_clinical_endpoint_orm.py +8 -12
  55. cidc_api/models/db/stage2/other_malignancy_orm.py +10 -11
  56. cidc_api/models/db/stage2/participant_orm.py +28 -28
  57. cidc_api/models/db/stage2/prior_treatment_orm.py +15 -14
  58. cidc_api/models/db/stage2/publication_orm.py +9 -11
  59. cidc_api/models/db/stage2/radiotherapy_dose_orm.py +9 -9
  60. cidc_api/models/db/stage2/response_by_system_orm.py +5 -3
  61. cidc_api/models/db/stage2/response_orm.py +6 -5
  62. cidc_api/models/db/stage2/shipment_orm.py +17 -17
  63. cidc_api/models/db/stage2/shipment_specimen_orm.py +4 -4
  64. cidc_api/models/db/stage2/specimen_orm.py +7 -6
  65. cidc_api/models/db/stage2/stem_cell_transplant_orm.py +7 -7
  66. cidc_api/models/db/stage2/surgery_orm.py +7 -7
  67. cidc_api/models/db/stage2/therapy_agent_dose_orm.py +8 -8
  68. cidc_api/models/db/stage2/treatment_orm.py +16 -15
  69. cidc_api/models/db/stage2/trial_orm.py +34 -33
  70. cidc_api/models/files/facets.py +4 -0
  71. cidc_api/models/models.py +154 -9
  72. cidc_api/models/pydantic/{stage2/base.py → base.py} +19 -1
  73. cidc_api/models/pydantic/stage1/__init__.py +56 -0
  74. cidc_api/models/pydantic/stage1/additional_treatment.py +23 -0
  75. cidc_api/models/pydantic/stage1/adverse_event.py +100 -0
  76. cidc_api/models/pydantic/stage1/baseline_clinical_assessment.py +23 -0
  77. cidc_api/models/pydantic/stage1/comorbidity.py +36 -0
  78. cidc_api/models/pydantic/stage1/consent_group.py +30 -0
  79. cidc_api/models/pydantic/stage1/demographic.py +123 -0
  80. cidc_api/models/pydantic/stage1/disease.py +158 -0
  81. cidc_api/models/pydantic/stage1/exposure.py +32 -0
  82. cidc_api/models/pydantic/stage1/gvhd_diagnosis_acute.py +33 -0
  83. cidc_api/models/pydantic/stage1/gvhd_diagnosis_chronic.py +32 -0
  84. cidc_api/models/pydantic/stage1/gvhd_organ_acute.py +22 -0
  85. cidc_api/models/pydantic/stage1/gvhd_organ_chronic.py +23 -0
  86. cidc_api/models/pydantic/stage1/medical_history.py +36 -0
  87. cidc_api/models/pydantic/stage1/other_malignancy.py +49 -0
  88. cidc_api/models/pydantic/stage1/participant.py +51 -0
  89. cidc_api/models/pydantic/stage1/prior_treatment.py +45 -0
  90. cidc_api/models/pydantic/stage1/radiotherapy_dose.py +79 -0
  91. cidc_api/models/pydantic/stage1/response.py +65 -0
  92. cidc_api/models/pydantic/stage1/response_by_system.py +112 -0
  93. cidc_api/models/pydantic/stage1/specimen.py +31 -0
  94. cidc_api/models/pydantic/stage1/stem_cell_transplant.py +35 -0
  95. cidc_api/models/pydantic/stage1/surgery.py +49 -0
  96. cidc_api/models/pydantic/stage1/therapy_agent_dose.py +67 -0
  97. cidc_api/models/pydantic/stage1/treatment.py +50 -0
  98. cidc_api/models/pydantic/stage1/trial.py +45 -0
  99. cidc_api/models/pydantic/stage2/additional_treatment.py +5 -5
  100. cidc_api/models/pydantic/stage2/administrative_person.py +1 -1
  101. cidc_api/models/pydantic/stage2/administrative_role_assignment.py +2 -2
  102. cidc_api/models/pydantic/stage2/adverse_event.py +2 -2
  103. cidc_api/models/pydantic/stage2/arm.py +2 -2
  104. cidc_api/models/pydantic/stage2/baseline_clinical_assessment.py +2 -2
  105. cidc_api/models/pydantic/stage2/cohort.py +1 -1
  106. cidc_api/models/pydantic/stage2/comorbidity.py +1 -1
  107. cidc_api/models/pydantic/stage2/consent_group.py +2 -2
  108. cidc_api/models/pydantic/stage2/contact.py +1 -1
  109. cidc_api/models/pydantic/stage2/demographic.py +27 -18
  110. cidc_api/models/pydantic/stage2/disease.py +33 -19
  111. cidc_api/models/pydantic/stage2/exposure.py +3 -3
  112. cidc_api/models/pydantic/stage2/file.py +2 -2
  113. cidc_api/models/pydantic/stage2/gvhd_diagnosis_acute.py +2 -2
  114. cidc_api/models/pydantic/stage2/gvhd_diagnosis_chronic.py +2 -2
  115. cidc_api/models/pydantic/stage2/gvhd_organ_acute.py +1 -1
  116. cidc_api/models/pydantic/stage2/gvhd_organ_chronic.py +1 -1
  117. cidc_api/models/pydantic/stage2/institution.py +1 -1
  118. cidc_api/models/pydantic/stage2/medical_history.py +2 -2
  119. cidc_api/models/pydantic/stage2/other_clinical_endpoint.py +1 -1
  120. cidc_api/models/pydantic/stage2/other_malignancy.py +12 -8
  121. cidc_api/models/pydantic/stage2/participant.py +10 -6
  122. cidc_api/models/pydantic/stage2/prior_treatment.py +14 -23
  123. cidc_api/models/pydantic/stage2/publication.py +2 -2
  124. cidc_api/models/pydantic/stage2/radiotherapy_dose.py +2 -2
  125. cidc_api/models/pydantic/stage2/response.py +5 -11
  126. cidc_api/models/pydantic/stage2/response_by_system.py +10 -7
  127. cidc_api/models/pydantic/stage2/shipment.py +2 -2
  128. cidc_api/models/pydantic/stage2/shipment_specimen.py +1 -1
  129. cidc_api/models/pydantic/stage2/specimen.py +8 -5
  130. cidc_api/models/pydantic/stage2/stem_cell_transplant.py +2 -2
  131. cidc_api/models/pydantic/stage2/surgery.py +1 -1
  132. cidc_api/models/pydantic/stage2/therapy_agent_dose.py +1 -1
  133. cidc_api/models/pydantic/stage2/treatment.py +2 -2
  134. cidc_api/models/pydantic/stage2/trial.py +19 -15
  135. cidc_api/models/types.py +45 -42
  136. cidc_api/shared/assay_handling.py +68 -0
  137. cidc_api/shared/auth.py +5 -5
  138. cidc_api/shared/file_handling.py +16 -4
  139. cidc_api/shared/gcloud_client.py +78 -16
  140. cidc_api/shared/utils.py +18 -9
  141. cidc_api/telemetry.py +101 -0
  142. {nci_cidc_api_modules-1.2.29.dist-info → nci_cidc_api_modules-1.2.45.dist-info}/METADATA +25 -14
  143. nci_cidc_api_modules-1.2.45.dist-info/RECORD +165 -0
  144. cidc_api/models/db/base_orm.py +0 -25
  145. nci_cidc_api_modules-1.2.29.dist-info/RECORD +0 -109
  146. {nci_cidc_api_modules-1.2.29.dist-info → nci_cidc_api_modules-1.2.45.dist-info}/WHEEL +0 -0
  147. {nci_cidc_api_modules-1.2.29.dist-info → nci_cidc_api_modules-1.2.45.dist-info}/licenses/LICENSE +0 -0
  148. {nci_cidc_api_modules-1.2.29.dist-info → nci_cidc_api_modules-1.2.45.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,15 @@
1
1
  """Utilities for interacting with the Google Cloud Platform APIs."""
2
2
 
3
- # pylint: disable=logging-fstring-interpolation,too-many-lines
3
+ # pylint: disable=logging-fstring-interpolation,too-many-lines, broad-exception-raised
4
4
 
5
+ import asyncio
5
6
  import base64
6
7
  import datetime
7
8
  import hashlib
8
9
  import io
9
10
  import json
10
11
  import os
12
+ import re
11
13
  import warnings
12
14
  from collections import namedtuple
13
15
  from concurrent.futures import Future
@@ -25,6 +27,8 @@ from typing import (
25
27
  )
26
28
 
27
29
  import googleapiclient.discovery
30
+ from gcloud.aio.storage import Storage
31
+ from pandas.core.frame import DataFrame
28
32
  import pandas as pd
29
33
  import requests
30
34
  from cidc_schemas.prism.constants import ASSAY_TO_FILEPATH
@@ -56,6 +60,7 @@ from ..config.settings import (
56
60
  GOOGLE_GRANT_DOWNLOAD_PERMISSIONS_TOPIC,
57
61
  GOOGLE_HL_CLINICAL_VALIDATION_TOPIC,
58
62
  GOOGLE_DL_CLINICAL_VALIDATION_TOPIC,
63
+ GOOGLE_ASSAY_METADATA_VALIDATION_TOPIC,
59
64
  TESTING,
60
65
  ENV,
61
66
  IS_EMAIL_ON,
@@ -361,15 +366,34 @@ def get_intake_bucket_name(user_email: str) -> str:
361
366
  return bucket_name
362
367
 
363
368
 
364
- def create_intake_bucket(user_email: str) -> storage.Bucket:
369
+ def get_trial_intake_bucket_name(trial_id: str) -> str:
365
370
  """
366
- Create a new data intake bucket for this user, or get the existing one.
371
+ Return a sanitized GCS bucket name for a given trial_id.
372
+
373
+ Produces: <GOOGLE_INTAKE_BUCKET>-<sanitized_trial_id>
374
+ where the trial_id segment is lowercased and restricted to [a-z0-9-].
375
+ """
376
+ # Replace non-allowed bucket chars with "-"
377
+ sanitized_id = re.sub(r"[^a-z0-9-]", "-", trial_id.lower())
378
+ # Collapse repeated "-" and trim from both ends
379
+ sanitized_id = re.sub(r"-+", "-", sanitized_id).strip("-")
380
+
381
+ return f"{GOOGLE_INTAKE_BUCKET}-{sanitized_id}"
382
+
383
+
384
+ def create_intake_bucket(user_email: str, trial_id: str = None) -> storage.Bucket:
385
+ """
386
+ Create (or retrieve) the appropriate data intake bucket.
387
+ If a trial_id is provided, a trial-specific bucket is used;
388
+ otherwise a user-specific intake bucket is used.
389
+
367
390
  Grant the user GCS object admin permissions on the bucket, or refresh those
368
391
  permissions if they've already been granted.
369
392
  Created with uniform bucket-level IAM access, so expiring permission.
370
393
  """
371
394
  storage_client = _get_storage_client()
372
- bucket_name = get_intake_bucket_name(user_email)
395
+ # Get trial-specific bucket name if trial_id is given, otherwise a user-specific bucket name.
396
+ bucket_name = get_trial_intake_bucket_name(trial_id) if trial_id else get_intake_bucket_name(user_email)
373
397
  bucket = storage_client.bucket(bucket_name)
374
398
 
375
399
  if not bucket.exists():
@@ -423,25 +447,50 @@ def upload_xlsx_to_intake_bucket(user_email: str, trial_id: str, upload_type: st
423
447
  return f"https://console.cloud.google.com/storage/browser/_details/{bucket_name}/{blob_name}"
424
448
 
425
449
 
426
- def gcs_xlsx_or_csv_file_to_pandas_dataframe(bucket_name: str, blob_name: str):
427
- """Reads an XLSX or CSV file from Google Cloud Storage into a Pandas DataFrame."""
428
- temp_file = get_file_bytes_from_gcs(bucket_name, blob_name)
429
-
430
- # TODO: specify sheet in xlsx file and/or accept tsv and xls files
431
- if blob_name[-3:] == "csv":
432
- return strip_whitespaces(pd.read_csv(temp_file))
433
- elif blob_name[-4:] == "xlsx":
434
- return strip_whitespaces(pd.read_excel(temp_file))
450
+ def prepare_dataframe(extension, bytes) -> DataFrame:
451
+ if extension == "csv":
452
+ return strip_whitespaces(pd.read_csv(bytes, dtype=str, keep_default_na=False))
453
+ elif extension == "xlsx":
454
+ return strip_whitespaces(pd.read_excel(bytes, dtype=str, keep_default_na=False))
435
455
  else:
436
456
  raise Exception("Can only read csv or xlsx files")
437
457
 
438
458
 
459
+ def gcs_xlsx_or_csv_file_to_pandas_dataframe(bucket_name: str, blob_name: str) -> DataFrame:
460
+ """Reads an XLSX or CSV file from Google Cloud Storage into a Pandas DataFrame."""
461
+ contents = get_file_bytes_from_gcs(bucket_name, blob_name)
462
+ extension = blob_name.split(".")[-1]
463
+ return prepare_dataframe(extension, contents)
464
+
465
+
439
466
  def get_file_bytes_from_gcs(bucket_name: str, blob_name: str) -> io.BytesIO:
440
467
  """Reads a file from Google Cloud Storage and returns it as BytesIO."""
441
468
  sheet_data = storage.Client().bucket(bucket_name).blob(blob_name).download_as_bytes()
442
469
  return io.BytesIO(sheet_data)
443
470
 
444
471
 
472
+ async def async_gcs_files_to_pandas_dataframes(bucket_name: str, blob_names: List[str]) -> List[DataFrame]:
473
+ """Async reads a XLSX or CSV files from Google Cloud Storage into a list of Pandas DataFrames."""
474
+
475
+ all_contents = await asyncio.gather(
476
+ *[async_get_file_bytes_from_gcs(bucket_name, blob_name) for blob_name in blob_names]
477
+ )
478
+ dataframes = []
479
+
480
+ for blob_name, contents in zip(blob_names, all_contents):
481
+ extension = blob_name.split(".")[-1]
482
+ dataframes.append(prepare_dataframe(extension, contents))
483
+ return dataframes
484
+
485
+
486
+ async def async_get_file_bytes_from_gcs(bucket_name: str, blob_name: str) -> io.BytesIO:
487
+ """Async reads a file from Google Cloud Storage and returns it as BytesIO."""
488
+
489
+ async with Storage() as client:
490
+ sheet_data = await client.download(bucket_name, blob_name)
491
+ return io.BytesIO(sheet_data)
492
+
493
+
445
494
  def _execute_multiblob_acl_change(
446
495
  user_email_list: List[str],
447
496
  blob_list: List[storage.Blob],
@@ -614,6 +663,7 @@ def _build_trial_upload_prefixes(
614
663
  trial_set: Set[str] = set()
615
664
  upload_set: Set[str] = set()
616
665
  if not trial_id:
666
+ # import is here becasue of circular import
617
667
  from ..models.models import TrialMetadata
618
668
 
619
669
  trial_set = {str(t.trial_id) for t in session.query(TrialMetadata).add_columns(TrialMetadata.trial_id)}
@@ -886,6 +936,7 @@ def get_signed_url(
886
936
  bucket_name: str = GOOGLE_ACL_DATA_BUCKET,
887
937
  method: str = "GET",
888
938
  expiry_mins: int = 30,
939
+ use_short_filename: bool = False,
889
940
  ) -> str:
890
941
  """
891
942
  Generate a signed URL for `object_name` to give a client temporary access.
@@ -900,7 +951,11 @@ def get_signed_url(
900
951
 
901
952
  # Generate the signed URL, allowing a client to use `method` for `expiry_mins` minutes
902
953
  expiration = datetime.timedelta(minutes=expiry_mins)
903
- full_filename = object_name.replace("/", "_").replace('"', "_").replace(" ", "_")
954
+ if use_short_filename:
955
+ filename = os.path.basename(object_name)
956
+ else:
957
+ # full filename with path included
958
+ filename = object_name.replace("/", "_").replace('"', "_").replace(" ", "_")
904
959
  other_kwargs = {}
905
960
  if os.environ.get("DEV_GOOGLE_STORAGE", None):
906
961
  other_kwargs["api_access_endpoint"] = (os.environ.get("DEV_GOOGLE_STORAGE") or "") + (
@@ -910,7 +965,7 @@ def get_signed_url(
910
965
  version="v2",
911
966
  expiration=expiration,
912
967
  method=method,
913
- response_disposition=f'attachment; filename="{full_filename}"',
968
+ response_disposition=f'attachment; filename="{filename}"',
914
969
  **other_kwargs,
915
970
  )
916
971
  logger.info(f"generated signed URL for {object_name}: {url}")
@@ -920,7 +975,8 @@ def get_signed_url(
920
975
 
921
976
  def _encode_and_publish(content: str, topic: str) -> Future:
922
977
  """Convert `content` to bytes and publish it to `topic`."""
923
- pubsub_publisher = pubsub.PublisherClient()
978
+ publisher_options = pubsub.types.PublisherOptions(enable_open_telemetry_tracing=ENV == "dev-int")
979
+ pubsub_publisher = pubsub.PublisherClient(publisher_options=publisher_options)
924
980
  topic = pubsub_publisher.topic_path(GOOGLE_CLOUD_PROJECT, topic)
925
981
  data = bytes(content, "utf-8")
926
982
 
@@ -994,6 +1050,12 @@ def publish_detailed_validation(job_id: int) -> None:
994
1050
  _report = _encode_and_publish(str(job_id), GOOGLE_DL_CLINICAL_VALIDATION_TOPIC)
995
1051
 
996
1052
 
1053
+ def publish_assay_metadata_validation(job_id: int) -> None:
1054
+ """Publish to the assay_metadata_validation topic that a job's assay metadata file is ready to be validated."""
1055
+ # Start validation asynchronously
1056
+ _report = _encode_and_publish(str(job_id), GOOGLE_ASSAY_METADATA_VALIDATION_TOPIC)
1057
+
1058
+
997
1059
  def send_email(to_emails: List[str], subject: str, html_content: str, **kw) -> None:
998
1060
  """
999
1061
  Publish an email-to-send to the emails topic.
cidc_api/shared/utils.py CHANGED
@@ -1,11 +1,20 @@
1
- def strip_whitespaces(df):
2
- def stripper(x):
3
- if x and isinstance(x, str):
4
- return x.strip()
5
- else:
6
- return x
7
-
8
- df.rename(columns=stripper, inplace=True)
9
- df = df.map(stripper)
1
+ from cidc_api.telemetry import trace_
2
+
3
+
4
+ def _stripper(x):
5
+ if x and isinstance(x, str):
6
+ return x.strip()
7
+ else:
8
+ return x
9
+
10
+
11
+ @trace_("sheet")
12
+ def strip_whitespaces(df, sheet=None):
13
+ if sheet:
14
+ df = df[sheet]
15
+
16
+ df.rename(columns=_stripper, inplace=True)
17
+ df = df.map(_stripper)
18
+ df.replace("", None, inplace=True)
10
19
 
11
20
  return df
cidc_api/telemetry.py ADDED
@@ -0,0 +1,101 @@
1
+ # standard modules
2
+ from functools import wraps
3
+
4
+ # external modules
5
+ from opentelemetry import trace
6
+ from opentelemetry.sdk.resources import Resource
7
+ from opentelemetry.sdk.trace import TracerProvider
8
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
9
+
10
+ # local modules
11
+ from .config.settings import ENV, TESTING
12
+
13
+ # pylint: disable=import-outside-toplevel
14
+
15
+
16
+ def instrument_flask(app):
17
+ from opentelemetry.instrumentation.flask import FlaskInstrumentor
18
+ from opentelemetry.propagate import set_global_textmap
19
+ from opentelemetry.propagators.cloud_trace_propagator import CloudTraceFormatPropagator
20
+
21
+ FlaskInstrumentor().instrument_app(app)
22
+
23
+ # use the X-Cloud-Trace-Context header
24
+ set_global_textmap(CloudTraceFormatPropagator())
25
+
26
+
27
+ def instrument_requests():
28
+ from opentelemetry.instrumentation.requests import RequestsInstrumentor
29
+
30
+ def _request_hook(span, request_obj):
31
+ span.update_name(f"requests {request_obj.method}")
32
+
33
+ RequestsInstrumentor().instrument(request_hook=_request_hook)
34
+
35
+
36
+ def instrument_sqlachemy(engine):
37
+ from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
38
+
39
+ SQLAlchemyInstrumentor().instrument(engine=engine)
40
+
41
+
42
+ resource = Resource(attributes={"service.name": f"CIDC-{ENV}"})
43
+ provider = TracerProvider(resource=resource)
44
+
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)
59
+
60
+ if ENV == "dev-int":
61
+ from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
62
+
63
+ # send span to Cloud Trace service - https://console.cloud.google.com/traces/explorer
64
+ exporter = CloudTraceSpanExporter()
65
+ processor = BatchSpanProcessor(exporter)
66
+ provider.add_span_processor(processor)
67
+
68
+
69
+ # NOTE: we don't run telemetry in upper tiers; no span processor is noop
70
+
71
+ trace.set_tracer_provider(provider)
72
+ tracer = trace.get_tracer(__name__)
73
+
74
+
75
+ def trace_(*args):
76
+ def decorator_factory(func):
77
+
78
+ @wraps(func)
79
+ def wrapper(*args_, **kwargs_):
80
+ func_name = f"{func.__module__.split(".")[-1]}.{func.__name__}"
81
+
82
+ with tracer.start_as_current_span(func_name) as span:
83
+ for arg in args:
84
+ value = kwargs_.get(arg)
85
+
86
+ # track id of argument if exists
87
+ if hasattr(value, "id"):
88
+ value = getattr(value, "id")
89
+
90
+ span.set_attributes({arg: value})
91
+
92
+ result = func(*args_, **kwargs_)
93
+
94
+ if isinstance(result, (str, int, float, bool)):
95
+ span.set_attribute("result", result)
96
+
97
+ return result
98
+
99
+ return wrapper
100
+
101
+ return decorator_factory
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nci_cidc_api_modules
3
- Version: 1.2.29
3
+ Version: 1.2.45
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
@@ -8,30 +8,41 @@ Requires-Python: >=3.13
8
8
  Description-Content-Type: text/markdown
9
9
  License-File: LICENSE
10
10
  Requires-Dist: certifi>=2025.11.12
11
- Requires-Dist: cloud-sql-python-connector[pg8000]>=1.18.5
11
+ Requires-Dist: cloud-sql-python-connector[pg8000]>=1.19.0
12
12
  Requires-Dist: flask>=3.1.2
13
13
  Requires-Dist: flask-migrate>=4.1.0
14
14
  Requires-Dist: flask-sqlalchemy>=3.1.1
15
- Requires-Dist: google-auth==2.43.0
16
- Requires-Dist: google-api-python-client>=2.185.0
17
- Requires-Dist: google-cloud-bigquery>=3.38.0
18
- Requires-Dist: google-cloud-pubsub>=2.33.0
19
- Requires-Dist: google-cloud-secret-manager>=2.25.0
20
- Requires-Dist: google-cloud-storage>=3.6.0
15
+ Requires-Dist: flask-talisman>=0.7.0
16
+ Requires-Dist: gcloud-aio-storage~=9.6.1
17
+ Requires-Dist: google-auth==2.45.0
18
+ Requires-Dist: google-api-python-client>=2.187.0
19
+ Requires-Dist: google-cloud-bigquery>=3.39.0
20
+ Requires-Dist: google-cloud-pubsub>=2.34.0
21
+ Requires-Dist: google-cloud-secret-manager>=2.26.0
22
+ Requires-Dist: google-cloud-storage>=3.7.0
21
23
  Requires-Dist: jinja2>=3.1.6
22
- Requires-Dist: marshmallow>=4.1.0
24
+ Requires-Dist: joserfc>=1.6.0
25
+ Requires-Dist: marshmallow>=4.1.2
23
26
  Requires-Dist: marshmallow-sqlalchemy>=1.4.2
24
- Requires-Dist: numpy>=2.3.5
27
+ Requires-Dist: numpy>=2.4.0
25
28
  Requires-Dist: packaging>=25.0
26
29
  Requires-Dist: pandas>=2.3.3
27
30
  Requires-Dist: pyarrow>=22.0.0
28
- Requires-Dist: pydantic~=2.12.4
31
+ Requires-Dist: pydantic~=2.12.5
29
32
  Requires-Dist: python-dotenv>=1.2.1
30
33
  Requires-Dist: requests>=2.32.5
31
- Requires-Dist: sqlalchemy>=2.0.44
34
+ Requires-Dist: sqlalchemy>=2.0.45
32
35
  Requires-Dist: sqlalchemy-mixins~=2.0.5
33
- Requires-Dist: werkzeug>=3.1.3
34
- Requires-Dist: nci-cidc-schemas==0.28.10
36
+ Requires-Dist: werkzeug>=3.1.4
37
+ Requires-Dist: opentelemetry-api>=1.39.1
38
+ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.39.1
39
+ Requires-Dist: opentelemetry-sdk>=1.39.1
40
+ Requires-Dist: opentelemetry-instrumentation-flask>=0.59b0
41
+ Requires-Dist: opentelemetry-instrumentation-requests>=0.59b0
42
+ Requires-Dist: opentelemetry-instrumentation-sqlalchemy>=0.59b0
43
+ Requires-Dist: opentelemetry-exporter-gcp-trace>=1.11.0
44
+ Requires-Dist: opentelemetry-propagator-gcp>=1.11.0
45
+ Requires-Dist: nci-cidc-schemas==0.28.11
35
46
  Dynamic: description
36
47
  Dynamic: description-content-type
37
48
  Dynamic: home-page
@@ -0,0 +1,165 @@
1
+ cidc_api/__init__.py,sha256=TdZZy40X_55kxtHKjwbGqPrDr5qQe_uUz_Fvup_EuNI,23
2
+ cidc_api/telemetry.py,sha256=LuZWkG8CKCn23O41RTNxEOQwMmfpp-fQ6zSInZhVJg8,3333
3
+ cidc_api/config/__init__.py,sha256=5mX8GAPxUKV84iS-aGOoE-4m68LsOCGCDptXNdlgvj0,148
4
+ cidc_api/config/db.py,sha256=mEz8ugjvRNGylCqDYHaaMqaZfDh7sbd76BowmfBvq5c,2428
5
+ cidc_api/config/logging.py,sha256=abhVYtn8lfhIt0tyV2WHFgSmp_s2eeJh7kodB6LH4J0,1149
6
+ cidc_api/config/secrets.py,sha256=jRFj7W43pWuPf9DZQLCKF7WPXf5cUv-BAaS3ASqhV_Q,1481
7
+ cidc_api/config/settings.py,sha256=Zql6Gxb5MNBW-rxcDNIPUqPiYHnD9nxd2-ievHaEYcI,4744
8
+ cidc_api/models/__init__.py,sha256=bl445G8Zic9YbhZ8ZBni07wtBMhLJRMBA-JqjLxx2bw,66
9
+ cidc_api/models/data.py,sha256=TyVXk5jJO1NN05CLqejLf5BPp6yLSNNxPmFOgXUnA1M,1313
10
+ cidc_api/models/migrations.py,sha256=UlS5How3J4ryaRuZT6F5VQtAKikkl0LTv9MgMO_ltiQ,11161
11
+ cidc_api/models/models.py,sha256=DTt0eFkFVVxVn23Aqr6TA8CQ7qb2hdqQ0xVJ3RakBBw,153684
12
+ cidc_api/models/schemas.py,sha256=6IE2dJoEMcMbi0Vr1V3cYKnPKU0hv9vRKBixOZHe88s,2766
13
+ cidc_api/models/types.py,sha256=WVOTEH61FH5aGVwRbEcfNOce8NLjiEnl1ixtPWav2OA,30058
14
+ cidc_api/models/db/stage1/__init__.py,sha256=zsxOSoogzL_xZ8B5OwcwLXvAmm6g2GYx1Zf6LLw8dq8,1917
15
+ cidc_api/models/db/stage1/additional_treatment_orm.py,sha256=GCMzsYZl_B-7-TWYDBUXbt_Gri6YDv8XkYRheDLh3pc,926
16
+ cidc_api/models/db/stage1/adverse_event_orm.py,sha256=32Kjpg6fvByp4gEVy6K28MlgMWQryG6GPh3un1F8x64,1914
17
+ cidc_api/models/db/stage1/base_orm.py,sha256=izKizTf2W1fCK2CRUeyuErJbOaxNp6d299n4gqKWHDQ,246
18
+ cidc_api/models/db/stage1/baseline_clinical_assessment_orm.py,sha256=pGanJlwnAm38LPVTu6XF4qP_mw-SqYP8AC3O8w8XPnw,911
19
+ cidc_api/models/db/stage1/comorbidity_orm.py,sha256=C4P3kcpNpnt1ICgU2Xm5S-ou6Xhx6yr690g6F_hBoZ0,881
20
+ cidc_api/models/db/stage1/consent_group_orm.py,sha256=O7oZr7-Rvmzn8o4kS_40e270sF2e-KX85VkXemsa2MM,1111
21
+ cidc_api/models/db/stage1/demographic_orm.py,sha256=JR6CXCVLEvFTgElQazuNEgopIuf5Ik1w0m1kSkxp4Q0,1693
22
+ cidc_api/models/db/stage1/disease_orm.py,sha256=Ni1kxQxgVi0MKUlovcsnmpR9v1TwKWcO6tpEr1oZcsE,1965
23
+ cidc_api/models/db/stage1/exposure_orm.py,sha256=u6YKhAAX_0uYHMOYf8sQQFy3l9KluRykaIiggn9v_EY,811
24
+ cidc_api/models/db/stage1/gvhd_diagnosis_acute_orm.py,sha256=0ItEmNE2H2Xmh4W3t0DNg3W764Uk00d5gni6maMQ3N0,1470
25
+ cidc_api/models/db/stage1/gvhd_diagnosis_chronic_orm.py,sha256=seH3WO9IzQmau47N7zjJOa6OCSYi9_25WE5jOvxl6q8,1552
26
+ cidc_api/models/db/stage1/gvhd_organ_acute_orm.py,sha256=knzkKz2qkRU0R6HjdtfClOuRpGEYEsLxbhqYUmOAjSg,838
27
+ cidc_api/models/db/stage1/gvhd_organ_chronic_orm.py,sha256=mkhS1jFgCt_2NRXZfoqHWZh1ShkzLoPLR9W8ac_wzGs,862
28
+ cidc_api/models/db/stage1/medical_history_orm.py,sha256=nyNOG_odEvPWqgu2DmiHT3K-FBB1ltI536iiNwCF0wA,1282
29
+ cidc_api/models/db/stage1/other_malignancy_orm.py,sha256=wfoSCPUIzfw4anDi6FeqyhTzYrwTNlpDRCvL608zmXM,1303
30
+ cidc_api/models/db/stage1/participant_orm.py,sha256=3-s6F0yTFzBYFZrDKv2xJMv5p3VBt5EL1Quu9cK4sd8,3541
31
+ cidc_api/models/db/stage1/prior_treatment_orm.py,sha256=SNa_JOIF5gS61N0HFEfM7BHv3JYsflt6LQz03WLb8Lo,1325
32
+ cidc_api/models/db/stage1/radiotherapy_dose_orm.py,sha256=dLp_ENJAq-_f6MAspaGK-6R6ngHceFKpsrjoBM8yviI,1500
33
+ cidc_api/models/db/stage1/response_by_system_orm.py,sha256=pOQSE5g-EFUApgEDOXwxyQCmvoC6M9atSfBDsF9f3KI,1524
34
+ cidc_api/models/db/stage1/response_orm.py,sha256=P9a0ZVq_hJ3dipRgJTFcdjuWi9ueyGd0oQYHRiACNpc,1163
35
+ cidc_api/models/db/stage1/specimen_orm.py,sha256=m1cq5SfkUETk6dk4s3uYLyXohpZgE8iU1Rzh0xVRTmw,1354
36
+ cidc_api/models/db/stage1/stem_cell_transplant_orm.py,sha256=CqOAo-Pnj11XqR9a0CeaIGyLn5Ar42Hl_10tqEllnO0,1097
37
+ cidc_api/models/db/stage1/surgery_orm.py,sha256=DCggc6xbjGhm-G93bueY5u43t896aBPTvRFTk3Gq8ys,1018
38
+ cidc_api/models/db/stage1/therapy_agent_dose_orm.py,sha256=y8vaJazcoJ5mb0dmGXiAN8dQPP6yyDGpAOs_wDlH3SY,1306
39
+ cidc_api/models/db/stage1/treatment_orm.py,sha256=gRhCswnYYr7DLHbUzz-Lyg3pDVJy6j0UjBZ8SQX3C9k,1625
40
+ cidc_api/models/db/stage1/trial_orm.py,sha256=bS2zIngkv8ruXdXskinSwz8H1EeAgs9aHnteUWqaplE,1266
41
+ cidc_api/models/db/stage2/__init__.py,sha256=R33BM1VDKJya5uuyYnPoGFVfhSViclIOklSoHH-EtBQ,2691
42
+ cidc_api/models/db/stage2/additional_treatment_orm.py,sha256=G8_3EgGWH0vf3Rt5hTmM9xFids4NPcEBkQSS7hsrIdI,924
43
+ cidc_api/models/db/stage2/administrative_person_orm.py,sha256=yjBktBwIjzHwqIbs1BAqJhh3y8f6swmEJRGs-XDQ_OI,1016
44
+ cidc_api/models/db/stage2/administrative_role_assignment_orm.py,sha256=9X3Fkecj8_pxui99LpvA7WIMuWcagYW-QiRcvI7gAHg,1291
45
+ cidc_api/models/db/stage2/adverse_event_orm.py,sha256=lsou-Ej7F_LW7D4pZbgAoR3BLPkW-KJkNjPPv2RtlY0,1914
46
+ cidc_api/models/db/stage2/arm_orm.py,sha256=5jSoHENJXpqW36UrECmBphz_MRQUcYIXGF0fvhMDMWY,707
47
+ cidc_api/models/db/stage2/base_orm.py,sha256=nk9q0wlHxbuC7vzMtPCsnqeIRQufh3MCniMxu-h7__w,246
48
+ cidc_api/models/db/stage2/baseline_clinical_assessment_orm.py,sha256=4V8RuiWGNf2c8Plzeikusslk8pZGhedDm_oXMMDcRbc,911
49
+ cidc_api/models/db/stage2/cohort_orm.py,sha256=ag3Epvnr8__3yfmyUjR7n9fBfMnBlpU1wPyvFZFIWpo,710
50
+ cidc_api/models/db/stage2/comorbidity_orm.py,sha256=EbjqUj9vsFEL0KJFqsc04I-KFGjILje2H71WD2uP3ZQ,881
51
+ cidc_api/models/db/stage2/consent_group_orm.py,sha256=d6r00XxWf7wHwYDAhBsEjSq3Ma2jixAa5l5mNDePNFk,1111
52
+ cidc_api/models/db/stage2/contact_orm.py,sha256=__YFzOrYnMs_TNC1IAI1EL-Xx2stRJTUkcru5GDYleY,1267
53
+ cidc_api/models/db/stage2/demographic_orm.py,sha256=qTO8w0AVTP7NpryfeinaR2X22ll6qA_jxCVGXmAjN60,1693
54
+ cidc_api/models/db/stage2/disease_orm.py,sha256=-PVtbDHFI9PmlYTOsry8LFbuR2YVMq_NrVNkDSZMbLo,1965
55
+ cidc_api/models/db/stage2/exposure_orm.py,sha256=LLuGsaW-dA8B5e__LP9LGAaU7QH5fo5-4Btx28mpE3A,811
56
+ cidc_api/models/db/stage2/file_orm.py,sha256=Zn6HzTz_UcsZRAJUi3OvcASMMtYNh4AFLQTcq_NuG3A,1253
57
+ cidc_api/models/db/stage2/gvhd_diagnosis_acute_orm.py,sha256=n1FtHoUzpKEhDkC5z9VQ_pMKj-M-RpPb3tOerDYwIe4,1470
58
+ cidc_api/models/db/stage2/gvhd_diagnosis_chronic_orm.py,sha256=tA-upQ9q755klfu01HQyRVxBZq7RX_WclZWHuGHGBiw,1552
59
+ cidc_api/models/db/stage2/gvhd_organ_acute_orm.py,sha256=3ShOfSGKgrRBmu83KkpHY3-tCbLL60UWtpcbLqqnq9Y,838
60
+ cidc_api/models/db/stage2/gvhd_organ_chronic_orm.py,sha256=1oVQal-jDylHhfbop43UKZ86YQzrSMu9qCiF8_ELXnY,862
61
+ cidc_api/models/db/stage2/institution_orm.py,sha256=gE0tceyc8mOqZRoOtOAJIAPJUSwJcDaU8L6J4HuUSYM,1248
62
+ cidc_api/models/db/stage2/medical_history_orm.py,sha256=4g-0nk8TIRpzrqwYTAlj9w6o_bPwzic0ygX6sK4x9SU,1282
63
+ cidc_api/models/db/stage2/other_clinical_endpoint_orm.py,sha256=k2Vglc8jASKzSBYBc6C5HCfKUD-cC-HKgvNmuxMFOQc,1091
64
+ cidc_api/models/db/stage2/other_malignancy_orm.py,sha256=qcPL29e8RwAGquMWEp2-ZGSecJGhlgpKdfYFNIHXHvI,1303
65
+ cidc_api/models/db/stage2/participant_orm.py,sha256=U2OICb5OUPpMuh7SPeQJ9GsX4uq5MDc2UYMt52BJYb4,3695
66
+ cidc_api/models/db/stage2/prior_treatment_orm.py,sha256=pvtB3DD6UPtS7GQpAinvJi64mlZjEwWVcp0bFaHpASo,1305
67
+ cidc_api/models/db/stage2/publication_orm.py,sha256=EMAq83WtgFYy5yBESgaajruB6fzYfMTLNyZDZiIJBLM,1040
68
+ cidc_api/models/db/stage2/radiotherapy_dose_orm.py,sha256=QBNmehh5VsKzkc59u_CHsFIgWYKsEp4lFyxp5HPMysM,1500
69
+ cidc_api/models/db/stage2/response_by_system_orm.py,sha256=FzbxFkcIt-IGzwejVITHVFZ5qYA5Oece3yEBqhzjPHw,1524
70
+ cidc_api/models/db/stage2/response_orm.py,sha256=p4xtydLvuZovVwcMvq3-WdLsKeKtIl6KTklGlWGVBSA,1163
71
+ cidc_api/models/db/stage2/shipment_orm.py,sha256=dDmj_PK2yAq23oh60sAcT2dkw5PjWmBoqO55YtFtExc,2007
72
+ cidc_api/models/db/stage2/shipment_specimen_orm.py,sha256=0ufXOQPDjgucu5qXQWDAKnRXajvuldsZezj5ez6hsgg,909
73
+ cidc_api/models/db/stage2/specimen_orm.py,sha256=-UDfQ0IlbmCs6GxCwVIF2eiFPgsjdgq_OAV-X58lH_U,4165
74
+ cidc_api/models/db/stage2/stem_cell_transplant_orm.py,sha256=YxiDGUXIiEtHyBU0zieniG1Ey8cTozuuRym6fv_8DsA,1097
75
+ cidc_api/models/db/stage2/surgery_orm.py,sha256=68g7__ZncQ6Yui5nGhSOHZjWIXWrb5PRAZRS6FCWD18,1018
76
+ cidc_api/models/db/stage2/therapy_agent_dose_orm.py,sha256=dRgXJqlv_EAGaPZ99xFR3xSXBiCPcC-G5e5eJSxqiVY,1306
77
+ cidc_api/models/db/stage2/treatment_orm.py,sha256=ualcYhXrl2obJ-wXDRx5riQsgOWEbdOmmKY8YNiPdZY,1919
78
+ cidc_api/models/db/stage2/trial_orm.py,sha256=7_5kA1Yv80GFQykK8AIlFC0vtrsb2H8xLstlgt8J370,2855
79
+ cidc_api/models/files/__init__.py,sha256=8BMTnUSHzUbz0lBeEQY6NvApxDD3GMWMduoVMos2g4Y,213
80
+ cidc_api/models/files/details.py,sha256=sZkGM7iEV4-J6IDQCdiMV6KBDLbPxCOqUMaU3aY9rX8,65153
81
+ cidc_api/models/files/facets.py,sha256=vieZ3z_tYB29NqtvNzvKqajVwxM5ZLLZKeyQTq5lheU,33776
82
+ cidc_api/models/pydantic/base.py,sha256=6Lsf4fekIS1E-DwZmgCXlfm3Qq9_23dA_v3iz1w2JoA,1427
83
+ cidc_api/models/pydantic/stage1/__init__.py,sha256=H-NDjCd8isHTgZ7sqz-MIbzC-PpW1kEtR-x-Dyc8r6Q,1667
84
+ cidc_api/models/pydantic/stage1/additional_treatment.py,sha256=TFVzjd1NPLSfH9UnL2W3aZhpZSzDsg97j9IK8t882YA,1050
85
+ cidc_api/models/pydantic/stage1/adverse_event.py,sha256=zPy02xoRQeyaXBSmqDdQt4BaRTD6OHBjycVHVtBaAow,3880
86
+ cidc_api/models/pydantic/stage1/baseline_clinical_assessment.py,sha256=5FYvtOVf_nIK1h3xTs3vQgJe2ivQSVHt_wfrTSDgVxU,1119
87
+ cidc_api/models/pydantic/stage1/comorbidity.py,sha256=dk4SJnkaaju-boexf2SW_rNNGXl-Os2LR5lnIxCb-dA,1413
88
+ cidc_api/models/pydantic/stage1/consent_group.py,sha256=aP_nd0GKK6K7VH-6IZRSDDX2RxFM_j9B36FIRGwysOU,1216
89
+ cidc_api/models/pydantic/stage1/demographic.py,sha256=X0YoFdK7S8JQ9p_Jm3l-R5HgEl9sZnZVFXrECA2i_zw,6007
90
+ cidc_api/models/pydantic/stage1/disease.py,sha256=fwK1FSxTm33BzViGTNsb6AahoDc2BTTXMitCGY3KyDk,7650
91
+ cidc_api/models/pydantic/stage1/exposure.py,sha256=ERoVQLuNWynw2zugh5bVdUT0NzNbJ6fIUmKhPSCQfqk,1306
92
+ cidc_api/models/pydantic/stage1/gvhd_diagnosis_acute.py,sha256=jVMoWuLfspOZtOpC7rg1EzdvEAg8qEabcTaS6RRobZ8,1395
93
+ cidc_api/models/pydantic/stage1/gvhd_diagnosis_chronic.py,sha256=SRwkAL07YyIZM4bYCqm01aKqSvj3yRmpsqFKxAZEZoM,1382
94
+ cidc_api/models/pydantic/stage1/gvhd_organ_acute.py,sha256=VxlNGRJYjm9tW2lyLll8pD0t9JZs18gicnH-Ci_nR2I,771
95
+ cidc_api/models/pydantic/stage1/gvhd_organ_chronic.py,sha256=Fnp1Qm-ZDDtDLrgOAnEOUnQVNWI9WrTSomiFPStGmjc,810
96
+ cidc_api/models/pydantic/stage1/medical_history.py,sha256=IXtEvXrrLhTt6JEsj2MpDiZtLAkmrrsUS4aoe7sxans,1697
97
+ cidc_api/models/pydantic/stage1/other_malignancy.py,sha256=V-Jru6uukipocKs7lDbmkGyJsHBxlM3A3NJoTShjJ9I,2188
98
+ cidc_api/models/pydantic/stage1/participant.py,sha256=iDQrwYYtJY_XrkdbhsRNZuL44ighvW8JHCGxH-qo8jk,2199
99
+ cidc_api/models/pydantic/stage1/prior_treatment.py,sha256=axhyPHwYCoIvcIonjyPJ95HUyaAXqIuOd6hhoDM3LJU,2101
100
+ cidc_api/models/pydantic/stage1/radiotherapy_dose.py,sha256=c5q0TxPOrYHX9KTd9he5B01zirKgcCxMnvW_7adzCrI,3327
101
+ cidc_api/models/pydantic/stage1/response.py,sha256=rPqfYZhaZRcA1E7dkGOaHe9f2_YIcIg9-KtIPSTh1s8,2886
102
+ cidc_api/models/pydantic/stage1/response_by_system.py,sha256=f8ZnLfNXGJLCfFL1X2jxlSUJPGvJKnOCf6PYbJVMXW8,4909
103
+ cidc_api/models/pydantic/stage1/specimen.py,sha256=scDekZ-RtXOQTTLburhqT3RF4KM34iY9NAV1wYi_HSg,1281
104
+ cidc_api/models/pydantic/stage1/stem_cell_transplant.py,sha256=XsDVUksqbIprPxHwLlwHGyji4jsIFNMcIk-S0H4rDnQ,1228
105
+ cidc_api/models/pydantic/stage1/surgery.py,sha256=53NrET_kNOlXXPP-mH5ecIPrNqDpDQt5s9WfsjPDM-s,2149
106
+ cidc_api/models/pydantic/stage1/therapy_agent_dose.py,sha256=Y2L0vAqkfA5eI2qg7iQrqzCORwbkjyVEsthc6f5HKas,3021
107
+ cidc_api/models/pydantic/stage1/treatment.py,sha256=1TeBaDJ352IfB10AtJH2iREdex3SZgDRXNUkhT0FZMo,2033
108
+ cidc_api/models/pydantic/stage1/trial.py,sha256=EFsMVMxxHbPeaBb6JG9JpX1qjtoho2t-GZnaI0gsYrY,1859
109
+ cidc_api/models/pydantic/stage2/__init__.py,sha256=OQRG5wFkNsyAU_0KI1W5PDn8pBDe1EJDcgKZ19shh_g,2331
110
+ cidc_api/models/pydantic/stage2/additional_treatment.py,sha256=TFVzjd1NPLSfH9UnL2W3aZhpZSzDsg97j9IK8t882YA,1050
111
+ cidc_api/models/pydantic/stage2/administrative_person.py,sha256=3UYRgGLP7RukwZYTvQEnviDxBvWXj1uqditsoLJOw0s,1552
112
+ cidc_api/models/pydantic/stage2/administrative_role_assignment.py,sha256=kWUciVSnczjejdBSlynsVxSQ4dytDdgw9OxSvgzParg,536
113
+ cidc_api/models/pydantic/stage2/adverse_event.py,sha256=zPy02xoRQeyaXBSmqDdQt4BaRTD6OHBjycVHVtBaAow,3880
114
+ cidc_api/models/pydantic/stage2/arm.py,sha256=I_gyRUkjyA-Eds3BOu-PUlBGdeGeKiG92ERM8WqA2n8,481
115
+ cidc_api/models/pydantic/stage2/baseline_clinical_assessment.py,sha256=5FYvtOVf_nIK1h3xTs3vQgJe2ivQSVHt_wfrTSDgVxU,1119
116
+ cidc_api/models/pydantic/stage2/cohort.py,sha256=NVlgp8D93nXGPDMSyrAJe-QbmrQfudyuTsc400WyOpg,493
117
+ cidc_api/models/pydantic/stage2/comorbidity.py,sha256=dk4SJnkaaju-boexf2SW_rNNGXl-Os2LR5lnIxCb-dA,1413
118
+ cidc_api/models/pydantic/stage2/consent_group.py,sha256=aP_nd0GKK6K7VH-6IZRSDDX2RxFM_j9B36FIRGwysOU,1216
119
+ cidc_api/models/pydantic/stage2/contact.py,sha256=Nfyy5Zn-n9_LiXG-CzC7OsQo7JrzdPcYjZIielpZiM0,883
120
+ cidc_api/models/pydantic/stage2/demographic.py,sha256=X0YoFdK7S8JQ9p_Jm3l-R5HgEl9sZnZVFXrECA2i_zw,6007
121
+ cidc_api/models/pydantic/stage2/disease.py,sha256=fwK1FSxTm33BzViGTNsb6AahoDc2BTTXMitCGY3KyDk,7650
122
+ cidc_api/models/pydantic/stage2/exposure.py,sha256=ERoVQLuNWynw2zugh5bVdUT0NzNbJ6fIUmKhPSCQfqk,1306
123
+ cidc_api/models/pydantic/stage2/file.py,sha256=MjZQAII_QqakLBhyfP1bq_Mn6au4yvVuwY0p0A5c664,1336
124
+ cidc_api/models/pydantic/stage2/gvhd_diagnosis_acute.py,sha256=jVMoWuLfspOZtOpC7rg1EzdvEAg8qEabcTaS6RRobZ8,1395
125
+ cidc_api/models/pydantic/stage2/gvhd_diagnosis_chronic.py,sha256=SRwkAL07YyIZM4bYCqm01aKqSvj3yRmpsqFKxAZEZoM,1382
126
+ cidc_api/models/pydantic/stage2/gvhd_organ_acute.py,sha256=VxlNGRJYjm9tW2lyLll8pD0t9JZs18gicnH-Ci_nR2I,771
127
+ cidc_api/models/pydantic/stage2/gvhd_organ_chronic.py,sha256=Fnp1Qm-ZDDtDLrgOAnEOUnQVNWI9WrTSomiFPStGmjc,810
128
+ cidc_api/models/pydantic/stage2/institution.py,sha256=41Lod9ryjB3x2jySHp_g3b7g1sj41Q4zwcNsGgXMGZ4,268
129
+ cidc_api/models/pydantic/stage2/medical_history.py,sha256=IXtEvXrrLhTt6JEsj2MpDiZtLAkmrrsUS4aoe7sxans,1697
130
+ cidc_api/models/pydantic/stage2/other_clinical_endpoint.py,sha256=bxZV3qIrrieX4xy3HbKyHdgfpwBcT-abQNlSodVCMns,1093
131
+ cidc_api/models/pydantic/stage2/other_malignancy.py,sha256=V-Jru6uukipocKs7lDbmkGyJsHBxlM3A3NJoTShjJ9I,2188
132
+ cidc_api/models/pydantic/stage2/participant.py,sha256=x7gKHaw46-BbqNCgKWPLnp7U8L2pnAkh7hdByKvG_Kk,2185
133
+ cidc_api/models/pydantic/stage2/prior_treatment.py,sha256=G5Cd8PSYRe3gdeApjQWn15QAErB4nDahMKcVRsr88_c,2011
134
+ cidc_api/models/pydantic/stage2/publication.py,sha256=GBuvqBf0lyOoopN1S-3qX310xFxhixk7oDJLuGzLuiw,1817
135
+ cidc_api/models/pydantic/stage2/radiotherapy_dose.py,sha256=c5q0TxPOrYHX9KTd9he5B01zirKgcCxMnvW_7adzCrI,3327
136
+ cidc_api/models/pydantic/stage2/response.py,sha256=rPqfYZhaZRcA1E7dkGOaHe9f2_YIcIg9-KtIPSTh1s8,2886
137
+ cidc_api/models/pydantic/stage2/response_by_system.py,sha256=f8ZnLfNXGJLCfFL1X2jxlSUJPGvJKnOCf6PYbJVMXW8,4909
138
+ cidc_api/models/pydantic/stage2/shipment.py,sha256=dsZcDPikCRS3cjr58EGHHJ2gjir78J4cdHh_1SwFyrQ,1595
139
+ cidc_api/models/pydantic/stage2/shipment_specimen.py,sha256=exR7joXsH3Xd-o5vJV_Z-az33Edr8BBm5LCDv01iMI4,488
140
+ cidc_api/models/pydantic/stage2/specimen.py,sha256=JchTeb4QSyPplZmP_gMxEsaIXVDlGI_z85xR5JG4JYY,8524
141
+ cidc_api/models/pydantic/stage2/stem_cell_transplant.py,sha256=XsDVUksqbIprPxHwLlwHGyji4jsIFNMcIk-S0H4rDnQ,1228
142
+ cidc_api/models/pydantic/stage2/surgery.py,sha256=53NrET_kNOlXXPP-mH5ecIPrNqDpDQt5s9WfsjPDM-s,2149
143
+ cidc_api/models/pydantic/stage2/therapy_agent_dose.py,sha256=Y2L0vAqkfA5eI2qg7iQrqzCORwbkjyVEsthc6f5HKas,3021
144
+ cidc_api/models/pydantic/stage2/treatment.py,sha256=dr23yeK5x0dKYZV32hgECFe_Y-GkSvdf4P3FdVn6F1Q,2039
145
+ cidc_api/models/pydantic/stage2/trial.py,sha256=PftvFY27s3quVBg1Sau1JdskUSMM0huIeGP-018fi5o,4020
146
+ cidc_api/reference/ctcae.py,sha256=H5JvDQ5R5kYbK37UxjhmXMnKD3d40HmG0DONxfhNxnE,871
147
+ cidc_api/reference/gvhd.py,sha256=r5jbHEa5yos0tmghjiruAeXI-r-4lU81JKCUpMHtO14,194
148
+ cidc_api/reference/icd10cm.py,sha256=K1vbTQB75uAQeKgj0U9izhtMKVb2vqp69_hyx3z_jro,300
149
+ cidc_api/reference/icdo3.py,sha256=A19yNX5-9Gs3X83kXcTlGgsXDTTJ9yR2dxEIoBVmpmU,296
150
+ cidc_api/reference/uberon.py,sha256=BO2mYNDvPzZyLdhL_ZjCyEgHSLHuVifnTJvfktUfVWA,148
151
+ cidc_api/shared/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
152
+ cidc_api/shared/assay_handling.py,sha256=zzWSqQ-ddLVzX5IuHvsaSib2H1lnjXpo9LbxaoqaHHs,2897
153
+ cidc_api/shared/auth.py,sha256=EIP9AKokLNrI79Fkpv3P7WdzaddJIsuGGICc1W494X0,9110
154
+ cidc_api/shared/email_layout.html,sha256=pBoTNw3ACHH-ncZFaNvcy5bXMqPwizR78usb0uCYtIc,7670
155
+ cidc_api/shared/emails.py,sha256=8kNFEaSnKpY-GX_iE59QUhSp3c4_uzy3SpHYt2QjuqI,6121
156
+ cidc_api/shared/file_handling.py,sha256=ObsLvBrkwHo6Nv48GvFXDKElNNl4inGpxv--001H_xc,5843
157
+ cidc_api/shared/gcloud_client.py,sha256=0B13MRF_CUMbzaKtKVDd9fxErNdNGBpxfkknQ5deu7w,39972
158
+ cidc_api/shared/jose.py,sha256=-qzGzEDAlokEp9E7WtBtQkXyyfPWTYXlwYpCqVJWmqM,1830
159
+ cidc_api/shared/rest_utils.py,sha256=RwR30WOUAYCxL7V-i2totEyeriG30GbBDvBcpLXhM9w,6594
160
+ cidc_api/shared/utils.py,sha256=-gLnzxCR9E6h0plt2xrNisUG5_Y6GhhVwz3DgDIzpvs,367
161
+ nci_cidc_api_modules-1.2.45.dist-info/licenses/LICENSE,sha256=pNYWVTHaYonnmJyplmeAp7tQAjosmDpAWjb34jjv7Xs,1102
162
+ nci_cidc_api_modules-1.2.45.dist-info/METADATA,sha256=ierSCHEAC0omx7bg4gjb-XPMPRTK_Qs6VMJXdW65C6A,40270
163
+ nci_cidc_api_modules-1.2.45.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
164
+ nci_cidc_api_modules-1.2.45.dist-info/top_level.txt,sha256=rNiRzL0lJGi5Q9tY9uSoMdTbJ-7u5c_D2E86KA94yRA,9
165
+ nci_cidc_api_modules-1.2.45.dist-info/RECORD,,
@@ -1,25 +0,0 @@
1
- from typing import Self
2
-
3
- from sqlalchemy_mixins import SerializeMixin, ReprMixin
4
-
5
- from cidc_api.config.db import db
6
-
7
-
8
- class BaseORM(db.Model, ReprMixin, SerializeMixin):
9
- __abstract__ = True
10
- __repr__ = ReprMixin.__repr__
11
-
12
- def merge(self, d: dict) -> Self:
13
- """Merge keys and values from dict d into this model, overwriting as necessary."""
14
- for key, value in d.items():
15
- setattr(self, key, value)
16
- return self
17
-
18
- def clone(self) -> "BaseORM":
19
- """Clones a SQLAlchemy ORM object, excluding primary keys."""
20
- mapper = self.__mapper__
21
- new_instance = self.__class__()
22
- for column in mapper.columns:
23
- if not column.primary_key:
24
- setattr(new_instance, column.key, getattr(self, column.key))
25
- return new_instance