commit before refactor to use pyqt6 input field names

This commit is contained in:
Landon Wark
2023-03-14 13:13:10 -05:00
parent 285ccecd73
commit fc334155ff
11 changed files with 193 additions and 150 deletions

View File

@@ -1,6 +1,6 @@
# __init__.py
# Version of the realpython-reader package
__version__ = "202303.1b"
__version__ = "202303.2b"
__author__ = {"name":"Landon Wark", "email":"Landon.Wark@phac-aspc.gc.ca"}
__copyright__ = "2022-2023, Government of Canada"

View File

@@ -92,7 +92,7 @@ def store_reagent(ctx:dict, reagent:models.Reagent) -> None|dict:
def construct_submission_info(ctx:dict, info_dict:dict) -> models.BasicSubmission:
"""
Construct submission obejct from dictionary
Construct submission object from dictionary
Args:
ctx (dict): settings passed down from gui
@@ -110,7 +110,6 @@ def construct_submission_info(ctx:dict, info_dict:dict) -> models.BasicSubmissio
msg = "A proper RSL plate number is required."
return instance, {'code': 2, 'message': "A proper RSL plate number is required."}
instance = ctx['database_session'].query(models.BasicSubmission).filter(models.BasicSubmission.rsl_plate_num==info_dict['rsl_plate_num']).first()
# get model based on submission type converted above
logger.debug(f"Looking at models for submission type: {query}")
model = getattr(models, query)
@@ -164,7 +163,6 @@ def construct_submission_info(ctx:dict, info_dict:dict) -> models.BasicSubmissio
logger.debug(f"Looks like that kit doesn't have cost breakdown yet, using full plate cost.")
instance.run_cost = instance.extraction_kit.cost_per_run
# We need to make sure there's a proper rsl plate number
try:
logger.debug(f"Constructed instance: {instance.to_string()}")
except AttributeError as e:
@@ -196,12 +194,13 @@ def construct_reagent(ctx:dict, info_dict:dict) -> models.Reagent:
case "type":
reagent.type = lookup_reagenttype_by_name(ctx=ctx, rt_name=info_dict[item].replace(" ", "_").lower())
# add end-of-life extension from reagent type to expiry date
try:
reagent.expiry = reagent.expiry + reagent.type.eol_ext
except TypeError as e:
logger.debug(f"We got a type error: {e}.")
except AttributeError:
pass
# Edit: this will now be done only in the reporting phase to account for potential changes in end-of-life extensions
# try:
# reagent.expiry = reagent.expiry + reagent.type.eol_ext
# except TypeError as e:
# logger.debug(f"We got a type error: {e}.")
# except AttributeError:
# pass
return reagent
@@ -465,7 +464,7 @@ def create_kit_from_yaml(ctx:dict, exp:dict) -> dict:
# except KeyError:
if not check_is_power_user(ctx=ctx):
logger.debug(f"{getuser()} does not have permission to add kits.")
return {'code':1, 'message':"This user does not have permission to add kits."}
return {'code':1, 'message':"This user does not have permission to add kits.", "status":"warning"}
for type in exp:
if type == "password":
continue
@@ -486,7 +485,7 @@ def create_kit_from_yaml(ctx:dict, exp:dict) -> dict:
logger.debug(kit.__dict__)
ctx['database_session'].add(kit)
ctx['database_session'].commit()
return {'code':0, 'message':'Kit has been added'}
return {'code':0, 'message':'Kit has been added', 'status': 'information'}
def create_org_from_yaml(ctx:dict, org:dict) -> dict:
"""

View File

