Post code-cleanup, moments before disaster.

This commit is contained in:
lwark
2025-03-26 14:19:13 -05:00
parent 3ec79fdcfe
commit d844211e1b
21 changed files with 183 additions and 285 deletions

View File

@@ -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.
- [ ] 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 omni_search and sample_search
- [x] Allow parsing of custom fields to a json 'custom' field in _basicsubmissions

View File

@@ -56,6 +56,12 @@ class BaseClass(Base):
omni_inheritable = []
searchables = []
def __repr__(self) -> str:
try:
return f"<{self.__class__.__name__}({self.name})>"
except AttributeError:
return f"<{self.__class__.__name__}({self.__name__})>"
# @classproperty
# def skip_on_edit(cls):
# if "association" in cls.__name__.lower() or cls.__name__.lower() == "discount":
@@ -444,6 +450,9 @@ class BaseClass(Base):
else:
return super().__setattr__(key, value)
def delete(self):
logger.error(f"Delete has not been implemented for {self.__class__.__name__}")
class ConfigItem(BaseClass):
"""

View File

@@ -2,6 +2,8 @@
All control related models.
"""
from __future__ import annotations
import itertools
from pprint import pformat
from PyQt6.QtWidgets import QWidget, QCheckBox, QLabel
from pandas import DataFrame
@@ -16,7 +18,6 @@ from typing import List, Literal, Tuple, Generator
from dateutil.parser import parse
from re import Pattern
logger = logging.getLogger(f"submissions.{__name__}")
@@ -29,8 +30,8 @@ class ControlType(BaseClass):
targets = Column(JSON) #: organisms checked for
instances = relationship("Control", back_populates="controltype") #: control samples created of this type.
def __repr__(self) -> str:
return f"<ControlType({self.name})>"
# def __repr__(self) -> str:
# return f"<ControlType({self.name})>"
@classmethod
@setup_lookup
@@ -88,7 +89,7 @@ class ControlType(BaseClass):
Returns:
Control: Associated Control class
"""
"""
return Control.find_polymorphic_subclass(polymorphic_identity=self.name)
@classmethod
@@ -103,7 +104,7 @@ class ControlType(BaseClass):
return (k for k, v in ct.items() if v)
@classmethod
def build_positive_regex(cls, control_type:str) -> Pattern:
def build_positive_regex(cls, control_type: str) -> Pattern:
"""
Creates a re.Pattern that will look for positive control types
@@ -305,10 +306,10 @@ class PCRControl(Control):
id = Column(INTEGER, ForeignKey('_control.id'), primary_key=True)
subtype = Column(String(16)) #: PC or NC
target = Column(String(16)) #: N1, N2, etc.
ct = Column(FLOAT) #: PCR result
ct = Column(FLOAT) #: PCR result
reagent_lot = Column(String(64), ForeignKey("_reagent.lot", ondelete="SET NULL",
name="fk_reagent_lot"))
reagent = relationship("Reagent", foreign_keys=reagent_lot) #: reagent used for this control
reagent = relationship("Reagent", foreign_keys=reagent_lot) #: reagent used for this control
__mapper_args__ = dict(polymorphic_identity="PCR Control",
polymorphic_load="inline",
@@ -320,7 +321,7 @@ class PCRControl(Control):
Returns:
dict: Output dict of name, ct, subtype, target, reagent_lot and submitted_date
"""
"""
return dict(
name=self.name,
ct=self.ct,
@@ -343,7 +344,7 @@ class PCRControl(Control):
Returns:
Tuple[Report, "PCRFigure"]: Report of status and resulting figure.
"""
"""
from frontend.visualizations.pcr_charts import PCRFigure
parent.mode_typer.clear()
parent.mode_typer.setEnabled(False)
@@ -352,6 +353,7 @@ class PCRControl(Control):
end_date=chart_settings['end_date'])
data = [control.to_sub_dict() for control in controls]
df = DataFrame.from_records(data)
# NOTE: Get all PCR controls with ct over 0
try:
df = df[df.ct > 0.0]
except AttributeError:
@@ -361,11 +363,11 @@ class PCRControl(Control):
def to_pydantic(self):
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):
subtyping_allowed = ['kraken']
id = Column(INTEGER, ForeignKey('_control.id'), primary_key=True)
@@ -379,11 +381,19 @@ class IridaControl(Control):
sample = relationship("BacterialCultureSample", back_populates="control") #: This control's submission sample
sample_id = Column(INTEGER,
ForeignKey("_basicsample.id", ondelete="SET NULL", name="cont_BCS_id")) #: sample id key
__mapper_args__ = dict(polymorphic_identity="Irida Control",
polymorphic_load="inline",
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")
def enforce_subtype_literals(self, key: str, value: str) -> str:
"""
@@ -398,7 +408,7 @@ class IridaControl(Control):
Returns:
str: Validated string.
"""
"""
acceptables = ['ATCC49226', 'ATCC49619', 'EN-NOS', "EN-SSTI", "MCS-NOS", "MCS-SSTI", "SN-NOS", "SN-SSTI"]
if value.upper() not in acceptables:
raise KeyError(f"Sub-type must be in {acceptables}")
@@ -416,19 +426,15 @@ class IridaControl(Control):
except TypeError:
kraken = {}
kraken_cnt_total = sum([item['kraken_count'] for item in kraken.values()])
new_kraken = [dict(name=item, kraken_count=kraken[item]['kraken_count'],
kraken_percent=f"{kraken[item]['kraken_count'] / kraken_cnt_total:0.2%}",
target=item in self.controltype.targets)
for item in kraken]
new_kraken = [dict(name=key, kraken_count=value['kraken_count'],
kraken_percent=f"{value['kraken_count'] / kraken_cnt_total:0.2%}",
target=key in self.controltype.targets)
for key, value in kraken.items()]
new_kraken = sorted(new_kraken, key=itemgetter('kraken_count'), reverse=True)
if self.controltype.targets:
targets = self.controltype.targets
else:
targets = ["None"]
output = dict(
name=self.name,
type=self.controltype.name,
targets=", ".join(targets),
targets=", ".join(self.targets),
kraken=new_kraken[0:10]
)
return output
@@ -521,7 +527,7 @@ class IridaControl(Control):
Returns:
Tuple[Report, "IridaFigure"]: Report of status and resulting figure.
"""
"""
from frontend.visualizations import IridaFigure
try:
checker = parent.findChild(QCheckBox, name="irida_check")

