Addition of turnaround time tracking.

This commit is contained in:
lwark
2024-12-04 12:11:10 -06:00
parent 37c5c1d3eb
commit cc53b894b2
14 changed files with 136 additions and 78 deletions

View File

@@ -1,3 +1,7 @@
## 202412.02
- Addition of turnaround time tracking
## 202411.05 ## 202411.05
- Can now calculate turnaround time including holidays. - Can now calculate turnaround time including holidays.

View File

@@ -69,6 +69,6 @@ def update_log(mapper, connection, target):
else: else:
logger.info(f"No changes detected, not updating logs.") logger.info(f"No changes detected, not updating logs.")
if ctx.database_schema == "sqlite": # if ctx.database_schema == "sqlite":
event.listen(LogMixin, 'after_update', update_log, propagate=True) event.listen(LogMixin, 'after_update', update_log, propagate=True)
event.listen(LogMixin, 'after_insert', update_log, propagate=True) event.listen(LogMixin, 'after_insert', update_log, propagate=True)

View File

@@ -279,7 +279,7 @@ class Control(BaseClass):
@classmethod @classmethod
def make_parent_buttons(cls, parent: QWidget) -> None: def make_parent_buttons(cls, parent: QWidget) -> None:
""" """
Super that will make buttons in a CustomFigure. Made to be overrided. Super that will make buttons in a CustomFigure. Made to be overridden.
Args: Args:
parent (QWidget): chart holding widget to add buttons to. parent (QWidget): chart holding widget to add buttons to.
@@ -299,6 +299,10 @@ class Control(BaseClass):
class PCRControl(Control): class PCRControl(Control):
"""
Class made to hold info from Design & Analysis software.
"""
id = Column(INTEGER, ForeignKey('_control.id'), primary_key=True) id = Column(INTEGER, ForeignKey('_control.id'), primary_key=True)
subtype = Column(String(16)) #: PC or NC subtype = Column(String(16)) #: PC or NC
target = Column(String(16)) #: N1, N2, etc. target = Column(String(16)) #: N1, N2, etc.
@@ -348,7 +352,7 @@ class PCRControl(Control):
df = df[df.ct > 0.0] df = df[df.ct > 0.0]
except AttributeError: except AttributeError:
df = df df = df
fig = PCRFigure(df=df, modes=[]) fig = PCRFigure(df=df, modes=[], settings=chart_settings)
return report, fig return report, fig
def to_pydantic(self): def to_pydantic(self):
@@ -433,12 +437,12 @@ class IridaControl(Control):
def convert_by_mode(self, control_sub_type: str, mode: Literal['kraken', 'matches', 'contains'], def convert_by_mode(self, control_sub_type: str, mode: Literal['kraken', 'matches', 'contains'],
consolidate: bool = False) -> Generator[dict, None, None]: consolidate: bool = False) -> Generator[dict, None, None]:
""" """
split this instance into analysis types for controls graphs split this instance into analysis types ('kraken', 'matches', 'contains') for controls graphs
Args: Args:
consolidate (bool): whether to merge all off-target genera. Defaults to False consolidate (bool): whether to merge all off-target genera. Defaults to False
control_sub_type (str): control subtype, 'MCS-NOS', etc. control_sub_type (str): control subtype, 'MCS-NOS', etc.
mode (str): analysis type, 'contains', etc. mode (Literal['kraken', 'matches', 'contains']): analysis type, 'contains', etc.
Returns: Returns:
List[dict]: list of records List[dict]: list of records
@@ -562,7 +566,7 @@ class IridaControl(Control):
df, modes = cls.prep_df(ctx=ctx, df=df) df, modes = cls.prep_df(ctx=ctx, df=df)
# logger.debug(f"prepped df: \n {df}") # logger.debug(f"prepped df: \n {df}")
fig = IridaFigure(df=df, ytitle=title, modes=modes, parent=parent, fig = IridaFigure(df=df, ytitle=title, modes=modes, parent=parent,
months=chart_settings['months']) settings=chart_settings)
return report, fig return report, fig
@classmethod @classmethod
@@ -571,9 +575,8 @@ class IridaControl(Control):
Convert list of control records to dataframe Convert list of control records to dataframe
Args: Args:
ctx (dict): settings passed from gui
input_df (list[dict]): list of dictionaries containing records input_df (list[dict]): list of dictionaries containing records
sub_type (str | None, optional): sub_type of submission type. Defaults to None. sub_mode (str | None, optional): sub_type of submission type. Defaults to None.
Returns: Returns:
DataFrame: dataframe of controls DataFrame: dataframe of controls

