post documentation and code clean-up.
This commit is contained in:
@@ -62,7 +62,7 @@ class App(QMainWindow):
|
||||
# logger.debug(f"Creating menu bar...")
|
||||
menuBar = self.menuBar()
|
||||
fileMenu = menuBar.addMenu("&File")
|
||||
# Creating menus using a title
|
||||
# NOTE: Creating menus using a title
|
||||
methodsMenu = menuBar.addMenu("&Methods")
|
||||
reportMenu = menuBar.addMenu("&Reports")
|
||||
maintenanceMenu = menuBar.addMenu("&Monthly")
|
||||
@@ -70,7 +70,6 @@ class App(QMainWindow):
|
||||
helpMenu.addAction(self.helpAction)
|
||||
helpMenu.addAction(self.docsAction)
|
||||
fileMenu.addAction(self.importAction)
|
||||
# fileMenu.addAction(self.importPCRAction)
|
||||
methodsMenu.addAction(self.searchLog)
|
||||
methodsMenu.addAction(self.searchSample)
|
||||
reportMenu.addAction(self.generateReportAction)
|
||||
@@ -94,7 +93,6 @@ class App(QMainWindow):
|
||||
"""
|
||||
# logger.debug(f"Creating actions...")
|
||||
self.importAction = QAction("&Import Submission", self)
|
||||
# self.importPCRAction = QAction("&Import PCR Results", self)
|
||||
self.addReagentAction = QAction("Add Reagent", self)
|
||||
self.generateReportAction = QAction("Make Report", self)
|
||||
self.addKitAction = QAction("Import Kit", self)
|
||||
@@ -112,7 +110,6 @@ class App(QMainWindow):
|
||||
"""
|
||||
# logger.debug(f"Connecting actions...")
|
||||
self.importAction.triggered.connect(self.table_widget.formwidget.importSubmission)
|
||||
# self.importPCRAction.triggered.connect(self.table_widget.formwidget.import_pcr_results)
|
||||
self.addReagentAction.triggered.connect(self.table_widget.formwidget.add_reagent)
|
||||
self.generateReportAction.triggered.connect(self.table_widget.sub_wid.generate_report)
|
||||
self.joinExtractionAction.triggered.connect(self.table_widget.sub_wid.link_extractions)
|
||||
@@ -166,12 +163,17 @@ class App(QMainWindow):
|
||||
dlg.exec()
|
||||
|
||||
def runSampleSearch(self):
|
||||
"""
|
||||
Create a search for samples.
|
||||
"""
|
||||
dlg = SearchBox(self)
|
||||
dlg.exec()
|
||||
|
||||
def backup_database(self):
|
||||
"""
|
||||
Copies the database into the backup directory the first time it is opened every month.
|
||||
"""
|
||||
month = date.today().strftime("%Y-%m")
|
||||
# day = date.today().strftime("%Y-%m-%d")
|
||||
# logger.debug(f"Here is the db directory: {self.ctx.database_path}")
|
||||
# logger.debug(f"Here is the backup directory: {self.ctx.backup_path}")
|
||||
current_month_bak = Path(self.ctx.backup_path).joinpath(f"submissions_backup-{month}").resolve().with_suffix(".db")
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
'''
|
||||
Handles display of control charts
|
||||
'''
|
||||
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
||||
from PyQt6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QComboBox, QHBoxLayout,
|
||||
@@ -54,43 +57,37 @@ class ControlsViewer(QWidget):
|
||||
"""
|
||||
self.controls_getter_function()
|
||||
|
||||
def chart_maker(self):
|
||||
"""
|
||||
Creates plotly charts for webview
|
||||
"""
|
||||
self.chart_maker_function()
|
||||
|
||||
def controls_getter_function(self):
|
||||
"""
|
||||
Get controls based on start/end dates
|
||||
"""
|
||||
report = Report()
|
||||
# subtype defaults to disabled
|
||||
# NOTE: subtype defaults to disabled
|
||||
try:
|
||||
self.sub_typer.disconnect()
|
||||
except TypeError:
|
||||
pass
|
||||
# correct start date being more recent than end date and rerun
|
||||
# NOTE: correct start date being more recent than end date and rerun
|
||||
if self.datepicker.start_date.date() > self.datepicker.end_date.date():
|
||||
logger.warning("Start date after end date is not allowed!")
|
||||
threemonthsago = self.datepicker.end_date.date().addDays(-60)
|
||||
# block signal that will rerun controls getter and set start date
|
||||
# NOTE: block signal that will rerun controls getter and set start date
|
||||
# Without triggering this function again
|
||||
with QSignalBlocker(self.datepicker.start_date) as blocker:
|
||||
self.datepicker.start_date.setDate(threemonthsago)
|
||||
self.controls_getter()
|
||||
self.report.add_result(report)
|
||||
return
|
||||
# convert to python useable date objects
|
||||
# NOTE: convert to python useable date objects
|
||||
self.start_date = self.datepicker.start_date.date().toPyDate()
|
||||
self.end_date = self.datepicker.end_date.date().toPyDate()
|
||||
self.con_type = self.control_typer.currentText()
|
||||
self.mode = self.mode_typer.currentText()
|
||||
self.sub_typer.clear()
|
||||
# lookup subtypes
|
||||
# NOTE: lookup subtypes
|
||||
sub_types = ControlType.query(name=self.con_type).get_subtypes(mode=self.mode)
|
||||
if sub_types != []:
|
||||
# block signal that will rerun controls getter and update sub_typer
|
||||
# NOTE: block signal that will rerun controls getter and update sub_typer
|
||||
with QSignalBlocker(self.sub_typer) as blocker:
|
||||
self.sub_typer.addItems(sub_types)
|
||||
self.sub_typer.setEnabled(True)
|
||||
@@ -100,7 +97,13 @@ class ControlsViewer(QWidget):
|
||||
self.sub_typer.setEnabled(False)
|
||||
self.chart_maker()
|
||||
self.report.add_result(report)
|
||||
|
||||
|
||||
def chart_maker(self):
|
||||
"""
|
||||
Creates plotly charts for webview
|
||||
"""
|
||||
self.chart_maker_function()
|
||||
|
||||
def chart_maker_function(self):
|
||||
"""
|
||||
Create html chart for controls reporting
|
||||
@@ -119,7 +122,7 @@ class ControlsViewer(QWidget):
|
||||
else:
|
||||
self.subtype = self.sub_typer.currentText()
|
||||
# logger.debug(f"Subtype: {self.subtype}")
|
||||
# query all controls using the type/start and end dates from the gui
|
||||
# NOTE: query all controls using the type/start and end dates from the gui
|
||||
controls = Control.query(control_type=self.con_type, start_date=self.start_date, end_date=self.end_date)
|
||||
# NOTE: if no data found from query set fig to none for reporting in webview
|
||||
if controls is None:
|
||||
@@ -139,7 +142,7 @@ class ControlsViewer(QWidget):
|
||||
title = self.mode
|
||||
else:
|
||||
title = f"{self.mode} - {self.subtype}"
|
||||
# send dataframe to chart maker
|
||||
# NOTE: send dataframe to chart maker
|
||||
fig = create_charts(ctx=self.app.ctx, df=df, ytitle=title)
|
||||
# logger.debug(f"Updating figure...")
|
||||
# NOTE: construct html for webview
|
||||
@@ -157,7 +160,7 @@ class ControlsDatePicker(QWidget):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.start_date = QDateEdit(calendarPopup=True)
|
||||
# start date is two months prior to end date by default
|
||||
# NOTE: 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)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import sys
|
||||
'''
|
||||
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,
|
||||
@@ -180,6 +182,9 @@ class RoleComboBox(QWidget):
|
||||
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():
|
||||
|
||||
@@ -23,32 +23,32 @@ class GelBox(QDialog):
|
||||
|
||||
def __init__(self, parent, img_path:str|Path, submission:WastewaterArtic):
|
||||
super().__init__(parent)
|
||||
# setting title
|
||||
# NOTE: setting title
|
||||
self.setWindowTitle("PyQtGraph")
|
||||
self.img_path = img_path
|
||||
self.submission = submission
|
||||
# setting geometry
|
||||
# NOTE: setting geometry
|
||||
self.setGeometry(50, 50, 1200, 900)
|
||||
# icon
|
||||
# NOTE: icon
|
||||
icon = QIcon("skin.png")
|
||||
# setting icon to the window
|
||||
# NOTE: setting icon to the window
|
||||
self.setWindowIcon(icon)
|
||||
# calling method
|
||||
# NOTE: calling method
|
||||
self.UiComponents()
|
||||
# showing all the widgets
|
||||
# NOTE: showing all the widgets
|
||||
|
||||
# method for components
|
||||
def UiComponents(self):
|
||||
"""
|
||||
Create widgets in ui
|
||||
"""
|
||||
# setting configuration options
|
||||
# NOTE: setting configuration options
|
||||
pg.setConfigOptions(antialias=True)
|
||||
# creating image view object
|
||||
# NOTE: creating image view object
|
||||
self.imv = pg.ImageView()
|
||||
# Create image.
|
||||
# For some reason, ImageView wants to flip the image, so we have to rotate and flip the array first.
|
||||
# Using the Image.rotate function results in cropped image.
|
||||
# NOTE: Create image.
|
||||
# NOTE: For some reason, ImageView wants to flip the image, so we have to rotate and flip the array first.
|
||||
# NOTE: Using the Image.rotate function results in cropped image, so using np.
|
||||
img = np.flip(np.rot90(np.array(Image.open(self.img_path)),1),0)
|
||||
self.imv.setImage(img)
|
||||
layout = QGridLayout()
|
||||
@@ -60,10 +60,10 @@ class GelBox(QDialog):
|
||||
self.gel_barcode = QLineEdit()
|
||||
self.gel_barcode.setText(self.submission.gel_barcode)
|
||||
layout.addWidget(self.gel_barcode, 0, 4)
|
||||
# setting this layout to the widget
|
||||
# plot window goes on right side, spanning 3 rows
|
||||
# NOTE: setting this layout to the widget
|
||||
# NOTE: plot window goes on right side, spanning 3 rows
|
||||
layout.addWidget(self.imv, 1, 1,20,20)
|
||||
# setting this widget as central widget of the main window
|
||||
# NOTE: setting this widget as central widget of the main window
|
||||
try:
|
||||
control_info = sorted(self.submission.gel_controls, key=lambda d: d['location'])
|
||||
except KeyError:
|
||||
@@ -123,16 +123,10 @@ class ControlsForm(QWidget):
|
||||
widge.setText("Neg")
|
||||
widge.setObjectName(f"{rows[iii]} : {columns[jjj]}")
|
||||
self.layout.addWidget(widge, iii+1, jjj+2, 1, 1)
|
||||
# try:
|
||||
# for iii, item in enumerate(control_info, start=1):
|
||||
# self.layout.addWidget(QLabel(f"{item['sample_id']} - {item['location']}"), iii+4, 1)
|
||||
# except TypeError:
|
||||
# pass
|
||||
self.layout.addWidget(QLabel("Comments:"), 0,5,1,1)
|
||||
self.comment_field = QTextEdit(self)
|
||||
self.comment_field.setFixedHeight(50)
|
||||
self.layout.addWidget(self.comment_field, 1,5,4,1)
|
||||
|
||||
self.setLayout(self.layout)
|
||||
|
||||
def parse_form(self) -> List[dict]:
|
||||
|
||||
@@ -30,27 +30,27 @@ class KitAdder(QWidget):
|
||||
scrollContent = QWidget(scroll)
|
||||
self.grid = QGridLayout()
|
||||
scrollContent.setLayout(self.grid)
|
||||
# insert submit button at top
|
||||
# NOTE: 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
|
||||
# NOTE: 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
|
||||
# NOTE: widget to get uses of kit
|
||||
used_for = QComboBox()
|
||||
used_for.setObjectName("used_for")
|
||||
# Insert all existing sample types
|
||||
# NOTE: Insert all existing sample types
|
||||
used_for.addItems([item.name for item in SubmissionType.query()])
|
||||
used_for.setEditable(True)
|
||||
self.grid.addWidget(used_for,3,1)
|
||||
# Get all fields in SubmissionTypeKitTypeAssociation
|
||||
# NOTE: 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.
|
||||
# NOTE: convert field name to human readable.
|
||||
field_name = column.name.replace("_", " ").title()
|
||||
self.grid.addWidget(QLabel(field_name),idx,0)
|
||||
match column.type:
|
||||
@@ -79,7 +79,7 @@ class KitAdder(QWidget):
|
||||
"""
|
||||
insert new reagent type row
|
||||
"""
|
||||
# get bottommost row
|
||||
# NOTE: get bottommost row
|
||||
maxrow = self.grid.rowCount()
|
||||
reg_form = ReagentRoleForm(parent=self)
|
||||
reg_form.setObjectName(f"ReagentForm_{maxrow}")
|
||||
@@ -90,14 +90,14 @@ class KitAdder(QWidget):
|
||||
send kit to database
|
||||
"""
|
||||
report = Report()
|
||||
# get form info
|
||||
# NOTE: get form info
|
||||
info, reagents = self.parse_form()
|
||||
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_roles'] = reagents
|
||||
# logger.debug(pformat(info))
|
||||
# send to kit constructor
|
||||
# NOTE: send to kit constructor
|
||||
kit = PydKit(name=info['kit_name'])
|
||||
for reagent in info['reagent_roles']:
|
||||
uses = {
|
||||
|
||||
@@ -27,15 +27,12 @@ class AddReagentForm(QDialog):
|
||||
super().__init__()
|
||||
if reagent_lot is None:
|
||||
reagent_lot = reagent_role
|
||||
|
||||
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
|
||||
# NOTE: widget to get lot info
|
||||
self.name_input = QComboBox()
|
||||
self.name_input.setObjectName("name")
|
||||
self.name_input.setEditable(True)
|
||||
@@ -43,10 +40,10 @@ class AddReagentForm(QDialog):
|
||||
self.lot_input = QLineEdit()
|
||||
self.lot_input.setObjectName("lot")
|
||||
self.lot_input.setText(reagent_lot)
|
||||
# widget to get expiry info
|
||||
# NOTE: 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
|
||||
# NOTE: if expiry is not passed in from gui, use today
|
||||
if expiry is None:
|
||||
self.exp_input.setDate(QDate.currentDate())
|
||||
else:
|
||||
@@ -54,17 +51,17 @@ class AddReagentForm(QDialog):
|
||||
self.exp_input.setDate(expiry)
|
||||
except TypeError:
|
||||
self.exp_input.setDate(QDate.currentDate())
|
||||
# widget to get reagent type info
|
||||
# NOTE: widget to get reagent type info
|
||||
self.type_input = QComboBox()
|
||||
self.type_input.setObjectName('type')
|
||||
self.type_input.addItems([item.name for item in ReagentRole.query()])
|
||||
# logger.debug(f"Trying to find index of {reagent_type}")
|
||||
# convert input to user friendly string?
|
||||
# NOTE: convert input to user friendly string?
|
||||
try:
|
||||
reagent_role = reagent_role.replace("_", " ").title()
|
||||
except AttributeError:
|
||||
reagent_role = None
|
||||
# set parsed reagent type to top of list
|
||||
# NOTE: set parsed reagent type to top of list
|
||||
index = self.type_input.findText(reagent_role, Qt.MatchFlag.MatchEndsWith)
|
||||
if index >= 0:
|
||||
self.type_input.setCurrentIndex(index)
|
||||
@@ -110,12 +107,12 @@ class ReportDatePicker(QDialog):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.setWindowTitle("Select Report Date Range")
|
||||
# make confirm/reject buttons
|
||||
# NOTE: 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
|
||||
# NOTE: widgets to ask for dates
|
||||
self.start_date = QDateEdit(calendarPopup=True)
|
||||
self.start_date.setObjectName("start_date")
|
||||
self.start_date.setDate(QDate.currentDate())
|
||||
@@ -139,48 +136,6 @@ class ReportDatePicker(QDialog):
|
||||
"""
|
||||
return dict(start_date=self.start_date.date().toPyDate(), end_date = self.end_date.date().toPyDate())
|
||||
|
||||
|
||||
class FirstStrandSalvage(QDialog):
|
||||
|
||||
def __init__(self, ctx:Settings, submitter_id:str, rsl_plate_num:str|None=None) -> None:
|
||||
super().__init__()
|
||||
if rsl_plate_num is 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) -> dict:
|
||||
"""
|
||||
Pulls first strand info from form.
|
||||
|
||||
Returns:
|
||||
dict: Output info
|
||||
"""
|
||||
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 LogParser(QDialog):
|
||||
|
||||
def __init__(self, parent):
|
||||
|
||||
@@ -22,13 +22,13 @@ class QuestionAsker(QDialog):
|
||||
def __init__(self, title:str, message:str) -> QDialog:
|
||||
super().__init__()
|
||||
self.setWindowTitle(title)
|
||||
# set yes/no buttons
|
||||
# NOTE: set yes/no buttons
|
||||
QBtn = QDialogButtonBox.StandardButton.Yes | QDialogButtonBox.StandardButton.No
|
||||
self.buttonBox = QDialogButtonBox(QBtn)
|
||||
self.buttonBox.accepted.connect(self.accept)
|
||||
self.buttonBox.rejected.connect(self.reject)
|
||||
self.layout = QVBoxLayout()
|
||||
# Text for the yes/no question
|
||||
# NOTE: Text for the yes/no question
|
||||
self.message = QLabel(message)
|
||||
self.layout.addWidget(self.message)
|
||||
self.layout.addWidget(self.buttonBox)
|
||||
@@ -41,7 +41,7 @@ class AlertPop(QMessageBox):
|
||||
"""
|
||||
def __init__(self, message:str, status:Literal['Information', 'Question', 'Warning', 'Critical'], owner:str|None=None) -> QMessageBox:
|
||||
super().__init__()
|
||||
# select icon by string
|
||||
# NOTE: select icon by string
|
||||
icon = getattr(QMessageBox.Icon, status)
|
||||
self.setIcon(icon)
|
||||
self.setInformativeText(message)
|
||||
@@ -61,13 +61,13 @@ class ObjectSelector(QDialog):
|
||||
items = [item.name for item in obj_type.query()]
|
||||
self.widget.addItems(items)
|
||||
self.widget.setEditable(False)
|
||||
# set yes/no buttons
|
||||
# NOTE: set yes/no buttons
|
||||
QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
|
||||
self.buttonBox = QDialogButtonBox(QBtn)
|
||||
self.buttonBox.accepted.connect(self.accept)
|
||||
self.buttonBox.rejected.connect(self.reject)
|
||||
self.layout = QVBoxLayout()
|
||||
# Text for the yes/no question
|
||||
# NOTE: Text for the yes/no question
|
||||
message = QLabel(message)
|
||||
self.layout.addWidget(message)
|
||||
self.layout.addWidget(self.widget)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
'''
|
||||
Search box that performs fuzzy search for samples
|
||||
'''
|
||||
from pprint import pformat
|
||||
from typing import Tuple
|
||||
from pandas import DataFrame
|
||||
@@ -33,6 +36,9 @@ class SearchBox(QDialog):
|
||||
self.update_widgets()
|
||||
|
||||
def update_widgets(self):
|
||||
"""
|
||||
Changes form inputs based on sample type
|
||||
"""
|
||||
deletes = [item for item in self.findChildren(FieldSearch)]
|
||||
# logger.debug(deletes)
|
||||
for item in deletes:
|
||||
@@ -45,13 +51,21 @@ class SearchBox(QDialog):
|
||||
widget = FieldSearch(parent=self, label=item['label'], field_name=item['field'])
|
||||
self.layout.addWidget(widget, start_row+iii, 0)
|
||||
|
||||
def parse_form(self):
|
||||
def parse_form(self) -> dict:
|
||||
"""
|
||||
Converts form into dictionary.
|
||||
|
||||
Returns:
|
||||
dict: Fields dictionary
|
||||
"""
|
||||
fields = [item.parse_form() for item in self.findChildren(FieldSearch)]
|
||||
return {item[0]:item[1] for item in fields if item[1] is not None}
|
||||
|
||||
def update_data(self):
|
||||
"""
|
||||
Shows dataframe of relevant samples.
|
||||
"""
|
||||
fields = self.parse_form()
|
||||
# data = self.type.samples_to_df(sample_type=self.type, **fields)
|
||||
data = self.type.fuzzy_search(sample_type=self.type, **fields)
|
||||
data = self.type.samples_to_df(sample_list=data)
|
||||
# logger.debug(f"Data: {data}")
|
||||
@@ -72,6 +86,9 @@ class FieldSearch(QWidget):
|
||||
self.search_widget.returnPressed.connect(self.enter_pressed)
|
||||
|
||||
def enter_pressed(self):
|
||||
"""
|
||||
Triggered when enter is pressed on this input field.
|
||||
"""
|
||||
self.parent().update_data()
|
||||
|
||||
def parse_form(self) -> Tuple:
|
||||
@@ -99,4 +116,5 @@ class SearchResults(QTableView):
|
||||
logger.error("Couldn't format id string.")
|
||||
proxy_model = QSortFilterProxyModel()
|
||||
proxy_model.setSourceModel(pandasModel(self.data))
|
||||
self.setModel(proxy_model)
|
||||
self.setModel(proxy_model)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from PyQt6.QtGui import QPageSize
|
||||
from PyQt6.QtPrintSupport import QPrinter
|
||||
from PyQt6.QtWidgets import (QDialog, QPushButton, QVBoxLayout, QMessageBox,
|
||||
'''
|
||||
Webview to show submission and sample details.
|
||||
'''
|
||||
from PyQt6.QtWidgets import (QDialog, QPushButton, QVBoxLayout,
|
||||
QDialogButtonBox, QTextEdit)
|
||||
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
||||
from PyQt6.QtWebChannel import QWebChannel
|
||||
@@ -9,14 +10,12 @@ from PyQt6.QtCore import Qt, pyqtSlot
|
||||
from backend.db.models import BasicSubmission, BasicSample
|
||||
from tools import is_power_user, html_to_pdf
|
||||
from .functions import select_save_file
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
import logging, base64
|
||||
import logging
|
||||
from getpass import getuser
|
||||
from datetime import datetime
|
||||
from pprint import pformat
|
||||
from html2image import Html2Image
|
||||
from PIL import Image
|
||||
|
||||
from typing import List
|
||||
from backend.excel.writer import DocxWriter
|
||||
|
||||
@@ -135,7 +134,7 @@ class SubmissionComment(QDialog):
|
||||
pass
|
||||
self.submission = submission
|
||||
self.setWindowTitle(f"{self.submission.rsl_plate_num} Submission Comment")
|
||||
# create text field
|
||||
# NOTE: create text field
|
||||
self.txt_editor = QTextEdit(self)
|
||||
self.txt_editor.setReadOnly(False)
|
||||
self.txt_editor.setText("Add Comment")
|
||||
|
||||
@@ -261,32 +261,32 @@ class SubmissionsSheet(QTableView):
|
||||
html = make_report_html(df=summary_df, start_date=info['start_date'], end_date=info['end_date'])
|
||||
# NOTE: get save location of report
|
||||
fname = select_save_file(obj=self, default_name=f"Submissions_Report_{info['start_date']}-{info['end_date']}.docx", extension="docx")
|
||||
html_to_pdf(html=html, output_file=fname)
|
||||
writer = pd.ExcelWriter(fname.with_suffix(".xlsx"), engine='openpyxl')
|
||||
summary_df.to_excel(writer, sheet_name="Report")
|
||||
detailed_df.to_excel(writer, sheet_name="Details", index=False)
|
||||
worksheet: Worksheet = writer.sheets['Report']
|
||||
for idx, col in enumerate(summary_df, start=1): # loop through all columns
|
||||
series = summary_df[col]
|
||||
max_len = max((
|
||||
series.astype(str).map(len).max(), # len of largest item
|
||||
len(str(series.name)) # len of column name/header
|
||||
)) + 20 # adding a little extra space
|
||||
try:
|
||||
# NOTE: Convert idx to letter
|
||||
col_letter = chr(ord('@') + idx)
|
||||
worksheet.column_dimensions[col_letter].width = max_len
|
||||
except ValueError:
|
||||
pass
|
||||
blank_row = get_first_blank_df_row(summary_df) + 1
|
||||
# logger.debug(f"Blank row index = {blank_row}")
|
||||
for col in range(3,6):
|
||||
col_letter = row_map[col]
|
||||
worksheet.cell(row=blank_row, column=col, value=f"=SUM({col_letter}2:{col_letter}{str(blank_row-1)})")
|
||||
for cell in worksheet['D']:
|
||||
if cell.row > 1:
|
||||
cell.style = 'Currency'
|
||||
writer.close()
|
||||
# rp = ReportMaker(start_date=info['start_date'], end_date=info['end_date'])
|
||||
# rp.write_report(filename=fname, obj=self)
|
||||
# html_to_pdf(html=html, output_file=fname)
|
||||
# writer = pd.ExcelWriter(fname.with_suffix(".xlsx"), engine='openpyxl')
|
||||
# summary_df.to_excel(writer, sheet_name="Report")
|
||||
# detailed_df.to_excel(writer, sheet_name="Details", index=False)
|
||||
# worksheet: Worksheet = writer.sheets['Report']
|
||||
# for idx, col in enumerate(summary_df, start=1): # loop through all columns
|
||||
# series = summary_df[col]
|
||||
# max_len = max((
|
||||
# series.astype(str).map(len).max(), # len of largest item
|
||||
# len(str(series.name)) # len of column name/header
|
||||
# )) + 20 # adding a little extra space
|
||||
# try:
|
||||
# # NOTE: Convert idx to letter
|
||||
# col_letter = chr(ord('@') + idx)
|
||||
# worksheet.column_dimensions[col_letter].width = max_len
|
||||
# except ValueError:
|
||||
# pass
|
||||
# blank_row = get_first_blank_df_row(summary_df) + 1
|
||||
# # logger.debug(f"Blank row index = {blank_row}")
|
||||
# for col in range(3,6):
|
||||
# col_letter = row_map[col]
|
||||
# worksheet.cell(row=blank_row, column=col, value=f"=SUM({col_letter}2:{col_letter}{str(blank_row-1)})")
|
||||
# for cell in worksheet['D']:
|
||||
# if cell.row > 1:
|
||||
# cell.style = 'Currency'
|
||||
# writer.close()
|
||||
rp = ReportMaker(start_date=info['start_date'], end_date=info['end_date'])
|
||||
rp.write_report(filename=fname, obj=self)
|
||||
self.report.add_result(report)
|
||||
|
||||
@@ -114,4 +114,5 @@ class InfoWidget(QWidget):
|
||||
sheets = self.sheet.text().split(","),
|
||||
row = self.row.value(),
|
||||
column = self.column.value()
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
'''
|
||||
Contains all submission related frontend functions
|
||||
'''
|
||||
import sys
|
||||
|
||||
from PyQt6.QtWidgets import (
|
||||
QWidget, QPushButton, QVBoxLayout,
|
||||
QComboBox, QDateEdit, QLineEdit, QLabel
|
||||
@@ -149,8 +151,6 @@ class SubmissionFormContainer(QWidget):
|
||||
|
||||
class SubmissionFormWidget(QWidget):
|
||||
|
||||
|
||||
|
||||
def __init__(self, parent: QWidget, submission: PydSubmission) -> None:
|
||||
super().__init__(parent)
|
||||
# self.report = Report()
|
||||
@@ -303,10 +303,10 @@ class SubmissionFormWidget(QWidget):
|
||||
# logger.debug(f"Base submission: {base_submission.to_dict()}")
|
||||
# NOTE: check output message for issues
|
||||
match result.code:
|
||||
# code 0: everything is fine.
|
||||
# NOTE: code 0: everything is fine.
|
||||
case 0:
|
||||
report.add_result(None)
|
||||
# code 1: ask for overwrite
|
||||
# NOTE: code 1: ask for overwrite
|
||||
case 1:
|
||||
dlg = QuestionAsker(title=f"Review {base_submission.rsl_plate_num}?", message=result.msg)
|
||||
if dlg.exec():
|
||||
@@ -319,7 +319,7 @@ class SubmissionFormWidget(QWidget):
|
||||
self.app.report.add_result(report)
|
||||
self.app.result_reporter()
|
||||
return
|
||||
# code 2: No RSL plate number given
|
||||
# NOTE: code 2: No RSL plate number given
|
||||
case 2:
|
||||
report.add_result(result)
|
||||
self.app.report.add_result(report)
|
||||
@@ -351,9 +351,8 @@ class SubmissionFormWidget(QWidget):
|
||||
if isinstance(fname, bool) or fname is None:
|
||||
fname = select_save_file(obj=self, default_name=self.pyd.construct_filename(), extension="csv")
|
||||
try:
|
||||
|
||||
# self.pyd.csv.to_csv(fname.__str__(), index=False)
|
||||
workbook_2_csv(worksheet=self.pyd.csv, filename=fname)
|
||||
self.pyd.export_csv(fname)
|
||||
# workbook_2_csv(worksheet=self.pyd.csv, filename=fname)
|
||||
except PermissionError:
|
||||
logger.warning(f"Could not get permissions to {fname}. Possibly the request was cancelled.")
|
||||
except AttributeError:
|
||||
|
||||
Reference in New Issue
Block a user