Created omni-manager, omni-addit

This commit is contained in:
lwark
2025-01-03 12:37:03 -06:00
parent 482b641569
commit b55258f677
19 changed files with 502 additions and 77 deletions

View File

@@ -12,6 +12,7 @@ from typing import Any, List
from pathlib import Path
from tools import report_result
# NOTE: Load testing environment
if 'pytest' in sys.modules:
sys.path.append(Path(__file__).parents[4].absolute().joinpath("tests").__str__())
@@ -167,7 +168,10 @@ class BaseClass(Base):
Returns:
Dataframe
"""
records = [obj.to_sub_dict(**kwargs) for obj in objects]
try:
records = [obj.to_sub_dict(**kwargs) for obj in objects]
except AttributeError:
records = [obj.to_dict() for obj in objects]
return DataFrame.from_records(records)
@classmethod
@@ -233,6 +237,15 @@ class BaseClass(Base):
report.add_result(Result(msg=e, status="Critical"))
return report
def to_dict(self):
return {k: v for k, v in self.__dict__.items() if k not in ["_sa_instance_state", "id"]}
@classmethod
def get_pydantic_model(cls):
from backend.validators import pydant
model = getattr(pydant, f"Pyd{cls.__name__}")
return model
class ConfigItem(BaseClass):
"""

View File

