Exemplo n.º 1
0
    def login():
        """
        Logs into LORIS using the stored credential. Must use PyCurl as Requests is not working.
        :return: BOOL if or not it is successful. also, the JSON token that is necessary to conduct further transactions.
        """

        from DICOMTransit.settings import config_get

        username = config_get("LORISusername")
        password = config_get("LORISpassword")

        data = json.dumps({"username": username, "password": password})

        # Login URL
        url = config_get("LORISurl")

        if type(url) is furl:
            updated_url = url.url + "login"
        else:
            updated_url = url + "login"

        # requests style login # NOT WORKING!
        r = requests.post(updated_url, data=data)

        logger.debug(str(r.status_code) + r.reason)

        response_json = r.json()

        return LORIS_helper.is_response(r.status_code, 200), response_json.get("token")
Exemplo n.º 2
0
def get_dev_orthanc_credentials() -> orthanc_credential:
    """
    Obtain the Development Orthanc instance credential
    :return:
    """
    from DICOMTransit.settings import config_get

    url = config_get("DevOrthancIP")
    user = config_get("DevOrthancUser")
    password = config_get("DevOrthancPassword")
    return orthanc_credential(url, user, password)
Exemplo n.º 3
0
def get_prod_orthanc_credentials() -> orthanc_credential:
    """
    Obtain the Production Orthanc instance credential
    :return:
    """
    from DICOMTransit.settings import config_get

    url = config_get("ProdOrthancIP")
    user = config_get("ProdOrthancUser")
    password = config_get("ProdOrthancPassword")
    return orthanc_credential(url, user, password)
Exemplo n.º 4
0
def append_StudyUID(MRN: int, StudyUID: str):
    """
    Update record with proper completion status which STUDY has particular MRN
    :param MRN:
    :return:
    """

    # convert new study UID to list.
    list_StudyUID_single_new = [StudyUID]
    database_path = config_get("LocalDatabasePath")

    # obtain
    list_StudyUID_existing_ones = get_StudyUIDs(MRN)
    if list_StudyUID_existing_ones is None:
        list_StudyUID_total = list_StudyUID_single_new
    else:
        list_StudyUID_total = list_StudyUID_existing_ones + list_StudyUID_single_new

    # JSON dumps.
    json_seriesUID = json.dumps(list_StudyUID_total)

    # Update the MRN record with StudyUID
    LocalDB_query.update_entry(
        database_path,
        CNBP_blueprint.table_name,
        CNBP_blueprint.keyfield,
        MRN,
        "StudyUID",
        json_seriesUID,
    )
Exemplo n.º 5
0
    def putCNBPDICOM(token, endpoint, imaging_data, isPhantom: bool = False):
        """
        Put some data to a LORIS end point.
        :param token:
        :param endpoint:
        :param imaging_data:
        :param isPhantom: whether the upload is a phantom data or not.
        :return: bool on if request is successful, r for the request (CAN BE NULL for 201 based requests)
        """

        logger.debug(f"Uploading Imaging data to: {endpoint}")

        url = config_get("LORISurl")
        updatedurl = url + endpoint

        if isPhantom:
            HEADERS = {"Authorization": f"token {token}", "X-Is-Phantom": "1"}
        else:
            HEADERS = {"Authorization": f"token {token}", "X-Is-Phantom": "0"}

        with requests.Session() as s:
            s.headers.update(HEADERS)
            r = s.put(updatedurl, data=imaging_data)
            logger.debug(f"Put Result: {str(r.status_code)} {r.reason}")

            return r.status_code, r
Exemplo n.º 6
0
def get_CNNID(MRN: int) -> Optional[List[str]]:
    """
    Assuming the MRN exist, get the CNNIDs of all scans that ever past through here.
    :param MRN: the MRN to look for
    :return: the CNBPID associated with that particular MRN number.
    """
    # Load local database from .env file
    database_path = config_get("LocalDatabasePath")
    MRN_exist_in_database, KeyRecords = LocalDB_query.check_value(
        database_path, CNBP_blueprint.table_name, CNBP_blueprint.keyfield, MRN)
    if MRN_exist_in_database is False:
        return None

    # Only ONE record per MRN.
    assert len(KeyRecords) == 1
    CNNID_index = LocalDB_query.check_header_index(database_path,
                                                   CNBP_blueprint.table_name,
                                                   "CNNID")

    # Load the json and return the variable structure
    json_CNNIDs = KeyRecords[0][CNNID_index]
    if json_CNNIDs is None:
        logger.warning("No existing CNNID Data information found.")
        return None
    list_CNNIDs = json.loads(json_CNNIDs)

    return list_CNNIDs
