Pre- pydsubmission toForm method.

This commit is contained in:
Landon Wark
2023-10-25 14:58:41 -05:00
parent 4b1f88f1d0
commit f3a7d75c6a
11 changed files with 298 additions and 115 deletions

View File

@@ -1,3 +1,5 @@
- [ ] Make the kit verifier make more sense.
- [ ] Slim down the Import and Submit functions in main_window_functions.
- [x] Create custom store methods for submission, reagent and sample.
- [x] Make pydantic models for other things that use constructors.
- [x] Move backend.db.functions.constructor functions into Pydantic models.

View File

@@ -138,7 +138,7 @@ def lookup_reagent_types(ctx:Settings,
assert reagent.type != []
logger.debug(f"Looking up reagent type for {type(kit_type)} {kit_type} and {type(reagent)} {reagent}")
logger.debug(f"Kit reagent types: {kit_type.reagent_types}")
logger.debug(f"Reagent reagent types: {reagent._sa_instance_state}")
# logger.debug(f"Reagent reagent types: {reagent._sa_instance_state}")
result = list(set(kit_type.reagent_types).intersection(reagent.type))
logger.debug(f"Result: {result}")
return result[0]

View File

@@ -7,6 +7,7 @@ from sqlalchemy.orm import relationship, validates
from sqlalchemy.ext.associationproxy import association_proxy
from datetime import date
import logging
from tools import Settings, check_authorization
logger = logging.getLogger(f'submissions.{__name__}')
@@ -100,6 +101,11 @@ class KitType(Base):
map['info'] = {}
return map
@check_authorization
def save(self, ctx:Settings):
ctx.database_session.add(self)
ctx.database_session.commit()
class ReagentType(Base):
"""
Base of reagent type abstract
@@ -265,12 +271,21 @@ class Reagent(Base):
rtype = reagent_role.name
except AttributeError:
rtype = "Unknown"
try:
expiry = self.expiry.strftime("%Y-%m-%d")
except:
expiry = date.today()
return {
"name":self.name,
"type": rtype,
"lot": self.lot,
"expiry": self.expiry.strftime("%Y-%m-%d")
}
def save(self, ctx:Settings):
ctx.database_session.add(self)
ctx.database_session.commit()
class Discount(Base):
"""

View File

@@ -853,7 +853,6 @@ class WastewaterSample(BasicSample):
output_dict["submitter_id"] = output_dict['ww_full_sample_id']
return output_dict
class BacterialCultureSample(BasicSample):
"""
base of bacterial culture sample

View File

@@ -112,7 +112,8 @@ class SheetParser(object):
kit = lookup_kit_types(ctx=self.ctx, name=self.sub['extraction_kit']['value'])
allowed_reagents = [item.name for item in kit.get_reagents()]
logger.debug(f"List of reagents for comparison with allowed_reagents: {pprint.pformat(self.sub['reagents'])}")
self.sub['reagents'] = [reagent for reagent in self.sub['reagents'] if reagent['value'].type in allowed_reagents]
# self.sub['reagents'] = [reagent for reagent in self.sub['reagents'] if reagent['value'].type in allowed_reagents]
self.sub['reagents'] = [reagent for reagent in self.sub['reagents'] if reagent.type in allowed_reagents]
def to_pydantic(self) -> PydSubmission:
"""
@@ -231,8 +232,9 @@ class ReagentParser(object):
lot = df.iat[relevant[item]['lot']['row']-1, relevant[item]['lot']['column']-1]
expiry = df.iat[relevant[item]['expiry']['row']-1, relevant[item]['expiry']['column']-1]
except (KeyError, IndexError):
listo.append(dict(value=PydReagent(ctx=self.ctx, type=item.strip(), lot=None, exp=None, name=None), parsed=False))
listo.append(PydReagent(ctx=self.ctx, type=item.strip(), lot=None, exp=None, name=None, parsed=False))
continue
# If the cell is blank tell the PydReagent
if check_not_nan(lot):
parsed = True
else:
@@ -240,7 +242,7 @@ class ReagentParser(object):
# logger.debug(f"Got lot for {item}-{name}: {lot} as {type(lot)}")
lot = str(lot)
logger.debug(f"Going into pydantic: name: {name}, lot: {lot}, expiry: {expiry}, type: {item.strip()}")
listo.append(dict(value=PydReagent(ctx=self.ctx, type=item.strip(), lot=lot, exp=expiry, name=name), parsed=parsed))
listo.append(PydReagent(ctx=self.ctx, type=item.strip(), lot=lot, expiry=expiry, name=name, parsed=parsed))
logger.debug(f"Returning listo: {listo}")
return listo
@@ -387,7 +389,11 @@ class SampleParser(object):
# Set row in lookup table to blank values to prevent multipe lookups.
try:
self.lookup_table.loc[self.lookup_table['Sample #']==addition['Sample #']] = np.nan
except ValueError:
except (ValueError, KeyError):
pass
try:
self.lookup_table.loc[self.lookup_table['Well']==addition['Well']] = np.nan
except (ValueError, KeyError):
pass
logger.debug(f"Output sample dict: {sample}")
logger.debug(f"Final lookup_table: \n\n {self.lookup_table}")

View File

@@ -2,6 +2,7 @@
Contains pydantic models and accompanying validators
'''
import uuid
from PyQt6 import QtCore
from pydantic import BaseModel, field_validator, Field
from datetime import date, datetime, timedelta
from dateutil.parser import parse
@@ -18,6 +19,7 @@ from backend.db.functions import (lookup_submissions, lookup_reagent_types, look
)
from backend.db.models import *
from sqlalchemy.exc import InvalidRequestError, StatementError
from PyQt6.QtWidgets import QComboBox, QWidget, QLabel, QVBoxLayout
logger = logging.getLogger(f"submissions.{__name__}")
@@ -25,8 +27,9 @@ class PydReagent(BaseModel):
ctx: Settings
lot: str|None
type: str|None
exp: date|None
expiry: date|None
name: str|None
parsed: bool = Field(default=False)
@field_validator("type", mode='before')
@classmethod
@@ -61,7 +64,7 @@ class PydReagent(BaseModel):
return value.upper()
return value
@field_validator("exp", mode="before")
@field_validator("expiry", mode="before")
@classmethod
def enforce_date(cls, value):
if value != None:
@@ -86,7 +89,7 @@ class PydReagent(BaseModel):
else:
return values.data['type']
def toSQL(self):# -> Tuple[Reagent, dict]:
def toSQL(self) -> Tuple[Reagent, dict]:
result = None
logger.debug(f"Reagent SQL constructor is looking up type: {self.type}, lot: {self.lot}")
reagent = lookup_reagents(ctx=self.ctx, lot_number=self.lot)
@@ -113,6 +116,10 @@ class PydReagent(BaseModel):
# NOTE: this will now be done only in the reporting phase to account for potential changes in end-of-life extensions
return reagent, result
def toForm(self, parent:QWidget, extraction_kit:str) -> QComboBox:
from frontend.custom_widgets.misc import ReagentFormWidget
return ReagentFormWidget(parent=parent, reagent=self, extraction_kit=extraction_kit)
class PydSample(BaseModel, extra='allow'):
submitter_id: str
@@ -127,13 +134,6 @@ class PydSample(BaseModel, extra='allow'):
return [value]
return value
# @field_validator(column)
# @classmethod
# def column_int_to_list(cls, value):
# if isinstance(value, int):
# return [value]
# return value
def toSQL(self, ctx:Settings, submission):
result = None
self.__dict__.update(self.model_extra)
@@ -302,6 +302,22 @@ class PydSubmission(BaseModel, extra='allow'):
value['value'] = values.data['submission_type']['value']
return value
def handle_duplicate_samples(self):
submitter_ids = list(set([sample.submitter_id for sample in self.samples]))
output = []
for id in submitter_ids:
relevants = [item for item in self.samples if item.submitter_id==id]
if len(relevants) <= 1:
output += relevants
else:
rows = [item.row[0] for item in relevants]
columns = [item.column[0] for item in relevants]
dummy = relevants[0]
dummy.row = rows
dummy.column = columns
output.append(dummy)
self.samples = output
def toSQL(self):
code = 0
msg = None
@@ -380,21 +396,7 @@ class PydSubmission(BaseModel, extra='allow'):
logger.debug(f"Constructed submissions message: {msg}")
return instance, {'code':code, 'message':msg}
def handle_duplicate_samples(self):
submitter_ids = list(set([sample.submitter_id for sample in self.samples]))
output = []
for id in submitter_ids:
relevants = [item for item in self.samples if item.submitter_id==id]
if len(relevants) <= 1:
output += relevants
else:
rows = [item.row[0] for item in relevants]
columns = [item.column[0] for item in relevants]
dummy = relevants[0]
dummy.row = rows
dummy.column = columns
output.append(dummy)
self.samples = output
def toForm(self):
class PydContact(BaseModel):
@@ -447,19 +449,21 @@ class PydReagentType(BaseModel):
assoc = None
if assoc == None:
assoc = KitTypeReagentTypeAssociation(kit_type=kit, reagent_type=instance, uses=self.uses, required=self.required)
kit.kit_reagenttype_associations.append(assoc)
# kit.kit_reagenttype_associations.append(assoc)
return instance
class PydKit(BaseModel):
name: str
reagent_types: List[PydReagentType]|None
reagent_types: List[PydReagentType] = []
def toSQL(self, ctx):
result = dict(message=None, status='Information')
instance = lookup_kit_types(ctx=ctx, name=self.name)
if instance == None:
instance = KitType(name=self.name)
instance.reagent_types = [item.toSQL(ctx, instance) for item in self.reagent_types]
return instance
# instance.reagent_types = [item.toSQL(ctx, instance) for item in self.reagent_types]
[item.toSQL(ctx, instance) for item in self.reagent_types]
return instance, result

View File

@@ -3,23 +3,22 @@ Constructs main application.
'''
from pprint import pformat
import sys
from typing import Tuple
from PyQt6.QtWidgets import (
QMainWindow, QToolBar,
QTabWidget, QWidget, QVBoxLayout,
QComboBox, QHBoxLayout,
QScrollArea, QLineEdit, QDateEdit
)
from PyQt6.QtCore import Qt, pyqtSignal
from PyQt6.QtCore import pyqtSignal
from PyQt6.QtGui import QAction
from PyQt6.QtWebEngineWidgets import QWebEngineView
from pathlib import Path
from backend.db import (
store_object, lookup_control_types, lookup_modes, #construct_reagent
lookup_control_types, lookup_modes
)
from backend.validators import PydSubmission, PydReagent
from tools import check_if_app, Settings
from frontend.custom_widgets import SubmissionsSheet, AlertPop, AddReagentForm, KitAdder, ControlsDatePicker, ImportReagent
from frontend.custom_widgets import SubmissionsSheet, AlertPop, AddReagentForm, KitAdder, ControlsDatePicker, ImportReagent, ReagentFormWidget
import logging
from datetime import date
import webbrowser
@@ -225,7 +224,9 @@ class App(QMainWindow):
reagent = PydReagent(ctx=self.ctx, **info)
# send reagent to db
# store_reagent(ctx=self.ctx, reagent=reagent)
result = store_object(ctx=self.ctx, object=reagent.toSQL()[0])
sqlobj, result = reagent.toSQL()
sqlobj.save(ctx=self.ctx)
# result = store_object(ctx=self.ctx, object=reagent.toSQL()[0])
self.result_reporter(result=result)
return reagent
@@ -320,7 +321,7 @@ class App(QMainWindow):
class AddSubForm(QWidget):
def __init__(self, parent):
def __init__(self, parent:QWidget):
logger.debug(f"Initializating subform...")
super(QWidget, self).__init__(parent)
self.layout = QVBoxLayout(self)
@@ -337,7 +338,7 @@ class AddSubForm(QWidget):
self.tabs.addTab(self.tab2,"Controls")
self.tabs.addTab(self.tab3, "Add Kit")
# Create submission adder form
self.formwidget = SubmissionFormWidget(self)
self.formwidget = SubmissionFormContainer(self)
self.formlayout = QVBoxLayout(self)
self.formwidget.setLayout(self.formlayout)
self.formwidget.setFixedWidth(300)
@@ -391,7 +392,7 @@ class AddSubForm(QWidget):
self.layout.addWidget(self.tabs)
self.setLayout(self.layout)
class SubmissionFormWidget(QWidget):
class SubmissionFormContainer(QWidget):
import_drag = pyqtSignal(Path)
@@ -399,9 +400,7 @@ class SubmissionFormWidget(QWidget):
logger.debug(f"Setting form widget...")
super().__init__(parent)
self.parent = parent
self.ignore = [None, "", "qt_spinbox_lineedit", "qt_scrollarea_viewport", "qt_scrollarea_hcontainer",
"qt_scrollarea_vcontainer", "submit_btn"
]
self.setAcceptDrops(True)
def dragEnterEvent(self, event):
@@ -414,6 +413,10 @@ class SubmissionFormWidget(QWidget):
fname = Path([u.toLocalFile() for u in event.mimeData().urls()][0])
self.import_drag.emit(fname)
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 = {}
@@ -421,11 +424,15 @@ class SubmissionFormWidget(QWidget):
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, exp=None)
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())
@@ -435,5 +442,6 @@ class SubmissionFormWidget(QWidget):
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

