Debugged reports.
This commit is contained in:
@@ -1,3 +1,7 @@
|
|||||||
|
# 202503.05
|
||||||
|
|
||||||
|
- Added concentrations chart tab.
|
||||||
|
|
||||||
# 202503.04
|
# 202503.04
|
||||||
|
|
||||||
- Kit editor debugging.
|
- Kit editor debugging.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'''
|
"""
|
||||||
Contains functions for generating summary reports
|
Contains functions for generating summary reports
|
||||||
'''
|
"""
|
||||||
import itertools
|
import itertools
|
||||||
import sys
|
import sys
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
@@ -10,7 +10,7 @@ from pathlib import Path
|
|||||||
from datetime import date
|
from datetime import date
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from backend.db.models import BasicSubmission, IridaControl
|
from backend.db.models import BasicSubmission, IridaControl
|
||||||
from tools import jinja_template_loading, get_first_blank_df_row, row_map, ctx
|
from tools import jinja_template_loading, get_first_blank_df_row, row_map
|
||||||
from PyQt6.QtWidgets import QWidget
|
from PyQt6.QtWidgets import QWidget
|
||||||
from openpyxl.worksheet.worksheet import Worksheet
|
from openpyxl.worksheet.worksheet import Worksheet
|
||||||
|
|
||||||
@@ -36,6 +36,7 @@ class ReportArchetype(object):
|
|||||||
filename = Path(filename)
|
filename = Path(filename)
|
||||||
filename = filename.absolute()
|
filename = filename.absolute()
|
||||||
self.writer = ExcelWriter(filename.with_suffix(".xlsx"), engine='openpyxl')
|
self.writer = ExcelWriter(filename.with_suffix(".xlsx"), engine='openpyxl')
|
||||||
|
self.df.index += 1
|
||||||
self.df.to_excel(self.writer, sheet_name=self.sheet_name)
|
self.df.to_excel(self.writer, sheet_name=self.sheet_name)
|
||||||
self.writer.close()
|
self.writer.close()
|
||||||
|
|
||||||
|
|||||||
@@ -22,86 +22,6 @@ class BaseOmni(BaseModel):
|
|||||||
def aliases(cls):
|
def aliases(cls):
|
||||||
return cls.class_object.aliases
|
return cls.class_object.aliases
|
||||||
|
|
||||||
# NOTE: Okay, this will not work for editing, since by definition not all attributes will line up.
|
|
||||||
# def check_all_attributes(self, attributes: dict) -> bool:
|
|
||||||
# """
|
|
||||||
# Checks this instance against a dictionary of attributes to determine if they are a match.
|
|
||||||
#
|
|
||||||
# Args:
|
|
||||||
# attributes (dict): A dictionary of attributes to be check for equivalence
|
|
||||||
#
|
|
||||||
# Returns:
|
|
||||||
# bool: If a single unequivocal value is found will be false, else true.
|
|
||||||
# """
|
|
||||||
# logger.debug(f"Incoming attributes: {attributes}")
|
|
||||||
# for key, value in attributes.items():
|
|
||||||
# logger.debug(f"Comparing value class: {value.__class__} to omni class")
|
|
||||||
# if isinstance(value, str):
|
|
||||||
# try:
|
|
||||||
# check = value.lower() == "none"
|
|
||||||
# except AttributeError:
|
|
||||||
# continue
|
|
||||||
# if check:
|
|
||||||
# value = None
|
|
||||||
# logger.debug(f"Attempting to grab attribute: {key}")
|
|
||||||
# try:
|
|
||||||
# self_value = getattr(self, key)
|
|
||||||
# class_attr = getattr(self.class_object, key)
|
|
||||||
# except AttributeError:
|
|
||||||
# continue
|
|
||||||
# try:
|
|
||||||
# logger.debug(f"Check if {self_value.__class__} is subclass of {BaseOmni}")
|
|
||||||
# check = issubclass(self_value.__class__, BaseOmni)
|
|
||||||
# except TypeError as e:
|
|
||||||
# logger.error(f"Couldn't check if {self_value.__class__} is subclass of {BaseOmni} due to {e}")
|
|
||||||
# check = False
|
|
||||||
# if check:
|
|
||||||
# logger.debug(f"Checking for subclass name.")
|
|
||||||
# self_value = self_value.name
|
|
||||||
# try:
|
|
||||||
# logger.debug(f"Check if {value.__class__} is subclass of {BaseOmni}")
|
|
||||||
# check = issubclass(value.__class__, BaseOmni)
|
|
||||||
# except TypeError as e:
|
|
||||||
# logger.error(f"Couldn't check if {value.__class__} is subclass of {BaseOmni} due to {e}")
|
|
||||||
# check = False
|
|
||||||
# if check:
|
|
||||||
# logger.debug(f"Checking for subclass name.")
|
|
||||||
# value = value.name
|
|
||||||
# logger.debug(f"Self value: {self_value}, class attr: {class_attr} of type: {type(class_attr)}")
|
|
||||||
# if isinstance(class_attr, property):
|
|
||||||
# filter = "property"
|
|
||||||
# else:
|
|
||||||
# filter = class_attr.property
|
|
||||||
# match filter:
|
|
||||||
# case ColumnProperty():
|
|
||||||
# match class_attr.type:
|
|
||||||
# case INTEGER():
|
|
||||||
# if value.lower() == "true":
|
|
||||||
# value = 1
|
|
||||||
# elif value.lower() == "false":
|
|
||||||
# value = 0
|
|
||||||
# else:
|
|
||||||
# value = int(value)
|
|
||||||
# case FLOAT():
|
|
||||||
# value = float(value)
|
|
||||||
# case "property":
|
|
||||||
# pass
|
|
||||||
# case _RelationshipDeclared():
|
|
||||||
# logger.debug(f"Checking relationship value: {self_value}")
|
|
||||||
# try:
|
|
||||||
# self_value = self_value.name
|
|
||||||
# except AttributeError:
|
|
||||||
# pass
|
|
||||||
# if class_attr.property.uselist:
|
|
||||||
# self_value = self_value.__str__()
|
|
||||||
# logger.debug(
|
|
||||||
# f"Checking self_value {self_value} of type {type(self_value)} against attribute {value} of type {type(value)}")
|
|
||||||
# if self_value != value:
|
|
||||||
# output = False
|
|
||||||
# logger.debug(f"Value {key} is False, returning.")
|
|
||||||
# return output
|
|
||||||
# return True
|
|
||||||
|
|
||||||
def check_all_attributes(self, attributes: dict) -> bool:
|
def check_all_attributes(self, attributes: dict) -> bool:
|
||||||
logger.debug(f"Incoming attributes: {attributes}")
|
logger.debug(f"Incoming attributes: {attributes}")
|
||||||
attributes = {k : v for k, v in attributes.items() if k in self.list_searchables.keys()}
|
attributes = {k : v for k, v in attributes.items() if k in self.list_searchables.keys()}
|
||||||
@@ -122,7 +42,6 @@ class BaseOmni(BaseModel):
|
|||||||
logger.debug("Everything checks out, these are the same object.")
|
logger.debug("Everything checks out, these are the same object.")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def __setattr__(self, key, value):
|
def __setattr__(self, key, value):
|
||||||
try:
|
try:
|
||||||
class_value = getattr(self.class_object, key)
|
class_value = getattr(self.class_object, key)
|
||||||
@@ -395,6 +314,13 @@ class OmniKitTypeReagentRoleAssociation(BaseOmni):
|
|||||||
return {}
|
return {}
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
@field_validator("required", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def rescue_required_none(cls, value):
|
||||||
|
if not value:
|
||||||
|
value = 1
|
||||||
|
return value
|
||||||
|
|
||||||
def __init__(self, instance_object: Any, **data):
|
def __init__(self, instance_object: Any, **data):
|
||||||
super().__init__(**data)
|
super().__init__(**data)
|
||||||
self.instance_object = instance_object
|
self.instance_object = instance_object
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Construct turnaround time charts
|
Construct BC control concentration charts
|
||||||
"""
|
"""
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from . import CustomFigure
|
from . import CustomFigure
|
||||||
@@ -21,17 +21,15 @@ class ConcentrationsChart(CustomFigure):
|
|||||||
super().__init__(df=df, modes=modes, settings=settings)
|
super().__init__(df=df, modes=modes, settings=settings)
|
||||||
self.df = df
|
self.df = df
|
||||||
self.construct_chart()
|
self.construct_chart()
|
||||||
# if threshold:
|
|
||||||
# self.add_hline(y=threshold)
|
|
||||||
self.update_layout(showlegend=False)
|
self.update_layout(showlegend=False)
|
||||||
|
|
||||||
def construct_chart(self, df: pd.DataFrame | None = None):
|
def construct_chart(self, df: pd.DataFrame | None = None):
|
||||||
if df:
|
if df:
|
||||||
self.df = df
|
self.df = df
|
||||||
# logger.debug(f"Constructing concentration chart with df:\n{self.df}")
|
|
||||||
try:
|
try:
|
||||||
self.df = self.df[self.df.concentration.notnull()]
|
self.df = self.df[self.df.concentration.notnull()]
|
||||||
self.df = self.df.sort_values(['submitted_date', 'submission'], ascending=[True, True]).reset_index(drop=True)
|
self.df = self.df.sort_values(['submitted_date', 'submission'], ascending=[True, True]).reset_index(
|
||||||
|
drop=True)
|
||||||
self.df = self.df.reset_index().rename(columns={"index": "idx"})
|
self.df = self.df.reset_index().rename(columns={"index": "idx"})
|
||||||
# logger.debug(f"DF after changes:\n{self.df}")
|
# logger.debug(f"DF after changes:\n{self.df}")
|
||||||
scatter = px.scatter(data_frame=self.df, x='submission', y="concentration",
|
scatter = px.scatter(data_frame=self.df, x='submission', y="concentration",
|
||||||
@@ -41,8 +39,6 @@ class ConcentrationsChart(CustomFigure):
|
|||||||
except (ValueError, AttributeError) as e:
|
except (ValueError, AttributeError) as e:
|
||||||
logger.error(f"Error constructing chart: {e}")
|
logger.error(f"Error constructing chart: {e}")
|
||||||
scatter = px.scatter()
|
scatter = px.scatter()
|
||||||
# logger.debug(f"Scatter data: {scatter.data}")
|
|
||||||
# self.add_traces(scatter.data)
|
|
||||||
# NOTE: For some reason if data is allowed to sort itself it leads to wrong ordering of x axis.
|
# NOTE: For some reason if data is allowed to sort itself it leads to wrong ordering of x axis.
|
||||||
traces = sorted(scatter.data, key=itemgetter("name"))
|
traces = sorted(scatter.data, key=itemgetter("name"))
|
||||||
for trace in traces:
|
for trace in traces:
|
||||||
@@ -60,6 +56,9 @@ class ConcentrationsChart(CustomFigure):
|
|||||||
tickmode='array',
|
tickmode='array',
|
||||||
tickvals=tickvals,
|
tickvals=tickvals,
|
||||||
ticktext=ticklabels,
|
ticktext=ticklabels,
|
||||||
|
),
|
||||||
|
yaxis=dict(
|
||||||
|
rangemode="nonnegative"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.update_traces(marker={'size': 15})
|
self.update_traces(marker={'size': 15})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Pane showing turnaround time summary.
|
Pane showing BC control concentrations summary.
|
||||||
"""
|
"""
|
||||||
from PyQt6.QtWidgets import QWidget, QPushButton, QComboBox, QLabel
|
from PyQt6.QtWidgets import QWidget, QPushButton
|
||||||
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
|
||||||
@@ -22,12 +22,6 @@ class Concentrations(InfoPane):
|
|||||||
self.layout.addWidget(self.export_button, 0, 3, 1, 1)
|
self.layout.addWidget(self.export_button, 0, 3, 1, 1)
|
||||||
self.fig = None
|
self.fig = None
|
||||||
self.report_object = None
|
self.report_object = None
|
||||||
# self.submission_typer = QComboBox(self)
|
|
||||||
# subs = ["All"] + [item.name for item in SubmissionType.query()]
|
|
||||||
# self.submission_typer.addItems(subs)
|
|
||||||
# self.layout.addWidget(QLabel("Submission Type"), 1, 0, 1, 1)
|
|
||||||
# self.layout.addWidget(self.submission_typer, 1, 1, 1, 3)
|
|
||||||
# self.submission_typer.currentTextChanged.connect(self.update_data)
|
|
||||||
self.update_data()
|
self.update_data()
|
||||||
|
|
||||||
def update_data(self) -> None:
|
def update_data(self) -> None:
|
||||||
@@ -40,17 +34,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)
|
||||||
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)
|
||||||
# if self.submission_typer.currentText() == "All":
|
|
||||||
# submission_type = None
|
|
||||||
# subtype_obj = None
|
|
||||||
# else:
|
|
||||||
# submission_type = self.submission_typer.currentText()
|
|
||||||
# subtype_obj = SubmissionType.query(name = submission_type)
|
|
||||||
# self.report_obj = ConcentrationMaker(start_date=self.start_date, end_date=self.end_date)#, submission_type=submission_type)
|
|
||||||
self.report_obj = ConcentrationMaker(**chart_settings)
|
self.report_obj = ConcentrationMaker(**chart_settings)
|
||||||
# if subtype_obj:
|
|
||||||
# threshold = subtype_obj.defaults['turnaround_time'] + 0.5
|
|
||||||
# else:
|
|
||||||
# threshold = None
|
|
||||||
self.fig = ConcentrationsChart(df=self.report_obj.df, settings=chart_settings, modes=[], months=months)
|
self.fig = ConcentrationsChart(df=self.report_obj.df, settings=chart_settings, modes=[], months=months)
|
||||||
self.webview.setHtml(self.fig.html)
|
self.webview.setHtml(self.fig.html)
|
||||||
|
|||||||
Reference in New Issue
Block a user