Exemplo n.º 7
0
def trigger_dicom_insert(scans):
    """ 
    Triggers insertion of uploaded dicom files that are not yet inserted into
    Loris.
    :param scans: An array of dictionaries that represent the params for each
    dicom file to insert
    :returns:
    """
    # @todo: 2018-11-30T133155EST should clean up here and ensure loading the URL using .env.
    # Create a dictionary with the required key 'dicoms'. Key value is scans
    p = {"dicoms": scans}

    # Convert to json.
    # See https://pythonspot.com/json-encoding-and-decoding-with-python/
    tmp = json.dumps(p, ensure_ascii=False)

    # Put json into dictionary as a value of the required key 'file_data'
    payload = {"file_data": tmp}
    print(payload)

    # Need to fix the load_dotenv call. Currently not getting trigger url

    trigger_dicom_insertion_url = config_get("InsertionAPI")

    # Trigger insertion by doing HTTP POST of payload to endpoint
    s = requests.post(trigger_dicom_insertion_url, data=payload)
    # print(trigger_dicom_insertion_url)

    print(s.text)
Exemplo n.º 8
0
def append_SeriesUID(MRN: int, SeriesUID: List[str]):
    """
    Append record with SeriesUID list. which has particular MRN
    :param MRN:
    :return:
    """
    database_path = config_get("LocalDatabasePath")
    existing_series_UID = get_SeriesUIDs(MRN)
    if existing_series_UID is None:
        total_series_UID = SeriesUID
    else:
        total_series_UID = existing_series_UID + SeriesUID

    # JSON dumps.
    json_seriesUID = json.dumps(total_series_UID)

    # Update the MRN record with SeriesUID
    LocalDB_query.update_entry(
        database_path,
        CNBP_blueprint.table_name,
        CNBP_blueprint.keyfield,
        MRN,
        "SeriesUID",
        json_seriesUID,
    )
Exemplo n.º 9
0
    def increaseTimepoint(token: str, DCCID: int) -> (bool, str):
        """
        Increment the existing timepoint by check if DCCID existed, then get the latest one, then increment its number by creating a new timepoint.
        :param token: auth token
        :param DCCID: the DCCID of the existing subject.
        :return: if creation request is successful, and what label is actually created.
        """
        # @todo: must handle the special edge case where timepoint reach V10 etc where two or three more digits are there!

        timepoint_label = ""

        # ensure valid input and subject actually exist.
        assert LORIS_validation.validate_DCCID(DCCID)
        subject_exist, _ = LORIS_candidates.checkDCCIDExist(token, DCCID)
        if not subject_exist:
            return False, None

        latest_timepoint = LORIS_timepoint.findLatestTimePoint(token, DCCID)

        if latest_timepoint is None:
            success = LORIS_timepoint.createTimepoint(token, DCCID, "V1")
            timepoint_label = "V1"
        else:
            visit_number = LORIS_timepoint.visit_number_extraction(
                latest_timepoint)
            new_visit_number = int_incrementor(visit_number)

            prefix = config_get("timepoint_prefix")

            timepoint_label = prefix + str(new_visit_number)

            success = LORIS_timepoint.createTimepoint(token, DCCID,
                                                      timepoint_label)

        return success, timepoint_label