View File

@@ -16,7 +16,6 @@ from pandas import ExcelFile
from pathlib import Path
from . import Base, BaseClass, Organization, LogMixin
from io import BytesIO
from inspect import getouterframes, currentframe
logger = logging.getLogger(f'submissions.{__name__}')
@@ -98,7 +97,6 @@ class KitType(BaseClass):
Base of kits used in submission processing
"""
# query_alias = "kit_type"
omni_sort = BaseClass.omni_sort + ["kit_submissiontype_associations", "kit_reagentrole_associations", "processes"]
id = Column(INTEGER, primary_key=True) #: primary key
@@ -128,13 +126,6 @@ class KitType(BaseClass):
creator=lambda ST: 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
def aliases(cls):
return super().aliases + [cls.query_alias, "kit_types", "kit_type"]
@@ -187,9 +178,7 @@ class KitType(BaseClass):
# NOTE: Account for submission_type variable type.
match submission_type:
case str():
# assocs = [item for item in self.kit_reagentrole_associations if
# item.submission_type.name == submission_type]
logger.debug(f"Query for {submission_type}")
# logger.debug(f"Query for {submission_type}")
submission_type = SubmissionType.query(name=submission_type)
case SubmissionType():
pass
@@ -213,18 +202,12 @@ class KitType(BaseClass):
)
if dlg.exec():
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)
logger.debug(f"Query result: {new_kit}")
# return new_kit.construct_xl_map_for_use(submission_type=submission_type)
# logger.debug(f"Query result: {new_kit}")
else:
return None, new_kit
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}
# logger.debug(f"Output: {output}")
return output, new_kit
@@ -232,7 +215,6 @@ class KitType(BaseClass):
@classmethod
def query_or_create(cls, **kwargs) -> Tuple[KitType, bool]:
from backend.validators.pydant import PydKitType
from backend.validators.omni_gui_objects import BaseOmni
new = False
disallowed = ['expiry']
sanitized_kwargs = {k: v for k, v in kwargs.items() if k not in disallowed}
@@ -248,7 +230,7 @@ class KitType(BaseClass):
@setup_lookup
def query(cls,
name: str = None,
used_for: str | SubmissionType | None = None,
submissiontype: str | SubmissionType | None = None,
id: int | None = None,
limit: int = 0,
**kwargs
@@ -266,11 +248,11 @@ class KitType(BaseClass):
KitType|List[KitType]: KitType(s) of interest.
"""
query: Query = cls.__database_session__.query(cls)
match used_for:
match submissiontype:
case str():
query = query.filter(cls.used_for.any(name=used_for))
query = query.filter(cls.submissiontype.any(name=submissiontype))
case SubmissionType():
query = query.filter(cls.used_for.contains(used_for))
query = query.filter(cls.submissiontype.contains(submissiontype))
case _:
pass
match name:
@@ -399,9 +381,6 @@ class KitType(BaseClass):
def to_omni(self, expand: bool = False) -> "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:
processes = [item.to_omni() for item in self.processes]
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
"""
skip_on_edit = False
id = Column(INTEGER, primary_key=True) #: primary key
name = Column(String(64)) #: name of role reagent plays
instances = relationship("Reagent", back_populates="role",
@@ -442,13 +423,6 @@ class ReagentRole(BaseClass):
creator=lambda kit: KitTypeReagentRoleAssociation(
kit_type=kit)) #: Association proxy to KitTypeReagentRoleAssociation
def __repr__(self) -> str:
"""
Returns:
str: Representation of object
"""
return f"<ReagentRole({self.name})>"
@classmethod
def query_or_create(cls, **kwargs) -> Tuple[ReagentRole, bool]:
new = False
@@ -603,7 +577,6 @@ class Reagent(BaseClass, LogMixin):
Returns:
dict: representation of the reagent's attributes
"""
if extraction_kit is not None:
# 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)),
@@ -713,8 +686,6 @@ class Reagent(BaseClass, LogMixin):
limit = 1
case _:
pass
# if not role and "reagentrole" in kwargs.keys():
# role = kwargs['reagentrole']
match role:
case str():
query = query.join(cls.role).filter(ReagentRole.name == role)
@@ -772,6 +743,7 @@ class Reagent(BaseClass, LogMixin):
@check_authorization
def edit_from_search(self, obj, **kwargs):
from frontend.widgets.omni_add_edit import AddEdit
# logger.debug(f"Calling edit_from_search for {self.name}")
dlg = AddEdit(parent=None, instance=self)
if dlg.exec():
pyd = dlg.parse_form()
@@ -1377,20 +1349,6 @@ class SubmissionType(BaseClass):
def to_omni(self, expand: bool = False):
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:
template_file = self.template_file
except AttributeError:
@@ -1555,8 +1513,6 @@ class SubmissionTypeKitTypeAssociation(BaseClass):
def to_omni(self, expand: bool = False):
from backend.validators.omni_gui_objects import OmniSubmissionTypeKitTypeAssociation
# level = len(getouterframes(currentframe()))
# logger.warning(f"Function level is {level}")
if expand:
try:
submissiontype = self.submission_type.to_omni()
@@ -1569,10 +1525,6 @@ class SubmissionTypeKitTypeAssociation(BaseClass):
else:
submissiontype = self.submission_type.name
kittype = self.kit_type.name
# try:
# processes = [item.to_omni() for item in self.submission_type.processes]
# except AttributeError:
# processes = []
return OmniSubmissionTypeKitTypeAssociation(
instance_object=self,
submissiontype=submissiontype,
@@ -1580,7 +1532,6 @@ class SubmissionTypeKitTypeAssociation(BaseClass):
mutable_cost_column=self.mutable_cost_column,
mutable_cost_sample=self.mutable_cost_sample,
constant_cost=self.constant_cost
# processes=processes,
)
@@ -1719,7 +1670,6 @@ class KitTypeReagentRoleAssociation(BaseClass):
pass
setattr(instance, k, v)
logger.info(f"Instance from query or create: {instance.__dict__}\nis new: {new}")
# sys.exit()
return instance, new
@classmethod
@@ -1854,6 +1804,8 @@ class SubmissionReagentAssociation(BaseClass):
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
submission_id = Column(INTEGER, ForeignKey("_basicsubmission.id"), primary_key=True) #: id of associated submission
comments = Column(String(1024)) #: Comments about reagents
@@ -1869,10 +1821,10 @@ class SubmissionReagentAssociation(BaseClass):
str: Representation of this SubmissionReagentAssociation
"""
try:
return f"<{self.submission.rsl_plate_num} & {self.reagent.lot}>"
return f"<SubmissionReagentAssociation({self.submission.rsl_plate_num} & {self.reagent.lot})>"
except AttributeError:
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):
if isinstance(reagent, list):
@@ -1963,12 +1915,12 @@ class Equipment(BaseClass, LogMixin):
submissions = association_proxy("equipment_submission_associations",
"submission") #: proxy to equipment_submission_associations.submission
def __repr__(self) -> str:
"""
Returns:
str: representation of this Equipment
"""
return f"<Equipment({self.name})>"
# def __repr__(self) -> str:
# """
# Returns:
# str: representation of this Equipment
# """
# return f"<Equipment({self.name})>"
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,
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
@@ -2133,12 +2085,12 @@ class EquipmentRole(BaseClass):
submission_types = association_proxy("equipmentrole_submissiontype_associations",
"submission_type") #: proxy to equipmentrole_submissiontype_associations.submission_type
def __repr__(self) -> str:
"""
Returns:
str: Representation of this EquipmentRole
"""
return f"<EquipmentRole({self.name})>"
# def __repr__(self) -> str:
# """
# Returns:
# str: Representation of this EquipmentRole
# """
# return f"<EquipmentRole({self.name})>"
def to_dict(self) -> dict:
"""
@@ -2395,7 +2347,6 @@ class Process(BaseClass):
id = Column(INTEGER, primary_key=True) #: Process id, primary key
name = Column(String(64), unique=True) #: Process name
# version = Column(String(32))
submission_types = relationship("SubmissionType", back_populates='processes',
secondary=submissiontypes_processes) #: relation to SubmissionType
equipment = relationship("Equipment", back_populates='processes',
@@ -2410,12 +2361,12 @@ class Process(BaseClass):
secondary=process_tiprole) #: relation to KitType
def __repr__(self) -> str:
"""
Returns:
str: Representation of this Process
"""
return f"<Process({self.name})>"
# def __repr__(self) -> str:
# """
# Returns:
# str: Representation of this Process
# """
# return f"<Process({self.name})>"
def set_attribute(self, key, value):
match key:
@@ -2506,13 +2457,20 @@ class Process(BaseClass):
def to_omni(self, expand: bool = False):
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(
instance_object=self,
name=self.name,
# version=self.version,
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]
submission_types=submission_types,
equipment_roles=equipment_roles,
tip_roles=tip_roles
)
@@ -2538,8 +2496,8 @@ class TipRole(BaseClass):
def tips(self):
return self.instances
def __repr__(self):
return f"<TipRole({self.name})>"
# def __repr__(self):
# return f"<TipRole({self.name})>"
@classmethod
def query_or_create(cls, **kwargs) -> Tuple[TipRole, bool]:
@@ -2573,10 +2531,14 @@ class TipRole(BaseClass):
def to_omni(self, expand: bool = False):
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(
instance_object=self,
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):
return self.role
def __repr__(self):
return f"<Tips({self.name})>"
# def __repr__(self):
# return f"<Tips({self.name})>"
@classmethod
def query_or_create(cls, **kwargs) -> Tuple[Tips, bool]:

