Code cleanup.

This commit is contained in:
lwark
2025-04-11 14:35:00 -05:00
parent ae6717bc77
commit 58f5d361b3
20 changed files with 53 additions and 689 deletions

View File

@@ -1,3 +1,4 @@
- [ ] move functions from widgets.functions to... app?
- [x] Apply below fix to all other date-based queries. - [x] Apply below fix to all other date-based queries.
- [x] Fix Control graph chart bug that excludes today's controls. - [x] Fix Control graph chart bug that excludes today's controls.
- [x] Convert logger to a custom class. - [x] Convert logger to a custom class.

View File

@@ -8,8 +8,6 @@ if check_if_app():
# NOTE: setup custom logger # NOTE: setup custom logger
logging.setLoggerClass(CustomLogger) logging.setLoggerClass(CustomLogger)
# logger = logging.getLogger("submissions")
# logger = setup_logger(verbosity=3)
from PyQt6.QtWidgets import QApplication from PyQt6.QtWidgets import QApplication
from frontend.widgets.app import App from frontend.widgets.app import App

View File

@@ -1573,17 +1573,13 @@ class BacterialCulture(BasicSubmission):
def get_provisional_controls(self, include: List[str] = []): def get_provisional_controls(self, include: List[str] = []):
# NOTE To ensure Samples are done last. # NOTE To ensure Samples are done last.
include = sorted(include) include = sorted(include)
logger.debug(include) # logger.debug(include)
pos_str = "(ATCC)|(MCS)" pos_str = "(ATCC)|(MCS)"
pos_regex = re.compile(rf"^{pos_str}") pos_regex = re.compile(rf"^{pos_str}")
neg_str = "(EN)" neg_str = "(EN)"
neg_regex = re.compile(rf"^{neg_str}") neg_regex = re.compile(rf"^{neg_str}")
total_str = pos_str + "|" + neg_str
total_regex = re.compile(rf"^{total_str}")
output = [] output = []
for item in include: for item in include:
# if self.controls:
# logger.debug(item)
match item: match item:
case "Positive": case "Positive":
if self.controls: if self.controls:

View File

@@ -21,7 +21,6 @@ class IridaFigure(CustomFigure):
self.df = df self.df = df
self.construct_chart(df=df, modes=modes, start_date=settings['start_date'], end_date=settings['end_date']) self.construct_chart(df=df, modes=modes, start_date=settings['start_date'], end_date=settings['end_date'])
def construct_chart(self, df: pd.DataFrame, modes: list, start_date: date, end_date:date): 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

View File

@@ -17,4 +17,4 @@ from .submission_widget import *
from .summary import * from .summary import *
from .turnaround import * from .turnaround import *
from .omni_add_edit import * from .omni_add_edit import *
from .omni_manager import * from .omni_manager_pydant import *

View File