Exemplo n.º 10
0
    def UnpackNewData(self):
        """
        Properly create the DICOM package and update its relevant basic informations like files reference and UIDs
        :return:
        """

        path_temp_zip = config_get("ZipPath")

        # Properly set the DICOM_package.
        temporary_folder = DICOMTransit.orthanc.API.unpack_subject_zip(
            self.DICOM_zip, path_temp_zip)

        # Orthanc files are GUARNTEED to have consistency name and ID so no need to check that. A cursory DICOM check is good enough for performance reasons.

        self.DICOM_package = DICOMPackage(
            temporary_folder,
            consistency_check=False)  # Series UID is extracted at this time.
        # Update unique UID information to help discriminate existing scans.
        # self.DICOM_package.update_sUID()

        self.DICOM_package.project = (
            "loris"
        )  # fixme: this is a place holder. This neeeds to be dyanmiclly updated.

        # Update the self.files to be scrutinized
        self.files.clear()
        self.files = self.DICOM_package.get_dicom_files(
            consistency_check=False)  # consistency do not need to be checked.

        logger.info("Successfully unpacked the data downloaded.")
Exemplo n.º 11
0
    def test_env(self):
        """
        This unit test ensures that the variables in .env is the same as the ones specified in the blueprint of the schema
        :return:
        """
        # Load the list of predefined variables as defined in the schema.
        list_variables = CNBP_blueprint.dotenv_variables

        # loop through each one and then attempt to load and validate them.
        for variable in list_variables:
            assert config_get(variable) is not None
Exemplo n.º 12
0
def change_to_zip_dir():
    # Load the name of the storage folder from the configuration file.
    folder_to_zip = config_get("ZipPath")

    # Find the root fo the project where the zip storage is related to the location of the current DICOM directory.
    project_root = get_abspath(__file__, 2)

    # Create absolute path to the folder to be zipped
    zip_path = os.path.join(project_root, folder_to_zip)
    #
    os.chdir(zip_path)
Exemplo n.º 13
0
def old_upload(local_path):
    """
    OBSOLETE Upload file to incoming folder.
    :param local_path:
    :return:
    """

    ProxyIP = config_get("ProxyIP")
    ProxyUsername = config_get("ProxyUsername")
    ProxyPassword = config_get("ProxyPassword")
    LORISHostIP = config_get("LORISHostIP")
    LORISHostUsername = config_get("LORISHostUsername")
    LORISHostPassword = config_get("LORISHostPassword")

    Client = LORIS_helper.getProxySSHClient(
        ProxyIP,
        ProxyUsername,
        ProxyPassword,
        LORISHostIP,
        LORISHostUsername,
        LORISHostPassword,
    )

    file_name = os.path.basename(local_path)
    LORIS_helper.uploadThroughClient(Client, "//data/incoming/" + file_name,
                                     local_path)
Exemplo n.º 14
0
def propose_CNBPID(DICOM_protocol: str):
    """
    This function takes in a string that is representative of the DICOM acquisition study protocol, and propose a CNBPID composed of two parts:
        Institution_ID (from the .env configuration file)
        Project_ID (inferred from the protocol and incremented
        SubjectCount (kept track by localDB.
    :param DICOM_protocol:
    :return:
    """
    # Get and retrieve  institution_ID
    InstitionID = config_get("institutionID")

    import DICOMTransit.DICOM.API

    # Check ProjectID string and then return the ProjectID;
    ProjectID = DICOMTransit.DICOM.API.study_validation(DICOM_protocol)

    # Use those two pieces of information to form a partial query pattern that can run on the SQLite
    partial_search_input = InstitionID + ProjectID

    DB_path = config_get("LocalDatabasePath")

    # Partial match search records: (currently has SQL error).
    success, matched_records = LocalDB_query.check_partial_value(
        DB_path, CNBP_blueprint.table_name, "CNBPID", partial_search_input)

    # Default subject ID when no match is found.
    latest_subject_ID = "0000001"

    if matched_records is None or len(matched_records) == 0:
        # no previous subjects found. Use default value.
        pass
    else:
        latest_subject_ID = check_all_existing_records(matched_records)

    # Combined all the parts to return the proposed CNBPID

    proposed_CNBPID = InstitionID + ProjectID + latest_subject_ID

    return proposed_CNBPID
Exemplo n.º 15
0
    def validate_instutitionID(input_institutionID: str) -> bool:
        """
        Check if the institution ID is compliant per the .env specification. String must STRICTLY match.
        :param input_institutionID:
        :return:
        """
        # Parse from the .env standardization
        InsitituionID = config_get("institutionID")

        # Check if institution ID matches
        if not (input_institutionID == InsitituionID):
            return False
        else:
            return True
