Various bug fixes for new forms.

This commit is contained in:
Landon Wark
2024-04-25 08:55:32 -05:00
parent 5466c78c3a
commit 8cc161ec56
18 changed files with 520 additions and 218 deletions

View File

@@ -85,10 +85,10 @@ class AddReagentForm(QDialog):
Returns:
dict: Output info
"""
return dict(name=self.name_input.currentText(),
lot=self.lot_input.text(),
return dict(name=self.name_input.currentText().strip(),
lot=self.lot_input.text().strip(),
expiry=self.exp_input.date().toPyDate(),
type=self.type_input.currentText())
type=self.type_input.currentText().strip())
def update_names(self):
"""

View File

@@ -7,7 +7,7 @@ from PyQt6.QtWidgets import (
)
from tools import jinja_template_loading
import logging
from backend.db.models import KitType, SubmissionType
from backend.db import models
from typing import Literal
logger = logging.getLogger(f"submissions.{__name__}")
@@ -45,16 +45,18 @@ class AlertPop(QMessageBox):
self.setInformativeText(message)
self.setWindowTitle(f"{owner} - {status.title()}")
class KitSelector(QDialog):
class ObjectSelector(QDialog):
"""
dialog to input KitType manually
dialog to input BaseClass type manually
"""
def __init__(self, title:str, message:str) -> QDialog:
def __init__(self, title:str, message:str, obj_type:str|models.BaseClass) -> QDialog:
super().__init__()
self.setWindowTitle(title)
self.widget = QComboBox()
kits = [item.name for item in KitType.query()]
self.widget.addItems(kits)
if isinstance(obj_type, str):
obj_type: models.BaseClass = getattr(models, obj_type)
items = [item.name for item in obj_type.query()]
self.widget.addItems(items)
self.widget.setEditable(False)
# set yes/no buttons
QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
@@ -78,36 +80,69 @@ class KitSelector(QDialog):
"""
return self.widget.currentText()
class SubmissionTypeSelector(QDialog):
"""
dialog to input SubmissionType manually
"""
def __init__(self, title:str, message:str) -> QDialog:
super().__init__()
self.setWindowTitle(title)
self.widget = QComboBox()
# sub_type = [item.name for item in lookup_submission_type(ctx=ctx)]
sub_type = [item.name for item in SubmissionType.query()]
self.widget.addItems(sub_type)
self.widget.setEditable(False)
# set yes/no buttons
QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
self.buttonBox = QDialogButtonBox(QBtn)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
self.layout = QVBoxLayout()
# Text for the yes/no question
message = QLabel(message)
self.layout.addWidget(message)
self.layout.addWidget(self.widget)
self.layout.addWidget(self.buttonBox)
self.setLayout(self.layout)
# class KitSelector(QDialog):
# """
# dialog to input KitType manually
# """
# def __init__(self, title:str, message:str) -> QDialog:
# super().__init__()
# self.setWindowTitle(title)
# self.widget = QComboBox()
# kits = [item.name for item in KitType.query()]
# self.widget.addItems(kits)
# self.widget.setEditable(False)
# # set yes/no buttons
# QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
# self.buttonBox = QDialogButtonBox(QBtn)
# self.buttonBox.accepted.connect(self.accept)
# self.buttonBox.rejected.connect(self.reject)
# self.layout = QVBoxLayout()
# # Text for the yes/no question
# message = QLabel(message)
# self.layout.addWidget(message)
# self.layout.addWidget(self.widget)
# self.layout.addWidget(self.buttonBox)
# self.setLayout(self.layout)
def parse_form(self) -> str:
"""
Pulls SubmissionType(str) from widget
# def getValues(self) -> str:
# """
# Get KitType(str) from widget
Returns:
str: SubmissionType as str
"""
return self.widget.currentText()
# Returns:
# str: KitType as str
# """
# return self.widget.currentText()
# class SubmissionTypeSelector(QDialog):
# """
# dialog to input SubmissionType manually
# """
# def __init__(self, title:str, message:str) -> QDialog:
# super().__init__()
# self.setWindowTitle(title)
# self.widget = QComboBox()
# # sub_type = [item.name for item in lookup_submission_type(ctx=ctx)]
# sub_type = [item.name for item in SubmissionType.query()]
# self.widget.addItems(sub_type)
# self.widget.setEditable(False)
# # set yes/no buttons
# QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
# self.buttonBox = QDialogButtonBox(QBtn)
# self.buttonBox.accepted.connect(self.accept)
# self.buttonBox.rejected.connect(self.reject)
# self.layout = QVBoxLayout()
# # Text for the yes/no question
# message = QLabel(message)
# self.layout.addWidget(message)
# self.layout.addWidget(self.widget)
# self.layout.addWidget(self.buttonBox)
# self.setLayout(self.layout)
# def parse_form(self) -> str:
# """
# Pulls SubmissionType(str) from widget
# Returns:
# str: SubmissionType as str
# """
# return self.widget.currentText()

View File

