Added submission editting functionality.
This commit is contained in:
@@ -1,3 +1,8 @@
|
|||||||
|
## 202403.02
|
||||||
|
|
||||||
|
- Moved functions out of submission container to submission form
|
||||||
|
- Added submission editting functionality.
|
||||||
|
|
||||||
## 202403.01
|
## 202403.01
|
||||||
|
|
||||||
- Added navigation in submission details to sample details.
|
- Added navigation in submission details to sample details.
|
||||||
|
|||||||
@@ -426,9 +426,10 @@ class BasicSubmission(BaseClass):
|
|||||||
dicto = self.to_dict(full_data=True, backup=backup)
|
dicto = self.to_dict(full_data=True, backup=backup)
|
||||||
new_dict = {}
|
new_dict = {}
|
||||||
for key, value in dicto.items():
|
for key, value in dicto.items():
|
||||||
|
missing = value is None or value == ""
|
||||||
match key:
|
match key:
|
||||||
case "reagents":
|
case "reagents":
|
||||||
new_dict[key] = [PydReagent(**reagent) for reagent in value]
|
new_dict[key] = [PydReagent(**reagent, missing=False) for reagent in value]
|
||||||
case "samples":
|
case "samples":
|
||||||
# samples = {k.lower().replace(" ", "_"):v for k,v in dicto['samples'].items()}
|
# samples = {k.lower().replace(" ", "_"):v for k,v in dicto['samples'].items()}
|
||||||
new_dict[key] = [PydSample(**{k.lower().replace(" ", "_"):v for k,v in sample.items()}) for sample in dicto['samples']]
|
new_dict[key] = [PydSample(**{k.lower().replace(" ", "_"):v for k,v in sample.items()}) for sample in dicto['samples']]
|
||||||
@@ -438,12 +439,12 @@ class BasicSubmission(BaseClass):
|
|||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
logger.error(f"Possible no equipment error: {e}")
|
logger.error(f"Possible no equipment error: {e}")
|
||||||
case "Plate Number":
|
case "Plate Number":
|
||||||
new_dict['rsl_plate_num'] = dict(value=value, missing=True)
|
new_dict['rsl_plate_num'] = dict(value=value, missing=missing)
|
||||||
case "Submitter Plate Number":
|
case "Submitter Plate Number":
|
||||||
new_dict['submitter_plate_num'] = dict(value=value, missing=True)
|
new_dict['submitter_plate_num'] = dict(value=value, missing=missing)
|
||||||
case _:
|
case _:
|
||||||
logger.debug(f"Setting dict {key} to {value}")
|
logger.debug(f"Setting dict {key} to {value}")
|
||||||
new_dict[key.lower().replace(" ", "_")] = dict(value=value, missing=True)
|
new_dict[key.lower().replace(" ", "_")] = dict(value=value, missing=missing)
|
||||||
new_dict['filepath'] = Path(tempfile.TemporaryFile().name)
|
new_dict['filepath'] = Path(tempfile.TemporaryFile().name)
|
||||||
return PydSubmission(**new_dict)
|
return PydSubmission(**new_dict)
|
||||||
|
|
||||||
|
|||||||
@@ -65,48 +65,48 @@ class SubmissionFormContainer(QWidget):
|
|||||||
self.report = Report()
|
self.report = Report()
|
||||||
self.app.result_reporter()
|
self.app.result_reporter()
|
||||||
|
|
||||||
def scrape_reagents(self, *args, **kwargs):
|
# def scrape_reagents(self, *args, **kwargs):
|
||||||
"""
|
# """
|
||||||
Called when a reagent is changed.
|
# Called when a reagent is changed.
|
||||||
"""
|
# """
|
||||||
caller = inspect.stack()[1].function.__repr__().replace("'", "")
|
# caller = inspect.stack()[1].function.__repr__().replace("'", "")
|
||||||
logger.debug(f"Args: {args}, kwargs: {kwargs}")
|
# logger.debug(f"Args: {args}, kwargs: {kwargs}")
|
||||||
self.scrape_reagents_function(args[0], caller=caller)
|
# self.scrape_reagents_function(args[0], caller=caller)
|
||||||
self.kit_integrity_completion()
|
# self.kit_integrity_completion()
|
||||||
self.app.report.add_result(self.report)
|
# self.app.report.add_result(self.report)
|
||||||
self.report = Report()
|
# self.report = Report()
|
||||||
match inspect.stack()[1].function.__repr__():
|
# match inspect.stack()[1].function.__repr__():
|
||||||
case "import_submission_function":
|
# case "import_submission_function":
|
||||||
pass
|
# pass
|
||||||
case _:
|
# case _:
|
||||||
self.app.result_reporter()
|
# self.app.result_reporter()
|
||||||
|
|
||||||
def kit_integrity_completion(self):
|
# def kit_integrity_completion(self):
|
||||||
"""
|
# """
|
||||||
Performs check of imported reagents
|
# Performs check of imported reagents
|
||||||
NOTE: this will not change self.reagents which should be fine
|
# NOTE: this will not change self.reagents which should be fine
|
||||||
since it's only used when looking up
|
# since it's only used when looking up
|
||||||
"""
|
# """
|
||||||
self.kit_integrity_completion_function()
|
# self.kit_integrity_completion_function()
|
||||||
self.app.report.add_result(self.report)
|
# self.app.report.add_result(self.report)
|
||||||
self.report = Report()
|
# self.report = Report()
|
||||||
match inspect.stack()[1].function.__repr__():
|
# match inspect.stack()[1].function.__repr__():
|
||||||
case "import_submission_function":
|
# case "import_submission_function":
|
||||||
pass
|
# pass
|
||||||
case _:
|
# case _:
|
||||||
self.app.result_reporter()
|
# self.app.result_reporter()
|
||||||
|
|
||||||
def submit_new_sample(self):
|
# def submit_new_sample(self):
|
||||||
"""
|
# """
|
||||||
Attempt to add sample to database when 'submit' button clicked
|
# Attempt to add sample to database when 'submit' button clicked
|
||||||
"""
|
# """
|
||||||
self.submit_new_sample_function()
|
# self.submit_new_sample_function()
|
||||||
self.app.report.add_result(self.report)
|
# self.app.report.add_result(self.report)
|
||||||
self.report = Report()
|
# self.report = Report()
|
||||||
self.app.result_reporter()
|
# self.app.result_reporter()
|
||||||
|
|
||||||
def export_csv(self, fname:Path|None=None):
|
# def export_csv(self, fname:Path|None=None):
|
||||||
self.export_csv_function(fname)
|
# self.export_csv_function(fname)
|
||||||
|
|
||||||
def import_submission_function(self, fname:Path|None=None):
|
def import_submission_function(self, fname:Path|None=None):
|
||||||
"""
|
"""
|
||||||
@@ -159,168 +159,168 @@ class SubmissionFormContainer(QWidget):
|
|||||||
logger.debug(f"Outgoing report: {self.report.results}")
|
logger.debug(f"Outgoing report: {self.report.results}")
|
||||||
logger.debug(f"All attributes of submission container:\n{pformat(self.__dict__)}")
|
logger.debug(f"All attributes of submission container:\n{pformat(self.__dict__)}")
|
||||||
|
|
||||||
def scrape_reagents_function(self, extraction_kit:str, caller:str|None=None):
|
# def scrape_reagents_function(self, extraction_kit:str, caller:str|None=None):
|
||||||
"""
|
# """
|
||||||
Extracted scrape reagents function that will run when
|
# Extracted scrape reagents function that will run when
|
||||||
form 'extraction_kit' widget is updated.
|
# form 'extraction_kit' widget is updated.
|
||||||
|
|
||||||
Args:
|
# Args:
|
||||||
obj (QMainWindow): updated main application
|
# obj (QMainWindow): updated main application
|
||||||
extraction_kit (str): name of extraction kit (in 'extraction_kit' widget)
|
# extraction_kit (str): name of extraction kit (in 'extraction_kit' widget)
|
||||||
|
|
||||||
Returns:
|
# Returns:
|
||||||
Tuple[QMainWindow, dict]: Updated application and result
|
# Tuple[QMainWindow, dict]: Updated application and result
|
||||||
"""
|
# """
|
||||||
self.form.reagents = []
|
# self.form.reagents = []
|
||||||
logger.debug(f"\n\n{caller}\n\n")
|
# logger.debug(f"\n\n{caller}\n\n")
|
||||||
report = Report()
|
# report = Report()
|
||||||
logger.debug(f"Extraction kit: {extraction_kit}")
|
# logger.debug(f"Extraction kit: {extraction_kit}")
|
||||||
# Remove previous reagent widgets
|
# # Remove previous reagent widgets
|
||||||
try:
|
# try:
|
||||||
old_reagents = self.form.find_widgets()
|
# old_reagents = self.form.find_widgets()
|
||||||
except AttributeError:
|
# except AttributeError:
|
||||||
logger.error(f"Couldn't find old reagents.")
|
# logger.error(f"Couldn't find old reagents.")
|
||||||
old_reagents = []
|
# old_reagents = []
|
||||||
# logger.debug(f"\n\nAttempting to clear: {old_reagents}\n\n")
|
# # logger.debug(f"\n\nAttempting to clear: {old_reagents}\n\n")
|
||||||
for reagent in old_reagents:
|
# for reagent in old_reagents:
|
||||||
if isinstance(reagent, ReagentFormWidget) or isinstance(reagent, QPushButton):
|
# if isinstance(reagent, ReagentFormWidget) or isinstance(reagent, QPushButton):
|
||||||
reagent.setParent(None)
|
# reagent.setParent(None)
|
||||||
match caller:
|
# match caller:
|
||||||
case "import_submission_function":
|
# case "import_submission_function":
|
||||||
self.form.reagents = self.prsr.sub['reagents']
|
# self.form.reagents = self.prsr.sub['reagents']
|
||||||
case _:
|
# case _:
|
||||||
already_have = [reagent for reagent in self.prsr.sub['reagents'] if not reagent.missing]
|
# already_have = [reagent for reagent in self.prsr.sub['reagents'] if not reagent.missing]
|
||||||
names = list(set([item.type for item in already_have]))
|
# names = list(set([item.type for item in already_have]))
|
||||||
logger.debug(f"reagents: {already_have}")
|
# logger.debug(f"reagents: {already_have}")
|
||||||
reagents = [item.to_pydantic() for item in KitType.query(name=extraction_kit).get_reagents(submission_type=self.pyd.submission_type) if item.name not in names]
|
# reagents = [item.to_pydantic() for item in KitType.query(name=extraction_kit).get_reagents(submission_type=self.pyd.submission_type) if item.name not in names]
|
||||||
self.form.reagents = already_have + reagents
|
# self.form.reagents = already_have + reagents
|
||||||
# logger.debug(f"Imported reagents: {obj.reagents}")
|
# # logger.debug(f"Imported reagents: {obj.reagents}")
|
||||||
# logger.debug(f"Missing reagents: {obj.missing_reagents}")
|
# # logger.debug(f"Missing reagents: {obj.missing_reagents}")
|
||||||
self.report.add_result(report)
|
# self.report.add_result(report)
|
||||||
logger.debug(f"Outgoing report: {self.report.results}")
|
# logger.debug(f"Outgoing report: {self.report.results}")
|
||||||
|
|
||||||
def kit_integrity_completion_function(self):
|
# def kit_integrity_completion_function(self):
|
||||||
"""
|
# """
|
||||||
Compare kit contents to parsed contents
|
# Compare kit contents to parsed contents
|
||||||
|
|
||||||
Args:
|
# Args:
|
||||||
obj (QMainWindow): The original app window
|
# obj (QMainWindow): The original app window
|
||||||
|
|
||||||
Returns:
|
# Returns:
|
||||||
Tuple[QMainWindow, dict]: Collection of new main app window and result dict
|
# Tuple[QMainWindow, dict]: Collection of new main app window and result dict
|
||||||
"""
|
# """
|
||||||
report = Report()
|
# report = Report()
|
||||||
missing_reagents = []
|
# missing_reagents = []
|
||||||
# logger.debug(inspect.currentframe().f_back.f_code.co_name)
|
# # logger.debug(inspect.currentframe().f_back.f_code.co_name)
|
||||||
# find the widget that contains kit info
|
# # find the widget that contains kit info
|
||||||
kit_widget = self.form.find_widgets(object_name="extraction_kit")[0].input
|
# kit_widget = self.form.find_widgets(object_name="extraction_kit")[0].input
|
||||||
logger.debug(f"Kit selector: {kit_widget}")
|
# logger.debug(f"Kit selector: {kit_widget}")
|
||||||
# get current kit being used
|
# # get current kit being used
|
||||||
self.ext_kit = kit_widget.currentText()
|
# self.ext_kit = kit_widget.currentText()
|
||||||
for reagent in self.form.reagents:
|
# for reagent in self.form.reagents:
|
||||||
logger.debug(f"Creating widget for {reagent}")
|
# logger.debug(f"Creating widget for {reagent}")
|
||||||
add_widget = ReagentFormWidget(parent=self, reagent=reagent, extraction_kit=self.ext_kit)
|
# add_widget = ReagentFormWidget(parent=self, reagent=reagent, extraction_kit=self.ext_kit)
|
||||||
# self.form.layout().addWidget(add_widget)
|
# # self.form.layout().addWidget(add_widget)
|
||||||
self.form.layout.addWidget(add_widget)
|
# self.form.layout.addWidget(add_widget)
|
||||||
if reagent.missing:
|
# if reagent.missing:
|
||||||
missing_reagents.append(reagent)
|
# missing_reagents.append(reagent)
|
||||||
logger.debug(f"Checking integrity of {self.ext_kit}")
|
# logger.debug(f"Checking integrity of {self.ext_kit}")
|
||||||
# TODO: put check_kit_integrity here instead of what's here?
|
# # TODO: put check_kit_integrity here instead of what's here?
|
||||||
# see if there are any missing reagents
|
# # see if there are any missing reagents
|
||||||
if len(missing_reagents) > 0:
|
# if len(missing_reagents) > 0:
|
||||||
result = Result(msg=f"""The submission you are importing is missing some reagents expected by the kit.\n\n
|
# result = Result(msg=f"""The submission you are importing is missing some reagents expected by the kit.\n\n
|
||||||
It looks like you are missing: {[item.type.upper() for item in missing_reagents]}\n\n
|
# It looks like you are missing: {[item.type.upper() for item in missing_reagents]}\n\n
|
||||||
Alternatively, you may have set the wrong extraction kit.\n\nThe program will populate lists using existing reagents.
|
# Alternatively, you may have set the wrong extraction kit.\n\nThe program will populate lists using existing reagents.
|
||||||
\n\nPlease make sure you check the lots carefully!""".replace(" ", ""), status="Warning")
|
# \n\nPlease make sure you check the lots carefully!""".replace(" ", ""), status="Warning")
|
||||||
report.add_result(result)
|
# report.add_result(result)
|
||||||
if hasattr(self.pyd, "csv"):
|
# if hasattr(self.pyd, "csv"):
|
||||||
export_csv_btn = QPushButton("Export CSV")
|
# export_csv_btn = QPushButton("Export CSV")
|
||||||
export_csv_btn.setObjectName("export_csv_btn")
|
# export_csv_btn.setObjectName("export_csv_btn")
|
||||||
self.form.layout().addWidget(export_csv_btn)
|
# self.form.layout().addWidget(export_csv_btn)
|
||||||
export_csv_btn.clicked.connect(self.export_csv)
|
# export_csv_btn.clicked.connect(self.export_csv)
|
||||||
submit_btn = QPushButton("Submit")
|
# submit_btn = QPushButton("Submit")
|
||||||
submit_btn.setObjectName("submit_btn")
|
# submit_btn.setObjectName("submit_btn")
|
||||||
self.form.layout().addWidget(submit_btn)
|
# self.form.layout().addWidget(submit_btn)
|
||||||
submit_btn.clicked.connect(self.submit_new_sample)
|
# submit_btn.clicked.connect(self.submit_new_sample)
|
||||||
self.report.add_result(report)
|
# self.report.add_result(report)
|
||||||
logger.debug(f"Outgoing report: {self.report.results}")
|
# logger.debug(f"Outgoing report: {self.report.results}")
|
||||||
|
|
||||||
def submit_new_sample_function(self) -> QWidget:
|
# def submit_new_sample_function(self) -> QWidget:
|
||||||
"""
|
# """
|
||||||
Parse forms and add sample to the database.
|
# Parse forms and add sample to the database.
|
||||||
|
|
||||||
Args:
|
# Args:
|
||||||
obj (QMainWindow): original app window
|
# obj (QMainWindow): original app window
|
||||||
|
|
||||||
Returns:
|
# Returns:
|
||||||
Tuple[QMainWindow, dict]: Collection of new main app window and result dict
|
# Tuple[QMainWindow, dict]: Collection of new main app window and result dict
|
||||||
"""
|
# """
|
||||||
logger.debug(f"\n\nBeginning Submission\n\n")
|
# logger.debug(f"\n\nBeginning Submission\n\n")
|
||||||
report = Report()
|
# report = Report()
|
||||||
self.pyd: PydSubmission = self.form.parse_form()
|
# self.pyd: PydSubmission = self.form.parse_form()
|
||||||
logger.debug(f"Submission: {pformat(self.pyd)}")
|
# logger.debug(f"Submission: {pformat(self.pyd)}")
|
||||||
logger.debug("Checking kit integrity...")
|
# logger.debug("Checking kit integrity...")
|
||||||
result = self.pyd.check_kit_integrity()
|
# result = self.pyd.check_kit_integrity()
|
||||||
report.add_result(result)
|
# report.add_result(result)
|
||||||
if len(result.results) > 0:
|
# if len(result.results) > 0:
|
||||||
self.report.add_result(report)
|
# self.report.add_result(report)
|
||||||
return
|
# return
|
||||||
base_submission, result = self.pyd.toSQL()
|
# base_submission, result = self.pyd.toSQL()
|
||||||
# logger.debug(f"Base submission: {base_submission.to_dict()}")
|
# # logger.debug(f"Base submission: {base_submission.to_dict()}")
|
||||||
# check output message for issues
|
# # check output message for issues
|
||||||
match result.code:
|
# match result.code:
|
||||||
# code 0: everything is fine.
|
# # code 0: everything is fine.
|
||||||
case 0:
|
# case 0:
|
||||||
self.report.add_result(None)
|
# self.report.add_result(None)
|
||||||
# code 1: ask for overwrite
|
# # code 1: ask for overwrite
|
||||||
case 1:
|
# case 1:
|
||||||
dlg = QuestionAsker(title=f"Review {base_submission.rsl_plate_num}?", message=result.msg)
|
# dlg = QuestionAsker(title=f"Review {base_submission.rsl_plate_num}?", message=result.msg)
|
||||||
if dlg.exec():
|
# if dlg.exec():
|
||||||
# Do not add duplicate reagents.
|
# # Do not add duplicate reagents.
|
||||||
# base_submission.reagents = []
|
# # base_submission.reagents = []
|
||||||
result = None
|
# result = None
|
||||||
else:
|
# else:
|
||||||
self.app.ctx.database_session.rollback()
|
# self.app.ctx.database_session.rollback()
|
||||||
self.report.add_result(Result(msg="Overwrite cancelled", status="Information"))
|
# self.report.add_result(Result(msg="Overwrite cancelled", status="Information"))
|
||||||
return
|
# return
|
||||||
# code 2: No RSL plate number given
|
# # code 2: No RSL plate number given
|
||||||
case 2:
|
# case 2:
|
||||||
self.report.add_result(result)
|
# self.report.add_result(result)
|
||||||
return
|
# return
|
||||||
case _:
|
# case _:
|
||||||
pass
|
# pass
|
||||||
# add reagents to submission object
|
# # add reagents to submission object
|
||||||
for reagent in base_submission.reagents:
|
# for reagent in base_submission.reagents:
|
||||||
# logger.debug(f"Updating: {reagent} with {reagent.lot}")
|
# # logger.debug(f"Updating: {reagent} with {reagent.lot}")
|
||||||
reagent.update_last_used(kit=base_submission.extraction_kit)
|
# reagent.update_last_used(kit=base_submission.extraction_kit)
|
||||||
# logger.debug(f"Here is the final submission: {pformat(base_submission.__dict__)}")
|
# # logger.debug(f"Here is the final submission: {pformat(base_submission.__dict__)}")
|
||||||
# logger.debug(f"Parsed reagents: {pformat(base_submission.reagents)}")
|
# # logger.debug(f"Parsed reagents: {pformat(base_submission.reagents)}")
|
||||||
# logger.debug(f"Sending submission: {base_submission.rsl_plate_num} to database.")
|
# # logger.debug(f"Sending submission: {base_submission.rsl_plate_num} to database.")
|
||||||
# logger.debug(f"Samples from pyd: {pformat(self.pyd.samples)}")
|
# # logger.debug(f"Samples from pyd: {pformat(self.pyd.samples)}")
|
||||||
# logger.debug(f"Samples SQL: {pformat([item.__dict__ for item in base_submission.samples])}")
|
# # logger.debug(f"Samples SQL: {pformat([item.__dict__ for item in base_submission.samples])}")
|
||||||
# logger.debug(f"")
|
# # logger.debug(f"")
|
||||||
base_submission.save()
|
# base_submission.save()
|
||||||
# update summary sheet
|
# # update summary sheet
|
||||||
self.app.table_widget.sub_wid.setData()
|
# self.app.table_widget.sub_wid.setData()
|
||||||
# reset form
|
# # reset form
|
||||||
self.form.setParent(None)
|
# self.form.setParent(None)
|
||||||
# logger.debug(f"All attributes of obj: {pformat(self.__dict__)}")
|
# # logger.debug(f"All attributes of obj: {pformat(self.__dict__)}")
|
||||||
self.report.add_result(report)
|
# self.report.add_result(report)
|
||||||
|
|
||||||
def export_csv_function(self, fname:Path|None=None):
|
# def export_csv_function(self, fname:Path|None=None):
|
||||||
"""
|
# """
|
||||||
Save the submission's csv file.
|
# Save the submission's csv file.
|
||||||
|
|
||||||
Args:
|
# Args:
|
||||||
fname (Path | None, optional): Input filename. Defaults to None.
|
# fname (Path | None, optional): Input filename. Defaults to None.
|
||||||
"""
|
# """
|
||||||
if isinstance(fname, bool) or fname == None:
|
# if isinstance(fname, bool) or fname == None:
|
||||||
fname = select_save_file(obj=self, default_name=self.pyd.construct_filename(), extension="csv")
|
# fname = select_save_file(obj=self, default_name=self.pyd.construct_filename(), extension="csv")
|
||||||
try:
|
# try:
|
||||||
self.pyd.csv.to_csv(fname.__str__(), index=False)
|
# self.pyd.csv.to_csv(fname.__str__(), index=False)
|
||||||
del self.pyd.csv
|
# del self.pyd.csv
|
||||||
except PermissionError:
|
# except PermissionError:
|
||||||
logger.debug(f"Could not get permissions to {fname}. Possibly the request was cancelled.")
|
# logger.debug(f"Could not get permissions to {fname}. Possibly the request was cancelled.")
|
||||||
|
|
||||||
def import_pcr_results(self):
|
def import_pcr_results(self):
|
||||||
"""
|
"""
|
||||||
@@ -396,7 +396,14 @@ class SubmissionFormWidget(QWidget):
|
|||||||
|
|
||||||
def __init__(self, parent: QWidget, **kwargs) -> None:
|
def __init__(self, parent: QWidget, **kwargs) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.ignore = ['filepath', 'samples', 'reagents', 'csv', 'ctx', 'comment', 'equipment', 'source_plates']
|
self.report = Report()
|
||||||
|
self.app = parent.app
|
||||||
|
# self.input = [{k:v} for k,v in kwargs.items()]
|
||||||
|
self.samples = []
|
||||||
|
self.missing_info = []
|
||||||
|
self.ignore = ['filepath', 'samples', 'reagents', 'csv', 'ctx', 'comment',
|
||||||
|
'equipment', 'source_plates', 'id', 'cost', 'extraction_info',
|
||||||
|
'controls', 'pcr_info', 'gel_info', 'gel_image']
|
||||||
self.recover = ['filepath', 'samples', 'csv', 'comment', 'equipment']
|
self.recover = ['filepath', 'samples', 'csv', 'comment', 'equipment']
|
||||||
self.layout = QVBoxLayout()
|
self.layout = QVBoxLayout()
|
||||||
for k, v in kwargs.items():
|
for k, v in kwargs.items():
|
||||||
@@ -404,9 +411,25 @@ class SubmissionFormWidget(QWidget):
|
|||||||
add_widget = self.create_widget(key=k, value=v, submission_type=kwargs['submission_type'])
|
add_widget = self.create_widget(key=k, value=v, submission_type=kwargs['submission_type'])
|
||||||
if add_widget != None:
|
if add_widget != None:
|
||||||
self.layout.addWidget(add_widget)
|
self.layout.addWidget(add_widget)
|
||||||
else:
|
if k == "extraction_kit":
|
||||||
setattr(self, k, v)
|
add_widget.input.currentTextChanged.connect(self.scrape_reagents)
|
||||||
|
# else:
|
||||||
|
self.__setattr__(k, v)
|
||||||
|
self.scrape_reagents(self.extraction_kit['value'])
|
||||||
|
# extraction kit must be added last so widget order makes sense.
|
||||||
|
# self.layout.addWidget(self.create_widget(key="extraction_kit", value=self.extraction_kit, submission_type=self.submission_type))
|
||||||
|
if hasattr(self, "csv"):
|
||||||
|
export_csv_btn = QPushButton("Export CSV")
|
||||||
|
export_csv_btn.setObjectName("export_csv_btn")
|
||||||
|
self.layout.addWidget(export_csv_btn)
|
||||||
|
export_csv_btn.clicked.connect(self.export_csv_function)
|
||||||
|
submit_btn = QPushButton("Submit")
|
||||||
|
submit_btn.setObjectName("submit_btn")
|
||||||
|
self.layout.addWidget(submit_btn)
|
||||||
|
submit_btn.clicked.connect(self.submit_new_sample_function)
|
||||||
self.setLayout(self.layout)
|
self.setLayout(self.layout)
|
||||||
|
self.app.report.add_result(self.report)
|
||||||
|
self.app.result_reporter()
|
||||||
|
|
||||||
def create_widget(self, key:str, value:dict, submission_type:str|None=None) -> "self.InfoItem":
|
def create_widget(self, key:str, value:dict, submission_type:str|None=None) -> "self.InfoItem":
|
||||||
"""
|
"""
|
||||||
@@ -422,16 +445,97 @@ class SubmissionFormWidget(QWidget):
|
|||||||
"""
|
"""
|
||||||
if key not in self.ignore:
|
if key not in self.ignore:
|
||||||
widget = self.InfoItem(self, key=key, value=value, submission_type=submission_type)
|
widget = self.InfoItem(self, key=key, value=value, submission_type=submission_type)
|
||||||
# match key:
|
|
||||||
# case "extraction_kit":
|
|
||||||
# # compare obj.reagents with expected reagents in kit
|
|
||||||
# self.scrape_reagents(widget.currentText())
|
|
||||||
# widget.currentTextChanged.connect(self.scrape_reagents)
|
|
||||||
# case _:
|
|
||||||
# pass
|
|
||||||
return widget
|
return widget
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def scrape_reagents(self, *args, **kwargs):#extraction_kit:str, caller:str|None=None):
|
||||||
|
"""
|
||||||
|
Extracted scrape reagents function that will run when
|
||||||
|
form 'extraction_kit' widget is updated.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obj (QMainWindow): updated main application
|
||||||
|
extraction_kit (str): name of extraction kit (in 'extraction_kit' widget)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple[QMainWindow, dict]: Updated application and result
|
||||||
|
"""
|
||||||
|
extraction_kit = args[0]
|
||||||
|
caller = inspect.stack()[1].function.__repr__().replace("'", "")
|
||||||
|
# self.reagents = []
|
||||||
|
# logger.debug(f"Self.reagents: {self.reagents}")
|
||||||
|
# logger.debug(f"\n\n{caller}\n\n")
|
||||||
|
# logger.debug(f"SubmissionType: {self.submission_type}")
|
||||||
|
report = Report()
|
||||||
|
logger.debug(f"Extraction kit: {extraction_kit}")
|
||||||
|
# Remove previous reagent widgets
|
||||||
|
try:
|
||||||
|
old_reagents = self.find_widgets()
|
||||||
|
except AttributeError:
|
||||||
|
logger.error(f"Couldn't find old reagents.")
|
||||||
|
old_reagents = []
|
||||||
|
# logger.debug(f"\n\nAttempting to clear: {old_reagents}\n\n")
|
||||||
|
for reagent in old_reagents:
|
||||||
|
if isinstance(reagent, self.ReagentFormWidget) or isinstance(reagent, QPushButton):
|
||||||
|
reagent.setParent(None)
|
||||||
|
# match caller:
|
||||||
|
# case "import_submission_function":
|
||||||
|
# self.reagents = self.prsr.sub['reagents']
|
||||||
|
# case _:
|
||||||
|
# already_have = [reagent for reagent in self.prsr.sub['reagents'] if not reagent.missing]
|
||||||
|
already_have = [reagent for reagent in self.reagents if not reagent.missing]
|
||||||
|
names = list(set([item.type for item in already_have]))
|
||||||
|
# logger.debug(f"Already have: {already_have}")
|
||||||
|
reagents = [item.to_pydantic() for item in KitType.query(name=extraction_kit).get_reagents(submission_type=self.submission_type['value']) if item.name not in names]
|
||||||
|
# logger.debug(f"Missing: {reagents}")
|
||||||
|
self.reagents = already_have + reagents
|
||||||
|
# logger.debug(f"Reagents: {self.reagents}")
|
||||||
|
self.kit_integrity_completion_function(extraction_kit=extraction_kit)
|
||||||
|
# logger.debug(f"Missing reagents: {obj.missing_reagents}")
|
||||||
|
self.report.add_result(report)
|
||||||
|
logger.debug(f"Outgoing report: {self.report.results}")
|
||||||
|
|
||||||
|
def kit_integrity_completion_function(self, extraction_kit:str|None=None):
|
||||||
|
"""
|
||||||
|
Compare kit contents to parsed contents and creates widgets.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obj (QMainWindow): The original app window
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple[QMainWindow, dict]: Collection of new main app window and result dict
|
||||||
|
"""
|
||||||
|
report = Report()
|
||||||
|
missing_reagents = []
|
||||||
|
# logger.debug(inspect.currentframe().f_back.f_code.co_name)
|
||||||
|
# find the widget that contains kit info
|
||||||
|
if extraction_kit is None:
|
||||||
|
kit_widget = self.find_widgets(object_name="extraction_kit")[0].input
|
||||||
|
logger.debug(f"Kit selector: {kit_widget}")
|
||||||
|
# get current kit being used
|
||||||
|
self.ext_kit = kit_widget.currentText()
|
||||||
|
else:
|
||||||
|
self.ext_kit = extraction_kit
|
||||||
|
for reagent in self.reagents:
|
||||||
|
logger.debug(f"Creating widget for {reagent}")
|
||||||
|
add_widget = self.ReagentFormWidget(parent=self, reagent=reagent, extraction_kit=self.ext_kit)
|
||||||
|
# self.form.layout().addWidget(add_widget)
|
||||||
|
self.layout.addWidget(add_widget)
|
||||||
|
if reagent.missing:
|
||||||
|
missing_reagents.append(reagent)
|
||||||
|
logger.debug(f"Checking integrity of {self.ext_kit}")
|
||||||
|
# TODO: put check_kit_integrity here instead of what's here?
|
||||||
|
# see if there are any missing reagents
|
||||||
|
if len(missing_reagents) > 0:
|
||||||
|
result = Result(msg=f"""The submission you are importing is missing some reagents expected by the kit.\n\n
|
||||||
|
It looks like you are missing: {[item.type.upper() for item in missing_reagents]}\n\n
|
||||||
|
Alternatively, you may have set the wrong extraction kit.\n\nThe program will populate lists using existing reagents.
|
||||||
|
\n\nPlease make sure you check the lots carefully!""".replace(" ", ""), status="Warning")
|
||||||
|
report.add_result(result)
|
||||||
|
|
||||||
|
self.report.add_result(report)
|
||||||
|
logger.debug(f"Outgoing report: {self.report.results}")
|
||||||
|
|
||||||
def clear_form(self):
|
def clear_form(self):
|
||||||
"""
|
"""
|
||||||
Removes all form widgets
|
Removes all form widgets
|
||||||
@@ -454,6 +558,83 @@ class SubmissionFormWidget(QWidget):
|
|||||||
query = [widget for widget in query if widget.objectName()==object_name]
|
query = [widget for widget in query if widget.objectName()==object_name]
|
||||||
return query
|
return query
|
||||||
|
|
||||||
|
def submit_new_sample_function(self) -> QWidget:
|
||||||
|
"""
|
||||||
|
Parse forms and add sample to the database.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obj (QMainWindow): original app window
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple[QMainWindow, dict]: Collection of new main app window and result dict
|
||||||
|
"""
|
||||||
|
logger.debug(f"\n\nBeginning Submission\n\n")
|
||||||
|
report = Report()
|
||||||
|
self.pyd: PydSubmission = self.parse_form()
|
||||||
|
logger.debug(f"Submission: {pformat(self.pyd)}")
|
||||||
|
logger.debug("Checking kit integrity...")
|
||||||
|
result = self.pyd.check_kit_integrity()
|
||||||
|
report.add_result(result)
|
||||||
|
if len(result.results) > 0:
|
||||||
|
self.report.add_result(report)
|
||||||
|
return
|
||||||
|
base_submission, result = self.pyd.toSQL()
|
||||||
|
# logger.debug(f"Base submission: {base_submission.to_dict()}")
|
||||||
|
# check output message for issues
|
||||||
|
match result.code:
|
||||||
|
# code 0: everything is fine.
|
||||||
|
case 0:
|
||||||
|
self.report.add_result(None)
|
||||||
|
# code 1: ask for overwrite
|
||||||
|
case 1:
|
||||||
|
dlg = QuestionAsker(title=f"Review {base_submission.rsl_plate_num}?", message=result.msg)
|
||||||
|
if dlg.exec():
|
||||||
|
# Do not add duplicate reagents.
|
||||||
|
# base_submission.reagents = []
|
||||||
|
result = None
|
||||||
|
else:
|
||||||
|
self.app.ctx.database_session.rollback()
|
||||||
|
self.report.add_result(Result(msg="Overwrite cancelled", status="Information"))
|
||||||
|
return
|
||||||
|
# code 2: No RSL plate number given
|
||||||
|
case 2:
|
||||||
|
self.report.add_result(result)
|
||||||
|
return
|
||||||
|
case _:
|
||||||
|
pass
|
||||||
|
# add reagents to submission object
|
||||||
|
for reagent in base_submission.reagents:
|
||||||
|
# logger.debug(f"Updating: {reagent} with {reagent.lot}")
|
||||||
|
reagent.update_last_used(kit=base_submission.extraction_kit)
|
||||||
|
# logger.debug(f"Here is the final submission: {pformat(base_submission.__dict__)}")
|
||||||
|
# logger.debug(f"Parsed reagents: {pformat(base_submission.reagents)}")
|
||||||
|
# logger.debug(f"Sending submission: {base_submission.rsl_plate_num} to database.")
|
||||||
|
# logger.debug(f"Samples from pyd: {pformat(self.pyd.samples)}")
|
||||||
|
# logger.debug(f"Samples SQL: {pformat([item.__dict__ for item in base_submission.samples])}")
|
||||||
|
# logger.debug(f"")
|
||||||
|
base_submission.save()
|
||||||
|
# update summary sheet
|
||||||
|
self.app.table_widget.sub_wid.setData()
|
||||||
|
# reset form
|
||||||
|
self.setParent(None)
|
||||||
|
# logger.debug(f"All attributes of obj: {pformat(self.__dict__)}")
|
||||||
|
self.report.add_result(report)
|
||||||
|
|
||||||
|
def export_csv_function(self, fname:Path|None=None):
|
||||||
|
"""
|
||||||
|
Save the submission's csv file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
fname (Path | None, optional): Input filename. Defaults to None.
|
||||||
|
"""
|
||||||
|
pyd = self.parse_form()
|
||||||
|
if isinstance(fname, bool) or fname == None:
|
||||||
|
fname = select_save_file(obj=self, default_name=pyd.construct_filename(), extension="csv")
|
||||||
|
try:
|
||||||
|
pyd.csv.to_csv(fname.__str__(), index=False)
|
||||||
|
except PermissionError:
|
||||||
|
logger.debug(f"Could not get permissions to {fname}. Possibly the request was cancelled.")
|
||||||
|
|
||||||
def parse_form(self) -> PydSubmission:
|
def parse_form(self) -> PydSubmission:
|
||||||
"""
|
"""
|
||||||
Transforms form info into PydSubmission
|
Transforms form info into PydSubmission
|
||||||
@@ -467,7 +648,7 @@ class SubmissionFormWidget(QWidget):
|
|||||||
for widget in self.findChildren(QWidget):
|
for widget in self.findChildren(QWidget):
|
||||||
# logger.debug(f"Parsed widget of type {type(widget)}")
|
# logger.debug(f"Parsed widget of type {type(widget)}")
|
||||||
match widget:
|
match widget:
|
||||||
case ReagentFormWidget():
|
case self.ReagentFormWidget():
|
||||||
reagent, _ = widget.parse_form()
|
reagent, _ = widget.parse_form()
|
||||||
if reagent != None:
|
if reagent != None:
|
||||||
reagents.append(reagent)
|
reagents.append(reagent)
|
||||||
@@ -581,7 +762,7 @@ class SubmissionFormWidget(QWidget):
|
|||||||
logger.error(f"Couldn't find {obj.prsr.sub['extraction_kit']}")
|
logger.error(f"Couldn't find {obj.prsr.sub['extraction_kit']}")
|
||||||
obj.ext_kit = uses[0]
|
obj.ext_kit = uses[0]
|
||||||
add_widget.addItems(uses)
|
add_widget.addItems(uses)
|
||||||
# Run reagent scraper whenever extraction kit is changed.
|
|
||||||
case 'submitted_date':
|
case 'submitted_date':
|
||||||
# uses base calendar
|
# uses base calendar
|
||||||
add_widget = QDateEdit(calendarPopup=True)
|
add_widget = QDateEdit(calendarPopup=True)
|
||||||
@@ -652,7 +833,7 @@ class SubmissionFormWidget(QWidget):
|
|||||||
output = key.replace('_', ' ')
|
output = key.replace('_', ' ')
|
||||||
self.setText(f"UPDATED {output}")
|
self.setText(f"UPDATED {output}")
|
||||||
|
|
||||||
class ReagentFormWidget(QWidget):
|
class ReagentFormWidget(QWidget):
|
||||||
|
|
||||||
def __init__(self, parent:QWidget, reagent:PydReagent, extraction_kit:str):
|
def __init__(self, parent:QWidget, reagent:PydReagent, extraction_kit:str):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|||||||
Reference in New Issue
Block a user