Exemplo n.º 16
0
    def deleteCandidateCNBP(DCCID, PSCID):
        # @todo: this should really be done through API. But Currently LORIS does not offer such API.
        # NOTE! If you EVER get NULL coalesce not recognized error, make sure that the PHP version being called from
        # the SSH session is 7+ or else. We had a major issue where the PHP version from SSH session being LOWER
        # than the .bashrc profile imported edition. Also keep in mind that EVEN if .bashrc import this, it MOST LIKELY
        # will not apply to the SSH session!

        LORISHostPassword = config_get("LORISHostPassword")
        LORISHostUsername = config_get("LORISHostUsername")
        LORISHostIP = config_get("LORISHostIP")
        DeletionScript = config_get("DeletionScript")

        # NOTE! If you EVER get NULL coalesce not recognized error, make sure that the PHP version being called from
        # the SSH session is 7+ or else. We had a major issue where the PHP version from SSH session being LOWER
        # than the bashrc profile imported edition. Also keep in mind that EVEN if .bashrc import this, it MOST LIKELY
        # will not apply to the SSH session!

        command_string = f"/opt/rh//rh-php70/root/usr/bin/php {DeletionScript} delete_candidate {str(DCCID)} {PSCID} confirm"

        logger.debug(command_string)

        # Establish connection to client.
        Client = LORIS_helper.getProxySSHClient(
            ProxyIP,
            ProxyUsername,
            ProxyPassword,
            LORISHostIP,
            LORISHostUsername,
            LORISHostPassword,
        )

        # Execute the command
        LORIS_helper.triggerCommand(Client, command_string)

        # Close the client.
        Client.close()
Exemplo n.º 17
0
def check_MRN(MRN: int) -> bool:
    """
    Return true if the MRN exist within the current database
    :return:
    """

    # Load local database from .env file
    database_path = config_get("LocalDatabasePath")

    # Store MRN in database.
    MRN_exist_in_database, _ = LocalDB_query.check_value(
        database_path, CNBP_blueprint.table_name, CNBP_blueprint.keyfield, MRN)
    if MRN_exist_in_database:
        logger.info("MRN found to exist at local database")
    return MRN_exist_in_database
Exemplo n.º 18
0
    def checkPSCIDExist(token, proposed_PSCID):
        """
        Check if Site/Study already contain the PSCID
        @todo: mostly obsolete as now PSCID is completely generated server side.
        HOWEVER! THere are /candidate_list that can once for all get

        :param token:
        :param proposed_PSCID:
        :return: bool for connection, bool on if such PSCID (INSTITUTIONID + PROJECTID + SUBJECTID) exist already.
        """

        logger.debug("Checking if PSCID exist: " + proposed_PSCID)
        institution_check = config_get("institutionID")

        # Get list of projects
        response_success, loris_project = LORIS_query.getCNBP(
            token, r"projects/loris")
        if not response_success:
            return response_success, None

        # Get list of candidates (Candidates in v0.0.1)
        candidates = loris_project.get("Candidates")
        logger.debug(candidates)

        for (DCCID) in (
                candidates
        ):  # these candidates should really be only from the same ID regions.
            response_success, candidate_json = LORIS_query.getCNBP(
                token, r"candidates/" + DCCID)
            if not response_success:
                return response_success, False
            # Each site following the naming convention should have SITE prefix and PI prefix and PROJECT prefix to avoid collision

            # A site check here.
            candidate_meta = candidate_json.get("Meta")
            candidate_pscID = candidate_meta.get("PSCID")

            # Not gonna check is institution ID doesn't even match.
            if candidate_pscID[0:3] != institution_check:
                continue

            elif candidate_pscID == proposed_PSCID:
                return response_success, True

                # latest_timepoint = findLatestTimePoint(DCCID)

        return True, False