View File

@@ -20,9 +20,10 @@ from backend.db.models import SubmissionTypeKitTypeAssociation
from sqlalchemy import FLOAT, INTEGER
import logging
import numpy as np
from .pop_ups import AlertPop
from backend.validators import PydReagent
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__}")
@@ -92,7 +93,7 @@ class AddReagentForm(QDialog):
def parse_form(self):
return dict(name=self.name_input.currentText(),
lot=self.lot_input.text(),
exp=self.exp_input.date().toPyDate(),
expiry=self.exp_input.date().toPyDate(),
type=self.type_input.currentText())
def update_names(self):
@@ -242,21 +243,22 @@ class KitAdder(QWidget):
logger.debug(f"kit info: {pformat(info)}")
logger.debug(f"kit reagents: {pformat(reagents)}")
info['reagent_types'] = reagents
# for reagent in reagents:
# new_dict = {}
# for k,v in reagent.items():
# if "_" in k:
# key, sub_key = k.split("_")
# if key not in new_dict.keys():
# new_dict[key] = {}
# logger.debug(f"Adding key {key}, {sub_key} and value {v} to {new_dict}")
# new_dict[key][sub_key] = v
# else:
# new_dict[k] = v
# info['reagent_types'].append(new_dict)
logger.debug(pformat(info))
# send to kit constructor
result = construct_kit_from_yaml(ctx=self.ctx, kit_dict=info)
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)
@@ -521,5 +523,149 @@ class FirstStrandPlateList(QDialog):
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

