Files
Submissions-App/src/submissions/frontend/widgets/controls_chart.py
2024-12-06 12:26:28 -06:00

174 lines
7.6 KiB
Python

"""
Handles display of control charts
"""
from datetime import date
from pprint import pformat
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWidgets import (
QWidget, QComboBox, QPushButton, QGridLayout
)
from PyQt6.QtCore import QSignalBlocker
from backend.db import ControlType, IridaControl
import logging
from tools import Report, report_result, Result
from frontend.visualizations import CustomFigure
from .misc import StartEndDatePicker
from .info_tab import InfoPane
logger = logging.getLogger(f"submissions.{__name__}")
class ControlsViewer(InfoPane):
def __init__(self, parent: QWidget, archetype: str) -> None:
super().__init__(parent)
logger.debug(f"Incoming Archetype: {archetype}")
self.archetype = ControlType.query(name=archetype)
if not self.archetype:
return
logger.debug(f"Archetype set as: {self.archetype}")
# self.app = self.parent().parent()
# logger.debug(f"\n\n{self.app}\n\n")
# self.report = Report()
# self.datepicker = StartEndDatePicker(default_start=-180)
# self.webengineview = QWebEngineView()
# NOTE: set tab2 layout
# self.layout = QGridLayout(self)
self.control_sub_typer = QComboBox()
# NOTE: fetch types of controls
con_sub_types = [item for item in self.archetype.targets.keys()]
self.control_sub_typer.addItems(con_sub_types)
# NOTE: create custom widget to get types of analysis -- disabled by PCR control
self.mode_typer = QComboBox()
mode_types = IridaControl.get_modes()
self.mode_typer.addItems(mode_types)
# NOTE: create custom widget to get subtypes of analysis -- disabled by PCR control
self.mode_sub_typer = QComboBox()
self.mode_sub_typer.setEnabled(False)
# NOTE: add widgets to tab2 layout
# 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.export_button = QPushButton("Save Data", parent=self)
self.layout.addWidget(self.export_button, 0, 3, 1, 1)
self.layout.addWidget(self.control_sub_typer, 1, 0, 1, 4)
self.layout.addWidget(self.mode_typer, 2, 0, 1, 4)
self.layout.addWidget(self.mode_sub_typer, 3, 0, 1, 4)
self.archetype.get_instance_class().make_parent_buttons(parent=self)
# self.layout.addWidget(self.webengineview, self.layout.rowCount(), 0, 1, 4)
# self.setLayout(self.layout)
self.update_data()
self.control_sub_typer.currentIndexChanged.connect(self.update_data)
self.mode_typer.currentIndexChanged.connect(self.update_data)
# self.datepicker.start_date.dateChanged.connect(self.update_data)
# self.datepicker.end_date.dateChanged.connect(self.update_data)
self.save_button.pressed.connect(self.save_chart_function)
self.export_button.pressed.connect(self.save_data_function)
def save_chart_function(self):
self.fig.save_figure(parent=self)
def save_data_function(self):
self.fig.save_data(parent=self)
@report_result
def update_data(self, *args, **kwargs):
"""
Get controls based on start/end dates
"""
super().update_data()
# NOTE: mode_sub_type defaults to disabled
try:
self.mode_sub_typer.disconnect()
except TypeError:
pass
# NOTE: correct start date being more recent than end date and rerun
# if self.datepicker.start_date.date() > self.datepicker.end_date.date():
# threemonthsago = self.datepicker.end_date.date().addDays(-60)
# msg = f"Start date after end date is not allowed! Setting to {threemonthsago.toString()}."
# logger.warning(msg)
# # 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.update_data()
# report.add_result(Result(owner=self.__str__(), msg=msg, status="Warning"))
# return report
# # 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_sub_type = self.control_sub_typer.currentText()
self.mode = self.mode_typer.currentText()
self.mode_sub_typer.clear()
# NOTE: lookup subtypes
try:
sub_types = self.archetype.get_modes(mode=self.mode)
except AttributeError:
sub_types = []
# NOTE: added in allowed to have subtypes in case additions made in future.
if sub_types and self.mode.lower() in self.archetype.get_instance_class().subtyping_allowed:
# NOTE: block signal that will rerun controls getter and update mode_sub_typer
with QSignalBlocker(self.mode_sub_typer) as blocker:
self.mode_sub_typer.addItems(sub_types)
self.mode_sub_typer.setEnabled(True)
self.mode_sub_typer.currentTextChanged.connect(self.chart_maker_function)
else:
self.mode_sub_typer.clear()
self.mode_sub_typer.setEnabled(False)
self.chart_maker_function()
# return report
@classmethod
def diff_month(self, d1: date, d2: date) -> float:
"""
Gets the number of months difference between two different dates
Args:
d1 (date): Start date.
d2 (date): End date.
Returns:
float: Number of months difference
"""
return abs((d1.year - d2.year) * 12 + d1.month - d2.month)
@report_result
def chart_maker_function(self, *args, **kwargs):
# TODO: Generalize this by moving as much code as possible to IridaControl
"""
Create html chart for controls reporting
Args:
obj (QMainWindow): original app window
Returns:
Tuple[QMainWindow, dict]: Collection of new main app window and result dict
"""
report = Report()
# logger.debug(f"Control getter context: \n\tControl type: {self.con_sub_type}\n\tMode: {self.mode}\n\tStart \
# Date: {self.start_date}\n\tEnd Date: {self.end_date}")
# NOTE: set the mode_sub_type for kraken
if self.mode_sub_typer.currentText() == "":
self.mode_sub_type = None
else:
self.mode_sub_type = self.mode_sub_typer.currentText()
logger.debug(f"Subtype: {self.mode_sub_type}")
months = self.diff_month(self.start_date, self.end_date)
# NOTE: query all controls using the type/start and end dates from the gui
chart_settings = dict(sub_type=self.con_sub_type, start_date=self.start_date, end_date=self.end_date,
mode=self.mode,
sub_mode=self.mode_sub_type, parent=self, months=months)
self.fig = self.archetype.get_instance_class().make_chart(chart_settings=chart_settings, parent=self, ctx=self.app.ctx)
if issubclass(self.fig.__class__, CustomFigure):
self.save_button.setEnabled(True)
# logger.debug(f"Updating figure...")
# NOTE: construct html for webview
try:
html = self.fig.to_html()
except AttributeError:
html = ""
# logger.debug(f"The length of html code is: {len(html)}")
self.webview.setHtml(html)
self.webview.update()
# logger.debug("Figure updated... I hope.")
return report