View File

@@ -168,7 +168,6 @@ class KitType(BaseClass):
else: else:
return (item.reagent_role for item in relevant_associations) return (item.reagent_role for item in relevant_associations)
# TODO: Move to BasicSubmission?
def construct_xl_map_for_use(self, submission_type: str | SubmissionType) -> Generator[(str, str), None, None]: def construct_xl_map_for_use(self, submission_type: str | SubmissionType) -> Generator[(str, str), None, None]:
""" """
Creates map of locations in Excel workbook for a SubmissionType Creates map of locations in Excel workbook for a SubmissionType
@@ -274,8 +273,7 @@ class KitType(BaseClass):
for kk, vv in assoc.to_export_dict().items(): for kk, vv in assoc.to_export_dict().items():
v[kk] = vv v[kk] = vv
base_dict['reagent roles'].append(v) base_dict['reagent roles'].append(v)
# for k, v in submission_type.construct_equipment_map(): for k, v in submission_type.construct_field_map("equipment"):
for k, v in submission_type.contstruct_field_map("equipment"):
try: try:
assoc = next(item for item in submission_type.submissiontype_equipmentrole_associations if assoc = next(item for item in submission_type.submissiontype_equipmentrole_associations if
item.equipment_role.name == k) item.equipment_role.name == k)
@@ -428,7 +426,7 @@ class Reagent(BaseClass, LogMixin):
submission=sub)) #: Association proxy to SubmissionSampleAssociation.samples submission=sub)) #: Association proxy to SubmissionSampleAssociation.samples
def __repr__(self): def __repr__(self):
if self.name is not None: if self.name:
return f"<Reagent({self.name}-{self.lot})>" return f"<Reagent({self.name}-{self.lot})>"
else: else:
return f"<Reagent({self.role.name}-{self.lot})>" return f"<Reagent({self.role.name}-{self.lot})>"
@@ -447,11 +445,12 @@ class Reagent(BaseClass, LogMixin):
if extraction_kit is not None: if extraction_kit is not None:
# NOTE: Get the intersection of this reagent's ReagentType and all ReagentTypes in KitType # NOTE: Get the intersection of this reagent's ReagentType and all ReagentTypes in KitType
try: reagent_role = next((item for item in set(self.role).intersection(extraction_kit.reagent_roles)), self.role[0])
reagent_role = list(set(self.role).intersection(extraction_kit.reagent_roles))[0] # try:
# NOTE: Most will be able to fall back to first ReagentType in itself because most will only have 1. # reagent_role = list(set(self.role).intersection(extraction_kit.reagent_roles))[0]
except: # # NOTE: Most will be able to fall back to first ReagentType in itself because most will only have 1.
reagent_role = self.role[0] # except:
# reagent_role = self.role[0]
else: else:
try: try:
reagent_role = self.role[0] reagent_role = self.role[0]

View File

