Renaming ReagentType to ReagentRole

This commit is contained in:
lwark
2024-06-04 15:24:40 -05:00
parent cdcce80898
commit a355c5bb76
8 changed files with 56 additions and 15 deletions

View File

@@ -1,3 +1,8 @@
## 202406.02
- Attached Contact to Submission.
- Renamed ReagentType to ReagentRole to prevent confusion.
## 202405.04 ## 202405.04
- Improved Webview of submission details. - Improved Webview of submission details.

View File

@@ -83,6 +83,7 @@ class Contact(BaseClass):
email = Column(String(64)) #: contact email email = Column(String(64)) #: contact email
phone = Column(String(32)) #: contact phone number phone = Column(String(32)) #: contact phone number
organization = relationship("Organization", back_populates="contacts", uselist=True, secondary=orgs_contacts) #: relationship to joined organization organization = relationship("Organization", back_populates="contacts", uselist=True, secondary=orgs_contacts) #: relationship to joined organization
submissions = relationship("BasicSubmission", back_populates="contact") #: submissions this contact has submitted
def __repr__(self) -> str: def __repr__(self) -> str:
""" """

View File

@@ -10,7 +10,7 @@ from zipfile import ZipFile
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from operator import attrgetter, itemgetter from operator import attrgetter, itemgetter
from pprint import pformat from pprint import pformat
from . import BaseClass, Reagent, SubmissionType, KitType, Organization from . import BaseClass, Reagent, SubmissionType, KitType, Organization, Contact
from sqlalchemy import Column, String, TIMESTAMP, INTEGER, ForeignKey, JSON, FLOAT, case from sqlalchemy import Column, String, TIMESTAMP, INTEGER, ForeignKey, JSON, FLOAT, case
from sqlalchemy.orm import relationship, validates, Query from sqlalchemy.orm import relationship, validates, Query
from sqlalchemy.orm.attributes import flag_modified from sqlalchemy.orm.attributes import flag_modified
@@ -65,6 +65,9 @@ class BasicSubmission(BaseClass):
String(64)) #: ["Research", "Diagnostic", "Surveillance", "Validation"], else defaults to submission_type_name String(64)) #: ["Research", "Diagnostic", "Surveillance", "Validation"], else defaults to submission_type_name
cost_centre = Column( cost_centre = Column(
String(64)) #: Permanent storage of used cost centre in case organization field changed in the future. String(64)) #: Permanent storage of used cost centre in case organization field changed in the future.
contact = relationship("Contact", back_populates="submissions") #: client org
contact_id = Column(INTEGER, ForeignKey("_contact.id", ondelete="SET NULL",
name="fk_BS_contact_id")) #: client lab id from _organizations
submission_sample_associations = relationship( submission_sample_associations = relationship(
"SubmissionSampleAssociation", "SubmissionSampleAssociation",
@@ -255,6 +258,7 @@ class BasicSubmission(BaseClass):
"sample_count": self.sample_count, "sample_count": self.sample_count,
"extraction_kit": ext_kit, "extraction_kit": ext_kit,
"cost": self.run_cost, "cost": self.run_cost,
} }
if report: if report:
return output return output
@@ -304,6 +308,9 @@ class BasicSubmission(BaseClass):
output["equipment"] = equipment output["equipment"] = equipment
output["cost_centre"] = cost_centre output["cost_centre"] = cost_centre
output["signed_by"] = self.signed_by output["signed_by"] = self.signed_by
output["contact"] = self.contact.name
output["contact_phone"] = self.contact.phone
return output return output
def calculate_column_count(self) -> int: def calculate_column_count(self) -> int:
@@ -453,6 +460,8 @@ class BasicSubmission(BaseClass):
# logger.debug(f"Looking up organization: {value}") # logger.debug(f"Looking up organization: {value}")
field_value = Organization.query(name=value) field_value = Organization.query(name=value)
# logger.debug(f"Got {field_value} for organization {value}") # logger.debug(f"Got {field_value} for organization {value}")
case "contact":
field_value = Contact.query(name=value)
case "samples": case "samples":
for sample in value: for sample in value:
# logger.debug(f"Parsing {sample} to sql.") # logger.debug(f"Parsing {sample} to sql.")
@@ -1196,7 +1205,7 @@ class BacterialCulture(BasicSubmission):
new_lot = matched.group() new_lot = matched.group()
try: try:
pos_control_reg = \ pos_control_reg = \
[reg for reg in input_dict['reagents'] if reg['type'] == "Bacterial-Positive Control"][0] [reg for reg in input_dict['reagents'] if reg['role'] == "Bacterial-Positive Control"][0]
except IndexError: except IndexError:
logger.error(f"No positive control reagent listed") logger.error(f"No positive control reagent listed")
return input_dict return input_dict

View File

@@ -148,7 +148,7 @@ class ReagentWriter(object):
output = [] output = []
for reagent in reagent_list: for reagent in reagent_list:
try: try:
mp_info = map[reagent['type']] mp_info = map[reagent['role']]
except KeyError: except KeyError:
continue continue
placeholder = copy(reagent) placeholder = copy(reagent)

View File

@@ -104,7 +104,7 @@ class PydReagent(BaseModel):
if value != None: if value != None:
return convert_nans_to_nones(str(value)) return convert_nans_to_nones(str(value))
else: else:
return values.data['type'] return values.data['role']
def improved_dict(self) -> dict: def improved_dict(self) -> dict:
try: try:
@@ -123,7 +123,7 @@ class PydReagent(BaseModel):
# output[k] = value # output[k] = value
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]: def toSQL(self, submission: BasicSubmission | str = None) -> Tuple[Reagent, SubmissionReagentAssociation, Report]:
""" """
Converts this instance into a backend.db.models.kit.Reagent instance Converts this instance into a backend.db.models.kit.Reagent instance
@@ -168,6 +168,7 @@ class PydReagent(BaseModel):
# reagent.reagent_submission_associations.append(assoc) # reagent.reagent_submission_associations.append(assoc)
else: else:
assoc = None assoc = None
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)
@@ -177,7 +178,8 @@ class PydReagent(BaseModel):
assoc = None 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
return reagent, assoc, report
class PydSample(BaseModel, extra='allow'): class PydSample(BaseModel, extra='allow'):
@@ -353,6 +355,7 @@ class PydSubmission(BaseModel, extra='allow'):
samples: List[PydSample] samples: List[PydSample]
equipment: List[PydEquipment] | None = [] equipment: List[PydEquipment] | None = []
cost_centre: dict | None = Field(default=dict(value=None, missing=True), validate_default=True) cost_centre: dict | None = Field(default=dict(value=None, missing=True), validate_default=True)
contact: dict | None = Field(default=dict(value=None, missing=True), validate_default=True)
@field_validator('equipment', mode='before') @field_validator('equipment', mode='before')
@classmethod @classmethod
@@ -567,6 +570,17 @@ class PydSubmission(BaseModel, extra='allow'):
case _: case _:
return value return value
@field_validator("contact")
@classmethod
def get_contact_from_org(cls, value, values):
check = Contact.query(name=value['value'])
if check is None:
org = Organization.query(name=values.data['submitting_lab']['value'])
contact = org.contacts[0].name
return dict(value=contact, missing=True)
else:
return value
def __init__(self, **data): def __init__(self, **data):
super().__init__(**data) super().__init__(**data)
# this could also be done with default_factory # this could also be done with default_factory
@@ -664,7 +678,7 @@ class PydSubmission(BaseModel, extra='allow'):
instance.submission_reagent_associations = [] instance.submission_reagent_associations = []
# logger.debug(f"Looking through {self.reagents}") # logger.debug(f"Looking through {self.reagents}")
for reagent in self.reagents: for reagent in self.reagents:
reagent, assoc = reagent.toSQL(submission=instance) reagent, assoc, _ = 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:
instance.submission_reagent_associations.append(assoc) instance.submission_reagent_associations.append(assoc)

