Moments before disaster

This commit is contained in:
Landon Wark
2024-03-11 08:21:48 -05:00
parent d0418ebc9e
commit 17572b4a55
5 changed files with 43 additions and 48 deletions

View File

@@ -1,3 +1,8 @@
## 202403.01
- Added navigation in submission details to sample details.
- Updated cost calculations.
## 202402.04 ## 202402.04
- Addition of comments to gel box. - Addition of comments to gel box.

View File

@@ -4,6 +4,7 @@ Contains all models for sqlalchemy
import sys import sys
from sqlalchemy.orm import DeclarativeMeta, declarative_base, Query from sqlalchemy.orm import DeclarativeMeta, declarative_base, Query
from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.ext.declarative import declared_attr
# Load testing environment
if 'pytest' in sys.modules: if 'pytest' in sys.modules:
from pathlib import Path from pathlib import Path
sys.path.append(Path(__file__).parents[4].absolute().joinpath("tests").__str__()) sys.path.append(Path(__file__).parents[4].absolute().joinpath("tests").__str__())

View File

@@ -3,7 +3,7 @@ Models for the main submission types.
''' '''
from __future__ import annotations from __future__ import annotations
from getpass import getuser from getpass import getuser
import math, json, logging, uuid, tempfile, re, yaml, base64 import json, logging, uuid, tempfile, re, yaml, base64
from zipfile import ZipFile from zipfile import ZipFile
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from reportlab.graphics.barcode import createBarcodeImageInMemory from reportlab.graphics.barcode import createBarcodeImageInMemory
@@ -237,6 +237,7 @@ class BasicSubmission(BaseClass):
self.run_cost = assoc.constant_cost + (assoc.mutable_cost_column * cols_count_96) + (assoc.mutable_cost_sample * int(self.sample_count)) self.run_cost = assoc.constant_cost + (assoc.mutable_cost_column * cols_count_96) + (assoc.mutable_cost_sample * int(self.sample_count))
except Exception as e: except Exception as e:
logger.error(f"Calculation error: {e}") logger.error(f"Calculation error: {e}")
self.run_cost = round(self.run_cost, 2)
def calculate_column_count(self) -> int: def calculate_column_count(self) -> int:
""" """
@@ -338,7 +339,7 @@ class BasicSubmission(BaseClass):
logger.debug(f"Got {len(subs)} submissions.") logger.debug(f"Got {len(subs)} submissions.")
df = pd.DataFrame.from_records(subs) df = pd.DataFrame.from_records(subs)
# Exclude sub information # Exclude sub information
for item in ['controls', 'extraction_info', 'pcr_info', 'comment', 'comments', 'samples', 'reagents', 'equipment', 'gel_info', 'gel_image', 'dna_core_submission_number']: for item in ['controls', 'extraction_info', 'pcr_info', 'comment', 'comments', 'samples', 'reagents', 'equipment', 'gel_info', 'gel_image', 'dna_core_submission_number', 'source_plates']:
try: try:
df = df.drop(item, axis=1) df = df.drop(item, axis=1)
except: except:
@@ -873,8 +874,8 @@ class BasicSubmission(BaseClass):
Returns: Returns:
dict: dictionary of functions dict: dictionary of functions
""" """
names = ["Delete", "Details", "Add Comment", "Add Equipment", "Export"] names = ["Delete", "Details", "Edit", "Add Comment", "Add Equipment", "Export"]
funcs = [self.delete, self.show_details, self.add_comment, self.add_equipment, self.backup] funcs = [self.delete, self.show_details, self.edit, self.add_comment, self.add_equipment, self.backup]
dicto = {item[0]:item[1] for item in zip(names, funcs)} dicto = {item[0]:item[1] for item in zip(names, funcs)}
return dicto return dicto
@@ -915,6 +916,15 @@ class BasicSubmission(BaseClass):
if dlg.exec(): if dlg.exec():
pass pass
def edit(self, obj):
from frontend.widgets.submission_widget import SubmissionFormWidget
for widg in obj.app.table_widget.formwidget.findChildren(SubmissionFormWidget):
logger.debug(widg)
widg.setParent(None)
pyd = self.to_pydantic(backup=True)
form = pyd.toForm(parent=obj)
obj.app.table_widget.formwidget.layout().addWidget(form)
def add_comment(self, obj): def add_comment(self, obj):
""" """
Creates widget for adding comments to submissions Creates widget for adding comments to submissions
@@ -1300,25 +1310,6 @@ class WastewaterArtic(BasicSubmission):
polymorphic_load="inline", polymorphic_load="inline",
inherit_condition=(id == BasicSubmission.id)) inherit_condition=(id == BasicSubmission.id))
def calculate_base_cost(self):
"""
This method overrides parent method due to multiple output plates from a single submission
"""
logger.debug(f"Hello from calculate base cost in WWArtic")
try:
cols_count_96 = math.ceil(int(self.sample_count) / 8)
except Exception as e:
logger.error(f"Column count error: {e}")
assoc = [item for item in self.extraction_kit.kit_submissiontype_associations if item.submission_type == self.submission_type][0]
# Since we have multiple output plates per submission form, the constant cost will have to reflect this.
output_plate_count = math.ceil(int(self.sample_count) / 16)
logger.debug(f"Looks like we have {output_plate_count} output plates.")
const_cost = assoc.constant_cost * output_plate_count
try:
self.run_cost = const_cost + (assoc.mutable_cost_column * cols_count_96) + (assoc.mutable_cost_sample * int(self.sample_count))
except Exception as e:
logger.error(f"Calculation error: {e}")
def to_dict(self, full_data:bool=False, backup:bool=False) -> dict: def to_dict(self, full_data:bool=False, backup:bool=False) -> dict:
""" """
Extends parent class method to add controls to dict Extends parent class method to add controls to dict
@@ -1859,7 +1850,7 @@ class BasicSample(BaseClass):
Returns: Returns:
Tuple(dict, Template): (Updated dictionary, Template to be rendered) Tuple(dict, Template): (Updated dictionary, Template to be rendered)
""" """
base_dict['excluded'] = ['submissions', 'excluded'] base_dict['excluded'] = ['submissions', 'excluded', 'colour', 'tooltip']
env = jinja_template_loading() env = jinja_template_loading()
temp_name = f"{cls.__name__.lower()}_details.html" temp_name = f"{cls.__name__.lower()}_details.html"
logger.debug(f"Returning template: {temp_name}") logger.debug(f"Returning template: {temp_name}")

