From 8662bbdc2f0af298324734148f4ff81bed6a46d0 Mon Sep 17 00:00:00 2001 From: lwark Date: Mon, 6 Jan 2025 09:15:43 -0600 Subject: [PATCH] Updating AddEdit to hopefully include ReagentAdd --- CHANGELOG.md | 8 +++++++ TODO.md | 1 + src/submissions/backend/db/models/kits.py | 2 +- .../backend/db/models/submissions.py | 24 ++++++++++++++++++- src/submissions/backend/validators/pydant.py | 8 ++++--- src/submissions/frontend/widgets/app.py | 2 +- .../frontend/widgets/omni_add_edit.py | 19 +++++++++++---- .../frontend/widgets/submission_widget.py | 10 ++++++++ src/submissions/tools/__init__.py | 5 +++- 9 files changed, 67 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 530b6d2..d10a4b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# 202501.02 + +- Fixed bug where Wastewater ENs were not receiving rsl_number and therefore not getting PCR data. + +# 202501.01 + +- Created Client Manager to be run by super users. + # 202412.06 - Switched startup/teardown scripts to importlib/getattr addition to ctx. diff --git a/TODO.md b/TODO.md index ae8c451..c9f6cd8 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,4 @@ +- [ ] Find a way to merge AddEdit with ReagentAdder - [x] Find a way to merge omni_search and sample_search - [x] Allow parsing of custom fields to a json 'custom' field in _basicsubmissions - [x] Upgrade to generators when returning lists. diff --git a/src/submissions/backend/db/models/kits.py b/src/submissions/backend/db/models/kits.py index ae21374..60a9ecc 100644 --- a/src/submissions/backend/db/models/kits.py +++ b/src/submissions/backend/db/models/kits.py @@ -608,7 +608,7 @@ class Reagent(BaseClass, LogMixin): if isinstance(value, str): field_value = datetime.strptime(value, "%Y-%m-%d") elif isinstance(value, date): - field_value = datetime.combine(value, datetime.min.time()) + field_value = datetime.combine(value, datetime.max.time()) else: field_value = value field_value.replace(tzinfo=timezone) diff --git a/src/submissions/backend/db/models/submissions.py b/src/submissions/backend/db/models/submissions.py index 50e8001..c6378d6 100644 --- a/src/submissions/backend/db/models/submissions.py +++ b/src/submissions/backend/db/models/submissions.py @@ -1502,6 +1502,25 @@ class Wastewater(BasicSubmission): logger.error(f"Error handling couldn't get csv due to: {e}") return input_dict + @classmethod + def parse_samples(cls, input_dict: dict) -> dict: + """ + Update sample dictionary with type specific information. Extends parent + + Args: + input_dict (dict): Input sample dictionary + + Returns: + dict: Updated sample dictionary + """ + input_dict = super().parse_samples(input_dict=input_dict) + # NOTE: Had to put in this section due to ENs not having rsl_number and therefore not getting PCR results. + check = check_key_or_attr("rsl_number", input_dict) + if not check: + input_dict['rsl_number'] = input_dict['submitter_id'] + # logger.debug(pformat(input_dict, indent=4)) + return input_dict + @classmethod def parse_pcr(cls, xl: Workbook, rsl_plate_num: str) -> Generator[dict, None, None]: """ @@ -1633,6 +1652,7 @@ class Wastewater(BasicSubmission): self.save(original=False) for sample in self.samples: try: + # NOTE: Fix for ENs which have no rsl_number... sample_dict = next(item for item in pcr_samples if item['sample'] == sample.rsl_number) except StopIteration: continue @@ -2539,7 +2559,7 @@ class WastewaterSample(BasicSample): Custom sample parser. Extends parent Args: - input_dict (dict): Basic parser results. + input_dict (dict): Basic parser results for this sample. Returns: dict: Updated parser results. @@ -2554,6 +2574,8 @@ class WastewaterSample(BasicSample): output_dict['rsl_number'] = "RSL-WW-" + output_dict['ww_processing_num'] if output_dict['ww_full_sample_id'] is not None and output_dict["submitter_id"] in disallowed: output_dict["submitter_id"] = output_dict['ww_full_sample_id'] + check = check_key_or_attr("rsl_number", output_dict, check_none=True) + # logger.debug(pformat(output_dict, indent=4)) return output_dict @classmethod diff --git a/src/submissions/backend/validators/pydant.py b/src/submissions/backend/validators/pydant.py index c0701b3..b05803c 100644 --- a/src/submissions/backend/validators/pydant.py +++ b/src/submissions/backend/validators/pydant.py @@ -144,7 +144,9 @@ class PydReagent(BaseModel): case "expiry": if isinstance(value, str): value = date(year=1970, month=1, day=1) - value = datetime.combine(value, datetime.min.time()) + # NOTE: if min time is used, any reagent set to expire today (Bac postive control, eg) will have expired at midnight and therefore be flagged. + # NOTE: Make expiry at date given, plus now time + 1 hour + value = datetime.combine(value, datetime.max.time()) reagent.expiry = value.replace(tzinfo=timezone) case _: try: @@ -826,7 +828,7 @@ class PydSubmission(BaseModel, extra='allow'): case item if item in instance.timestamps(): logger.warning(f"Incoming timestamp key: {item}, with value: {value}") if isinstance(value, date): - value = datetime.combine(value, datetime.min.time()) + value = datetime.combine(value, datetime.max.time()) value = value.replace(tzinfo=timezone) elif isinstance(value, str): value: datetime = datetime.strptime(value, "%Y-%m-%d") @@ -961,7 +963,7 @@ class PydSubmission(BaseModel, extra='allow'): if reagent not in exempt: role_expiry = ReagentRole.query(name=reagent.role).eol_ext try: - dt = datetime.combine(reagent.expiry, datetime.min.time()) + dt = datetime.combine(reagent.expiry, datetime.max.time()) except TypeError: continue if datetime.now() > dt + role_expiry: diff --git a/src/submissions/frontend/widgets/app.py b/src/submissions/frontend/widgets/app.py index 5373c24..c9387c9 100644 --- a/src/submissions/frontend/widgets/app.py +++ b/src/submissions/frontend/widgets/app.py @@ -117,7 +117,7 @@ class App(QMainWindow): connect menu and tool bar item to functions """ self.importAction.triggered.connect(self.table_widget.formwidget.importSubmission) - self.addReagentAction.triggered.connect(self.table_widget.formwidget.add_reagent) + self.addReagentAction.triggered.connect(self.table_widget.formwidget.new_add_reagent) self.joinExtractionAction.triggered.connect(self.table_widget.sub_wid.link_extractions) self.joinPCRAction.triggered.connect(self.table_widget.sub_wid.link_pcr) self.helpAction.triggered.connect(self.showAbout) diff --git a/src/submissions/frontend/widgets/omni_add_edit.py b/src/submissions/frontend/widgets/omni_add_edit.py index cb7eb05..f15e6b9 100644 --- a/src/submissions/frontend/widgets/omni_add_edit.py +++ b/src/submissions/frontend/widgets/omni_add_edit.py @@ -1,3 +1,4 @@ +from datetime import date from typing import Any from PyQt6.QtWidgets import ( @@ -12,7 +13,7 @@ logger = logging.getLogger(f"submissions.{__name__}") class AddEdit(QDialog): - def __init__(self, parent, instance: Any): + def __init__(self, parent, instance: Any|None=None): super().__init__(parent) self.instance = instance self.object_type = instance.__class__ @@ -22,13 +23,16 @@ class AddEdit(QDialog): self.buttonBox = QDialogButtonBox(QBtn) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) - fields = {k: v for k, v in self.object_type.__dict__.items() if - isinstance(v, InstrumentedAttribute) and k != "id"} + # fields = {k: v for k, v in self.object_type.__dict__.items() if + # isinstance(v, InstrumentedAttribute) and k != "id"} + fields = {k: v for k, v in self.object_type.__dict__.items() if k != "id"} for key, field in fields.items(): + logger.debug(f"") try: widget = EditProperty(self, key=key, column_type=field.property.expression.type, value=getattr(self.instance, key)) - except AttributeError: + except AttributeError as e: + logger.error(f"Problem setting widget {key}: {e}") continue self.layout.addWidget(widget, self.layout.rowCount(), 0) self.layout.addWidget(self.buttonBox) @@ -62,14 +66,19 @@ class EditProperty(QWidget): self.setObjectName(key) match column_type: case String(): + if not value: + value = "" self.widget = QLineEdit(self) self.widget.setText(value) case TIMESTAMP(): - self.widget = QDateEdit(self) + self.widget = QDateEdit(self, calendarPopup=True) + if not value: + value = date.today() self.widget.setDate(value) case _: logger.error(f"{column_type} not a supported type.") self.widget = None + return self.layout.addWidget(self.widget, 0, 1, 1, 3) self.setLayout(self.layout) diff --git a/src/submissions/frontend/widgets/submission_widget.py b/src/submissions/frontend/widgets/submission_widget.py index b8ca3cb..6a5d150 100644 --- a/src/submissions/frontend/widgets/submission_widget.py +++ b/src/submissions/frontend/widgets/submission_widget.py @@ -19,6 +19,7 @@ from backend.db import ( from pprint import pformat from .pop_ups import QuestionAsker, AlertPop from .misc import AddReagentForm +from .omni_add_edit import AddEdit from typing import List, Tuple from datetime import date @@ -139,6 +140,15 @@ class SubmissionFormContainer(QWidget): self.layout().addWidget(self.form) return report + + def new_add_reagent(self): + instance = Reagent() + dlg = AddEdit(parent=self, instance=instance) + if dlg.exec(): + obj = dlg.parse_form() + print(obj) + + @report_result def add_reagent(self, reagent_lot: str | None = None, reagent_role: str | None = None, expiry: date | None = None, name: str | None = None, kit: str | KitType | None = None) -> Tuple[PydReagent, Report]: diff --git a/src/submissions/tools/__init__.py b/src/submissions/tools/__init__.py index 9684887..a85a20c 100644 --- a/src/submissions/tools/__init__.py +++ b/src/submissions/tools/__init__.py @@ -449,7 +449,10 @@ class Settings(BaseSettings, extra="allow"): def set_from_db(self): if 'pytest' in sys.modules: - output = dict(power_users=['lwark', 'styson', 'ruwang']) + output = dict(power_users=['lwark', 'styson', 'ruwang'], + startup_scripts=dict(hello=None), + teardown_scripts=dict(goodbye=None) + ) else: # print(f"Hello from database settings getter.") # print(self.__dict__)