@@ -1,7 +1,7 @@
""" """
Constructs main application. Constructs main application.
""" """
import getpass, logging, webbrowser, sys, shutil import getpass, logging, webbrowser, sys
from pprint import pformat from pprint import pformat
from PyQt6.QtCore import qInstallMessageHandler from PyQt6.QtCore import qInstallMessageHandler
from PyQt6.QtWidgets import ( from PyQt6.QtWidgets import (
@@ -13,8 +13,7 @@ from PyQt6.QtGui import QAction
from pathlib import Path from pathlib import Path
from markdown import markdown from markdown import markdown
from pandas import ExcelWriter from pandas import ExcelWriter
from __init__ import project_path from backend import Reagent, BasicSample, Organization, KitType, BasicSubmission
from backend import SubmissionType, Reagent, BasicSample, Organization, KitType, BasicSubmission
from tools import ( from tools import (
check_if_app, Settings, Report, jinja_template_loading, check_authorization, page_size, is_power_user, check_if_app, Settings, Report, jinja_template_loading, check_authorization, page_size, is_power_user,
under_development under_development
@@ -30,7 +29,6 @@ from .summary import Summary
from .turnaround import TurnaroundTime from .turnaround import TurnaroundTime
from .concentrations import Concentrations from .concentrations import Concentrations
from .omni_search import SearchBox from .omni_search import SearchBox
from .omni_manager import ManagerWindow
logger = logging.getLogger(f'submissions.{__name__}') logger = logging.getLogger(f'submissions.{__name__}')
@@ -83,7 +81,6 @@ class App(QMainWindow):
helpMenu.addAction(self.githubAction) helpMenu.addAction(self.githubAction)
fileMenu.addAction(self.importAction) fileMenu.addAction(self.importAction)
fileMenu.addAction(self.archiveSubmissionsAction) fileMenu.addAction(self.archiveSubmissionsAction)
# fileMenu.addAction(self.yamlImportAction)
methodsMenu.addAction(self.searchSample) methodsMenu.addAction(self.searchSample)
maintenanceMenu.addAction(self.joinExtractionAction) maintenanceMenu.addAction(self.joinExtractionAction)
maintenanceMenu.addAction(self.joinPCRAction) maintenanceMenu.addAction(self.joinPCRAction)
@@ -123,7 +120,7 @@ class App(QMainWindow):
""" """
connect menu and tool bar item to functions connect menu and tool bar item to functions
""" """
self.importAction.triggered.connect(self.table_widget.formwidget.importSubmission) self.importAction.triggered.connect(lambda fname: self.table_widget.formwidget.import_submission_function(fname=fname))
self.addReagentAction.triggered.connect(self.table_widget.formwidget.add_reagent) self.addReagentAction.triggered.connect(self.table_widget.formwidget.add_reagent)
self.joinExtractionAction.triggered.connect(self.table_widget.sub_wid.link_extractions) self.joinExtractionAction.triggered.connect(self.table_widget.sub_wid.link_extractions)
self.joinPCRAction.triggered.connect(self.table_widget.sub_wid.link_pcr) self.joinPCRAction.triggered.connect(self.table_widget.sub_wid.link_pcr)
@@ -192,7 +189,6 @@ class App(QMainWindow):
# TODO: Change this to the Pydantic version. # TODO: Change this to the Pydantic version.
def manage_orgs(self): def manage_orgs(self):
from frontend.widgets.omni_manager_pydant import ManagerWindow as ManagerWindowPyd from frontend.widgets.omni_manager_pydant import ManagerWindow as ManagerWindowPyd
# dlg = ManagerWindow(parent=self, object_type=Organization, extras=[], add_edit='edit', managers=set())
dlg = ManagerWindowPyd(parent=self, object_type=Organization, extras=[], add_edit='edit', managers=set()) dlg = ManagerWindowPyd(parent=self, object_type=Organization, extras=[], add_edit='edit', managers=set())
if dlg.exec(): if dlg.exec():
new_org = dlg.parse_form() new_org = dlg.parse_form()
@@ -202,10 +198,10 @@ class App(QMainWindow):
from frontend.widgets.omni_manager_pydant import ManagerWindow as ManagerWindowPyd from frontend.widgets.omni_manager_pydant import ManagerWindow as ManagerWindowPyd
dlg = ManagerWindowPyd(parent=self, object_type=KitType, extras=[], add_edit='edit', managers=set()) dlg = ManagerWindowPyd(parent=self, object_type=KitType, extras=[], add_edit='edit', managers=set())
if dlg.exec(): if dlg.exec():
logger.debug("\n\nBeginning parsing\n\n") # logger.debug("\n\nBeginning parsing\n\n")
output = dlg.parse_form() output = dlg.parse_form()
logger.debug(f"Kit output: {pformat(output.__dict__)}") # logger.debug(f"Kit output: {pformat(output.__dict__)}")
logger.debug("\n\nBeginning transformation\n\n") # logger.debug("\n\nBeginning transformation\n\n")
sql = output.to_sql() sql = output.to_sql()
assert isinstance(sql, KitType) assert isinstance(sql, KitType)
sql.save() sql.save()

View File

@@ -1,7 +1,7 @@
""" """
Pane showing BC control concentrations summary. Pane showing BC control concentrations summary.
""" """
from PyQt6.QtWidgets import QWidget, QPushButton, QCheckBox, QLabel from PyQt6.QtWidgets import QWidget, QPushButton, QLabel
from .info_tab import InfoPane from .info_tab import InfoPane
from backend.excel.reports import ConcentrationMaker from backend.excel.reports import ConcentrationMaker
from frontend.visualizations.concentrations_chart import ConcentrationsChart from frontend.visualizations.concentrations_chart import ConcentrationsChart
@@ -25,6 +25,7 @@ class Concentrations(InfoPane):
self.pos_neg = CheckableComboBox(parent=self) self.pos_neg = CheckableComboBox(parent=self)
self.pos_neg.model().itemChanged.connect(self.update_data) self.pos_neg.model().itemChanged.connect(self.update_data)
self.pos_neg.setEditable(False) self.pos_neg.setEditable(False)
self.pos_neg.addItem("Select", header=True)
self.pos_neg.addItem("Positive") self.pos_neg.addItem("Positive")
self.pos_neg.addItem("Negative") self.pos_neg.addItem("Negative")
self.pos_neg.addItem("Samples", start_checked=False) self.pos_neg.addItem("Samples", start_checked=False)
@@ -46,7 +47,6 @@ class Concentrations(InfoPane):
super().update_data() super().update_data()
months = self.diff_month(self.start_date, self.end_date) months = self.diff_month(self.start_date, self.end_date)
# logger.debug(f"Box checked: {self.all_box.isChecked()}") # logger.debug(f"Box checked: {self.all_box.isChecked()}")
# chart_settings = dict(start_date=self.start_date, end_date=self.end_date, controls_only=self.all_box.isChecked())
chart_settings = dict(start_date=self.start_date, end_date=self.end_date, chart_settings = dict(start_date=self.start_date, end_date=self.end_date,
include=include) include=include)
self.report_obj = ConcentrationMaker(**chart_settings) self.report_obj = ConcentrationMaker(**chart_settings)

View File

@@ -108,7 +108,7 @@ class ControlsViewer(InfoPane):
parent=self, parent=self,
months=months months=months
) )
logger.debug(f"Chart settings: {chart_settings}") # logger.debug(f"Chart settings: {chart_settings}")
self.fig = self.archetype.instance_class.make_chart(chart_settings=chart_settings, parent=self, ctx=self.app.ctx) self.fig = self.archetype.instance_class.make_chart(chart_settings=chart_settings, parent=self, ctx=self.app.ctx)
self.report_obj = ChartReportMaker(df=self.fig.df, sheet_name=self.archetype.name) self.report_obj = ChartReportMaker(df=self.fig.df, sheet_name=self.archetype.name)
if issubclass(self.fig.__class__, CustomFigure): if issubclass(self.fig.__class__, CustomFigure):

View File

@@ -1,6 +1,5 @@
from PyQt6.QtWidgets import ( from PyQt6.QtWidgets import (
QLabel, QVBoxLayout, QDialog, QVBoxLayout, QDialog, QDialogButtonBox
QDialogButtonBox, QMessageBox, QComboBox
) )
from .misc import CheckableComboBox, StartEndDatePicker from .misc import CheckableComboBox, StartEndDatePicker
from backend.db import SubmissionType from backend.db import SubmissionType
@@ -28,7 +27,8 @@ class DateTypePicker(QDialog):
self.setLayout(self.layout) self.setLayout(self.layout)
def parse_form(self): def parse_form(self):
sub_types = [self.typepicker.itemText(i) for i in range(self.typepicker.count()) if self.typepicker.itemChecked(i)] # sub_types = [self.typepicker.itemText(i) for i in range(self.typepicker.count()) if self.typepicker.itemChecked(i)]
sub_types = self.typepicker.get_checked()
start_date = self.datepicker.start_date.date().toPyDate() start_date = self.datepicker.start_date.date().toPyDate()
end_date = self.datepicker.end_date.date().toPyDate() end_date = self.datepicker.end_date.date().toPyDate()
return dict(submissiontype=sub_types, start_date=start_date, end_date=end_date) return dict(submissiontype=sub_types, start_date=start_date, end_date=end_date)

View File

@@ -74,10 +74,10 @@ class EquipmentUsage(QDialog):
self.layout.addWidget(self.check, 0, 0) self.layout.addWidget(self.check, 0, 0)
self.check.stateChanged.connect(self.check_all) self.check.stateChanged.connect(self.check_all)
for iii, item in enumerate(["Role", "Equipment", "Process", "Tips"], start=1): for iii, item in enumerate(["Role", "Equipment", "Process", "Tips"], start=1):
l = QLabel(item) label = QLabel(item)
l.setMaximumWidth(200) label.setMaximumWidth(200)
l.setMinimumWidth(200) label.setMinimumWidth(200)
self.layout.addWidget(l, 0, iii, alignment=Qt.AlignmentFlag.AlignRight) self.layout.addWidget(label, 0, iii, alignment=Qt.AlignmentFlag.AlignRight)
self.setLayout(self.layout) self.setLayout(self.layout)
def check_all(self): def check_all(self):

View File

@@ -1,7 +1,7 @@
""" """
A pane to show info e.g. cost reports and turnaround times. A pane to show info e.g. cost reports and turnaround times.
""" """
from datetime import date, datetime from datetime import date
from PyQt6.QtCore import QSignalBlocker from PyQt6.QtCore import QSignalBlocker
from PyQt6.QtWebEngineWidgets import QWebEngineView from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWidgets import QWidget, QGridLayout from PyQt6.QtWidgets import QWidget, QGridLayout
@@ -32,11 +32,9 @@ class InfoPane(QWidget):
@report_result @report_result
def update_data(self, *args, **kwargs): def update_data(self, *args, **kwargs):
report = Report() report = Report()
# self.start_date = self.datepicker.start_date.date().toPyDate()
# self.end_date = self.datepicker.end_date.date().toPyDate()
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()
logger.debug(f"Start date: {self.start_date}, End date: {self.end_date}") # logger.debug(f"Start date: {self.start_date}, End date: {self.end_date}")
if self.datepicker.start_date.date() > self.datepicker.end_date.date(): if self.datepicker.start_date.date() > self.datepicker.end_date.date():
lastmonth = self.datepicker.end_date.date().addDays(-31) lastmonth = self.datepicker.end_date.date().addDays(-31)
msg = f"Start date after end date is not allowed! Setting to {lastmonth.toString()}." msg = f"Start date after end date is not allowed! Setting to {lastmonth.toString()}."

View File

@@ -44,7 +44,7 @@ class StartEndDatePicker(QWidget):
class CheckableComboBox(QComboBox): class CheckableComboBox(QComboBox):
# once there is a checkState set, it is rendered # once there is a checkState set, it is rendered
# here we assume default Unchecked # here we assume default checked
def addItem(self, item, header: bool = False, start_checked: bool = True): def addItem(self, item, header: bool = False, start_checked: bool = True):
super(CheckableComboBox, self).addItem(item) super(CheckableComboBox, self).addItem(item)
@@ -64,8 +64,7 @@ class CheckableComboBox(QComboBox):
self.updated.emit() self.updated.emit()
def get_checked(self): def get_checked(self):
checked = [self.itemText(i) for i in range(self.count()) if self.itemChecked(i)] return [self.itemText(i) for i in range(self.count()) if self.itemChecked(i)]
return checked
class Pagifier(QWidget): class Pagifier(QWidget):

View File

@@ -1,7 +1,6 @@
""" """
A widget to handle adding/updating any database object. A widget to handle adding/updating any database object.
""" """
from copy import deepcopy
from datetime import date from datetime import date
from pprint import pformat from pprint import pformat
from typing import Any, Tuple from typing import Any, Tuple
@@ -11,7 +10,7 @@ from PyQt6.QtWidgets import (
QCheckBox QCheckBox
) )
from sqlalchemy import String, TIMESTAMP, INTEGER, FLOAT, JSON, BLOB from sqlalchemy import String, TIMESTAMP, INTEGER, FLOAT, JSON, BLOB
from sqlalchemy.orm import InstrumentedAttribute, ColumnProperty from sqlalchemy.orm import ColumnProperty
import logging import logging
from sqlalchemy.orm.relationships import _RelationshipDeclared from sqlalchemy.orm.relationships import _RelationshipDeclared
from tools import Report, report_result from tools import Report, report_result
@@ -23,18 +22,11 @@ class AddEdit(QDialog):
def __init__(self, parent, instance: Any | None = None, managers: set = set()): def __init__(self, parent, instance: Any | None = None, managers: set = set()):
super().__init__(parent) super().__init__(parent)
logger.debug(f"Managers: {managers}") # logger.debug(f"Managers: {managers}")
self.instance = instance self.instance = instance
self.object_type = instance.__class__ self.object_type = instance.__class__
# self.managers = deepcopy(managers)
self.managers = managers self.managers = managers
# if instance.level < 2: # logger.debug(f"Managers: {managers}")
# try:
# logger.debug(f"Parent instance: {self.parent().instance}")
# self.managers.add(self.parent().instance)
# except AttributeError:
# pass
logger.debug(f"Managers: {managers}")
self.layout = QGridLayout(self) self.layout = QGridLayout(self)
QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
self.buttonBox = QDialogButtonBox(QBtn) self.buttonBox = QDialogButtonBox(QBtn)
@@ -72,11 +64,11 @@ class AddEdit(QDialog):
report = Report() report = Report()
parsed = {result[0].strip(":"): result[1] for result in parsed = {result[0].strip(":"): result[1] for result in
[item.parse_form() for item in self.findChildren(EditProperty)] if result[0]} [item.parse_form() for item in self.findChildren(EditProperty)] if result[0]}
logger.debug(f"Parsed form: {parsed}") # logger.debug(f"Parsed form: {parsed}")
model = self.object_type.pydantic_model model = self.object_type.pydantic_model
logger.debug(f"Model type: {model.__name__}") # logger.debug(f"Model type: {model.__name__}")
if model.__name__ == "PydElastic": if model.__name__ == "PydElastic":
logger.debug(f"We have an elastic model.") # logger.debug(f"We have an elastic model.")
parsed['instance'] = self.instance parsed['instance'] = self.instance
# NOTE: Hand-off to pydantic model for validation. # NOTE: Hand-off to pydantic model for validation.
# NOTE: Also, why am I not just using the toSQL method here. I could write one for contacts. # NOTE: Also, why am I not just using the toSQL method here. I could write one for contacts.
@@ -120,7 +112,7 @@ class EditProperty(QWidget):
# logger.debug(self.parent().managers) # logger.debug(self.parent().managers)
for manager in self.parent().managers: for manager in self.parent().managers:
if self.name in manager.aliases: if self.name in manager.aliases:
logger.debug(f"Name: {self.name} is in aliases: {manager.aliases}") # logger.debug(f"Name: {self.name} is in aliases: {manager.aliases}")
choices = [manager.name] choices = [manager.name]
self.widget.setEnabled(False) self.widget.setEnabled(False)
break break
@@ -139,7 +131,7 @@ class EditProperty(QWidget):
self.widget.addItems(choices) self.widget.addItems(choices)
def column_property_set(self, column_property, value=None): def column_property_set(self, column_property, value=None):
logger.debug(f"Column Property: {column_property['class_attr'].expression} {column_property}, Value: {value}") # logger.debug(f"Column Property: {column_property['class_attr'].expression} {column_property}, Value: {value}")
match column_property['class_attr'].expression.type: match column_property['class_attr'].expression.type:
case String(): case String():
if value is None: if value is None:
@@ -184,7 +176,8 @@ class EditProperty(QWidget):
check = self.widget check = self.widget
except AttributeError: except AttributeError:
return None, None return None, None
match self.widget: # match self.widget
match check:
case QLineEdit(): case QLineEdit():
value = self.widget.text() value = self.widget.text()
case QDateEdit(): case QDateEdit():
@@ -198,5 +191,3 @@ class EditProperty(QWidget):
case _: case _:
value = None value = None
return self.objectName(), value return self.objectName(), value

View File

@@ -1,594 +0,0 @@
"""
Provides a screen for managing all attributes of a database object.
"""
import json, logging
from pprint import pformat
from typing import Any, List, Literal
from PyQt6.QtCore import QSortFilterProxyModel, Qt
from PyQt6.QtGui import QAction, QCursor
from PyQt6.QtWidgets import (
QLabel, QDialog,
QTableView, QWidget, QLineEdit, QGridLayout, QComboBox, QPushButton, QDialogButtonBox, QDateEdit, QMenu,
QDoubleSpinBox, QSpinBox, QCheckBox, QTextEdit, QVBoxLayout, QHBoxLayout
)
from sqlalchemy import String, TIMESTAMP, FLOAT, INTEGER, JSON, BLOB
from sqlalchemy.orm import InstrumentedAttribute
from sqlalchemy.orm.properties import ColumnProperty
from sqlalchemy.orm.relationships import _RelationshipDeclared
from pandas import DataFrame
from backend import db
from tools import check_object_in_manager
from .omni_search import SearchBox
from frontend.widgets.submission_table import pandasModel
logger = logging.getLogger(f"submissions.{__name__}")
class ManagerWindow(QDialog):
"""
Initially this is a window to manage Organization Contacts, but hope to abstract it more later.
"""
def __init__(self, parent,
object_type: Any,
extras: List[str],
manager: Any | None = None,
add_edit: Literal['add', 'edit'] = 'edit',
**kwargs):
super().__init__(parent)
self.class_object = self.original_type = object_type
self.add_edit = add_edit
# NOTE: Should I pass in an instance?
self.instance = None
if manager is None:
try:
self.manager = self.parent().instance
except AttributeError:
self.manager = None
else:
self.manager = manager
# logger.debug(f"Managers: {managers}")
self.extras = extras
self.context = kwargs
self.layout = QGridLayout(self)
QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
self.buttonBox = QDialogButtonBox(QBtn)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
self.setMinimumSize(600, 600)
sub_classes = ["Any"] + [cls.__name__ for cls in self.class_object.__subclasses__()]
if len(sub_classes) > 1:
self.sub_class = QComboBox(self)
self.sub_class.setObjectName("sub_class")
self.sub_class.addItems(sub_classes)
self.sub_class.currentTextChanged.connect(self.update_options)
self.sub_class.setEditable(False)
self.sub_class.setMinimumWidth(self.minimumWidth())
self.layout.addWidget(self.sub_class, 0, 0)
else:
self.sub_class = None
if self.add_edit == "edit":
self.options = QComboBox(self)
self.options.setObjectName("options")
self.update_options()
else:
self.update_data(initial=True)
self.setLayout(self.layout)
self.setWindowTitle(f"Manage {self.class_object.__name__} - Manager: {self.manager}")
def update_options(self) -> None:
"""
Changes form inputs based on sample type
"""
# logger.debug(f"Instance: {self.instance}")
if self.sub_class:
self.class_object = getattr(db, self.sub_class.currentText())
# logger.debug(f"From update options, managers: {self.managers}")
try:
query_kwargs = {self.parent().instance.query_alias: self.parent().instance}
except AttributeError as e:
# logger.debug(f"Couldn't set query kwargs due to: {e}")
query_kwargs = {}
# logger.debug(f"Query kwargs: {query_kwargs}")
options = [item.name for item in self.class_object.query(**query_kwargs)]
# logger.debug(f"self.class_object: {self.class_object}")
if self.instance:
try:
inserter = options.pop(options.index(self.instance.name))
except ValueError:
inserter = self.instance.name
options.insert(0, inserter)
self.options.clear()
self.options.addItems(options)
self.options.setEditable(False)
self.options.setMinimumWidth(self.minimumWidth())
self.layout.addWidget(self.options, 1, 0, 1, 1)
self.add_button = QPushButton("Add New")
self.layout.addWidget(self.add_button, 1, 1, 1, 1)
self.add_button.clicked.connect(self.add_new)
self.options.currentTextChanged.connect(self.update_data)
# logger.debug(f"Instance: {self.instance}")
self.update_data()
def update_data(self, initial: bool = False) -> None:
"""
Performs updating of widgets on first run and after options change.
Returns:
None
"""
# NOTE: Remove all old widgets.
deletes = [item for item in self.findChildren(EditProperty)] + \
[item for item in self.findChildren(EditRelationship)] + \
[item for item in self.findChildren(QDialogButtonBox)]
for item in deletes:
item.setParent(None)
# logger.debug(f"Current options text lower: {self.options.currentText().lower()}")
if self.add_edit == "edit" and initial:
# logger.debug(f"Querying with {self.options.currentText()}")
self.instance = self.class_object.query(name=self.options.currentText(), limit=1)
# logger.debug(f"Instance: {self.instance}")
if not self.instance:
self.instance = self.class_object()
# logger.debug(f"self.instance: {self.instance}")
fields = self.instance.omnigui_instance_dict
for key, field in fields.items():
try:
value = getattr(self.instance, key)
except AttributeError:
value = None
match field['class_attr'].property:
# NOTE: ColumnProperties will be directly edited.
case ColumnProperty():
# NOTE: field.property.expression.type gives db column type eg. STRING or TIMESTAMP
widget = EditProperty(self, key=key, column_type=field,
value=value)
# NOTE: RelationshipDeclareds will be given a list of existing related objects.
case _RelationshipDeclared():
if key != "submissions":
# NOTE: field.comparator.class_object.class_ gives the relationship class
widget = EditRelationship(self, key=key, class_object=field['class_attr'].comparator.entity.class_,
value=value)
else:
continue
case _:
continue
if widget:
self.layout.addWidget(widget, self.layout.rowCount(), 0, 1, 2)
# NOTE: Add OK|Cancel to bottom of dialog.
self.layout.addWidget(self.buttonBox, self.layout.rowCount(), 0, 1, 2)
def parse_form(self) -> Any:
"""
Returns the instance associated with this window.
Returns:
Any: The instance with updated fields.
"""
results = [item.parse_form() for item in self.findChildren(EditProperty)]
for result in results:
# logger.debug(f"Incoming result: {result}")
setattr(self.instance, result['field'], result['value'])
# logger.debug(f"Set result: {getattr(self.instance, result['field'])}")
results = [item.parse_form() for item in self.findChildren(EditRelationship)]
for result in results:
logger.debug(f"Incoming result: {result}")
if not getattr(self.instance, result['field']):
setattr(self.instance, result['field'], result['value'])
logger.debug(f"Set result: {getattr(self.instance, result['field'])}")
logger.debug(f"Instance coming from parsed form: {self.instance.__dict__}")
return self.instance
def add_new(self):
new_instance = self.class_object()
self.instance = new_instance
self.update_options()
def add_to_json(self, caller_child=None):
try:
name = caller_child.objectName()
except AttributeError:
name = "No Caller"
jsonedit = JsonEditScreen(parent=self, key=name)
if jsonedit.exec():
data = jsonedit.parse_form()
logger.debug(f"Data: {pformat(data)}")
current_value = getattr(self.instance, name)
if isinstance(jsonedit.json_field, dict):
value = data
elif isinstance(jsonedit.json_field, list):
if isinstance(data, list):
value = current_value + data
else:
value = current_value + [data]
setattr(self.instance, name, value)
def toggle_textedit(self, caller_child=None):
already_exists = self.findChildren(LargeTextEdit)
if not already_exists:
try:
name = caller_child.objectName()
except AttributeError:
name = "No Caller"
logger.debug(f"Name: {name}, instance: {self.instance}")
textedit = LargeTextEdit(parent=self, key=name)
self.layout.addWidget(textedit, 1, self.layout.columnCount(), self.layout.rowCount() - 1, 1)
data = getattr(self.instance, name)
logger.debug(f"Data: {data}")
data = json.dumps(data, indent=4)
textedit.widget.setText(data)
else:
for item in already_exists:
item.setParent(None)
item.destroy()
class EditProperty(QWidget):
def __init__(self, parent: ManagerWindow, key: str, column_type: Any, value):
super().__init__(parent)
self.label = QLabel(key.title().replace("_", " "))
self.layout = QGridLayout()
self.layout.addWidget(self.label, 0, 0, 1, 1)
self.setObjectName(key)
logger.debug(f"Column type for {key}: {column_type['class_attr'].property.expression.type}")
match column_type['class_attr'].property.expression.type:
case String():
self.widget = QLineEdit(self)
self.widget.setText(value)
case INTEGER():
if isinstance(column_type['instance_attr'], bool):
self.widget = QCheckBox()
self.widget.setChecked(value)
else:
if value is None:
value = 0
self.widget = QSpinBox()
self.widget.setMaximum(999)
self.widget.setValue(value)
case FLOAT():
if not value:
value = 0.0
self.widget = QDoubleSpinBox()
self.widget.setMaximum(999.99)
self.widget.setValue(value)
case TIMESTAMP():
self.widget = QDateEdit(self)
self.widget.setDate(value)
case JSON():
self.widget = JsonEditButton(parent=self, key=key)
self.widget.viewButton.clicked.connect(lambda: self.parent().toggle_textedit(self.widget))
self.widget.addButton.clicked.connect(lambda: self.parent().add_to_json(self.widget))
case BLOB():
self.widget = QLabel("BLOB Under construction")
case _:
self.widget = None
self.layout.addWidget(self.widget, 0, 1, 1, 3)
self.setLayout(self.layout)
def parse_form(self):
match self.widget:
case QLineEdit():
value = self.widget.text()
case QDateEdit():
value = self.widget.date()
case QSpinBox() | QDoubleSpinBox():
value = self.widget.value()
case QCheckBox():
value = self.widget.isChecked()
case _:
value = None
return dict(field=self.objectName(), value=value)
class EditRelationship(QWidget):
def __init__(self, parent, key: str, class_object: Any, value):
super().__init__(parent)
self.class_object = class_object #: The class of interest
self.setParent(parent)
# logger.debug(f"Edit relationship class_object: {self.class_object}")
self.label = QLabel(key.title().replace("_", " "))
self.setObjectName(key) #: key is the name of the relationship this represents
self.relationship = getattr(self.parent().instance.__class__,
key) #: relationship object for type differentiation
# logger.debug(f"self.relationship: {self.relationship}")
# logger.debug(f"Relationship uses list: {self.relationship.property.uselist}")
# NOTE: value is a database object in this case.
# logger.debug(f"Data for edit relationship: {self.data}")
self.widget = QTableView()
self.add_button = QPushButton("Add New")
self.add_button.clicked.connect(self.add_new)
self.existing_button = QPushButton("Add Existing")
self.existing_button.clicked.connect(self.add_existing)
# self.existing_button.setEnabled(self.class_object.level == 1)
if not isinstance(value, list):
if value is not None:
value = [value]
else:
value = []
self.data = value
checked_manager, is_primary = check_object_in_manager(self.parent().manager, self.objectName())
if checked_manager:
logger.debug(f"Checked manager for {self.objectName()}: {checked_manager}")
logger.debug(f"Omni will inherit: {self.class_object.omni_inheritable} from {self.parent().class_object}")
if checked_manager is not None and not self.data and self.objectName() in self.parent().class_object.omni_inheritable:
logger.debug(f"Setting {checked_manager} in self.data")
self.data = [checked_manager]
if not self.relationship.property.uselist:
self.add_button.setEnabled(False)
self.existing_button.setEnabled(False)
if is_primary:
self.widget.setEnabled(False)
self.layout = QGridLayout()
self.layout.addWidget(self.label, 0, 0, 1, 5)
self.layout.addWidget(self.widget, 1, 0, 1, 8)
self.layout.addWidget(self.add_button, 0, 6, 1, 1, alignment=Qt.AlignmentFlag.AlignRight)
self.layout.addWidget(self.existing_button, 0, 7, 1, 1, alignment=Qt.AlignmentFlag.AlignRight)
self.setLayout(self.layout)
self.set_data()
def update_buttons(self):
if not self.relationship.property.uselist and len(self.data) >= 1:
logger.debug(f"Property {self.relationship} doesn't use list and data is of length: {len(self.data)}")
self.add_button.setEnabled(False)
self.existing_button.setEnabled(False)
else:
self.add_button.setEnabled(True)
self.existing_button.setEnabled(True)
def parse_row(self, x):
context = {item: x.sibling(x.row(), self.df.columns.get_loc(item)).data() for item in self.df.columns}
try:
object = self.class_object.query(**context)
except KeyError:
object = None
self.widget.doubleClicked.disconnect()
self.add_edit(instance=object)
def add_new(self, instance: Any = None):
# NOTE: if an existing instance is not being edited, create a new instance
if not instance:
instance = self.class_object()
manager = self.parent().manager
# logger.debug(f"Managers going into add new: {managers}")
dlg = ManagerWindow(self.parent(), object_type=instance.__class__, extras=[], manager=manager, add_edit="add")
if dlg.exec():
new_instance = dlg.parse_form()
# NOTE: My custom __setattr__ should take care of any list problems.
self.parent().instance.__setattr__(self.objectName(), new_instance)
self.parent().update_data()
def add_existing(self):
dlg = SearchBox(self, object_type=self.class_object, returnable=True, extras=[])
if dlg.exec():
rows = dlg.return_selected_rows()
for row in rows:
# logger.debug(f"Querying with {row}")
instance = self.class_object.query(**row)
# NOTE: My custom __setattr__ should take care of any list problems.
self.parent().instance.__setattr__(self.objectName(), instance)
self.parent().update_data()
def set_data(self) -> None:
"""
sets data in model
"""
logger.debug(f"Self.data: {self.data}")
try:
records = [{k: v['instance_attr'] for k, v in item.omnigui_instance_dict.items()} for item in self.data]
except AttributeError:
records = []
# logger.debug(f"Records: {records}")
self.df = DataFrame.from_records(records)
try:
self.columns_of_interest = [dict(name=item, column=self.df.columns.get_loc(item)) for item in self.extras]
except (KeyError, AttributeError):
self.columns_of_interest = []
try:
self.df['id'] = self.df['id'].apply(str)
self.df['id'] = self.df['id'].str.zfill(4)
except KeyError as e:
logger.error(f"Could not alter id to string due to KeyError: {e}")
proxy_model = QSortFilterProxyModel()
proxy_model.setSourceModel(pandasModel(self.df))
self.widget.setModel(proxy_model)
self.widget.resizeColumnsToContents()
self.widget.resizeRowsToContents()
self.widget.setSortingEnabled(True)
self.widget.doubleClicked.connect(self.parse_row)
self.update_buttons()
def contextMenuEvent(self, event):
"""
Creates actions for right click menu events.
Args:
event (_type_): the item of interest
"""
if not self.widget.isEnabled():
logger.warning(f"{self.objectName()} is disabled.")
return
id = self.widget.selectionModel().currentIndex()
# NOTE: the overly complicated {column_name: row_value} dictionary construction
row_data = {self.df.columns[column]: self.widget.model().index(id.row(), column).data() for column in
range(self.widget.model().columnCount())}
logger.debug(f"Row data: {row_data}")
logger.debug(f"Attempting to grab {self.objectName()} from {self.parent().instance}")
object = getattr(self.parent().instance, self.objectName())
if isinstance(object, list):
object = next((item for item in object if item.check_all_attributes(attributes=row_data)), None)
logger.debug(f"Object of interest: {object}")
# logger.debug(object)
self.menu = QMenu(self)
try:
remove_action = QAction(f"Remove {object.name}", self)
except AttributeError:
remove_action = QAction(f"Remove object", self)
remove_action.triggered.connect(lambda: self.remove_item(object=object))
self.menu.addAction(remove_action)
try:
edit_action = QAction(f"Edit {object.name}", self)
except AttributeError:
edit_action = QAction(f"Edit object", self)
edit_action.triggered.connect(lambda: self.add_new(instance=object))
self.menu.addAction(edit_action)
self.menu.popup(QCursor.pos())
def remove_item(self, object):
logger.debug(f"Attempting to remove {object} from {self.parent().instance.__dict__}")
editor = getattr(self.parent().instance, self.objectName().lower())
logger.debug(f"Editor: {editor}")
if object == self.parent().manager:
logger.error(f"Can't remove manager object.")
return
try:
self.data.remove(object)
except (AttributeError, ValueError) as e:
logger.error(f"Could remove object from self.data due to: {e}")
self.data = []
try:
logger.debug(f"Using remove technique")
editor.remove(object)
except AttributeError as e:
logger.error(f"Remove failed using set to None for {self.objectName().lower()}.")
setattr(self.parent().instance, self.objectName().lower(), None)
except ValueError as e:
logger.error(f"Remove failed for {self.objectName().lower()} due to {e}.")
self.parent().instance.save()
self.set_data()
def parse_form(self):
return dict(field=self.objectName(), value=self.data)
class JsonEditButton(QWidget):
def __init__(self, parent, key: str):
super().__init__(parent)
self.setParent(parent)
self.setObjectName(key)
self.addButton = QPushButton("Add Entry", parent=self)
self.viewButton = QPushButton("View >>>", parent=self)
self.layout = QGridLayout()
self.layout.addWidget(self.addButton, 0, 0)
self.layout.addWidget(self.viewButton, 0, 1)
self.setLayout(self.layout)
class JsonEditScreen(QDialog):
def __init__(self, parent, key: str):
super().__init__(parent)
self.class_obj = parent.class_object
self.layout = QGridLayout()
self.setWindowTitle(key)
self.json_field = self.class_obj.json_edit_fields
match self.json_field:
case dict():
for key, value in self.json_field.items():
logger.debug(f"Key: {key}, Value: {value}")
row = self.layout.rowCount()
self.layout.addWidget(QLabel(key), row, 0)
match value:
case "int":
self.widget = QSpinBox()
case "str":
self.widget = QLineEdit()
case dict():
self.widget = DictionaryJsonSubEdit(parent=self, key=key, dic=value)
case _:
continue
self.widget.setObjectName(key)
self.layout.addWidget(self.widget, row, 1)
QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
self.buttonBox = QDialogButtonBox(QBtn)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
self.layout.addWidget(self.buttonBox, self.layout.rowCount(), 0, 1, 2)
self.setLayout(self.layout)
def parse_form(self):
widgets = [item for item in self.findChildren(QWidget) if item.objectName() in self.json_field.keys()]
logger.debug(f"Widgets: {widgets}")
logger.debug(type(self.json_field))
if isinstance(self.json_field, dict):
output = {}
elif isinstance(self.json_field, list):
output = []
else:
raise ValueError(f"Inappropriate data type: {type(self.json_field)}")
for widget in widgets:
logger.debug(f"JsonEditScreen Widget: {widget}")
key = widget.objectName()
match widget:
case QSpinBox():
value = widget.value()
case QLineEdit():
value = widget.text()
case DictionaryJsonSubEdit():
value = widget.parse_form()
case _:
continue
if isinstance(self.json_field, dict):
output[key] = value
elif isinstance(self.json_field, list):
if isinstance(value, list):
output += value
else:
output.append(value)
else:
raise ValueError(f"Inappropriate data type: {type(self.json_field)}")
return output
class DictionaryJsonSubEdit(QWidget):
def __init__(self, parent, key, dic: dict):
super().__init__(parent)
self.layout = QHBoxLayout()
self.setObjectName(key)
self.data = dic
for key, value in self.data.items():
self.layout.addWidget(QLabel(key))
match value:
case "int":
self.widget = QSpinBox()
case "str":
self.widget = QLineEdit()
case dict():
self.widget = DictionaryJsonSubEdit(parent, key=key, dic=value)
self.widget.setObjectName(key)
self.layout.addWidget(self.widget)
self.setLayout(self.layout)
def parse_form(self):
widgets = [item for item in self.findChildren(QWidget) if item.objectName() in self.data.keys()]
logger.debug(f"Widgets: {widgets}")
output = {}
for widget in widgets:
logger.debug(f"DictionaryJsonSubEdit Widget: {widget}")
key = widget.objectName()
match widget:
case QSpinBox():
value = widget.value()
case QLineEdit():
value = widget.text()
case DictionaryJsonSubEdit():
value = widget.parse_form()
case _:
continue
output[key] = value
return output
class LargeTextEdit(QWidget):
def __init__(self, parent, key: str):
super().__init__(parent)
self.setParent(parent)
self.setObjectName(key)
self.widget = QTextEdit()
self.layout = QVBoxLayout()
self.layout.addWidget(self.widget)
self.setLayout(self.layout)

View File

@@ -358,7 +358,6 @@ class EditRelationship(QWidget):
obj = getattr(self.parent().omni_object, self.objectName()) obj = getattr(self.parent().omni_object, self.objectName())
if isinstance(obj, list): if isinstance(obj, list):
logger.debug(f"This is a list") logger.debug(f"This is a list")
# obj = obj[index]
try: try:
# NOTE: Okay, this will not work for editing, since by definition not all attributes will line up. # NOTE: Okay, this will not work for editing, since by definition not all attributes will line up.
# NOTE: Set items to search by in the Omni object itself? # NOTE: Set items to search by in the Omni object itself?
@@ -389,7 +388,7 @@ class EditRelationship(QWidget):
""" """
sets data in model sets data in model
""" """
logger.debug(f"Self.data: {self.data}") # logger.debug(f"Self.data: {self.data}")
try: try:
records = [item.to_dataframe_dict() for item in self.data] records = [item.to_dataframe_dict() for item in self.data]
except AttributeError: except AttributeError:

View File

@@ -172,7 +172,7 @@ class SearchResults(QTableView):
self.extras = extras + [item for item in deepcopy(self.object_type.searchables)] self.extras = extras + [item for item in deepcopy(self.object_type.searchables)]
except AttributeError: except AttributeError:
self.extras = extras self.extras = extras
logger.debug(f"Extras: {self.extras}") # logger.debug(f"Extras: {self.extras}")
def setData(self, df: DataFrame) -> None: def setData(self, df: DataFrame) -> None:
""" """

View File

@@ -1,14 +1,11 @@
import logging import logging
from pathlib import Path from pathlib import Path
from typing import List, Generator from typing import List
from PyQt6.QtCore import Qt, pyqtSlot from PyQt6.QtCore import Qt, pyqtSlot
from PyQt6.QtWebChannel import QWebChannel from PyQt6.QtWebChannel import QWebChannel
from PyQt6.QtWebEngineWidgets import QWebEngineView from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWidgets import (QDialog, QPushButton, QVBoxLayout, from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QGridLayout
QDialogButtonBox, QTextEdit, QGridLayout)
from backend.validators import PydSubmission from backend.validators import PydSubmission
from tools import get_application_from_parent, jinja_template_loading from tools import get_application_from_parent, jinja_template_loading
@@ -16,6 +13,7 @@ env = jinja_template_loading()
logger = logging.getLogger(f"submissions.{__name__}") logger = logging.getLogger(f"submissions.{__name__}")
class SampleChecker(QDialog): class SampleChecker(QDialog):
def __init__(self, parent, title:str, pyd: PydSubmission): def __init__(self, parent, title:str, pyd: PydSubmission):
@@ -32,7 +30,6 @@ class SampleChecker(QDialog):
self.channel = QWebChannel() self.channel = QWebChannel()
self.channel.registerObject('backend', self) self.channel.registerObject('backend', self)
# NOTE: Used to maintain javascript functions. # NOTE: Used to maintain javascript functions.
# self.webview.page().setWebChannel(self.channel)
template = env.get_template("sample_checker.html") template = env.get_template("sample_checker.html")
template_path = Path(template.environment.loader.__getattribute__("searchpath")[0]) template_path = Path(template.environment.loader.__getattribute__("searchpath")[0])
with open(template_path.joinpath("css", "styles.css"), "r") as f: with open(template_path.joinpath("css", "styles.css"), "r") as f:
@@ -72,7 +69,3 @@ class SampleChecker(QDialog):
sample['color'] = "black" sample['color'] = "black"
output.append(sample) output.append(sample)
return output return output

View File

@@ -71,7 +71,7 @@ class SubmissionFormContainer(QWidget):
self.setStyleSheet('background-color: light grey;') self.setStyleSheet('background-color: light grey;')
self.setAcceptDrops(True) self.setAcceptDrops(True)
# NOTE: if import_drag is emitted, importSubmission will fire # NOTE: if import_drag is emitted, importSubmission will fire
self.import_drag.connect(self.importSubmission) self.import_drag.connect(lambda fname: self.import_submission_function(fname=fname))
def dragEnterEvent(self, event): def dragEnterEvent(self, event):
""" """
@@ -90,17 +90,6 @@ class SubmissionFormContainer(QWidget):
self.app.last_dir = fname.parent self.app.last_dir = fname.parent
self.import_drag.emit(fname) self.import_drag.emit(fname)
@report_result
def importSubmission(self, fname: Path | None = None):
"""
import submission from excel sheet into form
"""
self.app.raise_()
self.app.activateWindow()
report = Report()
self.import_submission_function(fname)
return report
@report_result @report_result
def import_submission_function(self, fname: Path | None = None) -> Report: def import_submission_function(self, fname: Path | None = None) -> Report:
""" """
@@ -112,6 +101,8 @@ class SubmissionFormContainer(QWidget):
Returns: Returns:
Report: Object to give results of import. Report: Object to give results of import.
""" """
self.app.raise_()
self.app.activateWindow()
logger.info(f"\n\nStarting Import...\n\n") logger.info(f"\n\nStarting Import...\n\n")
report = Report() report = Report()
# NOTE: Clear any previous forms. # NOTE: Clear any previous forms.
@@ -436,7 +427,7 @@ class SubmissionFormWidget(QWidget):
if field is not None: if field is not None:
info[field] = value info[field] = value
self.pyd.reagents = reagents self.pyd.reagents = reagents
logger.debug(f"Reagents from form: {reagents}") # logger.debug(f"Reagents from form: {reagents}")
for item in self.recover: for item in self.recover:
if hasattr(self, item): if hasattr(self, item):
value = getattr(self, item) value = getattr(self, item)
@@ -446,6 +437,7 @@ class SubmissionFormWidget(QWidget):
report.add_result(report) report.add_result(report)
return report return report
class InfoItem(QWidget): class InfoItem(QWidget):
def __init__(self, parent: QWidget, key: str, value: dict, submission_type: str | SubmissionType | None = None, def __init__(self, parent: QWidget, key: str, value: dict, submission_type: str | SubmissionType | None = None,
@@ -691,7 +683,6 @@ class SubmissionFormWidget(QWidget):
if new: if new:
dlg = QuestionAsker(title=f"Add {lot}?", dlg = QuestionAsker(title=f"Add {lot}?",
message=f"Couldn't find reagent type {self.reagent.role}: {lot} in the database.\n\nWould you like to add it?") message=f"Couldn't find reagent type {self.reagent.role}: {lot} in the database.\n\nWould you like to add it?")
if dlg.exec(): if dlg.exec():
wanted_reagent = self.parent.parent().add_reagent(instance=wanted_reagent) wanted_reagent = self.parent.parent().add_reagent(instance=wanted_reagent)
return wanted_reagent, report return wanted_reagent, report

View File

@@ -40,7 +40,7 @@ class Summary(InfoPane):
None None
""" """
super().update_data() super().update_data()
orgs = [self.org_select.itemText(i) for i in range(self.org_select.count()) if self.org_select.itemChecked(i)] orgs = self.org_select.get_checked()
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)
if self.report_obj.subs: if self.report_obj.subs:

View File

@@ -27,6 +27,7 @@ from sqlalchemy.exc import IntegrityError as sqlalcIntegrityError
from pytz import timezone as tz from pytz import timezone as tz
from functools import wraps from functools import wraps
timezone = tz("America/Winnipeg") timezone = tz("America/Winnipeg")
logger = logging.getLogger(f"submissions.{__name__}") logger = logging.getLogger(f"submissions.{__name__}")
@@ -248,7 +249,6 @@ def timer(func):
func (__function__): incoming function func (__function__): incoming function
""" """
@wraps(func) @wraps(func)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
start_time = time.perf_counter() start_time = time.perf_counter()
@@ -257,7 +257,6 @@ def timer(func):
run_time = end_time - start_time run_time = end_time - start_time
print(f"Finished {func.__name__}() in {run_time:.4f} secs") print(f"Finished {func.__name__}() in {run_time:.4f} secs")
return value return value
return wrapper return wrapper
@@ -483,12 +482,10 @@ def setup_lookup(func):
elif v is not None: elif v is not None:
sanitized_kwargs[k] = v sanitized_kwargs[k] = v
return func(*args, **sanitized_kwargs) return func(*args, **sanitized_kwargs)
return wrapper return wrapper
def check_object_in_manager(manager: list, object_name: object) -> Tuple[Any, bool]: def check_object_in_manager(manager: list, object_name: object) -> Tuple[Any, bool]:
# for manager in managers:
if manager is None: if manager is None:
return None, False return None, False
# logger.debug(f"Manager: {manager}, aliases: {manager.aliases}, Key: {object_name}") # logger.debug(f"Manager: {manager}, aliases: {manager.aliases}, Key: {object_name}")
@@ -535,6 +532,7 @@ def get_application_from_parent(widget):
class Result(BaseModel, arbitrary_types_allowed=True): class Result(BaseModel, arbitrary_types_allowed=True):
owner: str = Field(default="", validate_default=True) owner: str = Field(default="", validate_default=True)
code: int = Field(default=0) code: int = Field(default=0)
msg: str | Exception msg: str | Exception
@@ -639,7 +637,6 @@ def rreplace(s: str, old: str, new: str) -> str:
def list_sort_dict(input_dict: dict, sort_list: list) -> dict: def list_sort_dict(input_dict: dict, sort_list: list) -> dict:
# sort_list.reverse()
sort_list = reversed(sort_list) sort_list = reversed(sort_list)
for item in sort_list: for item in sort_list:
try: try:
@@ -661,7 +658,10 @@ def remove_key_from_list_of_dicts(input_list: list, key: str) -> list:
list: List of updated dictionaries list: List of updated dictionaries
""" """
for item in input_list: for item in input_list:
del item[key] try:
del item[key]
except KeyError:
continue
return input_list return input_list
@@ -688,6 +688,7 @@ def super_splitter(ins_str: str, substring: str, idx: int) -> str:
try: try:
return ins_str.split(substring)[idx] return ins_str.split(substring)[idx]
except IndexError: except IndexError:
logger.error(f"Index of split {idx} not found.")
return ins_str return ins_str
@@ -767,7 +768,6 @@ def under_development(func):
Result(owner=func.__str__(), code=1, msg=error_msg, Result(owner=func.__str__(), code=1, msg=error_msg,
status="warning")) status="warning"))
return report return report
return wrapper return wrapper
@@ -856,7 +856,6 @@ def create_holidays_for_year(year: int | None = None) -> List[date]:
offset = -d.weekday() # weekday == 0 means Monday offset = -d.weekday() # weekday == 0 means Monday
output = d + timedelta(offset) output = d + timedelta(offset)
return output.date() return output.date()
if not year: if not year:
year = date.today().year year = date.today().year
# NOTE: Includes New Year's day for next year. # NOTE: Includes New Year's day for next year.
@@ -886,7 +885,7 @@ def check_dictionary_inclusion_equality(listo: List[dict] | dict, dicto: dict) -
Returns: Returns:
bool: True if dicto is equal to any dictionary in the list. bool: True if dicto is equal to any dictionary in the list.
""" """
logger.debug(f"Comparing: {listo} and {dicto}") # logger.debug(f"Comparing: {listo} and {dicto}")
if isinstance(dicto, list) and isinstance(listo, list): if isinstance(dicto, list) and isinstance(listo, list):
return listo == dicto return listo == dicto
elif isinstance(dicto, dict) and isinstance(listo, dict): elif isinstance(dicto, dict) and isinstance(listo, dict):
@@ -957,7 +956,6 @@ class Settings(BaseSettings, extra="allow"):
settings_path = None settings_path = None
if settings_path is None: if settings_path is None:
# NOTE: Check user .config/submissions directory # NOTE: Check user .config/submissions directory
# if CONFIGDIR.joinpath("config.yml").exists():
if cls.configdir.joinpath("config.yml").exists(): if cls.configdir.joinpath("config.yml").exists():
settings_path = cls.configdir.joinpath("config.yml") settings_path = cls.configdir.joinpath("config.yml")
# NOTE: Check user .submissions directory # NOTE: Check user .submissions directory
@@ -969,8 +967,6 @@ class Settings(BaseSettings, extra="allow"):
settings_path = Path(sys._MEIPASS).joinpath("files", "config.yml") settings_path = Path(sys._MEIPASS).joinpath("files", "config.yml")
else: else:
settings_path = project_path.joinpath('src', 'config.yml') settings_path = project_path.joinpath('src', 'config.yml')
# with open(settings_path, "r") as dset:
# default_settings = yaml.load(dset, Loader=yaml.Loader)
else: else:
# NOTE: check if user defined path is directory # NOTE: check if user defined path is directory
if settings_path.is_dir(): if settings_path.is_dir():
@@ -1285,3 +1281,4 @@ class Settings(BaseSettings, extra="allow"):
ctx = Settings() ctx = Settings()