View File

@@ -29,8 +29,6 @@ class Organization(BaseClass):
Base of organization
"""
id = Column(INTEGER, primary_key=True) #: primary key
name = Column(String(64)) #: organization name
submissions = relationship("BasicSubmission",
@@ -43,8 +41,8 @@ class Organization(BaseClass):
def contact(self):
return self.contacts
def __repr__(self) -> str:
return f"<Organization({self.name})>"
# def __repr__(self) -> str:
# return f"<Organization({self.name})>"
@classmethod
@setup_lookup
@@ -139,8 +137,8 @@ class Contact(BaseClass):
secondary=orgs_contacts) #: relationship to joined organization
submissions = relationship("BasicSubmission", back_populates="contact") #: submissions this contact has submitted
def __repr__(self) -> str:
return f"<Contact({self.name})>"
# def __repr__(self) -> str:
# return f"<Contact({self.name})>"
@classproperty
def searchables(cls):

View File

@@ -402,7 +402,6 @@ class BasicSubmission(BaseClass, LogMixin):
columns = set([assoc.column for assoc in self.submission_sample_associations])
return len(columns)
def calculate_base_cost(self) -> None:
"""
Calculates cost of the plate
@@ -733,7 +732,7 @@ class BasicSubmission(BaseClass, LogMixin):
@classmethod
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.

