Before addition of tips.

This commit is contained in:
lwark
2024-06-10 14:18:11 -05:00
parent c94ead5db2
commit 3b9dc69618
14 changed files with 31 additions and 97 deletions

View File

@@ -2,7 +2,6 @@
All kit and reagent related models All kit and reagent related models
""" """
from __future__ import annotations from __future__ import annotations
from copy import copy
from sqlalchemy import Column, String, TIMESTAMP, JSON, INTEGER, ForeignKey, Interval, Table, FLOAT, BLOB from sqlalchemy import Column, String, TIMESTAMP, JSON, INTEGER, ForeignKey, Interval, Table, FLOAT, BLOB
from sqlalchemy.orm import relationship, validates, Query from sqlalchemy.orm import relationship, validates, Query
from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.ext.associationproxy import association_proxy

View File

@@ -8,8 +8,6 @@ from pprint import pformat
from typing import List from typing import List
import pandas as pd import pandas as pd
from openpyxl import load_workbook, Workbook from openpyxl import load_workbook, Workbook
from openpyxl.worksheet.protection import SheetProtection
import numpy as np
from pathlib import Path from pathlib import Path
from backend.db.models import * from backend.db.models import *
from backend.validators import PydSubmission, PydReagent, RSLNamer, PydSample, PydEquipment from backend.validators import PydSubmission, PydReagent, RSLNamer, PydSample, PydEquipment
@@ -17,14 +15,12 @@ import logging, re
from collections import OrderedDict from collections import OrderedDict
from datetime import date from datetime import date
from dateutil.parser import parse, ParserError from dateutil.parser import parse, ParserError
from tools import check_not_nan, convert_nans_to_nones, row_map, row_keys, is_missing, remove_key_from_list_of_dicts from tools import check_not_nan, convert_nans_to_nones, is_missing, remove_key_from_list_of_dicts
logger = logging.getLogger(f"submissions.{__name__}") logger = logging.getLogger(f"submissions.{__name__}")
# row_keys = {v:k for k,v in row_map.items()}
class SheetParser(object): class SheetParser(object):
""" """
object to pull and contain data from excel file object to pull and contain data from excel file

View File

@@ -1,12 +1,10 @@
import logging import logging
from copy import copy from copy import copy
from pathlib import Path # from pathlib import Path
from pprint import pformat from pprint import pformat
from typing import List from typing import List
from openpyxl import load_workbook, Workbook from openpyxl import load_workbook, Workbook
from tools import row_keys from backend.db.models import SubmissionType, KitType, BasicSubmission
from backend.db.models import SubmissionType, KitType, BasicSample, BasicSubmission
from backend.validators.pydant import PydSubmission from backend.validators.pydant import PydSubmission
from io import BytesIO from io import BytesIO
from collections import OrderedDict from collections import OrderedDict

View File

@@ -2,7 +2,6 @@
Contains pydantic models and accompanying validators Contains pydantic models and accompanying validators
''' '''
from __future__ import annotations from __future__ import annotations
import sys import sys
from operator import attrgetter from operator import attrgetter
import uuid, re, logging import uuid, re, logging

View File

@@ -8,6 +8,7 @@ from PyQt6.QtWidgets import QMainWindow, QFileDialog
logger = logging.getLogger(f"submissions.{__name__}") logger = logging.getLogger(f"submissions.{__name__}")
def select_open_file(obj:QMainWindow, file_extension:str) -> Path: def select_open_file(obj:QMainWindow, file_extension:str) -> Path:
""" """
File dialog to select a file to read from File dialog to select a file to read from
@@ -29,6 +30,7 @@ def select_open_file(obj:QMainWindow, file_extension:str) -> Path:
obj.last_dir = fname.parent obj.last_dir = fname.parent
return fname return fname
def select_save_file(obj:QMainWindow, default_name:str, extension:str) -> Path: def select_save_file(obj:QMainWindow, default_name:str, extension:str) -> Path:
""" """
File dialog to select a file to write to File dialog to select a file to write to

View File

