Improved generic submissions to replace viral culture addition.

This commit is contained in:
lwark
2024-09-06 11:22:05 -05:00
parent bcb6e15449
commit 7f6a476cd7
11 changed files with 166 additions and 155 deletions

View File

@@ -113,15 +113,6 @@ class BasicSubmission(BaseClass):
__mapper_args__ = {
"polymorphic_identity": "Basic Submission",
"polymorphic_on": submission_type_name,
# "polymorphic_on": case(
#
# (submission_type_name == "Bacterial Culture", "Bacterial Culture"),
# (submission_type_name == "Wastewater Artic", "Wastewater Artic"),
# (submission_type_name == "Wastewater", "Wastewater"),
# (submission_type_name == "Viral Culture", "Bacterial Culture"),
#
# else_="Basic Sample"
# ),
"with_polymorphic": "*",
}
@@ -160,7 +151,7 @@ class BasicSubmission(BaseClass):
return output
@classmethod
def get_default_info(cls, *args):
def get_default_info(cls, *args, submission_type: SubmissionType | None = None):
# NOTE: Create defaults for all submission_types
parent_defs = super().get_default_info()
recover = ['filepath', 'samples', 'csv', 'comment', 'equipment']
@@ -185,7 +176,10 @@ class BasicSubmission(BaseClass):
continue
else:
output[k] = v
st = cls.get_submission_type()
if isinstance(submission_type, SubmissionType):
st = submission_type
else:
st = cls.get_submission_type(submission_type)
if st is None:
logger.error("No default info for BasicSubmission.")
else:
@@ -205,18 +199,23 @@ class BasicSubmission(BaseClass):
return output
@classmethod
def get_submission_type(cls) -> SubmissionType:
def get_submission_type(cls, sub_type: str | SubmissionType | None = None) -> SubmissionType:
"""
Gets the SubmissionType associated with this class
Returns:
SubmissionType: SubmissionType with name equal to this polymorphic identity
"""
name = cls.__mapper_args__['polymorphic_identity']
return SubmissionType.query(name=name)
match sub_type:
case str():
return SubmissionType.query(name=sub_type)
case SubmissionType():
return sub_type
case _:
return SubmissionType.query(cls.__mapper_args__['polymorphic_identity'])
@classmethod
def construct_info_map(cls, mode: Literal["read", "write"]) -> dict:
def construct_info_map(cls, submission_type:SubmissionType|None=None, mode: Literal["read", "write"]="read") -> dict:
"""
Method to call submission type's construct info map.
@@ -226,17 +225,17 @@ class BasicSubmission(BaseClass):
Returns:
dict: Map of info locations.
"""
return cls.get_submission_type().construct_info_map(mode=mode)
return cls.get_submission_type(submission_type).construct_info_map(mode=mode)
@classmethod
def construct_sample_map(cls) -> dict:
def construct_sample_map(cls, submission_type:SubmissionType|None=None) -> dict:
"""
Method to call submission type's construct_sample_map
Returns:
dict: sample location map
"""
return cls.get_submission_type().construct_sample_map()
return cls.get_submission_type(submission_type).construct_sample_map()
@classmethod
def finalize_details(cls, input_dict: dict) -> dict:
@@ -466,7 +465,7 @@ class BasicSubmission(BaseClass):
Returns:
pd.DataFrame: Pandas Dataframe of all relevant submissions
"""
# logger.debug(f"Querying Type: {submission_type}")
logger.debug(f"Querying Type: {submission_type}")
# logger.debug(f"Using limit: {limit}")
# NOTE: use lookup function to create list of dicts
subs = [item.to_dict() for item in
@@ -635,14 +634,13 @@ class BasicSubmission(BaseClass):
super().save()
@classmethod
def get_regex(cls) -> str:
"""
Dummy for inheritence.
Returns:
str: Regex for submission type.
"""
return cls.construct_regex()
def get_regex(cls, submission_type:SubmissionType|str|None=None):
# logger.debug(f"Attempting to get regex for {cls.__mapper_args__['polymorphic_identity']}")
logger.debug(f"Attempting to get regex for {submission_type}")
try:
return cls.get_submission_type(submission_type).defaults['regex']
except AttributeError as e:
raise AttributeError(f"Couldn't get submission type for {cls.__mapper_args__['polymorphic_identity']}")
# Polymorphic functions
@@ -654,7 +652,8 @@ class BasicSubmission(BaseClass):
Returns:
re.Pattern: Regular expression pattern to discriminate between submission types.
"""
rstring = rf'{"|".join([item.get_regex() for item in cls.__subclasses__()])}'
# rstring = rf'{"|".join([item.get_regex() for item in cls.__subclasses__()])}'
rstring = rf'{"|".join([item.defaults["regex"] for item in SubmissionType.query()])}'
regex = re.compile(rstring, flags=re.IGNORECASE | re.VERBOSE)
return regex
@@ -685,7 +684,7 @@ class BasicSubmission(BaseClass):
# item.__mapper_args__['polymorphic_identity'] == polymorphic_identity][0]
model = cls.__mapper__.polymorphic_map[polymorphic_identity].class_
except Exception as e:
logger.error(f"Could not get polymorph {polymorphic_identity} of {cls} due to {e}")
logger.error(f"Could not get polymorph {polymorphic_identity} of {cls} due to {e}, falling back to BasicSubmission")
case _:
pass
if attrs is None or len(attrs) == 0:
@@ -796,11 +795,13 @@ class BasicSubmission(BaseClass):
# logger.info(f"Hello from {cls.__mapper_args__['polymorphic_identity']} Enforcer!")
from backend.validators import RSLNamer
# logger.debug(f"instr coming into {cls}: {instr}")
# logger.debug(f"data coming into {cls}: {data}")
defaults = cls.get_default_info("abbreviation", "submission_type")
data['abbreviation'] = defaults['abbreviation']
if 'submission_type' not in data.keys() or data['submission_type'] in [None, ""]:
data['submission_type'] = defaults['submission_type']
logger.debug(f"data coming into {cls}: {data}")
# defaults = cls.get_default_info("abbreviation", "submission_type")
data['abbreviation'] = cls.get_default_info("abbreviation", submission_type=data['submission_type'])
# logger.debug(f"Default info: {defaults}")
# data['abbreviation'] = defaults['abbreviation']
# if 'submission_type' not in data.keys() or data['submission_type'] in [None, ""]:
# data['submission_type'] = defaults['submission_type']
if instr in [None, ""]:
# logger.debug("Sending to RSLNamer to make new plate name.")
outstr = RSLNamer.construct_new_plate_name(data=data)
@@ -829,9 +830,11 @@ class BasicSubmission(BaseClass):
except AttributeError as e:
repeat = ""
outstr = re.sub(r"(-\dR)\d?", rf"\1 {repeat}", outstr).replace(" ", "")
abb = cls.get_default_info('abbreviation')
outstr = re.sub(rf"RSL{abb}", rf"RSL-{abb}", outstr)
return re.sub(rf"{abb}(\d)", rf"{abb}-\1", outstr)
# abb = cls.get_default_info('abbreviation')
# outstr = re.sub(rf"RSL{abb}", rf"RSL-{abb}", outstr)
# return re.sub(rf"{abb}(\d)", rf"{abb}-\1", outstr)
outstr = re.sub(rf"RSL{data['abbreviation']}", rf"RSL-{data['abbreviation']}", outstr)
return re.sub(rf"{data['abbreviation']}(\d)", rf"{data['abbreviation']}-\1", outstr)
@classmethod
def parse_pcr(cls, xl: Workbook, rsl_plate_num: str) -> list:
@@ -1261,15 +1264,15 @@ class BacterialCulture(BasicSubmission):
output['controls'] = [item.to_sub_dict() for item in self.controls]
return output
@classmethod
def get_regex(cls) -> str:
"""
Retrieves string for regex construction.
Returns:
str: string for regex construction
"""
return "(?P<Bacterial_Culture>RSL(?:-|_)?BC(?:-|_)?20\d{2}-?\d{2}-?\d{2}(?:(_|-)?\d?([^_0123456789\sA-QS-Z]|$)?R?\d?)?)"
# @classmethod
# def get_regex(cls) -> str:
# """
# Retrieves string for regex construction.
#
# Returns:
# str: string for regex construction
# """
# return "(?P<Bacterial_Culture>RSL(?:-|_)?BC(?:-|_)?20\d{2}-?\d{2}-?\d{2}(?:(_|-)?\d?([^_0123456789\sA-QS-Z]|$)?R?\d?)?)"
@classmethod
def filename_template(cls):
@@ -1339,47 +1342,47 @@ class BacterialCulture(BasicSubmission):
return input_dict
class ViralCulture(BasicSubmission):
id = Column(INTEGER, ForeignKey('_basicsubmission.id'), primary_key=True)
__mapper_args__ = dict(polymorphic_identity="Viral Culture",
polymorphic_load="inline",
inherit_condition=(id == BasicSubmission.id))
@classmethod
def get_regex(cls) -> str:
"""
Retrieves string for regex construction.
Returns:
str: string for regex construction
"""
return "(?P<Viral_Culture>RSL(?:-|_)?VE(?:-|_)?20\d{2}-?\d{2}-?\d{2}(?:(_|-)?\d?([^_0123456789\sA-QS-Z]|$)?R?\d?)?)"
@classmethod
def custom_sample_autofill_row(cls, sample, worksheet: Worksheet) -> int:
"""
Extends parent
"""
# logger.debug(f"Checking {sample.well}")
# logger.debug(f"here's the worksheet: {worksheet}")
row = super().custom_sample_autofill_row(sample, worksheet)
df = pd.DataFrame(list(worksheet.values))
# logger.debug(f"Here's the dataframe: {df}")
idx = df[df[0] == sample.well]
if idx.empty:
new = f"{sample.well[0]}{sample.well[1:].zfill(2)}"
# logger.debug(f"Checking: {new}")
idx = df[df[0] == new]
# logger.debug(f"Here is the row: {idx}")
row = idx.index.to_list()[0]
return row + 1
@classmethod
def custom_info_parser(cls, input_dict: dict, xl: Workbook | None = None, custom_fields: dict = {}) -> dict:
input_dict = super().custom_info_parser(input_dict=input_dict, xl=xl, custom_fields=custom_fields)
logger.debug(f"\n\nInfo dictionary:\n\n{pformat(input_dict)}\n\n")
return input_dict
# class ViralCulture(BasicSubmission):
#
# id = Column(INTEGER, ForeignKey('_basicsubmission.id'), primary_key=True)
# __mapper_args__ = dict(polymorphic_identity="Viral Culture",
# polymorphic_load="inline",
# inherit_condition=(id == BasicSubmission.id))
#
# # @classmethod
# # def get_regex(cls) -> str:
# # """
# # Retrieves string for regex construction.
# #
# # Returns:
# # str: string for regex construction
# # """
# # return "(?P<Viral_Culture>RSL(?:-|_)?VE(?:-|_)?20\d{2}-?\d{2}-?\d{2}(?:(_|-)?\d?([^_0123456789\sA-QS-Z]|$)?R?\d?)?)"
#
# @classmethod
# def custom_sample_autofill_row(cls, sample, worksheet: Worksheet) -> int:
# """
# Extends parent
# """
# # logger.debug(f"Checking {sample.well}")
# # logger.debug(f"here's the worksheet: {worksheet}")
# row = super().custom_sample_autofill_row(sample, worksheet)
# df = pd.DataFrame(list(worksheet.values))
# # logger.debug(f"Here's the dataframe: {df}")
# idx = df[df[0] == sample.well]
# if idx.empty:
# new = f"{sample.well[0]}{sample.well[1:].zfill(2)}"
# # logger.debug(f"Checking: {new}")
# idx = df[df[0] == new]
# # logger.debug(f"Here is the row: {idx}")
# row = idx.index.to_list()[0]
# return row + 1
#
# @classmethod
# def custom_info_parser(cls, input_dict: dict, xl: Workbook | None = None, custom_fields: dict = {}) -> dict:
# input_dict = super().custom_info_parser(input_dict=input_dict, xl=xl, custom_fields=custom_fields)
# logger.debug(f"\n\nInfo dictionary:\n\n{pformat(input_dict)}\n\n")
# return input_dict
class Wastewater(BasicSubmission):
@@ -1499,15 +1502,15 @@ class Wastewater(BasicSubmission):
outstr = super().enforce_name(instr=instr, data=data)
return outstr
@classmethod
def get_regex(cls) -> str:
"""
Retrieves string for regex construction
Returns:
str: String for regex construction
"""
return "(?P<Wastewater>RSL(?:-|_)?WW(?:-|_)?20\d{2}-?\d{2}-?\d{2}(?:(_|-)?\d?([^_0123456789\sA-QS-Z]|$)?R?\d?)?)"
# @classmethod
# def get_regex(cls) -> str:
# """
# Retrieves string for regex construction
#
# Returns:
# str: String for regex construction
# """
# return "(?P<Wastewater>RSL(?:-|_)?WW(?:-|_)?20\d{2}-?\d{2}-?\d{2}(?:(_|-)?\d?([^_0123456789\sA-QS-Z]|$)?R?\d?)?)"
@classmethod
def adjust_autofill_samples(cls, samples: List[Any]) -> List[Any]:
@@ -1919,15 +1922,15 @@ class WastewaterArtic(BasicSubmission):
# logger.debug(f"Final EN name: {final_en_name}")
return final_en_name
@classmethod
def get_regex(cls) -> str:
"""
Retrieves string for regex construction
Returns:
str: string for regex construction.
"""
return "(?P<Wastewater_Artic>(\\d{4}-\\d{2}-\\d{2}(?:-|_)(?:\\d_)?artic)|(RSL(?:-|_)?AR(?:-|_)?20\\d{2}-?\\d{2}-?\\d{2}(?:(_|-)\\d?(\\D|$)R?\\d?)?))"
# @classmethod
# def get_regex(cls) -> str:
# """
# Retrieves string for regex construction
#
# Returns:
# str: string for regex construction.
# """
# return "(?P<Wastewater_Artic>(\\d{4}-\\d{2}-\\d{2}(?:-|_)(?:\\d_)?artic)|(RSL(?:-|_)?AR(?:-|_)?20\\d{2}-?\\d{2}-?\\d{2}(?:(_|-)\\d?(\\D|$)R?\\d?)?))"
@classmethod
def finalize_parse(cls, input_dict: dict, xl: pd.ExcelFile | None = None, info_map: dict | None = None) -> dict: