nci-cidc-api-modules 1.2.1__py3-none-any.whl → 1.2.3__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 CHANGED
@@ -45,33 +45,7 @@ def get_sqlalchemy_database_uri(testing: bool = False) -> str:
45
45
  # Connect to the test database
46
46
  db_uri = environ.get("TEST_POSTGRES_URI", "fake-conn-string")
47
47
  elif not db_uri:
48
- # TODO: figure out why IAM authentication causes an issue with the huge filter query
49
- # db_uri = f"postgresql+pg8000://{environ.get('CLOUD_SQL_DB_USER')}:xxx@/{environ.get('CLOUD_SQL_DB_NAME')}"
50
-
51
- secrets = get_secrets_manager(testing)
52
-
53
- # If POSTGRES_URI env variable is not set,
54
- # we're connecting to a Cloud SQL instance.
55
-
56
- config: dict = {
57
- "drivername": "postgresql",
58
- "username": environ.get("CLOUD_SQL_DB_USER"),
59
- "password": secrets.get(environ.get("CLOUD_SQL_DB_PASS_ID")),
60
- "database": environ.get("CLOUD_SQL_DB_NAME"),
61
- }
62
-
63
- if environ.get("CLOUD_SQL_INSTANCE_NAME"):
64
- socket_dir = environ.get("CLOUD_SQL_SOCKET_DIR", "/cloudsql/")
65
-
66
- # If CLOUD_SQL_INSTANCE_NAME is defined, we're connecting
67
- # via a unix socket from inside App Engine.
68
- config["query"] = {"host": f'{socket_dir}{environ.get("CLOUD_SQL_INSTANCE_NAME")}'}
69
- else:
70
- raise RuntimeError(
71
- "Either POSTGRES_URI or CLOUD_SQL_INSTANCE_NAME must be defined to connect " + "to a database."
72
- )
73
-
74
- db_uri = str(URL.create(**config).render_as_string(hide_password=False))
48
+ db_uri = f"postgresql+pg8000://{environ.get('CLOUD_SQL_DB_USER')}:xxx@/{environ.get('CLOUD_SQL_DB_NAME')}"
75
49
 
76
50
  assert db_uri
77
51
 
@@ -80,9 +54,7 @@ def get_sqlalchemy_database_uri(testing: bool = False) -> str:
80
54
 
81
55
  # Use SQLALCHEMY_ENGINE_OPTIONS to connect to the cloud but use uri for local db
82
56
  def cloud_connector(testing: bool = False):
83
- return {}
84
- # TODO: figure out IAM authentication
85
- # if not testing and not environ.get("POSTGRES_URI"):
86
- # return {"creator": getconn}
87
- # else:
88
- # return {}
57
+ if not testing and not environ.get("POSTGRES_URI"):
58
+ return {"creator": getconn}
59
+ else:
60
+ return {}
@@ -361,6 +361,11 @@ assay_facets: Facets = {
361
361
  "colorized image": FacetConfig(["/visium/colorized.tiff"]),
362
362
  "cytassist image": FacetConfig(["/visium/cytassist.tiff"]),
363
363
  },
