Skip to content

Commit

Permalink
factorize subject session
Browse files Browse the repository at this point in the history
  • Loading branch information
maximemulder committed Sep 27, 2024
1 parent 72a546f commit c1aab1f
Show file tree
Hide file tree
Showing 18 changed files with 253 additions and 125 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ include = [
"python/tests",
"python/lib/db",
"python/lib/exception",
"python/lib/get_subject_session.py",
"python/lib/validate_subject_ids.py",
]
typeCheckingMode = "strict"
Expand Down
6 changes: 6 additions & 0 deletions python/lib/database_lib/session_db.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"""This class performs session table related database queries and common checks"""

from typing_extensions import deprecated

__license__ = "GPLv3"


@deprecated('Use `lib.db.model.session.DbSession` instead')
class SessionDB:
"""
This class performs database queries for session table.
Expand Down Expand Up @@ -35,6 +37,7 @@ def __init__(self, db, verbose):
self.db = db
self.verbose = verbose

@deprecated('Use `lib.db.query.try_get_candidate_with_cand_id_visit_label` instead')
def create_session_dict(self, cand_id, visit_label):
"""
Queries the session table for a particular candidate ID and visit label and returns a dictionary
Expand All @@ -56,6 +59,7 @@ def create_session_dict(self, cand_id, visit_label):

return results[0] if results else None

@deprecated('Use `lib.db.query.site.try_get_site_with_psc_id_visit_label` instead')
def get_session_center_info(self, pscid, visit_label):
"""
Get site information for a given visit.
Expand All @@ -77,6 +81,7 @@ def get_session_center_info(self, pscid, visit_label):

return results[0] if results else None

@deprecated('Use `lib.get_subject_session.get_candidate_next_visit_number` instead')
def determine_next_session_site_id_and_visit_number(self, cand_id):
"""
Determines the next session site and visit number based on the last session inserted for a given candidate.
Expand All @@ -99,6 +104,7 @@ def determine_next_session_site_id_and_visit_number(self, cand_id):

return results[0] if results else None

