Renaming ReagentType to ReagentRole

This commit is contained in:
lwark
2024-06-03 13:32:04 -05:00
parent 1a716984a7
commit cdcce80898
10 changed files with 182 additions and 140 deletions

View File

@@ -56,9 +56,9 @@ version_path_separator = os # Use os.pathsep. Default configuration used for ne
# output_encoding = utf-8 # output_encoding = utf-8
; sqlalchemy.url = sqlite:///L:\Robotics Laboratory Support\Submissions\submissions.db ; sqlalchemy.url = sqlite:///L:\Robotics Laboratory Support\Submissions\submissions.db
; sqlalchemy.url = sqlite:///C:\Users\lwark\Documents\Archives\Submissions_app_backups\DB_backups\submissions-demo.db sqlalchemy.url = sqlite:///C:\Users\lwark\Documents\Archives\Submissions_app_backups\DB_backups\submissions-demo.db
;sqlalchemy.url = sqlite:///C:\Users\lwark\Documents\Archives\Submissions_app_backups\DB_backups\submissions-prototypes.db ;sqlalchemy.url = sqlite:///C:\Users\lwark\Documents\Archives\Submissions_app_backups\DB_backups\submissions-prototypes.db
sqlalchemy.url = sqlite:///C:\Users\lwark\Documents\python\submissions\mytests\test_assets\submissions-test.db ; sqlalchemy.url = sqlite:///C:\Users\lwark\Documents\python\submissions\mytests\test_assets\submissions-test.db
[post_write_hooks] [post_write_hooks]

View File

