Pre-removal of constructors module.
This commit is contained in:
@@ -1,43 +1,42 @@
|
||||
'''
|
||||
Contains all models for sqlalchemy
|
||||
'''
|
||||
from typing import Any
|
||||
from sqlalchemy.orm import declarative_base, DeclarativeMeta
|
||||
import logging
|
||||
from pprint import pformat
|
||||
|
||||
Base: DeclarativeMeta = declarative_base()
|
||||
metadata = Base.metadata
|
||||
|
||||
logger = logging.getLogger(f"submissions.{__name__}")
|
||||
|
||||
def find_subclasses(parent:Any, attrs:dict|None=None, rsl_number:str|None=None) -> Any:
|
||||
"""
|
||||
Finds subclasses of a parent that does contain all
|
||||
attributes if the parent does not.
|
||||
# def find_subclasses(parent:Any, attrs:dict|None=None, rsl_number:str|None=None) -> Any:
|
||||
# """
|
||||
# Finds subclasses of a parent that does contain all
|
||||
# attributes if the parent does not.
|
||||
# NOTE: Depreciated, moved to classmethods in individual base models.
|
||||
|
||||
Args:
|
||||
parent (_type_): Parent class.
|
||||
attrs (dict): Key:Value dictionary of attributes
|
||||
# Args:
|
||||
# parent (_type_): Parent class.
|
||||
# attrs (dict): Key:Value dictionary of attributes
|
||||
|
||||
Raises:
|
||||
AttributeError: Raised if no subclass is found.
|
||||
# Raises:
|
||||
# AttributeError: Raised if no subclass is found.
|
||||
|
||||
Returns:
|
||||
_type_: Parent or subclass.
|
||||
"""
|
||||
if len(attrs) == 0 or attrs == None:
|
||||
return parent
|
||||
if any([not hasattr(parent, attr) for attr in attrs]):
|
||||
# looks for first model that has all included kwargs
|
||||
try:
|
||||
model = [subclass for subclass in parent.__subclasses__() if all([hasattr(subclass, attr) for attr in attrs])][0]
|
||||
except IndexError as e:
|
||||
raise AttributeError(f"Couldn't find existing class/subclass of {parent} with all attributes:\n{pformat(attrs)}")
|
||||
else:
|
||||
model = parent
|
||||
logger.debug(f"Using model: {model}")
|
||||
return model
|
||||
# Returns:
|
||||
# _type_: Parent or subclass.
|
||||
# """
|
||||
# if len(attrs) == 0 or attrs == None:
|
||||
# return parent
|
||||
# if any([not hasattr(parent, attr) for attr in attrs]):
|
||||
# # looks for first model that has all included kwargs
|
||||
# try:
|
||||
# model = [subclass for subclass in parent.__subclasses__() if all([hasattr(subclass, attr) for attr in attrs])][0]
|
||||
# except IndexError as e:
|
||||
# raise AttributeError(f"Couldn't find existing class/subclass of {parent} with all attributes:\n{pformat(attrs)}")
|
||||
# else:
|
||||
# model = parent
|
||||
# logger.debug(f"Using model: {model}")
|
||||
# return model
|
||||
|
||||
from .controls import Control, ControlType
|
||||
from .kits import KitType, ReagentType, Reagent, Discount, KitTypeReagentTypeAssociation, SubmissionType, SubmissionTypeKitTypeAssociation
|
||||
|
||||
@@ -31,7 +31,8 @@ class KitType(Base):
|
||||
|
||||
# association proxy of "user_keyword_associations" collection
|
||||
# to "keyword" attribute
|
||||
reagent_types = association_proxy("kit_reagenttype_associations", "reagent_type")
|
||||
# creator function: https://stackoverflow.com/questions/11091491/keyerror-when-adding-objects-to-sqlalchemy-association-object/11116291#11116291
|
||||
reagent_types = association_proxy("kit_reagenttype_associations", "reagent_type", creator=lambda RT: KitTypeReagentTypeAssociation(reagent_type=RT))
|
||||
|
||||
kit_submissiontype_associations = relationship(
|
||||
"SubmissionTypeKitTypeAssociation",
|
||||
@@ -118,7 +119,8 @@ class ReagentType(Base):
|
||||
|
||||
# association proxy of "user_keyword_associations" collection
|
||||
# to "keyword" attribute
|
||||
kit_types = association_proxy("reagenttype_kit_associations", "kit_type")
|
||||
# creator function: https://stackoverflow.com/questions/11091491/keyerror-when-adding-objects-to-sqlalchemy-association-object/11116291#11116291
|
||||
kit_types = association_proxy("reagenttype_kit_associations", "kit_type", creator=lambda kit: KitTypeReagentTypeAssociation(kit_type=kit))
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""
|
||||
@@ -150,6 +152,7 @@ class KitTypeReagentTypeAssociation(Base):
|
||||
reagent_type = relationship(ReagentType, back_populates="reagenttype_kit_associations")
|
||||
|
||||
def __init__(self, kit_type=None, reagent_type=None, uses=None, required=1):
|
||||
logger.debug(f"Parameters: Kit={kit_type}, RT={reagent_type}, Uses={uses}, Required={required}")
|
||||
self.kit_type = kit_type
|
||||
self.reagent_type = reagent_type
|
||||
self.uses = uses
|
||||
@@ -186,9 +189,9 @@ class Reagent(Base):
|
||||
|
||||
def __repr__(self):
|
||||
if self.name != None:
|
||||
return f"Reagent({self.name}-{self.lot})"
|
||||
return f"<Reagent({self.name}-{self.lot})>"
|
||||
else:
|
||||
return f"Reagent({self.type.name}-{self.lot})"
|
||||
return f"<Reagent({self.type.name}-{self.lot})>"
|
||||
|
||||
|
||||
def __str__(self) -> str:
|
||||
|
||||
@@ -32,6 +32,13 @@ class Organization(Base):
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Organization({self.name})>"
|
||||
|
||||
def save(self, ctx):
|
||||
ctx.database_session.add(self)
|
||||
ctx.database_session.commit()
|
||||
|
||||
def set_attribute(self, name:str, value):
|
||||
setattr(self, name, value)
|
||||
|
||||
|
||||
class Contact(Base):
|
||||
|
||||
@@ -13,7 +13,6 @@ from json.decoder import JSONDecodeError
|
||||
from math import ceil
|
||||
from sqlalchemy.ext.associationproxy import association_proxy
|
||||
import uuid
|
||||
from pandas import Timestamp
|
||||
from dateutil.parser import parse
|
||||
import re
|
||||
import pandas as pd
|
||||
@@ -301,6 +300,7 @@ class BasicSubmission(Base):
|
||||
@classmethod
|
||||
def enforce_name(cls, ctx:Settings, instr:str) -> str:
|
||||
logger.debug(f"Hello from {cls.__mapper_args__['polymorphic_identity']} Enforcer!")
|
||||
logger.debug(f"Attempting enforcement on {instr}")
|
||||
return instr
|
||||
|
||||
@classmethod
|
||||
@@ -344,6 +344,11 @@ class BasicSubmission(Base):
|
||||
logger.debug(f"Hello from {cls.__mapper_args__['polymorphic_identity']} PCR parser!")
|
||||
return []
|
||||
|
||||
def save(self, ctx:Settings):
|
||||
self.uploaded_by = getuser()
|
||||
ctx.database_session.add(self)
|
||||
ctx.database_session.commit()
|
||||
|
||||
# Below are the custom submission types
|
||||
|
||||
class BacterialCulture(BasicSubmission):
|
||||
@@ -536,6 +541,8 @@ class Wastewater(BasicSubmission):
|
||||
def construct():
|
||||
today = datetime.now()
|
||||
return f"RSL-WW-{today.year}{str(today.month).zfill(2)}{str(today.day).zfill(2)}"
|
||||
if outstr == None:
|
||||
outstr = construct()
|
||||
try:
|
||||
outstr = re.sub(r"PCR(-|_)", "", outstr)
|
||||
except AttributeError as e:
|
||||
@@ -743,6 +750,11 @@ class BasicSample(Base):
|
||||
logger.error(f"Could not get polymorph {polymorphic_identity} of {cls} due to {e}")
|
||||
return cls
|
||||
|
||||
@classmethod
|
||||
def parse_sample(cls, input_dict:dict) -> dict:
|
||||
logger.debug(f"Called {cls.__name__} sample parser")
|
||||
return input_dict
|
||||
|
||||
class WastewaterSample(BasicSample):
|
||||
"""
|
||||
Derivative wastewater sample
|
||||
@@ -757,51 +769,51 @@ class WastewaterSample(BasicSample):
|
||||
__mapper_args__ = {"polymorphic_identity": "Wastewater Sample", "polymorphic_load": "inline"}
|
||||
|
||||
|
||||
@validates("collected-date")
|
||||
def convert_cdate_time(self, key, value):
|
||||
logger.debug(f"Validating {key}: {value}")
|
||||
if isinstance(value, Timestamp):
|
||||
return value.date()
|
||||
if isinstance(value, str):
|
||||
return parse(value)
|
||||
return value
|
||||
# @validates("collected-date")
|
||||
# def convert_cdate_time(self, key, value):
|
||||
# logger.debug(f"Validating {key}: {value}")
|
||||
# if isinstance(value, Timestamp):
|
||||
# return value.date()
|
||||
# if isinstance(value, str):
|
||||
# return parse(value)
|
||||
# return value
|
||||
|
||||
@validates("rsl_number")
|
||||
def use_submitter_id(self, key, value):
|
||||
logger.debug(f"Validating {key}: {value}")
|
||||
return value or self.submitter_id
|
||||
# @validates("rsl_number")
|
||||
# def use_submitter_id(self, key, value):
|
||||
# logger.debug(f"Validating {key}: {value}")
|
||||
# return value or self.submitter_id
|
||||
|
||||
def set_attribute(self, name:str, value):
|
||||
"""
|
||||
Set an attribute of this object. Extends parent.
|
||||
# def set_attribute(self, name:str, value):
|
||||
# """
|
||||
# Set an attribute of this object. Extends parent.
|
||||
|
||||
Args:
|
||||
name (str): name of the attribute
|
||||
value (_type_): value to be set
|
||||
"""
|
||||
# Due to the plate map being populated with RSL numbers, we have to do some shuffling.
|
||||
match name:
|
||||
case "submitter_id":
|
||||
# If submitter_id already has a value, stop
|
||||
if self.submitter_id != None:
|
||||
return
|
||||
# otherwise also set rsl_number to the same value
|
||||
else:
|
||||
super().set_attribute("rsl_number", value)
|
||||
case "ww_full_sample_id":
|
||||
# If value present, set ww_full_sample_id and make this the submitter_id
|
||||
if value != None:
|
||||
super().set_attribute(name, value)
|
||||
name = "submitter_id"
|
||||
case 'collection_date':
|
||||
# If this is a string use dateutils to parse into date()
|
||||
if isinstance(value, str):
|
||||
logger.debug(f"collection_date {value} is a string. Attempting parse...")
|
||||
value = parse(value)
|
||||
case "rsl_number":
|
||||
if value == None:
|
||||
value = self.submitter_id
|
||||
super().set_attribute(name, value)
|
||||
# Args:
|
||||
# name (str): name of the attribute
|
||||
# value (_type_): value to be set
|
||||
# """
|
||||
# # Due to the plate map being populated with RSL numbers, we have to do some shuffling.
|
||||
# match name:
|
||||
# case "submitter_id":
|
||||
# # If submitter_id already has a value, stop
|
||||
# if self.submitter_id != None:
|
||||
# return
|
||||
# # otherwise also set rsl_number to the same value
|
||||
# else:
|
||||
# super().set_attribute("rsl_number", value)
|
||||
# case "ww_full_sample_id":
|
||||
# # If value present, set ww_full_sample_id and make this the submitter_id
|
||||
# if value != None:
|
||||
# super().set_attribute(name, value)
|
||||
# name = "submitter_id"
|
||||
# case 'collection_date':
|
||||
# # If this is a string use dateutils to parse into date()
|
||||
# if isinstance(value, str):
|
||||
# logger.debug(f"collection_date {value} is a string. Attempting parse...")
|
||||
# value = parse(value)
|
||||
# case "rsl_number":
|
||||
# if value == None:
|
||||
# value = self.submitter_id
|
||||
# super().set_attribute(name, value)
|
||||
|
||||
def to_hitpick(self, submission_rsl:str) -> dict|None:
|
||||
"""
|
||||
@@ -832,6 +844,16 @@ class WastewaterSample(BasicSample):
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def parse_sample(cls, input_dict: dict) -> dict:
|
||||
output_dict = super().parse_sample(input_dict)
|
||||
if output_dict['rsl_number'] == None:
|
||||
output_dict['rsl_number'] = output_dict['submitter_id']
|
||||
if output_dict['ww_full_sample_id'] != None:
|
||||
output_dict["submitter_id"] = output_dict['ww_full_sample_id']
|
||||
return output_dict
|
||||
|
||||
|
||||
class BacterialCultureSample(BasicSample):
|
||||
"""
|
||||
base of bacterial culture sample
|
||||
@@ -873,7 +895,7 @@ class SubmissionSampleAssociation(Base):
|
||||
# Refers to the type of parent.
|
||||
# Hooooooo boy, polymorphic association type, now we're getting into the weeds!
|
||||
__mapper_args__ = {
|
||||
"polymorphic_identity": "basic_association",
|
||||
"polymorphic_identity": "Basic Association",
|
||||
"polymorphic_on": base_sub_type,
|
||||
"with_polymorphic": "*",
|
||||
}
|
||||
@@ -886,6 +908,19 @@ class SubmissionSampleAssociation(Base):
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<SubmissionSampleAssociation({self.submission.rsl_plate_num} & {self.sample.submitter_id})"
|
||||
|
||||
@classmethod
|
||||
def find_polymorphic_subclass(cls, polymorphic_identity:str|None=None):
|
||||
if isinstance(polymorphic_identity, dict):
|
||||
polymorphic_identity = polymorphic_identity['value']
|
||||
if polymorphic_identity == None:
|
||||
return cls
|
||||
else:
|
||||
try:
|
||||
return [item for item in cls.__subclasses__() if item.__mapper_args__['polymorphic_identity']==polymorphic_identity][0]
|
||||
except Exception as e:
|
||||
logger.error(f"Could not get polymorph {polymorphic_identity} of {cls} due to {e}")
|
||||
return cls
|
||||
|
||||
class WastewaterAssociation(SubmissionSampleAssociation):
|
||||
"""
|
||||
@@ -897,5 +932,5 @@ class WastewaterAssociation(SubmissionSampleAssociation):
|
||||
n2_status = Column(String(32)) #: positive or negative for N2
|
||||
pcr_results = Column(JSON) #: imported PCR status from QuantStudio
|
||||
|
||||
__mapper_args__ = {"polymorphic_identity": "wastewater", "polymorphic_load": "inline"}
|
||||
__mapper_args__ = {"polymorphic_identity": "Wastewater Association", "polymorphic_load": "inline"}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user