@@ -5,9 +5,8 @@ from PyQt6.QtWidgets import (QWidget, QDialog, QGridLayout,
QLabel, QLineEdit, QDialogButtonBox, QLabel, QLineEdit, QDialogButtonBox,
QTextEdit QTextEdit
) )
import numpy as np
import pyqtgraph as pg import pyqtgraph as pg
from PyQt6.QtGui import QIcon, QFont from PyQt6.QtGui import QIcon
from PIL import Image from PIL import Image
import numpy as np import numpy as np
import logging import logging
@@ -18,6 +17,7 @@ from backend.db.models import WastewaterArtic
logger = logging.getLogger(f"submissions.{__name__}") logger = logging.getLogger(f"submissions.{__name__}")
# Main window class # Main window class
class GelBox(QDialog): class GelBox(QDialog):
@@ -86,7 +86,8 @@ class GelBox(QDialog):
gel_barcode = self.gel_barcode.text() gel_barcode = self.gel_barcode.text()
values, comment = self.form.parse_form() values, comment = self.form.parse_form()
return dna_core_submission_number, gel_barcode, self.img_path, values, comment return dna_core_submission_number, gel_barcode, self.img_path, values, comment
class ControlsForm(QWidget): class ControlsForm(QWidget):
def __init__(self, parent, control_info:List=None) -> None: def __init__(self, parent, control_info:List=None) -> None:

View File

@@ -138,7 +138,8 @@ class KitAdder(QWidget):
case QDateEdit(): case QDateEdit():
info[widget.objectName()] = widget.date().toPyDate() info[widget.objectName()] = widget.date().toPyDate()
return info, reagents return info, reagents
class ReagentRoleForm(QWidget): class ReagentRoleForm(QWidget):
""" """
custom widget to add information about a new reagenttype custom widget to add information about a new reagenttype

View File

@@ -13,12 +13,12 @@ from backend.db.models import *
import logging import logging
from .pop_ups import AlertPop from .pop_ups import AlertPop
from .functions import select_open_file from .functions import select_open_file
from tools import Settings
logger = logging.getLogger(f"submissions.{__name__}") logger = logging.getLogger(f"submissions.{__name__}")
env = jinja_template_loading() env = jinja_template_loading()
class AddReagentForm(QDialog): class AddReagentForm(QDialog):
""" """
dialog to add gather info about new reagent dialog to add gather info about new reagent
@@ -102,6 +102,7 @@ class AddReagentForm(QDialog):
lookup = Reagent.query(reagent_role=self.type_input.currentText()) lookup = Reagent.query(reagent_role=self.type_input.currentText())
self.name_input.addItems(list(set([item.name for item in lookup]))) self.name_input.addItems(list(set([item.name for item in lookup])))
class ReportDatePicker(QDialog): class ReportDatePicker(QDialog):
""" """
custom dialog to ask for report start/stop dates custom dialog to ask for report start/stop dates
@@ -138,6 +139,7 @@ class ReportDatePicker(QDialog):
""" """
return dict(start_date=self.start_date.date().toPyDate(), end_date = self.end_date.date().toPyDate()) return dict(start_date=self.start_date.date().toPyDate(), end_date = self.end_date.date().toPyDate())
class FirstStrandSalvage(QDialog): class FirstStrandSalvage(QDialog):
def __init__(self, ctx:Settings, submitter_id:str, rsl_plate_num:str|None=None) -> None: def __init__(self, ctx:Settings, submitter_id:str, rsl_plate_num:str|None=None) -> None:
@@ -178,6 +180,7 @@ class FirstStrandSalvage(QDialog):
""" """
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()}") 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): class LogParser(QDialog):
def __init__(self, parent): def __init__(self, parent):
@@ -197,7 +200,6 @@ class LogParser(QDialog):
self.btn.clicked.connect(self.runsearch) self.btn.clicked.connect(self.runsearch)
self.setMinimumWidth(400) self.setMinimumWidth(400)
def filelookup(self): def filelookup(self):
""" """
Select file to search Select file to search

View File