@@ -19,11 +19,11 @@ from io import BytesIO
logger = logging.getLogger(f'submissions.{__name__}') logger = logging.getLogger(f'submissions.{__name__}')
# logger.debug("Table for ReagentType/Reagent relations") # logger.debug("Table for ReagentType/Reagent relations")
reagenttypes_reagents = Table( reagentroles_reagents = Table(
"_reagenttypes_reagents", "_reagentroles_reagents",
Base.metadata, Base.metadata,
Column("reagent_id", INTEGER, ForeignKey("_reagent.id")), Column("reagent_id", INTEGER, ForeignKey("_reagent.id")),
Column("reagenttype_id", INTEGER, ForeignKey("_reagenttype.id")), Column("reagentrole_id", INTEGER, ForeignKey("_reagentrole.id")),
extend_existing=True extend_existing=True
) )
@@ -84,16 +84,16 @@ class KitType(BaseClass):
processes = relationship("Process", back_populates="kit_types", processes = relationship("Process", back_populates="kit_types",
secondary=kittypes_processes) #: equipment processes used by this kit secondary=kittypes_processes) #: equipment processes used by this kit
kit_reagenttype_associations = relationship( kit_reagentrole_associations = relationship(
"KitTypeReagentTypeAssociation", "KitTypeReagentRoleAssociation",
back_populates="kit_type", back_populates="kit_type",
cascade="all, delete-orphan", cascade="all, delete-orphan",
) )
# creator function: https://stackoverflow.com/questions/11091491/keyerror-when-adding-objects-to-sqlalchemy-association-object/11116291#11116291 # 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", reagent_roles = association_proxy("kit_reagentrole_associations", "reagent_role",
creator=lambda RT: KitTypeReagentTypeAssociation( creator=lambda RT: KitTypeReagentRoleAssociation(
reagent_type=RT)) #: Association proxy to KitTypeReagentTypeAssociation reagent_role=RT)) #: Association proxy to KitTypeReagentRoleAssociation
kit_submissiontype_associations = relationship( kit_submissiontype_associations = relationship(
"SubmissionTypeKitTypeAssociation", "SubmissionTypeKitTypeAssociation",
@@ -111,7 +111,7 @@ class KitType(BaseClass):
""" """
return f"<KitType({self.name})>" return f"<KitType({self.name})>"
def get_reagents(self, required: bool = False, submission_type: str | SubmissionType | None = None) -> List[ReagentType]: def get_reagents(self, required: bool = False, submission_type: str | SubmissionType | None = None) -> List[ReagentRole]:
""" """
Return ReagentTypes linked to kit through KitTypeReagentTypeAssociation. Return ReagentTypes linked to kit through KitTypeReagentTypeAssociation.
@@ -120,25 +120,25 @@ class KitType(BaseClass):
submission_type (str | Submissiontype | None, optional): Submission type to narrow results. Defaults to None. submission_type (str | Submissiontype | None, optional): Submission type to narrow results. Defaults to None.
Returns: Returns:
List[ReagentType]: List of reagents linked to this kit. List[ReagentRole]: List of reagents linked to this kit.
""" """
match submission_type: match submission_type:
case SubmissionType(): case SubmissionType():
# logger.debug(f"Getting reagents by SubmissionType {submission_type}") # logger.debug(f"Getting reagents by SubmissionType {submission_type}")
relevant_associations = [item for item in self.kit_reagenttype_associations if relevant_associations = [item for item in self.kit_reagentrole_associations if
item.submission_type == submission_type] item.submission_type == submission_type]
case str(): case str():
# logger.debug(f"Getting reagents by str {submission_type}") # logger.debug(f"Getting reagents by str {submission_type}")
relevant_associations = [item for item in self.kit_reagenttype_associations if relevant_associations = [item for item in self.kit_reagentrole_associations if
item.submission_type.name == submission_type] item.submission_type.name == submission_type]
case _: case _:
# logger.debug(f"Getting reagents") # logger.debug(f"Getting reagents")
relevant_associations = [item for item in self.kit_reagenttype_associations] relevant_associations = [item for item in self.kit_reagentrole_associations]
if required: if required:
# logger.debug(f"Filtering by required.") # logger.debug(f"Filtering by required.")
return [item.reagent_type for item in relevant_associations if item.required == 1] return [item.reagent_role for item in relevant_associations if item.required == 1]
else: else:
return [item.reagent_type for item in relevant_associations] return [item.reagent_role for item in relevant_associations]
# TODO: Move to BasicSubmission? # TODO: Move to BasicSubmission?
def construct_xl_map_for_use(self, submission_type: str | SubmissionType) -> dict: def construct_xl_map_for_use(self, submission_type: str | SubmissionType) -> dict:
@@ -156,17 +156,17 @@ class KitType(BaseClass):
match submission_type: match submission_type:
case str(): case str():
# logger.debug(f"Constructing xl map with str {submission_type}") # logger.debug(f"Constructing xl map with str {submission_type}")
assocs = [item for item in self.kit_reagenttype_associations if assocs = [item for item in self.kit_reagentrole_associations if
item.submission_type.name == submission_type] item.submission_type.name == submission_type]
case SubmissionType(): case SubmissionType():
# logger.debug(f"Constructing xl map with SubmissionType {submission_type}") # logger.debug(f"Constructing xl map with SubmissionType {submission_type}")
assocs = [item for item in self.kit_reagenttype_associations if item.submission_type == submission_type] assocs = [item for item in self.kit_reagentrole_associations if item.submission_type == submission_type]
case _: case _:
raise ValueError(f"Wrong variable type: {type(submission_type)} used!") raise ValueError(f"Wrong variable type: {type(submission_type)} used!")
# logger.debug("Get all KitTypeReagentTypeAssociation for SubmissionType") # logger.debug("Get all KitTypeReagentTypeAssociation for SubmissionType")
for assoc in assocs: for assoc in assocs:
try: try:
info_map[assoc.reagent_type.name] = assoc.uses info_map[assoc.reagent_role.name] = assoc.uses
except TypeError: except TypeError:
continue continue
return info_map return info_map
@@ -226,34 +226,34 @@ class KitType(BaseClass):
super().save() super().save()
class ReagentType(BaseClass): class ReagentRole(BaseClass):
""" """
Base of reagent type abstract Base of reagent type abstract
""" """
id = Column(INTEGER, primary_key=True) #: primary key id = Column(INTEGER, primary_key=True) #: primary key
name = Column(String(64)) #: name of reagent type name = Column(String(64)) #: name of reagent type
instances = relationship("Reagent", back_populates="type", instances = relationship("Reagent", back_populates="role",
secondary=reagenttypes_reagents) #: concrete instances of this reagent type secondary=reagentroles_reagents) #: concrete instances of this reagent type
eol_ext = Column(Interval()) #: extension of life interval eol_ext = Column(Interval()) #: extension of life interval
reagenttype_kit_associations = relationship( reagentrole_kit_associations = relationship(
"KitTypeReagentTypeAssociation", "KitTypeReagentRoleAssociation",
back_populates="reagent_type", back_populates="reagent_role",
cascade="all, delete-orphan", cascade="all, delete-orphan",
) #: Relation to KitTypeReagentTypeAssociation ) #: Relation to KitTypeReagentTypeAssociation
# creator function: https://stackoverflow.com/questions/11091491/keyerror-when-adding-objects-to-sqlalchemy-association-object/11116291#11116291 # 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", kit_types = association_proxy("reagentrole_kit_associations", "kit_type",
creator=lambda kit: KitTypeReagentTypeAssociation( creator=lambda kit: KitTypeReagentRoleAssociation(
kit_type=kit)) #: Association proxy to KitTypeReagentTypeAssociation kit_type=kit)) #: Association proxy to KitTypeReagentRoleAssociation
def __repr__(self) -> str: def __repr__(self) -> str:
""" """
Returns: Returns:
str: Representation of object str: Representation of object
""" """
return f"<ReagentType({self.name})>" return f"<ReagentRole({self.name})>"
@classmethod @classmethod
@setup_lookup @setup_lookup
@@ -262,7 +262,7 @@ class ReagentType(BaseClass):
kit_type: KitType | str | None = None, kit_type: KitType | str | None = None,
reagent: Reagent | str | None = None, reagent: Reagent | str | None = None,
limit: int = 0, limit: int = 0,
) -> ReagentType | List[ReagentType]: ) -> ReagentRole | List[ReagentRole]:
""" """
Lookup reagent types in the database. Lookup reagent types in the database.
@@ -276,7 +276,7 @@ class ReagentType(BaseClass):
ValueError: Raised if only kit_type or reagent, not both, given. ValueError: Raised if only kit_type or reagent, not both, given.
Returns: Returns:
ReagentType|List[ReagentType]: ReagentType or list of ReagentTypes matching filter. ReagentRole|List[ReagentRole]: ReagentRole or list of ReagentRoles matching filter.
""" """
query: Query = cls.__database_session__.query(cls) query: Query = cls.__database_session__.query(cls)
if (kit_type is not None and reagent is None) or (reagent is not None and kit_type is None): if (kit_type is not None and reagent is None) or (reagent is not None and kit_type is None):
@@ -296,10 +296,10 @@ class ReagentType(BaseClass):
reagent = Reagent.query(lot_number=reagent) reagent = Reagent.query(lot_number=reagent)
case _: case _:
pass pass
assert reagent.type assert reagent.role
# logger.debug(f"Looking up reagent type for {type(kit_type)} {kit_type} and {type(reagent)} {reagent}") # 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}") # logger.debug(f"Kit reagent types: {kit_type.reagent_types}")
result = list(set(kit_type.reagent_types).intersection(reagent.type)) result = list(set(kit_type.reagent_roles).intersection(reagent.role))
# logger.debug(f"Result: {result}") # logger.debug(f"Result: {result}")
try: try:
return result[0] return result[0]
@@ -322,7 +322,7 @@ class ReagentType(BaseClass):
PydReagent: PydReagent representation of this object. PydReagent: PydReagent representation of this object.
""" """
from backend.validators.pydant import PydReagent from backend.validators.pydant import PydReagent
return PydReagent(lot=None, type=self.name, name=self.name, expiry=date.today()) return PydReagent(lot=None, role=self.name, name=self.name, expiry=date.today())
@check_authorization @check_authorization
def save(self): def save(self):
@@ -335,10 +335,10 @@ class Reagent(BaseClass):
""" """
id = Column(INTEGER, primary_key=True) #: primary key id = Column(INTEGER, primary_key=True) #: primary key
type = relationship("ReagentType", back_populates="instances", role = relationship("ReagentRole", back_populates="instances",
secondary=reagenttypes_reagents) #: joined parent reagent type secondary=reagentroles_reagents) #: joined parent reagent type
type_id = Column(INTEGER, ForeignKey("_reagenttype.id", ondelete='SET NULL', role_id = Column(INTEGER, ForeignKey("_reagentrole.id", ondelete='SET NULL',
name="fk_reagent_type_id")) #: id of parent reagent type name="fk_reagent_role_id")) #: id of parent reagent type
name = Column(String(64)) #: reagent name name = Column(String(64)) #: reagent name
lot = Column(String(64)) #: lot number of reagent lot = Column(String(64)) #: lot number of reagent
expiry = Column(TIMESTAMP) #: expiry date - extended by eol_ext of parent programmatically expiry = Column(TIMESTAMP) #: expiry date - extended by eol_ext of parent programmatically
@@ -356,7 +356,7 @@ class Reagent(BaseClass):
if self.name is not None: if self.name is not None:
return f"<Reagent({self.name}-{self.lot})>" return f"<Reagent({self.name}-{self.lot})>"
else: else:
return f"<Reagent({self.type.name}-{self.lot})>" return f"<Reagent({self.role.name}-{self.lot})>"
def to_sub_dict(self, extraction_kit: KitType = None) -> dict: def to_sub_dict(self, extraction_kit: KitType = None) -> dict:
""" """
@@ -371,12 +371,12 @@ class Reagent(BaseClass):
if extraction_kit is not None: if extraction_kit is not None:
# NOTE: Get the intersection of this reagent's ReagentType and all ReagentTypes in KitType # NOTE: Get the intersection of this reagent's ReagentType and all ReagentTypes in KitType
try: try:
reagent_role = list(set(self.type).intersection(extraction_kit.reagent_types))[0] reagent_role = list(set(self.role).intersection(extraction_kit.reagent_roles))[0]
# NOTE: Most will be able to fall back to first ReagentType in itself because most will only have 1. # NOTE: Most will be able to fall back to first ReagentType in itself because most will only have 1.
except: except:
reagent_role = self.type[0] reagent_role = self.role[0]
else: else:
reagent_role = self.type[0] reagent_role = self.role[0]
try: try:
rtype = reagent_role.name.replace("_", " ") rtype = reagent_role.name.replace("_", " ")
except AttributeError: except AttributeError:
@@ -393,7 +393,7 @@ class Reagent(BaseClass):
place_holder = place_holder.strftime("%Y-%m-%d") place_holder = place_holder.strftime("%Y-%m-%d")
return dict( return dict(
name=self.name, name=self.name,
type=rtype, role=rtype,
lot=self.lot, lot=self.lot,
expiry=place_holder, expiry=place_holder,
missing=False missing=False
@@ -411,10 +411,10 @@ class Reagent(BaseClass):
""" """
report = Report() report = Report()
# logger.debug(f"Attempting update of last used 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) rt = ReagentRole.query(kit_type=kit, reagent=self, limit=1)
if rt is not None: 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) assoc = KitTypeReagentRoleAssociation.query(kit_type=kit, reagent_role=rt)
if assoc is not None: if assoc is not None:
if assoc.last_used != self.lot: 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}")
@@ -429,7 +429,7 @@ class Reagent(BaseClass):
@setup_lookup @setup_lookup
def query(cls, def query(cls,
id: int | None = None, id: int | None = None,
reagent_type: str | ReagentType | None = None, reagent_role: str | ReagentRole | None = None,
lot_number: str | None = None, lot_number: str | None = None,
name: str | None = None, name: str | None = None,
limit: int = 0 limit: int = 0
@@ -438,7 +438,7 @@ class Reagent(BaseClass):
Lookup a list of reagents from the database. Lookup a list of reagents from the database.
Args: Args:
reagent_type (str | models.ReagentType | None, optional): Reagent type. Defaults to None. reagent_role (str | models.ReagentType | None, optional): Reagent type. Defaults to None.
lot_number (str | None, optional): Reagent lot number. Defaults to None. lot_number (str | None, optional): Reagent lot number. Defaults to None.
name (str | None, optional): Reagent name. Defaults to None. name (str | None, optional): Reagent name. Defaults to None.
limit (int, optional): limit of results returned. Defaults to 0. limit (int, optional): limit of results returned. Defaults to 0.
@@ -453,13 +453,13 @@ class Reagent(BaseClass):
limit = 1 limit = 1
case _: case _:
pass pass
match reagent_type: match reagent_role:
case str(): case str():
# logger.debug(f"Looking up reagents by reagent type str: {reagent_type}") # logger.debug(f"Looking up reagents by reagent type str: {reagent_type}")
query = query.join(cls.type).filter(ReagentType.name == reagent_type) query = query.join(cls.role).filter(ReagentRole.name == reagent_role)
case ReagentType(): case ReagentRole():
# logger.debug(f"Looking up reagents by reagent type ReagentType: {reagent_type}") # logger.debug(f"Looking up reagents by reagent type ReagentType: {reagent_type}")
query = query.filter(cls.type.contains(reagent_type)) query = query.filter(cls.role.contains(reagent_role))
case _: case _:
pass pass
match name: match name:
@@ -582,7 +582,7 @@ class SubmissionType(BaseClass):
"equipment_role") #: Proxy of equipmentrole associations "equipment_role") #: Proxy of equipmentrole associations
submissiontype_kit_rt_associations = relationship( submissiontype_kit_rt_associations = relationship(
"KitTypeReagentTypeAssociation", "KitTypeReagentRoleAssociation",
back_populates="submission_type", back_populates="submission_type",
cascade="all, delete-orphan" cascade="all, delete-orphan"
) #: triple association of KitTypes, ReagentTypes, SubmissionTypes ) #: triple association of KitTypes, ReagentTypes, SubmissionTypes
@@ -617,7 +617,7 @@ class SubmissionType(BaseClass):
if isinstance(filepath, str): if isinstance(filepath, str):
filepath = Path(filepath) filepath = Path(filepath)
try: try:
xl = ExcelFile(filepath) ExcelFile(filepath)
except ValueError: except ValueError:
raise ValueError(f"File {filepath} is not of appropriate type.") raise ValueError(f"File {filepath} is not of appropriate type.")
with open(filepath, "rb") as f: with open(filepath, "rb") as f:
@@ -836,13 +836,13 @@ class SubmissionTypeKitTypeAssociation(BaseClass):
return cls.execute_query(query=query, limit=limit) return cls.execute_query(query=query, limit=limit)
class KitTypeReagentTypeAssociation(BaseClass): class KitTypeReagentRoleAssociation(BaseClass):
""" """
table containing reagenttype/kittype associations table containing reagenttype/kittype associations
DOC: https://docs.sqlalchemy.org/en/14/orm/extensions/associationproxy.html DOC: https://docs.sqlalchemy.org/en/14/orm/extensions/associationproxy.html
""" """
reagent_types_id = Column(INTEGER, ForeignKey("_reagenttype.id"), reagent_roles_id = Column(INTEGER, ForeignKey("_reagentrole.id"),
primary_key=True) #: id of associated reagent type primary_key=True) #: id of associated reagent type
kits_id = Column(INTEGER, ForeignKey("_kittype.id"), primary_key=True) #: id of associated reagent type kits_id = Column(INTEGER, ForeignKey("_kittype.id"), primary_key=True) #: id of associated reagent type
submission_type_id = Column(INTEGER, ForeignKey("_submissiontype.id"), primary_key=True) submission_type_id = Column(INTEGER, ForeignKey("_submissiontype.id"), primary_key=True)
@@ -851,23 +851,23 @@ class KitTypeReagentTypeAssociation(BaseClass):
last_used = Column(String(32)) #: last used lot number of this type of reagent last_used = Column(String(32)) #: last used lot number of this type of reagent
kit_type = relationship(KitType, kit_type = relationship(KitType,
back_populates="kit_reagenttype_associations") #: relationship to associated KitType back_populates="kit_reagentrole_associations") #: relationship to associated KitType
# reference to the "ReagentType" object # reference to the "ReagentType" object
reagent_type = relationship(ReagentType, reagent_role = relationship(ReagentRole,
back_populates="reagenttype_kit_associations") #: relationship to associated ReagentType back_populates="reagentrole_kit_associations") #: relationship to associated ReagentType
submission_type = relationship(SubmissionType, submission_type = relationship(SubmissionType,
back_populates="submissiontype_kit_rt_associations") #: relationship to associated SubmissionType back_populates="submissiontype_kit_rt_associations") #: relationship to associated SubmissionType
def __init__(self, kit_type=None, reagent_type=None, uses=None, required=1): def __init__(self, kit_type=None, reagent_role=None, uses=None, required=1):
self.kit_type = kit_type self.kit_type = kit_type
self.reagent_type = reagent_type self.reagent_role = reagent_role
self.uses = uses self.uses = uses
self.required = required self.required = required
def __repr__(self) -> str: def __repr__(self) -> str:
return f"<KitTypeReagentTypeAssociation({self.kit_type} & {self.reagent_type})>" return f"<KitTypeReagentRoleAssociation({self.kit_type} & {self.reagent_role})>"
@validates('required') @validates('required')
def validate_age(self, key, value): def validate_age(self, key, value):
@@ -888,8 +888,8 @@ class KitTypeReagentTypeAssociation(BaseClass):
raise ValueError(f'Invalid required value {value}. Must be 0 or 1.') raise ValueError(f'Invalid required value {value}. Must be 0 or 1.')
return value return value
@validates('reagenttype') @validates('reagentrole')
def validate_reagenttype(self, key, value): def validate_reagentrole(self, key, value):
""" """
Ensures reagenttype is an actual ReagentType Ensures reagenttype is an actual ReagentType
@@ -903,23 +903,23 @@ class KitTypeReagentTypeAssociation(BaseClass):
Returns: Returns:
_type_: ReagentType _type_: ReagentType
""" """
if not isinstance(value, ReagentType): if not isinstance(value, ReagentRole):
raise ValueError(f'{value} is not a reagenttype') raise ValueError(f'{value} is not a reagentrole')
return value return value
@classmethod @classmethod
@setup_lookup @setup_lookup
def query(cls, def query(cls,
kit_type: KitType | str | None = None, kit_type: KitType | str | None = None,
reagent_type: ReagentType | str | None = None, reagent_role: ReagentRole | str | None = None,
limit: int = 0 limit: int = 0
) -> KitTypeReagentTypeAssociation | List[KitTypeReagentTypeAssociation]: ) -> KitTypeReagentRoleAssociation | List[KitTypeReagentRoleAssociation]:
""" """
Lookup junction of ReagentType and KitType Lookup junction of ReagentType and KitType
Args: Args:
kit_type (models.KitType | str | None): KitType of interest. kit_type (models.KitType | str | None): KitType of interest.
reagent_type (models.ReagentType | str | None): ReagentType of interest. reagent_role (models.ReagentType | str | None): ReagentType of interest.
limit (int, optional): Maximum number of results to return (0 = all). Defaults to 0. limit (int, optional): Maximum number of results to return (0 = all). Defaults to 0.
Returns: Returns:
@@ -935,16 +935,16 @@ class KitTypeReagentTypeAssociation(BaseClass):
query = query.join(KitType).filter(KitType.name == kit_type) query = query.join(KitType).filter(KitType.name == kit_type)
case _: case _:
pass pass
match reagent_type: match reagent_role:
case ReagentType(): case ReagentRole():
# logger.debug(f"Lookup KitTypeReagentTypeAssociation by reagent_type ReagentType {reagent_type}") # logger.debug(f"Lookup KitTypeReagentTypeAssociation by reagent_type ReagentType {reagent_type}")
query = query.filter(cls.reagent_type == reagent_type) query = query.filter(cls.reagent_role == reagent_role)
case str(): case str():
# logger.debug(f"Lookup KitTypeReagentTypeAssociation by reagent_type ReagentType {reagent_type}") # logger.debug(f"Lookup KitTypeReagentTypeAssociation by reagent_type ReagentType {reagent_type}")
query = query.join(ReagentType).filter(ReagentType.name == reagent_type) query = query.join(ReagentRole).filter(ReagentRole.name == reagent_role)
case _: case _:
pass pass
if kit_type != None and reagent_type != None: if kit_type != None and reagent_role != None:
limit = 1 limit = 1
return cls.execute_query(query=query, limit=limit) return cls.execute_query(query=query, limit=limit)
@@ -1478,4 +1478,45 @@ class Process(BaseClass):
return cls.execute_query(query=query, limit=limit) return cls.execute_query(query=query, limit=limit)
# class Tips(Reagent): # class TipRole(BaseClass):
#
# id = Column(INTEGER, primary_key=True) #: primary key
# name = Column(String(64)) #: name of reagent type
# instances = relationship("Tips", back_populates="role",
# secondary=reagenttypes_reagents) #: concrete instances of this reagent type
#
# tiprole_kit_associations = relationship(
# "KitTypeTipRoleAssociation",
# back_populates="tip_role",
# cascade="all, delete-orphan",
# ) #: Relation to KitTypeReagentTypeAssociation
#
# # creator function: https://stackoverflow.com/questions/11091491/keyerror-when-adding-objects-to-sqlalchemy-association-object/11116291#11116291
# kit_types = association_proxy("tiprole_kit_associations", "kit_type",
# creator=lambda kit: KitTypeReagentTypeAssociation(
# kit_type=kit)) #: Association proxy to KitTypeReagentTypeAssociation
#
# def __repr__(self):
# return f"<TipRole({self.name})>"
#
# class Tips(BaseClass):
#
# id = Column(INTEGER, primary_key=True) #: primary key
# role = relationship("TipRole", back_populates="instances",
# secondary=reagenttypes_reagents) #: joined parent reagent type
# role_id = Column(INTEGER, ForeignKey("_tiprole.id", ondelete='SET NULL',
# name="fk_tip_role_id")) #: id of parent reagent type
# name = Column(String(64)) #: tip common name
# lot = Column(String(64)) #: lot number of tips
#
# tips_submission_associations = relationship(
# "SubmissionTipsAssociation",
# back_populates="tips",
# cascade="all, delete-orphan",
# ) #: Relation to SubmissionSampleAssociation
#
# submissions = association_proxy("tips_submission_associations",
# "submission") #: Association proxy to SubmissionSampleAssociation.samples
#
# def __repr__(self):
# return f"<Tips({self.name})>"

View File

@@ -266,9 +266,9 @@ class BasicSubmission(BaseClass):
for k in self.extraction_kit.construct_xl_map_for_use(self.submission_type): for k in self.extraction_kit.construct_xl_map_for_use(self.submission_type):
if k == 'info': if k == 'info':
continue continue
if not any([item['type'] == k for item in reagents]): if not any([item['role'] == k for item in reagents]):
reagents.append( reagents.append(
dict(type=k, name="Not Applicable", lot="NA", expiry=date(year=1970, month=1, day=1), dict(role=k, name="Not Applicable", lot="NA", expiry=date(year=1970, month=1, day=1),
missing=True)) missing=True))
except Exception as e: except Exception as e:
logger.error(f"We got an error retrieving reagents: {e}") logger.error(f"We got an error retrieving reagents: {e}")

View File

@@ -139,6 +139,7 @@ class SheetParser(object):
# logger.debug(f"Submission dictionary coming into 'to_pydantic':\n{pformat(self.sub)}") # logger.debug(f"Submission dictionary coming into 'to_pydantic':\n{pformat(self.sub)}")
pyd_dict = copy(self.sub) pyd_dict = copy(self.sub)
pyd_dict['samples'] = [PydSample(**sample) for sample in self.sub['samples']] pyd_dict['samples'] = [PydSample(**sample) for sample in self.sub['samples']]
logger.debug(f"Reagents: {pformat(self.sub['reagents'])}")
pyd_dict['reagents'] = [PydReagent(**reagent) for reagent in self.sub['reagents']] pyd_dict['reagents'] = [PydReagent(**reagent) for reagent in self.sub['reagents']]
# logger.debug(f"Equipment: {self.sub['equipment']}") # logger.debug(f"Equipment: {self.sub['equipment']}")
try: try:
@@ -307,7 +308,7 @@ class ReagentParser(object):
comment = "" comment = ""
except (KeyError, IndexError): except (KeyError, IndexError):
listo.append( listo.append(
PydReagent(type=item.strip(), lot=None, expiry=None, name=None, comment="", missing=True)) PydReagent(role=item.strip(), lot=None, expiry=None, name=None, comment="", missing=True))
continue continue
# NOTE: If the cell is blank tell the PydReagent # NOTE: If the cell is blank tell the PydReagent
if check_not_nan(lot): if check_not_nan(lot):
@@ -324,7 +325,7 @@ class ReagentParser(object):
logger.warning(f"name is not a string.") logger.warning(f"name is not a string.")
check = True check = True
if check: if check:
listo.append(dict(type=item.strip(), lot=lot, expiry=expiry, name=name, comment=comment, listo.append(dict(role=item.strip(), lot=lot, expiry=expiry, name=name, comment=comment,
missing=missing)) missing=missing))
return listo return listo

View File

@@ -178,5 +178,5 @@ class RSLNamer(object):
return template.render(**kwargs) return template.render(**kwargs)
from .pydant import PydSubmission, PydKit, PydContact, PydOrganization, PydSample, PydReagent, PydReagentType, \ from .pydant import PydSubmission, PydKit, PydContact, PydOrganization, PydSample, PydReagent, PydReagentRole, \
PydEquipment, PydEquipmentRole PydEquipment, PydEquipmentRole

View File

@@ -25,7 +25,7 @@ logger = logging.getLogger(f"submissions.{__name__}")
class PydReagent(BaseModel): class PydReagent(BaseModel):
lot: str | None lot: str | None
type: str | None role: str | None
expiry: date | Literal['NA'] | None expiry: date | Literal['NA'] | None
name: str | None name: str | None
missing: bool = Field(default=True) missing: bool = Field(default=True)
@@ -38,7 +38,7 @@ class PydReagent(BaseModel):
return "" return ""
return value return value
@field_validator("type", mode='before') @field_validator("role", mode='before')
@classmethod @classmethod
def remove_undesired_types(cls, value): def remove_undesired_types(cls, value):
match value: match value:
@@ -47,7 +47,7 @@ class PydReagent(BaseModel):
case _: case _:
return value return value
@field_validator("type") @field_validator("role")
@classmethod @classmethod
def rescue_type_with_lookup(cls, value, values): def rescue_type_with_lookup(cls, value, values):
if value == None and values.data['lot'] != None: if value == None and values.data['lot'] != None:
@@ -147,10 +147,10 @@ class PydReagent(BaseModel):
match key: match key:
case "lot": case "lot":
reagent.lot = value.upper() reagent.lot = value.upper()
case "type": case "role":
reagent_type = ReagentType.query(name=value) reagent_role = ReagentRole.query(name=value)
if reagent_type != None: if reagent_role is not None:
reagent.type.append(reagent_type) reagent.role.append(reagent_role)
case "comment": case "comment":
continue continue
case "expiry": case "expiry":
@@ -792,11 +792,11 @@ class PydSubmission(BaseModel, extra='allow'):
# logger.debug(f"Kit reagents: {ext_kit_rtypes}") # logger.debug(f"Kit reagents: {ext_kit_rtypes}")
# logger.debug(f"Submission reagents: {self.reagents}") # logger.debug(f"Submission reagents: {self.reagents}")
# Exclude any reagenttype found in this pyd not expected in kit. # Exclude any reagenttype found in this pyd not expected in kit.
expected_check = [item.type for item in ext_kit_rtypes] expected_check = [item.role for item in ext_kit_rtypes]
output_reagents = [rt for rt in self.reagents if rt.type in expected_check] output_reagents = [rt for rt in self.reagents if rt.role in expected_check]
# logger.debug(f"Already have these reagent types: {output_reagents}") # logger.debug(f"Already have these reagent types: {output_reagents}")
missing_check = [item.type for item in output_reagents] missing_check = [item.role for item in output_reagents]
missing_reagents = [rt for rt in ext_kit_rtypes if rt.type not in missing_check] missing_reagents = [rt for rt in ext_kit_rtypes if rt.role not in missing_check]
missing_reagents += [rt for rt in output_reagents if rt.missing] missing_reagents += [rt for rt in output_reagents if rt.missing]
output_reagents += [rt for rt in missing_reagents if rt not in output_reagents] output_reagents += [rt for rt in missing_reagents if rt not in output_reagents]
# logger.debug(f"Missing reagents types: {missing_reagents}") # logger.debug(f"Missing reagents types: {missing_reagents}")
@@ -805,7 +805,7 @@ class PydSubmission(BaseModel, extra='allow'):
result = None result = None
else: else:
result = Result( result = Result(
msg=f"The excel sheet you are importing is missing some reagents expected by the kit.\n\nIt looks like you are missing: {[item.type.upper() for item in missing_reagents]}\n\nAlternatively, you may have set the wrong extraction kit.\n\nThe program will populate lists using existing reagents.\n\nPlease make sure you check the lots carefully!", msg=f"The excel sheet you are importing is missing some reagents expected by the kit.\n\nIt looks like you are missing: {[item.role.upper() for item in missing_reagents]}\n\nAlternatively, you may have set the wrong extraction kit.\n\nThe program will populate lists using existing reagents.\n\nPlease make sure you check the lots carefully!",
status="Warning") status="Warning")
report.add_result(result) report.add_result(result)
return output_reagents, report return output_reagents, report
@@ -850,7 +850,7 @@ class PydOrganization(BaseModel):
return instance return instance
class PydReagentType(BaseModel): class PydReagentRole(BaseModel):
name: str name: str
eol_ext: timedelta | int | None eol_ext: timedelta | int | None
uses: dict | None uses: dict | None
@@ -863,33 +863,33 @@ class PydReagentType(BaseModel):
return timedelta(days=value) return timedelta(days=value)
return value return value
def toSQL(self, kit: KitType) -> ReagentType: def toSQL(self, kit: KitType) -> ReagentRole:
""" """
Converts this instance into a backend.db.models.ReagentType instance Converts this instance into a backend.db.models.ReagentType instance
Args: Args:
kit (KitType): KitType joined to the reagenttype kit (KitType): KitType joined to the reagentrole
Returns: Returns:
ReagentType: ReagentType instance ReagentRole: ReagentType instance
""" """
instance: ReagentType = ReagentType.query(name=self.name) instance: ReagentRole = ReagentRole.query(name=self.name)
if instance == None: if instance == None:
instance = ReagentType(name=self.name, eol_ext=self.eol_ext) instance = ReagentRole(name=self.name, eol_ext=self.eol_ext)
# logger.debug(f"This is the reagent type instance: {instance.__dict__}") # logger.debug(f"This is the reagent type instance: {instance.__dict__}")
try: try:
assoc = KitTypeReagentTypeAssociation.query(reagent_type=instance, kit_type=kit) assoc = KitTypeReagentRoleAssociation.query(reagent_role=instance, kit_type=kit)
except StatementError: except StatementError:
assoc = None assoc = None
if assoc == None: if assoc == None:
assoc = KitTypeReagentTypeAssociation(kit_type=kit, reagent_type=instance, uses=self.uses, assoc = KitTypeReagentRoleAssociation(kit_type=kit, reagent_role=instance, uses=self.uses,
required=self.required) required=self.required)
return instance return instance
class PydKit(BaseModel): class PydKit(BaseModel):
name: str name: str
reagent_types: List[PydReagentType] = [] reagent_roles: List[PydReagentRole] = []
def toSQL(self) -> Tuple[KitType, Report]: def toSQL(self) -> Tuple[KitType, Report]:
""" """
@@ -902,7 +902,7 @@ class PydKit(BaseModel):
instance = KitType.query(name=self.name) instance = KitType.query(name=self.name)
if instance == None: if instance == None:
instance = KitType(name=self.name) instance = KitType(name=self.name)
[item.toSQL(instance) for item in self.reagent_types] [item.toSQL(instance) for item in self.reagent_roles]
return instance, report return instance, report

View File

@@ -5,8 +5,8 @@ from PyQt6.QtWidgets import (
QSpinBox, QDateEdit QSpinBox, QDateEdit
) )
from sqlalchemy import FLOAT, INTEGER from sqlalchemy import FLOAT, INTEGER
from backend.db import SubmissionTypeKitTypeAssociation, SubmissionType, ReagentType from backend.db import SubmissionTypeKitTypeAssociation, SubmissionType, ReagentRole
from backend.validators import PydReagentType, PydKit from backend.validators import PydReagentRole, PydKit
import logging import logging
from pprint import pformat from pprint import pformat
from tools import Report from tools import Report
@@ -81,7 +81,7 @@ class KitAdder(QWidget):
""" """
# get bottommost row # get bottommost row
maxrow = self.grid.rowCount() maxrow = self.grid.rowCount()
reg_form = ReagentTypeForm(parent=self) reg_form = ReagentRoleForm(parent=self)
reg_form.setObjectName(f"ReagentForm_{maxrow}") reg_form.setObjectName(f"ReagentForm_{maxrow}")
self.grid.addWidget(reg_form, maxrow,0,1,4) self.grid.addWidget(reg_form, maxrow,0,1,4)
@@ -95,11 +95,11 @@ class KitAdder(QWidget):
info = {k:v for k,v in info.items() if k in [column.name for column in self.columns] + ['kit_name', 'used_for']} info = {k:v for k,v in info.items() if k in [column.name for column in self.columns] + ['kit_name', 'used_for']}
# logger.debug(f"kit info: {pformat(info)}") # logger.debug(f"kit info: {pformat(info)}")
# logger.debug(f"kit reagents: {pformat(reagents)}") # logger.debug(f"kit reagents: {pformat(reagents)}")
info['reagent_types'] = reagents info['reagent_roles'] = reagents
# logger.debug(pformat(info)) # logger.debug(pformat(info))
# send to kit constructor # send to kit constructor
kit = PydKit(name=info['kit_name']) kit = PydKit(name=info['kit_name'])
for reagent in info['reagent_types']: for reagent in info['reagent_roles']:
uses = { uses = {
info['used_for']: info['used_for']:
{'sheet':reagent['sheet'], {'sheet':reagent['sheet'],
@@ -107,7 +107,7 @@ class KitAdder(QWidget):
'lot':reagent['lot'], 'lot':reagent['lot'],
'expiry':reagent['expiry'] 'expiry':reagent['expiry']
}} }}
kit.reagent_types.append(PydReagentType(name=reagent['rtname'], eol_ext=reagent['eol'], uses=uses)) kit.reagent_roles.append(PydReagentRole(name=reagent['rtname'], eol_ext=reagent['eol'], uses=uses))
# logger.debug(f"Output pyd object: {kit.__dict__}") # logger.debug(f"Output pyd object: {kit.__dict__}")
sqlobj, result = kit.toSQL(self.ctx) sqlobj, result = kit.toSQL(self.ctx)
report.add_result(result=result) report.add_result(result=result)
@@ -125,11 +125,11 @@ class KitAdder(QWidget):
# logger.debug(f"Hello from {self.__class__} parser!") # logger.debug(f"Hello from {self.__class__} parser!")
info = {} info = {}
reagents = [] reagents = []
widgets = [widget for widget in self.findChildren(QWidget) if widget.objectName() not in self.ignore and not isinstance(widget.parent(), ReagentTypeForm)] widgets = [widget for widget in self.findChildren(QWidget) if widget.objectName() not in self.ignore and not isinstance(widget.parent(), ReagentRoleForm)]
for widget in widgets: for widget in widgets:
# logger.debug(f"Parsed widget: {widget.objectName()} of type {type(widget)} with parent {widget.parent()}") # logger.debug(f"Parsed widget: {widget.objectName()} of type {type(widget)} with parent {widget.parent()}")
match widget: match widget:
case ReagentTypeForm(): case ReagentRoleForm():
reagents.append(widget.parse_form()) reagents.append(widget.parse_form())
case QLineEdit(): case QLineEdit():
info[widget.objectName()] = widget.text() info[widget.objectName()] = widget.text()
@@ -139,7 +139,7 @@ class KitAdder(QWidget):
info[widget.objectName()] = widget.date().toPyDate() info[widget.objectName()] = widget.date().toPyDate()
return info, reagents return info, reagents
class ReagentTypeForm(QWidget): class ReagentRoleForm(QWidget):
""" """
custom widget to add information about a new reagenttype custom widget to add information about a new reagenttype
""" """
@@ -152,13 +152,13 @@ class ReagentTypeForm(QWidget):
self.reagent_getter = QComboBox() self.reagent_getter = QComboBox()
self.reagent_getter.setObjectName("rtname") self.reagent_getter.setObjectName("rtname")
# lookup all reagent type names from db # lookup all reagent type names from db
lookup = ReagentType.query() lookup = ReagentRole.query()
# logger.debug(f"Looked up ReagentType names: {lookup}") # logger.debug(f"Looked up ReagentType names: {lookup}")
self.reagent_getter.addItems([item.name for item in lookup]) self.reagent_getter.addItems([item.name for item in lookup])
self.reagent_getter.setEditable(True) self.reagent_getter.setEditable(True)
grid.addWidget(self.reagent_getter,0,1) grid.addWidget(self.reagent_getter,0,1)
grid.addWidget(QLabel("Extension of Life (months):"),0,2) grid.addWidget(QLabel("Extension of Life (months):"),0,2)
# widget to get extension of life # NOTE: widget to get extension of life
self.eol = QSpinBox() self.eol = QSpinBox()
self.eol.setObjectName('eol') self.eol.setObjectName('eol')
self.eol.setMinimum(0) self.eol.setMinimum(0)

View File

@@ -23,10 +23,10 @@ class AddReagentForm(QDialog):
""" """
dialog to add gather info about new reagent dialog to add gather info about new reagent
""" """
def __init__(self, reagent_lot:str|None=None, reagent_type:str|None=None, expiry:date|None=None, reagent_name:str|None=None) -> None: def __init__(self, reagent_lot:str|None=None, reagent_role: str | None=None, expiry: date | None=None, reagent_name: str | None=None) -> None:
super().__init__() super().__init__()
if reagent_lot == None: if reagent_lot == None:
reagent_lot = reagent_type reagent_lot = reagent_role
self.setWindowTitle("Add Reagent") self.setWindowTitle("Add Reagent")
@@ -57,15 +57,15 @@ class AddReagentForm(QDialog):
# widget to get reagent type info # widget to get reagent type info
self.type_input = QComboBox() self.type_input = QComboBox()
self.type_input.setObjectName('type') self.type_input.setObjectName('type')
self.type_input.addItems([item.name for item in ReagentType.query()]) self.type_input.addItems([item.name for item in ReagentRole.query()])
# logger.debug(f"Trying to find index of {reagent_type}") # logger.debug(f"Trying to find index of {reagent_type}")
# convert input to user friendly string? # convert input to user friendly string?
try: try:
reagent_type = reagent_type.replace("_", " ").title() reagent_role = reagent_role.replace("_", " ").title()
except AttributeError: except AttributeError:
reagent_type = None reagent_role = None
# set parsed reagent type to top of list # set parsed reagent type to top of list
index = self.type_input.findText(reagent_type, Qt.MatchFlag.MatchEndsWith) index = self.type_input.findText(reagent_role, Qt.MatchFlag.MatchEndsWith)
if index >= 0: if index >= 0:
self.type_input.setCurrentIndex(index) self.type_input.setCurrentIndex(index)
self.layout = QVBoxLayout() self.layout = QVBoxLayout()
@@ -91,7 +91,7 @@ class AddReagentForm(QDialog):
return dict(name=self.name_input.currentText().strip(), return dict(name=self.name_input.currentText().strip(),
lot=self.lot_input.text().strip(), lot=self.lot_input.text().strip(),
expiry=self.exp_input.date().toPyDate(), expiry=self.exp_input.date().toPyDate(),
type=self.type_input.currentText().strip()) role=self.type_input.currentText().strip())
def update_names(self): def update_names(self):
""" """
@@ -99,7 +99,7 @@ class AddReagentForm(QDialog):
""" """
# logger.debug(self.type_input.currentText()) # logger.debug(self.type_input.currentText())
self.name_input.clear() self.name_input.clear()
lookup = Reagent.query(reagent_type=self.type_input.currentText()) lookup = Reagent.query(reagent_role=self.type_input.currentText())
self.name_input.addItems(list(set([item.name for item in lookup]))) self.name_input.addItems(list(set([item.name for item in lookup])))
class ReportDatePicker(QDialog): class ReportDatePicker(QDialog):

View File

@@ -14,7 +14,7 @@ from backend.excel.parser import SheetParser
from backend.validators import PydSubmission, PydReagent from backend.validators import PydSubmission, PydReagent
from backend.db import ( from backend.db import (
KitType, Organization, SubmissionType, Reagent, KitType, Organization, SubmissionType, Reagent,
ReagentType, KitTypeReagentTypeAssociation ReagentRole, KitTypeReagentRoleAssociation
) )
from pprint import pformat from pprint import pformat
from .pop_ups import QuestionAsker, AlertPop from .pop_ups import QuestionAsker, AlertPop
@@ -112,14 +112,14 @@ class SubmissionFormContainer(QWidget):
# logger.debug(f"Outgoing report: {self.report.results}") # logger.debug(f"Outgoing report: {self.report.results}")
# logger.debug(f"All attributes of submission container:\n{pformat(self.__dict__)}") # logger.debug(f"All attributes of submission container:\n{pformat(self.__dict__)}")
def add_reagent(self, reagent_lot: str | None = None, reagent_type: str | None = None, expiry: date | None = None, def add_reagent(self, reagent_lot: str | None = None, reagent_role: str | None = None, expiry: date | None = None,
name: str | None = None): name: str | None = None):
""" """
Action to create new reagent in DB. Action to create new reagent in DB.
Args: Args:
reagent_lot (str | None, optional): Parsed reagent from import form. Defaults to None. reagent_lot (str | None, optional): Parsed reagent from import form. Defaults to None.
reagent_type (str | None, optional): Parsed reagent type from import form. Defaults to None. reagent_role (str | None, optional): Parsed reagent type from import form. Defaults to None.
expiry (date | None, optional): Parsed reagent expiry data. Defaults to None. expiry (date | None, optional): Parsed reagent expiry data. Defaults to None.
name (str | None, optional): Parsed reagent name. Defaults to None. name (str | None, optional): Parsed reagent name. Defaults to None.
@@ -130,7 +130,7 @@ class SubmissionFormContainer(QWidget):
if isinstance(reagent_lot, bool): if isinstance(reagent_lot, bool):
reagent_lot = "" reagent_lot = ""
# NOTE: create form # NOTE: create form
dlg = AddReagentForm(reagent_lot=reagent_lot, reagent_type=reagent_type, expiry=expiry, reagent_name=name) dlg = AddReagentForm(reagent_lot=reagent_lot, reagent_role=reagent_role, expiry=expiry, reagent_name=name)
if dlg.exec(): if dlg.exec():
# extract form info # extract form info
info = dlg.parse_form() info = dlg.parse_form()
@@ -581,13 +581,13 @@ class SubmissionFormWidget(QWidget):
""" """
lot = self.lot.currentText() lot = self.lot.currentText()
# logger.debug(f"Using this lot for the reagent {self.reagent}: {lot}") # logger.debug(f"Using this lot for the reagent {self.reagent}: {lot}")
wanted_reagent = Reagent.query(lot_number=lot, reagent_type=self.reagent.type) wanted_reagent = Reagent.query(lot_number=lot, reagent_role=self.reagent.role)
# NOTE: if reagent doesn't exist in database, offer to add it (uses App.add_reagent) # NOTE: if reagent doesn't exist in database, offer to add it (uses App.add_reagent)
if wanted_reagent == None: if wanted_reagent == None:
dlg = QuestionAsker(title=f"Add {lot}?", dlg = QuestionAsker(title=f"Add {lot}?",
message=f"Couldn't find reagent type {self.reagent.type}: {lot} in the database.\n\nWould you like to add it?") message=f"Couldn't find reagent type {self.reagent.type}: {lot} in the database.\n\nWould you like to add it?")
if dlg.exec(): if dlg.exec():
wanted_reagent = self.parent().parent().add_reagent(reagent_lot=lot, reagent_type=self.reagent.type, wanted_reagent = self.parent().parent().add_reagent(reagent_lot=lot, reagent_role=self.reagent.role,
expiry=self.reagent.expiry, expiry=self.reagent.expiry,
name=self.reagent.name) name=self.reagent.name)
return wanted_reagent, None return wanted_reagent, None
@@ -598,10 +598,10 @@ class SubmissionFormWidget(QWidget):
else: else:
# NOTE: Since this now gets passed in directly from the parser -> pyd -> form and the parser gets the name # NOTE: Since this now gets passed in directly from the parser -> pyd -> form and the parser gets the name
# from the db, it should no longer be necessary to query the db with reagent/kit, but with rt name directly. # from the db, it should no longer be necessary to query the db with reagent/kit, but with rt name directly.
rt = ReagentType.query(name=self.reagent.type) rt = ReagentRole.query(name=self.reagent.role)
if rt == None: if rt == None:
rt = ReagentType.query(kit_type=self.extraction_kit, reagent=wanted_reagent) rt = ReagentRole.query(kit_type=self.extraction_kit, reagent=wanted_reagent)
return PydReagent(name=wanted_reagent.name, lot=wanted_reagent.lot, type=rt.name, return PydReagent(name=wanted_reagent.name, lot=wanted_reagent.lot, role=rt.name,
expiry=wanted_reagent.expiry, missing=False), None expiry=wanted_reagent.expiry, missing=False), None
def updated(self): def updated(self):
@@ -619,20 +619,20 @@ class SubmissionFormWidget(QWidget):
check = not reagent.missing check = not reagent.missing
except: except:
check = False check = False
self.setObjectName(f"{reagent.type}_label") self.setObjectName(f"{reagent.role}_label")
if check: if check:
self.setText(f"Parsed {reagent.type}") self.setText(f"Parsed {reagent.role}")
else: else:
self.setText(f"MISSING {reagent.type}") self.setText(f"MISSING {reagent.role}")
def updated(self, reagent_type: str): def updated(self, reagent_role: str):
""" """
Marks widget as updated Marks widget as updated
Args: Args:
reagent_type (str): _description_ reagent_role (str): _description_
""" """
self.setText(f"UPDATED {reagent_type}") self.setText(f"UPDATED {reagent_role}")
class ReagentLot(QComboBox): class ReagentLot(QComboBox):
@@ -641,7 +641,7 @@ class SubmissionFormWidget(QWidget):
self.setEditable(True) self.setEditable(True)
# logger.debug(f"Attempting lookup of reagents by type: {reagent.type}") # logger.debug(f"Attempting lookup of reagents by type: {reagent.type}")
# NOTE: below was lookup_reagent_by_type_name_and_kit_name, but I couldn't get it to work. # NOTE: below was lookup_reagent_by_type_name_and_kit_name, but I couldn't get it to work.
lookup = Reagent.query(reagent_type=reagent.type) lookup = Reagent.query(reagent_role=reagent.role)
relevant_reagents = [str(item.lot) for item in lookup] relevant_reagents = [str(item.lot) for item in lookup]
output_reg = [] output_reg = []
for rel_reagent in relevant_reagents: for rel_reagent in relevant_reagents:
@@ -658,7 +658,7 @@ class SubmissionFormWidget(QWidget):
if check_not_nan(reagent.lot): if check_not_nan(reagent.lot):
relevant_reagents.insert(0, str(reagent.lot)) relevant_reagents.insert(0, str(reagent.lot))
else: else:
looked_up_rt = KitTypeReagentTypeAssociation.query(reagent_type=reagent.type, looked_up_rt = KitTypeReagentRoleAssociation.query(reagent_role=reagent.role,
kit_type=extraction_kit) kit_type=extraction_kit)
try: try:
looked_up_reg = Reagent.query(lot_number=looked_up_rt.last_used) looked_up_reg = Reagent.query(lot_number=looked_up_rt.last_used)
@@ -684,5 +684,5 @@ class SubmissionFormWidget(QWidget):
# logger.debug(f"Found {reagent.lot} in relevant reagents: {relevant_reagents}. But no need to move due to short list.") # logger.debug(f"Found {reagent.lot} in relevant reagents: {relevant_reagents}. But no need to move due to short list.")
pass pass
# logger.debug(f"New relevant reagents: {relevant_reagents}") # logger.debug(f"New relevant reagents: {relevant_reagents}")
self.setObjectName(f"lot_{reagent.type}") self.setObjectName(f"lot_{reagent.role}")
self.addItems(relevant_reagents) self.addItems(relevant_reagents)

View File

@@ -47,7 +47,7 @@
{% endfor %}</p> {% endfor %}</p>
<h3><u>Reagents:</u></h3> <h3><u>Reagents:</u></h3>
<p>{% for item in sub['reagents'] %} <p>{% for item in sub['reagents'] %}
&nbsp;&nbsp;&nbsp;&nbsp;<b>{{ item['type'] }}</b>: {{ item['lot'] }} (EXP: {{ item['expiry'] }})<br> &nbsp;&nbsp;&nbsp;&nbsp;<b>{{ item['role'] }}</b>: {{ item['lot'] }} (EXP: {{ item['expiry'] }})<br>
{% endfor %}</p> {% endfor %}</p>
{% if sub['equipment'] %} {% if sub['equipment'] %}
<h3><u>Equipment:</u></h3> <h3><u>Equipment:</u></h3>