View File

@@ -59,7 +59,6 @@ class SheetParser(object):
Pulls basic information from the excel sheet
"""
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
# NOTE: in order to accommodate generic submission types we have to check for the type in the excel sheet and rerun accordingly
try:
@@ -274,37 +273,12 @@ class ReagentParser(object):
Returns:
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)
reagent_map = {k: v for k, v in associations.items() if k != 'info'}
try:
del reagent_map['info']
except KeyError:
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
@property
@@ -375,9 +349,6 @@ class SampleParser(object):
self.sub_object = sub_object
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.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
def sample_map(self) -> dict:
@@ -391,11 +362,6 @@ class SampleParser(object):
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)
@property
@@ -519,7 +485,6 @@ class EquipmentParser(object):
submission_type = SubmissionType.query(name=submission_type)
self.submission_type = submission_type
self.xl = xl
# self.equipment_map = self.fetch_equipment_map()
@property
def equipment_map(self) -> dict:
@@ -597,7 +562,6 @@ class TipParser(object):
submission_type = SubmissionType.query(name=submission_type)
self.submission_type = submission_type
self.xl = xl
# self.map = self.fetch_tip_map()
@property
def tip_map(self) -> dict:
@@ -669,7 +633,6 @@ class PCRParser(object):
else:
self.submission_obj = submission
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.controls = self.submission_obj.parse_pcr_controls(xl=self.xl, rsl_plate_num=rsl_plate_num)

View File

@@ -28,7 +28,8 @@ class RSLNamer(object):
logger.info(f"got submission type: {self.submission_type}")
if 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:
data = dict(submission_type=self.submission_type)
if "submission_type" not in data.keys():
@@ -50,24 +51,36 @@ class RSLNamer(object):
Returns:
str: parsed submission type
"""
def st_from_path(filename:Path) -> str:
if filename.exists():
wb = load_workbook(filename)
def st_from_path(filepath: Path) -> str:
"""
Sub def to get submissiontype from a file path
Args:
filepath ():
Returns:
"""
if filepath.exists():
wb = load_workbook(filepath)
try:
# NOTE: Gets first category in the metadata.
categories = wb.properties.category.split(";")
submission_type = next(item.strip().title() for item in categories)
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:
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:
# NOTE: On failure recurse using filename as string for string method
submission_type = cls.retrieve_submission_type(filename=filename.stem.__str__())
# NOTE: On failure recurse using filepath as string for string method
submission_type = cls.retrieve_submission_type(filename=filepath.stem.__str__())
else:
submission_type = cls.retrieve_submission_type(filename=filename.stem.__str__())
submission_type = cls.retrieve_submission_type(filename=filepath.stem.__str__())
return submission_type
def st_from_str(filename:str) -> str:
def st_from_str(filename: str) -> str:
if filename.startswith("tmp"):
return "Bacterial Culture"
regex = BasicSubmission.regex
@@ -78,9 +91,10 @@ class RSLNamer(object):
submission_type = None
logger.critical(f"No submission type found or submission type found!: {e}")
return submission_type
match filename:
case Path():
submission_type = st_from_path(filename=filename)
submission_type = st_from_path(filepath=filename)
case str():
submission_type = st_from_str(filename=filename)
case _:

View File

@@ -1,5 +1,5 @@
from __future__ import annotations
import logging, sys
import logging
from pydantic import BaseModel, field_validator, Field
from typing import List, ClassVar
from backend.db.models import *
@@ -16,7 +16,7 @@ class BaseOmni(BaseModel):
try:
return f"<{self.__class__.__name__}({self.name})>"
except AttributeError:
return f"<{self.__class__.__name__}(NO NAME)>"
return f"<{self.__class__.__name__}({self.__repr_name__})>"
@classproperty
def aliases(cls):
@@ -478,7 +478,6 @@ class OmniProcess(BaseOmni):
# NOTE: How am I going to figure out relatioinships without getting into recursion issues?
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",
title="SubmissionType")
equipment_roles: List[OmniEquipmentRole] | List[str] = Field(default=[], description="relationship",
@@ -507,13 +506,6 @@ class OmniProcess(BaseOmni):
return ""
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):
instance, new = self.class_object.query_or_create(name=self.name)
for st in self.submission_types:
@@ -539,12 +531,8 @@ class OmniKitType(BaseOmni):
class_object: ClassVar[Any] = KitType
name: str = Field(default="", description="property")
kit_submissiontype_associations: List[OmniSubmissionTypeKitTypeAssociation] | List[str] = Field(default=[],
description="relationship",
title="SubmissionTypeKitTypeAssociation")
kit_reagentrole_associations: List[OmniKitTypeReagentRoleAssociation] | List[str] = Field(default=[],
description="relationship",
title="KitTypeReagentRoleAssociation")
kit_submissiontype_associations: List[OmniSubmissionTypeKitTypeAssociation] | List[str] = Field(default=[], description="relationship", 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")
@field_validator("name", mode="before")
@@ -577,7 +565,6 @@ class OmniKitType(BaseOmni):
new_rr.append(new_assoc)
logger.debug(f"Setting kit_reagentrole_associations to {pformat([item.__dict__ for item in new_rr])}")
kit.kit_reagentrole_associations = new_rr
# sys.exit()
new_st = []
for st_assoc in self.kit_submissiontype_associations:
new_assoc = st_assoc.to_sql()

View File

@@ -288,8 +288,6 @@ class PydTips(BaseModel):
tips = Tips.query(name=self.name, limit=1)
# logger.debug(f"Tips query has yielded: {tips}")
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
@@ -355,14 +353,13 @@ class PydEquipment(BaseModel, extra='ignore'):
# 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: I need to find a way to filter this by the kit involved.
if len(self.processes) > 1:
process = Process.query(submissiontype=submission.get_submission_type(), kittype=extraction_kit, equipmentrole=self.role)
else:
process = Process.query(name=self.processes[0])
if process is None:
logger.error(f"Found unknown process: {process}.")
logger.debug(f"Using process: {process}")
# logger.debug(f"Using process: {process}")
assoc.process = process
assoc.role = self.role
else:
@@ -746,7 +743,16 @@ class PydSubmission(BaseModel, extra='allow'):
output = {k: self.filter_field(k) for k in fields}
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)
match item:
case dict():
@@ -780,9 +786,8 @@ class PydSubmission(BaseModel, extra='allow'):
"""
report = Report()
dicto = self.improved_dict()
logger.debug(f"Pydantic submission type: {self.submission_type['value']}")
logger.debug(f"Pydantic improved_dict: {pformat(dicto)}")
# At this point, pcr_info is not duplicated
# logger.debug(f"Pydantic submission type: {self.submission_type['value']}")
# logger.debug(f"Pydantic improved_dict: {pformat(dicto)}")
instance, result = BasicSubmission.query_or_create(submission_type=self.submission_type['value'],
rsl_plate_num=self.rsl_plate_num['value'])
# logger.debug(f"Created or queried instance: {instance}")
@@ -792,8 +797,7 @@ class PydSubmission(BaseModel, extra='allow'):
report.add_result(result)
self.handle_duplicate_samples()
for key, value in dicto.items():
logger.debug(f"Checking key {key}, value {value}")
# At this point, pcr_info is not duplicated.
# logger.debug(f"Checking key {key}, value {value}")
if isinstance(value, dict):
try:
value = value['value']
@@ -849,8 +853,7 @@ class PydSubmission(BaseModel, extra='allow'):
value = value
instance.set_attribute(key=key, value=value)
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:
ii = value.items()
except AttributeError:
@@ -860,8 +863,7 @@ class PydSubmission(BaseModel, extra='allow'):
value[k] = v.strftime("%Y-%m-%d %H:%M:%S")
else:
pass
logger.debug(f"Setting json value: {item} to value:{pformat(value)}")
# At this point, pcr_info is not duplicated.
# logger.debug(f"Setting json value: {item} to value:{pformat(value)}")
instance.set_attribute(key=key, value=value)
case _:
try:
@@ -878,7 +880,6 @@ class PydSubmission(BaseModel, extra='allow'):
continue
else:
logger.warning(f"{key} already == {value} so no updating.")
logger.debug(f"Entering cost calculation for {instance}")
try:
instance.calculate_base_cost()
except (TypeError, AttributeError) as e:
@@ -937,7 +938,6 @@ class PydSubmission(BaseModel, extra='allow'):
"/", "")
return render
# @report_result
def check_kit_integrity(self, extraction_kit: str | dict | None = None, exempt: List[PydReagent] = []) -> Tuple[
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
matches: list | dict #: unstructured hashes in matches.tsv for each organism
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"]
refseq_version: str #: version of refseq used in fastq parsing
kraken2_version: str
@@ -1264,7 +1263,6 @@ class PydProcess(BaseModel, extra="allow"):
instance = Process.query(name=self.name)
if not instance:
instance = Process()
# dicto = instance.omnigui_instance_dict
fields = [item for item in self.model_fields]
for field in fields:
logger.debug(f"Field: {field}")
@@ -1315,5 +1313,3 @@ class PydElastic(BaseModel, extra="allow", arbitrary_types_allowed=True):
field_value = getattr(self, field)
self.instance.__setattr__(field, field_value)
return self.instance

View File

@@ -19,10 +19,6 @@ class IridaFigure(CustomFigure):
super().__init__(df=df, modes=modes, settings=settings)
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'])

View File

@@ -3,10 +3,8 @@ Functions for constructing irida controls graphs using plotly.
"""
from pprint import pformat
from . import CustomFigure
import plotly.express as px
import pandas as pd
from PyQt6.QtWidgets import QWidget
import logging
import logging, plotly.express as px, pandas as pd
logger = logging.getLogger(f"submissions.{__name__}")
@@ -17,10 +15,6 @@ class PCRFigure(CustomFigure):
months: int = 6):
super().__init__(df=df, modes=modes, settings=settings)
self.df = df
# try:
# months = int(settings['months'])
# except KeyError:
# months = 6
self.construct_chart(df=df)
def construct_chart(self, df: pd.DataFrame):

