Allow for grabbing of single kit if only one exists for submission type.
This commit is contained in:
@@ -683,6 +683,11 @@ class SubmissionType(BaseClass):
|
|||||||
"""
|
"""
|
||||||
return f"<SubmissionType({self.name})>"
|
return f"<SubmissionType({self.name})>"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def retrieve_template_file(cls):
|
||||||
|
submission_type = cls.query(name="Bacterial Culture")
|
||||||
|
return submission_type.template_file
|
||||||
|
|
||||||
def get_template_file_sheets(self) -> List[str]:
|
def get_template_file_sheets(self) -> List[str]:
|
||||||
logger.debug(f"Submission type to get sheets for: {self.name}")
|
logger.debug(f"Submission type to get sheets for: {self.name}")
|
||||||
"""
|
"""
|
||||||
@@ -779,6 +784,12 @@ class SubmissionType(BaseClass):
|
|||||||
tmap = {}
|
tmap = {}
|
||||||
yield item.tip_role.name, tmap
|
yield item.tip_role.name, tmap
|
||||||
|
|
||||||
|
def get_default_kit(self) -> KitType | None:
|
||||||
|
if len(self.kit_types) == 1:
|
||||||
|
return self.kit_types[0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def get_equipment(self, extraction_kit: str | KitType | None = None) -> Generator['PydEquipmentRole', None, None]:
|
def get_equipment(self, extraction_kit: str | KitType | None = None) -> Generator['PydEquipmentRole', None, None]:
|
||||||
"""
|
"""
|
||||||
Returns PydEquipmentRole of all equipment associated with this SubmissionType
|
Returns PydEquipmentRole of all equipment associated with this SubmissionType
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from pprint import pformat
|
|||||||
from sqlalchemy import Column, String, INTEGER, ForeignKey, Table
|
from sqlalchemy import Column, String, INTEGER, ForeignKey, Table
|
||||||
from sqlalchemy.orm import relationship, Query
|
from sqlalchemy.orm import relationship, Query
|
||||||
from . import Base, BaseClass
|
from . import Base, BaseClass
|
||||||
from tools import check_authorization, setup_lookup
|
from tools import check_authorization, setup_lookup, yaml_regex_creator
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
logger = logging.getLogger(f"submissions.{__name__}")
|
logger = logging.getLogger(f"submissions.{__name__}")
|
||||||
@@ -88,6 +88,7 @@ class Organization(BaseClass):
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
yaml.add_constructor("!regex", yaml_regex_creator)
|
||||||
if isinstance(filepath, str):
|
if isinstance(filepath, str):
|
||||||
filepath = Path(filepath)
|
filepath = Path(filepath)
|
||||||
if not filepath.exists():
|
if not filepath.exists():
|
||||||
@@ -97,7 +98,7 @@ class Organization(BaseClass):
|
|||||||
if filepath.suffix == ".json":
|
if filepath.suffix == ".json":
|
||||||
import_dict = json.load(fp=f)
|
import_dict = json.load(fp=f)
|
||||||
elif filepath.suffix == ".yml":
|
elif filepath.suffix == ".yml":
|
||||||
import_dict = yaml.safe_load(stream=f)
|
import_dict = yaml.load(stream=f, Loader=yaml.Loader)
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Filetype {filepath.suffix} not supported.")
|
raise Exception(f"Filetype {filepath.suffix} not supported.")
|
||||||
data = import_dict['orgs']
|
data = import_dict['orgs']
|
||||||
|
|||||||
@@ -1038,7 +1038,7 @@ class BasicSubmission(BaseClass):
|
|||||||
chronologic: bool = False,
|
chronologic: bool = False,
|
||||||
limit: int = 0,
|
limit: int = 0,
|
||||||
page: int = 1,
|
page: int = 1,
|
||||||
page_size: int = 250,
|
page_size: None | int = 250,
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> BasicSubmission | List[BasicSubmission]:
|
) -> BasicSubmission | List[BasicSubmission]:
|
||||||
"""
|
"""
|
||||||
@@ -1059,6 +1059,7 @@ class BasicSubmission(BaseClass):
|
|||||||
"""
|
"""
|
||||||
# logger.debug(f"Incoming kwargs: {kwargs}")
|
# logger.debug(f"Incoming kwargs: {kwargs}")
|
||||||
# NOTE: if you go back to using 'model' change the appropriate cls to model in the query filters
|
# NOTE: if you go back to using 'model' change the appropriate cls to model in the query filters
|
||||||
|
# logger.debug(f"Page size: {page_size}")
|
||||||
if submission_type is not None:
|
if submission_type is not None:
|
||||||
model = cls.find_polymorphic_subclass(polymorphic_identity=submission_type)
|
model = cls.find_polymorphic_subclass(polymorphic_identity=submission_type)
|
||||||
elif len(kwargs) > 0:
|
elif len(kwargs) > 0:
|
||||||
@@ -1139,7 +1140,7 @@ class BasicSubmission(BaseClass):
|
|||||||
# if chronologic:
|
# if chronologic:
|
||||||
# logger.debug("Attempting sort by date descending")
|
# logger.debug("Attempting sort by date descending")
|
||||||
query = query.order_by(cls.submitted_date.desc())
|
query = query.order_by(cls.submitted_date.desc())
|
||||||
if page_size is not None:
|
if page_size > 0:
|
||||||
query = query.limit(page_size)
|
query = query.limit(page_size)
|
||||||
page = page - 1
|
page = page - 1
|
||||||
if page is not None:
|
if page is not None:
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ class SheetParser(object):
|
|||||||
parser = ReagentParser(xl=self.xl, submission_type=self.submission_type,
|
parser = ReagentParser(xl=self.xl, submission_type=self.submission_type,
|
||||||
extraction_kit=extraction_kit)
|
extraction_kit=extraction_kit)
|
||||||
self.sub['reagents'] = parser.parse_reagents()
|
self.sub['reagents'] = parser.parse_reagents()
|
||||||
logger.debug(f"Reagents out of parser: {pformat(self.sub['reagents'])}")
|
# logger.debug(f"Reagents out of parser: {pformat(self.sub['reagents'])}")
|
||||||
|
|
||||||
def parse_samples(self):
|
def parse_samples(self):
|
||||||
"""
|
"""
|
||||||
@@ -273,11 +273,11 @@ class ReagentParser(object):
|
|||||||
self.kit_object = KitType.query(name=extraction_kit)
|
self.kit_object = KitType.query(name=extraction_kit)
|
||||||
logger.debug(f"Got extraction kit object: {self.kit_object}")
|
logger.debug(f"Got extraction kit object: {self.kit_object}")
|
||||||
self.map = self.fetch_kit_info_map(submission_type=submission_type)
|
self.map = self.fetch_kit_info_map(submission_type=submission_type)
|
||||||
# logger.debug(f"Reagent Parser map: {self.map}")
|
logger.debug(f"Reagent Parser map: {self.map}")
|
||||||
self.xl = xl
|
self.xl = xl
|
||||||
|
|
||||||
@report_result
|
@report_result
|
||||||
def fetch_kit_info_map(self, submission_type: str|SubmissionType) -> Tuple[Report, dict]:
|
def fetch_kit_info_map(self, submission_type: str | SubmissionType) -> Tuple[Report, dict]:
|
||||||
"""
|
"""
|
||||||
Gets location of kit reagents from database
|
Gets location of kit reagents from database
|
||||||
|
|
||||||
@@ -296,15 +296,24 @@ class ReagentParser(object):
|
|||||||
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.
|
||||||
if not reagent_map.keys():
|
if not reagent_map.keys():
|
||||||
try:
|
temp_kit_object = self.submission_type_obj.get_default_kit()
|
||||||
ext_kit_loc = self.submission_type_obj.info_map['extraction_kit']['read'][0]
|
if temp_kit_object:
|
||||||
location_string = f"Sheet: {ext_kit_loc['sheet']}, Row: {ext_kit_loc['row']}, Column: {ext_kit_loc['column']}?"
|
self.kit_object = temp_kit_object
|
||||||
except:
|
reagent_map = {k: v for k, v in self.kit_object.construct_xl_map_for_use(submission_type)}
|
||||||
location_string = ""
|
logger.warning(f"Attempting to salvage {self.kit_object} with default kit map: {reagent_map}")
|
||||||
report.add_result(Result(owner=__name__, code=0, msg=f"No kit map found for {self.kit_object.name}.\n\n"
|
if not reagent_map.keys():
|
||||||
f"Are you sure you put the right kit in:\n\n{location_string}?",
|
logger.error(f"Still no reagent map, displaying error.")
|
||||||
status="Critical"))
|
try:
|
||||||
|
ext_kit_loc = self.submission_type_obj.info_map['extraction_kit']['read'][0]
|
||||||
|
location_string = f"Sheet: {ext_kit_loc['sheet']}, Row: {ext_kit_loc['row']}, Column: {ext_kit_loc['column']}?"
|
||||||
|
except (IndexError, KeyError):
|
||||||
|
location_string = ""
|
||||||
|
report.add_result(Result(owner=__name__, code=0,
|
||||||
|
msg=f"No kit map found for {self.kit_object.name}.\n\n"
|
||||||
|
f"Are you sure you put the right kit in:\n\n{location_string}?",
|
||||||
|
status="Critical"))
|
||||||
return report, reagent_map
|
return report, reagent_map
|
||||||
|
|
||||||
def parse_reagents(self) -> Generator[dict, None, None]:
|
def parse_reagents(self) -> Generator[dict, None, None]:
|
||||||
@@ -317,7 +326,7 @@ class ReagentParser(object):
|
|||||||
for sheet in self.xl.sheetnames:
|
for sheet in self.xl.sheetnames:
|
||||||
ws = self.xl[sheet]
|
ws = self.xl[sheet]
|
||||||
relevant = {k.strip(): v for k, v in self.map.items() if sheet in self.map[k]['sheet']}
|
relevant = {k.strip(): v for k, v in self.map.items() if sheet in self.map[k]['sheet']}
|
||||||
# logger.debug(f"relevant map for {sheet}: {pformat(relevant)}")
|
logger.debug(f"relevant map for {sheet}: {pformat(relevant)}")
|
||||||
if relevant == {}:
|
if relevant == {}:
|
||||||
continue
|
continue
|
||||||
for item in relevant:
|
for item in relevant:
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ env = jinja_template_loading()
|
|||||||
|
|
||||||
class ReportMaker(object):
|
class ReportMaker(object):
|
||||||
|
|
||||||
def __init__(self, start_date: date, end_date: date, organizations:list|None=None):
|
def __init__(self, start_date: date, end_date: date, organizations: list | None = None):
|
||||||
self.start_date = start_date
|
self.start_date = start_date
|
||||||
self.end_date = end_date
|
self.end_date = end_date
|
||||||
self.subs = BasicSubmission.query(start_date=start_date, 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)
|
||||||
|
# logger.debug(f"Number of subs returned: {len(self.subs)}")
|
||||||
if organizations is not None:
|
if organizations is not None:
|
||||||
self.subs = [sub for sub in self.subs if sub.submitting_lab.name in organizations]
|
self.subs = [sub for sub in self.subs if sub.submitting_lab.name in organizations]
|
||||||
self.detailed_df, self.summary_df = self.make_report_xlsx()
|
self.detailed_df, self.summary_df = self.make_report_xlsx()
|
||||||
@@ -46,6 +48,7 @@ class ReportMaker(object):
|
|||||||
# logger.debug(f"Output daftaframe for xlsx: {df2.columns}")
|
# logger.debug(f"Output daftaframe for xlsx: {df2.columns}")
|
||||||
df = df.drop('id', axis=1)
|
df = df.drop('id', axis=1)
|
||||||
df = df.sort_values(['submitting_lab', "submitted_date"])
|
df = df.sort_values(['submitting_lab', "submitted_date"])
|
||||||
|
logger.debug(f"Details dataframe:\n{df2}")
|
||||||
return df, df2
|
return df, df2
|
||||||
|
|
||||||
def make_report_html(self, df: DataFrame) -> str:
|
def make_report_html(self, df: DataFrame) -> str:
|
||||||
@@ -97,7 +100,7 @@ class ReportMaker(object):
|
|||||||
Args:
|
Args:
|
||||||
filename (Path | str): Basename of output file
|
filename (Path | str): Basename of output file
|
||||||
obj (QWidget | None, optional): Parent object. Defaults to None.
|
obj (QWidget | None, optional): Parent object. Defaults to None.
|
||||||
"""
|
"""
|
||||||
if isinstance(filename, str):
|
if isinstance(filename, str):
|
||||||
filename = Path(filename)
|
filename = Path(filename)
|
||||||
filename = filename.absolute()
|
filename = filename.absolute()
|
||||||
@@ -111,7 +114,7 @@ class ReportMaker(object):
|
|||||||
def fix_up_xl(self):
|
def fix_up_xl(self):
|
||||||
"""
|
"""
|
||||||
Handles formatting of xl file, mediocrely.
|
Handles formatting of xl file, mediocrely.
|
||||||
"""
|
"""
|
||||||
# logger.debug(f"Updating worksheet")
|
# logger.debug(f"Updating worksheet")
|
||||||
worksheet: Worksheet = self.writer.sheets['Report']
|
worksheet: Worksheet = self.writer.sheets['Report']
|
||||||
for idx, col in enumerate(self.summary_df, start=1): # NOTE: loop through all columns
|
for idx, col in enumerate(self.summary_df, start=1): # NOTE: loop through all columns
|
||||||
@@ -134,5 +137,3 @@ 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'
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ class SheetWriter(object):
|
|||||||
# except Exception as e:
|
# except Exception as e:
|
||||||
# logger.error(f"Couldn't open workbook due to {e}")
|
# logger.error(f"Couldn't open workbook due to {e}")
|
||||||
template = self.submission_type.template_file
|
template = self.submission_type.template_file
|
||||||
|
if not template:
|
||||||
|
logger.error(f"No template file found, falling back to Bacterial Culture")
|
||||||
|
template = SubmissionType.retrieve_template_file()
|
||||||
workbook = load_workbook(BytesIO(template))
|
workbook = load_workbook(BytesIO(template))
|
||||||
# self.workbook = workbook
|
# self.workbook = workbook
|
||||||
self.xl = workbook
|
self.xl = workbook
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Constructs main application.
|
Constructs main application.
|
||||||
"""
|
"""
|
||||||
|
import os
|
||||||
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 (
|
||||||
@@ -213,7 +214,7 @@ class App(QMainWindow):
|
|||||||
None
|
None
|
||||||
"""
|
"""
|
||||||
if check_if_app():
|
if check_if_app():
|
||||||
yaml_path = Path(sys._MEIPASS).joinpath("resources", "viral_culture.yml")
|
yaml_path = Path(sys._MEIPASS).joinpath("files", "resources", "viral_culture.yml")
|
||||||
else:
|
else:
|
||||||
yaml_path = project_path.joinpath("src", "submissions", "resources", "viral_culture.yml")
|
yaml_path = project_path.joinpath("src", "submissions", "resources", "viral_culture.yml")
|
||||||
fname = select_save_file(obj=self, default_name="Submission Type Template.yml", extension="yml")
|
fname = select_save_file(obj=self, default_name="Submission Type Template.yml", extension="yml")
|
||||||
|
|||||||
@@ -225,7 +225,8 @@ class SubmissionFormWidget(QWidget):
|
|||||||
if k == "extraction_kit":
|
if k == "extraction_kit":
|
||||||
add_widget.input.currentTextChanged.connect(self.scrape_reagents)
|
add_widget.input.currentTextChanged.connect(self.scrape_reagents)
|
||||||
self.setStyleSheet(main_form_style)
|
self.setStyleSheet(main_form_style)
|
||||||
self.scrape_reagents(self.pyd.extraction_kit)
|
# self.scrape_reagents(self.pyd.extraction_kit)
|
||||||
|
self.scrape_reagents(self.extraction_kit)
|
||||||
|
|
||||||
def create_widget(self, key: str, value: dict | PydReagent, submission_type: str | SubmissionType| None = None,
|
def create_widget(self, key: str, value: dict | PydReagent, submission_type: str | SubmissionType| None = None,
|
||||||
extraction_kit: str | None = None, sub_obj: BasicSubmission | None = None,
|
extraction_kit: str | None = None, sub_obj: BasicSubmission | None = None,
|
||||||
@@ -275,9 +276,9 @@ class SubmissionFormWidget(QWidget):
|
|||||||
Returns:
|
Returns:
|
||||||
Tuple[QMainWindow, dict]: Updated application and result
|
Tuple[QMainWindow, dict]: Updated application and result
|
||||||
"""
|
"""
|
||||||
extraction_kit = args[0]
|
self.extraction_kit = args[0]
|
||||||
report = Report()
|
report = Report()
|
||||||
logger.debug(f"Extraction kit: {extraction_kit}")
|
logger.debug(f"Extraction kit: {self.extraction_kit}")
|
||||||
# NOTE: Remove previous reagent widgets
|
# NOTE: Remove previous reagent widgets
|
||||||
try:
|
try:
|
||||||
old_reagents = self.find_widgets()
|
old_reagents = self.find_widgets()
|
||||||
@@ -288,10 +289,11 @@ class SubmissionFormWidget(QWidget):
|
|||||||
for reagent in old_reagents:
|
for reagent in old_reagents:
|
||||||
if isinstance(reagent, self.ReagentFormWidget) or isinstance(reagent, QPushButton):
|
if isinstance(reagent, self.ReagentFormWidget) or isinstance(reagent, QPushButton):
|
||||||
reagent.setParent(None)
|
reagent.setParent(None)
|
||||||
reagents, integrity_report = self.pyd.check_kit_integrity(extraction_kit=extraction_kit)
|
reagents, integrity_report = self.pyd.check_kit_integrity(extraction_kit=self.extraction_kit)
|
||||||
logger.debug(f"Got reagents: {pformat(reagents)}")
|
logger.debug(f"Got reagents: {pformat(reagents)}")
|
||||||
for reagent in reagents:
|
for reagent in reagents:
|
||||||
add_widget = self.ReagentFormWidget(parent=self, reagent=reagent, extraction_kit=self.pyd.extraction_kit)
|
# add_widget = self.ReagentFormWidget(parent=self, reagent=reagent, extraction_kit=self.pyd.extraction_kit)
|
||||||
|
add_widget = self.ReagentFormWidget(parent=self, reagent=reagent, extraction_kit=self.extraction_kit)
|
||||||
self.layout.addWidget(add_widget)
|
self.layout.addWidget(add_widget)
|
||||||
report.add_result(integrity_report)
|
report.add_result(integrity_report)
|
||||||
# logger.debug(f"Outgoing report: {report.results}")
|
# logger.debug(f"Outgoing report: {report.results}")
|
||||||
@@ -569,6 +571,7 @@ class SubmissionFormWidget(QWidget):
|
|||||||
obj.ext_kit = uses[0]
|
obj.ext_kit = uses[0]
|
||||||
add_widget.addItems(uses)
|
add_widget.addItems(uses)
|
||||||
add_widget.setToolTip("Select extraction kit.")
|
add_widget.setToolTip("Select extraction kit.")
|
||||||
|
parent.extraction_kit = add_widget.currentText()
|
||||||
case 'submission_category':
|
case 'submission_category':
|
||||||
add_widget = MyQComboBox(scrollWidget=parent)
|
add_widget = MyQComboBox(scrollWidget=parent)
|
||||||
cats = ['Diagnostic', "Surveillance", "Research"]
|
cats = ['Diagnostic', "Surveillance", "Research"]
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ class Summary(QWidget):
|
|||||||
# 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()
|
||||||
|
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)
|
||||||
if self.report_obj.subs:
|
if self.report_obj.subs:
|
||||||
|
|||||||
Reference in New Issue
Block a user