Added report tab with HTML and excel export.
This commit is contained in:
@@ -24,6 +24,7 @@ from .controls_chart import ControlsViewer
|
||||
from .kit_creator import KitAdder
|
||||
from .submission_type_creator import SubmissionTypeAdder, SubmissionType
|
||||
from .sample_search import SearchBox
|
||||
from .summary import Summary
|
||||
|
||||
logger = logging.getLogger(f'submissions.{__name__}')
|
||||
logger.info("Hello, I am a logger")
|
||||
@@ -69,7 +70,7 @@ class App(QMainWindow):
|
||||
fileMenu = menuBar.addMenu("&File")
|
||||
# NOTE: Creating menus using a title
|
||||
methodsMenu = menuBar.addMenu("&Methods")
|
||||
reportMenu = menuBar.addMenu("&Reports")
|
||||
# reportMenu = menuBar.addMenu("&Reports")
|
||||
maintenanceMenu = menuBar.addMenu("&Monthly")
|
||||
helpMenu = menuBar.addMenu("&Help")
|
||||
helpMenu.addAction(self.helpAction)
|
||||
@@ -80,7 +81,7 @@ class App(QMainWindow):
|
||||
fileMenu.addAction(self.yamlImportAction)
|
||||
methodsMenu.addAction(self.searchLog)
|
||||
methodsMenu.addAction(self.searchSample)
|
||||
reportMenu.addAction(self.generateReportAction)
|
||||
# reportMenu.addAction(self.generateReportAction)
|
||||
maintenanceMenu.addAction(self.joinExtractionAction)
|
||||
maintenanceMenu.addAction(self.joinPCRAction)
|
||||
|
||||
@@ -102,7 +103,7 @@ class App(QMainWindow):
|
||||
# logger.debug(f"Creating actions...")
|
||||
self.importAction = QAction("&Import Submission", 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.addOrgAction = QAction("Import Org", self)
|
||||
self.joinExtractionAction = QAction("Link Extraction Logs")
|
||||
@@ -122,7 +123,7 @@ class App(QMainWindow):
|
||||
# logger.debug(f"Connecting actions...")
|
||||
self.importAction.triggered.connect(self.table_widget.formwidget.importSubmission)
|
||||
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.joinPCRAction.triggered.connect(self.table_widget.sub_wid.link_pcr)
|
||||
self.helpAction.triggered.connect(self.showAbout)
|
||||
@@ -254,8 +255,8 @@ class AddSubForm(QWidget):
|
||||
# NOTE: Add tabs
|
||||
self.tabs.addTab(self.tab1,"Submissions")
|
||||
self.tabs.addTab(self.tab2,"Controls")
|
||||
self.tabs.addTab(self.tab3, "Add SubmissionType")
|
||||
self.tabs.addTab(self.tab4, "Add Kit")
|
||||
self.tabs.addTab(self.tab3, "Summary Report")
|
||||
# self.tabs.addTab(self.tab4, "Add Kit")
|
||||
# NOTE: Create submission adder form
|
||||
self.formwidget = SubmissionFormContainer(self)
|
||||
self.formlayout = QVBoxLayout(self)
|
||||
@@ -282,14 +283,15 @@ class AddSubForm(QWidget):
|
||||
self.tab2.layout.addWidget(self.controls_viewer)
|
||||
self.tab2.setLayout(self.tab2.layout)
|
||||
# NOTE: create custom widget to add new tabs
|
||||
ST_adder = SubmissionTypeAdder(self)
|
||||
# ST_adder = SubmissionTypeAdder(self)
|
||||
summary_report = Summary(self)
|
||||
self.tab3.layout = QVBoxLayout(self)
|
||||
self.tab3.layout.addWidget(ST_adder)
|
||||
self.tab3.layout.addWidget(summary_report)
|
||||
self.tab3.setLayout(self.tab3.layout)
|
||||
kit_adder = KitAdder(self)
|
||||
self.tab4.layout = QVBoxLayout(self)
|
||||
self.tab4.layout.addWidget(kit_adder)
|
||||
self.tab4.setLayout(self.tab4.layout)
|
||||
# kit_adder = KitAdder(self)
|
||||
# self.tab4.layout = QVBoxLayout(self)
|
||||
# self.tab4.layout.addWidget(kit_adder)
|
||||
# self.tab4.setLayout(self.tab4.layout)
|
||||
# NOTE: add tabs to main widget
|
||||
self.layout.addWidget(self.tabs)
|
||||
self.setLayout(self.layout)
|
||||
|
||||
@@ -8,7 +8,7 @@ from typing import Tuple
|
||||
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
||||
from PyQt6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QComboBox, QHBoxLayout,
|
||||
QDateEdit, QLabel, QSizePolicy, QPushButton
|
||||
QDateEdit, QLabel, QSizePolicy, QPushButton, QGridLayout
|
||||
)
|
||||
from PyQt6.QtCore import QSignalBlocker
|
||||
from backend.db import ControlType, Control
|
||||
@@ -17,7 +17,7 @@ import logging
|
||||
from pandas import DataFrame
|
||||
from tools import Report, Result, get_unique_values_in_df_column, Settings, report_result
|
||||
from frontend.visualizations.control_charts import CustomFigure
|
||||
|
||||
from .misc import StartEndDatePicker
|
||||
logger = logging.getLogger(f"submissions.{__name__}")
|
||||
|
||||
|
||||
@@ -28,10 +28,10 @@ class ControlsViewer(QWidget):
|
||||
self.app = self.parent().parent()
|
||||
# logger.debug(f"\n\n{self.app}\n\n")
|
||||
self.report = Report()
|
||||
self.datepicker = ControlsDatePicker()
|
||||
self.datepicker = StartEndDatePicker(default_start=-180)
|
||||
self.webengineview = QWebEngineView()
|
||||
# NOTE: set tab2 layout
|
||||
self.layout = QVBoxLayout(self)
|
||||
self.layout = QGridLayout(self)
|
||||
self.control_typer = QComboBox()
|
||||
# NOTE: fetch types of controls
|
||||
con_types = [item.name for item in ControlType.query()]
|
||||
@@ -44,18 +44,20 @@ class ControlsViewer(QWidget):
|
||||
self.sub_typer = QComboBox()
|
||||
self.sub_typer.setEnabled(False)
|
||||
# NOTE: add widgets to tab2 layout
|
||||
self.layout.addWidget(self.datepicker)
|
||||
self.layout.addWidget(self.control_typer)
|
||||
self.layout.addWidget(self.mode_typer)
|
||||
self.layout.addWidget(self.sub_typer)
|
||||
self.layout.addWidget(self.webengineview)
|
||||
self.layout.addWidget(self.datepicker, 0,0,1,2)
|
||||
self.save_button = QPushButton("Save Chart", parent=self)
|
||||
self.layout.addWidget(self.save_button, 0,2,1,1)
|
||||
self.layout.addWidget(self.control_typer, 1,0,1,3)
|
||||
self.layout.addWidget(self.mode_typer, 2,0,1,3)
|
||||
self.layout.addWidget(self.sub_typer, 3,0,1,3)
|
||||
self.layout.addWidget(self.webengineview, 4,0,1,3)
|
||||
self.setLayout(self.layout)
|
||||
self.controls_getter()
|
||||
self.control_typer.currentIndexChanged.connect(self.controls_getter)
|
||||
self.mode_typer.currentIndexChanged.connect(self.controls_getter)
|
||||
self.datepicker.start_date.dateChanged.connect(self.controls_getter)
|
||||
self.datepicker.end_date.dateChanged.connect(self.controls_getter)
|
||||
self.datepicker.save_button.pressed.connect(self.save_chart_function)
|
||||
self.save_button.pressed.connect(self.save_chart_function)
|
||||
|
||||
def save_chart_function(self):
|
||||
self.fig.save_figure(parent=self)
|
||||
@@ -141,7 +143,7 @@ class ControlsViewer(QWidget):
|
||||
# NOTE: if no data found from query set fig to none for reporting in webview
|
||||
if controls is None:
|
||||
fig = None
|
||||
self.datepicker.save_button.setEnabled(False)
|
||||
self.save_button.setEnabled(False)
|
||||
else:
|
||||
# NOTE: change each control to list of dictionaries
|
||||
data = [control.convert_by_mode(mode=self.mode) for control in controls]
|
||||
@@ -160,7 +162,7 @@ class ControlsViewer(QWidget):
|
||||
# NOTE: send dataframe to chart maker
|
||||
df, modes = self.prep_df(ctx=self.app.ctx, df=df)
|
||||
fig = CustomFigure(df=df, ytitle=title, modes=modes, parent=self)
|
||||
self.datepicker.save_button.setEnabled(True)
|
||||
self.save_button.setEnabled(True)
|
||||
# logger.debug(f"Updating figure...")
|
||||
self.fig = fig
|
||||
# NOTE: construct html for webview
|
||||
@@ -200,8 +202,6 @@ class ControlsViewer(QWidget):
|
||||
continue
|
||||
# NOTE: The actual percentage from kraken was off due to exclusion of NaN, recalculating.
|
||||
df[column] = 100 * df[count_col] / df.groupby('name')[count_col].transform('sum')
|
||||
logger.debug(df)
|
||||
logger.debug(safe)
|
||||
df = df[[c for c in df.columns if c in safe]]
|
||||
# NOTE: move date of sample submitted on same date as previous ahead one.
|
||||
df = self.displace_date(df=df)
|
||||
@@ -340,28 +340,27 @@ class ControlsViewer(QWidget):
|
||||
return df
|
||||
|
||||
|
||||
class ControlsDatePicker(QWidget):
|
||||
"""
|
||||
custom widget to pick start and end dates for controls graphs
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.start_date = QDateEdit(calendarPopup=True)
|
||||
# NOTE: start date is two months prior to end date by default
|
||||
sixmonthsago = QDate.currentDate().addDays(-180)
|
||||
self.start_date.setDate(sixmonthsago)
|
||||
self.end_date = QDateEdit(calendarPopup=True)
|
||||
self.end_date.setDate(QDate.currentDate())
|
||||
self.layout = QHBoxLayout()
|
||||
self.layout.addWidget(QLabel("Start Date"))
|
||||
self.layout.addWidget(self.start_date)
|
||||
self.layout.addWidget(QLabel("End Date"))
|
||||
self.layout.addWidget(self.end_date)
|
||||
self.setLayout(self.layout)
|
||||
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||
self.save_button = QPushButton("Save Chart", parent=self)
|
||||
self.layout.addWidget(self.save_button)
|
||||
|
||||
def sizeHint(self) -> QSize:
|
||||
return QSize(80, 20)
|
||||
# class ControlsDatePicker(QWidget):
|
||||
# """
|
||||
# custom widget to pick start and end dates for controls graphs
|
||||
# """
|
||||
#
|
||||
# def __init__(self) -> None:
|
||||
# super().__init__()
|
||||
# self.start_date = QDateEdit(calendarPopup=True)
|
||||
# # NOTE: start date is two months prior to end date by default
|
||||
# sixmonthsago = QDate.currentDate().addDays(-180)
|
||||
# self.start_date.setDate(sixmonthsago)
|
||||
# self.end_date = QDateEdit(calendarPopup=True)
|
||||
# self.end_date.setDate(QDate.currentDate())
|
||||
# self.layout = QHBoxLayout()
|
||||
# self.layout.addWidget(QLabel("Start Date"))
|
||||
# self.layout.addWidget(self.start_date)
|
||||
# self.layout.addWidget(QLabel("End Date"))
|
||||
# self.layout.addWidget(self.end_date)
|
||||
# self.setLayout(self.layout)
|
||||
# self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||
#
|
||||
#
|
||||
# def sizeHint(self) -> QSize:
|
||||
# return QSize(80, 20)
|
||||
|
||||
@@ -2,17 +2,20 @@
|
||||
Contains miscellaneous widgets for frontend functions
|
||||
'''
|
||||
from datetime import date
|
||||
|
||||
from PyQt6.QtGui import QPageLayout, QPageSize, QStandardItem, QStandardItemModel
|
||||
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
||||
from PyQt6.QtWidgets import (
|
||||
QLabel, QVBoxLayout,
|
||||
QLineEdit, QComboBox, QDialog,
|
||||
QDialogButtonBox, QDateEdit, QPushButton, QFormLayout
|
||||
QLineEdit, QComboBox, QDialog,
|
||||
QDialogButtonBox, QDateEdit, QPushButton, QFormLayout, QWidget, QHBoxLayout, QSizePolicy
|
||||
)
|
||||
from PyQt6.QtCore import Qt, QDate
|
||||
from PyQt6.QtCore import Qt, QDate, QSize, QMarginsF, pyqtSlot, pyqtSignal, QEvent
|
||||
from tools import jinja_template_loading
|
||||
from backend.db.models import *
|
||||
import logging
|
||||
from .pop_ups import AlertPop
|
||||
from .functions import select_open_file
|
||||
from .functions import select_open_file, select_save_file
|
||||
|
||||
logger = logging.getLogger(f"submissions.{__name__}")
|
||||
|
||||
@@ -22,8 +25,10 @@ env = jinja_template_loading()
|
||||
class AddReagentForm(QDialog):
|
||||
"""
|
||||
dialog to add gather info about new reagent
|
||||
"""
|
||||
def __init__(self, reagent_lot:str|None=None, reagent_role: str | None=None, expiry: date | None=None, reagent_name: str | None=None) -> None:
|
||||
"""
|
||||
|
||||
def __init__(self, reagent_lot: str | None = None, reagent_role: str | None = None, expiry: date | None = None,
|
||||
reagent_name: str | None = None) -> None:
|
||||
super().__init__()
|
||||
if reagent_lot is None:
|
||||
reagent_lot = reagent_role
|
||||
@@ -71,7 +76,8 @@ class AddReagentForm(QDialog):
|
||||
self.layout.addWidget(self.name_input)
|
||||
self.layout.addWidget(QLabel("Lot:"))
|
||||
self.layout.addWidget(self.lot_input)
|
||||
self.layout.addWidget(QLabel("Expiry:\n(use exact date on reagent.\nEOL will be calculated from kit automatically)"))
|
||||
self.layout.addWidget(
|
||||
QLabel("Expiry:\n(use exact date on reagent.\nEOL will be calculated from kit automatically)"))
|
||||
self.layout.addWidget(self.exp_input)
|
||||
self.layout.addWidget(QLabel("Type:"))
|
||||
self.layout.addWidget(self.type_input)
|
||||
@@ -85,57 +91,57 @@ class AddReagentForm(QDialog):
|
||||
|
||||
Returns:
|
||||
dict: Output info
|
||||
"""
|
||||
return dict(name=self.name_input.currentText().strip(),
|
||||
lot=self.lot_input.text().strip(),
|
||||
"""
|
||||
return dict(name=self.name_input.currentText().strip(),
|
||||
lot=self.lot_input.text().strip(),
|
||||
expiry=self.exp_input.date().toPyDate(),
|
||||
role=self.type_input.currentText().strip())
|
||||
|
||||
def update_names(self):
|
||||
"""
|
||||
Updates reagent names form field with examples from reagent type
|
||||
"""
|
||||
"""
|
||||
# logger.debug(self.type_input.currentText())
|
||||
self.name_input.clear()
|
||||
lookup = Reagent.query(reagent_role=self.type_input.currentText())
|
||||
self.name_input.addItems(list(set([item.name for item in lookup])))
|
||||
|
||||
|
||||
class ReportDatePicker(QDialog):
|
||||
"""
|
||||
custom dialog to ask for report start/stop dates
|
||||
"""
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.setWindowTitle("Select Report Date Range")
|
||||
# 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)
|
||||
# NOTE: widgets to ask for dates
|
||||
self.start_date = QDateEdit(calendarPopup=True)
|
||||
self.start_date.setObjectName("start_date")
|
||||
self.start_date.setDate(QDate.currentDate())
|
||||
self.end_date = QDateEdit(calendarPopup=True)
|
||||
self.end_date.setObjectName("end_date")
|
||||
self.end_date.setDate(QDate.currentDate())
|
||||
self.layout = QVBoxLayout()
|
||||
self.layout.addWidget(QLabel("Start Date"))
|
||||
self.layout.addWidget(self.start_date)
|
||||
self.layout.addWidget(QLabel("End Date"))
|
||||
self.layout.addWidget(self.end_date)
|
||||
self.layout.addWidget(self.buttonBox)
|
||||
self.setLayout(self.layout)
|
||||
|
||||
def parse_form(self) -> dict:
|
||||
"""
|
||||
Converts information in this object to a dict
|
||||
|
||||
Returns:
|
||||
dict: output dict.
|
||||
"""
|
||||
return dict(start_date=self.start_date.date().toPyDate(), end_date = self.end_date.date().toPyDate())
|
||||
# class ReportDatePicker(QDialog):
|
||||
# """
|
||||
# custom dialog to ask for report start/stop dates
|
||||
# """
|
||||
# def __init__(self) -> None:
|
||||
# super().__init__()
|
||||
# self.setWindowTitle("Select Report Date Range")
|
||||
# # 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)
|
||||
# # NOTE: widgets to ask for dates
|
||||
# self.start_date = QDateEdit(calendarPopup=True)
|
||||
# self.start_date.setObjectName("start_date")
|
||||
# self.start_date.setDate(QDate.currentDate())
|
||||
# self.end_date = QDateEdit(calendarPopup=True)
|
||||
# self.end_date.setObjectName("end_date")
|
||||
# self.end_date.setDate(QDate.currentDate())
|
||||
# self.layout = QVBoxLayout()
|
||||
# self.layout.addWidget(QLabel("Start Date"))
|
||||
# self.layout.addWidget(self.start_date)
|
||||
# self.layout.addWidget(QLabel("End Date"))
|
||||
# self.layout.addWidget(self.end_date)
|
||||
# self.layout.addWidget(self.buttonBox)
|
||||
# self.setLayout(self.layout)
|
||||
#
|
||||
# def parse_form(self) -> dict:
|
||||
# """
|
||||
# Converts information in this object to a dict
|
||||
#
|
||||
# Returns:
|
||||
# dict: output dict.
|
||||
# """
|
||||
# return dict(start_date=self.start_date.date().toPyDate(), end_date = self.end_date.date().toPyDate())
|
||||
|
||||
|
||||
class LogParser(QDialog):
|
||||
@@ -160,13 +166,13 @@ class LogParser(QDialog):
|
||||
def filelookup(self):
|
||||
"""
|
||||
Select file to search
|
||||
"""
|
||||
"""
|
||||
self.fname = select_open_file(self, "tabular")
|
||||
|
||||
def runsearch(self):
|
||||
"""
|
||||
Gets total/percent occurences of string in tabular file.
|
||||
"""
|
||||
"""
|
||||
count: int = 0
|
||||
total: int = 0
|
||||
# logger.debug(f"Current search term: {self.phrase_looker.currentText()}")
|
||||
@@ -177,7 +183,7 @@ class LogParser(QDialog):
|
||||
for line in chunk:
|
||||
if self.phrase_looker.currentText().lower() in line.lower():
|
||||
count += 1
|
||||
percent = (count/total)*100
|
||||
percent = (count / total) * 100
|
||||
msg = f"I found {count} instances of the search phrase out of {total} = {percent:.2f}%."
|
||||
status = "Information"
|
||||
except AttributeError:
|
||||
@@ -186,3 +192,56 @@ class LogParser(QDialog):
|
||||
dlg = AlertPop(message=msg, status=status)
|
||||
dlg.exec()
|
||||
|
||||
|
||||
class StartEndDatePicker(QWidget):
|
||||
"""
|
||||
custom widget to pick start and end dates for controls graphs
|
||||
"""
|
||||
|
||||
def __init__(self, default_start: int) -> None:
|
||||
super().__init__()
|
||||
self.start_date = QDateEdit(calendarPopup=True)
|
||||
# NOTE: start date is two months prior to end date by default
|
||||
default_start = QDate.currentDate().addDays(default_start)
|
||||
self.start_date.setDate(default_start)
|
||||
self.end_date = QDateEdit(calendarPopup=True)
|
||||
self.end_date.setDate(QDate.currentDate())
|
||||
self.layout = QHBoxLayout()
|
||||
self.layout.addWidget(QLabel("Start Date"))
|
||||
self.layout.addWidget(self.start_date)
|
||||
self.layout.addWidget(QLabel("End Date"))
|
||||
self.layout.addWidget(self.end_date)
|
||||
self.setLayout(self.layout)
|
||||
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||
|
||||
def sizeHint(self) -> QSize:
|
||||
return QSize(80, 20)
|
||||
|
||||
|
||||
def save_pdf(obj: QWebEngineView, filename: Path):
|
||||
page_layout = QPageLayout()
|
||||
page_layout.setPageSize(QPageSize(QPageSize.PageSizeId.A4))
|
||||
page_layout.setOrientation(QPageLayout.Orientation.Portrait)
|
||||
page_layout.setMargins(QMarginsF(25, 25, 25, 25))
|
||||
obj.page().printToPdf(filename.absolute().__str__(), page_layout)
|
||||
|
||||
|
||||
# subclass
|
||||
class CheckableComboBox(QComboBox):
|
||||
# once there is a checkState set, it is rendered
|
||||
# here we assume default Unchecked
|
||||
|
||||
def addItem(self, item, header: bool = False):
|
||||
super(CheckableComboBox, self).addItem(item)
|
||||
item: QStandardItem = self.model().item(self.count() - 1, 0)
|
||||
if not header:
|
||||
item.setFlags(Qt.ItemFlag.ItemIsUserCheckable | Qt.ItemFlag.ItemIsEnabled)
|
||||
item.setCheckState(Qt.CheckState.Checked)
|
||||
|
||||
def itemChecked(self, index):
|
||||
item = self.model().item(index, 0)
|
||||
return item.checkState() == Qt.CheckState.Checked
|
||||
|
||||
def changed(self):
|
||||
logger.debug("emitting updated")
|
||||
self.updated.emit()
|
||||
|
||||
@@ -10,8 +10,9 @@ from PyQt6.QtWebChannel import QWebChannel
|
||||
from PyQt6.QtCore import Qt, pyqtSlot, QMarginsF
|
||||
from jinja2 import TemplateNotFound
|
||||
from backend.db.models import BasicSubmission, BasicSample, Reagent, KitType
|
||||
from tools import is_power_user, html_to_pdf, jinja_template_loading
|
||||
from tools import is_power_user, jinja_template_loading
|
||||
from .functions import select_save_file
|
||||
from .misc import save_pdf
|
||||
from pathlib import Path
|
||||
import logging
|
||||
from getpass import getuser
|
||||
@@ -177,11 +178,12 @@ class SubmissionDetails(QDialog):
|
||||
Renders submission to html, then creates and saves .pdf file to user selected file.
|
||||
"""
|
||||
fname = select_save_file(obj=self, default_name=self.export_plate, extension="pdf")
|
||||
page_layout = QPageLayout()
|
||||
page_layout.setPageSize(QPageSize(QPageSize.PageSizeId.A4))
|
||||
page_layout.setOrientation(QPageLayout.Orientation.Portrait)
|
||||
page_layout.setMargins(QMarginsF(25, 25, 25, 25))
|
||||
self.webview.page().printToPdf(fname.with_suffix(".pdf").__str__(), page_layout)
|
||||
# page_layout = QPageLayout()
|
||||
# page_layout.setPageSize(QPageSize(QPageSize.PageSizeId.A4))
|
||||
# page_layout.setOrientation(QPageLayout.Orientation.Portrait)
|
||||
# page_layout.setMargins(QMarginsF(25, 25, 25, 25))
|
||||
# self.webview.page().printToPdf(fname.with_suffix(".pdf").__str__(), page_layout)
|
||||
save_pdf(obj=self, filename=fname)
|
||||
|
||||
class SubmissionComment(QDialog):
|
||||
"""
|
||||
|
||||
@@ -10,7 +10,7 @@ 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 .misc import ReportDatePicker
|
||||
# from .misc import ReportDatePicker
|
||||
|
||||
logger = logging.getLogger(f"submissions.{__name__}")
|
||||
|
||||
@@ -226,32 +226,32 @@ class SubmissionsSheet(QTableView):
|
||||
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
|
||||
# @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
|
||||
|
||||
78
src/submissions/frontend/widgets/summary.py
Normal file
78
src/submissions/frontend/widgets/summary.py
Normal file
@@ -0,0 +1,78 @@
|
||||
from PyQt6.QtCore import QSignalBlocker
|
||||
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
||||
from PyQt6.QtWidgets import QWidget, QGridLayout, QPushButton, QComboBox, QLabel
|
||||
from backend.db import Organization
|
||||
from backend.excel import ReportMaker
|
||||
from tools import Report
|
||||
from .misc import StartEndDatePicker, save_pdf, CheckableComboBox
|
||||
from .functions import select_save_file
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(f"submissions.{__name__}")
|
||||
|
||||
|
||||
class Summary(QWidget):
|
||||
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent)
|
||||
self.app = self.parent().parent()
|
||||
# logger.debug(f"\n\n{self.app}\n\n")
|
||||
self.report = Report()
|
||||
self.datepicker = StartEndDatePicker(default_start=-31)
|
||||
self.webview = QWebEngineView()
|
||||
self.datepicker.start_date.dateChanged.connect(self.get_report)
|
||||
self.datepicker.end_date.dateChanged.connect(self.get_report)
|
||||
self.layout = QGridLayout(self)
|
||||
self.layout.addWidget(self.datepicker, 0, 0, 1, 2)
|
||||
self.save_excel_button = QPushButton("Save Excel", parent=self)
|
||||
self.save_excel_button.pressed.connect(self.save_excel)
|
||||
self.save_pdf_button = QPushButton("Save PDF", parent=self)
|
||||
self.save_pdf_button.pressed.connect(self.save_pdf)
|
||||
self.org_select = CheckableComboBox()
|
||||
self.org_select.setEditable(False)
|
||||
self.org_select.addItem("Select", header=True)
|
||||
for org in [org.name for org in Organization.query()]:
|
||||
self.org_select.addItem(org)
|
||||
self.org_select.model().itemChanged.connect(self.get_report)
|
||||
# self.org_select.itemChecked.connect(self.get_report)
|
||||
self.layout.addWidget(self.save_excel_button, 0, 2, 1, 1)
|
||||
self.layout.addWidget(self.save_pdf_button, 0, 3, 1, 1)
|
||||
self.layout.addWidget(self.webview, 2, 0, 1, 4)
|
||||
self.layout.addWidget(QLabel("Client"), 1, 0, 1, 1)
|
||||
self.layout.addWidget(self.org_select, 1, 1, 1, 3)
|
||||
self.setLayout(self.layout)
|
||||
self.get_report()
|
||||
|
||||
|
||||
def get_report(self):
|
||||
orgs = [self.org_select.itemText(i) for i in range(self.org_select.count()) if self.org_select.itemChecked(i)]
|
||||
if self.datepicker.start_date.date() > self.datepicker.end_date.date():
|
||||
logger.warning("Start date after end date is not allowed!")
|
||||
lastmonth = self.datepicker.end_date.date().addDays(-31)
|
||||
# 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(lastmonth)
|
||||
self.get_report()
|
||||
return
|
||||
# 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.report_obj = ReportMaker(start_date=self.start_date, end_date=self.end_date, organizations=orgs)
|
||||
self.webview.setHtml(self.report_obj.html)
|
||||
if self.report_obj.subs:
|
||||
self.save_pdf_button.setEnabled(True)
|
||||
self.save_excel_button.setEnabled(True)
|
||||
else:
|
||||
self.save_pdf_button.setEnabled(False)
|
||||
self.save_excel_button.setEnabled(False)
|
||||
|
||||
def save_excel(self):
|
||||
fname = select_save_file(self, default_name=f"Report {self.start_date.strftime('%Y%m%d')} - {self.end_date.strftime('%Y%m%d')}", extension="xlsx")
|
||||
self.report_obj.write_report(fname, obj=self)
|
||||
|
||||
def save_pdf(self):
|
||||
fname = select_save_file(obj=self,
|
||||
default_name=f"Report {self.start_date.strftime('%Y%m%d')} - {self.end_date.strftime('%Y%m%d')}",
|
||||
extension="pdf")
|
||||
save_pdf(obj=self.webview, filename=fname)
|
||||
Reference in New Issue
Block a user