View File

@@ -3,10 +3,8 @@ Construct turnaround time charts
"""
from pprint import pformat
from . import CustomFigure
import plotly.express as px
import pandas as pd
from PyQt6.QtWidgets import QWidget
import logging
import logging, plotly.express as px, pandas as pd
logger = logging.getLogger(f"submissions.{__name__}")

View File

@@ -230,7 +230,9 @@ class App(QMainWindow):
def update_data(self):
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):
from frontend.widgets.omni_manager_pydant import ManagerWindow as ManagerWindowPyd
dlg = ManagerWindow(parent=self, object_type=Organization, extras=[], add_edit='edit', managers=set())
if dlg.exec():
new_org = dlg.parse_form()
@@ -245,13 +247,9 @@ class App(QMainWindow):
logger.debug("\n\nBeginning parsing\n\n")
output = dlg.parse_form()
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")
sql = output.to_sql()
assert isinstance(sql, KitType)
# with open(f"{output.name}.sql", "wb") as f:
# pickle.dump(sql, f)
sql.save()

View File

@@ -113,10 +113,6 @@ class ControlsViewer(InfoPane):
if issubclass(self.fig.__class__, CustomFigure):
self.save_button.setEnabled(True)
# NOTE: construct html for webview
# try:
# html = self.fig.html
# except AttributeError:
# html = ""
self.webview.setHtml(self.fig.html)
self.webview.update()
return report

View File

@@ -28,12 +28,12 @@ class AddEdit(QDialog):
self.object_type = instance.__class__
# self.managers = deepcopy(managers)
self.managers = managers
if instance.level < 2:
try:
logger.debug(f"Parent instance: {self.parent().instance}")
self.managers.add(self.parent().instance)
except AttributeError:
pass
# if instance.level < 2:
# try:
# logger.debug(f"Parent instance: {self.parent().instance}")
# self.managers.add(self.parent().instance)
# except AttributeError:
# pass
logger.debug(f"Managers: {managers}")
self.layout = QGridLayout(self)
QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
@@ -111,14 +111,13 @@ class EditProperty(QWidget):
case _:
logger.error(f"{column_type} not a supported type.")
return
# if not self.is_list:
self.layout.addWidget(self.label, 0, 0, 1, 1)
self.layout.addWidget(self.widget, 0, 1, 1, 3)
self.setLayout(self.layout)
def relationship_property_set(self, relationship, value=None):
self.widget = QComboBox()
logger.debug(self.parent().managers)
# logger.debug(self.parent().managers)
for manager in self.parent().managers:
if self.name in manager.aliases:
logger.debug(f"Name: {self.name} is in aliases: {manager.aliases}")

View File

@@ -47,7 +47,6 @@ class ManagerWindow(QDialog):
self.manager = None
else:
self.manager = manager
# logger.debug(f"Managers: {managers}")
self.extras = extras
self.context = kwargs
@@ -104,7 +103,6 @@ class ManagerWindow(QDialog):
self.options.setEditable(False)
self.options.setMinimumWidth(self.minimumWidth())
self.layout.addWidget(self.options, 1, 0, 1, 1)
# if len(options) > 0:
self.add_button = QPushButton("Add New")
self.layout.addWidget(self.add_button, 1, 1, 1, 1)
self.add_button.clicked.connect(self.add_new)
@@ -126,12 +124,6 @@ class ManagerWindow(QDialog):
for item in deletes:
item.setParent(None)
# 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:
# logger.debug(f"Querying with {self.options.currentText()}")
self.instance = self.class_object.query(name=self.options.currentText(), limit=1)
@@ -173,7 +165,6 @@ class ManagerWindow(QDialog):
Returns:
Any: The instance with updated fields.
"""
# TODO: Need Relationship property here too?
results = [item.parse_form() for item in self.findChildren(EditProperty)]
for result in results:
# logger.debug(f"Incoming result: {result}")
@@ -211,7 +202,6 @@ class ManagerWindow(QDialog):
else:
value = current_value + [data]
setattr(self.instance, name, value)
# self.instance.save()
def toggle_textedit(self, caller_child=None):
already_exists = self.findChildren(LargeTextEdit)
@@ -305,7 +295,6 @@ class EditRelationship(QWidget):
# logger.debug(f"self.relationship: {self.relationship}")
# logger.debug(f"Relationship uses list: {self.relationship.property.uselist}")
# NOTE: value is a database object in this case.
# logger.debug(f"Data for edit relationship: {self.data}")
self.widget = QTableView()
self.add_button = QPushButton("Add New")
@@ -319,7 +308,6 @@ class EditRelationship(QWidget):
else:
value = []
self.data = value
# self.update_buttons()
checked_manager, is_primary = check_object_in_manager(self.parent().manager, self.objectName())
if checked_manager:
logger.debug(f"Checked manager for {self.objectName()}: {checked_manager}")
@@ -369,7 +357,6 @@ class EditRelationship(QWidget):
new_instance = dlg.parse_form()
# NOTE: My custom __setattr__ should take care of any list problems.
self.parent().instance.__setattr__(self.objectName(), new_instance)
# self.parent().instance.save()
self.parent().update_data()
def add_existing(self):
@@ -381,7 +368,6 @@ class EditRelationship(QWidget):
instance = self.class_object.query(**row)
# NOTE: My custom __setattr__ should take care of any list problems.
self.parent().instance.__setattr__(self.objectName(), instance)
# self.parent().instance.save()
self.parent().update_data()
def set_data(self) -> None:
@@ -420,7 +406,6 @@ class EditRelationship(QWidget):
Args:
event (_type_): the item of interest
"""
# print(self.widget.isEnabled())
if not self.widget.isEnabled():
logger.warning(f"{self.objectName()} is disabled.")
return
@@ -471,7 +456,6 @@ class EditRelationship(QWidget):
except ValueError as e:
logger.error(f"Remove failed for {self.objectName().lower()} due to {e}.")
self.parent().instance.save()
# self.parent().update_data()
self.set_data()
def parse_form(self):
@@ -555,7 +539,6 @@ class JsonEditScreen(QDialog):
output.append(value)
else:
raise ValueError(f"Inappropriate data type: {type(self.json_field)}")
# output[key] = value
return output

View File

@@ -120,7 +120,6 @@ class ManagerWindow(QDialog):
# logger.debug(f"Querying with {self.options.currentText()}")
self.instance = self.class_object.query(name=self.options.currentText(), limit=1)
except AttributeError:
# self.instance = None
pass
# logger.debug(f"Instance: {self.instance}")
if not self.instance:
@@ -164,11 +163,6 @@ class ManagerWindow(QDialog):
# NOTE: RelationshipDeclareds will be given a list of existing related objects.
case "relationship":
# 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)
case _:
continue
@@ -294,7 +288,6 @@ class EditRelationship(QWidget):
value = []
self.data = value
# logger.debug(f"Set data: {self.data}")
# self.update_buttons()
# logger.debug(f"Parent manager: {self.parent().manager}")
checked_manager, is_primary = check_object_in_manager(self.parent().manager, self.objectName())
if checked_manager:
@@ -374,8 +367,6 @@ class EditRelationship(QWidget):
return
logger.debug(f"Updating \n{pformat(obj)} with \n{pformat(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__)}")
# NOTE: somewhere in the update_data I'm losing changes.
self.parent().update_data()

View File

@@ -45,7 +45,6 @@ class SearchBox(QDialog):
self.setLayout(self.layout)
self.setWindowTitle(f"Search {self.object_type.__name__}")
self.update_widgets()
# self.update_data()
if returnable:
QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
self.buttonBox = QDialogButtonBox(QBtn)
@@ -60,7 +59,6 @@ class SearchBox(QDialog):
Changes form inputs based on sample type
"""
search_fields = []
# search_fields = self.object_type.searchables
logger.debug(f"Search fields: {search_fields}")
deletes = [item for item in self.findChildren(FieldSearch)]
for item in deletes:
@@ -69,7 +67,6 @@ class SearchBox(QDialog):
if not self.sub_class:
logger.warning(f"No subclass selected.")
self.update_data()
# return
else:
if self.sub_class.currentText() == "Any":
self.object_type = self.original_type

