During code cleanup

This commit is contained in:
lwark
2024-05-23 15:06:22 -05:00
parent d1bf12e8d1
commit 2814be8980
10 changed files with 297 additions and 356 deletions

View File

@@ -3,7 +3,6 @@ Contains all models for sqlalchemy
'''
from __future__ import annotations
import sys, logging
from sqlalchemy import Column, INTEGER, String, JSON
from sqlalchemy.orm import DeclarativeMeta, declarative_base, Query, Session
from sqlalchemy.ext.declarative import declared_attr
@@ -81,7 +80,13 @@ class BaseClass(Base):
return ctx.backup_path
@classmethod
def get_default_info(cls, *args) -> dict | List[str]:
def get_default_info(cls, *args) -> dict | list | str:
"""
Returns default info for a model
Returns:
dict | list | str: Output of key:value dict or single (list, str) desired variable
"""
dicto = dict(singles=['id'])
output = {}
for k, v in dicto.items():
@@ -95,7 +100,13 @@ class BaseClass(Base):
return output
@classmethod
def query(cls, **kwargs):
def query(cls, **kwargs) -> Any | List[Any]:
"""
Default query function for models
Returns:
Any | List[Any]: Result of query execution.
"""
return cls.execute_query(**kwargs)
@classmethod
@@ -119,13 +130,12 @@ class BaseClass(Base):
singles = model.get_default_info('singles')
logger.debug(f"Querying: {model}, with kwargs: {kwargs}")
for k, v in kwargs.items():
logger.debug(f"Using key: {k} with value: {v}")
# logger.debug(f"That key found attribute: {attr} with type: {attr}")
# logger.debug(f"Using key: {k} with value: {v}")
try:
attr = getattr(model, k)
query = query.filter(attr == v)
except (ArgumentError, AttributeError) as e:
logger.error(f"Attribute {k} available due to:\n\t{e}\nSkipping.")
logger.error(f"Attribute {k} unavailable due to:\n\t{e}\nSkipping.")
if k in singles:
limit = 1
with query.session.no_autoflush:
@@ -155,6 +165,9 @@ class BaseClass(Base):
class ConfigItem(BaseClass):
"""
Key:JSON objects to store config settings in database.
"""
id = Column(INTEGER, primary_key=True)
key = Column(String(32))
value = Column(JSON)
@@ -163,8 +176,18 @@ class ConfigItem(BaseClass):
return f"ConfigItem({self.key} : {self.value})"
@classmethod
def get_config_items(cls):
return cls.__database_session__.query(cls).all()
def get_config_items(cls, *args) -> ConfigItem|List[ConfigItem]:
"""
Get desired config items from database
Returns:
ConfigItem|List[ConfigItem]: Config item(s)
"""
config_items = cls.__database_session__.query(cls).all()
config_items = [item for item in config_items if item.key in args]
if len(args) == 1:
config_items = config_items[0]
return config_items
from .controls import *

View File

@@ -13,6 +13,8 @@ from typing import List, Literal
from pandas import ExcelFile
from pathlib import Path
from . import Base, BaseClass, Organization
from io import BytesIO
logger = logging.getLogger(f'submissions.{__name__}')
@@ -300,7 +302,7 @@ class ReagentType(BaseClass):
# logger.debug(f"Looking up reagent type for {type(kit_type)} {kit_type} and {type(reagent)} {reagent}")
# logger.debug(f"Kit reagent types: {kit_type.reagent_types}")
result = list(set(kit_type.reagent_types).intersection(reagent.type))
logger.debug(f"Result: {result}")
# logger.debug(f"Result: {result}")
try:
return result[0]
except IndexError:
@@ -386,7 +388,7 @@ class Reagent(BaseClass):
place_holder = self.expiry + reagent_role.eol_ext
except (TypeError, AttributeError) as e:
place_holder = date.today()
logger.debug(f"We got a type error setting {self.lot} expiry: {e}. setting to today for testing")
logger.error(f"We got a type error setting {self.lot} expiry: {e}. setting to today for testing")
if self.expiry.year == 1970:
place_holder = "NA"
else:
@@ -410,14 +412,14 @@ class Reagent(BaseClass):
Report: Result of operation
"""
report = Report()
logger.debug(f"Attempting update of reagent type at intersection of ({self}), ({kit})")
# logger.debug(f"Attempting update of last used reagent type at intersection of ({self}), ({kit})")
rt = ReagentType.query(kit_type=kit, reagent=self, limit=1)
if rt is not None:
logger.debug(f"got reagenttype {rt}")
# logger.debug(f"got reagenttype {rt}")
assoc = KitTypeReagentTypeAssociation.query(kit_type=kit, reagent_type=rt)
if assoc is not None:
if assoc.last_used != self.lot:
logger.debug(f"Updating {assoc} last used to {self.lot}")
# logger.debug(f"Updating {assoc} last used to {self.lot}")
assoc.last_used = self.lot
result = assoc.save()
report.add_result(result)
@@ -607,7 +609,7 @@ class SubmissionType(BaseClass):
Returns:
List[str]: List of sheet names
"""
return ExcelFile(self.template_file).sheet_names
return ExcelFile(BytesIO(self.template_file)).sheet_names
def set_template_file(self, filepath: Path | str):
"""
@@ -633,7 +635,7 @@ class SubmissionType(BaseClass):
def construct_info_map(self, mode: Literal['read', 'write']) -> dict:
info = self.info_map
logger.debug(f"Info map: {info}")
# logger.debug(f"Info map: {info}")
output = {}
# for k,v in info.items():
# info[k]['write'] += info[k]['read']
@@ -956,7 +958,7 @@ class SubmissionReagentAssociation(BaseClass):
Returns:
str: Representation of this SubmissionReagentAssociation
"""
return f"<{self.submission.rsl_plate_num}&{self.reagent.lot}>"
return f"<{self.submission.rsl_plate_num} & {self.reagent.lot}>"
def __init__(self, reagent=None, submission=None):
if isinstance(reagent, list):

View File

@@ -226,7 +226,7 @@ class BasicSubmission(BaseClass):
if report:
return output
if full_data:
logger.debug(f"Attempting reagents.")
# logger.debug(f"Attempting reagents.")
try:
reagents = [item.to_sub_dict(extraction_kit=self.extraction_kit) for item in
self.submission_reagent_associations]
@@ -240,9 +240,9 @@ class BasicSubmission(BaseClass):
except Exception as e:
logger.error(f"We got an error retrieving reagents: {e}")
reagents = None
logger.debug(f"Running samples.")
# logger.debug(f"Running samples.")
samples = self.adjust_to_dict_samples(backup=backup)
logger.debug("Running equipment")
# logger.debug("Running equipment")
try:
equipment = [item.to_sub_dict() for item in self.submission_equipment_associations]
if len(equipment) == 0:
@@ -297,7 +297,7 @@ class BasicSubmission(BaseClass):
# Get kit associated with this submission
assoc = [item for item in self.extraction_kit.kit_submissiontype_associations if
item.submission_type == self.submission_type][0]
logger.debug(f"Came up with association: {assoc}")
# logger.debug(f"Came up with association: {assoc}")
# If every individual cost is 0 this is probably an old plate.
if all(item == 0.0 for item in [assoc.constant_cost, assoc.mutable_cost_column, assoc.mutable_cost_sample]):
try:
@@ -369,7 +369,7 @@ class BasicSubmission(BaseClass):
return [item.role for item in self.submission_equipment_associations]
@classmethod
def submissions_to_df(cls, submission_type: str | None = None, limit: int = 0) -> pd.DataFrame:
def submissions_to_df(cls, submission_type: str | None = None, limit: int = 0, chronologic:bool=True) -> pd.DataFrame:
"""
Convert all submissions to dataframe
@@ -380,11 +380,11 @@ class BasicSubmission(BaseClass):
Returns:
pd.DataFrame: Pandas Dataframe of all relevant submissions
"""
logger.debug(f"Querying Type: {submission_type}")
logger.debug(f"Using limit: {limit}")
# logger.debug(f"Querying Type: {submission_type}")
# logger.debug(f"Using limit: {limit}")
# use lookup function to create list of dicts
subs = [item.to_dict() for item in cls.query(submission_type=submission_type, limit=limit)]
logger.debug(f"Got {len(subs)} submissions.")
subs = [item.to_dict() for item in cls.query(submission_type=submission_type, limit=limit, chronologic=chronologic)]
# logger.debug(f"Got {len(subs)} submissions.")
df = pd.DataFrame.from_records(subs)
# Exclude sub information
for item in ['controls', 'extraction_info', 'pcr_info', 'comment', 'comments', 'samples', 'reagents',
@@ -393,6 +393,8 @@ class BasicSubmission(BaseClass):
df = df.drop(item, axis=1)
except:
logger.warning(f"Couldn't drop '{item}' column from submissionsheet df.")
if chronologic:
df.sort_values(by="Submitted Date", axis=0, inplace=True, ascending=False)
return df
def set_attribute(self, key: str, value):
@@ -466,7 +468,10 @@ class BasicSubmission(BaseClass):
flag_modified(self, key)
return
case _:
field_value = value
try:
field_value = value.strip()
except AttributeError:
field_value = value
# insert into field
try:
self.__setattr__(key, field_value)
@@ -502,7 +507,7 @@ class BasicSubmission(BaseClass):
"""
from backend.validators import PydSubmission, PydSample, PydReagent, PydEquipment
dicto = self.to_dict(full_data=True, backup=backup)
logger.debug("To dict complete")
# logger.debug("To dict complete")
new_dict = {}
for key, value in dicto.items():
# start = time()
@@ -526,11 +531,11 @@ class BasicSubmission(BaseClass):
case "id":
pass
case _:
logger.debug(f"Setting dict {key} to {value}")
# logger.debug(f"Setting dict {key} to {value}")
new_dict[key.lower().replace(" ", "_")] = dict(value=value, missing=missing)
# logger.debug(f"{key} complete after {time()-start}")
new_dict['filepath'] = Path(tempfile.TemporaryFile().name)
logger.debug("Done converting fields.")
# logger.debug("Done converting fields.")
return PydSubmission(**new_dict)
def save(self, original: bool = True):
@@ -604,20 +609,20 @@ class BasicSubmission(BaseClass):
# Child class custom functions
@classmethod
def custom_platemap(cls, xl: pd.ExcelFile, plate_map: pd.DataFrame) -> pd.DataFrame:
"""
Stupid stopgap solution to there being an issue with the Bacterial Culture plate map
Args:
xl (pd.ExcelFile): original xl workbook, used for child classes mostly
plate_map (pd.DataFrame): original plate map
Returns:
pd.DataFrame: updated plate map.
"""
logger.info(f"Calling {cls.__mapper_args__['polymorphic_identity']} plate mapper.")
return plate_map
# @classmethod
# def custom_platemap(cls, xl: pd.ExcelFile, plate_map: pd.DataFrame) -> pd.DataFrame:
# """
# Stupid stopgap solution to there being an issue with the Bacterial Culture plate map
#
# Args:
# xl (pd.ExcelFile): original xl workbook, used for child classes mostly
# plate_map (pd.DataFrame): original plate map
#
# Returns:
# pd.DataFrame: updated plate map.
# """
# logger.info(f"Calling {cls.__mapper_args__['polymorphic_identity']} plate mapper.")
# return plate_map
@classmethod
def custom_info_parser(cls, input_dict: dict, xl: Workbook | None = None) -> dict:
@@ -696,15 +701,15 @@ class BasicSubmission(BaseClass):
# logger.info(f"Hello from {cls.__mapper_args__['polymorphic_identity']} Enforcer!")
# return instr
from backend.validators import RSLNamer
logger.debug(f"instr coming into {cls}: {instr}")
logger.debug(f"data coming into {cls}: {data}")
# 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']
# outstr = super().enforce_name(instr=instr, data=data)
if instr in [None, ""]:
logger.debug("Sending to RSLNamer to make new plate name.")
# logger.debug("Sending to RSLNamer to make new plate name.")
outstr = RSLNamer.construct_new_plate_name(data=data)
else:
outstr = instr
@@ -761,9 +766,9 @@ class BasicSubmission(BaseClass):
Returns:
list: _description_
"""
logger.debug(f"Hello from {cls.__mapper_args__['polymorphic_identity']} PCR parser!")
# logger.debug(f"Hello from {cls.__mapper_args__['polymorphic_identity']} PCR parser!")
pcr_sample_map = cls.get_submission_type().sample_map['pcr_samples']
logger.debug(f'sample map: {pcr_sample_map}')
# logger.debug(f'sample map: {pcr_sample_map}')
main_sheet = xl[pcr_sample_map['main_sheet']]
samples = []
fields = {k: v for k, v in pcr_sample_map.items() if k not in ['main_sheet', 'start_row']}
@@ -816,7 +821,7 @@ class BasicSubmission(BaseClass):
Returns:
List[dict]: Updated dictionaries
"""
logger.debug(f"Hello from {self.__class__.__name__} dictionary sample adjuster.")
# logger.debug(f"Hello from {self.__class__.__name__} dictionary sample adjuster.")
return [item.to_sub_dict() for item in self.submission_sample_associations]
@classmethod
@@ -833,7 +838,7 @@ class BasicSubmission(BaseClass):
base_dict['excluded'] = cls.get_default_info('details_ignore')
env = jinja_template_loading()
temp_name = f"{cls.__name__.lower()}_details.html"
logger.debug(f"Returning template: {temp_name}")
# logger.debug(f"Returning template: {temp_name}")
try:
template = env.get_template(temp_name)
except TemplateNotFound as e:
@@ -872,7 +877,7 @@ class BasicSubmission(BaseClass):
Returns:
models.BasicSubmission | List[models.BasicSubmission]: Submission(s) of interest
"""
logger.debug(f"Incoming kwargs: {kwargs}")
# logger.debug(f"Incoming kwargs: {kwargs}")
# NOTE: if you go back to using 'model' change the appropriate cls to model in the query filters
if submission_type is not None:
# if isinstance(submission_type, SubmissionType):
@@ -882,7 +887,7 @@ class BasicSubmission(BaseClass):
# model = cls.find_subclasses(submission_type=submission_type)
elif len(kwargs) > 0:
# find the subclass containing the relevant attributes
logger.debug(f"Attributes for search: {kwargs}")
# logger.debug(f"Attributes for search: {kwargs}")
# model = cls.find_subclasses(attrs=kwargs)
model = cls.find_polymorphic_subclass(attrs=kwargs)
else:
@@ -895,7 +900,7 @@ class BasicSubmission(BaseClass):
logger.warning(f"End date with no start date, using Jan 1, 2023")
start_date = date(2023, 1, 1)
if start_date is not None:
logger.debug(f"Querying with start date: {start_date} and end date: {end_date}")
# logger.debug(f"Querying with start date: {start_date} and end date: {end_date}")
match start_date:
case date():
# logger.debug(f"Lookup BasicSubmission by start_date({start_date})")
@@ -919,7 +924,7 @@ class BasicSubmission(BaseClass):
# logger.debug(f"Lookup BasicSubmission by parsed str end_date {end_date}")
end_date = parse(end_date).strftime("%Y-%m-%d")
# logger.debug(f"Looking up BasicSubmissions from start date: {start_date} and end date: {end_date}")
logger.debug(f"Start date {start_date} == End date {end_date}: {start_date == end_date}")
# logger.debug(f"Start date {start_date} == End date {end_date}: {start_date == end_date}")
# logger.debug(f"Compensating for same date by using time")
if start_date == end_date:
start_date = datetime.strptime(start_date, "%Y-%m-%d").strftime("%Y-%m-%d %H:%M:%S.%f")
@@ -999,7 +1004,7 @@ class BasicSubmission(BaseClass):
f"{key} is not allowed as a query argument as it could lead to creation of duplicate objects. Use .query() instead.")
instance = cls.query(submission_type=submission_type, limit=1, **kwargs)
# logger.debug(f"Retrieved instance: {instance}")
if instance == None:
if instance is None:
used_class = cls.find_polymorphic_subclass(attrs=kwargs, polymorphic_identity=submission_type)
instance = used_class(**kwargs)
match submission_type:
@@ -1041,11 +1046,11 @@ class BasicSubmission(BaseClass):
e: _description_
"""
from frontend.widgets.pop_ups import QuestionAsker
logger.debug("Hello from delete")
# logger.debug("Hello from delete")
fname = self.__backup_path__.joinpath(f"{self.rsl_plate_num}-backup({date.today().strftime('%Y%m%d')})")
msg = QuestionAsker(title="Delete?", message=f"Are you sure you want to delete {self.rsl_plate_num}?\n")
if msg.exec():
# self.backup(fname=fname, full_backup=True)
self.backup(fname=fname, full_backup=True)
self.__database_session__.delete(self)
try:
self.__database_session__.commit()
@@ -1061,7 +1066,7 @@ class BasicSubmission(BaseClass):
Args:
obj (_type_): parent widget
"""
logger.debug("Hello from details")
# logger.debug("Hello from details")
from frontend.widgets.submission_details import SubmissionDetails
dlg = SubmissionDetails(parent=obj, sub=self)
if dlg.exec():
@@ -1070,7 +1075,7 @@ class BasicSubmission(BaseClass):
def edit(self, obj):
from frontend.widgets.submission_widget import SubmissionFormWidget
for widg in obj.app.table_widget.formwidget.findChildren(SubmissionFormWidget):
logger.debug(widg)
# logger.debug(widg)
widg.setParent(None)
pyd = self.to_pydantic(backup=True)
form = pyd.to_form(parent=obj)
@@ -1088,7 +1093,7 @@ class BasicSubmission(BaseClass):
if dlg.exec():
comment = dlg.parse_form()
self.set_attribute(key='comment', value=comment)
logger.debug(self.comment)
# logger.debug(self.comment)
self.save(original=False)
def add_equipment(self, obj):
@@ -1102,11 +1107,11 @@ class BasicSubmission(BaseClass):
dlg = EquipmentUsage(parent=obj, submission=self)
if dlg.exec():
equipment = dlg.parse_form()
logger.debug(f"We've got equipment: {equipment}")
# logger.debug(f"We've got equipment: {equipment}")
for equip in equipment:
logger.debug(f"Processing: {equip}")
# logger.debug(f"Processing: {equip}")
_, assoc = equip.toSQL(submission=self)
logger.debug(f"Appending SubmissionEquipmentAssociation: {assoc}")
# logger.debug(f"Appending SubmissionEquipmentAssociation: {assoc}")
assoc.save()
else:
pass
@@ -1120,14 +1125,14 @@ class BasicSubmission(BaseClass):
fname (Path | None, optional): Filename of xlsx file. Defaults to None.
full_backup (bool, optional): Whether or not to make yaml file. Defaults to False.
"""
logger.debug("Hello from backup.")
# logger.debug("Hello from backup.")
pyd = self.to_pydantic(backup=True)
if fname == None:
if fname is None:
from frontend.widgets.functions import select_save_file
fname = select_save_file(default_name=pyd.construct_filename(), extension="xlsx", obj=obj)
logger.debug(fname.name)
# logger.debug(fname.name)
if fname.name == "":
logger.debug(f"export cancelled.")
# logger.debug(f"export cancelled.")
return
# pyd.filepath = fname
if full_backup:
@@ -1171,28 +1176,28 @@ class BacterialCulture(BasicSubmission):
output['controls'] = [item.to_sub_dict() for item in self.controls]
return output
@classmethod
def custom_platemap(cls, xl: pd.ExcelFile, plate_map: pd.DataFrame) -> pd.DataFrame:
"""
Stupid stopgap solution to there being an issue with the Bacterial Culture plate map. Extends parent.
Args:
xl (pd.ExcelFile): original xl workbook
plate_map (pd.DataFrame): original plate map
Returns:
pd.DataFrame: updated plate map.
"""
plate_map = super().custom_platemap(xl, plate_map)
num1 = xl.parse("Sample List").iloc[40, 1]
num2 = xl.parse("Sample List").iloc[41, 1]
logger.debug(f"Broken: {plate_map.iloc[5, 0]}, {plate_map.iloc[6, 0]}")
logger.debug(f"Replace: {num1}, {num2}")
if not check_not_nan(plate_map.iloc[5, 0]):
plate_map.iloc[5, 0] = num1
if not check_not_nan(plate_map.iloc[6, 0]):
plate_map.iloc[6, 0] = num2
return plate_map
# @classmethod
# def custom_platemap(cls, xl: pd.ExcelFile, plate_map: pd.DataFrame) -> pd.DataFrame:
# """
# Stupid stopgap solution to there being an issue with the Bacterial Culture plate map. Extends parent.
#
# Args:
# xl (pd.ExcelFile): original xl workbook
# plate_map (pd.DataFrame): original plate map
#
# Returns:
# pd.DataFrame: updated plate map.
# """
# plate_map = super().custom_platemap(xl, plate_map)
# num1 = xl.parse("Sample List").iloc[40, 1]
# num2 = xl.parse("Sample List").iloc[41, 1]
# # logger.debug(f"Broken: {plate_map.iloc[5, 0]}, {plate_map.iloc[6, 0]}")
# # logger.debug(f"Replace: {num1}, {num2}")
# if not check_not_nan(plate_map.iloc[5, 0]):
# plate_map.iloc[5, 0] = num1
# if not check_not_nan(plate_map.iloc[6, 0]):
# plate_map.iloc[6, 0] = num2
# return plate_map
# @classmethod
# def custom_writer(cls, input_excel: Workbook, info: dict | None = None, backup: bool = False) -> Workbook:
@@ -1255,7 +1260,7 @@ class BacterialCulture(BasicSubmission):
for sample in input_dict['samples']:
matched = regex.match(sample['submitter_id'])
if bool(matched):
logger.debug(f"Control match found: {sample['submitter_id']}")
# logger.debug(f"Control match found: {sample['submitter_id']}")
new_lot = matched.group()
try:
pos_control_reg = \
@@ -1272,8 +1277,8 @@ class BacterialCulture(BasicSubmission):
"""
Extends parent
"""
logger.debug(f"Checking {sample.well}")
logger.debug(f"here's the worksheet: {worksheet}")
# 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}")
@@ -1282,7 +1287,7 @@ class BacterialCulture(BasicSubmission):
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}")
# logger.debug(f"Here is the row: {idx}")
row = idx.index.to_list()[0]
return row + 1
@@ -1386,14 +1391,19 @@ class Wastewater(BasicSubmission):
Parse specific to wastewater samples.
"""
samples = super().parse_pcr(xl=xl, rsl_plate_num=rsl_plate_num)
logger.debug(f'Samples from parent pcr parser: {pformat(samples)}')
# logger.debug(f'Samples from parent pcr parser: {pformat(samples)}')
output = []
for sample in samples:
# NOTE: remove '-{target}' from controls
sample['sample'] = re.sub('-N\\d$', '', sample['sample'])
# NOTE: if sample is already in output skip
if sample['sample'] in [item['sample'] for item in output]:
continue
# NOTE: Set ct values
sample[f"ct_{sample['target'].lower()}"] = sample['ct'] if isinstance(sample['ct'], float) else 0.0
# NOTE: Set assessment
sample[f"{sample['target'].lower()}_status"] = sample['assessment']
# NOTE: Get sample having other target
other_targets = [s for s in samples if re.sub('-N\\d$', '', s['sample']) == sample['sample']]
for s in other_targets:
sample[f"ct_{s['target'].lower()}"] = s['ct'] if isinstance(s['ct'], float) else 0.0
@@ -1446,13 +1456,13 @@ class Wastewater(BasicSubmission):
"""
Extends parent
"""
logger.debug(f"Checking {sample.well}")
logger.debug(f"here's the worksheet: {worksheet}")
# 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}")
# logger.debug(f"Here's the dataframe: {df}")
idx = df[df[1] == sample.sample_location]
logger.debug(f"Here is the row: {idx}")
# logger.debug(f"Here is the row: {idx}")
row = idx.index.to_list()[0]
return row + 1
@@ -1468,10 +1478,10 @@ class Wastewater(BasicSubmission):
parser = PCRParser(filepath=fname)
self.set_attribute("pcr_info", parser.pcr)
self.save(original=False)
logger.debug(f"Got {len(parser.samples)} samples to update!")
logger.debug(f"Parser samples: {parser.samples}")
# logger.debug(f"Got {len(parser.samples)} samples to update!")
# logger.debug(f"Parser samples: {parser.samples}")
for sample in self.samples:
logger.debug(f"Running update on: {sample}")
# logger.debug(f"Running update on: {sample}")
try:
sample_dict = [item for item in parser.samples if item['sample'] == sample.rsl_number][0]
except IndexError:
@@ -1551,19 +1561,18 @@ class WastewaterArtic(BasicSubmission):
Extends parent
"""
try:
# Deal with PCR file.
# NOTE: Deal with PCR file.
instr = re.sub(r"Artic", "", instr, flags=re.IGNORECASE)
except (AttributeError, TypeError) as e:
logger.error(f"Problem using regex: {e}")
logger.debug(f"Before RSL addition: {instr}")
# logger.debug(f"Before RSL addition: {instr}")
try:
instr = instr.replace("-", "")
except AttributeError:
instr = date.today().strftime("%Y%m%d")
instr = re.sub(r"^(\d{6})", f"RSL-AR-\\1", instr)
logger.debug(f"name coming out of Artic namer: {instr}")
# logger.debug(f"name coming out of Artic namer: {instr}")
outstr = super().enforce_name(instr=instr, data=data)
return outstr
@classmethod
@@ -1611,39 +1620,39 @@ class WastewaterArtic(BasicSubmission):
Returns:
str: output name
"""
logger.debug(f"input string raw: {input_str}")
# logger.debug(f"input string raw: {input_str}")
# Remove letters.
processed = input_str.replace("RSL", "")
processed = re.sub(r"\(.*\)$", "", processed).strip()
processed = re.sub(r"[A-QS-Z]+\d*", "", processed)
# Remove trailing '-' if any
processed = processed.strip("-")
logger.debug(f"Processed after stripping letters: {processed}")
# logger.debug(f"Processed after stripping letters: {processed}")
try:
en_num = re.search(r"\-\d{1}$", processed).group()
processed = rreplace(processed, en_num, "")
except AttributeError:
en_num = "1"
en_num = en_num.strip("-")
logger.debug(f"Processed after en_num: {processed}")
# logger.debug(f"Processed after en_num: {processed}")
try:
plate_num = re.search(r"\-\d{1}R?\d?$", processed).group()
processed = rreplace(processed, plate_num, "")
except AttributeError:
plate_num = "1"
plate_num = plate_num.strip("-")
logger.debug(f"Processed after plate-num: {processed}")
# logger.debug(f"Processed after plate-num: {processed}")
day = re.search(r"\d{2}$", processed).group()
processed = rreplace(processed, day, "")
logger.debug(f"Processed after day: {processed}")
# logger.debug(f"Processed after day: {processed}")
month = re.search(r"\d{2}$", processed).group()
processed = rreplace(processed, month, "")
processed = processed.replace("--", "")
logger.debug(f"Processed after month: {processed}")
# logger.debug(f"Processed after month: {processed}")
year = re.search(r'^(?:\d{2})?\d{2}', processed).group()
year = f"20{year}"
final_en_name = f"EN{en_num}-{year}{month}{day}"
logger.debug(f"Final EN name: {final_en_name}")
# logger.debug(f"Final EN name: {final_en_name}")
return final_en_name
@classmethod
@@ -1657,14 +1666,14 @@ class WastewaterArtic(BasicSubmission):
Returns:
str: output name
"""
logger.debug(f"input string raw: {input_str}")
# logger.debug(f"input string raw: {input_str}")
# Remove letters.
processed = input_str.replace("RSL", "")
processed = re.sub(r"\(.*\)$", "", processed).strip()
processed = re.sub(r"[A-QS-Z]+\d*", "", processed)
# Remove trailing '-' if any
processed = processed.strip("-")
logger.debug(f"Processed after stripping letters: {processed}")
# logger.debug(f"Processed after stripping letters: {processed}")
# try:
# en_num = re.search(r"\-\d{1}$", processed).group()
# processed = rreplace(processed, en_num, "")
@@ -1678,23 +1687,23 @@ class WastewaterArtic(BasicSubmission):
except AttributeError:
plate_num = "1"
plate_num = plate_num.strip("-")
logger.debug(f"Plate num: {plate_num}")
# logger.debug(f"Plate num: {plate_num}")
repeat_num = re.search(r"R(?P<repeat>\d)?$", "PBS20240426-2R").groups()[0]
if repeat_num is None and "R" in plate_num:
repeat_num = "1"
plate_num = re.sub(r"R", rf"R{repeat_num}", plate_num)
logger.debug(f"Processed after plate-num: {processed}")
# logger.debug(f"Processed after plate-num: {processed}")
day = re.search(r"\d{2}$", processed).group()
processed = rreplace(processed, day, "")
logger.debug(f"Processed after day: {processed}")
# logger.debug(f"Processed after day: {processed}")
month = re.search(r"\d{2}$", processed).group()
processed = rreplace(processed, month, "")
processed = processed.replace("--", "")
logger.debug(f"Processed after month: {processed}")
# logger.debug(f"Processed after month: {processed}")
year = re.search(r'^(?:\d{2})?\d{2}', processed).group()
year = f"20{year}"
final_en_name = f"PBS{year}{month}{day}-{plate_num}"
logger.debug(f"Final EN name: {final_en_name}")
# logger.debug(f"Final EN name: {final_en_name}")
return final_en_name
@classmethod
@@ -1722,12 +1731,12 @@ class WastewaterArtic(BasicSubmission):
dict: Updated parser product.
"""
input_dict = super().finalize_parse(input_dict, xl, info_map)
logger.debug(f"Incoming input_dict: {pformat(input_dict)}")
# logger.debug(f"Incoming input_dict: {pformat(input_dict)}")
# TODO: Move to validator?
for sample in input_dict['samples']:
logger.debug(f"Sample: {sample}")
# logger.debug(f"Sample: {sample}")
if re.search(r"^NTC", sample['submitter_id']):
sample['submitter_id'] = sample['submitter_id'] + "-WWG-" + input_dict['rsl_plate_num']['value']
sample['submitter_id'] = f"{sample['submitter_id']}-WWG-{input_dict['rsl_plate_num']['value']}"
input_dict['csv'] = xl["hitpicks_csv_to_export"]
return input_dict
@@ -1748,13 +1757,13 @@ class WastewaterArtic(BasicSubmission):
# worksheet = input_excel["First Strand List"]
# samples = cls.query(rsl_number=info['rsl_plate_num']['value']).submission_sample_associations
# samples = sorted(samples, key=attrgetter('column', 'row'))
logger.debug(f"Info:\n{pformat(info)}")
# logger.debug(f"Info:\n{pformat(info)}")
check = 'source_plates' in info.keys() and info['source_plates'] is not None
if check:
worksheet = input_excel['First Strand List']
start_row = 8
for iii, plate in enumerate(info['source_plates']['value']):
logger.debug(f"Plate: {plate}")
# logger.debug(f"Plate: {plate}")
row = start_row + iii
try:
worksheet.cell(row=row, column=3, value=plate['plate'])
@@ -1782,12 +1791,16 @@ class WastewaterArtic(BasicSubmission):
# logger.debug(f"vj: {vj}")
column = start_column + 2 + jjj
worksheet.cell(row=start_row, column=column, value=kj['name'])
worksheet.cell(row=row, column=column, value=kj['value'])
# logger.debug(f"Writing {kj['name']} with value {kj['value']} to row {row}, column {column}")
try:
worksheet.cell(row=row, column=column, value=kj['value'])
except AttributeError:
logger.error(f"Failed {kj['name']} with value {kj['value']} to row {row}, column {column}")
check = 'gel_image' in info.keys() and info['gel_image']['value'] is not None
if check:
if info['gel_image'] != None:
worksheet = input_excel['Egel results']
logger.debug(f"We got an image: {info['gel_image']}")
# logger.debug(f"We got an image: {info['gel_image']}")
with ZipFile(cls.__directory_path__.joinpath("submission_imgs.zip")) as zipped:
z = zipped.extract(info['gel_image']['value'], Path(TemporaryDirectory().name))
img = OpenpyxlImage(z)
@@ -1817,7 +1830,7 @@ class WastewaterArtic(BasicSubmission):
headers = [item['name'] for item in base_dict['gel_info'][0]['values']]
base_dict['headers'] = [''] * (4 - len(headers))
base_dict['headers'] += headers
logger.debug(f"Gel info: {pformat(base_dict['headers'])}")
# logger.debug(f"Gel info: {pformat(base_dict['headers'])}")
check = 'gel_image' in base_dict.keys() and base_dict['gel_image'] != None
if check:
with ZipFile(cls.__directory_path__.joinpath("submission_imgs.zip")) as zipped:
@@ -1834,7 +1847,7 @@ class WastewaterArtic(BasicSubmission):
Returns:
List[dict]: Updated dictionaries
"""
logger.debug(f"Hello from {self.__class__.__name__} dictionary sample adjuster.")
# logger.debug(f"Hello from {self.__class__.__name__} dictionary sample adjuster.")
output = []
set_plate = None
for assoc in self.submission_sample_associations:
@@ -1888,7 +1901,7 @@ class WastewaterArtic(BasicSubmission):
self.comment.append(com)
else:
self.comment = [com]
logger.debug(pformat(self.gel_info))
# logger.debug(pformat(self.gel_info))
with ZipFile(self.__directory_path__.joinpath("submission_imgs.zip"), 'a') as zipf:
# Add a file located at the source_path to the destination within the zip
# file. It will overwrite existing files if the names collide, but it
@@ -1966,7 +1979,7 @@ class BasicSample(BaseClass):
Returns:
dict: well location and name (sample id, organism) NOTE: keys must sync with WWSample to_sub_dict above
"""
logger.debug(f"Converting {self} to dict.")
# logger.debug(f"Converting {self} to dict.")
# start = time()
sample = {}
sample['Submitter ID'] = self.submitter_id
@@ -2042,7 +2055,7 @@ class BasicSample(BaseClass):
Returns:
dict: Updated parser results.
"""
logger.debug(f"Hello from {cls.__name__} sample parser!")
# logger.debug(f"Hello from {cls.__name__} sample parser!")
return input_dict
@classmethod
@@ -2059,7 +2072,7 @@ class BasicSample(BaseClass):
base_dict['excluded'] = ['submissions', 'excluded', 'colour', 'tooltip']
env = jinja_template_loading()
temp_name = f"{cls.__name__.lower()}_details.html"
logger.debug(f"Returning template: {temp_name}")
# logger.debug(f"Returning template: {temp_name}")
try:
template = env.get_template(temp_name)
except TemplateNotFound as e:
@@ -2091,7 +2104,7 @@ class BasicSample(BaseClass):
model = cls.find_polymorphic_subclass(attrs=kwargs)
else:
model = cls.find_polymorphic_subclass(polymorphic_identity=sample_type)
logger.debug(f"Length of kwargs: {len(kwargs)}")
# logger.debug(f"Length of kwargs: {len(kwargs)}")
# model = models.BasicSample.find_subclasses(ctx=ctx, attrs=kwargs)
# query: Query = setup_lookup(ctx=ctx, locals=locals()).query(model)
query: Query = cls.__database_session__.query(model)
@@ -2141,7 +2154,7 @@ class BasicSample(BaseClass):
# f"{key} is not allowed as a query argument as it could lead to creation of duplicate objects.")
sanitized_kwargs = {k:v for k,v in kwargs.items() if k not in disallowed}
instance = cls.query(sample_type=sample_type, limit=1, **kwargs)
logger.debug(f"Retrieved instance: {instance}")
# logger.debug(f"Retrieved instance: {instance}")
if instance is None:
used_class = cls.find_polymorphic_subclass(attrs=sanitized_kwargs, polymorphic_identity=sample_type)
instance = used_class(**sanitized_kwargs)
@@ -2219,7 +2232,7 @@ class WastewaterSample(BasicSample):
dict: Updated parser results.
"""
output_dict = super().parse_sample(input_dict)
logger.debug(f"Initial sample dict: {pformat(output_dict)}")
# logger.debug(f"Initial sample dict: {pformat(output_dict)}")
disallowed = ["", None, "None"]
try:
check = output_dict['rsl_number'] in [None, "None"]
@@ -2258,7 +2271,7 @@ class WastewaterSample(BasicSample):
plates = [item.rsl_plate_num for item in
self.submissions[:self.submissions.index(current_artic_submission)]]
subs = [sub for sub in self.submissions if sub.rsl_plate_num in plates]
logger.debug(f"Submissions: {subs}")
# logger.debug(f"Submissions: {subs}")
try:
return subs[-1]
except IndexError:
@@ -2339,7 +2352,7 @@ class SubmissionSampleAssociation(BaseClass):
self.id = id
else:
self.id = self.__class__.autoincrement_id()
logger.debug(f"Using id: {self.id}")
# logger.debug(f"Using submission sample association id: {self.id}")
def __repr__(self) -> str:
try:
@@ -2356,9 +2369,9 @@ class SubmissionSampleAssociation(BaseClass):
dict: Updated dictionary with row, column and well updated
"""
# Get sample info
logger.debug(f"Running {self.__repr__()}")
# logger.debug(f"Running {self.__repr__()}")
sample = self.sample.to_sub_dict()
logger.debug("Sample conversion complete.")
# logger.debug("Sample conversion complete.")
sample['Name'] = self.sample.submitter_id
sample['Row'] = self.row
sample['Column'] = self.column
@@ -2382,7 +2395,7 @@ class SubmissionSampleAssociation(BaseClass):
"""
# Since there is no PCR, negliable result is necessary.
sample = self.to_sub_dict()
logger.debug(f"Sample dict to hitpick: {sample}")
# logger.debug(f"Sample dict to hitpick: {sample}")
env = jinja_template_loading()
template = env.get_template("tooltip.html")
tooltip_text = template.render(fields=sample)
@@ -2430,7 +2443,7 @@ class SubmissionSampleAssociation(BaseClass):
except Exception as e:
logger.error(f"Could not get polymorph {polymorphic_identity} of {cls} due to {e}")
output = cls
logger.debug(f"Using SubmissionSampleAssociation subclass: {output}")
# logger.debug(f"Using SubmissionSampleAssociation subclass: {output}")
return output
@classmethod
@@ -2519,7 +2532,7 @@ class SubmissionSampleAssociation(BaseClass):
Returns:
SubmissionSampleAssociation: Queried or new association.
"""
logger.debug(f"Attempting create or query with {kwargs}")
# logger.debug(f"Attempting create or query with {kwargs}")
match submission:
case BasicSubmission():
pass