@@ -14,6 +14,7 @@ logger = logging.getLogger(f"submissions.{__name__}")
env = jinja_template_loading() env = jinja_template_loading()
class QuestionAsker(QDialog): class QuestionAsker(QDialog):
""" """
dialog to ask yes/no questions dialog to ask yes/no questions
@@ -33,6 +34,7 @@ class QuestionAsker(QDialog):
self.layout.addWidget(self.buttonBox) self.layout.addWidget(self.buttonBox)
self.setLayout(self.layout) self.setLayout(self.layout)
class AlertPop(QMessageBox): class AlertPop(QMessageBox):
""" """
Dialog to show an alert. Dialog to show an alert.
@@ -45,6 +47,7 @@ class AlertPop(QMessageBox):
self.setInformativeText(message) self.setInformativeText(message)
self.setWindowTitle(f"{owner} - {status.title()}") self.setWindowTitle(f"{owner} - {status.title()}")
class ObjectSelector(QDialog): class ObjectSelector(QDialog):
""" """
dialog to input BaseClass type manually dialog to input BaseClass type manually
@@ -79,70 +82,3 @@ class ObjectSelector(QDialog):
str: KitType as str str: KitType as str
""" """
return self.widget.currentText() return self.widget.currentText()
# class KitSelector(QDialog):
# """
# dialog to input KitType manually
# """
# def __init__(self, title:str, message:str) -> QDialog:
# super().__init__()
# self.setWindowTitle(title)
# self.widget = QComboBox()
# kits = [item.name for item in KitType.query()]
# self.widget.addItems(kits)
# self.widget.setEditable(False)
# # 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
# message = QLabel(message)
# self.layout.addWidget(message)
# self.layout.addWidget(self.widget)
# self.layout.addWidget(self.buttonBox)
# self.setLayout(self.layout)
# def getValues(self) -> str:
# """
# Get KitType(str) from widget
# Returns:
# str: KitType as str
# """
# return self.widget.currentText()
# class SubmissionTypeSelector(QDialog):
# """
# dialog to input SubmissionType manually
# """
# def __init__(self, title:str, message:str) -> QDialog:
# super().__init__()
# self.setWindowTitle(title)
# self.widget = QComboBox()
# # sub_type = [item.name for item in lookup_submission_type(ctx=ctx)]
# sub_type = [item.name for item in SubmissionType.query()]
# self.widget.addItems(sub_type)
# self.widget.setEditable(False)
# # 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
# message = QLabel(message)
# self.layout.addWidget(message)
# self.layout.addWidget(self.widget)
# self.layout.addWidget(self.buttonBox)
# self.setLayout(self.layout)
# def parse_form(self) -> str:
# """
# Pulls SubmissionType(str) from widget
# Returns:
# str: SubmissionType as str
# """
# return self.widget.currentText()

View File

@@ -1,14 +1,13 @@
from pprint import pformat from pprint import pformat
from typing import Tuple from typing import Tuple
from pandas import DataFrame from pandas import DataFrame
from PyQt6.QtCore import QAbstractTableModel, Qt, QEvent, QSortFilterProxyModel from PyQt6.QtCore import QSortFilterProxyModel
from PyQt6.QtWidgets import ( from PyQt6.QtWidgets import (
QLabel, QVBoxLayout, QDialog, QLabel, QVBoxLayout, QDialog,
QDialogButtonBox, QMessageBox, QComboBox, QTableView, QWidget, QLineEdit, QGridLayout QComboBox, QTableView, QWidget, QLineEdit, QGridLayout
) )
from backend.db.models import BasicSample from backend.db.models import BasicSample
from .submission_table import pandasModel from .submission_table import pandasModel
from .submission_details import SubmissionDetails
import logging import logging
logger = logging.getLogger(f"submissions.{__name__}") logger = logging.getLogger(f"submissions.{__name__}")

View File

@@ -21,6 +21,7 @@ from typing import List
logger = logging.getLogger(f"submissions.{__name__}") logger = logging.getLogger(f"submissions.{__name__}")
class SubmissionDetails(QDialog): class SubmissionDetails(QDialog):
""" """
a window showing text details of submission a window showing text details of submission
@@ -92,8 +93,8 @@ class SubmissionDetails(QDialog):
self.base_dict, self.template = submission.get_details_template(base_dict=self.base_dict) self.base_dict, self.template = submission.get_details_template(base_dict=self.base_dict)
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)
with open("test.html", "w") as f: # with open("test.html", "w") as f:
f.write(self.html) # f.write(self.html)
self.setWindowTitle(f"Submission Details - {submission.rsl_plate_num}") self.setWindowTitle(f"Submission Details - {submission.rsl_plate_num}")
@pyqtSlot(str) @pyqtSlot(str)
@@ -125,8 +126,6 @@ class SubmissionDetails(QDialog):
del self.base_dict['platemap'] del self.base_dict['platemap']
self.html2 = self.template.render(sub=self.base_dict) self.html2 = self.template.render(sub=self.base_dict)
try: try:
# with open(fname, "w+b") as f:
# pisa.CreatePDF(self.html2, dest=f)
html_to_pdf(html=self.html2, output_file=fname) html_to_pdf(html=self.html2, output_file=fname)
except PermissionError as e: except PermissionError as e:
logger.error(f"Error saving pdf: {e}") logger.error(f"Error saving pdf: {e}")
@@ -135,7 +134,8 @@ class SubmissionDetails(QDialog):
msg.setInformativeText(f"Looks like {fname.__str__()} is open.\nPlease close it and try again.") msg.setInformativeText(f"Looks like {fname.__str__()} is open.\nPlease close it and try again.")
msg.setWindowTitle("Permission Error") msg.setWindowTitle("Permission Error")
msg.exec() msg.exec()
class SubmissionComment(QDialog): class SubmissionComment(QDialog):
""" """
a window for adding comment text to a submission a window for adding comment text to a submission
@@ -174,4 +174,3 @@ class SubmissionComment(QDialog):
full_comment = {"name":commenter, "time": dt, "text": comment} full_comment = {"name":commenter, "time": dt, "text": comment}
# logger.debug(f"Full comment: {full_comment}") # logger.debug(f"Full comment: {full_comment}")
return full_comment return full_comment