@@ -24,7 +24,7 @@ from sqlite3 import OperationalError as SQLOperationalError, IntegrityError as S
from openpyxl import Workbook from openpyxl import Workbook
from openpyxl.drawing.image import Image as OpenpyxlImage from openpyxl.drawing.image import Image as OpenpyxlImage
from tools import row_map, setup_lookup, jinja_template_loading, rreplace, row_keys, check_key_or_attr, Result, Report, \ from tools import row_map, setup_lookup, jinja_template_loading, rreplace, row_keys, check_key_or_attr, Result, Report, \
report_result, create_holidays_for_year report_result, create_holidays_for_year, ctx
from datetime import datetime, date, timedelta from datetime import datetime, date, timedelta
from typing import List, Any, Tuple, Literal, Generator from typing import List, Any, Tuple, Literal, Generator
from dateutil.parser import parse from dateutil.parser import parse
@@ -127,7 +127,7 @@ class BasicSubmission(BaseClass, LogMixin):
def __repr__(self) -> str: def __repr__(self) -> str:
submission_type = self.submission_type or "Basic" submission_type = self.submission_type or "Basic"
return f"<{submission_type}Submission({self.rsl_plate_num})>" return f"<Submission({self.rsl_plate_num})>"
@classmethod @classmethod
def jsons(cls) -> List[str]: def jsons(cls) -> List[str]:
@@ -1380,17 +1380,22 @@ class BasicSubmission(BaseClass, LogMixin):
writer = pyd.to_writer() writer = pyd.to_writer()
writer.xl.save(filename=fname.with_suffix(".xlsx")) writer.xl.save(filename=fname.with_suffix(".xlsx"))
def get_turnaround_time(self): def get_turnaround_time(self) -> Tuple[int|None, bool|None]:
completed = self.completed_date or datetime.now() try:
return self.calculate_turnaround(start_date=self.submitted_date.date(), end_date=completed.date()) completed = self.completed_date.date()
except AttributeError:
completed = None
return self.calculate_turnaround(start_date=self.submitted_date.date(), end_date=completed)
@classmethod @classmethod
def calculate_turnaround(cls, start_date:date|None=None, end_date:date|None=None) -> int|None: def calculate_turnaround(cls, start_date:date|None=None, end_date:date|None=None) -> Tuple[int|None, bool|None]:
if not end_date:
return None, None
try: try:
delta = np.busday_count(start_date, end_date, holidays=create_holidays_for_year(start_date.year)) delta = np.busday_count(start_date, end_date, holidays=create_holidays_for_year(start_date.year)) + 1
except ValueError: except ValueError:
return None return None, None
return delta + 1 return delta, delta <= ctx.TaT_threshold
# Below are the custom submission types # Below are the custom submission types

View File

@@ -293,20 +293,24 @@ class ReagentParser(object):
report = Report() report = Report()
if isinstance(submission_type, dict): if isinstance(submission_type, dict):
submission_type = submission_type['value'] submission_type = submission_type['value']
if isinstance(submission_type, str):
submission_type = SubmissionType.query(name=submission_type)
reagent_map = {k: v for k, v in self.kit_object.construct_xl_map_for_use(submission_type)} reagent_map = {k: v for k, v in self.kit_object.construct_xl_map_for_use(submission_type)}
try: try:
del reagent_map['info'] del reagent_map['info']
except KeyError: except KeyError:
pass pass
# logger.debug(f"Reagent map: {pformat(reagent_map)}") logger.debug(f"Reagent map: {pformat(reagent_map)}")
# NOTE: If reagent map is empty, maybe the wrong kit was given, check if there's only one kit for that submission type and use it if so. # NOTE: If reagent map is empty, maybe the wrong kit was given, check if there's only one kit for that submission type and use it if so.
if not reagent_map.keys(): if not reagent_map:
temp_kit_object = self.submission_type_obj.get_default_kit() temp_kit_object = self.submission_type_obj.get_default_kit()
logger.debug(f"Temp kit: {temp_kit_object}")
if temp_kit_object: if temp_kit_object:
self.kit_object = temp_kit_object self.kit_object = temp_kit_object
reagent_map = {k: v for k, v in self.kit_object.construct_xl_map_for_use(submission_type)} # reagent_map = {k: v for k, v in self.kit_object.construct_xl_map_for_use(submission_type)}
logger.warning(f"Attempting to salvage {self.kit_object} with default kit map: {reagent_map}") logger.warning(f"Attempting to salvage with default kit {self.kit_object} and submission_type: {self.submission_type_obj}")
if not reagent_map.keys(): return self.fetch_kit_info_map(submission_type=self.submission_type_obj)
else:
logger.error(f"Still no reagent map, displaying error.") logger.error(f"Still no reagent map, displaying error.")
try: try:
ext_kit_loc = self.submission_type_obj.info_map['extraction_kit']['read'][0] ext_kit_loc = self.submission_type_obj.info_map['extraction_kit']['read'][0]

View File