View File

@@ -315,8 +315,6 @@ class SubmissionFormWidget(QWidget):
query = self.findChildren(QWidget, name=object_name)
else:
query = self.findChildren(QWidget)
# if object_name is not None:
# query = [widget for widget in query if widget.objectName() == object_name]
return query
@report_result
@@ -338,7 +336,6 @@ class SubmissionFormWidget(QWidget):
if self.disabler.checkbox.isChecked():
_, result, _ = self.pyd.check_kit_integrity(exempt=exempt)
report.add_result(result)
# result = self.pyd.check_reagent_expiries(exempt=exempt)
if len(result.results) > 0:
return report
base_submission = self.pyd.to_sql()
@@ -372,7 +369,6 @@ class SubmissionFormWidget(QWidget):
pass
# NOTE: add reagents to submission object
if base_submission is None:
# self.app.table_widget.sub_wid.setData()
return
for reagent in base_submission.reagents:
reagent.update_last_used(kit=base_submission.extraction_kit)
@@ -752,11 +748,9 @@ class SubmissionFormWidget(QWidget):
looked_up_reg = None
if looked_up_reg:
try:
# relevant_reagents.remove(str(looked_up_reg.lot))
relevant_reagents.insert(0, relevant_reagents.pop(relevant_reagents.index(looked_up_reg.lot)))
except ValueError as e:
logger.error(f"Error reordering relevant reagents: {e}")
# relevant_reagents.insert(0, str(looked_up_reg.lot))
else:
if len(relevant_reagents) > 1:
idx = relevant_reagents.index(str(reagent.lot))

View File

@@ -2,16 +2,11 @@
Contains miscellaenous functions used by both frontend and backend.
'''
from __future__ import annotations
import builtins
import importlib
import time
import builtins, importlib, time, logging, re, yaml, sys, os, stat, platform, getpass, json, numpy as np, pandas as pd
from datetime import date, datetime, timedelta
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 inspect import getmembers, isfunction, stack
from types import GeneratorType
from dateutil.easter import easter
from jinja2 import Environment, FileSystemLoader
from logging import handlers
@@ -19,11 +14,9 @@ from pathlib import Path
from sqlalchemy.orm import Session, InstrumentedAttribute
from sqlalchemy import create_engine, text, MetaData
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 sqlalchemy.orm.relationships import _RelationshipDeclared
from __init__ import project_path
from configparser import ConfigParser
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
def check_not_nan(cell_contents) -> bool:
"""
Check to ensure excel sheet cell contents are not blank.
@@ -432,7 +424,6 @@ class Settings(BaseSettings, extra="allow"):
return package
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_from_db()
self.set_scripts()
@@ -1231,3 +1222,29 @@ class classproperty(property):
builtins.classproperty = classproperty
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,
)