View File

@@ -9,7 +9,6 @@ from PyQt6.QtGui import QAction, QCursor
from backend.db.models import BasicSubmission from backend.db.models import BasicSubmission
from backend.excel import make_report_html, make_report_xlsx from backend.excel import make_report_html, make_report_xlsx
from tools import Report, Result, row_map, get_first_blank_df_row, html_to_pdf from tools import Report, Result, row_map, get_first_blank_df_row, html_to_pdf
# from xhtml2pdf import pisa
from .functions import select_save_file, select_open_file from .functions import select_save_file, select_open_file
from .misc import ReportDatePicker from .misc import ReportDatePicker
import pandas as pd import pandas as pd
@@ -17,6 +16,7 @@ from openpyxl.worksheet.worksheet import Worksheet
logger = logging.getLogger(f"submissions.{__name__}") logger = logging.getLogger(f"submissions.{__name__}")
class pandasModel(QAbstractTableModel): class pandasModel(QAbstractTableModel):
""" """
pandas model for inserting summary sheet into gui pandas model for inserting summary sheet into gui
@@ -60,7 +60,8 @@ class pandasModel(QAbstractTableModel):
if orientation == Qt.Orientation.Horizontal and role == Qt.ItemDataRole.DisplayRole: if orientation == Qt.Orientation.Horizontal and role == Qt.ItemDataRole.DisplayRole:
return self._data.columns[col] return self._data.columns[col]
return None return None
class SubmissionsSheet(QTableView): class SubmissionsSheet(QTableView):
""" """
presents submission summary to user in tab1 presents submission summary to user in tab1

View File

@@ -12,6 +12,7 @@ from .functions import select_open_file
logger = logging.getLogger(f"submissions.{__name__}") logger = logging.getLogger(f"submissions.{__name__}")
class SubmissionTypeAdder(QWidget): class SubmissionTypeAdder(QWidget):
def __init__(self, parent) -> None: def __init__(self, parent) -> None:
@@ -80,6 +81,7 @@ class SubmissionTypeAdder(QWidget):
self.template_path = select_open_file(obj=self, file_extension="xlsx") self.template_path = select_open_file(obj=self, file_extension="xlsx")
self.template_label.setText(self.template_path.__str__()) self.template_label.setText(self.template_path.__str__())
class InfoWidget(QWidget): class InfoWidget(QWidget):
def __init__(self, parent: QWidget, key) -> None: def __init__(self, parent: QWidget, key) -> None:

View File

@@ -4,7 +4,6 @@ Contains miscellaenous functions used by both frontend and backend.
from __future__ import annotations from __future__ import annotations
import json import json
from pathlib import Path
import numpy as np import numpy as np
import logging, re, yaml, sys, os, stat, platform, getpass, inspect, csv import logging, re, yaml, sys, os, stat, platform, getpass, inspect, csv
import pandas as pd import pandas as pd
@@ -16,7 +15,7 @@ from sqlalchemy import create_engine, text
from pydantic import field_validator, BaseModel, Field from pydantic import field_validator, BaseModel, Field
from pydantic_settings import BaseSettings, SettingsConfigDict from pydantic_settings import BaseSettings, SettingsConfigDict
from typing import Any, Tuple, Literal, List from typing import Any, Tuple, Literal, List
from PyQt6.QtGui import QTextDocument, QPageSize from PyQt6.QtGui import QPageSize
from PyQt6.QtWebEngineWidgets import QWebEngineView from PyQt6.QtWebEngineWidgets import QWebEngineView
from openpyxl.worksheet.worksheet import Worksheet from openpyxl.worksheet.worksheet import Worksheet