Moved import PCR results to context menu.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
## 202404.02
|
## 202404.02
|
||||||
|
|
||||||
- Various bug fixes.
|
- Various bug fixes.
|
||||||
|
- Move import PCR results to context menu.
|
||||||
- Added ability to sign off on submission in submission details.
|
- Added ability to sign off on submission in submission details.
|
||||||
|
|
||||||
## 202403.03
|
## 202403.03
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Models for the main submission types.
|
|||||||
'''
|
'''
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from getpass import getuser
|
from getpass import getuser
|
||||||
import json, logging, uuid, tempfile, re, yaml, base64
|
import logging, uuid, tempfile, re, yaml, base64
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from reportlab.graphics.barcode import createBarcodeImageInMemory
|
from reportlab.graphics.barcode import createBarcodeImageInMemory
|
||||||
@@ -14,7 +14,6 @@ from pprint import pformat
|
|||||||
from . import Reagent, SubmissionType, KitType, Organization
|
from . import Reagent, SubmissionType, KitType, Organization
|
||||||
from sqlalchemy import Column, String, TIMESTAMP, INTEGER, ForeignKey, JSON, FLOAT, case
|
from sqlalchemy import Column, String, TIMESTAMP, INTEGER, ForeignKey, JSON, FLOAT, case
|
||||||
from sqlalchemy.orm import relationship, validates, Query
|
from sqlalchemy.orm import relationship, validates, Query
|
||||||
from json.decoder import JSONDecodeError
|
|
||||||
from sqlalchemy.ext.associationproxy import association_proxy
|
from sqlalchemy.ext.associationproxy import association_proxy
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from openpyxl import Workbook
|
from openpyxl import Workbook
|
||||||
@@ -252,7 +251,7 @@ class BasicSubmission(BaseClass):
|
|||||||
sample_list = self.hitpick_plate()
|
sample_list = self.hitpick_plate()
|
||||||
# logger.debug("Setting background colours")
|
# logger.debug("Setting background colours")
|
||||||
for sample in sample_list:
|
for sample in sample_list:
|
||||||
if sample['Positive']:
|
if sample['positive']:
|
||||||
sample['background_color'] = "#f10f07"
|
sample['background_color'] = "#f10f07"
|
||||||
else:
|
else:
|
||||||
if "colour" in sample.keys():
|
if "colour" in sample.keys():
|
||||||
@@ -1268,6 +1267,54 @@ class Wastewater(BasicSubmission):
|
|||||||
row = idx.index.to_list()[0]
|
row = idx.index.to_list()[0]
|
||||||
return row + 1
|
return row + 1
|
||||||
|
|
||||||
|
def custom_context_events(self) -> dict:
|
||||||
|
events = super().custom_context_events()
|
||||||
|
events['Link PCR'] = self.link_pcr
|
||||||
|
return events
|
||||||
|
|
||||||
|
def link_pcr(self, obj):
|
||||||
|
from backend.excel import PCRParser
|
||||||
|
from frontend.widgets import select_open_file
|
||||||
|
fname = select_open_file(obj=obj, file_extension="xlsx")
|
||||||
|
parser = PCRParser(filepath=fname)
|
||||||
|
# Check if PCR info already exists
|
||||||
|
if hasattr(self, 'pcr_info') and self.pcr_info != None:
|
||||||
|
# existing = json.loads(sub.pcr_info)
|
||||||
|
existing = self.pcr_info
|
||||||
|
else:
|
||||||
|
existing = None
|
||||||
|
if existing != None:
|
||||||
|
# update pcr_info
|
||||||
|
try:
|
||||||
|
logger.debug(f"Updating {type(existing)}: {existing} with {type(parser.pcr)}: {parser.pcr}")
|
||||||
|
# if json.dumps(parser.pcr) not in sub.pcr_info:
|
||||||
|
if parser.pcr not in self.pcr_info:
|
||||||
|
existing.append(parser.pcr)
|
||||||
|
logger.debug(f"Setting: {existing}")
|
||||||
|
# sub.pcr_info = json.dumps(existing)
|
||||||
|
self.pcr_info = existing
|
||||||
|
except TypeError:
|
||||||
|
logger.error(f"Error updating!")
|
||||||
|
# sub.pcr_info = json.dumps([parser.pcr])
|
||||||
|
self.pcr_info = [parser.pcr]
|
||||||
|
logger.debug(f"Final pcr info for {self.rsl_plate_num}: {self.pcr_info}")
|
||||||
|
else:
|
||||||
|
# sub.pcr_info = json.dumps([parser.pcr])
|
||||||
|
self.pcr_info = [parser.pcr]
|
||||||
|
logger.debug(f"Existing {type(self.pcr_info)}: {self.pcr_info}")
|
||||||
|
logger.debug(f"Inserting {type(parser.pcr)}: {parser.pcr}")
|
||||||
|
self.save(original=False)
|
||||||
|
logger.debug(f"Got {len(parser.samples)} samples to update!")
|
||||||
|
logger.debug(f"Parser samples: {parser.samples}")
|
||||||
|
for sample in self.samples:
|
||||||
|
logger.debug(f"Running update on: {sample}")
|
||||||
|
try:
|
||||||
|
sample_dict = [item for item in parser.samples if item['sample']==sample.rsl_number][0]
|
||||||
|
except IndexError:
|
||||||
|
continue
|
||||||
|
self.update_subsampassoc(sample=sample, input_dict=sample_dict)
|
||||||
|
# self.report.add_result(Result(msg=f"We added PCR info to {sub.rsl_plate_num}.", status='Information'))
|
||||||
|
|
||||||
class WastewaterArtic(BasicSubmission):
|
class WastewaterArtic(BasicSubmission):
|
||||||
"""
|
"""
|
||||||
derivative submission type for artic wastewater
|
derivative submission type for artic wastewater
|
||||||
@@ -2094,7 +2141,7 @@ class SubmissionSampleAssociation(BaseClass):
|
|||||||
logger.error(f"Unable to find row {self.row} in row_map.")
|
logger.error(f"Unable to find row {self.row} in row_map.")
|
||||||
sample['Well'] = None
|
sample['Well'] = None
|
||||||
sample['Plate Name'] = self.submission.rsl_plate_num
|
sample['Plate Name'] = self.submission.rsl_plate_num
|
||||||
sample['Positive'] = False
|
sample['positive'] = False
|
||||||
sample['submitted_date'] = self.submission.submitted_date
|
sample['submitted_date'] = self.submission.submitted_date
|
||||||
return sample
|
return sample
|
||||||
|
|
||||||
|
|||||||
@@ -8,19 +8,16 @@ from PyQt6.QtWidgets import (
|
|||||||
)
|
)
|
||||||
from PyQt6.QtGui import QAction
|
from PyQt6.QtGui import QAction
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from backend.validators import PydReagent
|
|
||||||
from tools import check_if_app, Settings, Report
|
from tools import check_if_app, Settings, Report
|
||||||
from .pop_ups import AlertPop
|
from .pop_ups import AlertPop
|
||||||
from .misc import AddReagentForm, LogParser
|
from .misc import LogParser
|
||||||
import logging, webbrowser, sys
|
import logging, webbrowser, sys
|
||||||
from datetime import date
|
|
||||||
from .submission_table import SubmissionsSheet
|
from .submission_table import SubmissionsSheet
|
||||||
from .submission_widget import SubmissionFormContainer
|
from .submission_widget import SubmissionFormContainer
|
||||||
from .controls_chart import ControlsViewer
|
from .controls_chart import ControlsViewer
|
||||||
from .kit_creator import KitAdder
|
from .kit_creator import KitAdder
|
||||||
from .submission_type_creator import SubmissionTypeAdder
|
from .submission_type_creator import SubmissionTypeAdder
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(f'submissions.{__name__}')
|
logger = logging.getLogger(f'submissions.{__name__}')
|
||||||
logger.info("Hello, I am a logger")
|
logger.info("Hello, I am a logger")
|
||||||
|
|
||||||
@@ -70,7 +67,7 @@ class App(QMainWindow):
|
|||||||
helpMenu.addAction(self.helpAction)
|
helpMenu.addAction(self.helpAction)
|
||||||
helpMenu.addAction(self.docsAction)
|
helpMenu.addAction(self.docsAction)
|
||||||
fileMenu.addAction(self.importAction)
|
fileMenu.addAction(self.importAction)
|
||||||
fileMenu.addAction(self.importPCRAction)
|
# fileMenu.addAction(self.importPCRAction)
|
||||||
methodsMenu.addAction(self.searchLog)
|
methodsMenu.addAction(self.searchLog)
|
||||||
reportMenu.addAction(self.generateReportAction)
|
reportMenu.addAction(self.generateReportAction)
|
||||||
maintenanceMenu.addAction(self.joinExtractionAction)
|
maintenanceMenu.addAction(self.joinExtractionAction)
|
||||||
@@ -93,7 +90,7 @@ class App(QMainWindow):
|
|||||||
"""
|
"""
|
||||||
logger.debug(f"Creating actions...")
|
logger.debug(f"Creating actions...")
|
||||||
self.importAction = QAction("&Import Submission", self)
|
self.importAction = QAction("&Import Submission", self)
|
||||||
self.importPCRAction = QAction("&Import PCR Results", self)
|
# self.importPCRAction = QAction("&Import PCR Results", self)
|
||||||
self.addReagentAction = QAction("Add Reagent", self)
|
self.addReagentAction = QAction("Add Reagent", self)
|
||||||
self.generateReportAction = QAction("Make Report", self)
|
self.generateReportAction = QAction("Make Report", self)
|
||||||
self.addKitAction = QAction("Import Kit", self)
|
self.addKitAction = QAction("Import Kit", self)
|
||||||
@@ -110,7 +107,7 @@ class App(QMainWindow):
|
|||||||
"""
|
"""
|
||||||
logger.debug(f"Connecting actions...")
|
logger.debug(f"Connecting actions...")
|
||||||
self.importAction.triggered.connect(self.table_widget.formwidget.importSubmission)
|
self.importAction.triggered.connect(self.table_widget.formwidget.importSubmission)
|
||||||
self.importPCRAction.triggered.connect(self.table_widget.formwidget.import_pcr_results)
|
# self.importPCRAction.triggered.connect(self.table_widget.formwidget.import_pcr_results)
|
||||||
self.addReagentAction.triggered.connect(self.table_widget.formwidget.add_reagent)
|
self.addReagentAction.triggered.connect(self.table_widget.formwidget.add_reagent)
|
||||||
self.generateReportAction.triggered.connect(self.table_widget.sub_wid.generate_report)
|
self.generateReportAction.triggered.connect(self.table_widget.sub_wid.generate_report)
|
||||||
self.joinExtractionAction.triggered.connect(self.table_widget.sub_wid.link_extractions)
|
self.joinExtractionAction.triggered.connect(self.table_widget.sub_wid.link_extractions)
|
||||||
@@ -158,8 +155,6 @@ class App(QMainWindow):
|
|||||||
else:
|
else:
|
||||||
self.statusBar().showMessage("Action completed sucessfully.", 5000)
|
self.statusBar().showMessage("Action completed sucessfully.", 5000)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def runSearch(self):
|
def runSearch(self):
|
||||||
dlg = LogParser(self)
|
dlg = LogParser(self)
|
||||||
dlg.exec()
|
dlg.exec()
|
||||||
|
|||||||
@@ -95,10 +95,12 @@ class SubmissionDetails(QDialog):
|
|||||||
self.html = self.template.render(sub=self.base_dict, signing_permission=is_power_user())
|
self.html = self.template.render(sub=self.base_dict, signing_permission=is_power_user())
|
||||||
self.webview.setHtml(self.html)
|
self.webview.setHtml(self.html)
|
||||||
self.setWindowTitle(f"Submission Details - {submission.rsl_plate_num}")
|
self.setWindowTitle(f"Submission Details - {submission.rsl_plate_num}")
|
||||||
|
with open("details.html", "w") as f:
|
||||||
|
f.write(self.html)
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def sign_off(self, submission:str|BasicSubmission):
|
def sign_off(self, submission:str|BasicSubmission):
|
||||||
logger.debug(f"Signing off on {submission}")
|
logger.debug(f"Signing off on {submission} - ({getuser()})")
|
||||||
if isinstance(submission, str):
|
if isinstance(submission, str):
|
||||||
submission = BasicSubmission.query(rsl_number=submission)
|
submission = BasicSubmission.query(rsl_number=submission)
|
||||||
submission.uploaded_by = getuser()
|
submission.uploaded_by = getuser()
|
||||||
|
|||||||
@@ -112,81 +112,6 @@ class SubmissionFormContainer(QWidget):
|
|||||||
logger.debug(f"Outgoing report: {self.report.results}")
|
logger.debug(f"Outgoing report: {self.report.results}")
|
||||||
logger.debug(f"All attributes of submission container:\n{pformat(self.__dict__)}")
|
logger.debug(f"All attributes of submission container:\n{pformat(self.__dict__)}")
|
||||||
|
|
||||||
def import_pcr_results(self):
|
|
||||||
"""
|
|
||||||
Pull QuantStudio results into db
|
|
||||||
"""
|
|
||||||
self.import_pcr_results_function()
|
|
||||||
self.app.report.add_result(self.report)
|
|
||||||
self.report = Report()
|
|
||||||
self.app.result_reporter()
|
|
||||||
|
|
||||||
def import_pcr_results_function(self):
|
|
||||||
"""
|
|
||||||
Import Quant-studio PCR data to an imported submission
|
|
||||||
|
|
||||||
Args:
|
|
||||||
obj (QMainWindow): original app window
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Tuple[QMainWindow, dict]: Collection of new main app window and result dict
|
|
||||||
"""
|
|
||||||
report = Report()
|
|
||||||
fname = select_open_file(self, file_extension="xlsx")
|
|
||||||
parser = PCRParser(filepath=fname)
|
|
||||||
logger.debug(f"Attempting lookup for {parser.plate_num}")
|
|
||||||
sub = BasicSubmission.query(rsl_number=parser.plate_num)
|
|
||||||
try:
|
|
||||||
logger.debug(f"Found submission: {sub.rsl_plate_num}")
|
|
||||||
except AttributeError:
|
|
||||||
# If no plate is found, may be because this is a repeat. Lop off the '-1' or '-2' and repeat
|
|
||||||
logger.error(f"Submission of number {parser.plate_num} not found. Attempting rescue of plate repeat.")
|
|
||||||
parser.plate_num = "-".join(parser.plate_num.split("-")[:-1])
|
|
||||||
sub = BasicSubmission.query(rsl_number=parser.plate_num)
|
|
||||||
try:
|
|
||||||
logger.debug(f"Found submission: {sub.rsl_plate_num}")
|
|
||||||
except AttributeError:
|
|
||||||
logger.error(f"Rescue of {parser.plate_num} failed.")
|
|
||||||
self.report.add_result(Result(msg="Couldn't find a submission with that RSL number.", status="Warning"))
|
|
||||||
return
|
|
||||||
# Check if PCR info already exists
|
|
||||||
if hasattr(sub, 'pcr_info') and sub.pcr_info != None:
|
|
||||||
# existing = json.loads(sub.pcr_info)
|
|
||||||
existing = sub.pcr_info
|
|
||||||
else:
|
|
||||||
existing = None
|
|
||||||
if existing != None:
|
|
||||||
# update pcr_info
|
|
||||||
try:
|
|
||||||
logger.debug(f"Updating {type(existing)}: {existing} with {type(parser.pcr)}: {parser.pcr}")
|
|
||||||
# if json.dumps(parser.pcr) not in sub.pcr_info:
|
|
||||||
if parser.pcr not in sub.pcr_info:
|
|
||||||
existing.append(parser.pcr)
|
|
||||||
logger.debug(f"Setting: {existing}")
|
|
||||||
# sub.pcr_info = json.dumps(existing)
|
|
||||||
sub.pcr_info = existing
|
|
||||||
except TypeError:
|
|
||||||
logger.error(f"Error updating!")
|
|
||||||
# sub.pcr_info = json.dumps([parser.pcr])
|
|
||||||
sub.pcr_info = [parser.pcr]
|
|
||||||
logger.debug(f"Final pcr info for {sub.rsl_plate_num}: {sub.pcr_info}")
|
|
||||||
else:
|
|
||||||
# sub.pcr_info = json.dumps([parser.pcr])
|
|
||||||
sub.pcr_info = [parser.pcr]
|
|
||||||
logger.debug(f"Existing {type(sub.pcr_info)}: {sub.pcr_info}")
|
|
||||||
logger.debug(f"Inserting {type(json.dumps(parser.pcr))}: {json.dumps(parser.pcr)}")
|
|
||||||
sub.save(original=False)
|
|
||||||
logger.debug(f"Got {len(parser.samples)} samples to update!")
|
|
||||||
logger.debug(f"Parser samples: {parser.samples}")
|
|
||||||
for sample in sub.samples:
|
|
||||||
logger.debug(f"Running update on: {sample}")
|
|
||||||
try:
|
|
||||||
sample_dict = [item for item in parser.samples if item['sample']==sample.rsl_number][0]
|
|
||||||
except IndexError:
|
|
||||||
continue
|
|
||||||
sub.update_subsampassoc(sample=sample, input_dict=sample_dict)
|
|
||||||
self.report.add_result(Result(msg=f"We added PCR info to {sub.rsl_plate_num}.", status='Information'))
|
|
||||||
|
|
||||||
def add_reagent(self, reagent_lot:str|None=None, reagent_type:str|None=None, expiry:date|None=None, name:str|None=None):
|
def add_reagent(self, reagent_lot:str|None=None, reagent_type:str|None=None, expiry:date|None=None, name:str|None=None):
|
||||||
"""
|
"""
|
||||||
Action to create new reagent in DB.
|
Action to create new reagent in DB.
|
||||||
|
|||||||
Reference in New Issue
Block a user