@@ -1,6 +1,8 @@
''' '''
Contains functions for generating summary reports Contains functions for generating summary reports
''' '''
from pprint import pformat
from pandas import DataFrame, ExcelWriter from pandas import DataFrame, ExcelWriter
import logging import logging
from pathlib import Path from pathlib import Path
@@ -137,3 +139,35 @@ class ReportMaker(object):
for cell in worksheet['D']: for cell in worksheet['D']:
if cell.row > 1: if cell.row > 1:
cell.style = 'Currency' cell.style = 'Currency'
class TurnaroundMaker(object):
def __init__(self, start_date: date, end_date: date):
self.start_date = start_date
self.end_date = end_date
# NOTE: Set page size to zero to override limiting query size.
self.subs = BasicSubmission.query(start_date=start_date, end_date=end_date, page_size=0)
records = [self.build_record(sub) for sub in self.subs]
self.df = DataFrame.from_records(records)
@classmethod
def build_record(cls, sub):
days, tat_ok = sub.get_turnaround_time()
return dict(name=sub.rsl_plate_num, days=days, submitted_date=sub.submitted_date,
completed_date=sub.completed_date, acceptable=tat_ok)
def write_report(self, filename: Path | str, obj: QWidget | None = None):
"""
Writes info to files.
Args:
filename (Path | str): Basename of output file
obj (QWidget | None, optional): Parent object. Defaults to None.
"""
if isinstance(filename, str):
filename = Path(filename)
filename = filename.absolute()
self.writer = ExcelWriter(filename.with_suffix(".xlsx"), engine='openpyxl')
self.df.to_excel(self.writer, sheet_name="Turnaround")
# logger.debug(f"Writing report to: {filename}")
self.writer.close()

View File

@@ -1,6 +1,8 @@
''' '''
Contains all operations for creating charts, graphs and visual effects. Contains all operations for creating charts, graphs and visual effects.
''' '''
from datetime import timedelta
from PyQt6.QtWidgets import QWidget from PyQt6.QtWidgets import QWidget
import plotly, logging import plotly, logging
from plotly.graph_objects import Figure from plotly.graph_objects import Figure
@@ -14,10 +16,15 @@ class CustomFigure(Figure):
df = None df = None
def __init__(self, df: pd.DataFrame, modes: list, ytitle: str | None = None, parent: QWidget | None = None, def __init__(self, df: pd.DataFrame, settings: dict, modes: list, ytitle: str | None = None, parent: QWidget | None = None):
months: int = 6):
super().__init__() super().__init__()
# self.settings = settings
try:
months = int(settings['months'])
except KeyError:
months = 6
self.df = df self.df = df
self.update_xaxes(range=[settings['start_date'] - timedelta(days=1), settings['end_date']])
def save_figure(self, group_name: str = "plotly_output", parent: QWidget | None = None): def save_figure(self, group_name: str = "plotly_output", parent: QWidget | None = None):
""" """

View File

@@ -16,19 +16,23 @@ logger = logging.getLogger(f"submissions.{__name__}")
class IridaFigure(CustomFigure): class IridaFigure(CustomFigure):
def __init__(self, df: pd.DataFrame, modes: list, ytitle: str | None = None, parent: QWidget | None = None, def __init__(self, df: pd.DataFrame, modes: list, settings: dict, ytitle: str | None = None, parent: QWidget | None = None):
months: int = 6):
super().__init__(df=df, modes=modes) super().__init__(df=df, modes=modes, settings=settings)
try:
self.construct_chart(df=df, modes=modes) months = int(settings['months'])
except KeyError:
months = 6
self.construct_chart(df=df, modes=modes, start_date=settings['start_date'], end_date=settings['end_date'])
self.generic_figure_markers(modes=modes, ytitle=ytitle, months=months) self.generic_figure_markers(modes=modes, ytitle=ytitle, months=months)
def construct_chart(self, df: pd.DataFrame, modes: list): def construct_chart(self, df: pd.DataFrame, modes: list, start_date: date, end_date:date):
""" """
Creates a plotly chart for controls from a pandas dataframe Creates a plotly chart for controls from a pandas dataframe
Args: Args:
end_date ():
start_date ():
df (pd.DataFrame): input dataframe of controls df (pd.DataFrame): input dataframe of controls
modes (list): analysis modes to construct charts for modes (list): analysis modes to construct charts for
ytitle (str | None, optional): title on the y-axis. Defaults to None. ytitle (str | None, optional): title on the y-axis. Defaults to None.

View File

