Added report tab with HTML and excel export.

This commit is contained in:
lwark
2024-10-04 15:24:00 -05:00
parent c89ec2b62c
commit 5fe5c22222
5 changed files with 106 additions and 88 deletions

View File

@@ -12,11 +12,11 @@ from pathlib import Path
from markdown import markdown
from __init__ import project_path
from tools import check_if_app, Settings, Report, jinja_template_loading, check_authorization
from tools import check_if_app, Settings, Report, jinja_template_loading, check_authorization, page_size
from .functions import select_save_file,select_open_file
from datetime import date
from .pop_ups import HTMLPop, AlertPop
from .misc import LogParser
from .misc import LogParser, Pagifier
import logging, webbrowser, sys, shutil
from .submission_table import SubmissionsSheet
from .submission_widget import SubmissionFormContainer
@@ -49,6 +49,7 @@ class App(QMainWindow):
self.height = 1000
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.page_size = page_size
# NOTE: insert tabs into main app
self.table_widget = AddSubForm(self)
self.setCentralWidget(self.table_widget)
@@ -133,6 +134,7 @@ class App(QMainWindow):
self.githubAction.triggered.connect(self.openGithub)
self.yamlExportAction.triggered.connect(self.export_ST_yaml)
self.yamlImportAction.triggered.connect(self.import_ST_yaml)
self.table_widget.pager.current_page.textChanged.connect(self.update_data)
def showAbout(self):
"""
@@ -237,6 +239,8 @@ class App(QMainWindow):
else:
logger.warning("Save of submission type cancelled.")
def update_data(self):
self.table_widget.sub_wid.setData(page=int(self.table_widget.pager.current_page.text()), page_size=page_size)
class AddSubForm(QWidget):
@@ -272,7 +276,9 @@ class AddSubForm(QWidget):
self.sheetlayout = QVBoxLayout(self)
self.sheetwidget.setLayout(self.sheetlayout)
self.sub_wid = SubmissionsSheet(parent=parent)
self.pager = Pagifier(page_max=self.sub_wid.total_count/page_size)
self.sheetlayout.addWidget(self.sub_wid)
self.sheetlayout.addWidget(self.pager)
# NOTE: Create layout of first tab to hold form and sheet
self.tab1.layout = QHBoxLayout(self)
self.tab1.setLayout(self.tab1.layout)

View File

@@ -1,21 +1,22 @@
'''
Contains miscellaneous widgets for frontend functions
'''
import math
from datetime import date
from PyQt6.QtGui import QPageLayout, QPageSize, QStandardItem, QStandardItemModel
from PyQt6.QtGui import QPageLayout, QPageSize, QStandardItem, QIcon
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWidgets import (
QLabel, QVBoxLayout,
QLineEdit, QComboBox, QDialog,
QDialogButtonBox, QDateEdit, QPushButton, QFormLayout, QWidget, QHBoxLayout, QSizePolicy
)
from PyQt6.QtCore import Qt, QDate, QSize, QMarginsF, pyqtSlot, pyqtSignal, QEvent
from PyQt6.QtCore import Qt, QDate, QSize, QMarginsF
from tools import jinja_template_loading
from backend.db.models import *
import logging
from .pop_ups import AlertPop
from .functions import select_open_file, select_save_file
from .functions import select_open_file
logger = logging.getLogger(f"submissions.{__name__}")
@@ -245,3 +246,42 @@ class CheckableComboBox(QComboBox):
def changed(self):
logger.debug("emitting updated")
self.updated.emit()
class Pagifier(QWidget):
def __init__(self, page_max:int):
super().__init__()
self.page_max = math.ceil(page_max)
next = QPushButton(parent=self, icon = QIcon.fromTheme(QIcon.ThemeIcon.GoNext))
next.pressed.connect(self.increment_page)
previous = QPushButton(parent=self, icon=QIcon.fromTheme(QIcon.ThemeIcon.GoPrevious))
previous.pressed.connect(self.decrement_page)
label = QLabel(f"/ {self.page_max}")
label.setMinimumWidth(200)
label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.current_page = QLineEdit(self)
self.current_page.setEnabled(False)
# onlyInt = QIntValidator()
# onlyInt.setRange(1, 4)
# self.current_page.setValidator(onlyInt)
self.current_page.setText("1")
self.current_page.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.layout = QHBoxLayout()
self.layout.addWidget(previous)
self.layout.addWidget(self.current_page)
self.layout.addWidget(label)
self.layout.addWidget(next)
self.setLayout(self.layout)
def increment_page(self):
new = int(self.current_page.text())+1
if new <= self.page_max:
self.current_page.setText(str(new))
def decrement_page(self):
new = int(self.current_page.text())-1
if new >= 1:
self.current_page.setText(str(new))

View File

@@ -7,9 +7,9 @@ from PyQt6.QtWidgets import QTableView, QMenu
from PyQt6.QtCore import Qt, QAbstractTableModel, QSortFilterProxyModel
from PyQt6.QtGui import QAction, QCursor
from backend.db.models import BasicSubmission
from backend.excel import ReportMaker
from tools import Report, Result, report_result
from .functions import select_save_file, select_open_file
from .functions import select_open_file
# from .misc import ReportDatePicker
logger = logging.getLogger(f"submissions.{__name__}")
@@ -20,6 +20,7 @@ class pandasModel(QAbstractTableModel):
pandas model for inserting summary sheet into gui
NOTE: Copied from Stack Overflow. I have no idea how it actually works.
"""
def __init__(self, data) -> None:
QAbstractTableModel.__init__(self)
self._data = data
@@ -45,10 +46,10 @@ class pandasModel(QAbstractTableModel):
Returns:
int: number of columns in data
"""
"""
return self._data.shape[1]
def data(self, index, role=Qt.ItemDataRole.DisplayRole) -> str|None:
def data(self, index, role=Qt.ItemDataRole.DisplayRole) -> str | None:
if index.isValid():
if role == Qt.ItemDataRole.DisplayRole:
return str(self._data.iloc[index.row(), index.column()])
@@ -63,28 +64,30 @@ class pandasModel(QAbstractTableModel):
class SubmissionsSheet(QTableView):
"""
presents submission summary to user in tab1
"""
"""
def __init__(self, parent) -> None:
"""
initialize
Args:
ctx (dict): settings passed from gui
"""
"""
super().__init__(parent)
self.app = self.parent()
self.report = Report()
self.setData()
self.setData(page=1, page_size=self.app.page_size)
self.resizeColumnsToContents()
self.resizeRowsToContents()
self.setSortingEnabled(True)
self.doubleClicked.connect(lambda x: BasicSubmission.query(id=x.sibling(x.row(), 0).data()).show_details(self))
def setData(self) -> None:
self.total_count = BasicSubmission.__database_session__.query(BasicSubmission).count()
def setData(self, page: int = 1, page_size: int = 250) -> None:
"""
sets data in model
"""
self.data = BasicSubmission.submissions_to_df()
"""
self.data = BasicSubmission.submissions_to_df(page=page)
try:
self.data['Id'] = self.data['Id'].apply(str)
self.data['Id'] = self.data['Id'].str.zfill(4)
@@ -93,17 +96,17 @@ class SubmissionsSheet(QTableView):
proxyModel = QSortFilterProxyModel()
proxyModel.setSourceModel(pandasModel(self.data))
self.setModel(proxyModel)
def contextMenuEvent(self, event):
"""
Creates actions for right click menu events.
Args:
event (_type_): the item of interest
"""
"""
# logger.debug(event().__dict__)
id = self.selectionModel().currentIndex()
id = id.sibling(id.row(),0).data()
id = id.sibling(id.row(), 0).data()
submission = BasicSubmission.query(id=id)
self.menu = QMenu(self)
self.con_actions = submission.custom_context_events()
@@ -115,13 +118,13 @@ class SubmissionsSheet(QTableView):
# add other required actions
self.menu.popup(QCursor.pos())
def triggered_action(self, action_name:str):
def triggered_action(self, action_name: str):
"""
Calls the triggered action from the context menu
Args:
action_name (str): name of the action from the menu
"""
"""
# logger.debug(f"Action: {action_name}")
# logger.debug(f"Responding with {self.con_actions[action_name]}")
func = self.con_actions[action_name]
@@ -155,16 +158,16 @@ class SubmissionsSheet(QTableView):
count = 0
for run in runs:
new_run = dict(
start_time=run[0].strip(),
rsl_plate_num=run[1].strip(),
sample_count=run[2].strip(),
status=run[3].strip(),
experiment_name=run[4].strip(),
end_time=run[5].strip()
)
start_time=run[0].strip(),
rsl_plate_num=run[1].strip(),
sample_count=run[2].strip(),
status=run[3].strip(),
experiment_name=run[4].strip(),
end_time=run[5].strip()
)
# NOTE: elution columns are item 6 in the comma split list to the end
for ii in range(6, len(run)):
new_run[f"column{str(ii-5)}_vol"] = run[ii]
new_run[f"column{str(ii - 5)}_vol"] = run[ii]
# NOTE: Lookup imported submissions
sub = BasicSubmission.query(rsl_plate_num=new_run['rsl_plate_num'])
# NOTE: If no such submission exists, move onto the next run
@@ -208,13 +211,13 @@ class SubmissionsSheet(QTableView):
count = 0
for run in runs:
new_run = dict(
start_time=run[0].strip(),
rsl_plate_num=run[1].strip(),
biomek_status=run[2].strip(),
quant_status=run[3].strip(),
experiment_name=run[4].strip(),
end_time=run[5].strip()
)
start_time=run[0].strip(),
rsl_plate_num=run[1].strip(),
biomek_status=run[2].strip(),
quant_status=run[3].strip(),
experiment_name=run[4].strip(),
end_time=run[5].strip()
)
# NOTE: lookup imported submission
sub = BasicSubmission.query(rsl_number=new_run['rsl_plate_num'])
# NOTE: if imported submission doesn't exist move on to next run
@@ -225,33 +228,3 @@ class SubmissionsSheet(QTableView):
sub.save()
report.add_result(Result(msg=f"We added {count} logs to the database.", status='Information'))
return report
# @report_result
# def generate_report(self, *args):
# """
# Make a report
# """
# report = Report()
# result = self.generate_report_function()
# report.add_result(result)
# return report
#
# def generate_report_function(self):
# """
# Generate a summary of activities for a time period
#
# Args:
# obj (QMainWindow): original app window
#
# Returns:
# Tuple[QMainWindow, dict]: Collection of new main app window and result dict
# """
# report = Report()
# # NOTE: ask for date ranges
# dlg = ReportDatePicker()
# if dlg.exec():
# info = dlg.parse_form()
# fname = select_save_file(obj=self, default_name=f"Submissions_Report_{info['start_date']}-{info['end_date']}.xlsx", extension="xlsx")
# rp = ReportMaker(start_date=info['start_date'], end_date=info['end_date'])
# rp.write_report(filename=fname, obj=self)
# return report