View File

@@ -153,7 +153,7 @@ class App(QMainWindow):
# logger.debug(f"We've got some results!") # logger.debug(f"We've got some results!")
for result in self.report.results: for result in self.report.results:
# logger.debug(f"Showing result: {result}") # logger.debug(f"Showing result: {result}")
if result != None: if result is not None:
alert = result.report() alert = result.report()
if alert.exec(): if alert.exec():
pass pass

View File

@@ -33,9 +33,10 @@ class SubmissionFormContainer(QWidget):
# logger.debug(f"Setting form widget...") # logger.debug(f"Setting form widget...")
super().__init__(parent) super().__init__(parent)
self.app = self.parent().parent() self.app = self.parent().parent()
# logger.debug(f"App: {self.app}")
self.report = Report() self.report = Report()
self.setAcceptDrops(True) self.setAcceptDrops(True)
# if import_drag is emitted, importSubmission will fire # NOTE: if import_drag is emitted, importSubmission will fire
self.import_drag.connect(self.importSubmission) self.import_drag.connect(self.importSubmission)
def dragEnterEvent(self, event): def dragEnterEvent(self, event):
@@ -138,9 +139,10 @@ 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, result = reagent.toSQL() sqlobj, assoc, result = reagent.toSQL()
sqlobj.save() sqlobj.save()
report.add_result(result) report.add_result(result)
self.app.report.add_result(report)
self.app.result_reporter() self.app.result_reporter()
return reagent return reagent
@@ -310,10 +312,14 @@ class SubmissionFormWidget(QWidget):
else: else:
self.app.ctx.database_session.rollback() self.app.ctx.database_session.rollback()
report.add_result(Result(msg="Overwrite cancelled", status="Information")) report.add_result(Result(msg="Overwrite cancelled", status="Information"))
self.app.report.add_result(report)
self.app.result_reporter()
return return
# code 2: No RSL plate number given # code 2: No RSL plate number given
case 2: case 2:
report.add_result(result) report.add_result(result)
self.app.report.add_result(report)
self.app.result_reporter()
return return
case _: case _:
pass pass
@@ -585,7 +591,7 @@ class SubmissionFormWidget(QWidget):
# 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.role}: {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_role=self.reagent.role, wanted_reagent = self.parent().parent().add_reagent(reagent_lot=lot, reagent_role=self.reagent.role,
expiry=self.reagent.expiry, expiry=self.reagent.expiry,
@@ -686,3 +692,4 @@ class SubmissionFormWidget(QWidget):
# logger.debug(f"New relevant reagents: {relevant_reagents}") # logger.debug(f"New relevant reagents: {relevant_reagents}")
self.setObjectName(f"lot_{reagent.role}") self.setObjectName(f"lot_{reagent.role}")
self.addItems(relevant_reagents) self.addItems(relevant_reagents)
self.setStyleSheet("{ background-color: white }")