@@ -39,9 +39,9 @@ class Control(Base):
# UniqueConstraint('name', name='uq_control_name')
submission_id = Column(INTEGER, ForeignKey("_submissions.id")) #: parent submission id
submission = relationship("BacterialCulture", back_populates="controls", foreign_keys=[submission_id]) #: parent submission
refseq_version = Column(String(16))
kraken2_version = Column(String(16))
kraken2_db_version = Column(String(32))
refseq_version = Column(String(16)) #: version of refseq used in fastq parsing
kraken2_version = Column(String(16)) #: version of kraken2 used in fastq parsing
kraken2_db_version = Column(String(32)) #: folder name of kraken2 db
def to_sub_dict(self) -> dict:
@@ -51,17 +51,22 @@ class Control(Base):
Returns:
dict: output dictionary containing: Name, Type, Targets, Top Kraken results
"""
# load json string into dict
kraken = json.loads(self.kraken)
# calculate kraken count total to use in percentage
kraken_cnt_total = sum([kraken[item]['kraken_count'] for item in kraken])
new_kraken = []
for item in kraken:
# calculate kraken percent (overwrites what's already been scraped)
kraken_percent = kraken[item]['kraken_count'] / kraken_cnt_total
new_kraken.append({'name': item, 'kraken_count':kraken[item]['kraken_count'], 'kraken_percent':"{0:.0%}".format(kraken_percent)})
new_kraken = sorted(new_kraken, key=itemgetter('kraken_count'), reverse=True)
# set targets
if self.controltype.targets == []:
targets = ["None"]
else:
targets = self.controltype.targets
# construct output dictionary
output = {
"name" : self.name,
"type" : self.controltype.name,
@@ -72,36 +77,43 @@ class Control(Base):
def convert_by_mode(self, mode:str) -> list[dict]:
"""
split control object into analysis types
split control object into analysis types for controls graphs
Args:
control (models.Control): control to be parsed into list
mode (str): analysis type
mode (str): analysis type, 'contains', etc
Returns:
list[dict]: list of records
"""
output = []
# load json string for mode (i.e. contains, matches, kraken2)
data = json.loads(getattr(self, mode))
# if len(data) == 0:
# data = self.create_dummy_data(mode)
logger.debug(f"Length of data: {len(data)}")
# dict keys are genera of bacteria, e.g. 'Streptococcus'
for genus in data:
_dict = {}
_dict['name'] = self.name
_dict['submitted_date'] = self.submitted_date
_dict['genus'] = genus
# get Target or Off-target of genus
_dict['target'] = 'Target' if genus.strip("*") in self.controltype.targets else "Off-target"
# set 'contains_hashes', etc for genus,
for key in data[genus]:
_dict[key] = data[genus][key]
if _dict[key] == {}:
print(self.name, mode)
output.append(_dict)
# logger.debug(output)
return output
def create_dummy_data(self, mode):
def create_dummy_data(self, mode:str) -> dict:
"""
Create non-zero length data to maintain entry of zero length 'contains' (depreciated)
Args:
mode (str): analysis type, 'contains', etc
Returns:
dict: dictionary of 'Nothing' genus
"""
match mode:
case "contains":
data = {"Nothing": {"contains_hashes":"0/400", "contains_ratio":0.0}}

View File

@@ -1,6 +1,10 @@
from copy import deepcopy
from . import Base
from sqlalchemy import Column, String, TIMESTAMP, JSON, INTEGER, ForeignKey, Interval, Table, FLOAT
from sqlalchemy.orm import relationship
import logging
logger = logging.getLogger(f'submissions.{__name__}')
# Table containing reagenttype-kittype relationships
@@ -44,7 +48,6 @@ class ReagentType(Base):
kit_id = Column(INTEGER, ForeignKey("_kits.id", ondelete="SET NULL", use_alter=True, name="fk_RT_kits_id")) #: id of joined kit type
kits = relationship("KitType", back_populates="reagent_types", uselist=True, foreign_keys=[kit_id]) #: kits this reagent is used in
instances = relationship("Reagent", back_populates="type") #: concrete instances of this reagent type
# instances_id = Column(INTEGER, ForeignKey("_reagents.id", ondelete='SET NULL'))
eol_ext = Column(Interval()) #: extension of life interval
def __str__(self) -> str:
@@ -76,9 +79,11 @@ class Reagent(Base):
string representing this object
Returns:
str: string representing this object's lot number
str: string representing this object's type and lot number
"""
return str(self.lot)
lot = str(self.lot)
r_type = str(self.type)
return f"{r_type} - {lot}"
def to_sub_dict(self) -> dict:
"""
@@ -91,8 +96,15 @@ class Reagent(Base):
type = self.type.name.replace("_", " ").title()
except AttributeError:
type = "Unknown"
try:
place_holder = self.expiry + self.type.eol_ext
# logger.debug(f"EOL_ext for {self.lot} -- {self.expiry} + {self.type.eol_ext} = {place_holder}")
except TypeError as e:
logger.debug(f"We got a type error setting {self.lot} expiry: {e}.")
except AttributeError as e:
logger.debug(f"We got an attribute error setting {self.lot} expiry: {e}.")
return {
"type": type,
"lot": self.lot,
"expiry": self.expiry.strftime("%Y-%m-%d")
"expiry": place_holder.strftime("%Y-%m-%d")
}

View File

@@ -1,6 +1,6 @@
from . import Base
from sqlalchemy import Column, String, TIMESTAMP, JSON, Float, INTEGER, ForeignKey, UniqueConstraint, Table
from sqlalchemy.orm import relationship, validates
from sqlalchemy import Column, String, INTEGER, ForeignKey, Table
from sqlalchemy.orm import relationship
# table containing organization/contact relationship
@@ -32,7 +32,7 @@ class Organization(Base):
class Contact(Base):
"""
Base of Contace
Base of Contact
"""
__tablename__ = "_contacts"

View File

@@ -10,7 +10,7 @@ class WWSample(Base):
__tablename__ = "_ww_samples"
id = Column(INTEGER, primary_key=True) #: primary key
ww_processing_num = Column(String(64))
ww_processing_num = Column(String(64)) #: wastewater processing number
ww_sample_full_id = Column(String(64), nullable=False)
rsl_number = Column(String(64)) #: rsl plate identification number
rsl_plate = relationship("Wastewater", back_populates="samples") #: relationship to parent plate
@@ -40,7 +40,7 @@ class WWSample(Base):
gui friendly dictionary
Returns:
dict: well location and id
dict: well location and id NOTE: keys must sync with BCSample to_sub_dict below
"""
return {
"well": self.well_number,
@@ -76,7 +76,7 @@ class BCSample(Base):
gui friendly dictionary
Returns:
dict: well location and name (sample id, organism)
dict: well location and name (sample id, organism) NOTE: keys must sync with WWSample to_sub_dict above
"""
return {
"well": self.well_number,

View File

@@ -22,18 +22,18 @@ class BasicSubmission(Base):
submitter_plate_num = Column(String(127), unique=True) #: The number given to the submission by the submitting lab
submitted_date = Column(TIMESTAMP) #: Date submission received
submitting_lab = relationship("Organization", back_populates="submissions") #: client org
submitting_lab_id = Column(INTEGER, ForeignKey("_organizations.id", ondelete="SET NULL", name="fk_BS_sublab_id"))
submitting_lab_id = Column(INTEGER, ForeignKey("_organizations.id", ondelete="SET NULL", name="fk_BS_sublab_id")) #: client lab id from _organizations
sample_count = Column(INTEGER) #: Number of samples in the submission
extraction_kit = relationship("KitType", back_populates="submissions") #: The extraction kit used
extraction_kit_id = Column(INTEGER, ForeignKey("_kits.id", ondelete="SET NULL", name="fk_BS_extkit_id"))
submission_type = Column(String(32)) #: submission type (should be string in D3 of excel sheet)
technician = Column(String(64)) #: initials of processing tech
technician = Column(String(64)) #: initials of processing tech(s)
# Move this into custom types?
reagents = relationship("Reagent", back_populates="submissions", secondary=reagents_submissions) #: relationship to reagents
reagents_id = Column(String, ForeignKey("_reagents.id", ondelete="SET NULL", name="fk_BS_reagents_id")) #: id of used reagents
extraction_info = Column(JSON) #: unstructured output from the extraction table logger.
run_cost = Column(FLOAT(2))
uploaded_by = Column(String(32))
run_cost = Column(FLOAT(2)) #: total cost of running the plate. Set from kit costs at time of creation.
uploaded_by = Column(String(32)) #: user name of person who submitted the submission to the database.
__mapper_args__ = {
"polymorphic_identity": "basic_submission",
@@ -71,6 +71,7 @@ class BasicSubmission(Base):
ext_kit = self.extraction_kit.name
except AttributeError:
ext_kit = None
# load scraped extraction info
try:
ext_info = json.loads(self.extraction_info)
except TypeError:
@@ -80,7 +81,8 @@ class BasicSubmission(Base):
logger.debug(f"Json error in {self.rsl_plate_num}: {e}")
try:
reagents = [item.to_sub_dict() for item in self.reagents]
except:
except Exception as e:
logger.error(f"We got an error retrieving reagents: {e}")
reagents = None
try:
samples = [item.to_sub_dict() for item in self.samples]

View File

@@ -1,5 +1,6 @@
import json
import re
from typing import Tuple
from PyQt6.QtWidgets import (
QMainWindow, QLabel, QToolBar,
QTabWidget, QWidget, QVBoxLayout,
@@ -30,14 +31,14 @@ from backend.db import (construct_submission_info, lookup_reagent,
)
from backend.db import lookup_kittype_by_name
from .functions import check_kit_integrity, insert_reagent_import
from tools import check_not_nan, create_reagent_list
from .functions import check_kit_integrity
from tools import check_not_nan
from backend.excel.reports import make_report_xlsx, make_report_html
import numpy
from frontend.custom_widgets.sub_details import SubmissionsSheet
from frontend.custom_widgets.pop_ups import AlertPop, QuestionAsker
from frontend.custom_widgets import AddReagentForm, ReportDatePicker, KitAdder, ControlsDatePicker
from frontend.custom_widgets import AddReagentForm, ReportDatePicker, KitAdder, ControlsDatePicker, ImportReagent
import logging
import difflib
from getpass import getuser
@@ -172,7 +173,6 @@ class App(QMainWindow):
(?P<samples>)^samples$ |
(?P<reagent>^lot_.*$)
""", re.VERBOSE)
# reagents = []
for item in prsr.sub:
logger.debug(f"Item: {item}")
# attempt to match variable name to regex group
@@ -201,16 +201,16 @@ class App(QMainWindow):
self.table_widget.formlayout.addWidget(QLabel(item.replace("_", " ").title()))
# if extraction kit not available, all other values fail
if not check_not_nan(prsr.sub[item]):
msg = AlertPop(message="Make sure to check your extraction kit!", status="warning")
msg = AlertPop(message="Make sure to check your extraction kit in the excel sheet!", status="warning")
msg.exec()
# create combobox to hold looked up kits
add_widget = QComboBox()
# lookup existing kits by 'submission_type' decided on by sheetparser
uses = [item.__str__() for item in lookup_kittype_by_use(ctx=self.ctx, used_by=prsr.sub['submission_type'])]
if len(uses) > 0:
# if len(uses) > 0:
add_widget.addItems(uses)
else:
add_widget.addItems(['bacterial_culture'])
# else:
# add_widget.addItems(['bacterial_culture'])
self.ext_kit = prsr.sub[item]
case 'submitted_date':
# create label
@@ -224,40 +224,10 @@ class App(QMainWindow):
except:
add_widget.setDate(date.today())
case 'reagent':
# create label
self.table_widget.formlayout.addWidget(QLabel(item.replace("_", " ").title()))
# add_widget = QComboBox()
# add_widget.setEditable(True)
# # Ensure that all reagenttypes have a name that matches the items in the excel parser
# query_var = item.replace("lot_", "")
# logger.debug(f"Query for: {query_var}")
# if isinstance(prsr.sub[item], numpy.float64):
# logger.debug(f"{prsr.sub[item]['lot']} is a numpy float!")
# try:
# prsr.sub[item] = int(prsr.sub[item]['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.
# relevant_reagents = [item.__str__() for item in lookup_regent_by_type_name(ctx=self.ctx, type_name=query_var)]#, kit_name=prsr.sub['extraction_kit'])]
# output_reg = []
# for reagent in relevant_reagents:
# if isinstance(reagent, set):
# for thing in reagent:
# output_reg.append(thing)
# elif isinstance(reagent, str):
# output_reg.append(reagent)
# relevant_reagents = output_reg
# logger.debug(f"Relevant reagents for {prsr.sub[item]}: {relevant_reagents}")
# # if reagent in sheet is not found insert it into items
# if str(prsr.sub[item]['lot']) not in relevant_reagents and prsr.sub[item]['lot'] != 'nan':
# if check_not_nan(prsr.sub[item]['lot']):
# relevant_reagents.insert(0, str(prsr.sub[item]['lot']))
# logger.debug(f"New relevant reagents: {relevant_reagents}")
# add_widget.addItems(relevant_reagents)
add_widget = insert_reagent_import(ctx=self.ctx, item=item, prsr=prsr)
# create reagent choice widget
add_widget = ImportReagent(ctx=self.ctx, item=item, prsr=prsr)
self.reagents[item] = prsr.sub[item]
case 'samples':
# hold samples in 'self' until form submitted
@@ -278,7 +248,7 @@ class App(QMainWindow):
msg.exec()
for item in kit_integrity['missing']:
self.table_widget.formlayout.addWidget(QLabel(f"Lot {item.replace('_', ' ').title()}"))
add_widget = insert_reagent_import(ctx=self.ctx, item=item)
add_widget = ImportReagent(ctx=self.ctx, item=item)
self.table_widget.formlayout.addWidget(add_widget)
# create submission button
submit_btn = QPushButton("Submit")
@@ -302,7 +272,6 @@ class App(QMainWindow):
logger.debug(f"Looked up reagent: {wanted_reagent}")
# if reagent not found offer to add to database
if wanted_reagent == None:
# dlg = AddReagentQuestion(reagent_type=reagent, reagent_lot=reagents[reagent])
r_lot = reagents[reagent]
dlg = QuestionAsker(title=f"Add {r_lot}?", message=f"Couldn't find reagent type {reagent.replace('_', ' ').title().strip('Lot')}: {r_lot} in the database.\n\nWould you like to add it?")
if dlg.exec():
@@ -310,10 +279,10 @@ class App(QMainWindow):
expiry_date = self.reagents[reagent]['exp']
wanted_reagent = self.add_reagent(reagent_lot=r_lot, reagent_type=reagent.replace("lot_", ""), expiry=expiry_date)
else:
# In this case we will have an empty reagent and the submission will fail kit integrity check
logger.debug("Will not add reagent.")
if wanted_reagent != None:
parsed_reagents.append(wanted_reagent)
# logger.debug(info)
# move samples into preliminary submission dict
info['samples'] = self.samples
info['uploaded_by'] = getuser()
@@ -322,14 +291,14 @@ class App(QMainWindow):
base_submission, result = construct_submission_info(ctx=self.ctx, info_dict=info)
# check output message for issues
match result['code']:
# code 1: ask for overwrite
case 1:
# if output['code'] > 0:
# dlg = OverwriteSubQuestion(output['message'], base_submission.rsl_plate_num)
dlg = QuestionAsker(title=f"Review {base_submission.rsl_plate_num}?", message=result['message'])
if dlg.exec():
base_submission.reagents = []
else:
return
# code 2: No RSL plate number given
case 2:
dlg = AlertPop(message=result['message'], status='critical')
dlg.exec()
@@ -386,12 +355,12 @@ class App(QMainWindow):
return reagent
def extract_form_info(self, object):
def extract_form_info(self, object) -> Tuple[list, list]:
"""
retrieves arbitrary number of labels, values from form
Args:
object (_type_): _description_
object (_type_): the form widget
Returns:
_type_: _description_
@@ -399,6 +368,7 @@ class App(QMainWindow):
labels = []
values = []
# grab all widgets in form
prev_item = None
for item in object.layout.parentWidget().findChildren(QWidget):
match item:
case QLabel():

View File

@@ -9,12 +9,15 @@ from PyQt6.QtWidgets import (
)
from PyQt6.QtCore import Qt, QDate, QSize
# from PyQt6.QtGui import QFontMetrics, QAction
from backend.db import get_all_reagenttype_names, lookup_all_sample_types, create_kit_from_yaml
from tools import check_not_nan
from backend.db import get_all_reagenttype_names, lookup_all_sample_types, create_kit_from_yaml, lookup_regent_by_type_name
from backend.excel.parser import SheetParser
from jinja2 import Environment, FileSystemLoader
import sys
from pathlib import Path
import logging
import numpy as np
from .pop_ups import AlertPop
logger = logging.getLogger(f"submissions.{__name__}")
@@ -45,15 +48,18 @@ class AddReagentForm(QDialog):
self.buttonBox.rejected.connect(self.reject)
# get lot info
lot_input = QLineEdit()
lot_input.setObjectName("lot")
lot_input.setText(reagent_lot)
# get expiry info
exp_input = QDateEdit(calendarPopup=True)
exp_input.setObjectName('expiry')
if expiry == None:
exp_input.setDate(QDate.currentDate())
else:
exp_input.setDate(expiry)
# get reagent type info
type_input = QComboBox()
type_input.setObjectName('type')
type_input.addItems([item.replace("_", " ").title() for item in get_all_reagenttype_names(ctx=ctx)])
logger.debug(f"Trying to find index of {reagent_type}")
# convert input to user friendly string?
@@ -175,17 +181,18 @@ class KitAdder(QWidget):
# send to kit constructor
result = create_kit_from_yaml(ctx=self.ctx, exp=yml_type)
# result = create_kit_from_yaml(ctx=self.ctx, exp=exp)
msg = QMessageBox()
# msg.setIcon(QMessageBox.critical)
match result['code']:
case 0:
msg.setText("Kit added")
msg.setInformativeText(result['message'])
msg.setWindowTitle("Kit added")
case 1:
msg.setText("Permission Error")
msg.setInformativeText(result['message'])
msg.setWindowTitle("Permission Error")
msg = AlertPop(message=result['message'], status=result['status'])
# match result['code']:
# case 0:
# msg = AlertPop(message=result['message'], status="information")
# # msg.setText()
# # msg.setInformativeText(result['message'])
# # msg.setWindowTitle("Kit added")
# case 1:
# msg = AlertPop(m)
# msg.setText("Permission Error")
# msg.setInformativeText(result['message'])
# msg.setWindowTitle("Permission Error")
msg.exec()
def extract_form_info(self, object):
@@ -276,3 +283,41 @@ class ControlsDatePicker(QWidget):
def sizeHint(self) -> QSize:
return QSize(80,20)
class ImportReagent(QComboBox):
def __init__(self, ctx:dict, item:str, prsr:SheetParser|None=None):
super().__init__()
self.setEditable(True)
# Ensure that all reagenttypes have a name that matches the items in the excel parser
query_var = item.replace("lot_", "")
logger.debug(f"Query for: {query_var}")
if prsr != None:
if isinstance(prsr.sub[item], np.float64):
logger.debug(f"{prsr.sub[item]['lot']} is a numpy float!")
try:
prsr.sub[item] = int(prsr.sub[item]['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.
relevant_reagents = [item.__str__() for item in lookup_regent_by_type_name(ctx=ctx, type_name=query_var)]#, kit_name=prsr.sub['extraction_kit'])]
output_reg = []
for reagent in relevant_reagents:
if isinstance(reagent, set):
for thing in reagent:
output_reg.append(thing)
elif isinstance(reagent, str):
output_reg.append(reagent)
relevant_reagents = output_reg
# if reagent in sheet is not found insert it into items
if prsr != None:
logger.debug(f"Relevant reagents for {prsr.sub[item]}: {relevant_reagents}")
if str(prsr.sub[item]['lot']) not in relevant_reagents and prsr.sub[item]['lot'] != 'nan':
if check_not_nan(prsr.sub[item]['lot']):
relevant_reagents.insert(0, str(prsr.sub[item]['lot']))
logger.debug(f"New relevant reagents: {relevant_reagents}")
self.addItems(relevant_reagents)

View File

@@ -164,7 +164,7 @@ class SubmissionDetails(QDialog):
# get submision from db
data = lookup_submission_by_id(ctx=ctx, id=id)
self.base_dict = data.to_dict()
logger.debug(f"Base dict: {self.base_dict}")
# logger.debug(f"Base dict: {self.base_dict}")
# don't want id
del self.base_dict['id']
# convert sub objects to dicts

View File

@@ -1,24 +1,23 @@
# from ..models import *
from backend.db.models import *
from backend.db import lookup_regent_by_type_name
from tools import check_not_nan
# from backend.db import lookup_kittype_by_name
import logging
import numpy as np
from backend.excel.parser import SheetParser
from PyQt6.QtWidgets import (
QMainWindow, QLabel, QToolBar,
QTabWidget, QWidget, QVBoxLayout,
QPushButton, QFileDialog,
QLineEdit, QMessageBox, QComboBox, QDateEdit, QHBoxLayout,
QSpinBox, QScrollArea
)
logger = logging.getLogger(f"submissions.{__name__}")
def check_kit_integrity(sub:BasicSubmission|KitType, reagenttypes:list|None=None) -> dict|None:
"""
Ensures all reagents expected in kit are listed in Submission
Args:
sub (BasicSubmission | KitType): Object containing complete list of reagent types.
reagenttypes (list | None, optional): List to check against complete list. Defaults to None.
Returns:
dict|None: Result object containing a message and any missing components.
"""
logger.debug(type(sub))
# What type is sub?
match sub:
case BasicSubmission():
ext_kit_rtypes = [reagenttype.name for reagenttype in sub.extraction_kit.reagent_types]
@@ -27,10 +26,13 @@ def check_kit_integrity(sub:BasicSubmission|KitType, reagenttypes:list|None=None
ext_kit_rtypes = [reagenttype.name for reagenttype in sub.reagent_types]
logger.debug(f"Kit reagents: {ext_kit_rtypes}")
logger.debug(f"Submission reagents: {reagenttypes}")
# check if lists are equal
check = set(ext_kit_rtypes) == set(reagenttypes)
logger.debug(f"Checking if reagents match kit contents: {check}")
# what reagent types are in both lists?
common = list(set(ext_kit_rtypes).intersection(reagenttypes))
logger.debug(f"common reagents types: {common}")
# if lists are equal return no problem
if check:
result = None
else:
@@ -39,38 +41,39 @@ def check_kit_integrity(sub:BasicSubmission|KitType, reagenttypes:list|None=None
return result
def insert_reagent_import(ctx:dict, item:str, prsr:SheetParser|None=None) -> QComboBox:
add_widget = QComboBox()
add_widget.setEditable(True)
# Ensure that all reagenttypes have a name that matches the items in the excel parser
query_var = item.replace("lot_", "")
logger.debug(f"Query for: {query_var}")
if prsr != None:
if isinstance(prsr.sub[item], np.float64):
logger.debug(f"{prsr.sub[item]['lot']} is a numpy float!")
try:
prsr.sub[item] = int(prsr.sub[item]['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.
relevant_reagents = [item.__str__() for item in lookup_regent_by_type_name(ctx=ctx, type_name=query_var)]#, kit_name=prsr.sub['extraction_kit'])]
output_reg = []
for reagent in relevant_reagents:
if isinstance(reagent, set):
for thing in reagent:
output_reg.append(thing)
elif isinstance(reagent, str):
output_reg.append(reagent)
relevant_reagents = output_reg
# if reagent in sheet is not found insert it into items
if prsr != None:
logger.debug(f"Relevant reagents for {prsr.sub[item]}: {relevant_reagents}")
if str(prsr.sub[item]['lot']) not in relevant_reagents and prsr.sub[item]['lot'] != 'nan':
if check_not_nan(prsr.sub[item]['lot']):
relevant_reagents.insert(0, str(prsr.sub[item]['lot']))
logger.debug(f"New relevant reagents: {relevant_reagents}")
add_widget.addItems(relevant_reagents)
return add_widget
# Below is depreciated:
# def insert_reagent_import(ctx:dict, item:str, prsr:SheetParser|None=None) -> QComboBox:
# add_widget = QComboBox()
# add_widget.setEditable(True)
# # Ensure that all reagenttypes have a name that matches the items in the excel parser
# query_var = item.replace("lot_", "")
# logger.debug(f"Query for: {query_var}")
# if prsr != None:
# if isinstance(prsr.sub[item], np.float64):
# logger.debug(f"{prsr.sub[item]['lot']} is a numpy float!")
# try:
# prsr.sub[item] = int(prsr.sub[item]['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.
# relevant_reagents = [item.__str__() for item in lookup_regent_by_type_name(ctx=ctx, type_name=query_var)]#, kit_name=prsr.sub['extraction_kit'])]
# output_reg = []
# for reagent in relevant_reagents:
# if isinstance(reagent, set):
# for thing in reagent:
# output_reg.append(thing)
# elif isinstance(reagent, str):
# output_reg.append(reagent)
# relevant_reagents = output_reg
# # if reagent in sheet is not found insert it into items
# if prsr != None:
# logger.debug(f"Relevant reagents for {prsr.sub[item]}: {relevant_reagents}")
# if str(prsr.sub[item]['lot']) not in relevant_reagents and prsr.sub[item]['lot'] != 'nan':
# if check_not_nan(prsr.sub[item]['lot']):
# relevant_reagents.insert(0, str(prsr.sub[item]['lot']))
# logger.debug(f"New relevant reagents: {relevant_reagents}")
# add_widget.addItems(relevant_reagents)
# return add_widget