@deprecated('Use `lib.db.model.session.DbSession` instead')
def insert_into_session(self, fields, values):
"""
Insert a new row in the session table using fields list as column names and values as values.
Expand Down
11 changes: 9 additions & 2 deletions python/lib/db/model/candidate.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from datetime import date
from typing import Optional
from sqlalchemy.orm import Mapped, mapped_column
from typing import List, Optional
from sqlalchemy.orm import Mapped, mapped_column, relationship
from lib.db.base import Base
import lib.db.model.session as db_session
import lib.db.model.site as db_site


class DbCandidate(Base):
Expand Down Expand Up @@ -31,3 +33,8 @@ class DbCandidate(Base):
entity_type : Mapped[str] = mapped_column('Entity_type')
proband_sex : Mapped[Optional[str]] = mapped_column('ProbandSex')
proband_sate_of_birth : Mapped[Optional[date]] = mapped_column('ProbandDoB')

sessions : Mapped[List['db_session.DbSession']] \
= relationship('DbSession', back_populates='candidate')
registration_site : Mapped['db_site.DbSite'] \
= relationship('DbSite')
16 changes: 8 additions & 8 deletions python/lib/db/model/dicom_archive.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from datetime import date, datetime
from typing import List, Optional
from sqlalchemy import String
from sqlalchemy.orm import Mapped, mapped_column, relationship
from lib.db.base import Base
import lib.db.model.dicom_archive_file as db_dicom_archive_file
Expand All @@ -12,13 +11,7 @@ class DbDicomArchive(Base):
__tablename__ = 'tarchive'

id : Mapped[int] = mapped_column('TarchiveID', primary_key=True)
series : Mapped[List['db_dicom_archive_series.DbDicomArchiveSeries']] \
= relationship('DbDicomArchiveSeries', back_populates='archive')
files : Mapped[List['db_dicom_archive_file.DbDicomArchiveFile']] \
= relationship('DbDicomArchiveFile', back_populates='archive')
upload : Mapped[Optional['db_mri_upload.DbMriUpload']] \
= relationship('DbMriUpload', back_populates='dicom_archive')
study_uid : Mapped[str] = mapped_column('DicomArchiveID', type_ = String())
study_uid : Mapped[str] = mapped_column('DicomArchiveID')
patient_id : Mapped[str] = mapped_column('PatientID')
patient_name : Mapped[str] = mapped_column('PatientName')
patient_birthdate : Mapped[Optional[date]] = mapped_column('PatientDoB')
Expand Down Expand Up @@ -49,3 +42,10 @@ class DbDicomArchive(Base):
acquisition_metadata : Mapped[str] = mapped_column('AcquisitionMetadata')
date_sent : Mapped[Optional[datetime]] = mapped_column('DateSent')
pending_transfer : Mapped[int] = mapped_column('PendingTransfer')

series : Mapped[List['db_dicom_archive_series.DbDicomArchiveSeries']] \
= relationship('DbDicomArchiveSeries', back_populates='archive')
files : Mapped[List['db_dicom_archive_file.DbDicomArchiveFile']] \
= relationship('DbDicomArchiveFile', back_populates='archive')
upload : Mapped[Optional['db_mri_upload.DbMriUpload']] \
= relationship('DbMriUpload', back_populates='dicom_archive')
9 changes: 5 additions & 4 deletions python/lib/db/model/dicom_archive_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ class DbDicomArchiveFile(Base):

id : Mapped[int] = mapped_column('TarchiveFileID', primary_key=True)
archive_id : Mapped[int] = mapped_column('TarchiveID', ForeignKey('tarchive.TarchiveID'))
archive : Mapped['db_dicom_archive.DbDicomArchive'] \
= relationship('DbDicomArchive', back_populates='files')
series_id : Mapped[Optional[int]] \
= mapped_column('TarchiveSeriesID', ForeignKey('tarchive_series.TarchiveSeriesID'))
series : Mapped[Optional['db_dicom_archive_series.DbDicomArchiveSeries']] \
= relationship('DbDicomArchiveSeries', back_populates="files")
series_number : Mapped[Optional[int]] = mapped_column('SeriesNumber')
series_description : Mapped[Optional[str]] = mapped_column('SeriesDescription')
file_number : Mapped[Optional[int]] = mapped_column('FileNumber')
echo_number : Mapped[Optional[int]] = mapped_column('EchoNumber')
md5_sum : Mapped[str] = mapped_column('Md5Sum')
file_name : Mapped[str] = mapped_column('FileName')

archive : Mapped['db_dicom_archive.DbDicomArchive'] \
= relationship('DbDicomArchive', back_populates='files')
series : Mapped[Optional['db_dicom_archive_series.DbDicomArchiveSeries']] \
= relationship('DbDicomArchiveSeries', back_populates="files")
9 changes: 5 additions & 4 deletions python/lib/db/model/dicom_archive_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ class DbDicomArchiveSeries(Base):

id : Mapped[int] = mapped_column('TarchiveSeriesID', primary_key=True)
archive_id : Mapped[int] = mapped_column('TarchiveID', ForeignKey("tarchive.TarchiveID"))
archive : Mapped['db_dicom_archive.DbDicomArchive'] \
= relationship('DbDicomArchive', back_populates="series")
files : Mapped[List['db_dicom_archive_file.DbDicomArchiveFile']] \
= relationship('DbDicomArchiveFile', back_populates="series")
series_number : Mapped[int] = mapped_column('SeriesNumber')
series_description : Mapped[Optional[str]] = mapped_column('SeriesDescription')
sequence_name : Mapped[Optional[str]] = mapped_column('SequenceName')
Expand All @@ -26,3 +22,8 @@ class DbDicomArchiveSeries(Base):
number_of_files : Mapped[int] = mapped_column('NumberOfFiles')
series_uid : Mapped[Optional[str]] = mapped_column('SeriesUID')
modality : Mapped[Optional[str]] = mapped_column('Modality')

archive : Mapped['db_dicom_archive.DbDicomArchive'] \
= relationship('DbDicomArchive', back_populates="series")
files : Mapped[List['db_dicom_archive_file.DbDicomArchiveFile']] \
= relationship('DbDicomArchiveFile', back_populates="series")
5 changes: 3 additions & 2 deletions python/lib/db/model/mri_upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ class DbMriUpload(Base):
number_of_minc_created : Mapped[Optional[int]] = mapped_column('number_of_mincCreated')
dicom_archive_id : Mapped[Optional[int]] \
= mapped_column('TarchiveID', ForeignKey('tarchive.TarchiveID'))
dicom_archive : Mapped[Optional['db_dicom_archive.DbDicomArchive']] \
= relationship('DicomArchive', back_populates='upload')
session_id : Mapped[Optional[int]] = mapped_column('SessionID')
is_candidate_info_validated : Mapped[Optional[bool]] = mapped_column('IsCandidateInfoValidated')
is_dicom_archive_validated : Mapped[bool] = mapped_column('IsTarchiveValidated')
is_phantom : Mapped[str] = mapped_column('IsPhantom')

dicom_archive : Mapped[Optional['db_dicom_archive.DbDicomArchive']] \
= relationship('DicomArchive', back_populates='upload')
10 changes: 10 additions & 0 deletions python/lib/db/model/project_cohort.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from sqlalchemy.orm import Mapped, mapped_column
from lib.db.base import Base


class DbProjectCohort(Base):
__tablename__ = 'project_cohort_rel'

id : Mapped[int] = mapped_column('ProjectCohortRelID', primary_key=True)
project_id : Mapped[int] = mapped_column('ProjectID')
cohort_id : Mapped[int] = mapped_column('CohortID')
14 changes: 10 additions & 4 deletions python/lib/db/model/session.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
from datetime import date, datetime
from typing import Optional
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from lib.db.base import Base
import lib.db.model.candidate as db_candidate
import lib.db.model.site as db_site


class DbSession(Base):
__tablename__ = 'session'

id : Mapped[int] = mapped_column('ID', primary_key=True)
cand_id : Mapped[int] = mapped_column('CandID')
center_id : Mapped[int] = mapped_column('CenterID')
cand_id : Mapped[int] = mapped_column('CandID', ForeignKey('candidate.CandID'))
site_id : Mapped[int] = mapped_column('CenterID', ForeignKey('psc.ID'))
project_id : Mapped[int] = mapped_column('ProjectID')
visit_no : Mapped[Optional[int]] = mapped_column('VisitNo')
visit_number : Mapped[Optional[int]] = mapped_column('VisitNo')
visit_label : Mapped[str] = mapped_column('Visit_label')
cohort_id : Mapped[int] = mapped_column('CohortID')
submitted : Mapped[str] = mapped_column('Submitted')
Expand Down Expand Up @@ -42,3 +45,6 @@ class DbSession(Base):
mri_qc_last_change_time : Mapped[Optional[datetime]] = mapped_column('MRIQCLastChange')
mri_caveat : Mapped[str] = mapped_column('MRICaveat')
language_id : Mapped[Optional[int]] = mapped_column('languageID')

candidate : Mapped['db_candidate.DbCandidate'] = relationship('DbCandidate', back_populates='sessions')
site : Mapped['db_site.DbSite'] = relationship('DbSite')
23 changes: 23 additions & 0 deletions python/lib/db/model/site.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from typing import Optional
from sqlalchemy.orm import Mapped, mapped_column
from lib.db.base import Base


class DbSite(Base):
__tablename__ = 'psc'

id : Mapped[int] = mapped_column('CenterID', primary_key=True)
name : Mapped[str] = mapped_column('Name')
area : Mapped[Optional[str]] = mapped_column('PSCArea')
address : Mapped[Optional[str]] = mapped_column('Address')
city : Mapped[Optional[str]] = mapped_column('City')
state_id : Mapped[Optional[int]] = mapped_column('StateID')
zip : Mapped[Optional[str]] = mapped_column('ZIP')
phone_1 : Mapped[Optional[str]] = mapped_column('Phone1')
phone_2 : Mapped[Optional[str]] = mapped_column('Phone2')
contact_1 : Mapped[Optional[str]] = mapped_column('Contact1')
contact_2 : Mapped[Optional[str]] = mapped_column('Contact2')
alias : Mapped[str] = mapped_column('Alias')
mri_alias : Mapped[str] = mapped_column('MRI_alias')
account : Mapped[Optional[str]] = mapped_column('Account')
study_site : Mapped[Optional[str]] = mapped_column('Study_site')
17 changes: 17 additions & 0 deletions python/lib/db/query/project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

from sqlalchemy import select
from sqlalchemy.orm import Session as Database

from lib.db.model.project_cohort import DbProjectCohort


def try_get_project_cohort_with_project_id_cohort_id(db: Database, project_id: int, cohort_id: int):
"""
Get a project cohort relation from the database using its project ID and candidate ID, or
return `None` if no relation is found.
"""

return db.execute(select(DbProjectCohort)
.where(DbProjectCohort.project_id == project_id)
.where(DbProjectCohort.cohort_id == cohort_id)
).scalar_one_or_none()
17 changes: 17 additions & 0 deletions python/lib/db/query/session.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

from sqlalchemy import select
from sqlalchemy.orm import Session as Database

from lib.db.model.session import DbSession


def try_get_session_with_cand_id_visit_label(db: Database, cand_id: int, visit_label: str):
"""
Get a session from the database using its candidate CandID and visit label, or return `None`
if no session is found.
"""

return db.execute(select(DbSession)
.where(DbSession.cand_id == cand_id)
.where(DbSession.visit_label == visit_label)
).scalar_one_or_none()
20 changes: 20 additions & 0 deletions python/lib/db/query/site.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from sqlalchemy import select
from sqlalchemy.orm import Session as Database

from lib.db.model.candidate import DbCandidate
from lib.db.model.session import DbSession
from lib.db.model.site import DbSite


def try_get_site_with_psc_id_visit_label(db: Database, psc_id: str, visit_label: str):
"""
Get a session from the database using its candidate CandID and visit label, or return `None`
if no session is found.
"""

return db.execute(select(DbSite)
.join(DbSession.site)
.join(DbSession.candidate)
.where(DbCandidate.psc_id == psc_id)
.where(DbSession.visit_label == visit_label)
).scalar_one_or_none()
Loading

0 comments on commit c1aab1f

Please sign in to comment.