Exemplo n.º 19
0
    def createCandidate(token, project, birth_date, gender):
        """
        Create a candidate with a provided project information. This assumes that the PSCID and DCCIDs are automaticly assigned!
        :param token
        :param birth_date: Birth date MUST Be in YYYY-MM-DD format!
        :param gender: Gender must be Male or Female!
        :param project:
        :return: DCCID
        """

        logger.debug(f"Creating CNBP Candidates belong to project: {project}")

        Candidate = {}
        from DICOMTransit.LORIS.validate import LORIS_validation

        if not LORIS_validation.validate_birth_date_loris(
                birth_date
        ) or not LORIS_validation.validate_gender(
                gender
        ):  # not LORIS_validation.validate_project(project) or #@todo fix this project validation part during creation.
            logger.error(
                "Non-compliant PSCID component detected. Aborting PSCID creation "
            )
            return False, None

        Candidate["Project"] = project
        Candidate["EDC"] = birth_date
        # Candidate['PSCID'] = proposed_PSCID Now auto sequence.
        Candidate["DoB"] = birth_date
        Candidate["Gender"] = gender
        Candidate["Site"] = config_get("institutionName")

        data = {"Candidate": Candidate}

        data_json = json.dumps(data)

        response_code, response = LORIS_query.postCNBP(token, "candidates/",
                                                       data_json)
        if not LORIS_helper.is_response(response_code, 201):
            return False, None, None
        elif response is not None:  # only try to decode if response is not empty!
            response_json = response.json()
            meta = response_json.get("Meta")
            CandID = meta.get("CandID")
            return True, CandID
        else:
            return False, None
Exemplo n.º 20
0
def set_DCCID(MRN: int, DCCID: int):
    """
    Update record with proper DCCID which has particular MRN
    :param MRN:
    :return:
    """
    database_path = config_get("LocalDatabasePath")

    # Update the MRN record with DCCID
    LocalDB_query.update_entry(
        database_path,
        CNBP_blueprint.table_name,
        CNBP_blueprint.keyfield,
        MRN,
        "DCCID",
        DCCID,
    )
Exemplo n.º 21
0
def set_timepoint(MRN: int, Timepoint: str):
    """
    Update record with proper Timepoint which has particular MRN
    :param MRN:
    :return:
    """
    database_path = config_get("LocalDatabasePath")

    # Update the MRN record with Timepoint
    LocalDB_query.update_entry(
        database_path,
        CNBP_blueprint.table_name,
        CNBP_blueprint.keyfield,
        MRN,
        "Timepoint",
        Timepoint,
    )
Exemplo n.º 22
0
    def zip(self):
        """

        :return:
        """

        # load system default ZIP storage path.
        zip_storage_path = config_get("ZipPath")

        # Change to the storage folder before carrying out the zip operation.
        os.chdir(zip_storage_path)
        zip_with_name(self.dicom_folder, self.zipname)

        # update zip location, this is the ABSOLUTE path.
        self.zip_location = os.path.join(zip_storage_path,
                                         self.zipname + ".zip")
        self.is_zipped = True
Exemplo n.º 23
0
def get_list_CNBPID() -> List[str]:
    """
    Return a list of all CNBPID (Key) from the database.
    :return:
    """
    # Load local database from .env file
    database_path = config_get("LocalDatabasePath")

    list_CNBPID = []

    success, result_rows = LocalDB_query.get_all(database_path,
                                                 CNBP_blueprint.table_name,
                                                 "CNBPID")

    for row in result_rows:
        list_CNBPID.append(row[0])  # MRN is the first variable requested.

    return list_CNBPID
Exemplo n.º 24
0
def get_scan_date(MRN: int) -> Optional[str]:
    """
    Assuming the MRN exist, get the MRNID.
    :param MRN: the MRN to look for
    :return: the CNBPID associated with that particular MRN number.
    """
    # Load local database from .env file
    database_path = config_get("LocalDatabasePath")
    MRN_exist_in_database, KeyRecords = LocalDB_query.check_value(
        database_path, CNBP_blueprint.table_name, CNBP_blueprint.keyfield, MRN)
    if MRN_exist_in_database is False:
        return None

    # Only ONE record per MRN, even if there are multiple timepoint. We keep the latest one.
    assert len(KeyRecords) == 1
    date_header_index = LocalDB_query.check_header_index(
        database_path, CNBP_blueprint.table_name, "Date")
    scan_date = KeyRecords[0][date_header_index]
    return scan_date
