qmenta-client 1.1.dev1326__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 +254 -50
- {qmenta_client-1.1.dev1326.dist-info → qmenta_client-1.1.dev1337.dist-info}/METADATA +1 -1
- {qmenta_client-1.1.dev1326.dist-info → qmenta_client-1.1.dev1337.dist-info}/RECORD +4 -4
- {qmenta_client-1.1.dev1326.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,85 +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
|
-
|
|
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
|
+
)
|
|
1962
|
+
)
|
|
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
|
|
1968
|
+
|
|
1969
|
+
def get_qc_status_analysis(self, analysis_id):
|
|
1970
|
+
"""
|
|
1971
|
+
Gets the QC status of an anlaysis provided its Analysis ID.
|
|
1972
|
+
|
|
1973
|
+
Parameters
|
|
1974
|
+
----------
|
|
1975
|
+
analysis_id : int
|
|
1976
|
+
Analysis ID number
|
|
1977
|
+
|
|
1978
|
+
Returns
|
|
1979
|
+
-------
|
|
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.
|
|
1986
|
+
"""
|
|
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:
|
|
1877
2040
|
session = self.get_subjects_metadata(
|
|
1878
2041
|
search_criteria={
|
|
1879
|
-
"pars_patient_secret_name": f"string;
|
|
2042
|
+
"pars_patient_secret_name": f"string;{subject_name}",
|
|
1880
2043
|
"pars_ssid": f"integer;eq|{ssid}",
|
|
1881
2044
|
}
|
|
1882
2045
|
)
|
|
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
|
|
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"]
|
|
1897
2051
|
else:
|
|
1898
|
-
raise
|
|
1899
|
-
|
|
2052
|
+
raise ValueError("Either 'patient_id' or 'subject_name' and 'ssid'"
|
|
2053
|
+
" must not be empty.")
|
|
1900
2054
|
|
|
1901
|
-
def
|
|
2055
|
+
def set_project_pa_rules(self, rules_file_path, guidance_text=""):
|
|
1902
2056
|
"""
|
|
1903
|
-
|
|
1904
|
-
|
|
2057
|
+
Updates the active project's protocol adherence rules using the
|
|
2058
|
+
provided rules file.
|
|
1905
2059
|
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
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.
|
|
1909
2067
|
|
|
1910
|
-
Returns
|
|
1911
|
-
|
|
2068
|
+
Returns
|
|
2069
|
+
-------
|
|
2070
|
+
bool: True if the protocol adherence rules were set successfully,
|
|
2071
|
+
False otherwise.
|
|
1912
2072
|
"""
|
|
2073
|
+
logger = logging.getLogger(logger_name)
|
|
1913
2074
|
# Read the rules from the JSON file
|
|
1914
2075
|
try:
|
|
1915
2076
|
with open(rules_file_path, "r") as fr:
|
|
1916
2077
|
rules = json.load(fr)
|
|
1917
2078
|
except FileNotFoundError:
|
|
1918
|
-
|
|
2079
|
+
logger.error(f"Pprotocol adherence rule file '{rules_file_path}' "
|
|
2080
|
+
f"not found.")
|
|
1919
2081
|
return False
|
|
1920
2082
|
|
|
1921
2083
|
# Update the project's QA rules
|
|
1922
|
-
res = platform.post(
|
|
2084
|
+
res = platform.parse_response(platform.post(
|
|
1923
2085
|
auth=self._account.auth,
|
|
1924
2086
|
endpoint="projectset_manager/set_session_qa_requirements",
|
|
1925
2087
|
data={"project_id": self._project_id, "rules": json.dumps(rules), "guidance_text": guidance_text},
|
|
1926
|
-
)
|
|
2088
|
+
))
|
|
1927
2089
|
|
|
1928
|
-
if res.
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
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))
|
|
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}'.")
|
|
1934
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
|