@@ -5,12 +5,12 @@ from PyQt6.QtWebChannel import QWebChannel
from PyQt6.QtCore import Qt, pyqtSlot
from backend.db.models import BasicSubmission, BasicSample
from tools import check_if_app, check_authorization, is_power_user
from tools import is_power_user, html_to_pdf
from .functions import select_save_file
from io import BytesIO
from tempfile import TemporaryFile, TemporaryDirectory
from pathlib import Path
from xhtml2pdf import pisa
# from xhtml2pdf import pisa
import logging, base64
from getpass import getuser
from datetime import datetime
@@ -54,6 +54,7 @@ class SubmissionDetails(QDialog):
self.channel = QWebChannel()
self.channel.registerObject('backend', self)
self.submission_details(submission=sub)
self.rsl_plate_num = sub.rsl_plate_num
self.webview.page().setWebChannel(self.channel)
@pyqtSlot(str)
@@ -86,9 +87,9 @@ class SubmissionDetails(QDialog):
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(submission.make_plate_barcode(width=120, height=30)).decode('utf-8')
# logger.debug(f"Creating barcode.")
# if not check_if_app():
# self.base_dict['barcode'] = base64.b64encode(submission.make_plate_barcode(width=120, height=30)).decode('utf-8')
logger.debug(f"Making platemap...")
self.base_dict['platemap'] = submission.make_plate_map()
self.base_dict, self.template = submission.get_details_template(base_dict=self.base_dict)
@@ -103,8 +104,9 @@ class SubmissionDetails(QDialog):
logger.debug(f"Signing off on {submission} - ({getuser()})")
if isinstance(submission, str):
submission = BasicSubmission.query(rsl_number=submission)
submission.uploaded_by = getuser()
submission.signed_by = getuser()
submission.save()
self.submission_details(submission=self.rsl_plate_num)
def export(self):
"""
@@ -113,7 +115,7 @@ class SubmissionDetails(QDialog):
fname = select_save_file(obj=self, default_name=self.base_dict['Plate Number'], extension="pdf")
image_io = BytesIO()
temp_dir = Path(TemporaryDirectory().name)
hti = Html2Image(output_path=temp_dir, size=(1200, 750))
hti = Html2Image(output_path=temp_dir, size=(2400, 1500))
temp_file = Path(TemporaryFile(dir=temp_dir, suffix=".png").name)
screenshot = hti.screenshot(self.base_dict['platemap'], save_as=temp_file.name)
export_map = Image.open(screenshot[0])
@@ -126,8 +128,9 @@ class SubmissionDetails(QDialog):
del self.base_dict['platemap']
self.html2 = self.template.render(sub=self.base_dict)
try:
with open(fname, "w+b") as f:
pisa.CreatePDF(self.html2, dest=f)
# with open(fname, "w+b") as f:
# pisa.CreatePDF(self.html2, dest=f)
html_to_pdf(html=self.html2, output_file=fname)
except PermissionError as e:
logger.error(f"Error saving pdf: {e}")
msg = QMessageBox()

View File

@@ -8,8 +8,8 @@ from PyQt6.QtCore import Qt, QAbstractTableModel, QSortFilterProxyModel
from PyQt6.QtGui import QAction, QCursor
from backend.db.models import BasicSubmission
from backend.excel import make_report_html, make_report_xlsx
from tools import Report, Result, row_map, get_first_blank_df_row
from xhtml2pdf import pisa
from tools import Report, Result, row_map, get_first_blank_df_row, html_to_pdf
# from xhtml2pdf import pisa
from .functions import select_save_file, select_open_file
from .misc import ReportDatePicker
import pandas as pd
@@ -324,8 +324,9 @@ class SubmissionsSheet(QTableView):
html = make_report_html(df=summary_df, start_date=info['start_date'], end_date=info['end_date'])
# get save location of report
fname = select_save_file(obj=self, default_name=f"Submissions_Report_{info['start_date']}-{info['end_date']}.pdf", extension="pdf")
with open(fname, "w+b") as f:
pisa.CreatePDF(html, dest=f)
# with open(fname, "w+b") as f:
# pisa.CreatePDF(html, dest=f)
html_to_pdf(html=html, output_file=fname)
writer = pd.ExcelWriter(fname.with_suffix(".xlsx"), engine='openpyxl')
summary_df.to_excel(writer, sheet_name="Report")
detailed_df.to_excel(writer, sheet_name="Details", index=False)

View File

@@ -135,7 +135,7 @@ class SubmissionFormContainer(QWidget):
info = dlg.parse_form()
logger.debug(f"Reagent info: {info}")
# create reagent object
reagent = PydReagent(ctx=self.app.ctx, **info)
reagent = PydReagent(ctx=self.app.ctx, **info, missing=False)
# send reagent to db
sqlobj, result = reagent.toSQL()
sqlobj.save()
@@ -150,45 +150,29 @@ class SubmissionFormWidget(QWidget):
# self.report = Report()
self.app = parent.app
self.pyd = submission
# self.input = [{k:v} for k,v in kwargs.items()]
# self.samples = []
self.missing_info = []
self.ignore = ['filepath', 'samples', 'reagents', 'csv', 'ctx', 'comment',
'equipment', 'gel_controls', 'id', 'cost', 'extraction_info',
'controls', 'pcr_info', 'gel_info', 'gel_image']
self.recover = ['filepath', 'samples', 'csv', 'comment', 'equipment']
st = SubmissionType.query(name=self.pyd.submission_type['value']).get_submission_class()
defaults = st.get_default_info("form_recover", "form_ignore")
self.recover = defaults['form_recover']
self.ignore = defaults['form_ignore']
# self.ignore += self.recover
# logger.debug(f"Attempting to extend ignore list with {self.pyd.submission_type['value']}")
self.layout = QVBoxLayout()
# for k, v in kwargs.items():
for k in list(self.pyd.model_fields.keys()) + list(self.pyd.model_extra.keys()):
if k not in self.ignore:
try:
value = self.pyd.__getattribute__(k)
except AttributeError:
logger.error(f"Couldn't get attribute from pyd: {k}")
value = dict(value=None, missing=True)
add_widget = self.create_widget(key=k, value=value, submission_type=self.pyd.submission_type['value'])
if add_widget != None:
self.layout.addWidget(add_widget)
if k == "extraction_kit":
add_widget.input.currentTextChanged.connect(self.scrape_reagents)
# else:
# self.__setattr__(k, v)
# self.scrape_reagents(self.extraction_kit['value'])
if k in self.ignore:
continue
try:
value = self.pyd.__getattribute__(k)
except AttributeError:
logger.error(f"Couldn't get attribute from pyd: {k}")
value = dict(value=None, missing=True)
add_widget = self.create_widget(key=k, value=value, submission_type=self.pyd.submission_type['value'])
if add_widget != None:
self.layout.addWidget(add_widget)
if k == "extraction_kit":
add_widget.input.currentTextChanged.connect(self.scrape_reagents)
self.scrape_reagents(self.pyd.extraction_kit)
# 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.pyd, "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.app.report.add_result(self.report)
# self.app.result_reporter()
def create_widget(self, key:str, value:dict|PydReagent, submission_type:str|None=None, extraction_kit:str|None=None) -> "self.InfoItem":
"""
@@ -633,6 +617,11 @@ class SubmissionFormWidget(QWidget):
self.reagent = reagent
self.extraction_kit = extraction_kit
layout = QVBoxLayout()
# layout = QGridLayout()
# self.check_box = QCheckBox(self)
# self.check_box.setChecked(True)
# self.check_box.stateChanged.connect(self.check_uncheck)
# layout.addWidget(self.check_box, 0,0)
self.label = self.ReagentParsedLabel(reagent=reagent)
layout.addWidget(self.label)
self.lot = self.ReagentLot(reagent=reagent, extraction_kit=extraction_kit)
@@ -645,6 +634,14 @@ class SubmissionFormWidget(QWidget):
# If changed set self.missing to True and update self.label
self.lot.currentTextChanged.connect(self.updated)
# def check_uncheck(self):
# if self.check_box.isChecked():
# self.lot.setCurrentIndex(0)
# self.lot.setEnabled(True)
# else:
# self.lot.setCurrentText("Not Applicable")
# self.lot.setEnabled(False)
def parse_form(self) -> Tuple[PydReagent, dict]:
"""
Pulls form info into PydReagent
@@ -652,6 +649,8 @@ class SubmissionFormWidget(QWidget):
Returns:
Tuple[PydReagent, dict]: PydReagent and Report(?)
"""
# if not self.check_box.isChecked():
# return None, None
lot = self.lot.currentText()
logger.debug(f"Using this lot for the reagent {self.reagent}: {lot}")
wanted_reagent = Reagent.query(lot_number=lot, reagent_type=self.reagent.type)
@@ -671,7 +670,7 @@ class SubmissionFormWidget(QWidget):
rt = ReagentType.query(name=self.reagent.type)
if rt == None:
rt = ReagentType.query(kit_type=self.extraction_kit, reagent=wanted_reagent)
return PydReagent(name=wanted_reagent.name, lot=wanted_reagent.lot, type=rt.name, expiry=wanted_reagent.expiry, parsed=not self.missing), None
return PydReagent(name=wanted_reagent.name, lot=wanted_reagent.lot, type=rt.name, expiry=wanted_reagent.expiry, missing=False), None
def updated(self):
"""
@@ -736,8 +735,11 @@ class SubmissionFormWidget(QWidget):
looked_up_reg = None
# logger.debug(f"Because there was no reagent listed for {reagent.lot}, we will insert the last lot used: {looked_up_reg}")
if looked_up_reg != None:
relevant_reagents.remove(str(looked_up_reg.lot))
relevant_reagents.insert(0, str(looked_up_reg.lot))
try:
relevant_reagents.remove(str(looked_up_reg.lot))
relevant_reagents.insert(0, str(looked_up_reg.lot))
except ValueError as e:
logger.error(f"Error reordering relevant reagents: {e}")
else:
if len(relevant_reagents) > 1:
# logger.debug(f"Found {reagent.lot} in relevant reagents: {relevant_reagents}. Moving to front of list.")