Attempting fix of failure to update reagent on edit.
This commit is contained in:
@@ -70,5 +70,5 @@ def update_log(mapper, connection, target):
|
|||||||
logger.info(f"No changes detected, not updating logs.")
|
logger.info(f"No changes detected, not updating logs.")
|
||||||
|
|
||||||
|
|
||||||
event.listen(LogMixin, 'after_update', update_log, propagate=True)
|
# event.listen(LogMixin, 'after_update', update_log, propagate=True)
|
||||||
event.listen(LogMixin, 'after_insert', update_log, propagate=True)
|
# event.listen(LogMixin, 'after_insert', update_log, propagate=True)
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ class BaseClass(Base):
|
|||||||
query: Query = cls.__database_session__.query(model)
|
query: Query = cls.__database_session__.query(model)
|
||||||
# logger.debug(f"Grabbing singles using {model.get_default_info}")
|
# logger.debug(f"Grabbing singles using {model.get_default_info}")
|
||||||
singles = model.get_default_info('singles')
|
singles = model.get_default_info('singles')
|
||||||
logger.info(f"Querying: {model}, with kwargs: {kwargs}")
|
# logger.info(f"Querying: {model}, with kwargs: {kwargs}")
|
||||||
for k, v in kwargs.items():
|
for k, v in kwargs.items():
|
||||||
logger.info(f"Using key: {k} with value: {v}")
|
logger.info(f"Using key: {k} with value: {v}")
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ class Control(BaseClass):
|
|||||||
except StopIteration as e:
|
except StopIteration as e:
|
||||||
raise AttributeError(
|
raise AttributeError(
|
||||||
f"Couldn't find existing class/subclass of {cls} with all attributes:\n{pformat(attrs.keys())}")
|
f"Couldn't find existing class/subclass of {cls} with all attributes:\n{pformat(attrs.keys())}")
|
||||||
logger.info(f"Recruiting model: {model}")
|
# logger.info(f"Recruiting model: {model}")
|
||||||
return model
|
return model
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -535,8 +535,8 @@ class IridaControl(Control):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
consolidate = False
|
consolidate = False
|
||||||
report = Report()
|
report = Report()
|
||||||
# logger.debug(f"settings: {pformat(chart_settings)}")
|
logger.debug(f"settings: {pformat(chart_settings)}")
|
||||||
controls = cls.query(sub_type=chart_settings['sub_type'], start_date=chart_settings['start_date'],
|
controls = cls.query(subtype=chart_settings['sub_type'], start_date=chart_settings['start_date'],
|
||||||
end_date=chart_settings['end_date'])
|
end_date=chart_settings['end_date'])
|
||||||
# logger.debug(f"Controls found: {controls}")
|
# logger.debug(f"Controls found: {controls}")
|
||||||
if not controls:
|
if not controls:
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from tools import check_authorization, setup_lookup, Report, Result, check_regex
|
|||||||
from typing import List, Literal, Generator, Any
|
from typing import List, Literal, Generator, Any
|
||||||
from pandas import ExcelFile
|
from pandas import ExcelFile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from . import Base, BaseClass, Organization
|
from . import Base, BaseClass, Organization, LogMixin
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
logger = logging.getLogger(f'submissions.{__name__}')
|
logger = logging.getLogger(f'submissions.{__name__}')
|
||||||
@@ -797,7 +797,7 @@ class SubmissionType(BaseClass):
|
|||||||
fmap = item.uses
|
fmap = item.uses
|
||||||
if fmap is None:
|
if fmap is None:
|
||||||
fmap = {}
|
fmap = {}
|
||||||
yield getattr(item, f"{field}_role"), fmap
|
yield getattr(item, f"{field}_role").name, fmap
|
||||||
|
|
||||||
def get_default_kit(self) -> KitType | None:
|
def get_default_kit(self) -> KitType | None:
|
||||||
if len(self.kit_types) == 1:
|
if len(self.kit_types) == 1:
|
||||||
@@ -1361,7 +1361,7 @@ class Equipment(BaseClass):
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
"""
|
"""
|
||||||
Returns:
|
Returns:
|
||||||
str: represenation of this Equipment
|
str: representation of this Equipment
|
||||||
"""
|
"""
|
||||||
return f"<Equipment({self.name})>"
|
return f"<Equipment({self.name})>"
|
||||||
|
|
||||||
@@ -1502,7 +1502,7 @@ class Equipment(BaseClass):
|
|||||||
equipment_role = EquipmentRole.query(name=equipment_role)
|
equipment_role = EquipmentRole.query(name=equipment_role)
|
||||||
equipment = cls.query()
|
equipment = cls.query()
|
||||||
options = "\n".join([f"{ii}. {item.name}" for ii, item in enumerate(equipment)])
|
options = "\n".join([f"{ii}. {item.name}" for ii, item in enumerate(equipment)])
|
||||||
choices = input(f"Enter equipment numbers to add to {equipment_role.name} (space seperated):\n{options}\n\n")
|
choices = input(f"Enter equipment numbers to add to {equipment_role.name} (space separated):\n{options}\n\n")
|
||||||
output = []
|
output = []
|
||||||
for choice in choices.split(" "):
|
for choice in choices.split(" "):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -618,6 +618,17 @@ class BasicSubmission(BaseClass, LogMixin):
|
|||||||
result = assoc.save()
|
result = assoc.save()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def update_reagentassoc(self, reagent: Reagent, role: str):
|
||||||
|
from backend.db import SubmissionReagentAssociation
|
||||||
|
# NOTE: get the first reagent assoc that fills the given role.
|
||||||
|
try:
|
||||||
|
assoc = next(item for item in self.submission_reagent_associations if item.reagent and role in [role.name for role in item.reagent.role])
|
||||||
|
assoc.reagent = reagent
|
||||||
|
except StopIteration as e:
|
||||||
|
logger.error(f"Association for {role} not found, creating new association.")
|
||||||
|
assoc = SubmissionReagentAssociation(submission=self, reagent=reagent)
|
||||||
|
self.submission_reagent_associations.append(assoc)
|
||||||
|
|
||||||
def to_pydantic(self, backup: bool = False) -> "PydSubmission":
|
def to_pydantic(self, backup: bool = False) -> "PydSubmission":
|
||||||
"""
|
"""
|
||||||
Converts this instance into a PydSubmission
|
Converts this instance into a PydSubmission
|
||||||
@@ -758,7 +769,7 @@ class BasicSubmission(BaseClass, LogMixin):
|
|||||||
except StopIteration as e:
|
except StopIteration as e:
|
||||||
raise AttributeError(
|
raise AttributeError(
|
||||||
f"Couldn't find existing class/subclass of {cls} with all attributes:\n{pformat(attrs.keys())}")
|
f"Couldn't find existing class/subclass of {cls} with all attributes:\n{pformat(attrs.keys())}")
|
||||||
logger.info(f"Recruiting model: {model}")
|
# logger.info(f"Recruiting model: {model}")
|
||||||
return model
|
return model
|
||||||
|
|
||||||
# Child class custom functions
|
# Child class custom functions
|
||||||
@@ -1414,7 +1425,7 @@ class BacterialCulture(BasicSubmission):
|
|||||||
extends parent
|
extends parent
|
||||||
"""
|
"""
|
||||||
template = super().filename_template()
|
template = super().filename_template()
|
||||||
template += "_{{ submitting_lab }}_{{ submitter_plate_num }}"
|
template += "_{{ submitting_lab.name }}_{{ submitter_plate_num }}"
|
||||||
return template
|
return template
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -2356,7 +2367,7 @@ class BasicSample(BaseClass):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Could not get polymorph {polymorphic_identity} of {cls} due to {e}, using {cls}")
|
logger.error(f"Could not get polymorph {polymorphic_identity} of {cls} due to {e}, using {cls}")
|
||||||
model = cls
|
model = cls
|
||||||
logger.info(f"Recruiting model: {model}")
|
# logger.info(f"Recruiting model: {model}")
|
||||||
return model
|
return model
|
||||||
else:
|
else:
|
||||||
model = cls
|
model = cls
|
||||||
@@ -2370,7 +2381,7 @@ class BasicSample(BaseClass):
|
|||||||
except StopIteration as e:
|
except StopIteration as e:
|
||||||
raise AttributeError(
|
raise AttributeError(
|
||||||
f"Couldn't find existing class/subclass of {cls} with all attributes:\n{pformat(attrs.keys())}")
|
f"Couldn't find existing class/subclass of {cls} with all attributes:\n{pformat(attrs.keys())}")
|
||||||
logger.info(f"Recruiting model: {model}")
|
# logger.info(f"Recruiting model: {model}")
|
||||||
return model
|
return model
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -648,7 +648,8 @@ class TipParser(object):
|
|||||||
Returns:
|
Returns:
|
||||||
List[dict]: List of locations
|
List[dict]: List of locations
|
||||||
"""
|
"""
|
||||||
return {k: v for k, v in self.submission_type.construct_tips_map()}
|
# return {k: v for k, v in self.submission_type.construct_tips_map()}
|
||||||
|
return {k: v for k, v in self.submission_type.construct_field_map("tip")}
|
||||||
|
|
||||||
def parse_tips(self) -> List[dict]:
|
def parse_tips(self) -> List[dict]:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -187,6 +187,9 @@ class InfoWriter(object):
|
|||||||
sheet.cell(row=loc['row'], column=loc['column'], value=v['value'])
|
sheet.cell(row=loc['row'], column=loc['column'], value=v['value'])
|
||||||
except AttributeError as e:
|
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 {e}")
|
||||||
|
except ValueError as e:
|
||||||
|
logger.error(f"Can't write {v} to that cell due to {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'])
|
return self.sub_object.custom_info_writer(self.xl, info=final_info, custom_fields=self.info_map['custom'])
|
||||||
|
|
||||||
|
|
||||||
@@ -208,8 +211,8 @@ class ReagentWriter(object):
|
|||||||
if isinstance(submission_type, str):
|
if isinstance(submission_type, str):
|
||||||
submission_type = SubmissionType.query(name=submission_type)
|
submission_type = SubmissionType.query(name=submission_type)
|
||||||
if isinstance(extraction_kit, str):
|
if isinstance(extraction_kit, str):
|
||||||
kit_type = KitType.query(name=extraction_kit)
|
extraction_kit = KitType.query(name=extraction_kit)
|
||||||
reagent_map = {k: v for k, v in kit_type.construct_xl_map_for_use(submission_type)}
|
reagent_map = {k: v for k, v in extraction_kit.construct_xl_map_for_use(submission_type)}
|
||||||
self.reagents = self.reconcile_map(reagent_list=reagent_list, reagent_map=reagent_map)
|
self.reagents = self.reconcile_map(reagent_list=reagent_list, reagent_map=reagent_map)
|
||||||
|
|
||||||
def reconcile_map(self, reagent_list: List[dict], reagent_map: dict) -> Generator[dict, None, None]:
|
def reconcile_map(self, reagent_list: List[dict], reagent_map: dict) -> Generator[dict, None, None]:
|
||||||
@@ -359,7 +362,10 @@ class EquipmentWriter(object):
|
|||||||
if equipment_list is None:
|
if equipment_list is None:
|
||||||
return
|
return
|
||||||
for ii, equipment in enumerate(equipment_list, start=1):
|
for ii, equipment in enumerate(equipment_list, start=1):
|
||||||
mp_info = equipment_map[equipment['role']]
|
try:
|
||||||
|
mp_info = equipment_map[equipment['role']]
|
||||||
|
except KeyError:
|
||||||
|
logger.error(f"No {equipment['role']} in {pformat(equipment_map)}")
|
||||||
# logger.debug(f"{equipment['role']} map: {mp_info}")
|
# logger.debug(f"{equipment['role']} map: {mp_info}")
|
||||||
placeholder = copy(equipment)
|
placeholder = copy(equipment)
|
||||||
if mp_info == {}:
|
if mp_info == {}:
|
||||||
@@ -427,7 +433,7 @@ class TipWriter(object):
|
|||||||
submission_type = SubmissionType.query(name=submission_type)
|
submission_type = SubmissionType.query(name=submission_type)
|
||||||
self.submission_type = submission_type
|
self.submission_type = submission_type
|
||||||
self.xl = xl
|
self.xl = xl
|
||||||
tips_map = {k: v for k, v in self.submission_type.construct_tips_map()}
|
tips_map = {k: v for k, v in self.submission_type.construct_field_map("tip")}
|
||||||
self.tips = self.reconcile_map(tips_list=tips_list, tips_map=tips_map)
|
self.tips = self.reconcile_map(tips_list=tips_list, tips_map=tips_map)
|
||||||
|
|
||||||
def reconcile_map(self, tips_list: List[dict], tips_map: dict) -> Generator[dict, None, None]:
|
def reconcile_map(self, tips_list: List[dict], tips_map: dict) -> Generator[dict, None, None]:
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ class PydReagent(BaseModel):
|
|||||||
fields = list(self.model_fields.keys()) + extras
|
fields = list(self.model_fields.keys()) + extras
|
||||||
return {k: getattr(self, k) for k in fields}
|
return {k: getattr(self, k) for k in fields}
|
||||||
|
|
||||||
def toSQL(self, submission: BasicSubmission | str = None) -> Tuple[Reagent, SubmissionReagentAssociation, Report]:
|
def toSQL(self, submission: BasicSubmission | str = None) -> Tuple[Reagent, Report]:
|
||||||
"""
|
"""
|
||||||
Converts this instance into a backend.db.models.kit.Reagent instance
|
Converts this instance into a backend.db.models.kit.Reagent instance
|
||||||
|
|
||||||
@@ -164,13 +164,14 @@ class PydReagent(BaseModel):
|
|||||||
report.add_result(Result(owner=__name__, code=0, msg="New reagent created.", status="Information"))
|
report.add_result(Result(owner=__name__, code=0, msg="New reagent created.", status="Information"))
|
||||||
else:
|
else:
|
||||||
if submission is not None and reagent not in submission.reagents:
|
if submission is not None and reagent not in submission.reagents:
|
||||||
assoc = SubmissionReagentAssociation(reagent=reagent, submission=submission)
|
# assoc = SubmissionReagentAssociation(reagent=reagent, submission=submission)
|
||||||
assoc.comments = self.comment
|
# assoc.comments = self.comment
|
||||||
else:
|
submission.update_reagentassoc(reagent=reagent, role=self.role)
|
||||||
assoc = None
|
# else:
|
||||||
|
# assoc = None
|
||||||
# add end-of-life extension from reagent type to expiry date
|
# add end-of-life extension from reagent type to expiry date
|
||||||
# NOTE: this will now be done only in the reporting phase to account for potential changes in end-of-life extensions
|
# NOTE: this will now be done only in the reporting phase to account for potential changes in end-of-life extensions
|
||||||
return reagent, assoc, report
|
return reagent, report
|
||||||
|
|
||||||
|
|
||||||
class PydSample(BaseModel, extra='allow'):
|
class PydSample(BaseModel, extra='allow'):
|
||||||
@@ -299,6 +300,13 @@ class PydTips(BaseModel):
|
|||||||
lot: str | None = Field(default=None)
|
lot: str | None = Field(default=None)
|
||||||
role: str
|
role: str
|
||||||
|
|
||||||
|
@field_validator('role', mode='before')
|
||||||
|
@classmethod
|
||||||
|
def get_role_name(cls, value):
|
||||||
|
if isinstance(value, TipRole):
|
||||||
|
value = value.name
|
||||||
|
return value
|
||||||
|
|
||||||
def to_sql(self, submission: BasicSubmission) -> SubmissionTipsAssociation:
|
def to_sql(self, submission: BasicSubmission) -> SubmissionTipsAssociation:
|
||||||
"""
|
"""
|
||||||
Con
|
Con
|
||||||
@@ -324,6 +332,13 @@ class PydEquipment(BaseModel, extra='ignore'):
|
|||||||
role: str | None
|
role: str | None
|
||||||
tips: List[PydTips] | None = Field(default=None)
|
tips: List[PydTips] | None = Field(default=None)
|
||||||
|
|
||||||
|
@field_validator('role', mode='before')
|
||||||
|
@classmethod
|
||||||
|
def get_role_name(cls, value):
|
||||||
|
if isinstance(value, EquipmentRole):
|
||||||
|
value = value.name
|
||||||
|
return value
|
||||||
|
|
||||||
@field_validator('processes', mode='before')
|
@field_validator('processes', mode='before')
|
||||||
@classmethod
|
@classmethod
|
||||||
def make_empty_list(cls, value):
|
def make_empty_list(cls, value):
|
||||||
@@ -786,7 +801,7 @@ class PydSubmission(BaseModel, extra='allow'):
|
|||||||
"""
|
"""
|
||||||
report = Report()
|
report = Report()
|
||||||
dicto = self.improved_dict()
|
dicto = self.improved_dict()
|
||||||
logger.warning(f"\n\nQuery or create: {self.submission_type['value']}, {self.rsl_plate_num['value']}")
|
# logger.warning(f"\n\nQuery or create: {self.submission_type['value']}, {self.rsl_plate_num['value']}")
|
||||||
instance, result = BasicSubmission.query_or_create(submission_type=self.submission_type['value'],
|
instance, result = BasicSubmission.query_or_create(submission_type=self.submission_type['value'],
|
||||||
rsl_plate_num=self.rsl_plate_num['value'])
|
rsl_plate_num=self.rsl_plate_num['value'])
|
||||||
logger.debug(f"Result of query or create: {instance}")
|
logger.debug(f"Result of query or create: {instance}")
|
||||||
@@ -807,17 +822,15 @@ class PydSubmission(BaseModel, extra='allow'):
|
|||||||
# logger.debug(f"Setting {key} to {value}")
|
# logger.debug(f"Setting {key} to {value}")
|
||||||
match key:
|
match key:
|
||||||
case "reagents":
|
case "reagents":
|
||||||
# if report.results[0].code == 1:
|
|
||||||
# instance.submission_reagent_associations = []
|
|
||||||
# logger.debug(f"Looking through {self.reagents}")
|
|
||||||
for reagent in self.reagents:
|
for reagent in self.reagents:
|
||||||
reagent, assoc, _ = reagent.toSQL(submission=instance)
|
logger.debug(f"Checking reagent {reagent.lot}")
|
||||||
|
reagent, _ = reagent.toSQL(submission=instance)
|
||||||
# logger.debug(f"Association: {assoc}")
|
# logger.debug(f"Association: {assoc}")
|
||||||
if assoc is not None: # and assoc not in instance.submission_reagent_associations:
|
# if assoc is not None: # and assoc not in instance.submission_reagent_associations:
|
||||||
if assoc not in instance.submission_reagent_associations:
|
# if assoc not in instance.submission_reagent_associations:
|
||||||
instance.submission_reagent_associations.append(assoc)
|
# instance.submission_reagent_associations.append(assoc)
|
||||||
else:
|
# else:
|
||||||
logger.warning(f"Reagent association {assoc} is already present in {instance}")
|
# logger.warning(f"Reagent association {assoc} is already present in {instance.submission_reagent_associations}")
|
||||||
case "samples":
|
case "samples":
|
||||||
for sample in self.samples:
|
for sample in self.samples:
|
||||||
sample, associations, _ = sample.toSQL(submission=instance)
|
sample, associations, _ = sample.toSQL(submission=instance)
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ class SubmissionFormContainer(QWidget):
|
|||||||
# NOTE: create reagent object
|
# NOTE: create reagent object
|
||||||
reagent = PydReagent(ctx=self.app.ctx, **info, missing=False)
|
reagent = PydReagent(ctx=self.app.ctx, **info, missing=False)
|
||||||
# NOTE: send reagent to db
|
# NOTE: send reagent to db
|
||||||
sqlobj, assoc, result = reagent.toSQL()
|
sqlobj, result = reagent.toSQL()
|
||||||
sqlobj.save()
|
sqlobj.save()
|
||||||
report.add_result(result)
|
report.add_result(result)
|
||||||
# logger.debug(f"Reagent: {reagent}, Report: {report}")
|
# logger.debug(f"Reagent: {reagent}, Report: {report}")
|
||||||
@@ -334,6 +334,10 @@ class SubmissionFormWidget(QWidget):
|
|||||||
query = [widget for widget in query if widget.objectName() == object_name]
|
query = [widget for widget in query if widget.objectName() == object_name]
|
||||||
return query
|
return query
|
||||||
|
|
||||||
|
# def update_pyd(self):
|
||||||
|
# results = self.parse_form()
|
||||||
|
# logger.debug(pformat(results))
|
||||||
|
|
||||||
@report_result
|
@report_result
|
||||||
def submit_new_sample_function(self, *args) -> Report:
|
def submit_new_sample_function(self, *args) -> Report:
|
||||||
"""
|
"""
|
||||||
@@ -448,8 +452,9 @@ class SubmissionFormWidget(QWidget):
|
|||||||
if field is not None:
|
if field is not None:
|
||||||
info[field] = value
|
info[field] = value
|
||||||
# logger.debug(f"Info: {pformat(info)}")
|
# logger.debug(f"Info: {pformat(info)}")
|
||||||
# logger.debug(f"Reagents going into pyd: {pformat(reagents)}")
|
logger.debug(f"Reagents going into pyd: {pformat(reagents)}")
|
||||||
self.pyd.reagents = reagents
|
self.pyd.reagents = reagents
|
||||||
|
logger.debug(f"Reagents after insertion in pyd: {pformat(self.pyd.reagents)}")
|
||||||
# logger.debug(f"Attrs not in info: {[k for k, v in self.__dict__.items() if k not in info.keys()]}")
|
# logger.debug(f"Attrs not in info: {[k for k, v in self.__dict__.items() if k not in info.keys()]}")
|
||||||
for item in self.recover:
|
for item in self.recover:
|
||||||
# logger.debug(f"Attempting to recover: {item}")
|
# logger.debug(f"Attempting to recover: {item}")
|
||||||
|
|||||||
@@ -758,7 +758,7 @@ def setup_lookup(func):
|
|||||||
raise ValueError("Could not sanitize dictionary in query. Make sure you parse it first.")
|
raise ValueError("Could not sanitize dictionary in query. Make sure you parse it first.")
|
||||||
elif v is not None:
|
elif v is not None:
|
||||||
sanitized_kwargs[k] = v
|
sanitized_kwargs[k] = v
|
||||||
logger.debug(f"sanitized kwargs: {sanitized_kwargs}")
|
# logger.debug(f"sanitized kwargs: {sanitized_kwargs}")
|
||||||
return func(*args, **sanitized_kwargs)
|
return func(*args, **sanitized_kwargs)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|||||||
Reference in New Issue
Block a user