364
+ "NULISA": {
365
+ "Metadata": FacetConfig(["/nulisa/metadata.csv", "Metadata for NULISA run"]),
366
+ "NPQ File": FacetConfig(["/nulisa/npq_file.csv", "NPQ file for NULISA run"]),
367
+ "Raw Counts File": FacetConfig(["/nulisa/raw_counts_file.csv", "Raw counts file for NULISA run"]),
368
+ },
364
369
  "mIHC": {
365
370
  "Samples Report": FacetConfig(["/mihc/sample_report.csv"], "Samples report for mIHC run"),
366
371
  "Multitiffs": FacetConfig(["/mihc/multitiffs.tar.gz"], "Multi Tiffs file from mIHC run"),
cidc_api/models/models.py CHANGED
@@ -26,6 +26,7 @@ __all__ = [
26
26
  "FileValidationErrors",
27
27
  "IngestionJobs",
28
28
  "JobFileCategories",
29
+ "ValidationConfigs",
29
30
  "TRIAL_APPENDIX_A",
30
31
  "REQUEST_LETTER",
31
32
  "ADMIN_FILE_CATEGORIES",
@@ -531,6 +532,7 @@ class IAMException(Exception):
531
532
  EXTRA_DATA_TYPES = [
532
533
  "participants info",
533
534
  "samples info",
535
+ "clinical_upload",
534
536
  "file_group",
535
537
  ]
536
538
  ALL_UPLOAD_TYPES = set(
@@ -3117,7 +3119,7 @@ class DownloadableFiles(CommonColumns):
3117
3119
  cls.file_purpose.label(purp_col.key),
3118
3120
  func.json_agg(cls.id).label(ids_col.key),
3119
3121
  )
3120
- .group_by(cls.trial_id, cls.data_category_prefix, cls.file_purpose)
3122
+ .group_by(cls.trial_id, type_col, purp_col)
3121
3123
  .alias("id_bundles")
3122
3124
  )
3123
3125
  purpose_bundles = (
@@ -3429,7 +3431,7 @@ class IngestionJobs(CommonColumns):
3429
3431
  trial_id = Column(String, nullable=False)
3430
3432
  version = Column(Integer, nullable=False)
3431
3433
  pending = Column(Boolean, nullable=False, default=False)
3432
- start_date = Column(Date, nullable=True)
3434
+ start_date = Column(DateTime, nullable=True)
3433
3435
 
3434
3436
  @staticmethod
3435
3437
  @with_default_session
@@ -3497,11 +3499,33 @@ class IngestionJobs(CommonColumns):
3497
3499
  .first()
3498
3500
  )
3499
3501
 
3500
- # TODO: figure out which users have access to which jobs
3501
3502
  @classmethod
3502
3503
  @with_default_session
3503
3504
  def get_open_jobs_for_user(cls, user: Users, session: Session = None) -> list["IngestionJobs"]:
3504
- return session.query(cls).filter(cls.status.notin_(["DRAFT"])).order_by(cls._created.desc()).all()
3505
+ if user.role not in [CIDCRole.ADMIN.value, CIDCRole.CLINICAL_TRIAL_USER.value]:
3506
+ return []
3507
+ job_query = session.query(cls).filter(cls.status.notin_(["DRAFT"]))
3508
+ if (
3509
+ user.role != CIDCRole.ADMIN.value
3510
+ and not session.query(Permissions)
3511
+ .filter(
3512
+ Permissions.granted_to_user == user.id,
3513
+ Permissions.upload_type == "clinical_upload",
3514
+ Permissions.trial_id == Permissions.EVERY,
3515
+ )
3516
+ .all()
3517
+ ):
3518
+ authorized_trials = (
3519
+ session.query(Permissions)
3520
+ .filter(
3521
+ Permissions.granted_to_user == user.id,
3522
+ Permissions.upload_type == "clinical_upload",
3523
+ Permissions.trial_id != Permissions.EVERY,
3524
+ )
3525
+ .all()
3526
+ )
3527
+ job_query = job_query.filter(cls.trial_id.in_(map(lambda x: x.trial_id, authorized_trials)))
3528
+ return job_query.order_by(cls._created.desc()).all()
3505
3529
 
3506
3530
 
3507
3531
  class JobFileCategories(CommonColumns):
@@ -3579,3 +3603,23 @@ class FileValidationErrors(CommonColumns):
3579
3603
  if file_ids:
3580
3604
  session.query(cls).filter(cls.file_id.in_(file_ids)).delete(synchronize_session=False)
3581
3605
  session.commit()
3606
+
3607
+ @classmethod
3608
+ @with_default_session
3609
+ def get_errors_for_job_sorted_by_file_id(cls, job_id: int, session: Session = None) -> list["FileValidationErrors"]:
3610
+ return session.query(cls).filter(cls.job_id == job_id).order_by(cls.file_id.asc()).all()
3611
+
3612
+
3613
+ class ValidationConfigs(CommonColumns):
3614
+ __tablename__ = "validation_configs"
3615
+
3616
+ data_label = Column(String, nullable=True)
3617
+ validation = Column(String, nullable=True)
3618
+ scope = Column(String, nullable=True)
3619
+ active = Column(Boolean, nullable=False, default=True, server_default="true")
3620
+
3621
+ @classmethod
3622
+ @with_default_session
3623
+ def get_active_configs_by_scope(cls, scope: str, session=None) -> list["ValidationConfigs"]:
3624
+ """Return all active ValidationConfigs rows for the given scope."""
3625
+ return session.query(cls).filter(cls.scope == scope, cls.active.is_(True)).all()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nci_cidc_api_modules
3
- Version: 1.2.1
3
+ Version: 1.2.3
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
@@ -29,7 +29,7 @@ Requires-Dist: requests>=2.32.5
29
29
  Requires-Dist: jinja2>=3.1.6
30
30
  Requires-Dist: certifi>=2025.8.3
31
31
  Requires-Dist: cloud-sql-python-connector[pg8000]>=1.18.4
32
- Requires-Dist: nci-cidc-schemas==0.28.2
32
+ Requires-Dist: nci-cidc-schemas==0.28.3
33
33
  Dynamic: description
34
34
  Dynamic: description-content-type
35
35
  Dynamic: home-page
@@ -395,7 +395,7 @@ API authentication relies on _identity tokens_ generated by Auth0 to verify that
395
395
 
396
396
  - It is a well-formatted JWT.
397
397
  - It has not yet expired.
398
- - Its cryptographic signature is valid.
398
+ - Its cryptographic signature is valid.
399
399
 
400
400
  JWTs are a lot like passports - they convey personal information, they’re issued by a trusted entity, and they expire after a certain time. Moreover, like passports, JWTs **can be stolen** and used to impersonate someone. As such, JWTs should be kept private and treated sort of like short-lived passwords.
401
401
 
@@ -1,15 +1,15 @@
1
1
  cidc_api/config/__init__.py,sha256=5mX8GAPxUKV84iS-aGOoE-4m68LsOCGCDptXNdlgvj0,148
2
- cidc_api/config/db.py,sha256=kbCemCDYv_zyczw-V7H2JvLCa_XeEcvXkzvF28FxADw,2862
2
+ cidc_api/config/db.py,sha256=eFCkJDeykRIJZ25kePVjRNrZcrcKTc1s2K0yErr46LI,1683
3
3
  cidc_api/config/logging.py,sha256=abhVYtn8lfhIt0tyV2WHFgSmp_s2eeJh7kodB6LH4J0,1149
4
4
  cidc_api/config/secrets.py,sha256=jRFj7W43pWuPf9DZQLCKF7WPXf5cUv-BAaS3ASqhV_Q,1481
5
5
  cidc_api/config/settings.py,sha256=NsJbqW6Vqcz2f79xcAbk4th5tHne_I-RPCbKq_3hpz0,4427
6
6
  cidc_api/models/__init__.py,sha256=bl445G8Zic9YbhZ8ZBni07wtBMhLJRMBA-JqjLxx2bw,66
7
7
  cidc_api/models/migrations.py,sha256=gp9vtkYbA9FFy2s-7woelAmsvQbJ41LO2_DY-YkFIrQ,11464
8
- cidc_api/models/models.py,sha256=x9EMbMVQHDGN5slYrr3WFsKpVL9fYfK0dQz1SdUunNM,144461
8
+ cidc_api/models/models.py,sha256=b8V3TmETY0v8IVcKQPoNgIPgZ6iXP9TbSOE5CCLKJKM,146237
9
9
  cidc_api/models/schemas.py,sha256=6IE2dJoEMcMbi0Vr1V3cYKnPKU0hv9vRKBixOZHe88s,2766
10
10
  cidc_api/models/files/__init__.py,sha256=8BMTnUSHzUbz0lBeEQY6NvApxDD3GMWMduoVMos2g4Y,213
11
11
  cidc_api/models/files/details.py,sha256=sZkGM7iEV4-J6IDQCdiMV6KBDLbPxCOqUMaU3aY9rX8,65153
12
- cidc_api/models/files/facets.py,sha256=WqjfqtYJgY2tBnZ598Yc0eJdQUo2slFNLyTDaqPx_DE,32318
12
+ cidc_api/models/files/facets.py,sha256=bJChPlBHYJuDFgV1eMZkqyXE8sej7jRk2sQ2kNgE9m4,32620
13
13
  cidc_api/shared/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  cidc_api/shared/auth.py,sha256=PHqmVGkqDjbmUofytVFwD_9ssgUomESl3fFtFHPwZYQ,9062
15
15
  cidc_api/shared/emails.py,sha256=HQIixEUsR8yyu7Iv8S81RjtvEQeGuzQHzBfGsWIfP7k,4961
@@ -17,8 +17,8 @@ cidc_api/shared/file_handling.py,sha256=l4wiRkVJLL7QbCoODsLx-uki6Km8QoMmUlRVnUV9
17
17
  cidc_api/shared/gcloud_client.py,sha256=ETQ34qKtO7l0svVhiHXxXewiyNjwIMgoeVcqrg-Twtk,36902
18
18
  cidc_api/shared/jose.py,sha256=-qzGzEDAlokEp9E7WtBtQkXyyfPWTYXlwYpCqVJWmqM,1830
19
19
  cidc_api/shared/rest_utils.py,sha256=RwR30WOUAYCxL7V-i2totEyeriG30GbBDvBcpLXhM9w,6594
20
- nci_cidc_api_modules-1.2.1.dist-info/licenses/LICENSE,sha256=pNYWVTHaYonnmJyplmeAp7tQAjosmDpAWjb34jjv7Xs,1102
21
- nci_cidc_api_modules-1.2.1.dist-info/METADATA,sha256=U5F9jMinwYPPkYteR5lqZF0PECWitEh6pPf_6XQBojE,39537
22
- nci_cidc_api_modules-1.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
- nci_cidc_api_modules-1.2.1.dist-info/top_level.txt,sha256=rNiRzL0lJGi5Q9tY9uSoMdTbJ-7u5c_D2E86KA94yRA,9
24
- nci_cidc_api_modules-1.2.1.dist-info/RECORD,,
20
+ nci_cidc_api_modules-1.2.3.dist-info/licenses/LICENSE,sha256=pNYWVTHaYonnmJyplmeAp7tQAjosmDpAWjb34jjv7Xs,1102
21
+ nci_cidc_api_modules-1.2.3.dist-info/METADATA,sha256=_aGCpl7oQ9ikj7nwIYvcW6Zd6gg3DFkFsm3rhxkwaFk,39538
22
+ nci_cidc_api_modules-1.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
+ nci_cidc_api_modules-1.2.3.dist-info/top_level.txt,sha256=rNiRzL0lJGi5Q9tY9uSoMdTbJ-7u5c_D2E86KA94yRA,9
24
+ nci_cidc_api_modules-1.2.3.dist-info/RECORD,,