''' Contains miscellaneous widgets for frontend functions ''' from datetime import date from pprint import pformat from PyQt6 import QtCore from PyQt6.QtWidgets import ( QLabel, QVBoxLayout, QLineEdit, QComboBox, QDialog, QDialogButtonBox, QDateEdit, QSizePolicy, QWidget, QGridLayout, QPushButton, QSpinBox, QDoubleSpinBox, QHBoxLayout, QScrollArea, QFormLayout ) from PyQt6.QtCore import Qt, QDate, QSize from tools import check_not_nan, jinja_template_loading, Settings from backend.db.functions import \ lookup_reagent_types, lookup_reagents, lookup_submission_type, lookup_reagenttype_kittype_association, \ lookup_submissions#, construct_kit_from_yaml from backend.db.models import SubmissionTypeKitTypeAssociation from sqlalchemy import FLOAT, INTEGER import logging import numpy as np from .pop_ups import AlertPop, QuestionAsker from backend.validators import PydReagent, PydKit, PydReagentType, PydSubmission from typing import Tuple from pprint import pformat logger = logging.getLogger(f"submissions.{__name__}") env = jinja_template_loading() class AddReagentForm(QDialog): """ dialog to add gather info about new reagent """ def __init__(self, ctx:dict, reagent_lot:str|None=None, reagent_type:str|None=None, expiry:date|None=None, reagent_name:str|None=None) -> None: super().__init__() self.ctx = ctx if reagent_lot == None: reagent_lot = reagent_type self.setWindowTitle("Add Reagent") QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel self.buttonBox = QDialogButtonBox(QBtn) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) # widget to get lot info self.name_input = QComboBox() self.name_input.setObjectName("name") self.name_input.setEditable(True) self.name_input.setCurrentText(reagent_name) # self.name_input.setText(reagent_name) self.lot_input = QLineEdit() self.lot_input.setObjectName("lot") self.lot_input.setText(reagent_lot) # widget to get expiry info self.exp_input = QDateEdit(calendarPopup=True) self.exp_input.setObjectName('expiry') # if expiry is not passed in from gui, use today if expiry == None: self.exp_input.setDate(QDate.currentDate()) else: self.exp_input.setDate(expiry) # widget to get reagent type info self.type_input = QComboBox() self.type_input.setObjectName('type') self.type_input.addItems([item.name for item in lookup_reagent_types(ctx=ctx)]) logger.debug(f"Trying to find index of {reagent_type}") # convert input to user friendly string? try: reagent_type = reagent_type.replace("_", " ").title() except AttributeError: reagent_type = None # set parsed reagent type to top of list index = self.type_input.findText(reagent_type, Qt.MatchFlag.MatchEndsWith) if index >= 0: self.type_input.setCurrentIndex(index) self.layout = QVBoxLayout() self.layout.addWidget(QLabel("Name:")) self.layout.addWidget(self.name_input) self.layout.addWidget(QLabel("Lot:")) self.layout.addWidget(self.lot_input) self.layout.addWidget(QLabel("Expiry:\n(use exact date on reagent.\nEOL will be calculated from kit automatically)")) self.layout.addWidget(self.exp_input) self.layout.addWidget(QLabel("Type:")) self.layout.addWidget(self.type_input) self.layout.addWidget(self.buttonBox) self.setLayout(self.layout) self.type_input.currentTextChanged.connect(self.update_names) def parse_form(self): return dict(name=self.name_input.currentText(), lot=self.lot_input.text(), expiry=self.exp_input.date().toPyDate(), type=self.type_input.currentText()) def update_names(self): """ Updates reagent names form field with examples from reagent type """ logger.debug(self.type_input.currentText()) self.name_input.clear() lookup = lookup_reagents(ctx=self.ctx, reagent_type=self.type_input.currentText()) self.name_input.addItems(list(set([item.name for item in lookup]))) class ReportDatePicker(QDialog): """ custom dialog to ask for report start/stop dates """ def __init__(self) -> None: super().__init__() self.setWindowTitle("Select Report Date Range") # make confirm/reject buttons QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel self.buttonBox = QDialogButtonBox(QBtn) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) # widgets to ask for dates self.start_date = QDateEdit(calendarPopup=True) self.start_date.setObjectName("start_date") self.start_date.setDate(QDate.currentDate()) self.end_date = QDateEdit(calendarPopup=True) self.end_date.setObjectName("end_date") self.end_date.setDate(QDate.currentDate()) self.layout = QVBoxLayout() self.layout.addWidget(QLabel("Start Date")) self.layout.addWidget(self.start_date) self.layout.addWidget(QLabel("End Date")) self.layout.addWidget(self.end_date) self.layout.addWidget(self.buttonBox) self.setLayout(self.layout) def parse_form(self): return dict(start_date=self.start_date.date().toPyDate(), end_date = self.end_date.date().toPyDate()) class KitAdder(QWidget): """ dialog to get information to add kit """ def __init__(self, parent_ctx:Settings) -> None: super().__init__() self.ctx = parent_ctx main_box = QVBoxLayout(self) scroll = QScrollArea(self) main_box.addWidget(scroll) scroll.setWidgetResizable(True) scrollContent = QWidget(scroll) self.grid = QGridLayout() # self.setLayout(self.grid) scrollContent.setLayout(self.grid) # insert submit button at top self.submit_btn = QPushButton("Submit") self.grid.addWidget(self.submit_btn,0,0,1,1) self.grid.addWidget(QLabel("Kit Name:"),2,0) # widget to get kit name kit_name = QLineEdit() kit_name.setObjectName("kit_name") self.grid.addWidget(kit_name,2,1) self.grid.addWidget(QLabel("Used For Submission Type:"),3,0) # widget to get uses of kit used_for = QComboBox() used_for.setObjectName("used_for") # Insert all existing sample types used_for.addItems([item.name for item in lookup_submission_type(ctx=parent_ctx)]) used_for.setEditable(True) self.grid.addWidget(used_for,3,1) # Get all fields in SubmissionTypeKitTypeAssociation self.columns = [item for item in SubmissionTypeKitTypeAssociation.__table__.columns if len(item.foreign_keys) == 0] for iii, column in enumerate(self.columns): idx = iii + 4 # convert field name to human readable. field_name = column.name.replace("_", " ").title() self.grid.addWidget(QLabel(field_name),idx,0) match column.type: case FLOAT(): add_widget = QDoubleSpinBox() add_widget.setMinimum(0) add_widget.setMaximum(9999) case INTEGER(): add_widget = QSpinBox() add_widget.setMinimum(0) add_widget.setMaximum(9999) case _: add_widget = QLineEdit() add_widget.setObjectName(column.name) self.grid.addWidget(add_widget, idx,1) # self.grid.addWidget(QLabel("Constant cost per full plate (plates, work hours, etc.):"),4,0) # # widget to get constant cost # const_cost = QDoubleSpinBox() #QSpinBox() # const_cost.setObjectName("const_cost") # const_cost.setMinimum(0) # const_cost.setMaximum(9999) # self.grid.addWidget(const_cost,4,1) # self.grid.addWidget(QLabel("Cost per column (multidrop reagents, etc.):"),5,0) # # widget to get mutable costs per column # mut_cost_col = QDoubleSpinBox() #QSpinBox() # mut_cost_col.setObjectName("mut_cost_col") # mut_cost_col.setMinimum(0) # mut_cost_col.setMaximum(9999) # self.grid.addWidget(mut_cost_col,5,1) # self.grid.addWidget(QLabel("Cost per sample (tips, reagents, etc.):"),6,0) # # widget to get mutable costs per column # mut_cost_samp = QDoubleSpinBox() #QSpinBox() # mut_cost_samp.setObjectName("mut_cost_samp") # mut_cost_samp.setMinimum(0) # mut_cost_samp.setMaximum(9999) # self.grid.addWidget(mut_cost_samp,6,1) # button to add additional reagent types self.add_RT_btn = QPushButton("Add Reagent Type") self.grid.addWidget(self.add_RT_btn) self.add_RT_btn.clicked.connect(self.add_RT) self.submit_btn.clicked.connect(self.submit) scroll.setWidget(scrollContent) self.ignore = [None, "", "qt_spinbox_lineedit", "qt_scrollarea_viewport", "qt_scrollarea_hcontainer", "qt_scrollarea_vcontainer", "submit_btn" ] def add_RT(self) -> None: """ insert new reagent type row """ # get bottommost row maxrow = self.grid.rowCount() reg_form = ReagentTypeForm(ctx=self.ctx) reg_form.setObjectName(f"ReagentForm_{maxrow}") # self.grid.addWidget(reg_form, maxrow + 1,0,1,2) self.grid.addWidget(reg_form, maxrow,0,1,4) def submit(self) -> None: """ send kit to database """ # get form info info, reagents = self.parse_form() # info, reagents = extract_form_info(self) info = {k:v for k,v in info.items() if k in [column.name for column in self.columns] + ['kit_name', 'used_for']} logger.debug(f"kit info: {pformat(info)}") logger.debug(f"kit reagents: {pformat(reagents)}") info['reagent_types'] = reagents logger.debug(pformat(info)) # send to kit constructor kit = PydKit(name=info['kit_name']) for reagent in info['reagent_types']: uses = { info['used_for']: {'sheet':reagent['sheet'], 'name':reagent['name'], 'lot':reagent['lot'], 'expiry':reagent['expiry'] }} kit.reagent_types.append(PydReagentType(name=reagent['rtname'], eol_ext=reagent['eol'], uses=uses)) logger.debug(f"Output pyd object: {kit.__dict__}") # result = construct_kit_from_yaml(ctx=self.ctx, kit_dict=info) sqlobj, result = kit.toSQL(self.ctx) sqlobj.save(ctx=self.ctx) msg = AlertPop(message=result['message'], status=result['status']) msg.exec() self.__init__(self.ctx) def parse_form(self) -> Tuple[dict, list]: logger.debug(f"Hello from {self.__class__} parser!") info = {} reagents = [] widgets = [widget for widget in self.findChildren(QWidget) if widget.objectName() not in self.ignore and not isinstance(widget.parent(), ReagentTypeForm)] for widget in widgets: # logger.debug(f"Parsed widget: {widget.objectName()} of type {type(widget)} with parent {widget.parent()}") match widget: case ReagentTypeForm(): reagents.append(widget.parse_form()) case QLineEdit(): info[widget.objectName()] = widget.text() case QComboBox(): info[widget.objectName()] = widget.currentText() case QDateEdit(): info[widget.objectName()] = widget.date().toPyDate() return info, reagents class ReagentTypeForm(QWidget): """ custom widget to add information about a new reagenttype """ def __init__(self, ctx:Settings) -> None: super().__init__() grid = QGridLayout() self.setLayout(grid) grid.addWidget(QLabel("Reagent Type Name"),0,0) # Widget to get reagent info self.reagent_getter = QComboBox() self.reagent_getter.setObjectName("rtname") # lookup all reagent type names from db lookup = lookup_reagent_types(ctx=ctx) logger.debug(f"Looked up ReagentType names: {lookup}") self.reagent_getter.addItems([item.__str__() for item in lookup]) self.reagent_getter.setEditable(True) grid.addWidget(self.reagent_getter,0,1) grid.addWidget(QLabel("Extension of Life (months):"),0,2) # widget to get extension of life self.eol = QSpinBox() self.eol.setObjectName('eol') self.eol.setMinimum(0) grid.addWidget(self.eol, 0,3) grid.addWidget(QLabel("Excel Location Sheet Name:"),1,0) self.location_sheet_name = QLineEdit() self.location_sheet_name.setObjectName("sheet") self.location_sheet_name.setText("e.g. 'Reagent Info'") grid.addWidget(self.location_sheet_name, 1,1) for iii, item in enumerate(["Name", "Lot", "Expiry"]): idx = iii + 2 grid.addWidget(QLabel(f"{item} Row:"), idx, 0) row = QSpinBox() row.setFixedWidth(50) row.setObjectName(f'{item.lower()}_row') row.setMinimum(0) grid.addWidget(row, idx, 1) grid.addWidget(QLabel(f"{item} Column:"), idx, 2) col = QSpinBox() col.setFixedWidth(50) col.setObjectName(f'{item.lower()}_column') col.setMinimum(0) grid.addWidget(col, idx, 3) self.setFixedHeight(175) max_row = grid.rowCount() self.r_button = QPushButton("Remove") self.r_button.clicked.connect(self.remove) grid.addWidget(self.r_button,max_row,0,1,1) self.ignore = [None, "", "qt_spinbox_lineedit", "qt_scrollarea_viewport", "qt_scrollarea_hcontainer", "qt_scrollarea_vcontainer", "submit_btn", "eol", "sheet", "rtname" ] def remove(self): self.setParent(None) self.destroy() def parse_form(self) -> dict: logger.debug(f"Hello from {self.__class__} parser!") info = {} info['eol'] = self.eol.value() info['sheet'] = self.location_sheet_name.text() info['rtname'] = self.reagent_getter.currentText() widgets = [widget for widget in self.findChildren(QWidget) if widget.objectName() not in self.ignore] for widget in widgets: logger.debug(f"Parsed widget: {widget.objectName()} of type {type(widget)} with parent {widget.parent()}") match widget: case QLineEdit(): info[widget.objectName()] = widget.text() case QComboBox(): info[widget.objectName()] = widget.currentText() case QDateEdit(): info[widget.objectName()] = widget.date().toPyDate() case QSpinBox() | QDoubleSpinBox(): if "_" in widget.objectName(): key, sub_key = widget.objectName().split("_") if key not in info.keys(): info[key] = {} logger.debug(f"Adding key {key}, {sub_key} and value {widget.value()} to {info}") info[key][sub_key] = widget.value() return info class ControlsDatePicker(QWidget): """ custom widget to pick start and end dates for controls graphs """ def __init__(self) -> None: super().__init__() self.start_date = QDateEdit(calendarPopup=True) # start date is two months prior to end date by default twomonthsago = QDate.currentDate().addDays(-60) self.start_date.setDate(twomonthsago) self.end_date = QDateEdit(calendarPopup=True) self.end_date.setDate(QDate.currentDate()) self.layout = QHBoxLayout() self.layout.addWidget(QLabel("Start Date")) self.layout.addWidget(self.start_date) self.layout.addWidget(QLabel("End Date")) self.layout.addWidget(self.end_date) self.setLayout(self.layout) self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) def sizeHint(self) -> QSize: return QSize(80,20) class ImportReagent(QComboBox): def __init__(self, ctx:Settings, reagent:dict|PydReagent, extraction_kit:str): super().__init__() self.setEditable(True) if isinstance(reagent, dict): reagent = PydReagent(ctx=ctx, **reagent) # Ensure that all reagenttypes have a name that matches the items in the excel parser query_var = reagent.type logger.debug(f"Import Reagent is looking at: {reagent.lot} for {query_var}") if isinstance(reagent.lot, np.float64): logger.debug(f"{reagent.lot} is a numpy float!") try: reagent.lot = int(reagent.lot) except ValueError: pass # query for reagents using type name from sheet and kit from sheet logger.debug(f"Attempting lookup of reagents by type: {query_var}") # below was lookup_reagent_by_type_name_and_kit_name, but I couldn't get it to work. lookup = lookup_reagents(ctx=ctx, reagent_type=query_var) relevant_reagents = [item.__str__() for item in lookup] output_reg = [] for rel_reagent in relevant_reagents: # extract strings from any sets. if isinstance(rel_reagent, set): for thing in rel_reagent: output_reg.append(thing) elif isinstance(rel_reagent, str): output_reg.append(rel_reagent) relevant_reagents = output_reg # if reagent in sheet is not found insert it into the front of relevant reagents so it shows logger.debug(f"Relevant reagents for {reagent.lot}: {relevant_reagents}") if str(reagent.lot) not in relevant_reagents: if check_not_nan(reagent.lot): relevant_reagents.insert(0, str(reagent.lot)) else: # TODO: look up the last used reagent of this type in the database looked_up_rt = lookup_reagenttype_kittype_association(ctx=ctx, reagent_type=reagent.type, kit_type=extraction_kit) looked_up_reg = lookup_reagents(ctx=ctx, lot_number=looked_up_rt.last_used) logger.debug(f"Because there was no reagent listed for {reagent}, 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)) else: if len(relevant_reagents) > 1: logger.debug(f"Found {reagent.lot} in relevant reagents: {relevant_reagents}. Moving to front of list.") idx = relevant_reagents.index(str(reagent.lot)) logger.debug(f"The index we got for {reagent.lot} in {relevant_reagents} was {idx}") moved_reag = relevant_reagents.pop(idx) relevant_reagents.insert(0, moved_reag) else: logger.debug(f"Found {reagent.lot} in relevant reagents: {relevant_reagents}. But no need to move due to short list.") logger.debug(f"New relevant reagents: {relevant_reagents}") self.setObjectName(f"lot_{reagent.type}") self.addItems(relevant_reagents) class ParsedQLabel(QLabel): def __init__(self, input_object, field_name, title:bool=True, label_name:str|None=None): super().__init__() try: check = input_object['parsed'] except: return if label_name != None: self.setObjectName(label_name) if title: output = field_name.replace('_', ' ').title() else: output = field_name.replace('_', ' ') if check: self.setText(f"Parsed {output}") else: self.setText(f"MISSING {output}") class FirstStrandSalvage(QDialog): def __init__(self, ctx:Settings, submitter_id:str, rsl_plate_num:str|None=None) -> None: super().__init__() if rsl_plate_num == None: rsl_plate_num = "" self.setWindowTitle("Add Reagent") QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel self.buttonBox = QDialogButtonBox(QBtn) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.submitter_id_input = QLineEdit() self.submitter_id_input.setText(submitter_id) self.rsl_plate_num = QLineEdit() self.rsl_plate_num.setText(rsl_plate_num) self.row_letter = QComboBox() self.row_letter.addItems(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']) self.row_letter.setEditable(False) self.column_number = QComboBox() self.column_number.addItems(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12']) self.column_number.setEditable(False) self.layout = QFormLayout() self.layout.addRow(self.tr("&Sample Number:"), self.submitter_id_input) self.layout.addRow(self.tr("&Plate Number:"), self.rsl_plate_num) self.layout.addRow(self.tr("&Source Row:"), self.row_letter) self.layout.addRow(self.tr("&Source Column:"), self.column_number) self.layout.addWidget(self.buttonBox) self.setLayout(self.layout) def parse_form(self): return dict(plate=self.rsl_plate_num.text(), submitter_id=self.submitter_id_input.text(), well=f"{self.row_letter.currentText()}{self.column_number.currentText()}") class FirstStrandPlateList(QDialog): def __init__(self, ctx:Settings) -> None: super().__init__() self.setWindowTitle("First Strand Plates") QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel self.buttonBox = QDialogButtonBox(QBtn) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) ww = [item.rsl_plate_num for item in lookup_submissions(ctx=ctx, submission_type="Wastewater")] self.plate1 = QComboBox() self.plate2 = QComboBox() self.plate3 = QComboBox() self.layout = QFormLayout() for ii, plate in enumerate([self.plate1, self.plate2, self.plate3]): plate.addItems(ww) self.layout.addRow(self.tr(f"&Plate {ii+1}:"), plate) self.layout.addWidget(self.buttonBox) self.setLayout(self.layout) def parse_form(self): output = [] for plate in [self.plate1, self.plate2, self.plate3]: output.append(plate.currentText()) return output class ReagentFormWidget(QWidget): def __init__(self, parent:QWidget, reagent:PydReagent, extraction_kit:str): super().__init__() self.setParent(parent) logger.debug(f"Reagent form widget parent is: {self.parent()}") logger.debug(f"It's great grandparent is {self.parent().parent.parent} which has a method [add_reagent]: {hasattr(self.parent().parent.parent, 'add_reagent')}") self.reagent = reagent self.extraction_kit = extraction_kit self.ctx = reagent.ctx layout = QVBoxLayout() self.label = self.ReagentParsedLabel(reagent=reagent) layout.addWidget(self.label) self.lot = self.ReagentLot(reagent=reagent, extraction_kit=extraction_kit) layout.addWidget(self.lot) self.setLayout(layout) self.setObjectName(reagent.name) self.missing = not reagent.parsed def parse_form(self) -> Tuple[PydReagent, dict]: lot = self.lot.currentText() # type = self.label.text().replace("_label") wanted_reagent = lookup_reagents(ctx=self.ctx, lot_number=lot, reagent_type=self.reagent.type) if wanted_reagent == None: dlg = QuestionAsker(title=f"Add {lot}?", message=f"Couldn't find reagent type {self.reagent.type}: {lot} in the database.\n\nWould you like to add it?") if dlg.exec(): # logger.debug(f"Looking through {pformat(self.parent.reagents)} for reagent {reagent.name}") # try: # picked_reagent = [item for item in obj.reagents if item.type == reagent.name][0] # except IndexError: # logger.error(f"Couldn't find {reagent.name} in obj.reagents. Checking missing reagents {pprint.pformat(obj.missing_reagents)}") # picked_reagent = [item for item in obj.missing_reagents if item.type == reagent.name][0] # logger.debug(f"checking reagent: {reagent.name} in obj.reagents. Result: {picked_reagent}") # expiry_date = picked_reagent.expiry wanted_reagent = self.parent().parent.parent.add_reagent(reagent_lot=lot, reagent_type=self.reagent.type, expiry=self.reagent.expiry, name=self.reagent.name) return wanted_reagent, None else: # In this case we will have an empty reagent and the submission will fail kit integrity check logger.debug("Will not add reagent.") return None, dict(message="Failed integrity check", status="critical") else: rt = lookup_reagent_types(ctx=self.ctx, kit_type=self.extraction_kit, reagent=wanted_reagent) return PydReagent(ctx=self.ctx, name=wanted_reagent.name, lot=wanted_reagent.lot, type=rt.name, expiry=wanted_reagent.expiry, parsed=not self.missing), None class ReagentParsedLabel(QLabel): def __init__(self, reagent:PydReagent): super().__init__() try: check = reagent.parsed except: return self.setObjectName(f"{reagent.type}_label") if check: self.setText(f"Parsed {reagent.type}") else: self.setText(f"MISSING {reagent.type}") class ReagentLot(QComboBox): def __init__(self, reagent, extraction_kit:str) -> None: super().__init__() self.ctx = reagent.ctx self.setEditable(True) if reagent.parsed: pass logger.debug(f"Attempting lookup of reagents by type: {reagent.type}") # below was lookup_reagent_by_type_name_and_kit_name, but I couldn't get it to work. lookup = lookup_reagents(ctx=self.ctx, reagent_type=reagent.type) relevant_reagents = [item.__str__() for item in lookup] output_reg = [] for rel_reagent in relevant_reagents: # extract strings from any sets. if isinstance(rel_reagent, set): for thing in rel_reagent: output_reg.append(thing) elif isinstance(rel_reagent, str): output_reg.append(rel_reagent) relevant_reagents = output_reg # if reagent in sheet is not found insert it into the front of relevant reagents so it shows logger.debug(f"Relevant reagents for {reagent.lot}: {relevant_reagents}") if str(reagent.lot) not in relevant_reagents: if check_not_nan(reagent.lot): relevant_reagents.insert(0, str(reagent.lot)) else: # TODO: look up the last used reagent of this type in the database looked_up_rt = lookup_reagenttype_kittype_association(ctx=self.ctx, reagent_type=reagent.type, kit_type=extraction_kit) looked_up_reg = lookup_reagents(ctx=self.ctx, lot_number=looked_up_rt.last_used) logger.debug(f"Because there was no reagent listed for {reagent}, 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)) else: if len(relevant_reagents) > 1: logger.debug(f"Found {reagent.lot} in relevant reagents: {relevant_reagents}. Moving to front of list.") idx = relevant_reagents.index(str(reagent.lot)) logger.debug(f"The index we got for {reagent.lot} in {relevant_reagents} was {idx}") moved_reag = relevant_reagents.pop(idx) relevant_reagents.insert(0, moved_reag) else: logger.debug(f"Found {reagent.lot} in relevant reagents: {relevant_reagents}. But no need to move due to short list.") logger.debug(f"New relevant reagents: {relevant_reagents}") self.setObjectName(f"lot_{reagent.type}") self.addItems(relevant_reagents) class SubmissionFormWidget(QWidget): def __init__(self, parent: QWidget) -> None: super().__init__(parent) self.ignore = [None, "", "qt_spinbox_lineedit", "qt_scrollarea_viewport", "qt_scrollarea_hcontainer", "qt_scrollarea_vcontainer", "submit_btn" ] def clear_form(self): for item in self.findChildren(QWidget): item.setParent(None) def parse_form(self) -> PydSubmission: logger.debug(f"Hello from form parser!") info = {} reagents = [] samples = self.parent.parent.samples logger.debug(f"Using samples: {pformat(samples)}") widgets = [widget for widget in self.findChildren(QWidget) if widget.objectName() not in self.ignore] # widgets = [widget for widget in self.findChildren(QWidget)] for widget in widgets: logger.debug(f"Parsed widget: {widget.objectName()} of type {type(widget)}") match widget: case ReagentFormWidget(): reagent, _ = widget.parse_form() reagents.append(reagent) case ImportReagent(): reagent = dict(name=widget.objectName().replace("lot_", ""), lot=widget.currentText(), type=None, expiry=None) reagents.append(PydReagent(ctx=self.parent.parent.ctx, **reagent)) case QLineEdit(): info[widget.objectName()] = dict(value=widget.text()) case QComboBox(): info[widget.objectName()] = dict(value=widget.currentText()) case QDateEdit(): info[widget.objectName()] = dict(value=widget.date().toPyDate()) logger.debug(f"Info: {pformat(info)}") logger.debug(f"Reagents: {pformat(reagents)}") # sys.exit("Hi Landon. Check the reagents! frontend.__init__ line 442") submission = PydSubmission(ctx=self.parent.parent.ctx, filepath=self.parent.parent.current_file, reagents=reagents, samples=samples, **info) return submission