''' Creates forms that the user can enter equipment info into. ''' from pprint import pformat from PyQt6.QtCore import Qt from PyQt6.QtWidgets import (QDialog, QComboBox, QCheckBox, QLabel, QWidget, QVBoxLayout, QDialogButtonBox, QGridLayout) from backend.db.models import Equipment, BasicSubmission, Process from backend.validators.pydant import PydEquipment, PydEquipmentRole, PydTips import logging from typing import List logger = logging.getLogger(f"submissions.{__name__}") class EquipmentUsage(QDialog): def __init__(self, parent, submission: BasicSubmission): super().__init__(parent) self.submission = submission self.setWindowTitle(f"Equipment Checklist - {submission.rsl_plate_num}") self.used_equipment = self.submission.get_used_equipment() self.kit = self.submission.extraction_kit # logger.debug(f"Existing equipment: {self.used_equipment}") self.opt_equipment = submission.submission_type.get_equipment() # logger.debug(f"EquipmentRoles: {self.opt_equipment}") self.layout = QVBoxLayout() self.setLayout(self.layout) self.populate_form() def populate_form(self): """ Create form widgets """ 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) # logger.debug("Creating widgets for equipment") for eq in self.opt_equipment: widg = eq.to_form(parent=self, used=self.used_equipment) self.layout.addWidget(widg) widg.update_processes() self.layout.addWidget(self.buttonBox) def parse_form(self) -> List[PydEquipment]: """ Pull info from all RoleComboBox widgets Returns: List[PydEquipment]: All equipment pulled from widgets """ output = [] for widget in self.findChildren(QWidget): match widget: case RoleComboBox(): if widget.check.isChecked(): output.append(widget.parse_form()) case _: pass logger.debug(f"parsed output of Equsage form: {pformat(output)}") try: return [item.strip() for item in output if item is not None] except AttributeError: return [item for item in output if item is not None] class LabelRow(QWidget): 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): l = QLabel(item) l.setMaximumWidth(200) l.setMinimumWidth(200) self.layout.addWidget(l, 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()) class RoleComboBox(QWidget): def __init__(self, parent, role: PydEquipmentRole, used: list) -> None: super().__init__(parent) # self.layout = QHBoxLayout() self.layout = QGridLayout() self.role = role self.check = QCheckBox() if role.name in used: self.check.setChecked(False) else: self.check.setChecked(True) self.check.stateChanged.connect(self.toggle_checked) self.box = QComboBox() self.box.setMaximumWidth(200) self.box.setMinimumWidth(200) self.box.addItems([item.name for item in role.equipment]) self.box.currentTextChanged.connect(self.update_processes) self.process = QComboBox() self.process.setMaximumWidth(200) self.process.setMinimumWidth(200) self.process.setEditable(False) self.process.currentTextChanged.connect(self.update_tips) self.layout.addWidget(self.check, 0, 0) label = QLabel(f"{role.name}:") label.setMinimumWidth(200) label.setMaximumWidth(200) label.setAlignment(Qt.AlignmentFlag.AlignLeft) self.layout.addWidget(label, 0, 1) self.layout.addWidget(self.box, 0, 2) self.layout.addWidget(self.process, 0, 3) self.setLayout(self.layout) self.toggle_checked() def update_processes(self): """ Changes processes when equipment is changed """ equip = self.box.currentText() # logger.debug(f"Updating equipment: {equip}") equip2 = [item for item in self.role.equipment if item.name == equip][0] # logger.debug(f"Using: {equip2}") self.process.clear() self.process.addItems([item for item in equip2.processes if item in self.role.processes]) def update_tips(self): """ Changes what tips are available when process is changed """ process = self.process.currentText().strip() # logger.debug(f"Checking process: {process} for equipment {self.role.name}") process = Process.query(name=process) if process.tip_roles: for iii, tip_role in enumerate(process.tip_roles): widget = QComboBox() tip_choices = [item.name for item in tip_role.instances] widget.setEditable(False) widget.addItems(tip_choices) # logger.debug(f"Tiprole: {tip_role.__dict__}") widget.setObjectName(f"tips_{tip_role.name}") widget.setMinimumWidth(200) widget.setMaximumWidth(200) self.layout.addWidget(widget, iii, 4) else: widget = QLabel("") widget.setMinimumWidth(200) widget.setMaximumWidth(200) self.layout.addWidget(widget, 0, 4) def parse_form(self) -> PydEquipment | None: """ Creates PydEquipment for values in form Returns: PydEquipment|None: PydEquipment matching form """ eq = Equipment.query(name=self.box.currentText()) tips = [PydTips(name=item.currentText(), role=item.objectName().lstrip("tips").lstrip("_")) for item in self.findChildren(QComboBox) if item.objectName().startswith("tips")] logger.debug(tips) try: return PydEquipment( name=eq.name, processes=[self.process.currentText().strip()], role=self.role.name, asset_number=eq.asset_number, nickname=eq.nickname, tips=tips ) except Exception as e: logger.error(f"Could create PydEquipment due to: {e}") def toggle_checked(self): """ If this equipment is disabled, the input fields will be disabled. """ for widget in self.findChildren(QWidget): match widget: case QCheckBox(): continue case _: if self.check.isChecked(): widget.setEnabled(True) else: widget.setEnabled(False)