Exemplo n.º 25
0
def get_DCCID(MRN: int) -> Optional[str]:
    """
    Assuming the MRN exist, get the MRNID.
    :param MRN: the MRN to look for
    :return: the CNBPID associated with that particular MRN number.
    """
    # Load local database from .env file
    database_path = config_get("LocalDatabasePath")
    MRN_exist_in_database, KeyRecords = LocalDB_query.check_value(
        database_path, CNBP_blueprint.table_name, CNBP_blueprint.keyfield, MRN)
    if MRN_exist_in_database is False:
        return None

    # Only ONE record per MRN.
    assert len(KeyRecords) == 1
    dcc_header_index = LocalDB_query.check_header_index(
        database_path, CNBP_blueprint.table_name, "DCCID")

    return KeyRecords[0][dcc_header_index]
Exemplo n.º 26
0
    def validate_projectID(input_projectID: str) -> bool:
        """
        Provide any string, and it checkss again he dotenv for the proper project KEY which correspond to the ID.
        DICOM/API has the function to actually retrieve the relevant key, after calling this function.
        :param input_projectID:
        :return:
        """

        # Load ProjectIDs from the environment.
        projectID_dictionary_json: str = config_get("projectID_dictionary")
        projectID_list = json.loads(projectID_dictionary_json)

        # check if project ID is in the projectID list via a dictionary search
        key = dictionary_search(projectID_list, input_projectID)

        if key is not None:
            return True
        else:
            return False
Exemplo n.º 27
0
def set_CNNIDs(MRN: int, CaseIDs: List[int]):
    """
    Update record with proper CNN which has particular MRN
    :param MRN:
    :return:
    """
    database_path = config_get("LocalDatabasePath")

    # JSON dumps.
    json_CaseIDs = json.dumps(CaseIDs)

    # Update the MRN record with DCCID
    LocalDB_query.update_entry(
        database_path,
        CNBP_blueprint.table_name,
        CNBP_blueprint.keyfield,
        MRN,
        "CNNID",
        json_CaseIDs,
    )
Exemplo n.º 28
0
def get_list_MRN() -> List[int]:
    """
    Return a list_return of all MRN from the database.
    :return:
    """
    # Load local database from .env file
    database_path = config_get("LocalDatabasePath")

    list_MRN = []

    success, result_rows = LocalDB_query.get_all(database_path,
                                                 CNBP_blueprint.table_name,
                                                 "MRN")

    for row in result_rows:
        list_MRN.append(row[0])  # MRN is the first variable requested.

    return (
        list_MRN
    )  # @todo: verify this is in integer? or string as that has dire consequences.
Exemplo n.º 29
0
def study_validation(study: str) -> str:
    """
    Given a string read from the DICOM studies field, check it against the project ID dictionary to see if any of the project belongs.
    :param study:
    :return: PROJECT or NONE
    """
    import json

    # It checks if the key exist first.
    if LORIS_validation.validate_projectID(study) is False:
        return False

    projectID_dictionary_json: str = config_get("projectID_dictionary")
    projectID_list = json.loads(projectID_dictionary_json)

    # check if project ID is in the projectID list.
    key = dictionary_search(projectID_list, study)
    # @todo: what if the key does not exist?

    return key
Exemplo n.º 30
0
    def getCNBP(token, endpoint):
        """
        Get from a CNBP LORIS database endpoint
        :param token:
        :param endpoint:
        :return: bool on if such PSCID (INSTITUTIONID + PROJECTID + SUBJECTID) exist already.
        """

        logger.debug(f"Getting LORIS endpoint: {endpoint} at")
        url = config_get("LORISurl")
        updatedurl = urljoin(url, endpoint)
        logger.debug(updatedurl)
        HEADERS = {"Authorization": "token {}".format(token)}

        with requests.Session() as s:
            s.headers.update(HEADERS)
            r = s.get(updatedurl)
            logger.debug(f"Get Result: {str(r.status_code)} {r.reason}")

            return r.status_code, r.json()