@@ -13,9 +13,13 @@ logger = logging.getLogger(f"submissions.{__name__}")
class PCRFigure(CustomFigure): class PCRFigure(CustomFigure):
def __init__(self, df: pd.DataFrame, modes: list, ytitle: str | None = None, parent: QWidget | None = None, def __init__(self, df: pd.DataFrame, modes: list, settings: dict, ytitle: str | None = None, parent: QWidget | None = None,
months: int = 6): months: int = 6):
super().__init__(df=df, modes=modes) super().__init__(df=df, modes=modes, settings=settings)
try:
months = int(settings['months'])
except KeyError:
months = 6
# logger.debug(f"DF: {self.df}") # logger.debug(f"DF: {self.df}")
self.construct_chart(df=df) self.construct_chart(df=df)

View File

@@ -11,4 +11,6 @@ from .controls_chart import *
from .submission_details import * from .submission_details import *
from .equipment_usage import * from .equipment_usage import *
from .gel_checker import * from .gel_checker import *
from .summary import Summary
from .turnaround import TurnaroundTime
from .app import App from .app import App

View File

@@ -25,6 +25,7 @@ from .submission_widget import SubmissionFormContainer
from .controls_chart import ControlsViewer from .controls_chart import ControlsViewer
# from .sample_search import SampleSearchBox # from .sample_search import SampleSearchBox
from .summary import Summary from .summary import Summary
from .turnaround import TurnaroundTime
from .omni_search import SearchBox from .omni_search import SearchBox
logger = logging.getLogger(f'submissions.{__name__}') logger = logging.getLogger(f'submissions.{__name__}')
@@ -269,12 +270,14 @@ class AddSubForm(QWidget):
self.tab2 = QWidget() self.tab2 = QWidget()
self.tab3 = QWidget() self.tab3 = QWidget()
self.tab4 = QWidget() self.tab4 = QWidget()
self.tab5 = QWidget()
self.tabs.resize(300, 200) self.tabs.resize(300, 200)
# NOTE: Add tabs # NOTE: Add tabs
self.tabs.addTab(self.tab1, "Submissions") self.tabs.addTab(self.tab1, "Submissions")
self.tabs.addTab(self.tab2, "Irida Controls") self.tabs.addTab(self.tab2, "Irida Controls")
self.tabs.addTab(self.tab3, "PCR Controls") self.tabs.addTab(self.tab3, "PCR Controls")
self.tabs.addTab(self.tab4, "Cost Report") self.tabs.addTab(self.tab4, "Cost Report")
self.tabs.addTab(self.tab5, "Turnaround Times")
# NOTE: Create submission adder form # NOTE: Create submission adder form
self.formwidget = SubmissionFormContainer(self) self.formwidget = SubmissionFormContainer(self)
self.formlayout = QVBoxLayout(self) self.formlayout = QVBoxLayout(self)
@@ -310,6 +313,10 @@ class AddSubForm(QWidget):
self.tab4.layout = QVBoxLayout(self) self.tab4.layout = QVBoxLayout(self)
self.tab4.layout.addWidget(summary_report) self.tab4.layout.addWidget(summary_report)
self.tab4.setLayout(self.tab4.layout) self.tab4.setLayout(self.tab4.layout)
turnaround = TurnaroundTime(self)
self.tab5.layout = QVBoxLayout(self)
self.tab5.layout.addWidget(turnaround)
self.tab5.setLayout(self.tab5.layout)
# NOTE: add tabs to main widget # NOTE: add tabs to main widget
self.layout.addWidget(self.tabs) self.layout.addWidget(self.tabs)
self.setLayout(self.layout) self.setLayout(self.layout)

View File

@@ -172,11 +172,11 @@ class SubmissionDetails(QDialog):
@pyqtSlot(str) @pyqtSlot(str)
def sign_off(self, submission: str | BasicSubmission): def sign_off(self, submission: str | BasicSubmission):
# logger.debug(f"Signing off on {submission} - ({getuser()})") logger.debug(f"Signing off on {submission} - ({getuser()})")
if isinstance(submission, str): if isinstance(submission, str):
submission = BasicSubmission.query(rsl_plate_num=submission) submission = BasicSubmission.query(rsl_plate_num=submission)
submission.signed_by = getuser() submission.signed_by = getuser()
submission.completed = datetime.now().date() submission.completed_date = datetime.now().date()
submission.save() submission.save()
self.submission_details(submission=self.rsl_plate_num) self.submission_details(submission=self.rsl_plate_num)