@@ -124,6 +124,8 @@ class Contact(BaseClass):
Base of Contact
"""
searchables =[]
id = Column(INTEGER, primary_key=True) #: primary key
name = Column(String(64)) #: contact name
email = Column(String(64)) #: contact email

View File

@@ -7,6 +7,7 @@ from collections import OrderedDict
from copy import deepcopy
from getpass import getuser
import logging, uuid, tempfile, re, base64, numpy as np, pandas as pd, types, sys
from inspect import isclass
from zipfile import ZipFile, BadZipfile
from tempfile import TemporaryDirectory, TemporaryFile
from operator import itemgetter
@@ -175,7 +176,7 @@ class BasicSubmission(BaseClass, LogMixin):
# NOTE: Fields not placed in ui form
form_ignore=['reagents', 'ctx', 'id', 'cost', 'extraction_info', 'signed_by', 'comment', 'namer',
'submission_object', "tips", 'contact_phone', 'custom', 'cost_centre', 'completed_date',
'controls'] + recover,
'controls', "origin_plate"] + recover,
# NOTE: Fields not placed in ui form to be moved to pydantic
form_recover=recover
))
@@ -352,7 +353,10 @@ class BasicSubmission(BaseClass, LogMixin):
try:
contact = self.contact.name
except AttributeError as e:
contact = "NA"
try:
contact = f"Defaulted to: {self.submitting_lab.contacts[0].name}"
except (AttributeError, IndexError):
contact = "NA"
try:
contact_phone = self.contact.phone
except AttributeError:
@@ -627,8 +631,14 @@ class BasicSubmission(BaseClass, LogMixin):
continue
case "tips":
field_value = [item.to_pydantic() for item in self.submission_tips_associations]
case "submission_type" | "contact":
case "submission_type":
field_value = dict(value=self.__getattribute__(key).name, missing=missing)
# case "contact":
# try:
# field_value = dict(value=self.__getattribute__(key).name, missing=missing)
# except AttributeError:
# contact = self.submitting_lab.contacts[0]
# field_value = dict(value=contact.name, missing=True)
case "plate_number":
key = 'rsl_plate_num'
field_value = dict(value=self.rsl_plate_num, missing=missing)
@@ -640,10 +650,13 @@ class BasicSubmission(BaseClass, LogMixin):
case _:
try:
key = key.lower().replace(" ", "_")
field_value = dict(value=self.__getattribute__(key), missing=missing)
if isclass(value):
field_value = dict(value=self.__getattribute__(key).name, missing=missing)
else:
field_value = dict(value=self.__getattribute__(key), missing=missing)
except AttributeError:
logger.error(f"{key} is not available in {self}")
continue
field_value = dict(value="NA", missing=True)
new_dict[key] = field_value
new_dict['filepath'] = Path(tempfile.TemporaryFile().name)
dicto.update(new_dict)
@@ -1505,6 +1518,7 @@ class Wastewater(BasicSubmission):
# NOTE: Due to having to run through samples in for loop we need to convert to list.
output = []
for sample in samples:
logger.debug(sample)
# NOTE: remove '-{target}' from controls
sample['sample'] = re.sub('-N\\d*$', '', sample['sample'])
# NOTE: if sample is already in output skip
@@ -1512,14 +1526,16 @@ class Wastewater(BasicSubmission):
logger.warning(f"Already have {sample['sample']}")
continue
# NOTE: Set ct values
logger.debug(f"Sample ct: {sample['ct']}")
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']
logger.debug(f"Sample assessemnt: {sample['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
sample[f"{s['target'].lower()}_status"] = s['assessment']
# sample[f"{s['target'].lower()}_status"] = s['assessment']
try:
del sample['ct']
except KeyError:
@@ -2915,7 +2931,8 @@ class WastewaterAssociation(SubmissionSampleAssociation):
sample['background_color'] = f"rgb({red}, {grn}, {blu})"
try:
sample[
'tooltip'] += f"<br>- ct N1: {'{:.2f}'.format(self.ct_n1)} ({self.n1_status})<br>- ct N2: {'{:.2f}'.format(self.ct_n2)} ({self.n2_status})"
# 'tooltip'] += f"<br>- ct N1: {'{:.2f}'.format(self.ct_n1)} ({self.n1_status})<br>- ct N2: {'{:.2f}'.format(self.ct_n2)} ({self.n2_status})"
'tooltip'] += f"<br>- ct N1: {'{:.2f}'.format(self.ct_n1)}<br>- ct N2: {'{:.2f}'.format(self.ct_n2)}"
except (TypeError, AttributeError) as e:
logger.error(f"Couldn't set tooltip for {self.sample.rsl_number}. Looks like there isn't PCR data.")
return sample

View File

@@ -171,9 +171,9 @@ class InfoWriter(object):
try:
sheet.cell(row=loc['row'], column=loc['column'], value=v['value'])
except AttributeError as e:
logger.error(f"Can't write {k} to that cell due to {e}")
logger.error(f"Can't write {k} to that cell due to AttributeError: {e}")
except ValueError as e:
logger.error(f"Can't write {v} to that cell due to {e}")
logger.error(f"Can't write {v} to that cell due to ValueError: {e}")
sheet.cell(row=loc['row'], column=loc['column'], value=v['value'].name)
return self.sub_object.custom_info_writer(self.xl, info=final_info, custom_fields=self.info_map['custom'])

View File

@@ -645,6 +645,7 @@ class PydSubmission(BaseModel, extra='allow'):
@field_validator("contact")
@classmethod
def get_contact_from_org(cls, value, values):
logger.debug(f"Value coming in: {value}")
match value:
case dict():
if isinstance(value['value'], tuple):
@@ -653,14 +654,26 @@ class PydSubmission(BaseModel, extra='allow'):
value = dict(value=value[0], missing=False)
case _:
value = dict(value=value, missing=False)
logger.debug(f"Value after match: {value}")
check = Contact.query(name=value['value'])
if check is None:
org = Organization.query(name=values.data['submitting_lab']['value'])
contact = org.contacts[0].name
logger.debug(f"Check came back with {check}")
if not isinstance(check, Contact):
org = values.data['submitting_lab']['value']
logger.debug(f"Checking organization: {org}")
if isinstance(org, str):
org = Organization.query(name=values.data['submitting_lab']['value'], limit=1)
if isinstance(org, Organization):
contact = org.contacts[0].name
else:
logger.warning(f"All attempts at defaulting Contact failed, returning: {value}")
return value
if isinstance(contact, tuple):
contact = contact[0]
return dict(value=contact, missing=True)
value = dict(value=f"Defaulted to: {contact}", missing=True)
logger.debug(f"Value after query: {value}")
return
else:
logger.debug(f"Value after bypass check: {value}")
return value
def __init__(self, run_custom: bool = False, **data):
@@ -983,6 +996,17 @@ class PydContact(BaseModel):
phone: str | None
email: str | None
@field_validator("phone")
@classmethod
def enforce_phone_number(cls, value):
area_regex = re.compile(r"^\(?(\d{3})\)?(-| )?")
if len(value) > 8:
match = area_regex.match(value)
logger.debug(f"Match: {match.group(1)}")
value = area_regex.sub(f"({match.group(1).strip()}) ", value)
logger.debug(f"Output phone: {value}")
return value
def toSQL(self) -> Contact:
"""
Converts this instance into a backend.db.models.organization.Contact instance