Added report tab with HTML and excel export.
This commit is contained in:
@@ -13,7 +13,7 @@ from tempfile import TemporaryDirectory, TemporaryFile
|
|||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from . import BaseClass, Reagent, SubmissionType, KitType, Organization, Contact
|
from . import BaseClass, Reagent, SubmissionType, KitType, Organization, Contact
|
||||||
from sqlalchemy import Column, String, TIMESTAMP, INTEGER, ForeignKey, JSON, FLOAT, case
|
from sqlalchemy import Column, String, TIMESTAMP, INTEGER, ForeignKey, JSON, FLOAT, case, desc
|
||||||
from sqlalchemy.orm import relationship, validates, Query
|
from sqlalchemy.orm import relationship, validates, Query
|
||||||
from sqlalchemy.orm.attributes import flag_modified
|
from sqlalchemy.orm.attributes import flag_modified
|
||||||
from sqlalchemy.ext.associationproxy import association_proxy
|
from sqlalchemy.ext.associationproxy import association_proxy
|
||||||
@@ -283,7 +283,7 @@ class BasicSubmission(BaseClass):
|
|||||||
del input_dict['id']
|
del input_dict['id']
|
||||||
return input_dict
|
return input_dict
|
||||||
|
|
||||||
def generate_associations(self, name:str, extra:str|None=None):
|
def generate_associations(self, name: str, extra: str | None = None):
|
||||||
try:
|
try:
|
||||||
field = self.__getattribute__(name)
|
field = self.__getattribute__(name)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@@ -479,15 +479,6 @@ class BasicSubmission(BaseClass):
|
|||||||
Returns:
|
Returns:
|
||||||
str: html output string.
|
str: html output string.
|
||||||
"""
|
"""
|
||||||
# output_samples = []
|
|
||||||
# logger.debug("Setting locations.")
|
|
||||||
# for column in range(1, plate_columns + 1):
|
|
||||||
# for row in range(1, plate_rows + 1):
|
|
||||||
# try:
|
|
||||||
# well = next((item for item in sample_list if item['row'] == row and item['column'] == column), dict(name="", row=row, column=column, background_color="#ffffff"))
|
|
||||||
# except StopIteration:
|
|
||||||
# well = dict(name="", row=row, column=column, background_color="#ffffff")
|
|
||||||
# output_samples.append(well)
|
|
||||||
rows = range(1, plate_rows + 1)
|
rows = range(1, plate_rows + 1)
|
||||||
columns = range(1, plate_columns + 1)
|
columns = range(1, plate_columns + 1)
|
||||||
# NOTE: An overly complicated list comprehension create a list of sample locations
|
# NOTE: An overly complicated list comprehension create a list of sample locations
|
||||||
@@ -512,11 +503,12 @@ class BasicSubmission(BaseClass):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def submissions_to_df(cls, submission_type: str | None = None, limit: int = 0,
|
def submissions_to_df(cls, submission_type: str | None = None, limit: int = 0,
|
||||||
chronologic: bool = True) -> pd.DataFrame:
|
chronologic: bool = True, page: int = 1, page_size: int = 250) -> pd.DataFrame:
|
||||||
"""
|
"""
|
||||||
Convert all submissions to dataframe
|
Convert all submissions to dataframe
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
page (int, optional): Limits the number of submissions to a page size. Defaults to 1.
|
||||||
chronologic (bool, optional): Sort submissions in chronologic order. Defaults to True.
|
chronologic (bool, optional): Sort submissions in chronologic order. Defaults to True.
|
||||||
submission_type (str | None, optional): Filter by SubmissionType. Defaults to None.
|
submission_type (str | None, optional): Filter by SubmissionType. Defaults to None.
|
||||||
limit (int, optional): Maximum number of results to return. Defaults to 0.
|
limit (int, optional): Maximum number of results to return. Defaults to 0.
|
||||||
@@ -528,7 +520,7 @@ class BasicSubmission(BaseClass):
|
|||||||
# logger.debug(f"Using limit: {limit}")
|
# logger.debug(f"Using limit: {limit}")
|
||||||
# NOTE: use lookup function to create list of dicts
|
# NOTE: use lookup function to create list of dicts
|
||||||
subs = [item.to_dict() for item in
|
subs = [item.to_dict() for item in
|
||||||
cls.query(submission_type=submission_type, limit=limit, chronologic=chronologic)]
|
cls.query(submission_type=submission_type, limit=limit, chronologic=chronologic, page=page, page_size=page_size)]
|
||||||
# logger.debug(f"Got {len(subs)} submissions.")
|
# logger.debug(f"Got {len(subs)} submissions.")
|
||||||
df = pd.DataFrame.from_records(subs)
|
df = pd.DataFrame.from_records(subs)
|
||||||
# logger.debug(f"Column names: {df.columns}")
|
# logger.debug(f"Column names: {df.columns}")
|
||||||
@@ -829,7 +821,7 @@ class BasicSubmission(BaseClass):
|
|||||||
return input_dict
|
return input_dict
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def custom_validation(cls, pyd:"PydSubmission") -> dict:
|
def custom_validation(cls, pyd: "PydSubmission") -> dict:
|
||||||
"""
|
"""
|
||||||
Performs any final custom parsing of the excel file.
|
Performs any final custom parsing of the excel file.
|
||||||
|
|
||||||
@@ -886,7 +878,6 @@ class BasicSubmission(BaseClass):
|
|||||||
ws.cell(row=item['row'], column=item['column'], value=item['value'])
|
ws.cell(row=item['row'], column=item['column'], value=item['value'])
|
||||||
return input_excel
|
return input_excel
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def enforce_name(cls, instr: str, data: dict | None = {}) -> str:
|
def enforce_name(cls, instr: str, data: dict | None = {}) -> str:
|
||||||
"""
|
"""
|
||||||
@@ -1061,6 +1052,8 @@ class BasicSubmission(BaseClass):
|
|||||||
reagent: Reagent | str | None = None,
|
reagent: Reagent | str | None = None,
|
||||||
chronologic: bool = False,
|
chronologic: bool = False,
|
||||||
limit: int = 0,
|
limit: int = 0,
|
||||||
|
page: int = 1,
|
||||||
|
page_size: int = 250,
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> BasicSubmission | List[BasicSubmission]:
|
) -> BasicSubmission | List[BasicSubmission]:
|
||||||
"""
|
"""
|
||||||
@@ -1161,7 +1154,13 @@ class BasicSubmission(BaseClass):
|
|||||||
case _:
|
case _:
|
||||||
pass
|
pass
|
||||||
if chronologic:
|
if chronologic:
|
||||||
query.order_by(cls.submitted_date)
|
logger.debug("Attempting sort by date descending")
|
||||||
|
query = query.order_by(cls.submitted_date.desc())
|
||||||
|
if page_size is not None:
|
||||||
|
query = query.limit(page_size)
|
||||||
|
page = page - 1
|
||||||
|
if page is not None:
|
||||||
|
query = query.offset(page * page_size)
|
||||||
return cls.execute_query(query=query, model=model, limit=limit, **kwargs)
|
return cls.execute_query(query=query, model=model, limit=limit, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -611,7 +611,7 @@ class PydSubmission(BaseModel, extra='allow'):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def expand_samples(cls, value):
|
def expand_samples(cls, value):
|
||||||
if isinstance(value, Generator):
|
if isinstance(value, Generator):
|
||||||
# logger.debug("We have a generator")
|
# logger.debug("We have a generator")[
|
||||||
return [PydSample(**sample) for sample in value]
|
return [PydSample(**sample) for sample in value]
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ from pathlib import Path
|
|||||||
|
|
||||||
from markdown import markdown
|
from markdown import markdown
|
||||||
from __init__ import project_path
|
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 .functions import select_save_file,select_open_file
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from .pop_ups import HTMLPop, AlertPop
|
from .pop_ups import HTMLPop, AlertPop
|
||||||
from .misc import LogParser
|
from .misc import LogParser, Pagifier
|
||||||
import logging, webbrowser, sys, shutil
|
import logging, webbrowser, sys, shutil
|
||||||
from .submission_table import SubmissionsSheet
|
from .submission_table import SubmissionsSheet
|
||||||
from .submission_widget import SubmissionFormContainer
|
from .submission_widget import SubmissionFormContainer
|
||||||
@@ -49,6 +49,7 @@ class App(QMainWindow):
|
|||||||
self.height = 1000
|
self.height = 1000
|
||||||
self.setWindowTitle(self.title)
|
self.setWindowTitle(self.title)
|
||||||
self.setGeometry(self.left, self.top, self.width, self.height)
|
self.setGeometry(self.left, self.top, self.width, self.height)
|
||||||
|
self.page_size = page_size
|
||||||
# NOTE: insert tabs into main app
|
# NOTE: insert tabs into main app
|
||||||
self.table_widget = AddSubForm(self)
|
self.table_widget = AddSubForm(self)
|
||||||
self.setCentralWidget(self.table_widget)
|
self.setCentralWidget(self.table_widget)
|
||||||
@@ -133,6 +134,7 @@ class App(QMainWindow):
|
|||||||
self.githubAction.triggered.connect(self.openGithub)
|
self.githubAction.triggered.connect(self.openGithub)
|
||||||
self.yamlExportAction.triggered.connect(self.export_ST_yaml)
|
self.yamlExportAction.triggered.connect(self.export_ST_yaml)
|
||||||
self.yamlImportAction.triggered.connect(self.import_ST_yaml)
|
self.yamlImportAction.triggered.connect(self.import_ST_yaml)
|
||||||
|
self.table_widget.pager.current_page.textChanged.connect(self.update_data)
|
||||||
|
|
||||||
def showAbout(self):
|
def showAbout(self):
|
||||||
"""
|
"""
|
||||||
@@ -237,6 +239,8 @@ class App(QMainWindow):
|
|||||||
else:
|
else:
|
||||||
logger.warning("Save of submission type cancelled.")
|
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):
|
class AddSubForm(QWidget):
|
||||||
@@ -272,7 +276,9 @@ class AddSubForm(QWidget):
|
|||||||
self.sheetlayout = QVBoxLayout(self)
|
self.sheetlayout = QVBoxLayout(self)
|
||||||
self.sheetwidget.setLayout(self.sheetlayout)
|
self.sheetwidget.setLayout(self.sheetlayout)
|
||||||
self.sub_wid = SubmissionsSheet(parent=parent)
|
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.sub_wid)
|
||||||
|
self.sheetlayout.addWidget(self.pager)
|
||||||
# NOTE: Create layout of first tab to hold form and sheet
|
# NOTE: Create layout of first tab to hold form and sheet
|
||||||
self.tab1.layout = QHBoxLayout(self)
|
self.tab1.layout = QHBoxLayout(self)
|
||||||
self.tab1.setLayout(self.tab1.layout)
|
self.tab1.setLayout(self.tab1.layout)
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
'''
|
'''
|
||||||
Contains miscellaneous widgets for frontend functions
|
Contains miscellaneous widgets for frontend functions
|
||||||
'''
|
'''
|
||||||
|
import math
|
||||||
from datetime import date
|
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.QtWebEngineWidgets import QWebEngineView
|
||||||
from PyQt6.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
QLabel, QVBoxLayout,
|
QLabel, QVBoxLayout,
|
||||||
QLineEdit, QComboBox, QDialog,
|
QLineEdit, QComboBox, QDialog,
|
||||||
QDialogButtonBox, QDateEdit, QPushButton, QFormLayout, QWidget, QHBoxLayout, QSizePolicy
|
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 tools import jinja_template_loading
|
||||||
from backend.db.models import *
|
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, select_save_file
|
from .functions import select_open_file
|
||||||
|
|
||||||
logger = logging.getLogger(f"submissions.{__name__}")
|
logger = logging.getLogger(f"submissions.{__name__}")
|
||||||
|
|
||||||
@@ -245,3 +246,42 @@ class CheckableComboBox(QComboBox):
|
|||||||
def changed(self):
|
def changed(self):
|
||||||
logger.debug("emitting updated")
|
logger.debug("emitting updated")
|
||||||
self.updated.emit()
|
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))
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ from PyQt6.QtWidgets import QTableView, QMenu
|
|||||||
from PyQt6.QtCore import Qt, QAbstractTableModel, QSortFilterProxyModel
|
from PyQt6.QtCore import Qt, QAbstractTableModel, QSortFilterProxyModel
|
||||||
from PyQt6.QtGui import QAction, QCursor
|
from PyQt6.QtGui import QAction, QCursor
|
||||||
from backend.db.models import BasicSubmission
|
from backend.db.models import BasicSubmission
|
||||||
from backend.excel import ReportMaker
|
|
||||||
from tools import Report, Result, report_result
|
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
|
# from .misc import ReportDatePicker
|
||||||
|
|
||||||
logger = logging.getLogger(f"submissions.{__name__}")
|
logger = logging.getLogger(f"submissions.{__name__}")
|
||||||
@@ -20,6 +20,7 @@ class pandasModel(QAbstractTableModel):
|
|||||||
pandas model for inserting summary sheet into gui
|
pandas model for inserting summary sheet into gui
|
||||||
NOTE: Copied from Stack Overflow. I have no idea how it actually works.
|
NOTE: Copied from Stack Overflow. I have no idea how it actually works.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, data) -> None:
|
def __init__(self, data) -> None:
|
||||||
QAbstractTableModel.__init__(self)
|
QAbstractTableModel.__init__(self)
|
||||||
self._data = data
|
self._data = data
|
||||||
@@ -48,7 +49,7 @@ class pandasModel(QAbstractTableModel):
|
|||||||
"""
|
"""
|
||||||
return self._data.shape[1]
|
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 index.isValid():
|
||||||
if role == Qt.ItemDataRole.DisplayRole:
|
if role == Qt.ItemDataRole.DisplayRole:
|
||||||
return str(self._data.iloc[index.row(), index.column()])
|
return str(self._data.iloc[index.row(), index.column()])
|
||||||
@@ -64,6 +65,7 @@ class SubmissionsSheet(QTableView):
|
|||||||
"""
|
"""
|
||||||
presents submission summary to user in tab1
|
presents submission summary to user in tab1
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, parent) -> None:
|
def __init__(self, parent) -> None:
|
||||||
"""
|
"""
|
||||||
initialize
|
initialize
|
||||||
@@ -74,17 +76,18 @@ class SubmissionsSheet(QTableView):
|
|||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.app = self.parent()
|
self.app = self.parent()
|
||||||
self.report = Report()
|
self.report = Report()
|
||||||
self.setData()
|
self.setData(page=1, page_size=self.app.page_size)
|
||||||
self.resizeColumnsToContents()
|
self.resizeColumnsToContents()
|
||||||
self.resizeRowsToContents()
|
self.resizeRowsToContents()
|
||||||
self.setSortingEnabled(True)
|
self.setSortingEnabled(True)
|
||||||
self.doubleClicked.connect(lambda x: BasicSubmission.query(id=x.sibling(x.row(), 0).data()).show_details(self))
|
self.doubleClicked.connect(lambda x: BasicSubmission.query(id=x.sibling(x.row(), 0).data()).show_details(self))
|
||||||
|
self.total_count = BasicSubmission.__database_session__.query(BasicSubmission).count()
|
||||||
|
|
||||||
def setData(self) -> None:
|
def setData(self, page: int = 1, page_size: int = 250) -> None:
|
||||||
"""
|
"""
|
||||||
sets data in model
|
sets data in model
|
||||||
"""
|
"""
|
||||||
self.data = BasicSubmission.submissions_to_df()
|
self.data = BasicSubmission.submissions_to_df(page=page)
|
||||||
try:
|
try:
|
||||||
self.data['Id'] = self.data['Id'].apply(str)
|
self.data['Id'] = self.data['Id'].apply(str)
|
||||||
self.data['Id'] = self.data['Id'].str.zfill(4)
|
self.data['Id'] = self.data['Id'].str.zfill(4)
|
||||||
@@ -103,7 +106,7 @@ class SubmissionsSheet(QTableView):
|
|||||||
"""
|
"""
|
||||||
# logger.debug(event().__dict__)
|
# logger.debug(event().__dict__)
|
||||||
id = self.selectionModel().currentIndex()
|
id = self.selectionModel().currentIndex()
|
||||||
id = id.sibling(id.row(),0).data()
|
id = id.sibling(id.row(), 0).data()
|
||||||
submission = BasicSubmission.query(id=id)
|
submission = BasicSubmission.query(id=id)
|
||||||
self.menu = QMenu(self)
|
self.menu = QMenu(self)
|
||||||
self.con_actions = submission.custom_context_events()
|
self.con_actions = submission.custom_context_events()
|
||||||
@@ -115,7 +118,7 @@ class SubmissionsSheet(QTableView):
|
|||||||
# add other required actions
|
# add other required actions
|
||||||
self.menu.popup(QCursor.pos())
|
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
|
Calls the triggered action from the context menu
|
||||||
|
|
||||||
@@ -164,7 +167,7 @@ class SubmissionsSheet(QTableView):
|
|||||||
)
|
)
|
||||||
# NOTE: elution columns are item 6 in the comma split list to the end
|
# NOTE: elution columns are item 6 in the comma split list to the end
|
||||||
for ii in range(6, len(run)):
|
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
|
# NOTE: Lookup imported submissions
|
||||||
sub = BasicSubmission.query(rsl_plate_num=new_run['rsl_plate_num'])
|
sub = BasicSubmission.query(rsl_plate_num=new_run['rsl_plate_num'])
|
||||||
# NOTE: If no such submission exists, move onto the next run
|
# NOTE: If no such submission exists, move onto the next run
|
||||||
@@ -225,33 +228,3 @@ class SubmissionsSheet(QTableView):
|
|||||||
sub.save()
|
sub.save()
|
||||||
report.add_result(Result(msg=f"We added {count} logs to the database.", status='Information'))
|
report.add_result(Result(msg=f"We added {count} logs to the database.", status='Information'))
|
||||||
return report
|
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
|
|
||||||
|
|||||||
Reference in New Issue
Block a user