View File

@@ -1,5 +1,6 @@
from PyQt6.QtCore import QSignalBlocker from PyQt6.QtCore import QSignalBlocker
from PyQt6.QtWebEngineWidgets import QWebEngineView from PyQt6.QtWebEngineWidgets import QWebEngineView
from .info_tab import InfoPane
from PyQt6.QtWidgets import QWidget, QGridLayout, QPushButton, QLabel from PyQt6.QtWidgets import QWidget, QGridLayout, QPushButton, QLabel
from backend.db import Organization from backend.db import Organization
from backend.excel import ReportMaker from backend.excel import ReportMaker
@@ -11,38 +12,21 @@ import logging
logger = logging.getLogger(f"submissions.{__name__}") logger = logging.getLogger(f"submissions.{__name__}")
class Summary(QWidget): class Summary(InfoPane):
def __init__(self, parent: QWidget) -> None: def __init__(self, parent: QWidget) -> None:
super().__init__(parent) 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 = CheckableComboBox()
self.org_select.setEditable(False) self.org_select.setEditable(False)
self.org_select.addItem("Select", header=True) self.org_select.addItem("Select", header=True)
for org in [org.name for org in Organization.query()]: for org in [org.name for org in Organization.query()]:
self.org_select.addItem(org) self.org_select.addItem(org)
self.org_select.model().itemChanged.connect(self.get_report) self.org_select.model().itemChanged.connect(self.date_changed)
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(QLabel("Client"), 1, 0, 1, 1)
self.layout.addWidget(self.org_select, 1, 1, 1, 3) self.layout.addWidget(self.org_select, 1, 1, 1, 3)
self.setLayout(self.layout) self.date_changed()
self.get_report()
def get_report(self): def date_changed(self):
orgs = [self.org_select.itemText(i) for i in range(self.org_select.count()) if self.org_select.itemChecked(i)] 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(): if self.datepicker.start_date.date() > self.datepicker.end_date.date():
logger.warning("Start date after end date is not allowed!") logger.warning("Start date after end date is not allowed!")
@@ -51,11 +35,12 @@ class Summary(QWidget):
# Without triggering this function again # Without triggering this function again
with QSignalBlocker(self.datepicker.start_date) as blocker: with QSignalBlocker(self.datepicker.start_date) as blocker:
self.datepicker.start_date.setDate(lastmonth) self.datepicker.start_date.setDate(lastmonth)
self.get_report() self.date_changed()
return return
# NOTE: convert to python useable date objects # NOTE: convert to python useable date objects
self.start_date = self.datepicker.start_date.date().toPyDate() # self.start_date = self.datepicker.start_date.date().toPyDate()
self.end_date = self.datepicker.end_date.date().toPyDate() # self.end_date = self.datepicker.end_date.date().toPyDate()
super().date_changed()
logger.debug(f"Getting report from {self.start_date} to {self.end_date} using {orgs}") logger.debug(f"Getting report from {self.start_date} to {self.end_date} using {orgs}")
self.report_obj = ReportMaker(start_date=self.start_date, end_date=self.end_date, organizations=orgs) self.report_obj = ReportMaker(start_date=self.start_date, end_date=self.end_date, organizations=orgs)
self.webview.setHtml(self.report_obj.html) self.webview.setHtml(self.report_obj.html)
@@ -66,12 +51,12 @@ class Summary(QWidget):
self.save_pdf_button.setEnabled(False) self.save_pdf_button.setEnabled(False)
self.save_excel_button.setEnabled(False) self.save_excel_button.setEnabled(False)
def save_excel(self): # 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") # 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) # self.report_obj.write_report(fname, obj=self)
#
def save_pdf(self): # def save_pdf(self):
fname = select_save_file(obj=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')}", # default_name=f"Report {self.start_date.strftime('%Y%m%d')} - {self.end_date.strftime('%Y%m%d')}",
extension="pdf") # extension="pdf")
save_pdf(obj=self.webview, filename=fname) # save_pdf(obj=self.webview, filename=fname)