View File

@@ -33,21 +33,10 @@ class SubmissionDetails(QDialog):
self.app = parent.parent().parent().parent().parent().parent().parent() self.app = parent.parent().parent().parent().parent().parent().parent()
except AttributeError: except AttributeError:
self.app = None self.app = None
self.setWindowTitle(f"Submission Details - {sub.rsl_plate_num}") # self.setWindowTitle(f"Submission Details - {sub.rsl_plate_num}")
# create scrollable interior # create scrollable interior
interior = QScrollArea() interior = QScrollArea()
interior.setParent(self) interior.setParent(self)
# self.base_dict = sub.to_dict(full_data=True)
# logger.debug(f"Submission details data:\n{pformat({k:v for k,v in self.base_dict.items() if k != 'samples'})}")
# # don't want id
# del self.base_dict['id']
# logger.debug(f"Creating barcode.")
# if not check_if_app():
# self.base_dict['barcode'] = base64.b64encode(sub.make_plate_barcode(width=120, height=30)).decode('utf-8')
# logger.debug(f"Making platemap...")
# self.base_dict['platemap'] = sub.make_plate_map()
# self.base_dict, self.template = sub.get_details_template(base_dict=self.base_dict)
# self.html = self.template.render(sub=self.base_dict)
self.webview = QWebEngineView(parent=self) self.webview = QWebEngineView(parent=self)
self.webview.setMinimumSize(900, 500) self.webview.setMinimumSize(900, 500)
self.webview.setMaximumSize(900, 500) self.webview.setMaximumSize(900, 500)
@@ -80,6 +69,7 @@ class SubmissionDetails(QDialog):
base_dict, template = sample.get_details_template(base_dict=base_dict) base_dict, template = sample.get_details_template(base_dict=base_dict)
html = template.render(sample=base_dict) html = template.render(sample=base_dict)
self.webview.setHtml(html) self.webview.setHtml(html)
self.setWindowTitle(f"Sample Details - {sample.submitter_id}")
@pyqtSlot(str) @pyqtSlot(str)
def submission_details(self, submission:str|BasicSubmission): def submission_details(self, submission:str|BasicSubmission):
@@ -104,6 +94,7 @@ class SubmissionDetails(QDialog):
self.base_dict, self.template = submission.get_details_template(base_dict=self.base_dict) self.base_dict, self.template = submission.get_details_template(base_dict=self.base_dict)
self.html = self.template.render(sub=self.base_dict) self.html = self.template.render(sub=self.base_dict)
self.webview.setHtml(self.html) self.webview.setHtml(self.html)
self.setWindowTitle(f"Submission Details - {submission.rsl_plate_num}")
def export(self): def export(self):
""" """
@@ -124,8 +115,6 @@ class SubmissionDetails(QDialog):
self.base_dict['export_map'] = base64.b64encode(image_io.getvalue()).decode('utf-8') self.base_dict['export_map'] = base64.b64encode(image_io.getvalue()).decode('utf-8')
del self.base_dict['platemap'] del self.base_dict['platemap']
self.html2 = self.template.render(sub=self.base_dict) self.html2 = self.template.render(sub=self.base_dict)
with open("test.html", "w") as fw:
fw.write(self.html2)
try: try:
with open(fname, "w+b") as f: with open(fname, "w+b") as f:
pisa.CreatePDF(self.html2, dest=f) pisa.CreatePDF(self.html2, dest=f)

