Frontend code cleanup finished.

This commit is contained in:
lwark
2025-09-05 15:23:56 -05:00
parent 610859d84f
commit c9396d6c41
25 changed files with 154 additions and 460 deletions

View File

@@ -484,7 +484,8 @@ class BaseClass(Base):
if check:
try:
value = json.dumps(value)
except TypeError:
except TypeError as e:
logger.error(f"Error json dumping value: {e}")
value = str(value)
try:
self._misc_info.update({key: value})

View File

@@ -917,7 +917,7 @@ class Procedure(BaseClass):
obj (_type_): parent widget
"""
logger.info(f"Add equipment")
from frontend.widgets.equipment_usage_2 import EquipmentUsage
from frontend.widgets.equipment_usage import EquipmentUsage
dlg = EquipmentUsage(parent=obj, procedure=self.to_pydantic())
if dlg.exec():
dlg.save_procedure()

View File

@@ -646,7 +646,7 @@ class Run(BaseClass, LogMixin):
'permission', "clientsubmission"]
output['sample_count'] = self.sample_count
output['clientsubmission'] = self.clientsubmission.name
output['clientlab'] = self.clientsubmission.clientlab
# output['clientlab'] = self.clientsubmission.clientlab
output['started_date'] = self.started_date
output['completed_date'] = self.completed_date
return output

View File

@@ -126,7 +126,7 @@ class DefaultTABLEParser(DefaultParser):
df = df.dropna(axis=1, how='all')
for ii, row in enumerate(df.iterrows()):
output = {}
for key, value in row[1].details_dict().items():
for key, value in row[1].to_dict().items():
if isinstance(key, str):
key = key.lower().replace(" ", "_")
key = re.sub(r"_(\(.*\)|#)", "", key)

View File

@@ -265,5 +265,5 @@ class RSLNamer(object):
return ""
from .pydant import PydRun, PydKitType, PydContact, PydClientLab, PydSample, PydReagent, PydReagentRole, \
from .pydant import PydRun, PydContact, PydClientLab, PydSample, PydReagent, PydReagentRole, \
PydEquipment, PydEquipmentRole, PydTips, PydProcess, PydElastic, PydClientSubmission, PydProcedure, PydResults

View File

@@ -1497,9 +1497,11 @@ class PydClientSubmission(PydBaseClass):
@field_validator("sample_count")
@classmethod
def enforce_integer(cls, value):
if not value['value']:
value['value'] = 0
try:
value['value'] = int(value['value'])
except ValueError:
except (ValueError, TypeError):
raise f"sample count value must be an integer"
return value

View File

@@ -1,10 +1,9 @@
'''
"""
Contains all operations for creating charts, graphs and visual effects.
'''
"""
from datetime import timedelta, date
from pathlib import Path
from typing import Generator
import plotly
from PyQt6.QtWidgets import QWidget
import pandas as pd, logging
@@ -128,13 +127,10 @@ class CustomFigure(Figure):
html = f'<html><body>'
if self is not None:
# NOTE: Just cannot get this load from string to freaking work.
# html += self.to_html(include_plotlyjs='cdn', full_html=False)
html += plotly.offline.plot(self, output_type='div', include_plotlyjs="cdn")
else:
html += "<h1>No data was retrieved for the given parameters.</h1>"
html += '</body></html>'
# with open("test.html", "w", encoding="utf-8") as f:
# f.write(html)
return html

View File

@@ -3,10 +3,9 @@ Construct BC control concentration charts
"""
from pprint import pformat
from . import CustomFigure
import plotly.express as px
import logging, sys, plotly.express as px
import pandas as pd
from PyQt6.QtWidgets import QWidget
import logging
from operator import itemgetter
logger = logging.getLogger(f"submissions.{__name__}")
@@ -31,7 +30,6 @@ class ConcentrationsChart(CustomFigure):
self.df = self.df.sort_values(['submitted_date', 'procedure'], ascending=[True, True]).reset_index(
drop=True)
self.df = self.df.reset_index().rename(columns={"index": "idx"})
# logger.debug(f"DF after changes:\n{self.df}")
scatter = px.scatter(data_frame=self.df, x='procedure', y="concentration",
hover_data=["name", "procedure", "submitted_date", "concentration"],
color="positive", color_discrete_map={"positive": "red", "negative": "green", "sample":"orange"}

View File

@@ -3,11 +3,9 @@ Functions for constructing irida control graphs using plotly.
"""
from datetime import date
from pprint import pformat
import plotly.express as px
import pandas as pd
import logging, plotly.express as px, pandas as pd
from PyQt6.QtWidgets import QWidget
from . import CustomFigure
import logging
from tools import get_unique_values_in_df_column
logger = logging.getLogger(f"submissions.{__name__}")

View File

@@ -22,9 +22,9 @@ from .date_type_picker import DateTypePicker
from .functions import select_save_file
from .pop_ups import HTMLPop
from .misc import Pagifier
from .submission_table import SubmissionsSheet, SubmissionsTree, ClientSubmissionRunModel
from .submission_table import SubmissionsTree, ClientSubmissionRunModel
from .submission_widget import SubmissionFormContainer
from .controls_chart import ControlsViewer
# from .controls_chart import ControlsViewer
from .summary import Summary
from .turnaround import TurnaroundTime
from .concentrations import Concentrations
@@ -132,7 +132,7 @@ class App(QMainWindow):
self.table_widget.pager.current_page.textChanged.connect(self.update_data)
self.editReagentAction.triggered.connect(self.edit_reagent)
self.manageOrgsAction.triggered.connect(self.manage_orgs)
self.manageKitsAction.triggered.connect(self.manage_kits)
# self.manageKitsAction.triggered.connect(self.manage_kits)
def showAbout(self):
"""
@@ -195,24 +195,23 @@ class App(QMainWindow):
new_org = dlg.parse_form()
new_org.save()
def manage_kits(self, *args, **kwargs):
from frontend.widgets.omni_manager_pydant import ManagerWindow as ManagerWindowPyd
dlg = ManagerWindowPyd(parent=self, object_type=KitType, extras=[], add_edit='edit', managers=set())
if dlg.exec():
# logger.debug("\n\nBeginning parsing\n\n")
output = dlg.parse_form()
# logger.debug(f"Kit output: {pformat(output.__dict__)}")
# logger.debug("\n\nBeginning transformation\n\n")
sql = output.to_sql()
assert isinstance(sql, KitType)
sql.save()
# def manage_kits(self, *args, **kwargs):
# from frontend.widgets.omni_manager_pydant import ManagerWindow as ManagerWindowPyd
# dlg = ManagerWindowPyd(parent=self, object_type=KitType, extras=[], add_edit='edit', managers=set())
# if dlg.exec():
# output = dlg.parse_form()
# sql = output.to_sql()
# assert isinstance(sql, KitType)
# sql.save()
@under_development
def submissions_to_excel(self, *args, **kwargs):
from backend.db.models import Run
dlg = DateTypePicker(self)
if dlg.exec():
output = dlg.parse_form()
df = BasicRun.archive_submissions(**output)
# TODO: Move to ClientSubmissions
df = Run.archive_submissions(**output)
filepath = select_save_file(self, f"Submissions {output['start_date']}-{output['end_date']}", "xlsx")
writer = ExcelWriter(filepath, "openpyxl")
df.to_excel(writer)
@@ -254,7 +253,6 @@ class AddSubForm(QWidget):
self.sheetwidget = QWidget(self)
self.sheetlayout = QVBoxLayout(self)
self.sheetwidget.setLayout(self.sheetlayout)
# self.sub_wid = SubmissionsSheet(parent=parent)
self.sub_wid = SubmissionsTree(parent=parent, model=ClientSubmissionRunModel(self))
self.pager = Pagifier(page_max=self.sub_wid.total_count / page_size)
self.sheetlayout.addWidget(self.sub_wid)
@@ -265,12 +263,10 @@ class AddSubForm(QWidget):
self.tab1.layout.addWidget(self.interior)
self.tab1.layout.addWidget(self.sheetwidget)
self.tab2.layout = QVBoxLayout(self)
# self.irida_viewer = ControlsViewer(self, archetype="Irida Control")
self.irida_viewer = None
self.tab2.layout.addWidget(self.irida_viewer)
self.tab2.setLayout(self.tab2.layout)
self.tab3.layout = QVBoxLayout(self)
# self.pcr_viewer = ControlsViewer(self, archetype="PCR Control")
self.pcr_viewer = None
self.tab3.layout.addWidget(self.pcr_viewer)
self.tab3.setLayout(self.tab3.layout)

View File

@@ -43,10 +43,8 @@ class Concentrations(InfoPane):
None
"""
include = self.pos_neg.get_checked()
# logger.debug(f"Include: {include}")
super().update_data()
months = self.diff_month(self.start_date, self.end_date)
# logger.debug(f"Box checked: {self.all_box.isChecked()}")
chart_settings = dict(start_date=self.start_date, end_date=self.end_date,
include=include)
self.report_obj = ConcentrationMaker(**chart_settings)

View File

@@ -108,7 +108,6 @@ class ControlsViewer(InfoPane):
parent=self,
months=months
)
# logger.debug(f"Chart settings: {chart_settings}")
self.fig = self.archetype.instance_class.make_chart(chart_settings=chart_settings, parent=self, ctx=self.app.ctx)
self.report_obj = ChartReportMaker(df=self.fig.df, sheet_name=self.archetype.name)
if issubclass(self.fig.__class__, CustomFigure):

View File

@@ -1,8 +1,14 @@
"""
"""
from PyQt6.QtWidgets import (
QVBoxLayout, QDialog, QDialogButtonBox
)
from .misc import CheckableComboBox, StartEndDatePicker
from backend.db.models.procedures import SubmissionType
import logging
logger = logging.getLogger(f"submissions.{__name__}")
class DateTypePicker(QDialog):
@@ -27,10 +33,7 @@ class DateTypePicker(QDialog):
self.setLayout(self.layout)
def parse_form(self):
# sub_types = [self.typepicker.itemText(i) for i in range(self.typepicker.count()) if self.typepicker.itemChecked(i)]
sub_types = self.typepicker.get_checked()
start_date = self.datepicker.start_date.date().toPyDate()
end_date = self.datepicker.end_date.date().toPyDate()
return dict(submissiontype=sub_types, start_date=start_date, end_date=end_date)

View File

@@ -1,91 +1,97 @@
'''
"""
Creates forms that the user can enter equipment info into.
'''
"""
import sys, logging
from pprint import pformat
from PyQt6.QtCore import Qt, QSignalBlocker
from PyQt6.QtWidgets import (
QDialog, QComboBox, QCheckBox, QLabel, QWidget, QVBoxLayout, QDialogButtonBox, QGridLayout
)
from backend.db.models import Equipment, Run, Process, Procedure
from backend.validators.pydant import PydEquipment, PydEquipmentRole, PydTips
import logging
from typing import Generator
from PyQt6.QtCore import Qt, pyqtSlot, QSignalBlocker
from PyQt6.QtWebChannel import QWebChannel
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWidgets import (
QDialog, QVBoxLayout, QDialogButtonBox, QGridLayout, QWidget, QCheckBox, QComboBox, QLabel
)
from backend import Process
from backend.db.models import Equipment
from backend.validators.pydant import PydProcedure, PydEquipmentRole, PydTips, PydEquipment
from tools import get_application_from_parent, render_details_template
logger = logging.getLogger(f"submissions.{__name__}")
class EquipmentUsage(QDialog):
def __init__(self, parent, procedure: Procedure):
def __init__(self, parent, procedure: PydProcedure):
super().__init__(parent)
self.procedure = procedure
self.setWindowTitle(f"Equipment Checklist - {procedure.name}")
self.used_equipment = self.procedure.equipment
# self.kit = self.procedure.kittype
self.kit = self.procedure.kittype
self.opt_equipment = procedure.proceduretype.get_equipment()
self.layout = QVBoxLayout()
self.app = get_application_from_parent(parent)
self.webview = QWebEngineView(parent=self)
self.webview.setContextMenuPolicy(Qt.ContextMenuPolicy.NoContextMenu)
self.webview.setMinimumSize(1200, 800)
self.webview.setMaximumWidth(1200)
# NOTE: Decide if exporting should be allowed.
self.layout = QGridLayout()
# NOTE: button to export a pdf version
self.layout.addWidget(self.webview, 1, 0, 10, 10)
self.setLayout(self.layout)
self.populate_form()
def populate_form(self):
"""
Create form widgets
"""
self.setFixedWidth(self.webview.width() + 20)
# NOTE: setup channel
self.channel = QWebChannel()
self.channel.registerObject('backend', self)
html = self.construct_html(procedure=procedure)
self.webview.setHtml(html)
self.webview.page().setWebChannel(self.channel)
QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
self.buttonBox = QDialogButtonBox(QBtn)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
label = self.LabelRow(parent=self)
self.layout.addWidget(label)
for equipment in self.opt_equipment:
widg = equipment.to_form(parent=self, used=self.used_equipment)
self.layout.addWidget(widg)
widg.update_processes()
self.layout.addWidget(self.buttonBox)
self.layout.addWidget(self.buttonBox, 11, 1, 1, 1)
def parse_form(self) -> Generator[PydEquipment, None, None]:
"""
Pull info from all RoleComboBox widgets
@classmethod
def construct_html(cls, procedure: PydProcedure, child: bool = False):
proceduretype = procedure.proceduretype
proceduretype_dict = proceduretype.details_dict()
run = procedure.run
html = render_details_template(
template_name="support/equipment_usage",
css_in=[],
js_in=[],
proceduretype=proceduretype_dict,
run=run.details_dict(),
procedure=procedure.__dict__,
child=child
)
return html
Returns:
Generator[PydEquipment, None, None]: All equipment pulled from widgets
"""
for widget in self.findChildren(QWidget):
match widget:
case RoleComboBox():
if widget.check.isChecked():
item = widget.parse_form()
if item:
yield item
else:
continue
else:
continue
case _:
continue
@pyqtSlot(str, str, str, str)
def update_equipment(self, equipmentrole: str, equipment: str, process: str, tips: str):
try:
equipment_of_interest = next(
(item for item in self.procedure.equipment if item.equipmentrole == equipmentrole))
except StopIteration:
equipment_of_interest = None
equipment = Equipment.query(name=equipment)
if equipment_of_interest:
eoi = self.procedure.equipment.pop(self.procedure.equipment.index(equipment_of_interest))
else:
eoi = equipment.to_pydantic(proceduretype=self.procedure.proceduretype)
eoi.name = equipment.name
eoi.asset_number = equipment.asset_number
eoi.nickname = equipment.nickname
process = next((prcss for prcss in equipment.process if prcss.name == process))
eoi.process = process.to_pydantic()
tips = next((tps for tps in equipment.tips if tps.name == tips))
eoi.tips = tips.to_pydantic()
self.procedure.equipment.append(eoi)
logger.debug(f"Updated equipment: {self.procedure.equipment}")
class LabelRow(QWidget):
"""Provides column headers"""
def __init__(self, parent) -> None:
super().__init__(parent)
self.layout = QGridLayout()
self.check = QCheckBox()
self.layout.addWidget(self.check, 0, 0)
self.check.stateChanged.connect(self.check_all)
for iii, item in enumerate(["Role", "Equipment", "Process", "Tips"], start=1):
label = QLabel(item)
label.setMaximumWidth(200)
label.setMinimumWidth(200)
self.layout.addWidget(label, 0, iii, alignment=Qt.AlignmentFlag.AlignRight)
self.setLayout(self.layout)
def check_all(self):
"""
Toggles all checkboxes in the form
"""
for object in self.parent().findChildren(QCheckBox):
object.setChecked(self.check.isChecked())
def save_procedure(self):
sql, _ = self.procedure.to_sql()
sql.save()
class RoleComboBox(QWidget):
@@ -124,7 +130,6 @@ class RoleComboBox(QWidget):
"""
equip = self.box.currentText()
equip2 = next((item for item in self.role.equipment if item.name == equip), self.role.equipment[0])
logger.debug(f"Equip2: {equip2}")
with QSignalBlocker(self.process) as blocker:
self.process.clear()
self.process.addItems([item for item in equip2.process if item in self.role.process])

View File

@@ -1,131 +0,0 @@
'''
Creates forms that the user can enter equipment info into.
'''
import sys
from pprint import pformat
from PyQt6.QtCore import Qt, QSignalBlocker, pyqtSlot
from PyQt6.QtWebChannel import QWebChannel
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWidgets import (
QDialog, QComboBox, QCheckBox, QLabel, QWidget, QVBoxLayout, QDialogButtonBox, QGridLayout
)
from backend.db.models import Equipment, Run, Process, Procedure, Tips
from backend.validators.pydant import PydEquipment, PydEquipmentRole, PydTips, PydProcedure
import logging
from typing import Generator
from tools import get_application_from_parent, render_details_template, flatten_list
logger = logging.getLogger(f"submissions.{__name__}")
class EquipmentUsage(QDialog):
def __init__(self, parent, procedure: PydProcedure):
super().__init__(parent)
self.procedure = procedure
self.setWindowTitle(f"Equipment Checklist - {procedure.name}")
self.used_equipment = self.procedure.equipment
self.kit = self.procedure.kittype
self.opt_equipment = procedure.proceduretype.get_equipment()
self.layout = QVBoxLayout()
self.app = get_application_from_parent(parent)
self.webview = QWebEngineView(parent=self)
self.webview.setContextMenuPolicy(Qt.ContextMenuPolicy.NoContextMenu)
self.webview.setMinimumSize(1200, 800)
self.webview.setMaximumWidth(1200)
# NOTE: Decide if exporting should be allowed.
# self.webview.loadFinished.connect(self.activate_export)
self.layout = QGridLayout()
# NOTE: button to export a pdf version
self.layout.addWidget(self.webview, 1, 0, 10, 10)
self.setLayout(self.layout)
self.setFixedWidth(self.webview.width() + 20)
# NOTE: setup channel
self.channel = QWebChannel()
self.channel.registerObject('backend', self)
html = self.construct_html(procedure=procedure)
self.webview.setHtml(html)
self.webview.page().setWebChannel(self.channel)
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.addWidget(self.buttonBox, 11, 1, 1, 1)
@classmethod
def construct_html(cls, procedure: PydProcedure, child: bool = False):
proceduretype = procedure.proceduretype
proceduretype_dict = proceduretype.details_dict()
run = procedure.run
# proceduretype_dict['equipment_json'] = flatten_list([item['equipment_json'] for item in proceduretype_dict['equipment']])
# proceduretype_dict['equipment_json'] = [
# {'name': 'Liquid Handler', 'equipment': [
# {'name': 'Other', 'asset_number': 'XXX', 'processes': [
# {'name': 'Trust Me', 'tips': ['Blah']},
# {'name': 'No Me', 'tips': ['Blah', 'Crane']}
# ]
# },
# {'name': 'Biomek', 'asset_number': '5015530', 'processes': [
# {'name': 'Sample Addition', 'tips': ['Axygen 20uL']
# }
# ]
# }
# ]
# }
# ]
# if procedure.equipment:
# for equipmentrole in proceduretype_dict['equipment']:
# # NOTE: Check if procedure equipment is present and move to head of the list if so.
# try:
# relevant_procedure_item = next((equipment for equipment in procedure.equipment if
# equipment.equipmentrole == equipmentrole['name']))
# except StopIteration:
# continue
# item_in_er_list = next((equipment for equipment in equipmentrole['equipment'] if
# equipment['name'] == relevant_procedure_item.name))
# equipmentrole['equipment'].insert(0, equipmentrole['equipment'].pop(
# equipmentrole['equipment'].index(item_in_er_list)))
html = render_details_template(
template_name="support/equipment_usage",
css_in=[],
js_in=[],
proceduretype=proceduretype_dict,
run=run.details_dict(),
procedure=procedure.__dict__,
child=child
)
return html
@pyqtSlot(str, str, str, str)
def update_equipment(self, equipmentrole: str, equipment: str, process: str, tips: str):
try:
equipment_of_interest = next(
(item for item in self.procedure.equipment if item.equipmentrole == equipmentrole))
except StopIteration:
equipment_of_interest = None
equipment = Equipment.query(name=equipment)
if equipment_of_interest:
eoi = self.procedure.equipment.pop(self.procedure.equipment.index(equipment_of_interest))
else:
eoi = equipment.to_pydantic(proceduretype=self.procedure.proceduretype)
eoi.name = equipment.name
eoi.asset_number = equipment.asset_number
eoi.nickname = equipment.nickname
process = next((prcss for prcss in equipment.process if prcss.name == process))
eoi.process = process.to_pydantic()
tips = next((tps for tps in equipment.tips if tps.name == tips))
eoi.tips = tips.to_pydantic()
self.procedure.equipment.append(eoi)
logger.debug(f"Updated equipment: {self.procedure.equipment}")
def save_procedure(self):
sql, _ = self.procedure.to_sql()
logger.debug(pformat(sql.__dict__))
# import pickle
# with open("sql.pickle", "wb") as f:
# pickle.dump(sql, f)
# with open("pyd.pickle", "wb") as f:
# pickle.dump(self.procedure, f)
sql.save()

View File

@@ -39,7 +39,7 @@ def select_open_file(obj: QMainWindow, file_extension: str | None = None) -> Pat
logger.warning(f"No file selected, cancelling.")
return
obj.last_dir = fname.parent
logger.debug(f"File selected: {fname}")
logger.info(f"File selected: {fname}")
return fname

View File

@@ -5,10 +5,9 @@ from operator import itemgetter
from PyQt6.QtWidgets import (
QWidget, QDialog, QGridLayout, QLabel, QLineEdit, QDialogButtonBox, QTextEdit, QComboBox
)
import pyqtgraph as pg
from PyQt6.QtGui import QIcon
from PIL import Image
import logging, numpy as np
import logging, numpy as np, pyqtgraph as pg
from pprint import pformat
from typing import Tuple, List
from pathlib import Path

View File

@@ -34,7 +34,6 @@ class InfoPane(QWidget):
report = Report()
self.start_date = self.datepicker.start_date.date().toPyDate()
self.end_date = self.datepicker.end_date.date().toPyDate()
# logger.debug(f"Start date: {self.start_date}, End date: {self.end_date}")
if self.datepicker.start_date.date() > self.datepicker.end_date.date():
lastmonth = self.datepicker.end_date.date().addDays(-31)
msg = f"Start date after end date is not allowed! Setting to {lastmonth.toString()}."

View File

@@ -1,7 +1,7 @@
"""
Contains miscellaneous widgets for frontend functions
"""
import math
import math, logging
from PyQt6.QtGui import QStandardItem, QIcon
from PyQt6.QtWidgets import (
QLabel, QLineEdit, QComboBox, QDateEdit, QPushButton, QWidget,
@@ -10,7 +10,6 @@ from PyQt6.QtWidgets import (
from PyQt6.QtCore import Qt, QDate, QSize
from tools import jinja_template_loading
from backend.db.models import *
import logging
logger = logging.getLogger(f"submissions.{__name__}")

View File

@@ -8,7 +8,7 @@ from PyQt6.QtCore import pyqtSlot, Qt
from PyQt6.QtWebChannel import QWebChannel
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWidgets import QDialog, QGridLayout, QDialogButtonBox
from typing import TYPE_CHECKING, Any, List
from typing import TYPE_CHECKING, List
if TYPE_CHECKING:
from backend.validators import PydProcedure, PydEquipment
from tools import get_application_from_parent, render_details_template, sanitize_object_for_json
@@ -52,7 +52,7 @@ class ProcedureCreation(QDialog):
def set_html(self):
from .equipment_usage_2 import EquipmentUsage
from .equipment_usage import EquipmentUsage
proceduretype_dict = self.proceduretype.details_dict()
# NOTE: Add --New-- as an option for reagents.
for key, value in self.procedure.reagentrole.items():
@@ -88,11 +88,6 @@ class ProcedureCreation(QDialog):
@pyqtSlot(str, str, str, str)
def update_equipment(self, equipmentrole: str, equipment: str, process: str, tips: str):
from backend.db.models import Equipment, ProcessVersion, TipsLot
logger.debug(f"Updating equipment with"
f"\n\tEquipment role: {equipmentrole}"
f"\n\tEquipment: {equipment}"
f"\n\tProcess: {process}"
f"\n\tTips: {tips}")
try:
equipment_of_interest = next(
(item for item in self.procedure.equipment if item.equipmentrole == equipmentrole))
@@ -109,19 +104,13 @@ class ProcedureCreation(QDialog):
process_name, version = process.split("-v")
process = ProcessVersion.query(name=process_name, version=version, limit=1)
eoi.process = process
# sys.exit(f"Process:\n{pformat(eoi.process.__dict__)}")
try:
tips_manufacturer, tipsref, lot = [item if item != "" else None for item in tips.split("-")]
logger.debug(f"Querying with '{tips_manufacturer}', '{tipsref}', '{lot}'")
tips = TipsLot.query(manufacturer=tips_manufacturer, ref=tipsref, lot=lot)
logger.debug(f"Found tips: {tips}")
eoi.tips = tips
except ValueError:
logger.warning(f"No tips info to unpack")
# tips = TipsLot.query(manufacturer=tips_manufacturer, ref=tipsref, lot=lot)
# eoi.tips = tips
self.procedure.equipment.append(eoi)
logger.debug(f"Updated equipment:\n{pformat([item.__dict__ for item in self.procedure.equipment])}")
@pyqtSlot(str, str)
def text_changed(self, key: str, new_value: str):
@@ -167,11 +156,9 @@ class ProcedureCreation(QDialog):
@pyqtSlot(str, str)
def update_reagent(self, reagentrole: str, name_lot_expiry: str):
logger.debug(f"{reagentrole}: {name_lot_expiry}")
try:
name, lot, expiry = name_lot_expiry.split(" - ")
except ValueError as e:
logger.debug(f"Couldn't perform split due to {e}")
return
self.procedure.update_reagents(reagentrole=reagentrole, name=name, lot=lot, expiry=expiry)

View File

@@ -1,5 +1,7 @@
"""
"""
import logging
from pathlib import Path
from typing import List
from PyQt6.QtCore import Qt, pyqtSlot
from PyQt6.QtWebChannel import QWebChannel
@@ -22,7 +24,6 @@ class SampleChecker(QDialog):
self.rsl_plate_number = RSLNamer.construct_new_plate_name(clientsubmission.to_dict())
else:
self.rsl_plate_number = clientsubmission
logger.debug(f"RSL Plate number: {self.rsl_plate_number}")
self.samples = samples
self.setWindowTitle(title)
self.app = get_application_from_parent(parent)
@@ -35,16 +36,11 @@ class SampleChecker(QDialog):
self.channel = QWebChannel()
self.channel.registerObject('backend', self)
# NOTE: Used to maintain javascript functions.
# template = env.get_template("sample_checker.html")
# template_path = Path(template.environment.loader.__getattribute__("searchpath")[0])
# with open(template_path.joinpath("css", "styles.css"), "r") as f:
# css = [f.read()]
try:
samples = self.formatted_list
except AttributeError as e:
logger.error(f"Problem getting sample list: {e}")
samples = []
# html = template.render(samples=samples, css=css, rsl_plate_number=self.rsl_plate_number)
html = render_details_template(template_name="sample_checker", samples=samples, rsl_plate_number=self.rsl_plate_number)
self.webview.setHtml(html)
self.webview.page().setWebChannel(self.channel)
@@ -55,13 +51,8 @@ class SampleChecker(QDialog):
self.layout.addWidget(self.buttonBox, 11, 9, 1, 1, alignment=Qt.AlignmentFlag.AlignRight)
self.setLayout(self.layout)
# with open("sample_checker_rendered.html", "w") as f:
# f.write(html)
logger.debug(f"HTML sample checker written!")
@pyqtSlot(str, str, str)
def text_changed(self, submission_rank: str, key: str, new_value: str):
logger.debug(f"Name: {submission_rank}, Key: {key}, Value: {new_value}")
try:
item = next((sample for sample in self.samples if int(submission_rank) == sample.submission_rank))
except StopIteration:
@@ -71,7 +62,6 @@ class SampleChecker(QDialog):
@pyqtSlot(int, bool)
def enable_sample(self, submission_rank: int, enabled: bool):
logger.debug(f"Name: {submission_rank}, Enabled: {enabled}")
try:
item = next((sample for sample in self.samples if int(submission_rank) == sample.submission_rank))
except StopIteration:
@@ -81,14 +71,12 @@ class SampleChecker(QDialog):
@pyqtSlot(str)
def set_rsl_plate_number(self, rsl_plate_number: str):
logger.debug(f"RSL plate num: {rsl_plate_number}")
self.rsl_plate_number = rsl_plate_number
@property
def formatted_list(self) -> List[dict]:
output = []
for sample in self.samples:
# logger.debug(sample)
s = sample.improved_dict(dictionaries=False)
if s['sample_id'] in [item['sample_id'] for item in output]:
s['color'] = "red"

View File

@@ -62,14 +62,9 @@ class SubmissionDetails(QDialog):
css = f.read()
key = object.__class__.__name__.lower()
d = {key: details}
# logger.debug(f"Using details: {pformat(d['procedure']['equipment'])}")
html = template.render(**d, css=[css])
self.webview.setHtml(html)
self.setWindowTitle(f"{object.__class__.__name__} Details - {object.name}")
# with open(f"{object.__class__.__name__}_details_rendered.html", "w") as f:
# f.write(html)
# pass
def activate_export(self) -> None:
"""
@@ -96,10 +91,10 @@ class SubmissionDetails(QDialog):
@pyqtSlot(str)
def equipment_details(self, equipment: str | Equipment):
logger.debug(f"Equipment details")
if isinstance(equipment, str):
equipment = Equipment.query(name=equipment)
base_dict = equipment.to_sub_dict(full_data=True)
# base_dict = equipment.to_sub_dict(full_data=True)
base_dict = equipment.details_dict()
template = equipment.details_template
template_path = Path(template.environment.loader.__getattribute__("searchpath")[0])
with open(template_path.joinpath("css", "styles.css"), "r") as f:
@@ -110,10 +105,10 @@ class SubmissionDetails(QDialog):
@pyqtSlot(str)
def process_details(self, process: str | Process):
logger.debug(f"Process details")
if isinstance(process, str):
process = Process.query(name=process)
base_dict = process.to_sub_dict(full_data=True)
# base_dict = process.to_sub_dict(full_data=True)
base_dict = process.details_dict()
template = process.details_template
template_path = Path(template.environment.loader.__getattribute__("searchpath")[0])
with open(template_path.joinpath("css", "styles.css"), "r") as f:
@@ -124,10 +119,10 @@ class SubmissionDetails(QDialog):
@pyqtSlot(str)
def tips_details(self, tips: str | Tips):
logger.debug(f"Equipment details: {tips}")
if isinstance(tips, str):
tips = Tips.query(lot=tips)
base_dict = tips.to_sub_dict(full_data=True)
# base_dict = tips.to_sub_dict(full_data=True)
base_dict = tips.details_dict()
template = tips.details_template
template_path = Path(template.environment.loader.__getattribute__("searchpath")[0])
with open(template_path.joinpath("css", "styles.css"), "r") as f:
@@ -144,10 +139,10 @@ class SubmissionDetails(QDialog):
Args:
sample (str): Submitter Id of the sample.
"""
logger.debug(f"Sample details.")
if isinstance(sample, str):
sample = Sample.query(sample_id=sample)
base_dict = sample.to_sub_dict(full_data=True)
# base_dict = sample.to_sub_dict(full_data=True)
base_dict = sample.details_dict()
exclude = ['procedure', 'excluded', 'colour', 'tooltip']
base_dict['excluded'] = exclude
template = sample.details_template
@@ -155,8 +150,6 @@ class SubmissionDetails(QDialog):
with open(template_path.joinpath("css", "styles.css"), "r") as f:
css = f.read()
html = template.render(sample=base_dict, css=css)
# with open(f"{sample.sample_id}.html", 'w') as f:
# f.write(html)
self.webview.setHtml(html)
self.setWindowTitle(f"Sample Details - {sample.sample_id}")
@@ -169,13 +162,13 @@ class SubmissionDetails(QDialog):
kit (str | KitType): Name of kittype.
reagent (str | Reagent): Lot number of the reagent
"""
logger.debug(f"Reagent details.")
if isinstance(reagent, str):
reagent = Reagent.query(lot=reagent)
if isinstance(proceduretype, str):
self.proceduretype = ProcedureType.query(name=proceduretype)
base_dict = reagent.to_sub_dict(proceduretype=self.proceduretype, full_data=True)
# base_dict = reagent.to_sub_dict(proceduretype=self.proceduretype, full_data=True)
# base_dict = reagent.details_dict(proceduretype=self.proceduretype, full_data=True)
base_dict = reagent.details_dict()
env = jinja_template_loading()
temp_name = "reagent_details.html"
try:
@@ -221,7 +214,6 @@ class SubmissionDetails(QDialog):
Args:
run (str | BasicRun): Submission of interest.
"""
logger.debug(f"Run details.")
if isinstance(run, str):
run = Run.query(name=run)
self.rsl_plate_number = run.rsl_plate_number
@@ -234,7 +226,6 @@ class SubmissionDetails(QDialog):
template_path = Path(self.template.environment.loader.__getattribute__("searchpath")[0])
with open(template_path.joinpath("css", "styles.css"), "r") as f:
css = f.read()
# logger.debug(f"Base dictionary of procedure {self.name}: {pformat(self.base_dict)}")
self.html = self.template.render(sub=self.base_dict, permission=is_power_user(), css=css)
self.webview.setHtml(self.html)

View File

@@ -4,13 +4,11 @@ Contains widgets specific to the procedure summary and procedure details.
import sys, logging, re
from pprint import pformat
from PyQt6.QtWidgets import QTableView, QMenu, QTreeView, QStyledItemDelegate, QStyle, QStyleOptionViewItem, \
QHeaderView, QAbstractItemView, QWidget, QTreeWidgetItemIterator
from PyQt6.QtCore import Qt, QAbstractTableModel, QSortFilterProxyModel, pyqtSlot, QModelIndex
from PyQt6.QtGui import QAction, QCursor, QStandardItemModel, QStandardItem, QIcon, QColor, QContextMenuEvent
from typing import Dict, List
# from backend import Procedure
from backend.db.models.submissions import Run, ClientSubmission
from backend.db.models.procedures import Procedure
@@ -91,7 +89,6 @@ class SubmissionsSheet(QTableView):
"""
sets data in model
"""
# self.data = ClientSubmission.submissions_to_df(page=page, page_size=page_size)
self.data = Run.submissions_to_df(page=page, page_size=page_size)
try:
self.data['Id'] = self.data['Id'].apply(str)
@@ -232,29 +229,6 @@ class SubmissionsSheet(QTableView):
return report
# class ClientSubmissionDelegate(QStyledItemDelegate):
#
# def __init__(self, parent=None):
# super(ClientSubmissionDelegate, self).__init__(parent)
# pixmapi = QStyle.StandardPixmap.SP_ToolBarHorizontalExtensionButton
# icon1 = QWidget().style().standardIcon(pixmapi)
# pixmapi = QStyle.StandardPixmap.SP_ToolBarVerticalExtensionButton
# icon2 = QWidget().style().standardIcon(pixmapi)
# self._plus_icon = icon1
# self._minus_icon = icon2
#
# def initStyleOption(self, option, index):
# super(ClientSubmissionDelegate, self).initStyleOption(option, index)
# if not index.parent().isValid():
# is_open = bool(option.state & QStyle.StateFlag.State_Open)
# option.features |= QStyleOptionViewItem.ViewItemFeature.HasDecoration
# option.icon = self._minus_icon if is_open else self._plus_icon
# class RunDelegate(ClientSubmissionDelegate):
# pass
class SubmissionsTree(QTreeView):
"""
https://stackoverflow.com/questions/54385437/how-can-i-make-a-table-that-can-collapse-its-rows-into-categories-in-qt
@@ -264,20 +238,12 @@ class SubmissionsTree(QTreeView):
super(SubmissionsTree, self).__init__(parent)
self.app = get_application_from_parent(parent)
self.total_count = ClientSubmission.__database_session__.query(ClientSubmission).count()
# self.setIndentation(0)
self.setExpandsOnDoubleClick(False)
# self.clicked.connect(self.on_clicked)
# delegate1 = ClientSubmissionDelegate(self)
# self.setItemDelegateForColumn(0, delegate1)
self.model = model
self.setModel(self.model)
# self.header().setSectionResizeMode(0, QHeaderView.sectionResizeMode(self,0).ResizeToContents)
self.setSelectionBehavior(QAbstractItemView.selectionBehavior(self).SelectRows)
# self.setStyleSheet("background-color: #0D1225;")
self.set_data()
self.doubleClicked.connect(self.show_details)
# self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
# self.customContextMenuRequested.connect(self.open_menu)
self.setStyleSheet("""
QTreeView {
background-color: #f5f5f5;
@@ -294,20 +260,13 @@ class SubmissionsTree(QTreeView):
}
""")
# Enable alternating row colors
# Note: Enable alternating row colors
self.setAlternatingRowColors(True)
self.setIndentation(20)
self.setItemsExpandable(True)
# self.expanded.connect(self.expand_item)
for ii in range(2):
self.resizeColumnToContents(ii)
# @pyqtSlot(QModelIndex)
# def on_clicked(self, index):
# if not index.parent().isValid() and index.column() == 0:
# self.setExpanded(index, not self.isExpanded(index))
def expand_item(self, event: QModelIndex):
logger.debug(f"Data: {event.data()}")
logger.debug(f"Parent {event.parent().data()}")
@@ -327,18 +286,11 @@ class SubmissionsTree(QTreeView):
"""
indexes = self.selectedIndexes()
dicto = next((item.data(1) for item in indexes if item.data(1)))
logger.debug(f"Dicto: {pformat(dicto)}")
query_obj = dicto['item_type'].query(name=dicto['query_str'], limit=1)
logger.debug(f"Querying: {query_obj}")
# NOTE: Convert to data in id column (i.e. column 0)
# id = id.sibling(id.row(), 0).data()
# logger.debug(id.model().query_group_object(id.row()))
# clientsubmission = id.model().query_group_object(id.row())
self.menu = QMenu(self)
self.con_actions = query_obj.custom_context_events
logger.debug(f"Context menu actions: {self.con_actions}")
for key in self.con_actions.keys():
logger.debug(key)
match key.lower():
case "add procedure":
action = QMenu(self.menu)
@@ -362,7 +314,7 @@ class SubmissionsTree(QTreeView):
action = QAction(key, self)
action.triggered.connect(lambda _, action_name=key: self.con_actions[action_name](obj=self))
self.menu.addAction(action)
# # NOTE: add other required actions
# NOTE: add other required actions
self.menu.popup(QCursor.pos())
def set_data(self, page: int = 1, page_size: int = 250) -> None:
@@ -372,8 +324,6 @@ class SubmissionsTree(QTreeView):
self.clear()
self.data = [item.to_dict(full_data=True) for item in
ClientSubmission.query(chronologic=True, page=page, page_size=page_size)]
# logger.debug(f"setting data:\n {pformat(self.data)}")
# sys.exit()
root = self.model.invisibleRootItem()
for submission in self.data:
group_str = f"{submission['submissiontype']}-{submission['submitter_plate_id']}-{submission['submitted_date']}"
@@ -382,26 +332,21 @@ class SubmissionsTree(QTreeView):
query_str=submission['submitter_plate_id'],
item_type=ClientSubmission
))
# logger.debug(f"Added {submission_item}")
for run in submission['run']:
# self.model.append_element_to_group(group_item=group_item, element=run)
run_item = self.model.add_child(parent=submission_item, child=dict(
name=run['plate_number'],
query_str=run['plate_number'],
item_type=Run
))
# logger.debug(f"Added {run_item}")
for procedure in run['procedures']:
procedure_item = self.model.add_child(parent=run_item, child=dict(
name=procedure['name'],
query_str=procedure['name'],
item_type=Procedure
))
# logger.debug(f"Added {procedure_item}")
def _populateTree(self, children, parent):
for child in children:
logger.debug(child)
child_item = QStandardItem(child['name'])
parent.appendRow(child_item)
if isinstance(children, List):
@@ -409,22 +354,13 @@ class SubmissionsTree(QTreeView):
def clear(self):
if self.model != None:
# self.model.clear() # works
self.model.setRowCount(0) # works
def show_details(self, sel: QModelIndex):
# id = self.selectionModel().currentIndex()
# NOTE: Convert to data in id column (i.e. column 0)
# id = id.sibling(id.row(), 1)
indexes = self.selectedIndexes()
dicto = next((item.data(1) for item in indexes if item.data(1)))
# try:
# id = int(id.data())
# except ValueError:
# return
# Run.query(id=id).show_details(self)
obj = dicto['item_type'].query(name=dicto['query_str'], limit=1)
logger.debug(obj)
obj.show_details(self)
def link_extractions(self):
@@ -437,14 +373,9 @@ class SubmissionsTree(QTreeView):
class ClientSubmissionRunModel(QStandardItemModel):
def __init__(self, parent=None):
super(ClientSubmissionRunModel, self).__init__(parent)
# headers = ["", "id", "Plate Number", "Started Date", "Completed Date", "Signed By"]
# self.setColumnCount(len(headers))
# self.setHorizontalHeaderLabels(headers)
# def __init__(self, parent=None):
# super(ClientSubmissionRunModel, self).__init__(parent)
#
def add_child(self, parent: QStandardItem, child:dict):
item = QStandardItem(child['name'])
item.setData(dict(item_type=child['item_type'], query_str=child['query_str']), 1)

View File

@@ -1,15 +1,13 @@
"""
Contains all procedure related frontend functions
"""
import sys
import sys, logging
from PyQt6.QtWidgets import (
QWidget, QPushButton, QVBoxLayout,
QComboBox, QDateEdit, QLineEdit, QLabel, QCheckBox, QHBoxLayout, QGridLayout
)
from PyQt6.QtCore import pyqtSignal, Qt, QSignalBlocker
from .functions import select_open_file, select_save_file
import logging
from pathlib import Path
from tools import Report, Result, check_not_nan, main_form_style, report_result, get_application_from_parent
from backend.validators import PydReagent, PydClientSubmission, PydSample
@@ -121,37 +119,16 @@ class SubmissionFormContainer(QWidget):
report.add_result(Result(msg=f"File {fname.__str__()} not found.", status="critical"))
return report
# NOTE: create sheetparser using excel sheet and context from gui
# try:
# self.clientsubmissionparser = ClientSubmissionInfoParser(filepath=fname)
# except PermissionError:
# logger.error(f"Couldn't get permission to access file: {fname}")
# return
# except AttributeError:
# self.clientsubmissionparser = ClientSubmissionInfoParser(filepath=fname)
# try:
# # self.prsr = SheetParser(filepath=fname)
# self.sampleparser = ClientSubmissionSampleParser(filepath=fname)
# except PermissionError:
# logger.error(f"Couldn't get permission to access file: {fname}")
# return
# except AttributeError:
# self.sampleparser = ClientSubmissionSampleParser(filepath=fname)
# self.pydclientsubmission = self.clientsubmissionparser.to_pydantic()
# self.pydsamples = self.sampleparser.to_pydantic()
# logger.debug(f"Samples: {pformat(self.pydclientsubmission.sample)}")
self.clientsubmission_manager = DefaultClientSubmissionManager(parent=self, input_object=fname)
self.pydclientsubmission = self.clientsubmission_manager.to_pydantic()
checker = SampleChecker(self, "Sample Checker", self.pydclientsubmission.sample)
if checker.exec():
# logger.debug(pformat(self.pydclientsubmission.sample))
try:
assert isinstance(self.pydclientsubmission, PydClientSubmission)
except AssertionError as e:
logger.error(f"Got wrong type for {self.pydclientsubmission}: {type(self.pydclientsubmission)}")
raise e
self.form = self.pydclientsubmission.to_form(parent=self)
# self.form.samples = self.pydsamples
self.layout().addWidget(self.form)
else:
message = "Submission cancelled."
@@ -195,14 +172,11 @@ class SubmissionFormWidget(QWidget):
self.pyd = pyd
self.missing_info = []
self.submissiontype = SubmissionType.query(name=self.pyd.submissiontype['value'])
# basic_submission_class = self.submission_type.submission_class
# logger.debug(f"Basic procedure class: {basic_submission_class}")
defaults = Run.get_default_info("form_recover", "form_ignore", submissiontype=self.pyd.submissiontype['value'])
self.recover = defaults['form_recover']
self.ignore = defaults['form_ignore']
self.layout = QVBoxLayout()
for k in list(self.pyd.model_fields.keys()):# + list(self.pyd.model_extra.keys()):
logger.debug(f"Pydantic field: {k}")
if k in self.ignore:
logger.warning(f"{k} in form_ignore {self.ignore}, not creating widget")
continue
@@ -218,7 +192,6 @@ class SubmissionFormWidget(QWidget):
value = self.pyd.model_extra[k]
except KeyError:
value = dict(value=None, missing=True)
logger.debug(f"Pydantic value: {value}")
add_widget = self.create_widget(key=k, value=value, submission_type=self.submissiontype,
run_object=Run(), disable=check)
if add_widget is not None:
@@ -230,7 +203,6 @@ class SubmissionFormWidget(QWidget):
self.layout.addWidget(self.disabler)
self.disabler.checkbox.checkStateChanged.connect(self.disable_reagents)
self.setStyleSheet(main_form_style)
# self.scrape_reagents(self.kittype)
self.setLayout(self.layout)
def disable_reagents(self):
@@ -298,7 +270,6 @@ class SubmissionFormWidget(QWidget):
if isinstance(reagent, self.ReagentFormWidget) or isinstance(reagent, QPushButton):
reagent.setParent(None)
reagents, integrity_report, missing_reagents = self.pyd.check_kit_integrity(extraction_kit=self.extraction_kit)
# logger.debug(f"Reagents: {reagents}")
expiry_report = self.pyd.check_reagent_expiries(exempt=missing_reagents)
for reagent in reagents:
add_widget = self.ReagentFormWidget(parent=self, reagent=reagent, extraction_kit=self.extraction_kit)
@@ -364,34 +335,6 @@ class SubmissionFormWidget(QWidget):
return report
base_submission = self.pyd.to_sql()
# NOTE: check output message for issues
# try:
# trigger = result.results[-1]
# code = trigger.code
# except IndexError as e:
# logger.error(result.results)
# logger.error(f"Problem getting error code: {e}")
# code = 0
# match code:
# # NOTE: code 0: everything is fine.
# case 0:
# pass
# # NOTE: code 1: ask for overwrite
# case 1:
# dlg = QuestionAsker(title=f"Review {base_submission.rsl_plate_number}?", message=trigger.msg)
# if dlg.exec():
# # NOTE: Do not add duplicate reagents.
# pass
# else:
# self.app.ctx.database_session.rollback()
# report.add_result(Result(msg="Overwrite cancelled", status="Information"))
# return report
# # NOTE: code 2: No RSL plate number given
# case 2:
# report.add_result(result)
# return report
# case _:
# pass
# NOTE: add reagents to procedure object
if base_submission is None:
return
for reagent in base_submission.reagents:
@@ -450,7 +393,6 @@ class SubmissionFormWidget(QWidget):
if field is not None:
info[field] = value
self.pyd.reagents = reagents
# logger.debug(f"Reagents from form: {reagents}")
for item in self.recover:
if hasattr(self, item):
value = getattr(self, item)
@@ -558,29 +500,29 @@ class SubmissionFormWidget(QWidget):
# NOTE: set combobox values to lookedup values
add_widget.addItems(labs)
add_widget.setToolTip("Select submitting lab.")
case 'kittype':
# NOTE: if extraction kittype not available, all other values fail
if not check_not_nan(value):
msg = AlertPop(message="Make sure to check your extraction kittype in the excel sheet!",
status="warning")
msg.exec()
# NOTE: create combobox to hold looked up kits
add_widget = MyQComboBox(scrollWidget=parent)
# NOTE: lookup existing kits by 'proceduretype' decided on by sheetparser
uses = [item.name for item in submission_type.kit_types]
obj.uses = uses
if check_not_nan(value):
try:
uses.insert(0, uses.pop(uses.index(value)))
except ValueError:
logger.warning(f"Couldn't find kittype in list, skipping move to top of list.")
obj.ext_kit = value
else:
logger.error(f"Couldn't find {obj.prsr.sub['kittype']}")
obj.ext_kit = uses[0]
add_widget.addItems(uses)
add_widget.setToolTip("Select extraction kittype.")
parent.extraction_kit = add_widget.currentText()
# case 'kittype':
# # NOTE: if extraction kittype not available, all other values fail
# if not check_not_nan(value):
# msg = AlertPop(message="Make sure to check your extraction kittype in the excel sheet!",
# status="warning")
# msg.exec()
# # NOTE: create combobox to hold looked up kits
# add_widget = MyQComboBox(scrollWidget=parent)
# # NOTE: lookup existing kits by 'proceduretype' decided on by sheetparser
# uses = [item.name for item in submission_type.kit_types]
# obj.uses = uses
# if check_not_nan(value):
# try:
# uses.insert(0, uses.pop(uses.index(value)))
# except ValueError:
# logger.warning(f"Couldn't find kittype in list, skipping move to top of list.")
# obj.ext_kit = value
# else:
# logger.error(f"Couldn't find {obj.prsr.sub['kittype']}")
# obj.ext_kit = uses[0]
# add_widget.addItems(uses)
# add_widget.setToolTip("Select extraction kittype.")
# parent.extraction_kit = add_widget.currentText()
case 'submission_category':
add_widget = MyQComboBox(scrollWidget=parent)
categories = ['Diagnostic', "Surveillance", "Research"]
@@ -813,11 +755,8 @@ class ClientSubmissionFormWidget(SubmissionFormWidget):
self.disabler.setHidden(True)
except AttributeError:
pass
# save_btn = QPushButton("Save")
self.sample = samples
logger.debug(f"Samples: {self.sample}")
start_run_btn = QPushButton("Save")
# self.layout.addWidget(save_btn)
self.layout.addWidget(start_run_btn)
start_run_btn.clicked.connect(self.create_new_submission)
@@ -846,7 +785,6 @@ class ClientSubmissionFormWidget(SubmissionFormWidget):
field, value = widget.parse_form()
if field is not None:
info[field] = value
# logger.debug(f"Reagents from form: {reagents}")
for item in self.recover:
if hasattr(self, item):
value = getattr(self, item)
@@ -865,7 +803,6 @@ class ClientSubmissionFormWidget(SubmissionFormWidget):
@report_result
def create_new_submission(self, *args) -> Report:
pyd = self.to_pydantic()
logger.debug(f"Pydantic: {pyd}")
sql = pyd.to_sql()
for sample in pyd.sample:
if isinstance(sample, PydSample):
@@ -874,9 +811,7 @@ class ClientSubmissionFormWidget(SubmissionFormWidget):
if sample.sample_id.lower() in ["", "blank"]:
continue
sample.save()
# if sample not in sql.sample:
sql.add_sample(sample=sample)
logger.debug(pformat(sql.__dict__))
try:
del sql._misc_info['sample']
except KeyError: