Post code-cleanup, moments before disaster.
This commit is contained in:
5
TODO.md
5
TODO.md
@@ -1,6 +1,7 @@
|
|||||||
- [ ] Can my "to_dict", "to_sub_dict", "to_pydantic" methods be rewritten as properties?
|
- [ ] Change "Manage Organizations" to the Pydantic version.
|
||||||
|
- [x] Can my "to_dict", "to_sub_dict", "to_pydantic" methods be rewritten as properties?
|
||||||
- [ ] Stop displacing date on Irida controls and just do what Turnaround time does.
|
- [ ] Stop displacing date on Irida controls and just do what Turnaround time does.
|
||||||
- [ ] Get Manager window working for KitType, maybe SubmissionType
|
- [x] Get Manager window working for KitType, maybe SubmissionType
|
||||||
- [x] Find a way to merge AddEdit with ReagentAdder
|
- [x] Find a way to merge AddEdit with ReagentAdder
|
||||||
- [x] Find a way to merge omni_search and sample_search
|
- [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] Allow parsing of custom fields to a json 'custom' field in _basicsubmissions
|
||||||
|
|||||||
@@ -56,6 +56,12 @@ class BaseClass(Base):
|
|||||||
omni_inheritable = []
|
omni_inheritable = []
|
||||||
searchables = []
|
searchables = []
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
try:
|
||||||
|
return f"<{self.__class__.__name__}({self.name})>"
|
||||||
|
except AttributeError:
|
||||||
|
return f"<{self.__class__.__name__}({self.__name__})>"
|
||||||
|
|
||||||
# @classproperty
|
# @classproperty
|
||||||
# def skip_on_edit(cls):
|
# def skip_on_edit(cls):
|
||||||
# if "association" in cls.__name__.lower() or cls.__name__.lower() == "discount":
|
# if "association" in cls.__name__.lower() or cls.__name__.lower() == "discount":
|
||||||
@@ -444,6 +450,9 @@ class BaseClass(Base):
|
|||||||
else:
|
else:
|
||||||
return super().__setattr__(key, value)
|
return super().__setattr__(key, value)
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
logger.error(f"Delete has not been implemented for {self.__class__.__name__}")
|
||||||
|
|
||||||
|
|
||||||
class ConfigItem(BaseClass):
|
class ConfigItem(BaseClass):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
All control related models.
|
All control related models.
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import itertools
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from PyQt6.QtWidgets import QWidget, QCheckBox, QLabel
|
from PyQt6.QtWidgets import QWidget, QCheckBox, QLabel
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
@@ -16,7 +18,6 @@ from typing import List, Literal, Tuple, Generator
|
|||||||
from dateutil.parser import parse
|
from dateutil.parser import parse
|
||||||
from re import Pattern
|
from re import Pattern
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(f"submissions.{__name__}")
|
logger = logging.getLogger(f"submissions.{__name__}")
|
||||||
|
|
||||||
|
|
||||||
@@ -29,8 +30,8 @@ class ControlType(BaseClass):
|
|||||||
targets = Column(JSON) #: organisms checked for
|
targets = Column(JSON) #: organisms checked for
|
||||||
instances = relationship("Control", back_populates="controltype") #: control samples created of this type.
|
instances = relationship("Control", back_populates="controltype") #: control samples created of this type.
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
# def __repr__(self) -> str:
|
||||||
return f"<ControlType({self.name})>"
|
# return f"<ControlType({self.name})>"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@setup_lookup
|
@setup_lookup
|
||||||
@@ -352,6 +353,7 @@ class PCRControl(Control):
|
|||||||
end_date=chart_settings['end_date'])
|
end_date=chart_settings['end_date'])
|
||||||
data = [control.to_sub_dict() for control in controls]
|
data = [control.to_sub_dict() for control in controls]
|
||||||
df = DataFrame.from_records(data)
|
df = DataFrame.from_records(data)
|
||||||
|
# NOTE: Get all PCR controls with ct over 0
|
||||||
try:
|
try:
|
||||||
df = df[df.ct > 0.0]
|
df = df[df.ct > 0.0]
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@@ -361,11 +363,11 @@ class PCRControl(Control):
|
|||||||
|
|
||||||
def to_pydantic(self):
|
def to_pydantic(self):
|
||||||
from backend.validators import PydPCRControl
|
from backend.validators import PydPCRControl
|
||||||
return PydPCRControl(**self.to_sub_dict(), controltype_name=self.controltype_name, submission_id=self.submission_id)
|
return PydPCRControl(**self.to_sub_dict(), controltype_name=self.controltype_name,
|
||||||
|
submission_id=self.submission_id)
|
||||||
|
|
||||||
|
|
||||||
class IridaControl(Control):
|
class IridaControl(Control):
|
||||||
|
|
||||||
subtyping_allowed = ['kraken']
|
subtyping_allowed = ['kraken']
|
||||||
|
|
||||||
id = Column(INTEGER, ForeignKey('_control.id'), primary_key=True)
|
id = Column(INTEGER, ForeignKey('_control.id'), primary_key=True)
|
||||||
@@ -384,6 +386,14 @@ class IridaControl(Control):
|
|||||||
polymorphic_load="inline",
|
polymorphic_load="inline",
|
||||||
inherit_condition=(id == Control.id))
|
inherit_condition=(id == Control.id))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def targets(self):
|
||||||
|
if self.controltype.targets:
|
||||||
|
return list(itertools.chain.from_iterable([value for key, value in self.controltype.targets.items()
|
||||||
|
if key == self.subtype]))
|
||||||
|
else:
|
||||||
|
return ["None"]
|
||||||
|
|
||||||
@validates("subtype")
|
@validates("subtype")
|
||||||
def enforce_subtype_literals(self, key: str, value: str) -> str:
|
def enforce_subtype_literals(self, key: str, value: str) -> str:
|
||||||
"""
|
"""
|
||||||
@@ -416,19 +426,15 @@ class IridaControl(Control):
|
|||||||
except TypeError:
|
except TypeError:
|
||||||
kraken = {}
|
kraken = {}
|
||||||
kraken_cnt_total = sum([item['kraken_count'] for item in kraken.values()])
|
kraken_cnt_total = sum([item['kraken_count'] for item in kraken.values()])
|
||||||
new_kraken = [dict(name=item, kraken_count=kraken[item]['kraken_count'],
|
new_kraken = [dict(name=key, kraken_count=value['kraken_count'],
|
||||||
kraken_percent=f"{kraken[item]['kraken_count'] / kraken_cnt_total:0.2%}",
|
kraken_percent=f"{value['kraken_count'] / kraken_cnt_total:0.2%}",
|
||||||
target=item in self.controltype.targets)
|
target=key in self.controltype.targets)
|
||||||
for item in kraken]
|
for key, value in kraken.items()]
|
||||||
new_kraken = sorted(new_kraken, key=itemgetter('kraken_count'), reverse=True)
|
new_kraken = sorted(new_kraken, key=itemgetter('kraken_count'), reverse=True)
|
||||||
if self.controltype.targets:
|
|
||||||
targets = self.controltype.targets
|
|
||||||
else:
|
|
||||||
targets = ["None"]
|
|
||||||
output = dict(
|
output = dict(
|
||||||
name=self.name,
|
name=self.name,
|
||||||
type=self.controltype.name,
|
type=self.controltype.name,
|
||||||
targets=", ".join(targets),
|
targets=", ".join(self.targets),
|
||||||
kraken=new_kraken[0:10]
|
kraken=new_kraken[0:10]
|
||||||
)
|
)
|
||||||
return output
|
return output
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ from pandas import ExcelFile
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from . import Base, BaseClass, Organization, LogMixin
|
from . import Base, BaseClass, Organization, LogMixin
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from inspect import getouterframes, currentframe
|
|
||||||
|
|
||||||
logger = logging.getLogger(f'submissions.{__name__}')
|
logger = logging.getLogger(f'submissions.{__name__}')
|
||||||
|
|
||||||
@@ -98,7 +97,6 @@ class KitType(BaseClass):
|
|||||||
Base of kits used in submission processing
|
Base of kits used in submission processing
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# query_alias = "kit_type"
|
|
||||||
omni_sort = BaseClass.omni_sort + ["kit_submissiontype_associations", "kit_reagentrole_associations", "processes"]
|
omni_sort = BaseClass.omni_sort + ["kit_submissiontype_associations", "kit_reagentrole_associations", "processes"]
|
||||||
|
|
||||||
id = Column(INTEGER, primary_key=True) #: primary key
|
id = Column(INTEGER, primary_key=True) #: primary key
|
||||||
@@ -128,13 +126,6 @@ class KitType(BaseClass):
|
|||||||
creator=lambda ST: SubmissionTypeKitTypeAssociation(
|
creator=lambda ST: SubmissionTypeKitTypeAssociation(
|
||||||
submission_type=ST)) #: Association proxy to SubmissionTypeKitTypeAssociation
|
submission_type=ST)) #: Association proxy to SubmissionTypeKitTypeAssociation
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
"""
|
|
||||||
Returns:
|
|
||||||
str: A representation of the object.
|
|
||||||
"""
|
|
||||||
return f"<KitType({self.name})>"
|
|
||||||
|
|
||||||
@classproperty
|
@classproperty
|
||||||
def aliases(cls):
|
def aliases(cls):
|
||||||
return super().aliases + [cls.query_alias, "kit_types", "kit_type"]
|
return super().aliases + [cls.query_alias, "kit_types", "kit_type"]
|
||||||
@@ -187,9 +178,7 @@ class KitType(BaseClass):
|
|||||||
# NOTE: Account for submission_type variable type.
|
# NOTE: Account for submission_type variable type.
|
||||||
match submission_type:
|
match submission_type:
|
||||||
case str():
|
case str():
|
||||||
# assocs = [item for item in self.kit_reagentrole_associations if
|
# logger.debug(f"Query for {submission_type}")
|
||||||
# item.submission_type.name == submission_type]
|
|
||||||
logger.debug(f"Query for {submission_type}")
|
|
||||||
submission_type = SubmissionType.query(name=submission_type)
|
submission_type = SubmissionType.query(name=submission_type)
|
||||||
case SubmissionType():
|
case SubmissionType():
|
||||||
pass
|
pass
|
||||||
@@ -213,18 +202,12 @@ class KitType(BaseClass):
|
|||||||
)
|
)
|
||||||
if dlg.exec():
|
if dlg.exec():
|
||||||
dlg_result = dlg.parse_form()
|
dlg_result = dlg.parse_form()
|
||||||
logger.debug(f"Dialog result: {dlg_result}")
|
# logger.debug(f"Dialog result: {dlg_result}")
|
||||||
new_kit = self.__class__.query(name=dlg_result)
|
new_kit = self.__class__.query(name=dlg_result)
|
||||||
logger.debug(f"Query result: {new_kit}")
|
# logger.debug(f"Query result: {new_kit}")
|
||||||
# return new_kit.construct_xl_map_for_use(submission_type=submission_type)
|
|
||||||
else:
|
else:
|
||||||
return None, new_kit
|
return None, new_kit
|
||||||
assocs = [item for item in new_kit.kit_reagentrole_associations if item.submission_type == submission_type]
|
assocs = [item for item in new_kit.kit_reagentrole_associations if item.submission_type == submission_type]
|
||||||
# for assoc in assocs:
|
|
||||||
# try:
|
|
||||||
# yield assoc.reagent_role.name, assoc.uses
|
|
||||||
# except TypeError:
|
|
||||||
# continue
|
|
||||||
output = {assoc.reagent_role.name: assoc.uses for assoc in assocs}
|
output = {assoc.reagent_role.name: assoc.uses for assoc in assocs}
|
||||||
# logger.debug(f"Output: {output}")
|
# logger.debug(f"Output: {output}")
|
||||||
return output, new_kit
|
return output, new_kit
|
||||||
@@ -232,7 +215,6 @@ class KitType(BaseClass):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def query_or_create(cls, **kwargs) -> Tuple[KitType, bool]:
|
def query_or_create(cls, **kwargs) -> Tuple[KitType, bool]:
|
||||||
from backend.validators.pydant import PydKitType
|
from backend.validators.pydant import PydKitType
|
||||||
from backend.validators.omni_gui_objects import BaseOmni
|
|
||||||
new = False
|
new = False
|
||||||
disallowed = ['expiry']
|
disallowed = ['expiry']
|
||||||
sanitized_kwargs = {k: v for k, v in kwargs.items() if k not in disallowed}
|
sanitized_kwargs = {k: v for k, v in kwargs.items() if k not in disallowed}
|
||||||
@@ -248,7 +230,7 @@ class KitType(BaseClass):
|
|||||||
@setup_lookup
|
@setup_lookup
|
||||||
def query(cls,
|
def query(cls,
|
||||||
name: str = None,
|
name: str = None,
|
||||||
used_for: str | SubmissionType | None = None,
|
submissiontype: str | SubmissionType | None = None,
|
||||||
id: int | None = None,
|
id: int | None = None,
|
||||||
limit: int = 0,
|
limit: int = 0,
|
||||||
**kwargs
|
**kwargs
|
||||||
@@ -266,11 +248,11 @@ class KitType(BaseClass):
|
|||||||
KitType|List[KitType]: KitType(s) of interest.
|
KitType|List[KitType]: KitType(s) of interest.
|
||||||
"""
|
"""
|
||||||
query: Query = cls.__database_session__.query(cls)
|
query: Query = cls.__database_session__.query(cls)
|
||||||
match used_for:
|
match submissiontype:
|
||||||
case str():
|
case str():
|
||||||
query = query.filter(cls.used_for.any(name=used_for))
|
query = query.filter(cls.submissiontype.any(name=submissiontype))
|
||||||
case SubmissionType():
|
case SubmissionType():
|
||||||
query = query.filter(cls.used_for.contains(used_for))
|
query = query.filter(cls.submissiontype.contains(submissiontype))
|
||||||
case _:
|
case _:
|
||||||
pass
|
pass
|
||||||
match name:
|
match name:
|
||||||
@@ -399,9 +381,6 @@ class KitType(BaseClass):
|
|||||||
|
|
||||||
def to_omni(self, expand: bool = False) -> "OmniKitType":
|
def to_omni(self, expand: bool = False) -> "OmniKitType":
|
||||||
from backend.validators.omni_gui_objects import OmniKitType
|
from backend.validators.omni_gui_objects import OmniKitType
|
||||||
# logger.debug(f"self.name: {self.name}")
|
|
||||||
# level = len(getouterframes(currentframe()))
|
|
||||||
# logger.warning(f"Function level is {level}")
|
|
||||||
if expand:
|
if expand:
|
||||||
processes = [item.to_omni() for item in self.processes]
|
processes = [item.to_omni() for item in self.processes]
|
||||||
kit_reagentrole_associations = [item.to_omni() for item in self.kit_reagentrole_associations]
|
kit_reagentrole_associations = [item.to_omni() for item in self.kit_reagentrole_associations]
|
||||||
@@ -425,6 +404,8 @@ class ReagentRole(BaseClass):
|
|||||||
Base of reagent type abstract
|
Base of reagent type abstract
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
skip_on_edit = False
|
||||||
|
|
||||||
id = Column(INTEGER, primary_key=True) #: primary key
|
id = Column(INTEGER, primary_key=True) #: primary key
|
||||||
name = Column(String(64)) #: name of role reagent plays
|
name = Column(String(64)) #: name of role reagent plays
|
||||||
instances = relationship("Reagent", back_populates="role",
|
instances = relationship("Reagent", back_populates="role",
|
||||||
@@ -442,13 +423,6 @@ class ReagentRole(BaseClass):
|
|||||||
creator=lambda kit: KitTypeReagentRoleAssociation(
|
creator=lambda kit: KitTypeReagentRoleAssociation(
|
||||||
kit_type=kit)) #: Association proxy to KitTypeReagentRoleAssociation
|
kit_type=kit)) #: Association proxy to KitTypeReagentRoleAssociation
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
"""
|
|
||||||
Returns:
|
|
||||||
str: Representation of object
|
|
||||||
"""
|
|
||||||
return f"<ReagentRole({self.name})>"
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def query_or_create(cls, **kwargs) -> Tuple[ReagentRole, bool]:
|
def query_or_create(cls, **kwargs) -> Tuple[ReagentRole, bool]:
|
||||||
new = False
|
new = False
|
||||||
@@ -603,7 +577,6 @@ class Reagent(BaseClass, LogMixin):
|
|||||||
Returns:
|
Returns:
|
||||||
dict: representation of the reagent's attributes
|
dict: representation of the reagent's attributes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if extraction_kit is not None:
|
if extraction_kit is not None:
|
||||||
# NOTE: Get the intersection of this reagent's ReagentType and all ReagentTypes in KitType
|
# NOTE: Get the intersection of this reagent's ReagentType and all ReagentTypes in KitType
|
||||||
reagent_role = next((item for item in set(self.role).intersection(extraction_kit.reagent_roles)),
|
reagent_role = next((item for item in set(self.role).intersection(extraction_kit.reagent_roles)),
|
||||||
@@ -713,8 +686,6 @@ class Reagent(BaseClass, LogMixin):
|
|||||||
limit = 1
|
limit = 1
|
||||||
case _:
|
case _:
|
||||||
pass
|
pass
|
||||||
# if not role and "reagentrole" in kwargs.keys():
|
|
||||||
# role = kwargs['reagentrole']
|
|
||||||
match role:
|
match role:
|
||||||
case str():
|
case str():
|
||||||
query = query.join(cls.role).filter(ReagentRole.name == role)
|
query = query.join(cls.role).filter(ReagentRole.name == role)
|
||||||
@@ -772,6 +743,7 @@ class Reagent(BaseClass, LogMixin):
|
|||||||
@check_authorization
|
@check_authorization
|
||||||
def edit_from_search(self, obj, **kwargs):
|
def edit_from_search(self, obj, **kwargs):
|
||||||
from frontend.widgets.omni_add_edit import AddEdit
|
from frontend.widgets.omni_add_edit import AddEdit
|
||||||
|
# logger.debug(f"Calling edit_from_search for {self.name}")
|
||||||
dlg = AddEdit(parent=None, instance=self)
|
dlg = AddEdit(parent=None, instance=self)
|
||||||
if dlg.exec():
|
if dlg.exec():
|
||||||
pyd = dlg.parse_form()
|
pyd = dlg.parse_form()
|
||||||
@@ -1377,20 +1349,6 @@ class SubmissionType(BaseClass):
|
|||||||
|
|
||||||
def to_omni(self, expand: bool = False):
|
def to_omni(self, expand: bool = False):
|
||||||
from backend.validators.omni_gui_objects import OmniSubmissionType
|
from backend.validators.omni_gui_objects import OmniSubmissionType
|
||||||
# level = len(getouterframes(currentframe()))
|
|
||||||
# logger.warning(f"Function level is {level}")
|
|
||||||
# try:
|
|
||||||
# info_map = self.submission_type.info_map
|
|
||||||
# except AttributeError:
|
|
||||||
# info_map = {}
|
|
||||||
# try:
|
|
||||||
# defaults = self.submission_type.defaults
|
|
||||||
# except AttributeError:
|
|
||||||
# defaults = {}
|
|
||||||
# try:
|
|
||||||
# sample_map = self.submission_type.sample_map
|
|
||||||
# except AttributeError:
|
|
||||||
# sample_map = {}
|
|
||||||
try:
|
try:
|
||||||
template_file = self.template_file
|
template_file = self.template_file
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@@ -1555,8 +1513,6 @@ class SubmissionTypeKitTypeAssociation(BaseClass):
|
|||||||
|
|
||||||
def to_omni(self, expand: bool = False):
|
def to_omni(self, expand: bool = False):
|
||||||
from backend.validators.omni_gui_objects import OmniSubmissionTypeKitTypeAssociation
|
from backend.validators.omni_gui_objects import OmniSubmissionTypeKitTypeAssociation
|
||||||
# level = len(getouterframes(currentframe()))
|
|
||||||
# logger.warning(f"Function level is {level}")
|
|
||||||
if expand:
|
if expand:
|
||||||
try:
|
try:
|
||||||
submissiontype = self.submission_type.to_omni()
|
submissiontype = self.submission_type.to_omni()
|
||||||
@@ -1569,10 +1525,6 @@ class SubmissionTypeKitTypeAssociation(BaseClass):
|
|||||||
else:
|
else:
|
||||||
submissiontype = self.submission_type.name
|
submissiontype = self.submission_type.name
|
||||||
kittype = self.kit_type.name
|
kittype = self.kit_type.name
|
||||||
# try:
|
|
||||||
# processes = [item.to_omni() for item in self.submission_type.processes]
|
|
||||||
# except AttributeError:
|
|
||||||
# processes = []
|
|
||||||
return OmniSubmissionTypeKitTypeAssociation(
|
return OmniSubmissionTypeKitTypeAssociation(
|
||||||
instance_object=self,
|
instance_object=self,
|
||||||
submissiontype=submissiontype,
|
submissiontype=submissiontype,
|
||||||
@@ -1580,7 +1532,6 @@ class SubmissionTypeKitTypeAssociation(BaseClass):
|
|||||||
mutable_cost_column=self.mutable_cost_column,
|
mutable_cost_column=self.mutable_cost_column,
|
||||||
mutable_cost_sample=self.mutable_cost_sample,
|
mutable_cost_sample=self.mutable_cost_sample,
|
||||||
constant_cost=self.constant_cost
|
constant_cost=self.constant_cost
|
||||||
# processes=processes,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1719,7 +1670,6 @@ class KitTypeReagentRoleAssociation(BaseClass):
|
|||||||
pass
|
pass
|
||||||
setattr(instance, k, v)
|
setattr(instance, k, v)
|
||||||
logger.info(f"Instance from query or create: {instance.__dict__}\nis new: {new}")
|
logger.info(f"Instance from query or create: {instance.__dict__}\nis new: {new}")
|
||||||
# sys.exit()
|
|
||||||
return instance, new
|
return instance, new
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -1854,6 +1804,8 @@ class SubmissionReagentAssociation(BaseClass):
|
|||||||
DOC: https://docs.sqlalchemy.org/en/14/orm/extensions/associationproxy.html
|
DOC: https://docs.sqlalchemy.org/en/14/orm/extensions/associationproxy.html
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
skip_on_edit = True
|
||||||
|
|
||||||
reagent_id = Column(INTEGER, ForeignKey("_reagent.id"), primary_key=True) #: id of associated reagent
|
reagent_id = Column(INTEGER, ForeignKey("_reagent.id"), primary_key=True) #: id of associated reagent
|
||||||
submission_id = Column(INTEGER, ForeignKey("_basicsubmission.id"), primary_key=True) #: id of associated submission
|
submission_id = Column(INTEGER, ForeignKey("_basicsubmission.id"), primary_key=True) #: id of associated submission
|
||||||
comments = Column(String(1024)) #: Comments about reagents
|
comments = Column(String(1024)) #: Comments about reagents
|
||||||
@@ -1869,10 +1821,10 @@ class SubmissionReagentAssociation(BaseClass):
|
|||||||
str: Representation of this SubmissionReagentAssociation
|
str: Representation of this SubmissionReagentAssociation
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return f"<{self.submission.rsl_plate_num} & {self.reagent.lot}>"
|
return f"<SubmissionReagentAssociation({self.submission.rsl_plate_num} & {self.reagent.lot})>"
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
logger.error(f"Reagent {self.reagent.lot} submission association {self.reagent_id} has no submissions!")
|
logger.error(f"Reagent {self.reagent.lot} submission association {self.reagent_id} has no submissions!")
|
||||||
return f"<Unknown Submission & {self.reagent.lot}>"
|
return f"<SubmissionReagentAssociation(Unknown Submission & {self.reagent.lot})>"
|
||||||
|
|
||||||
def __init__(self, reagent=None, submission=None):
|
def __init__(self, reagent=None, submission=None):
|
||||||
if isinstance(reagent, list):
|
if isinstance(reagent, list):
|
||||||
@@ -1963,12 +1915,12 @@ class Equipment(BaseClass, LogMixin):
|
|||||||
submissions = association_proxy("equipment_submission_associations",
|
submissions = association_proxy("equipment_submission_associations",
|
||||||
"submission") #: proxy to equipment_submission_associations.submission
|
"submission") #: proxy to equipment_submission_associations.submission
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
# def __repr__(self) -> str:
|
||||||
"""
|
# """
|
||||||
Returns:
|
# Returns:
|
||||||
str: representation of this Equipment
|
# str: representation of this Equipment
|
||||||
"""
|
# """
|
||||||
return f"<Equipment({self.name})>"
|
# return f"<Equipment({self.name})>"
|
||||||
|
|
||||||
def to_dict(self, processes: bool = False) -> dict:
|
def to_dict(self, processes: bool = False) -> dict:
|
||||||
"""
|
"""
|
||||||
@@ -1987,7 +1939,7 @@ class Equipment(BaseClass, LogMixin):
|
|||||||
|
|
||||||
def get_processes(self, submission_type: str | SubmissionType | None = None,
|
def get_processes(self, submission_type: str | SubmissionType | None = None,
|
||||||
extraction_kit: str | KitType | None = None,
|
extraction_kit: str | KitType | None = None,
|
||||||
equipment_role: str | EquipmentRole | None = None) -> List[str]:
|
equipment_role: str | EquipmentRole | None = None) -> Generator[Process, None, None]:
|
||||||
"""
|
"""
|
||||||
Get all processes associated with this Equipment for a given SubmissionType
|
Get all processes associated with this Equipment for a given SubmissionType
|
||||||
|
|
||||||
@@ -2133,12 +2085,12 @@ class EquipmentRole(BaseClass):
|
|||||||
submission_types = association_proxy("equipmentrole_submissiontype_associations",
|
submission_types = association_proxy("equipmentrole_submissiontype_associations",
|
||||||
"submission_type") #: proxy to equipmentrole_submissiontype_associations.submission_type
|
"submission_type") #: proxy to equipmentrole_submissiontype_associations.submission_type
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
# def __repr__(self) -> str:
|
||||||
"""
|
# """
|
||||||
Returns:
|
# Returns:
|
||||||
str: Representation of this EquipmentRole
|
# str: Representation of this EquipmentRole
|
||||||
"""
|
# """
|
||||||
return f"<EquipmentRole({self.name})>"
|
# return f"<EquipmentRole({self.name})>"
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
"""
|
"""
|
||||||
@@ -2395,7 +2347,6 @@ class Process(BaseClass):
|
|||||||
|
|
||||||
id = Column(INTEGER, primary_key=True) #: Process id, primary key
|
id = Column(INTEGER, primary_key=True) #: Process id, primary key
|
||||||
name = Column(String(64), unique=True) #: Process name
|
name = Column(String(64), unique=True) #: Process name
|
||||||
# version = Column(String(32))
|
|
||||||
submission_types = relationship("SubmissionType", back_populates='processes',
|
submission_types = relationship("SubmissionType", back_populates='processes',
|
||||||
secondary=submissiontypes_processes) #: relation to SubmissionType
|
secondary=submissiontypes_processes) #: relation to SubmissionType
|
||||||
equipment = relationship("Equipment", back_populates='processes',
|
equipment = relationship("Equipment", back_populates='processes',
|
||||||
@@ -2410,12 +2361,12 @@ class Process(BaseClass):
|
|||||||
secondary=process_tiprole) #: relation to KitType
|
secondary=process_tiprole) #: relation to KitType
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
# def __repr__(self) -> str:
|
||||||
"""
|
# """
|
||||||
Returns:
|
# Returns:
|
||||||
str: Representation of this Process
|
# str: Representation of this Process
|
||||||
"""
|
# """
|
||||||
return f"<Process({self.name})>"
|
# return f"<Process({self.name})>"
|
||||||
|
|
||||||
def set_attribute(self, key, value):
|
def set_attribute(self, key, value):
|
||||||
match key:
|
match key:
|
||||||
@@ -2506,13 +2457,20 @@ class Process(BaseClass):
|
|||||||
|
|
||||||
def to_omni(self, expand: bool = False):
|
def to_omni(self, expand: bool = False):
|
||||||
from backend.validators.omni_gui_objects import OmniProcess
|
from backend.validators.omni_gui_objects import OmniProcess
|
||||||
|
if expand:
|
||||||
|
submission_types = [item.to_omni() for item in self.submission_types]
|
||||||
|
equipment_roles = [item.to_omni() for item in self.equipment_roles]
|
||||||
|
tip_roles = [item.to_omni() for item in self.tip_roles]
|
||||||
|
else:
|
||||||
|
submission_types = [item.name for item in self.submission_types]
|
||||||
|
equipment_roles = [item.name for item in self.equipment_roles]
|
||||||
|
tip_roles = [item.name for item in self.tip_roles]
|
||||||
return OmniProcess(
|
return OmniProcess(
|
||||||
instance_object=self,
|
instance_object=self,
|
||||||
name=self.name,
|
name=self.name,
|
||||||
# version=self.version,
|
submission_types=submission_types,
|
||||||
submission_types=[item.to_omni() for item in self.submission_types],
|
equipment_roles=equipment_roles,
|
||||||
equipment_roles=[item.to_omni() for item in self.equipment_roles],
|
tip_roles=tip_roles
|
||||||
tip_roles=[item.to_omni() for item in self.tip_roles]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -2538,8 +2496,8 @@ class TipRole(BaseClass):
|
|||||||
def tips(self):
|
def tips(self):
|
||||||
return self.instances
|
return self.instances
|
||||||
|
|
||||||
def __repr__(self):
|
# def __repr__(self):
|
||||||
return f"<TipRole({self.name})>"
|
# return f"<TipRole({self.name})>"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def query_or_create(cls, **kwargs) -> Tuple[TipRole, bool]:
|
def query_or_create(cls, **kwargs) -> Tuple[TipRole, bool]:
|
||||||
@@ -2573,10 +2531,14 @@ class TipRole(BaseClass):
|
|||||||
|
|
||||||
def to_omni(self, expand: bool = False):
|
def to_omni(self, expand: bool = False):
|
||||||
from backend.validators.omni_gui_objects import OmniTipRole
|
from backend.validators.omni_gui_objects import OmniTipRole
|
||||||
|
if expand:
|
||||||
|
tips = [item.to_omni() for item in self.tips]
|
||||||
|
else:
|
||||||
|
tips = [item.name for item in self.tips]
|
||||||
return OmniTipRole(
|
return OmniTipRole(
|
||||||
instance_object=self,
|
instance_object=self,
|
||||||
name=self.name,
|
name=self.name,
|
||||||
tips=[item.to_omni() for item in self.tips]
|
tips=tips
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -2605,8 +2567,8 @@ class Tips(BaseClass, LogMixin):
|
|||||||
def tiprole(self):
|
def tiprole(self):
|
||||||
return self.role
|
return self.role
|
||||||
|
|
||||||
def __repr__(self):
|
# def __repr__(self):
|
||||||
return f"<Tips({self.name})>"
|
# return f"<Tips({self.name})>"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def query_or_create(cls, **kwargs) -> Tuple[Tips, bool]:
|
def query_or_create(cls, **kwargs) -> Tuple[Tips, bool]:
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ class Organization(BaseClass):
|
|||||||
Base of organization
|
Base of organization
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
id = Column(INTEGER, primary_key=True) #: primary key
|
id = Column(INTEGER, primary_key=True) #: primary key
|
||||||
name = Column(String(64)) #: organization name
|
name = Column(String(64)) #: organization name
|
||||||
submissions = relationship("BasicSubmission",
|
submissions = relationship("BasicSubmission",
|
||||||
@@ -43,8 +41,8 @@ class Organization(BaseClass):
|
|||||||
def contact(self):
|
def contact(self):
|
||||||
return self.contacts
|
return self.contacts
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
# def __repr__(self) -> str:
|
||||||
return f"<Organization({self.name})>"
|
# return f"<Organization({self.name})>"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@setup_lookup
|
@setup_lookup
|
||||||
@@ -139,8 +137,8 @@ class Contact(BaseClass):
|
|||||||
secondary=orgs_contacts) #: relationship to joined organization
|
secondary=orgs_contacts) #: relationship to joined organization
|
||||||
submissions = relationship("BasicSubmission", back_populates="contact") #: submissions this contact has submitted
|
submissions = relationship("BasicSubmission", back_populates="contact") #: submissions this contact has submitted
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
# def __repr__(self) -> str:
|
||||||
return f"<Contact({self.name})>"
|
# return f"<Contact({self.name})>"
|
||||||
|
|
||||||
@classproperty
|
@classproperty
|
||||||
def searchables(cls):
|
def searchables(cls):
|
||||||
|
|||||||
@@ -402,7 +402,6 @@ class BasicSubmission(BaseClass, LogMixin):
|
|||||||
columns = set([assoc.column for assoc in self.submission_sample_associations])
|
columns = set([assoc.column for assoc in self.submission_sample_associations])
|
||||||
return len(columns)
|
return len(columns)
|
||||||
|
|
||||||
|
|
||||||
def calculate_base_cost(self) -> None:
|
def calculate_base_cost(self) -> None:
|
||||||
"""
|
"""
|
||||||
Calculates cost of the plate
|
Calculates cost of the plate
|
||||||
@@ -733,7 +732,7 @@ class BasicSubmission(BaseClass, LogMixin):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def find_polymorphic_subclass(cls, polymorphic_identity: str | SubmissionType | None = None,
|
def find_polymorphic_subclass(cls, polymorphic_identity: str | SubmissionType | None = None,
|
||||||
attrs: dict | None = None):
|
attrs: dict | None = None) -> BasicSubmission:
|
||||||
"""
|
"""
|
||||||
Find subclass based on polymorphic identity or relevant attributes.
|
Find subclass based on polymorphic identity or relevant attributes.
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ class SheetParser(object):
|
|||||||
Pulls basic information from the excel sheet
|
Pulls basic information from the excel sheet
|
||||||
"""
|
"""
|
||||||
parser = InfoParser(xl=self.xl, submission_type=self.submission_type, sub_object=self.sub_object)
|
parser = InfoParser(xl=self.xl, submission_type=self.submission_type, sub_object=self.sub_object)
|
||||||
# info = parser.parsed_info
|
|
||||||
self.info_map = parser.info_map
|
self.info_map = parser.info_map
|
||||||
# NOTE: in order to accommodate generic submission types we have to check for the type in the excel sheet and rerun accordingly
|
# NOTE: in order to accommodate generic submission types we have to check for the type in the excel sheet and rerun accordingly
|
||||||
try:
|
try:
|
||||||
@@ -274,37 +273,12 @@ class ReagentParser(object):
|
|||||||
Returns:
|
Returns:
|
||||||
dict: locations of reagent info for the kit.
|
dict: locations of reagent info for the kit.
|
||||||
"""
|
"""
|
||||||
# report = Report()
|
|
||||||
# if isinstance(submission_type, dict):
|
|
||||||
# submission_type = submission_type['value']
|
|
||||||
# if isinstance(submission_type, str):
|
|
||||||
# submission_type = SubmissionType.query(name=submission_type)
|
|
||||||
# logger.debug("Running kit map")
|
|
||||||
associations, self.kit_object = self.kit_object.construct_xl_map_for_use(submission_type=self.submission_type_obj)
|
associations, self.kit_object = self.kit_object.construct_xl_map_for_use(submission_type=self.submission_type_obj)
|
||||||
reagent_map = {k: v for k, v in associations.items() if k != 'info'}
|
reagent_map = {k: v for k, v in associations.items() if k != 'info'}
|
||||||
try:
|
try:
|
||||||
del reagent_map['info']
|
del reagent_map['info']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
# # 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:
|
|
||||||
# temp_kit_object = self.submission_type_obj.default_kit
|
|
||||||
# if temp_kit_object:
|
|
||||||
# self.kit_object = temp_kit_object
|
|
||||||
# logger.warning(f"Attempting to salvage with default kit {self.kit_object} and submission_type: {self.submission_type_obj}")
|
|
||||||
# return self.fetch_kit_map(submission_type=self.submission_type_obj)
|
|
||||||
# else:
|
|
||||||
# logger.error(f"Still no reagent map, displaying error.")
|
|
||||||
# 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"))
|
|
||||||
# logger.debug(f"Here is the map coming out: {reagent_map}")
|
|
||||||
return reagent_map
|
return reagent_map
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -375,9 +349,6 @@ class SampleParser(object):
|
|||||||
self.sub_object = sub_object
|
self.sub_object = sub_object
|
||||||
self.sample_type = self.sub_object.get_default_info("sample_type", submission_type=submission_type)
|
self.sample_type = self.sub_object.get_default_info("sample_type", submission_type=submission_type)
|
||||||
self.samp_object = BasicSample.find_polymorphic_subclass(polymorphic_identity=self.sample_type)
|
self.samp_object = BasicSample.find_polymorphic_subclass(polymorphic_identity=self.sample_type)
|
||||||
# self.sample_map = self.sample_map(submission_type=submission_type, sample_map=sample_map)
|
|
||||||
# self.plate_map_samples = self.parse_plate_map()
|
|
||||||
# self.lookup_samples = self.parse_lookup_table()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sample_map(self) -> dict:
|
def sample_map(self) -> dict:
|
||||||
@@ -391,11 +362,6 @@ class SampleParser(object):
|
|||||||
dict: Info locations.
|
dict: Info locations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# if sample_map is None:
|
|
||||||
# sample_info_map = self.sub_object.construct_sample_map(submission_type=self.submission_type_obj)
|
|
||||||
# else:
|
|
||||||
# sample_info_map = sample_map
|
|
||||||
# return sample_info_map
|
|
||||||
return self.sub_object.construct_sample_map(submission_type=self.submission_type_obj)
|
return self.sub_object.construct_sample_map(submission_type=self.submission_type_obj)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -519,7 +485,6 @@ class EquipmentParser(object):
|
|||||||
submission_type = SubmissionType.query(name=submission_type)
|
submission_type = SubmissionType.query(name=submission_type)
|
||||||
self.submission_type = submission_type
|
self.submission_type = submission_type
|
||||||
self.xl = xl
|
self.xl = xl
|
||||||
# self.equipment_map = self.fetch_equipment_map()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def equipment_map(self) -> dict:
|
def equipment_map(self) -> dict:
|
||||||
@@ -597,7 +562,6 @@ class TipParser(object):
|
|||||||
submission_type = SubmissionType.query(name=submission_type)
|
submission_type = SubmissionType.query(name=submission_type)
|
||||||
self.submission_type = submission_type
|
self.submission_type = submission_type
|
||||||
self.xl = xl
|
self.xl = xl
|
||||||
# self.map = self.fetch_tip_map()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tip_map(self) -> dict:
|
def tip_map(self) -> dict:
|
||||||
@@ -669,7 +633,6 @@ class PCRParser(object):
|
|||||||
else:
|
else:
|
||||||
self.submission_obj = submission
|
self.submission_obj = submission
|
||||||
rsl_plate_num = self.submission_obj.rsl_plate_num
|
rsl_plate_num = self.submission_obj.rsl_plate_num
|
||||||
# self.pcr = self.parse_general()
|
|
||||||
self.samples = self.submission_obj.parse_pcr(xl=self.xl, rsl_plate_num=rsl_plate_num)
|
self.samples = self.submission_obj.parse_pcr(xl=self.xl, rsl_plate_num=rsl_plate_num)
|
||||||
self.controls = self.submission_obj.parse_pcr_controls(xl=self.xl, rsl_plate_num=rsl_plate_num)
|
self.controls = self.submission_obj.parse_pcr_controls(xl=self.xl, rsl_plate_num=rsl_plate_num)
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ class RSLNamer(object):
|
|||||||
logger.info(f"got submission type: {self.submission_type}")
|
logger.info(f"got submission type: {self.submission_type}")
|
||||||
if self.submission_type:
|
if self.submission_type:
|
||||||
self.sub_object = BasicSubmission.find_polymorphic_subclass(polymorphic_identity=self.submission_type)
|
self.sub_object = BasicSubmission.find_polymorphic_subclass(polymorphic_identity=self.submission_type)
|
||||||
self.parsed_name = self.retrieve_rsl_number(filename=filename, regex=self.sub_object.get_regex(submission_type=submission_type))
|
self.parsed_name = self.retrieve_rsl_number(filename=filename, regex=self.sub_object.get_regex(
|
||||||
|
submission_type=submission_type))
|
||||||
if not data:
|
if not data:
|
||||||
data = dict(submission_type=self.submission_type)
|
data = dict(submission_type=self.submission_type)
|
||||||
if "submission_type" not in data.keys():
|
if "submission_type" not in data.keys():
|
||||||
@@ -50,23 +51,35 @@ class RSLNamer(object):
|
|||||||
Returns:
|
Returns:
|
||||||
str: parsed submission type
|
str: parsed submission type
|
||||||
"""
|
"""
|
||||||
def st_from_path(filename:Path) -> str:
|
|
||||||
if filename.exists():
|
def st_from_path(filepath: Path) -> str:
|
||||||
wb = load_workbook(filename)
|
"""
|
||||||
|
Sub def to get submissiontype from a file path
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filepath ():
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
if filepath.exists():
|
||||||
|
wb = load_workbook(filepath)
|
||||||
try:
|
try:
|
||||||
# NOTE: Gets first category in the metadata.
|
# NOTE: Gets first category in the metadata.
|
||||||
categories = wb.properties.category.split(";")
|
categories = wb.properties.category.split(";")
|
||||||
submission_type = next(item.strip().title() for item in categories)
|
submission_type = next(item.strip().title() for item in categories)
|
||||||
except (StopIteration, AttributeError):
|
except (StopIteration, AttributeError):
|
||||||
sts = {item.name: item.template_file_sheets for item in SubmissionType.query() if item.template_file}
|
sts = {item.name: item.template_file_sheets for item in SubmissionType.query() if
|
||||||
|
item.template_file}
|
||||||
try:
|
try:
|
||||||
submission_type = next(k.title() for k, v in sts.items() if wb.sheetnames == v)
|
submission_type = next(k.title() for k, v in sts.items() if wb.sheetnames == v)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
# NOTE: On failure recurse using filename as string for string method
|
# NOTE: On failure recurse using filepath as string for string method
|
||||||
submission_type = cls.retrieve_submission_type(filename=filename.stem.__str__())
|
submission_type = cls.retrieve_submission_type(filename=filepath.stem.__str__())
|
||||||
else:
|
else:
|
||||||
submission_type = cls.retrieve_submission_type(filename=filename.stem.__str__())
|
submission_type = cls.retrieve_submission_type(filename=filepath.stem.__str__())
|
||||||
return submission_type
|
return submission_type
|
||||||
|
|
||||||
def st_from_str(filename: str) -> str:
|
def st_from_str(filename: str) -> str:
|
||||||
if filename.startswith("tmp"):
|
if filename.startswith("tmp"):
|
||||||
return "Bacterial Culture"
|
return "Bacterial Culture"
|
||||||
@@ -78,9 +91,10 @@ class RSLNamer(object):
|
|||||||
submission_type = None
|
submission_type = None
|
||||||
logger.critical(f"No submission type found or submission type found!: {e}")
|
logger.critical(f"No submission type found or submission type found!: {e}")
|
||||||
return submission_type
|
return submission_type
|
||||||
|
|
||||||
match filename:
|
match filename:
|
||||||
case Path():
|
case Path():
|
||||||
submission_type = st_from_path(filename=filename)
|
submission_type = st_from_path(filepath=filename)
|
||||||
case str():
|
case str():
|
||||||
submission_type = st_from_str(filename=filename)
|
submission_type = st_from_str(filename=filename)
|
||||||
case _:
|
case _:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import logging, sys
|
import logging
|
||||||
from pydantic import BaseModel, field_validator, Field
|
from pydantic import BaseModel, field_validator, Field
|
||||||
from typing import List, ClassVar
|
from typing import List, ClassVar
|
||||||
from backend.db.models import *
|
from backend.db.models import *
|
||||||
@@ -16,7 +16,7 @@ class BaseOmni(BaseModel):
|
|||||||
try:
|
try:
|
||||||
return f"<{self.__class__.__name__}({self.name})>"
|
return f"<{self.__class__.__name__}({self.name})>"
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return f"<{self.__class__.__name__}(NO NAME)>"
|
return f"<{self.__class__.__name__}({self.__repr_name__})>"
|
||||||
|
|
||||||
@classproperty
|
@classproperty
|
||||||
def aliases(cls):
|
def aliases(cls):
|
||||||
@@ -478,7 +478,6 @@ class OmniProcess(BaseOmni):
|
|||||||
|
|
||||||
# NOTE: How am I going to figure out relatioinships without getting into recursion issues?
|
# NOTE: How am I going to figure out relatioinships without getting into recursion issues?
|
||||||
name: str = Field(default="", description="property") #: Process name
|
name: str = Field(default="", description="property") #: Process name
|
||||||
# version: str = Field(default="", description="property") #: Version (string to account for "in_use" or whatever)
|
|
||||||
submission_types: List[OmniSubmissionType] | List[str] = Field(default=[], description="relationship",
|
submission_types: List[OmniSubmissionType] | List[str] = Field(default=[], description="relationship",
|
||||||
title="SubmissionType")
|
title="SubmissionType")
|
||||||
equipment_roles: List[OmniEquipmentRole] | List[str] = Field(default=[], description="relationship",
|
equipment_roles: List[OmniEquipmentRole] | List[str] = Field(default=[], description="relationship",
|
||||||
@@ -507,13 +506,6 @@ class OmniProcess(BaseOmni):
|
|||||||
return ""
|
return ""
|
||||||
return value
|
return value
|
||||||
|
|
||||||
# @field_validator("version", mode="before")
|
|
||||||
# @classmethod
|
|
||||||
# def rescue_name_none(cls, value):
|
|
||||||
# if not value:
|
|
||||||
# return "1"
|
|
||||||
# return value
|
|
||||||
|
|
||||||
def to_sql(self):
|
def to_sql(self):
|
||||||
instance, new = self.class_object.query_or_create(name=self.name)
|
instance, new = self.class_object.query_or_create(name=self.name)
|
||||||
for st in self.submission_types:
|
for st in self.submission_types:
|
||||||
@@ -539,12 +531,8 @@ class OmniKitType(BaseOmni):
|
|||||||
class_object: ClassVar[Any] = KitType
|
class_object: ClassVar[Any] = KitType
|
||||||
|
|
||||||
name: str = Field(default="", description="property")
|
name: str = Field(default="", description="property")
|
||||||
kit_submissiontype_associations: List[OmniSubmissionTypeKitTypeAssociation] | List[str] = Field(default=[],
|
kit_submissiontype_associations: List[OmniSubmissionTypeKitTypeAssociation] | List[str] = Field(default=[], description="relationship", title="SubmissionTypeKitTypeAssociation")
|
||||||
description="relationship",
|
kit_reagentrole_associations: List[OmniKitTypeReagentRoleAssociation] | List[str] = Field(default=[], description="relationship", title="KitTypeReagentRoleAssociation")
|
||||||
title="SubmissionTypeKitTypeAssociation")
|
|
||||||
kit_reagentrole_associations: List[OmniKitTypeReagentRoleAssociation] | List[str] = Field(default=[],
|
|
||||||
description="relationship",
|
|
||||||
title="KitTypeReagentRoleAssociation")
|
|
||||||
processes: List[OmniProcess] | List[str] = Field(default=[], description="relationship", title="Process")
|
processes: List[OmniProcess] | List[str] = Field(default=[], description="relationship", title="Process")
|
||||||
|
|
||||||
@field_validator("name", mode="before")
|
@field_validator("name", mode="before")
|
||||||
@@ -577,7 +565,6 @@ class OmniKitType(BaseOmni):
|
|||||||
new_rr.append(new_assoc)
|
new_rr.append(new_assoc)
|
||||||
logger.debug(f"Setting kit_reagentrole_associations to {pformat([item.__dict__ for item in new_rr])}")
|
logger.debug(f"Setting kit_reagentrole_associations to {pformat([item.__dict__ for item in new_rr])}")
|
||||||
kit.kit_reagentrole_associations = new_rr
|
kit.kit_reagentrole_associations = new_rr
|
||||||
# sys.exit()
|
|
||||||
new_st = []
|
new_st = []
|
||||||
for st_assoc in self.kit_submissiontype_associations:
|
for st_assoc in self.kit_submissiontype_associations:
|
||||||
new_assoc = st_assoc.to_sql()
|
new_assoc = st_assoc.to_sql()
|
||||||
|
|||||||
@@ -288,8 +288,6 @@ class PydTips(BaseModel):
|
|||||||
tips = Tips.query(name=self.name, limit=1)
|
tips = Tips.query(name=self.name, limit=1)
|
||||||
# logger.debug(f"Tips query has yielded: {tips}")
|
# logger.debug(f"Tips query has yielded: {tips}")
|
||||||
assoc = SubmissionTipsAssociation.query_or_create(tips=tips, submission=submission, role=self.role, limit=1)
|
assoc = SubmissionTipsAssociation.query_or_create(tips=tips, submission=submission, role=self.role, limit=1)
|
||||||
# if assoc is None:
|
|
||||||
# assoc = SubmissionTipsAssociation(submission=submission, tips=tips, role_name=self.role)
|
|
||||||
return assoc, report
|
return assoc, report
|
||||||
|
|
||||||
|
|
||||||
@@ -355,14 +353,13 @@ class PydEquipment(BaseModel, extra='ignore'):
|
|||||||
# TODO: This seems precarious. What if there is more than one process?
|
# TODO: This seems precarious. What if there is more than one process?
|
||||||
# NOTE: It looks like the way fetching the processes is done in the SQL model, this shouldn't be a problem, but I'll include a failsafe.
|
# NOTE: It looks like the way fetching the processes is done in the SQL model, this shouldn't be a problem, but I'll include a failsafe.
|
||||||
# NOTE: I need to find a way to filter this by the kit involved.
|
# NOTE: I need to find a way to filter this by the kit involved.
|
||||||
|
|
||||||
if len(self.processes) > 1:
|
if len(self.processes) > 1:
|
||||||
process = Process.query(submissiontype=submission.get_submission_type(), kittype=extraction_kit, equipmentrole=self.role)
|
process = Process.query(submissiontype=submission.get_submission_type(), kittype=extraction_kit, equipmentrole=self.role)
|
||||||
else:
|
else:
|
||||||
process = Process.query(name=self.processes[0])
|
process = Process.query(name=self.processes[0])
|
||||||
if process is None:
|
if process is None:
|
||||||
logger.error(f"Found unknown process: {process}.")
|
logger.error(f"Found unknown process: {process}.")
|
||||||
logger.debug(f"Using process: {process}")
|
# logger.debug(f"Using process: {process}")
|
||||||
assoc.process = process
|
assoc.process = process
|
||||||
assoc.role = self.role
|
assoc.role = self.role
|
||||||
else:
|
else:
|
||||||
@@ -746,7 +743,16 @@ class PydSubmission(BaseModel, extra='allow'):
|
|||||||
output = {k: self.filter_field(k) for k in fields}
|
output = {k: self.filter_field(k) for k in fields}
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def filter_field(self, key: str):
|
def filter_field(self, key: str) -> Any:
|
||||||
|
"""
|
||||||
|
Attempts to get value from field dictionary
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key (str): name of the field of interest
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Any (): Value found.
|
||||||
|
"""
|
||||||
item = getattr(self, key)
|
item = getattr(self, key)
|
||||||
match item:
|
match item:
|
||||||
case dict():
|
case dict():
|
||||||
@@ -780,9 +786,8 @@ class PydSubmission(BaseModel, extra='allow'):
|
|||||||
"""
|
"""
|
||||||
report = Report()
|
report = Report()
|
||||||
dicto = self.improved_dict()
|
dicto = self.improved_dict()
|
||||||
logger.debug(f"Pydantic submission type: {self.submission_type['value']}")
|
# logger.debug(f"Pydantic submission type: {self.submission_type['value']}")
|
||||||
logger.debug(f"Pydantic improved_dict: {pformat(dicto)}")
|
# logger.debug(f"Pydantic improved_dict: {pformat(dicto)}")
|
||||||
# At this point, pcr_info is not duplicated
|
|
||||||
instance, result = BasicSubmission.query_or_create(submission_type=self.submission_type['value'],
|
instance, result = BasicSubmission.query_or_create(submission_type=self.submission_type['value'],
|
||||||
rsl_plate_num=self.rsl_plate_num['value'])
|
rsl_plate_num=self.rsl_plate_num['value'])
|
||||||
# logger.debug(f"Created or queried instance: {instance}")
|
# logger.debug(f"Created or queried instance: {instance}")
|
||||||
@@ -792,8 +797,7 @@ class PydSubmission(BaseModel, extra='allow'):
|
|||||||
report.add_result(result)
|
report.add_result(result)
|
||||||
self.handle_duplicate_samples()
|
self.handle_duplicate_samples()
|
||||||
for key, value in dicto.items():
|
for key, value in dicto.items():
|
||||||
logger.debug(f"Checking key {key}, value {value}")
|
# logger.debug(f"Checking key {key}, value {value}")
|
||||||
# At this point, pcr_info is not duplicated.
|
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
try:
|
try:
|
||||||
value = value['value']
|
value = value['value']
|
||||||
@@ -849,8 +853,7 @@ class PydSubmission(BaseModel, extra='allow'):
|
|||||||
value = value
|
value = value
|
||||||
instance.set_attribute(key=key, value=value)
|
instance.set_attribute(key=key, value=value)
|
||||||
case item if item in instance.jsons:
|
case item if item in instance.jsons:
|
||||||
# At this point pcr_info is not duplicated
|
# logger.debug(f"Validating json value: {item} to value:{pformat(value)}")
|
||||||
logger.debug(f"Validating json value: {item} to value:{pformat(value)}")
|
|
||||||
try:
|
try:
|
||||||
ii = value.items()
|
ii = value.items()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@@ -860,8 +863,7 @@ class PydSubmission(BaseModel, extra='allow'):
|
|||||||
value[k] = v.strftime("%Y-%m-%d %H:%M:%S")
|
value[k] = v.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
logger.debug(f"Setting json value: {item} to value:{pformat(value)}")
|
# logger.debug(f"Setting json value: {item} to value:{pformat(value)}")
|
||||||
# At this point, pcr_info is not duplicated.
|
|
||||||
instance.set_attribute(key=key, value=value)
|
instance.set_attribute(key=key, value=value)
|
||||||
case _:
|
case _:
|
||||||
try:
|
try:
|
||||||
@@ -878,7 +880,6 @@ class PydSubmission(BaseModel, extra='allow'):
|
|||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
logger.warning(f"{key} already == {value} so no updating.")
|
logger.warning(f"{key} already == {value} so no updating.")
|
||||||
logger.debug(f"Entering cost calculation for {instance}")
|
|
||||||
try:
|
try:
|
||||||
instance.calculate_base_cost()
|
instance.calculate_base_cost()
|
||||||
except (TypeError, AttributeError) as e:
|
except (TypeError, AttributeError) as e:
|
||||||
@@ -937,7 +938,6 @@ class PydSubmission(BaseModel, extra='allow'):
|
|||||||
"/", "")
|
"/", "")
|
||||||
return render
|
return render
|
||||||
|
|
||||||
# @report_result
|
|
||||||
def check_kit_integrity(self, extraction_kit: str | dict | None = None, exempt: List[PydReagent] = []) -> Tuple[
|
def check_kit_integrity(self, extraction_kit: str | dict | None = None, exempt: List[PydReagent] = []) -> Tuple[
|
||||||
List[PydReagent], Report, List[PydReagent]]:
|
List[PydReagent], Report, List[PydReagent]]:
|
||||||
"""
|
"""
|
||||||
@@ -1212,7 +1212,6 @@ class PydIridaControl(BaseModel, extra='ignore'):
|
|||||||
contains: list | dict #: unstructured hashes in contains.tsv for each organism
|
contains: list | dict #: unstructured hashes in contains.tsv for each organism
|
||||||
matches: list | dict #: unstructured hashes in matches.tsv for each organism
|
matches: list | dict #: unstructured hashes in matches.tsv for each organism
|
||||||
kraken: list | dict #: unstructured output from kraken_report
|
kraken: list | dict #: unstructured output from kraken_report
|
||||||
# subtype: str #: EN-NOS, MCS-NOS, etc
|
|
||||||
subtype: Literal["ATCC49226", "ATCC49619", "EN-NOS", "EN-SSTI", "MCS-NOS", "MCS-SSTI", "SN-NOS", "SN-SSTI"]
|
subtype: Literal["ATCC49226", "ATCC49619", "EN-NOS", "EN-SSTI", "MCS-NOS", "MCS-SSTI", "SN-NOS", "SN-SSTI"]
|
||||||
refseq_version: str #: version of refseq used in fastq parsing
|
refseq_version: str #: version of refseq used in fastq parsing
|
||||||
kraken2_version: str
|
kraken2_version: str
|
||||||
@@ -1264,7 +1263,6 @@ class PydProcess(BaseModel, extra="allow"):
|
|||||||
instance = Process.query(name=self.name)
|
instance = Process.query(name=self.name)
|
||||||
if not instance:
|
if not instance:
|
||||||
instance = Process()
|
instance = Process()
|
||||||
# dicto = instance.omnigui_instance_dict
|
|
||||||
fields = [item for item in self.model_fields]
|
fields = [item for item in self.model_fields]
|
||||||
for field in fields:
|
for field in fields:
|
||||||
logger.debug(f"Field: {field}")
|
logger.debug(f"Field: {field}")
|
||||||
@@ -1315,5 +1313,3 @@ class PydElastic(BaseModel, extra="allow", arbitrary_types_allowed=True):
|
|||||||
field_value = getattr(self, field)
|
field_value = getattr(self, field)
|
||||||
self.instance.__setattr__(field, field_value)
|
self.instance.__setattr__(field, field_value)
|
||||||
return self.instance
|
return self.instance
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,10 +19,6 @@ class IridaFigure(CustomFigure):
|
|||||||
|
|
||||||
super().__init__(df=df, modes=modes, settings=settings)
|
super().__init__(df=df, modes=modes, settings=settings)
|
||||||
self.df = df
|
self.df = df
|
||||||
# try:
|
|
||||||
# months = int(settings['months'])
|
|
||||||
# except KeyError:
|
|
||||||
# months = 6
|
|
||||||
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'])
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,8 @@ Functions for constructing irida controls graphs using plotly.
|
|||||||
"""
|
"""
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from . import CustomFigure
|
from . import CustomFigure
|
||||||
import plotly.express as px
|
|
||||||
import pandas as pd
|
|
||||||
from PyQt6.QtWidgets import QWidget
|
from PyQt6.QtWidgets import QWidget
|
||||||
import logging
|
import logging, plotly.express as px, pandas as pd
|
||||||
|
|
||||||
logger = logging.getLogger(f"submissions.{__name__}")
|
logger = logging.getLogger(f"submissions.{__name__}")
|
||||||
|
|
||||||
@@ -17,10 +15,6 @@ class PCRFigure(CustomFigure):
|
|||||||
months: int = 6):
|
months: int = 6):
|
||||||
super().__init__(df=df, modes=modes, settings=settings)
|
super().__init__(df=df, modes=modes, settings=settings)
|
||||||
self.df = df
|
self.df = df
|
||||||
# try:
|
|
||||||
# months = int(settings['months'])
|
|
||||||
# except KeyError:
|
|
||||||
# months = 6
|
|
||||||
self.construct_chart(df=df)
|
self.construct_chart(df=df)
|
||||||
|
|
||||||
def construct_chart(self, df: pd.DataFrame):
|
def construct_chart(self, df: pd.DataFrame):
|
||||||
|
|||||||
@@ -3,10 +3,8 @@ Construct turnaround time charts
|
|||||||
"""
|
"""
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from . import CustomFigure
|
from . import CustomFigure
|
||||||
import plotly.express as px
|
|
||||||
import pandas as pd
|
|
||||||
from PyQt6.QtWidgets import QWidget
|
from PyQt6.QtWidgets import QWidget
|
||||||
import logging
|
import logging, plotly.express as px, pandas as pd
|
||||||
|
|
||||||
logger = logging.getLogger(f"submissions.{__name__}")
|
logger = logging.getLogger(f"submissions.{__name__}")
|
||||||
|
|
||||||
|
|||||||
@@ -230,7 +230,9 @@ class App(QMainWindow):
|
|||||||
def update_data(self):
|
def update_data(self):
|
||||||
self.table_widget.sub_wid.setData(page=self.table_widget.pager.page_anchor, page_size=page_size)
|
self.table_widget.sub_wid.setData(page=self.table_widget.pager.page_anchor, page_size=page_size)
|
||||||
|
|
||||||
|
# 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
|
||||||
dlg = ManagerWindow(parent=self, object_type=Organization, extras=[], add_edit='edit', managers=set())
|
dlg = ManagerWindow(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()
|
||||||
@@ -245,13 +247,9 @@ class App(QMainWindow):
|
|||||||
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__)}")
|
||||||
# with open(f"{output.name}.obj", "wb") as f:
|
|
||||||
# pickle.dump(output, f)
|
|
||||||
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)
|
||||||
# with open(f"{output.name}.sql", "wb") as f:
|
|
||||||
# pickle.dump(sql, f)
|
|
||||||
sql.save()
|
sql.save()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -113,10 +113,6 @@ class ControlsViewer(InfoPane):
|
|||||||
if issubclass(self.fig.__class__, CustomFigure):
|
if issubclass(self.fig.__class__, CustomFigure):
|
||||||
self.save_button.setEnabled(True)
|
self.save_button.setEnabled(True)
|
||||||
# NOTE: construct html for webview
|
# NOTE: construct html for webview
|
||||||
# try:
|
|
||||||
# html = self.fig.html
|
|
||||||
# except AttributeError:
|
|
||||||
# html = ""
|
|
||||||
self.webview.setHtml(self.fig.html)
|
self.webview.setHtml(self.fig.html)
|
||||||
self.webview.update()
|
self.webview.update()
|
||||||
return report
|
return report
|
||||||
|
|||||||
@@ -28,12 +28,12 @@ class AddEdit(QDialog):
|
|||||||
self.object_type = instance.__class__
|
self.object_type = instance.__class__
|
||||||
# self.managers = deepcopy(managers)
|
# self.managers = deepcopy(managers)
|
||||||
self.managers = managers
|
self.managers = managers
|
||||||
if instance.level < 2:
|
# if instance.level < 2:
|
||||||
try:
|
# try:
|
||||||
logger.debug(f"Parent instance: {self.parent().instance}")
|
# logger.debug(f"Parent instance: {self.parent().instance}")
|
||||||
self.managers.add(self.parent().instance)
|
# self.managers.add(self.parent().instance)
|
||||||
except AttributeError:
|
# except AttributeError:
|
||||||
pass
|
# pass
|
||||||
logger.debug(f"Managers: {managers}")
|
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
|
||||||
@@ -111,14 +111,13 @@ class EditProperty(QWidget):
|
|||||||
case _:
|
case _:
|
||||||
logger.error(f"{column_type} not a supported type.")
|
logger.error(f"{column_type} not a supported type.")
|
||||||
return
|
return
|
||||||
# if not self.is_list:
|
|
||||||
self.layout.addWidget(self.label, 0, 0, 1, 1)
|
self.layout.addWidget(self.label, 0, 0, 1, 1)
|
||||||
self.layout.addWidget(self.widget, 0, 1, 1, 3)
|
self.layout.addWidget(self.widget, 0, 1, 1, 3)
|
||||||
self.setLayout(self.layout)
|
self.setLayout(self.layout)
|
||||||
|
|
||||||
def relationship_property_set(self, relationship, value=None):
|
def relationship_property_set(self, relationship, value=None):
|
||||||
self.widget = QComboBox()
|
self.widget = QComboBox()
|
||||||
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}")
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ class ManagerWindow(QDialog):
|
|||||||
self.manager = None
|
self.manager = None
|
||||||
else:
|
else:
|
||||||
self.manager = manager
|
self.manager = manager
|
||||||
|
|
||||||
# logger.debug(f"Managers: {managers}")
|
# logger.debug(f"Managers: {managers}")
|
||||||
self.extras = extras
|
self.extras = extras
|
||||||
self.context = kwargs
|
self.context = kwargs
|
||||||
@@ -104,7 +103,6 @@ class ManagerWindow(QDialog):
|
|||||||
self.options.setEditable(False)
|
self.options.setEditable(False)
|
||||||
self.options.setMinimumWidth(self.minimumWidth())
|
self.options.setMinimumWidth(self.minimumWidth())
|
||||||
self.layout.addWidget(self.options, 1, 0, 1, 1)
|
self.layout.addWidget(self.options, 1, 0, 1, 1)
|
||||||
# if len(options) > 0:
|
|
||||||
self.add_button = QPushButton("Add New")
|
self.add_button = QPushButton("Add New")
|
||||||
self.layout.addWidget(self.add_button, 1, 1, 1, 1)
|
self.layout.addWidget(self.add_button, 1, 1, 1, 1)
|
||||||
self.add_button.clicked.connect(self.add_new)
|
self.add_button.clicked.connect(self.add_new)
|
||||||
@@ -126,12 +124,6 @@ class ManagerWindow(QDialog):
|
|||||||
for item in deletes:
|
for item in deletes:
|
||||||
item.setParent(None)
|
item.setParent(None)
|
||||||
# logger.debug(f"Current options text lower: {self.options.currentText().lower()}")
|
# logger.debug(f"Current options text lower: {self.options.currentText().lower()}")
|
||||||
# NOTE: Find the instance this manager will update
|
|
||||||
# try:
|
|
||||||
# check = "blank" not in self.options.currentText().lower() and self.options.currentText() != ""
|
|
||||||
# except AttributeError:
|
|
||||||
# check = False
|
|
||||||
# if check:
|
|
||||||
if self.add_edit == "edit" and initial:
|
if self.add_edit == "edit" and initial:
|
||||||
# logger.debug(f"Querying with {self.options.currentText()}")
|
# logger.debug(f"Querying with {self.options.currentText()}")
|
||||||
self.instance = self.class_object.query(name=self.options.currentText(), limit=1)
|
self.instance = self.class_object.query(name=self.options.currentText(), limit=1)
|
||||||
@@ -173,7 +165,6 @@ class ManagerWindow(QDialog):
|
|||||||
Returns:
|
Returns:
|
||||||
Any: The instance with updated fields.
|
Any: The instance with updated fields.
|
||||||
"""
|
"""
|
||||||
# TODO: Need Relationship property here too?
|
|
||||||
results = [item.parse_form() for item in self.findChildren(EditProperty)]
|
results = [item.parse_form() for item in self.findChildren(EditProperty)]
|
||||||
for result in results:
|
for result in results:
|
||||||
# logger.debug(f"Incoming result: {result}")
|
# logger.debug(f"Incoming result: {result}")
|
||||||
@@ -211,7 +202,6 @@ class ManagerWindow(QDialog):
|
|||||||
else:
|
else:
|
||||||
value = current_value + [data]
|
value = current_value + [data]
|
||||||
setattr(self.instance, name, value)
|
setattr(self.instance, name, value)
|
||||||
# self.instance.save()
|
|
||||||
|
|
||||||
def toggle_textedit(self, caller_child=None):
|
def toggle_textedit(self, caller_child=None):
|
||||||
already_exists = self.findChildren(LargeTextEdit)
|
already_exists = self.findChildren(LargeTextEdit)
|
||||||
@@ -305,7 +295,6 @@ class EditRelationship(QWidget):
|
|||||||
# logger.debug(f"self.relationship: {self.relationship}")
|
# logger.debug(f"self.relationship: {self.relationship}")
|
||||||
# logger.debug(f"Relationship uses list: {self.relationship.property.uselist}")
|
# logger.debug(f"Relationship uses list: {self.relationship.property.uselist}")
|
||||||
# NOTE: value is a database object in this case.
|
# NOTE: value is a database object in this case.
|
||||||
|
|
||||||
# logger.debug(f"Data for edit relationship: {self.data}")
|
# logger.debug(f"Data for edit relationship: {self.data}")
|
||||||
self.widget = QTableView()
|
self.widget = QTableView()
|
||||||
self.add_button = QPushButton("Add New")
|
self.add_button = QPushButton("Add New")
|
||||||
@@ -319,7 +308,6 @@ class EditRelationship(QWidget):
|
|||||||
else:
|
else:
|
||||||
value = []
|
value = []
|
||||||
self.data = value
|
self.data = value
|
||||||
# self.update_buttons()
|
|
||||||
checked_manager, is_primary = check_object_in_manager(self.parent().manager, self.objectName())
|
checked_manager, is_primary = check_object_in_manager(self.parent().manager, self.objectName())
|
||||||
if checked_manager:
|
if checked_manager:
|
||||||
logger.debug(f"Checked manager for {self.objectName()}: {checked_manager}")
|
logger.debug(f"Checked manager for {self.objectName()}: {checked_manager}")
|
||||||
@@ -369,7 +357,6 @@ class EditRelationship(QWidget):
|
|||||||
new_instance = dlg.parse_form()
|
new_instance = dlg.parse_form()
|
||||||
# NOTE: My custom __setattr__ should take care of any list problems.
|
# NOTE: My custom __setattr__ should take care of any list problems.
|
||||||
self.parent().instance.__setattr__(self.objectName(), new_instance)
|
self.parent().instance.__setattr__(self.objectName(), new_instance)
|
||||||
# self.parent().instance.save()
|
|
||||||
self.parent().update_data()
|
self.parent().update_data()
|
||||||
|
|
||||||
def add_existing(self):
|
def add_existing(self):
|
||||||
@@ -381,7 +368,6 @@ class EditRelationship(QWidget):
|
|||||||
instance = self.class_object.query(**row)
|
instance = self.class_object.query(**row)
|
||||||
# NOTE: My custom __setattr__ should take care of any list problems.
|
# NOTE: My custom __setattr__ should take care of any list problems.
|
||||||
self.parent().instance.__setattr__(self.objectName(), instance)
|
self.parent().instance.__setattr__(self.objectName(), instance)
|
||||||
# self.parent().instance.save()
|
|
||||||
self.parent().update_data()
|
self.parent().update_data()
|
||||||
|
|
||||||
def set_data(self) -> None:
|
def set_data(self) -> None:
|
||||||
@@ -420,7 +406,6 @@ class EditRelationship(QWidget):
|
|||||||
Args:
|
Args:
|
||||||
event (_type_): the item of interest
|
event (_type_): the item of interest
|
||||||
"""
|
"""
|
||||||
# print(self.widget.isEnabled())
|
|
||||||
if not self.widget.isEnabled():
|
if not self.widget.isEnabled():
|
||||||
logger.warning(f"{self.objectName()} is disabled.")
|
logger.warning(f"{self.objectName()} is disabled.")
|
||||||
return
|
return
|
||||||
@@ -471,7 +456,6 @@ class EditRelationship(QWidget):
|
|||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logger.error(f"Remove failed for {self.objectName().lower()} due to {e}.")
|
logger.error(f"Remove failed for {self.objectName().lower()} due to {e}.")
|
||||||
self.parent().instance.save()
|
self.parent().instance.save()
|
||||||
# self.parent().update_data()
|
|
||||||
self.set_data()
|
self.set_data()
|
||||||
|
|
||||||
def parse_form(self):
|
def parse_form(self):
|
||||||
@@ -555,7 +539,6 @@ class JsonEditScreen(QDialog):
|
|||||||
output.append(value)
|
output.append(value)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Inappropriate data type: {type(self.json_field)}")
|
raise ValueError(f"Inappropriate data type: {type(self.json_field)}")
|
||||||
# output[key] = value
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,6 @@ class ManagerWindow(QDialog):
|
|||||||
# logger.debug(f"Querying with {self.options.currentText()}")
|
# logger.debug(f"Querying with {self.options.currentText()}")
|
||||||
self.instance = self.class_object.query(name=self.options.currentText(), limit=1)
|
self.instance = self.class_object.query(name=self.options.currentText(), limit=1)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# self.instance = None
|
|
||||||
pass
|
pass
|
||||||
# logger.debug(f"Instance: {self.instance}")
|
# logger.debug(f"Instance: {self.instance}")
|
||||||
if not self.instance:
|
if not self.instance:
|
||||||
@@ -164,11 +163,6 @@ class ManagerWindow(QDialog):
|
|||||||
# NOTE: RelationshipDeclareds will be given a list of existing related objects.
|
# NOTE: RelationshipDeclareds will be given a list of existing related objects.
|
||||||
case "relationship":
|
case "relationship":
|
||||||
# NOTE: field.comparator.class_object.class_ gives the relationship class
|
# NOTE: field.comparator.class_object.class_ gives the relationship class
|
||||||
# try:
|
|
||||||
# logger.debug(
|
|
||||||
# f"Creating relationship widget with value: {[pformat(item.__dict__) for item in value]}")
|
|
||||||
# except AttributeError:
|
|
||||||
# logger.debug(f"Creating relationship widget with value: {value}")
|
|
||||||
widget = EditRelationship(self, key=key, class_object=info.title, value=value)
|
widget = EditRelationship(self, key=key, class_object=info.title, value=value)
|
||||||
case _:
|
case _:
|
||||||
continue
|
continue
|
||||||
@@ -294,7 +288,6 @@ class EditRelationship(QWidget):
|
|||||||
value = []
|
value = []
|
||||||
self.data = value
|
self.data = value
|
||||||
# logger.debug(f"Set data: {self.data}")
|
# logger.debug(f"Set data: {self.data}")
|
||||||
# self.update_buttons()
|
|
||||||
# logger.debug(f"Parent manager: {self.parent().manager}")
|
# logger.debug(f"Parent manager: {self.parent().manager}")
|
||||||
checked_manager, is_primary = check_object_in_manager(self.parent().manager, self.objectName())
|
checked_manager, is_primary = check_object_in_manager(self.parent().manager, self.objectName())
|
||||||
if checked_manager:
|
if checked_manager:
|
||||||
@@ -374,8 +367,6 @@ class EditRelationship(QWidget):
|
|||||||
return
|
return
|
||||||
logger.debug(f"Updating \n{pformat(obj)} with \n{pformat(new_instance.__dict__)}")
|
logger.debug(f"Updating \n{pformat(obj)} with \n{pformat(new_instance.__dict__)}")
|
||||||
obj.__dict__.update(new_instance.__dict__)
|
obj.__dict__.update(new_instance.__dict__)
|
||||||
# # self.parent().omni_object.__setattr__(self.objectName(), obj)
|
|
||||||
# # instance.__dict__.update(new_instance.__dict__)
|
|
||||||
logger.debug(f"Final instance: {pformat(self.parent().omni_object.__dict__)}")
|
logger.debug(f"Final instance: {pformat(self.parent().omni_object.__dict__)}")
|
||||||
# NOTE: somewhere in the update_data I'm losing changes.
|
# NOTE: somewhere in the update_data I'm losing changes.
|
||||||
self.parent().update_data()
|
self.parent().update_data()
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ class SearchBox(QDialog):
|
|||||||
self.setLayout(self.layout)
|
self.setLayout(self.layout)
|
||||||
self.setWindowTitle(f"Search {self.object_type.__name__}")
|
self.setWindowTitle(f"Search {self.object_type.__name__}")
|
||||||
self.update_widgets()
|
self.update_widgets()
|
||||||
# self.update_data()
|
|
||||||
if returnable:
|
if returnable:
|
||||||
QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
|
QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
|
||||||
self.buttonBox = QDialogButtonBox(QBtn)
|
self.buttonBox = QDialogButtonBox(QBtn)
|
||||||
@@ -60,7 +59,6 @@ class SearchBox(QDialog):
|
|||||||
Changes form inputs based on sample type
|
Changes form inputs based on sample type
|
||||||
"""
|
"""
|
||||||
search_fields = []
|
search_fields = []
|
||||||
# search_fields = self.object_type.searchables
|
|
||||||
logger.debug(f"Search fields: {search_fields}")
|
logger.debug(f"Search fields: {search_fields}")
|
||||||
deletes = [item for item in self.findChildren(FieldSearch)]
|
deletes = [item for item in self.findChildren(FieldSearch)]
|
||||||
for item in deletes:
|
for item in deletes:
|
||||||
@@ -69,7 +67,6 @@ class SearchBox(QDialog):
|
|||||||
if not self.sub_class:
|
if not self.sub_class:
|
||||||
logger.warning(f"No subclass selected.")
|
logger.warning(f"No subclass selected.")
|
||||||
self.update_data()
|
self.update_data()
|
||||||
# return
|
|
||||||
else:
|
else:
|
||||||
if self.sub_class.currentText() == "Any":
|
if self.sub_class.currentText() == "Any":
|
||||||
self.object_type = self.original_type
|
self.object_type = self.original_type
|
||||||
|
|||||||
@@ -315,8 +315,6 @@ class SubmissionFormWidget(QWidget):
|
|||||||
query = self.findChildren(QWidget, name=object_name)
|
query = self.findChildren(QWidget, name=object_name)
|
||||||
else:
|
else:
|
||||||
query = self.findChildren(QWidget)
|
query = self.findChildren(QWidget)
|
||||||
# if object_name is not None:
|
|
||||||
# query = [widget for widget in query if widget.objectName() == object_name]
|
|
||||||
return query
|
return query
|
||||||
|
|
||||||
@report_result
|
@report_result
|
||||||
@@ -338,7 +336,6 @@ class SubmissionFormWidget(QWidget):
|
|||||||
if self.disabler.checkbox.isChecked():
|
if self.disabler.checkbox.isChecked():
|
||||||
_, result, _ = self.pyd.check_kit_integrity(exempt=exempt)
|
_, result, _ = self.pyd.check_kit_integrity(exempt=exempt)
|
||||||
report.add_result(result)
|
report.add_result(result)
|
||||||
# result = self.pyd.check_reagent_expiries(exempt=exempt)
|
|
||||||
if len(result.results) > 0:
|
if len(result.results) > 0:
|
||||||
return report
|
return report
|
||||||
base_submission = self.pyd.to_sql()
|
base_submission = self.pyd.to_sql()
|
||||||
@@ -372,7 +369,6 @@ class SubmissionFormWidget(QWidget):
|
|||||||
pass
|
pass
|
||||||
# NOTE: add reagents to submission object
|
# NOTE: add reagents to submission object
|
||||||
if base_submission is None:
|
if base_submission is None:
|
||||||
# self.app.table_widget.sub_wid.setData()
|
|
||||||
return
|
return
|
||||||
for reagent in base_submission.reagents:
|
for reagent in base_submission.reagents:
|
||||||
reagent.update_last_used(kit=base_submission.extraction_kit)
|
reagent.update_last_used(kit=base_submission.extraction_kit)
|
||||||
@@ -752,11 +748,9 @@ class SubmissionFormWidget(QWidget):
|
|||||||
looked_up_reg = None
|
looked_up_reg = None
|
||||||
if looked_up_reg:
|
if looked_up_reg:
|
||||||
try:
|
try:
|
||||||
# relevant_reagents.remove(str(looked_up_reg.lot))
|
|
||||||
relevant_reagents.insert(0, relevant_reagents.pop(relevant_reagents.index(looked_up_reg.lot)))
|
relevant_reagents.insert(0, relevant_reagents.pop(relevant_reagents.index(looked_up_reg.lot)))
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logger.error(f"Error reordering relevant reagents: {e}")
|
logger.error(f"Error reordering relevant reagents: {e}")
|
||||||
# relevant_reagents.insert(0, str(looked_up_reg.lot))
|
|
||||||
else:
|
else:
|
||||||
if len(relevant_reagents) > 1:
|
if len(relevant_reagents) > 1:
|
||||||
idx = relevant_reagents.index(str(reagent.lot))
|
idx = relevant_reagents.index(str(reagent.lot))
|
||||||
|
|||||||
@@ -2,16 +2,11 @@
|
|||||||
Contains miscellaenous functions used by both frontend and backend.
|
Contains miscellaenous functions used by both frontend and backend.
|
||||||
'''
|
'''
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import builtins
|
import builtins, importlib, time, logging, re, yaml, sys, os, stat, platform, getpass, json, numpy as np, pandas as pd
|
||||||
import importlib
|
|
||||||
import time
|
|
||||||
from datetime import date, datetime, timedelta
|
from datetime import date, datetime, timedelta
|
||||||
from json import JSONDecodeError
|
from json import JSONDecodeError
|
||||||
import logging, re, yaml, sys, os, stat, platform, getpass, json, numpy as np, pandas as pd
|
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from inspect import getmembers, isfunction, stack
|
from inspect import getmembers, isfunction, stack
|
||||||
from types import GeneratorType
|
|
||||||
|
|
||||||
from dateutil.easter import easter
|
from dateutil.easter import easter
|
||||||
from jinja2 import Environment, FileSystemLoader
|
from jinja2 import Environment, FileSystemLoader
|
||||||
from logging import handlers
|
from logging import handlers
|
||||||
@@ -19,11 +14,9 @@ from pathlib import Path
|
|||||||
from sqlalchemy.orm import Session, InstrumentedAttribute
|
from sqlalchemy.orm import Session, InstrumentedAttribute
|
||||||
from sqlalchemy import create_engine, text, MetaData
|
from sqlalchemy import create_engine, text, MetaData
|
||||||
from pydantic import field_validator, BaseModel, Field
|
from pydantic import field_validator, BaseModel, Field
|
||||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
from pydantic_settings import BaseSettings, SettingsConfigDict, PydanticBaseSettingsSource, YamlConfigSettingsSource
|
||||||
from typing import Any, Tuple, Literal, List, Generator
|
from typing import Any, Tuple, Literal, List, Generator
|
||||||
|
|
||||||
from sqlalchemy.orm.relationships import _RelationshipDeclared
|
from sqlalchemy.orm.relationships import _RelationshipDeclared
|
||||||
|
|
||||||
from __init__ import project_path
|
from __init__ import project_path
|
||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
from tkinter import Tk # NOTE: This is for choosing database path before app is created.
|
from tkinter import Tk # NOTE: This is for choosing database path before app is created.
|
||||||
@@ -148,7 +141,6 @@ def check_key_or_attr(key: str, interest: dict | object, check_none: bool = Fals
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def check_not_nan(cell_contents) -> bool:
|
def check_not_nan(cell_contents) -> bool:
|
||||||
"""
|
"""
|
||||||
Check to ensure excel sheet cell contents are not blank.
|
Check to ensure excel sheet cell contents are not blank.
|
||||||
@@ -432,7 +424,6 @@ class Settings(BaseSettings, extra="allow"):
|
|||||||
return package
|
return package
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.set_from_db()
|
self.set_from_db()
|
||||||
self.set_scripts()
|
self.set_scripts()
|
||||||
@@ -1231,3 +1222,29 @@ class classproperty(property):
|
|||||||
builtins.classproperty = classproperty
|
builtins.classproperty = classproperty
|
||||||
|
|
||||||
ctx = get_config(None)
|
ctx = get_config(None)
|
||||||
|
|
||||||
|
class Settings2(BaseSettings, extra="allow"):
|
||||||
|
|
||||||
|
model_config = SettingsConfigDict(yaml_file="C:\\Users\lwark\AppData\Local\submissions\config\config.yml",
|
||||||
|
yaml_file_encoding='utf-8')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def settings_customise_sources(
|
||||||
|
cls,
|
||||||
|
settings_cls: type[BaseSettings],
|
||||||
|
init_settings: PydanticBaseSettingsSource,
|
||||||
|
env_settings: PydanticBaseSettingsSource,
|
||||||
|
dotenv_settings: PydanticBaseSettingsSource,
|
||||||
|
file_secret_settings: PydanticBaseSettingsSource,
|
||||||
|
) -> tuple[PydanticBaseSettingsSource, ...]:
|
||||||
|
return (
|
||||||
|
YamlConfigSettingsSource(settings_cls),
|
||||||
|
init_settings,
|
||||||
|
env_settings,
|
||||||
|
dotenv_settings,
|
||||||
|
file_secret_settings,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user