View File

@@ -148,11 +148,11 @@ class SubmissionFormContainer(QWidget):
logger.debug(f"Pydantic result: \n\n{pformat(self.pyd)}\n\n") logger.debug(f"Pydantic result: \n\n{pformat(self.pyd)}\n\n")
self.form = self.pyd.toForm(parent=self) self.form = self.pyd.toForm(parent=self)
self.layout().addWidget(self.form) self.layout().addWidget(self.form)
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"Kitwidget {kit_widget}") # logger.debug(f"Kitwidget {kit_widget}")
self.scrape_reagents(kit_widget.currentText()) # self.scrape_reagents(kit_widget.currentText())
kit_widget.currentTextChanged.connect(self.scrape_reagents) # kit_widget.currentTextChanged.connect(self.scrape_reagents)
# compare obj.reagents with expected reagents in kit # # compare obj.reagents with expected reagents in kit
if self.prsr.sample_result != None: if self.prsr.sample_result != None:
report.add_result(msg=self.prsr.sample_result, status="Warning") report.add_result(msg=self.prsr.sample_result, status="Warning")
self.report.add_result(report) self.report.add_result(report)
@@ -220,7 +220,8 @@ class SubmissionFormContainer(QWidget):
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)
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}")
@@ -397,15 +398,15 @@ class SubmissionFormWidget(QWidget):
super().__init__(parent) super().__init__(parent)
self.ignore = ['filepath', 'samples', 'reagents', 'csv', 'ctx', 'comment', 'equipment', 'source_plates'] self.ignore = ['filepath', 'samples', 'reagents', 'csv', 'ctx', 'comment', 'equipment', 'source_plates']
self.recover = ['filepath', 'samples', 'csv', 'comment', 'equipment'] self.recover = ['filepath', 'samples', 'csv', 'comment', 'equipment']
layout = QVBoxLayout() self.layout = QVBoxLayout()
for k, v in kwargs.items(): for k, v in kwargs.items():
if k not in self.ignore: if k not in self.ignore:
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:
layout.addWidget(add_widget) self.layout.addWidget(add_widget)
else: else:
setattr(self, k, v) setattr(self, k, v)
self.setLayout(layout) self.setLayout(self.layout)
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":
""" """
@@ -420,7 +421,15 @@ class SubmissionFormWidget(QWidget):
self.InfoItem: Form widget to hold name:value self.InfoItem: Form widget to hold name:value
""" """
if key not in self.ignore: if key not in self.ignore:
return 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 None return None
def clear_form(self): def clear_form(self):