qmenta-client 1.1.dev1324__py3-none-any.whl → 1.1.dev1337__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.
- qmenta/client/Project.py +250 -111
- {qmenta_client-1.1.dev1324.dist-info → qmenta_client-1.1.dev1337.dist-info}/METADATA +1 -1
- {qmenta_client-1.1.dev1324.dist-info → qmenta_client-1.1.dev1337.dist-info}/RECORD +4 -4
- {qmenta_client-1.1.dev1324.dist-info → qmenta_client-1.1.dev1337.dist-info}/WHEEL +0 -0
qmenta/client/Project.py
CHANGED
|
@@ -200,14 +200,45 @@ def wrap_search_criteria(search_criteria={}):
|
|
|
200
200
|
return modality, tags, file_metadata
|
|
201
201
|
|
|
202
202
|
|
|
203
|
+
def convert_qc_value_to_qcstatus(value):
|
|
204
|
+
"""
|
|
205
|
+
Convert input string to QCStatus class.
|
|
206
|
+
|
|
207
|
+
Parameters
|
|
208
|
+
----------
|
|
209
|
+
value : str
|
|
210
|
+
Value to convert.
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
QCStatus or Bool
|
|
215
|
+
QCStatus.PASS, QCStatus.FAIL or QCStatus.UNDETERMINED
|
|
216
|
+
False if the Value cannot be convered.
|
|
217
|
+
"""
|
|
218
|
+
logger = logging.getLogger(logger_name)
|
|
219
|
+
if value == "pass":
|
|
220
|
+
return QCStatus.PASS
|
|
221
|
+
elif value == "fail":
|
|
222
|
+
return QCStatus.FAIL
|
|
223
|
+
elif value == "":
|
|
224
|
+
return QCStatus.UNDERTERMINED
|
|
225
|
+
else:
|
|
226
|
+
logger.error(f"The input value '{value}' cannot be converted "
|
|
227
|
+
f"to class QCStatus.")
|
|
228
|
+
return False
|
|
229
|
+
|
|
230
|
+
|
|
203
231
|
class QCStatus(Enum):
|
|
204
232
|
"""
|
|
205
233
|
Enum with the following options:
|
|
206
|
-
FAIL, PASS
|
|
234
|
+
FAIL, PASS, UNDERTERMINED
|
|
235
|
+
that are the possible options for the QA/QC status of both
|
|
236
|
+
analysis and session.
|
|
207
237
|
"""
|
|
208
238
|
|
|
209
239
|
PASS = "pass"
|
|
210
240
|
FAIL = "fail"
|
|
241
|
+
UNDERTERMINED = ""
|
|
211
242
|
|
|
212
243
|
|
|
213
244
|
class Project:
|
|
@@ -1841,7 +1872,8 @@ class Project:
|
|
|
1841
1872
|
modalities.append(modality)
|
|
1842
1873
|
return modalities
|
|
1843
1874
|
|
|
1844
|
-
def
|
|
1875
|
+
def set_qc_status_analysis(self, analysis_id,
|
|
1876
|
+
status=QCStatus.UNDERTERMINED, comments=""):
|
|
1845
1877
|
"""
|
|
1846
1878
|
Changes the analysis QC status.
|
|
1847
1879
|
|
|
@@ -1850,150 +1882,257 @@ class Project:
|
|
|
1850
1882
|
analysis_id : int
|
|
1851
1883
|
Analysis ID number
|
|
1852
1884
|
status : QCStatus
|
|
1853
|
-
QCStatus.PASS or QCStatus.
|
|
1885
|
+
QCStatus.PASS, QCStatus.FAIL or QCStatus.UNDETERMINED
|
|
1854
1886
|
comments : str, optional
|
|
1855
|
-
Additional comments explaining why the
|
|
1856
|
-
|
|
1887
|
+
Additional comments explaining why the reasoning of setting the
|
|
1888
|
+
QC status to such state.
|
|
1889
|
+
|
|
1890
|
+
Returns
|
|
1891
|
+
-------
|
|
1892
|
+
bool
|
|
1893
|
+
True if the QC status was correctly changed, False otherwise.
|
|
1857
1894
|
"""
|
|
1895
|
+
|
|
1858
1896
|
logger = logging.getLogger(__name__)
|
|
1859
1897
|
logger.info(f"Setting QC status to {status}: {comments}")
|
|
1860
1898
|
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1899
|
+
if not isinstance(status, QCStatus):
|
|
1900
|
+
raise ValueError("Status should be type 'QCStatus'.")
|
|
1901
|
+
|
|
1902
|
+
try:
|
|
1903
|
+
analysis_id = str(int(analysis_id))
|
|
1904
|
+
except ValueError:
|
|
1905
|
+
raise ValueError(f"analysis_id: '{analysis_id}' not valid. " f"Must be convertible to int.")
|
|
1906
|
+
|
|
1907
|
+
try:
|
|
1908
|
+
platform.parse_response(
|
|
1909
|
+
platform.post(
|
|
1910
|
+
auth=self._account.auth,
|
|
1911
|
+
endpoint="projectset_manager/set_qa_status",
|
|
1912
|
+
data={"item_ids": analysis_id, "status": status.value,
|
|
1913
|
+
"comments": str(comments), "entity": "analysis"},
|
|
1914
|
+
)
|
|
1866
1915
|
)
|
|
1867
|
-
|
|
1916
|
+
except Exception:
|
|
1917
|
+
logger.error(f"It was not possible to change the QC status of"
|
|
1918
|
+
f"Analysis ID: {analysis_id}")
|
|
1919
|
+
return False
|
|
1920
|
+
return True
|
|
1868
1921
|
|
|
1869
|
-
def
|
|
1922
|
+
def set_qc_status_subject(self, patient_id,
|
|
1923
|
+
status=QCStatus.UNDERTERMINED, comments=""):
|
|
1870
1924
|
"""
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1925
|
+
Changes the QC status of a Patient ID (equivalent to a
|
|
1926
|
+
Subject ID/Session ID).
|
|
1927
|
+
|
|
1928
|
+
Parameters
|
|
1929
|
+
----------
|
|
1930
|
+
patient_id : int
|
|
1931
|
+
Patient ID number of the session.
|
|
1932
|
+
status : QCStatus
|
|
1933
|
+
QCStatus.PASS, QCStatus.FAIL or QCStatus.UNDETERMINED
|
|
1934
|
+
comments : str, optional
|
|
1935
|
+
Additional comments explaining why the reasoning of setting the
|
|
1936
|
+
QC status to such state.
|
|
1874
1937
|
|
|
1938
|
+
Returns
|
|
1939
|
+
-------
|
|
1940
|
+
bool
|
|
1941
|
+
True if the QC status was correctly changed, False otherwise.
|
|
1875
1942
|
"""
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1943
|
+
logger = logging.getLogger(__name__)
|
|
1944
|
+
logger.info(f"Setting QC status to {status}: {comments}")
|
|
1945
|
+
|
|
1946
|
+
if not isinstance(status, QCStatus):
|
|
1947
|
+
raise ValueError("Status should be type 'QCStatus'.")
|
|
1948
|
+
|
|
1949
|
+
try:
|
|
1950
|
+
patient_id = str(int(patient_id))
|
|
1951
|
+
except ValueError:
|
|
1952
|
+
raise ValueError(f"'patient_id': '{patient_id}' not valid. " f"Must be convertible to int.")
|
|
1953
|
+
|
|
1954
|
+
try:
|
|
1955
|
+
platform.parse_response(
|
|
1956
|
+
platform.post(
|
|
1957
|
+
auth=self._account.auth,
|
|
1958
|
+
endpoint="projectset_manager/set_qa_status",
|
|
1959
|
+
data={"item_ids": patient_id, "status": status.value,
|
|
1960
|
+
"comments": str(comments), "entity": "patients"},
|
|
1961
|
+
)
|
|
1882
1962
|
)
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
][0]
|
|
1889
|
-
to_return = to_return["qa_status"], to_return["qa_comments"]
|
|
1890
|
-
except IndexError:
|
|
1891
|
-
# Handle the case where no matching analysis is found
|
|
1892
|
-
to_return = None
|
|
1893
|
-
except Exception as e:
|
|
1894
|
-
# Handle other potential exceptions
|
|
1895
|
-
print(f"An error occurred: {e}")
|
|
1896
|
-
to_return = None
|
|
1897
|
-
else:
|
|
1898
|
-
raise Exception(f"Must specify {patient_secret_name} and {ssid} or {analysis_id}.")
|
|
1899
|
-
return to_return
|
|
1963
|
+
except Exception:
|
|
1964
|
+
logger.error(f"It was not possible to change the QC status of"
|
|
1965
|
+
f"Patient ID: {patient_id}")
|
|
1966
|
+
return False
|
|
1967
|
+
return True
|
|
1900
1968
|
|
|
1901
|
-
def
|
|
1902
|
-
self,
|
|
1903
|
-
script_name,
|
|
1904
|
-
version,
|
|
1905
|
-
n_times,
|
|
1906
|
-
in_container_id=None,
|
|
1907
|
-
analysis_name=None,
|
|
1908
|
-
analysis_description=None,
|
|
1909
|
-
ignore_warnings=False,
|
|
1910
|
-
settings=None,
|
|
1911
|
-
tags=None,
|
|
1912
|
-
preferred_destination=None,
|
|
1913
|
-
):
|
|
1969
|
+
def get_qc_status_analysis(self, analysis_id):
|
|
1914
1970
|
"""
|
|
1915
|
-
|
|
1916
|
-
settings.
|
|
1971
|
+
Gets the QC status of an anlaysis provided its Analysis ID.
|
|
1917
1972
|
|
|
1918
1973
|
Parameters
|
|
1919
1974
|
----------
|
|
1920
|
-
|
|
1921
|
-
ID
|
|
1922
|
-
version: str
|
|
1923
|
-
Version of the script to be run, examples: 1.0, 5.3.4
|
|
1924
|
-
n_times: int
|
|
1925
|
-
Number of analyses to be scheduled
|
|
1926
|
-
in_container_id : int or dict
|
|
1927
|
-
The ID of the container to get the data from, or a dictionary with
|
|
1928
|
-
one or more container names as keys, and IDs as values.
|
|
1929
|
-
Input container names are generally prefixed with "input\\_".
|
|
1930
|
-
If not, the prefix will be automatically added.
|
|
1931
|
-
analysis_name : str
|
|
1932
|
-
Name of the analysis (optional)
|
|
1933
|
-
analysis_description : str
|
|
1934
|
-
Description of the analysis (optional)
|
|
1935
|
-
ignore_warnings : bool
|
|
1936
|
-
If False, warnings by server cause failure.
|
|
1937
|
-
settings : dict
|
|
1938
|
-
The input settings used to run the analysis.
|
|
1939
|
-
Use either settings or in_container_id. Input specification
|
|
1940
|
-
in the settings dict can be done by using the key "input".
|
|
1941
|
-
tags : list[str]
|
|
1942
|
-
The tags of the analysis.
|
|
1943
|
-
preferred_destination : str
|
|
1944
|
-
The machine on which to run the analysis
|
|
1975
|
+
analysis_id : int
|
|
1976
|
+
Analysis ID number
|
|
1945
1977
|
|
|
1946
|
-
|
|
1978
|
+
Returns
|
|
1947
1979
|
-------
|
|
1948
|
-
|
|
1949
|
-
|
|
1980
|
+
status : QCStatus or False
|
|
1981
|
+
QCStatus.PASS, QCStatus.FAIL or QCStatus.UNDETERMINED
|
|
1982
|
+
False if the status cannot be extracted.
|
|
1983
|
+
comments : str or False
|
|
1984
|
+
Comments defined in the QC Status.
|
|
1985
|
+
False if the comments cannot be extracted.
|
|
1950
1986
|
"""
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1987
|
+
try:
|
|
1988
|
+
search_criteria = {"id": analysis_id}
|
|
1989
|
+
to_return = self.list_analysis(search_criteria)
|
|
1990
|
+
return convert_qc_value_to_qcstatus(to_return[0]["qa_status"]), to_return[0]["qa_comments"]
|
|
1991
|
+
except IndexError:
|
|
1992
|
+
# Handle the case where no matching analysis is found
|
|
1993
|
+
logging.error(f"No analysis was found with such Analysis ID: '{analysis_id}'.")
|
|
1994
|
+
return False, False
|
|
1995
|
+
except Exception:
|
|
1996
|
+
# Handle other potential exceptions
|
|
1997
|
+
logging.error(f"It was not possible to extract the QC status"
|
|
1998
|
+
f"from Analysis ID: {analysis_id}")
|
|
1999
|
+
return False, False
|
|
2000
|
+
|
|
2001
|
+
def get_qc_status_subject(self, patient_id=None, subject_name=None,
|
|
2002
|
+
ssid=None):
|
|
2003
|
+
"""
|
|
2004
|
+
Gets the session QC status via the patient ID or the Subject ID
|
|
2005
|
+
and the Session ID.
|
|
2006
|
+
|
|
2007
|
+
Parameters
|
|
2008
|
+
----------
|
|
2009
|
+
patient_id : int
|
|
2010
|
+
Patient ID number of the session
|
|
2011
|
+
subject_name : string
|
|
2012
|
+
Subject ID of the session
|
|
2013
|
+
ssid : string
|
|
2014
|
+
Session ID
|
|
2015
|
+
|
|
2016
|
+
Returns
|
|
2017
|
+
-------
|
|
2018
|
+
status : QCStatus or False
|
|
2019
|
+
QCStatus.PASS, QCStatus.FAIL or QCStatus.UNDETERMINED
|
|
2020
|
+
False if the status cannot be extracted.
|
|
2021
|
+
comments : str or False
|
|
2022
|
+
Comments defined in the QC Status.
|
|
2023
|
+
False if the comments cannot be extracted.
|
|
2024
|
+
"""
|
|
2025
|
+
|
|
2026
|
+
if patient_id:
|
|
2027
|
+
try:
|
|
2028
|
+
patient_id = int(patient_id)
|
|
2029
|
+
except ValueError:
|
|
2030
|
+
raise ValueError(f"patient_id '{patient_id}' should be an "
|
|
2031
|
+
f"integer.")
|
|
2032
|
+
sessions = self.get_subjects_metadata(search_criteria={})
|
|
2033
|
+
session = [session for session in sessions if int(session["_id"]) == patient_id]
|
|
2034
|
+
if len(session) < 1:
|
|
2035
|
+
logging.error(f"No session was found with Patient ID: "
|
|
2036
|
+
f"'{patient_id}'.")
|
|
2037
|
+
return False, False
|
|
2038
|
+
return convert_qc_value_to_qcstatus(session[0]["qa_status"]), session[0]["qa_comments"]
|
|
2039
|
+
elif subject_name and ssid:
|
|
2040
|
+
session = self.get_subjects_metadata(
|
|
2041
|
+
search_criteria={
|
|
2042
|
+
"pars_patient_secret_name": f"string;{subject_name}",
|
|
2043
|
+
"pars_ssid": f"integer;eq|{ssid}",
|
|
2044
|
+
}
|
|
1964
2045
|
)
|
|
2046
|
+
if len(session) < 1:
|
|
2047
|
+
logging.error(f"No session was found with Subject ID: "
|
|
2048
|
+
f"'{subject_name}' and Session ID: '{ssid}'.")
|
|
2049
|
+
return False, False
|
|
2050
|
+
return convert_qc_value_to_qcstatus(session[0]["qa_status"]), session[0]["qa_comments"]
|
|
2051
|
+
else:
|
|
2052
|
+
raise ValueError("Either 'patient_id' or 'subject_name' and 'ssid'"
|
|
2053
|
+
" must not be empty.")
|
|
1965
2054
|
|
|
1966
|
-
def
|
|
2055
|
+
def set_project_pa_rules(self, rules_file_path, guidance_text=""):
|
|
1967
2056
|
"""
|
|
1968
|
-
|
|
1969
|
-
|
|
2057
|
+
Updates the active project's protocol adherence rules using the
|
|
2058
|
+
provided rules file.
|
|
1970
2059
|
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
2060
|
+
Parameters
|
|
2061
|
+
----------
|
|
2062
|
+
rules_file_path : str
|
|
2063
|
+
The file path to the JSON file containing the protocol
|
|
2064
|
+
adherence rules.
|
|
2065
|
+
guidance_text : str
|
|
2066
|
+
Description of the protocol adherence rules.
|
|
1974
2067
|
|
|
1975
|
-
Returns
|
|
1976
|
-
|
|
2068
|
+
Returns
|
|
2069
|
+
-------
|
|
2070
|
+
bool: True if the protocol adherence rules were set successfully,
|
|
2071
|
+
False otherwise.
|
|
1977
2072
|
"""
|
|
2073
|
+
logger = logging.getLogger(logger_name)
|
|
1978
2074
|
# Read the rules from the JSON file
|
|
1979
2075
|
try:
|
|
1980
2076
|
with open(rules_file_path, "r") as fr:
|
|
1981
2077
|
rules = json.load(fr)
|
|
1982
2078
|
except FileNotFoundError:
|
|
1983
|
-
|
|
2079
|
+
logger.error(f"Pprotocol adherence rule file '{rules_file_path}' "
|
|
2080
|
+
f"not found.")
|
|
1984
2081
|
return False
|
|
1985
2082
|
|
|
1986
2083
|
# Update the project's QA rules
|
|
1987
|
-
res = platform.post(
|
|
2084
|
+
res = platform.parse_response(platform.post(
|
|
1988
2085
|
auth=self._account.auth,
|
|
1989
2086
|
endpoint="projectset_manager/set_session_qa_requirements",
|
|
1990
2087
|
data={"project_id": self._project_id, "rules": json.dumps(rules), "guidance_text": guidance_text},
|
|
1991
|
-
)
|
|
2088
|
+
))
|
|
1992
2089
|
|
|
1993
|
-
if res.
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
2090
|
+
if not res.get("success") == 1:
|
|
2091
|
+
logger.error("There was an error setting up the protocol "
|
|
2092
|
+
"adherence rules.")
|
|
2093
|
+
logger.error(platform.parse_response(res))
|
|
2094
|
+
return False
|
|
2095
|
+
|
|
2096
|
+
return True
|
|
2097
|
+
|
|
2098
|
+
def get_project_pa_rules(self, rules_file_path):
|
|
2099
|
+
"""
|
|
2100
|
+
Retrive the active project's protocol adherence rules
|
|
2101
|
+
|
|
2102
|
+
Parameters
|
|
2103
|
+
----------
|
|
2104
|
+
rules_file_path : str
|
|
2105
|
+
The file path to the JSON file to store the protocol adherence
|
|
2106
|
+
rules.
|
|
2107
|
+
|
|
2108
|
+
Returns
|
|
2109
|
+
-------
|
|
2110
|
+
guidance_text : str
|
|
2111
|
+
Description of the protocol adherence rules,
|
|
2112
|
+
False if extraction of the protocol adhernece rules is not
|
|
2113
|
+
possible.
|
|
2114
|
+
"""
|
|
2115
|
+
logger = logging.getLogger(logger_name)
|
|
2116
|
+
|
|
2117
|
+
# Update the project's QA rules
|
|
2118
|
+
res = platform.parse_response(platform.post(
|
|
2119
|
+
auth=self._account.auth,
|
|
2120
|
+
endpoint="projectset_manager/get_session_qa_requirements",
|
|
2121
|
+
data={"project_id": self._project_id},
|
|
2122
|
+
))
|
|
2123
|
+
|
|
2124
|
+
if "rules" not in res:
|
|
2125
|
+
logger.error(f"There was an error extracting the protocol "
|
|
2126
|
+
f"adherence rules from {self._project_name}.")
|
|
2127
|
+
logger.error(platform.parse_response(res))
|
|
1999
2128
|
return False
|
|
2129
|
+
|
|
2130
|
+
try:
|
|
2131
|
+
with open(rules_file_path, "w") as fr:
|
|
2132
|
+
json.dump(res["rules"], fr, indent=4)
|
|
2133
|
+
except FileNotFoundError:
|
|
2134
|
+
logger.error(f"Protocol adherence rules could not be exported"
|
|
2135
|
+
f"to file: '{rules_file_path}'.")
|
|
2136
|
+
return False
|
|
2137
|
+
|
|
2138
|
+
return res["guidance_text"]
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
qmenta/__init__.py,sha256=jv2YF__bseklT3OWEzlqJ5qE24c4aWd5F4r0TTjOrWQ,65
|
|
2
2
|
qmenta/client/Account.py,sha256=MEljEy9cmg2uP2FG1d3TZAgfj66EE2_3PQAZ9rvpCXY,9647
|
|
3
3
|
qmenta/client/File.py,sha256=ZgvSqejIosUt4uoX7opUnPnp5XGEaJNMRwFC0mQVB8k,5344
|
|
4
|
-
qmenta/client/Project.py,sha256=
|
|
4
|
+
qmenta/client/Project.py,sha256=XzolIEPQzUWPelWtv67fiYvLGXp7bLRfH2-Ckvgl8Ao,75153
|
|
5
5
|
qmenta/client/Subject.py,sha256=lhxxVdQ6d-GNoQC8mrJwa4L1f44nJc4PcJtDspmKN7I,8756
|
|
6
6
|
qmenta/client/__init__.py,sha256=AjTojBhZeW5nl0i605KS8S1Gl5tPNc1hdzD47BGNfoI,147
|
|
7
7
|
qmenta/client/utils.py,sha256=5DK2T_HQprrCwLS0Ycm2CjseaYmAUKaJkJvYoW-Rqzc,2479
|
|
8
|
-
qmenta_client-1.1.
|
|
9
|
-
qmenta_client-1.1.
|
|
10
|
-
qmenta_client-1.1.
|
|
8
|
+
qmenta_client-1.1.dev1337.dist-info/METADATA,sha256=Epl4svIqv_lvXPjjfBh5zAU02b5i_sq1z-ZptA0c58o,672
|
|
9
|
+
qmenta_client-1.1.dev1337.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
|
10
|
+
qmenta_client-1.1.dev1337.dist-info/RECORD,,
|
|
File without changes
|