View File

@@ -375,6 +375,9 @@ class CustomFormatter(logging.Formatter):
def format(self, record): def format(self, record):
log_fmt = self.FORMATS.get(record.levelno) log_fmt = self.FORMATS.get(record.levelno)
formatter = logging.Formatter(log_fmt) formatter = logging.Formatter(log_fmt)
if check_if_app():
return record
else:
return formatter.format(record) return formatter.format(record)
@@ -473,7 +476,7 @@ def jinja_template_loading() -> Environment:
Returns jinja2 template environment. Returns jinja2 template environment.
Returns: Returns:
_type_: _description_ Environment: jinja2 environment object
""" """
# NOTE: determine if pyinstaller launcher is being used # NOTE: determine if pyinstaller launcher is being used
if check_if_app(): if check_if_app():
@@ -577,7 +580,7 @@ class Report(BaseModel):
def add_result(self, result: Result | Report | None): def add_result(self, result: Result | Report | None):
match result: match result:
case Result(): case Result():
logger.debug(f"Adding {result} to results.") logger.info(f"Adding {result} to results.")
try: try:
self.results.append(result) self.results.append(result)
except AttributeError: except AttributeError:
@@ -585,11 +588,13 @@ class Report(BaseModel):
case Report(): case Report():
# logger.debug(f"Adding all results in report to new report") # logger.debug(f"Adding all results in report to new report")
for res in result.results: for res in result.results:
logger.debug(f"Adding {res} from to results.") logger.info(f"Adding {res} from {result} to results.")
self.results.append(res) self.results.append(res)
case _: case _:
logger.error(f"Unknown variable type: {type(result)}") logger.error(f"Unknown variable type: {type(result)}")
def is_empty(self):
return bool(self.results)
def rreplace(s, old, new): def rreplace(s, old, new):
return (s[::-1].replace(old[::-1], new[::-1], 1))[::-1] return (s[::-1].replace(old[::-1], new[::-1], 1))[::-1]