View File

@@ -393,7 +393,6 @@ class BarcodeWindow(QDialog):
painter.drawPixmap(120, -20, image)
painter.end()
class SubmissionComment(QDialog):
"""
a window for adding comment text to a submission

View File

@@ -38,7 +38,7 @@ from .custom_widgets import ReportDatePicker
from .custom_widgets.misc import ImportReagent, ParsedQLabel
from .visualizations.control_charts import create_charts, construct_html
from pathlib import Path
from frontend.custom_widgets.misc import FirstStrandSalvage, FirstStrandPlateList
from frontend.custom_widgets.misc import FirstStrandSalvage, FirstStrandPlateList, ReagentFormWidget
logger = logging.getLogger(f"submissions.{__name__}")
@@ -78,8 +78,7 @@ def import_submission_function(obj:QMainWindow, fname:Path|None=None) -> Tuple[Q
except Exception as e:
return obj, dict(message= f"Problem creating pydantic model:\n\n{e}", status="critical")
# destroy any widgets from previous imports
for item in obj.table_widget.formlayout.parentWidget().findChildren(QWidget):
item.setParent(None)
obj.table_widget.formwidget.clear_form()
obj.current_submission_type = pyd.submission_type['value']
obj.current_file = pyd.filepath
# Get list of fields from pydantic model.
@@ -216,24 +215,26 @@ def kit_integrity_completion_function(obj:QMainWindow) -> Tuple[QMainWindow, dic
logger.debug(f"Kit selector: {kit_widget}")
# get current kit being used
obj.ext_kit = kit_widget.currentText()
for item in obj.reagents:
obj.table_widget.formlayout.addWidget(ParsedQLabel({'parsed':True}, item.type, title=False, label_name=f"lot_{item.type}"))
reagent = dict(type=item.type, lot=item.lot, exp=item.exp, name=item.name)
add_widget = ImportReagent(ctx=obj.ctx, reagent=reagent, extraction_kit=obj.ext_kit)
for reagent in obj.reagents:
# obj.table_widget.formlayout.addWidget(ParsedQLabel({'parsed':True}, item.type, title=False, label_name=f"lot_{item.type}"))
# reagent = dict(type=item.type, lot=item.lot, expiry=item.expiry, name=item.name)
# add_widget = ImportReagent(ctx=obj.ctx, reagent=reagent, extraction_kit=obj.ext_kit)
# obj.table_widget.formlayout.addWidget(add_widget)
add_widget = ReagentFormWidget(parent=obj.table_widget.formwidget, reagent=reagent, extraction_kit=obj.ext_kit)
obj.table_widget.formlayout.addWidget(add_widget)
logger.debug(f"Checking integrity of {obj.ext_kit}")
# see if there are any missing reagents
if len(obj.missing_reagents) > 0:
result = dict(message=f"The submission you are importing is missing some reagents expected by the kit.\n\nIt looks like you are missing: {[item.type.upper() for item in obj.missing_reagents]}\n\nAlternatively, you may have set the wrong extraction kit.\n\nThe program will populate lists using existing reagents.\n\nPlease make sure you check the lots carefully!", status="Warning")
for item in obj.missing_reagents:
# Add label that has parsed as False to show "MISSING" label.
obj.table_widget.formlayout.addWidget(ParsedQLabel({'parsed':False}, item.type, title=False, label_name=f"missing_{item.type}"))
# Set default parameters for the empty reagent.
reagent = dict(type=item.type, lot=None, exp=date.today(), name=None)
# create and add widget
# add_widget = ImportReagent(ctx=obj.ctx, reagent=PydReagent(**reagent), extraction_kit=obj.ext_kit)
add_widget = ImportReagent(ctx=obj.ctx, reagent=reagent, extraction_kit=obj.ext_kit)
obj.table_widget.formlayout.addWidget(add_widget)
# for item in obj.missing_reagents:
# # Add label that has parsed as False to show "MISSING" label.
# obj.table_widget.formlayout.addWidget(ParsedQLabel({'parsed':False}, item.type, title=False, label_name=f"missing_{item.type}"))
# # Set default parameters for the empty reagent.
# reagent = dict(type=item.type, lot=None, expiry=date.today(), name=None)
# # create and add widget
# # add_widget = ImportReagent(ctx=obj.ctx, reagent=PydReagent(**reagent), extraction_kit=obj.ext_kit)
# add_widget = ImportReagent(ctx=obj.ctx, reagent=reagent, extraction_kit=obj.ext_kit)
# obj.table_widget.formlayout.addWidget(add_widget)
# Add submit button to the form.
submit_btn = QPushButton("Submit")
submit_btn.setObjectName("submit_btn")
@@ -264,34 +265,34 @@ def submit_new_sample_function(obj:QMainWindow) -> Tuple[QMainWindow, dict]:
# info, reagents = obj.table_widget.formwidget.parse_form()
submission: PydSubmission = obj.table_widget.formwidget.parse_form()
logger.debug(f"Submission: {pprint.pformat(submission)}")
parsed_reagents = []
# parsed_reagents = []
# compare reagents in form to reagent database
for reagent in submission.reagents:
# Lookup any existing reagent of this type with this lot number
wanted_reagent = lookup_reagents(ctx=obj.ctx, lot_number=reagent.lot, reagent_type=reagent.name)
logger.debug(f"Looked up reagent: {wanted_reagent}")
# if reagent not found offer to add to database
if wanted_reagent == None:
# r_lot = reagent[reagent]
dlg = QuestionAsker(title=f"Add {reagent.lot}?", message=f"Couldn't find reagent type {reagent.name.strip('Lot')}: {reagent.lot} in the database.\n\nWould you like to add it?")
if dlg.exec():
logger.debug(f"Looking through {pprint.pformat(obj.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.exp
wanted_reagent = obj.add_reagent(reagent_lot=reagent.lot, reagent_type=reagent.name.replace("lot_", ""), expiry=expiry_date, name=picked_reagent.name)
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 obj, dict(message="Failed integrity check", status="critical")
# Append the PydReagent object o be added to the submission
parsed_reagents.append(reagent)
# move samples into preliminary submission dict
submission.reagents = parsed_reagents
# for reagent in submission.reagents:
# # Lookup any existing reagent of this type with this lot number
# wanted_reagent = lookup_reagents(ctx=obj.ctx, lot_number=reagent.lot, reagent_type=reagent.name)
# logger.debug(f"Looked up reagent: {wanted_reagent}")
# # if reagent not found offer to add to database
# if wanted_reagent == None:
# # r_lot = reagent[reagent]
# dlg = QuestionAsker(title=f"Add {reagent.lot}?", message=f"Couldn't find reagent type {reagent.name.strip('Lot')}: {reagent.lot} in the database.\n\nWould you like to add it?")
# if dlg.exec():
# logger.debug(f"Looking through {pprint.pformat(obj.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 = obj.add_reagent(reagent_lot=reagent.lot, reagent_type=reagent.name.replace("lot_", ""), expiry=expiry_date, name=picked_reagent.name)
# 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 obj, dict(message="Failed integrity check", status="critical")
# # Append the PydReagent object o be added to the submission
# parsed_reagents.append(reagent)
# # move samples into preliminary submission dict
# submission.reagents = parsed_reagents
# submission.uploaded_by = getuser()
# construct submission object
# logger.debug(f"Here is the info_dict: {pprint.pformat(info)}")
@@ -1020,16 +1021,17 @@ def scrape_reagents(obj:QMainWindow, extraction_kit:str) -> Tuple[QMainWindow, d
obj.reagents = []
obj.missing_reagents = []
# Remove previous reagent widgets
[item.setParent(None) for item in obj.table_widget.formlayout.parentWidget().findChildren(QWidget) if item.objectName().startswith("lot_") or item.objectName().startswith("missing_")]
[item.setParent(None) for item in obj.table_widget.formlayout.parentWidget().findChildren(QPushButton)]
# [item.setParent(None) for item in obj.table_widget.formlayout.parentWidget().findChildren(QWidget) if item.objectName().startswith("lot_") or item.objectName().startswith("missing_")]
# [item.setParent(None) for item in obj.table_widget.formlayout.parentWidget().findChildren(QPushButton)]
reagents = obj.prsr.parse_reagents(extraction_kit=extraction_kit)
logger.debug(f"Got reagents: {reagents}")
for reagent in obj.prsr.sub['reagents']:
# create label
if reagent['parsed']:
obj.reagents.append(reagent['value'])
else:
obj.missing_reagents.append(reagent['value'])
# for reagent in obj.prsr.sub['reagents']:
# # create label
# if reagent.parsed:
# obj.reagents.append(reagent)
# else:
# obj.missing_reagents.append(reagent)
obj.reagents = obj.prsr.sub['reagents']
logger.debug(f"Imported reagents: {obj.reagents}")
logger.debug(f"Missing reagents: {obj.missing_reagents}")
return obj, None

View File

@@ -65,6 +65,8 @@ def check_not_nan(cell_contents) -> bool:
cell_contents = np.nan
if cell_contents == None:
cell_contents = np.nan
if str(cell_contents).lower() == "none":
cell_contents = np.nan
try:
if pd.isnull(cell_contents):
cell_contents = np.nan