Renaming ReagentType to ReagentRole
This commit is contained in:
@@ -18,8 +18,9 @@ orgs_contacts = Table(
|
|||||||
Column("org_id", INTEGER, ForeignKey("_organization.id")),
|
Column("org_id", INTEGER, ForeignKey("_organization.id")),
|
||||||
Column("contact_id", INTEGER, ForeignKey("_contact.id")),
|
Column("contact_id", INTEGER, ForeignKey("_contact.id")),
|
||||||
# __table_args__ = {'extend_existing': True}
|
# __table_args__ = {'extend_existing': True}
|
||||||
extend_existing = True
|
extend_existing=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Organization(BaseClass):
|
class Organization(BaseClass):
|
||||||
"""
|
"""
|
||||||
@@ -28,9 +29,11 @@ class Organization(BaseClass):
|
|||||||
|
|
||||||
id = Column(INTEGER, primary_key=True) #: primary key
|
id = Column(INTEGER, primary_key=True) #: primary key
|
||||||
name = Column(String(64)) #: organization name
|
name = Column(String(64)) #: organization name
|
||||||
submissions = relationship("BasicSubmission", back_populates="submitting_lab") #: submissions this organization has submitted
|
submissions = relationship("BasicSubmission",
|
||||||
|
back_populates="submitting_lab") #: submissions this organization has submitted
|
||||||
cost_centre = Column(String()) #: cost centre used by org for payment
|
cost_centre = Column(String()) #: cost centre used by org for payment
|
||||||
contacts = relationship("Contact", back_populates="organization", secondary=orgs_contacts) #: contacts involved with this org
|
contacts = relationship("Contact", back_populates="organization",
|
||||||
|
secondary=orgs_contacts) #: contacts involved with this org
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"<Organization({self.name})>"
|
return f"<Organization({self.name})>"
|
||||||
@@ -38,10 +41,10 @@ class Organization(BaseClass):
|
|||||||
@classmethod
|
@classmethod
|
||||||
@setup_lookup
|
@setup_lookup
|
||||||
def query(cls,
|
def query(cls,
|
||||||
id:int|None=None,
|
id: int | None = None,
|
||||||
name:str|None=None,
|
name: str | None = None,
|
||||||
limit:int=0,
|
limit: int = 0,
|
||||||
) -> Organization|List[Organization]:
|
) -> Organization | List[Organization]:
|
||||||
"""
|
"""
|
||||||
Lookup organizations in the database by a number of parameters.
|
Lookup organizations in the database by a number of parameters.
|
||||||
|
|
||||||
@@ -55,7 +58,7 @@ class Organization(BaseClass):
|
|||||||
query: Query = cls.__database_session__.query(cls)
|
query: Query = cls.__database_session__.query(cls)
|
||||||
match id:
|
match id:
|
||||||
case int():
|
case int():
|
||||||
query = query.filter(cls.id==id)
|
query = query.filter(cls.id == id)
|
||||||
limit = 1
|
limit = 1
|
||||||
case _:
|
case _:
|
||||||
pass
|
pass
|
||||||
@@ -73,6 +76,7 @@ class Organization(BaseClass):
|
|||||||
def save(self):
|
def save(self):
|
||||||
super().save()
|
super().save()
|
||||||
|
|
||||||
|
|
||||||
class Contact(BaseClass):
|
class Contact(BaseClass):
|
||||||
"""
|
"""
|
||||||
Base of Contact
|
Base of Contact
|
||||||
@@ -82,7 +86,8 @@ class Contact(BaseClass):
|
|||||||
name = Column(String(64)) #: contact name
|
name = Column(String(64)) #: contact name
|
||||||
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
|
submissions = relationship("BasicSubmission", back_populates="contact") #: submissions this contact has submitted
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
@@ -95,11 +100,11 @@ class Contact(BaseClass):
|
|||||||
@classmethod
|
@classmethod
|
||||||
@setup_lookup
|
@setup_lookup
|
||||||
def query(cls,
|
def query(cls,
|
||||||
name:str|None=None,
|
name: str | None = None,
|
||||||
email:str|None=None,
|
email: str | None = None,
|
||||||
phone:str|None=None,
|
phone: str | None = None,
|
||||||
limit:int=0,
|
limit: int = 0,
|
||||||
) -> Contact|List[Contact]:
|
) -> Contact | List[Contact]:
|
||||||
"""
|
"""
|
||||||
Lookup contacts in the database by a number of parameters.
|
Lookup contacts in the database by a number of parameters.
|
||||||
|
|
||||||
@@ -117,23 +122,22 @@ class Contact(BaseClass):
|
|||||||
match name:
|
match name:
|
||||||
case str():
|
case str():
|
||||||
# logger.debug(f"Looking up contact with name: {name}")
|
# logger.debug(f"Looking up contact with name: {name}")
|
||||||
query = query.filter(cls.name==name)
|
query = query.filter(cls.name == name)
|
||||||
limit = 1
|
limit = 1
|
||||||
case _:
|
case _:
|
||||||
pass
|
pass
|
||||||
match email:
|
match email:
|
||||||
case str():
|
case str():
|
||||||
# logger.debug(f"Looking up contact with email: {name}")
|
# logger.debug(f"Looking up contact with email: {name}")
|
||||||
query = query.filter(cls.email==email)
|
query = query.filter(cls.email == email)
|
||||||
limit = 1
|
limit = 1
|
||||||
case _:
|
case _:
|
||||||
pass
|
pass
|
||||||
match phone:
|
match phone:
|
||||||
case str():
|
case str():
|
||||||
# logger.debug(f"Looking up contact with phone: {name}")
|
# logger.debug(f"Looking up contact with phone: {name}")
|
||||||
query = query.filter(cls.phone==phone)
|
query = query.filter(cls.phone == phone)
|
||||||
limit = 1
|
limit = 1
|
||||||
case _:
|
case _:
|
||||||
pass
|
pass
|
||||||
return cls.execute_query(query=query, limit=limit)
|
return cls.execute_query(query=query, limit=limit)
|
||||||
|
|
||||||
@@ -248,6 +248,7 @@ class BasicSubmission(BaseClass):
|
|||||||
ext_info = self.extraction_info
|
ext_info = self.extraction_info
|
||||||
except TypeError:
|
except TypeError:
|
||||||
ext_info = None
|
ext_info = None
|
||||||
|
|
||||||
output = {
|
output = {
|
||||||
"id": self.id,
|
"id": self.id,
|
||||||
"plate_number": self.rsl_plate_num,
|
"plate_number": self.rsl_plate_num,
|
||||||
@@ -257,8 +258,7 @@ class BasicSubmission(BaseClass):
|
|||||||
"submitting_lab": sub_lab,
|
"submitting_lab": sub_lab,
|
||||||
"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
|
||||||
@@ -299,6 +299,15 @@ class BasicSubmission(BaseClass):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error setting comment: {self.comment}, {e}")
|
logger.error(f"Error setting comment: {self.comment}, {e}")
|
||||||
comments = None
|
comments = None
|
||||||
|
try:
|
||||||
|
contact = self.contact.name
|
||||||
|
except AttributeError as e:
|
||||||
|
logger.error(f"Problem setting contact: {e}")
|
||||||
|
contact = "NA"
|
||||||
|
try:
|
||||||
|
contact_phone = self.contact.phone
|
||||||
|
except AttributeError:
|
||||||
|
contact_phone = "NA"
|
||||||
output["submission_category"] = self.submission_category
|
output["submission_category"] = self.submission_category
|
||||||
output["technician"] = self.technician
|
output["technician"] = self.technician
|
||||||
output["reagents"] = reagents
|
output["reagents"] = reagents
|
||||||
@@ -308,9 +317,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
|
# logger.debug(f"Setting contact to: {contact} of type: {type(contact)}")
|
||||||
output["contact_phone"] = self.contact.phone
|
output["contact"] = contact
|
||||||
|
output["contact_phone"] = contact_phone
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def calculate_column_count(self) -> int:
|
def calculate_column_count(self) -> int:
|
||||||
@@ -431,7 +440,7 @@ class BasicSubmission(BaseClass):
|
|||||||
excluded = ['controls', 'extraction_info', 'pcr_info', 'comment', 'comments', 'samples', 'reagents',
|
excluded = ['controls', 'extraction_info', 'pcr_info', 'comment', 'comments', 'samples', 'reagents',
|
||||||
'equipment', 'gel_info', 'gel_image', 'dna_core_submission_number', 'gel_controls',
|
'equipment', 'gel_info', 'gel_image', 'dna_core_submission_number', 'gel_controls',
|
||||||
'source_plates', 'pcr_technician', 'ext_technician', 'artic_technician', 'cost_centre',
|
'source_plates', 'pcr_technician', 'ext_technician', 'artic_technician', 'cost_centre',
|
||||||
'signed_by']
|
'signed_by', 'artic_date', 'gel_barcode', 'gel_date', 'ngs_date', 'contact_phone', 'contact']
|
||||||
for item in excluded:
|
for item in excluded:
|
||||||
try:
|
try:
|
||||||
df = df.drop(item, axis=1)
|
df = df.drop(item, axis=1)
|
||||||
@@ -544,7 +553,6 @@ class BasicSubmission(BaseClass):
|
|||||||
# logger.debug("To dict complete")
|
# logger.debug("To dict complete")
|
||||||
new_dict = {}
|
new_dict = {}
|
||||||
for key, value in dicto.items():
|
for key, value in dicto.items():
|
||||||
# start = time()
|
|
||||||
# logger.debug(f"Checking {key}")
|
# logger.debug(f"Checking {key}")
|
||||||
missing = value is None or value in ['', 'None']
|
missing = value is None or value in ['', 'None']
|
||||||
match key:
|
match key:
|
||||||
@@ -1403,6 +1411,10 @@ class WastewaterArtic(BasicSubmission):
|
|||||||
gel_info = Column(JSON) #: unstructured data from gel.
|
gel_info = Column(JSON) #: unstructured data from gel.
|
||||||
gel_controls = Column(JSON) #: locations of controls on the gel
|
gel_controls = Column(JSON) #: locations of controls on the gel
|
||||||
source_plates = Column(JSON) #: wastewater plates that samples come from
|
source_plates = Column(JSON) #: wastewater plates that samples come from
|
||||||
|
artic_date = Column(TIMESTAMP) #: Date Artic Performed
|
||||||
|
ngs_date = Column(TIMESTAMP) #: Date submission received
|
||||||
|
gel_date = Column(TIMESTAMP) #: Date submission received
|
||||||
|
gel_barcode = Column(String(16))
|
||||||
|
|
||||||
__mapper_args__ = dict(polymorphic_identity="Wastewater Artic",
|
__mapper_args__ = dict(polymorphic_identity="Wastewater Artic",
|
||||||
polymorphic_load="inline",
|
polymorphic_load="inline",
|
||||||
@@ -1418,12 +1430,18 @@ class WastewaterArtic(BasicSubmission):
|
|||||||
output = super().to_dict(full_data=full_data, backup=backup, report=report)
|
output = super().to_dict(full_data=full_data, backup=backup, report=report)
|
||||||
if self.artic_technician in [None, "None"]:
|
if self.artic_technician in [None, "None"]:
|
||||||
output['artic_technician'] = self.technician
|
output['artic_technician'] = self.technician
|
||||||
|
else:
|
||||||
|
output['artic_technician'] = self.artic_technician
|
||||||
if report:
|
if report:
|
||||||
return output
|
return output
|
||||||
output['gel_info'] = self.gel_info
|
output['gel_info'] = self.gel_info
|
||||||
output['gel_image'] = self.gel_image
|
output['gel_image'] = self.gel_image
|
||||||
output['dna_core_submission_number'] = self.dna_core_submission_number
|
output['dna_core_submission_number'] = self.dna_core_submission_number
|
||||||
output['source_plates'] = self.source_plates
|
output['source_plates'] = self.source_plates
|
||||||
|
output['artic_date'] = self.artic_date or self.submitted_date
|
||||||
|
output['ngs_date'] = self.ngs_date or self.submitted_date
|
||||||
|
output['gel_date'] = self.gel_date or self.submitted_date
|
||||||
|
output['gel_barcode'] = self.gel_barcode
|
||||||
return output
|
return output
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -1756,19 +1774,22 @@ class WastewaterArtic(BasicSubmission):
|
|||||||
output.append(dicto)
|
output.append(dicto)
|
||||||
continue
|
continue
|
||||||
for item in self.source_plates:
|
for item in self.source_plates:
|
||||||
|
if assoc.sample.id is None:
|
||||||
|
old_plate = None
|
||||||
|
else:
|
||||||
old_plate = WastewaterAssociation.query(submission=item['plate'], sample=assoc.sample, limit=1)
|
old_plate = WastewaterAssociation.query(submission=item['plate'], sample=assoc.sample, limit=1)
|
||||||
if old_plate is not None:
|
if old_plate is not None:
|
||||||
set_plate = old_plate.submission.rsl_plate_num
|
set_plate = old_plate.submission.rsl_plate_num
|
||||||
logger.debug(dicto['WW Processing Num'])
|
# logger.debug(f"Dictionary: {pformat(dicto)}")
|
||||||
if dicto['WW Processing Num'].startswith("NTC"):
|
if dicto['ww_processing_num'].startswith("NTC"):
|
||||||
dicto['Well'] = dicto['WW Processing Num']
|
dicto['well'] = dicto['ww_processing_num']
|
||||||
else:
|
else:
|
||||||
dicto['Well'] = f"{row_map[old_plate.row]}{old_plate.column}"
|
dicto['well'] = f"{row_map[old_plate.row]}{old_plate.column}"
|
||||||
break
|
break
|
||||||
elif dicto['WW Processing Num'].startswith("NTC"):
|
elif dicto['ww_processing_num'].startswith("NTC"):
|
||||||
dicto['Well'] = dicto['WW Processing Num']
|
dicto['well'] = dicto['ww_processing_num']
|
||||||
dicto['plate_name'] = set_plate
|
dicto['plate_name'] = set_plate
|
||||||
logger.debug(f"Here is our raw sample: {pformat(dicto)}")
|
# logger.debug(f"Here is our raw sample: {pformat(dicto)}")
|
||||||
output.append(dicto)
|
output.append(dicto)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
@@ -1801,7 +1822,7 @@ class WastewaterArtic(BasicSubmission):
|
|||||||
fname = select_open_file(obj=obj, file_extension="jpg")
|
fname = select_open_file(obj=obj, file_extension="jpg")
|
||||||
dlg = GelBox(parent=obj, img_path=fname, submission=self)
|
dlg = GelBox(parent=obj, img_path=fname, submission=self)
|
||||||
if dlg.exec():
|
if dlg.exec():
|
||||||
self.dna_core_submission_number, img_path, output, comment = dlg.parse_form()
|
self.dna_core_submission_number, self.gel_barcode, img_path, output, comment = dlg.parse_form()
|
||||||
self.gel_image = img_path.name
|
self.gel_image = img_path.name
|
||||||
self.gel_info = output
|
self.gel_info = output
|
||||||
dt = datetime.strftime(datetime.now(), "%Y-%m-%d %H:%M:%S")
|
dt = datetime.strftime(datetime.now(), "%Y-%m-%d %H:%M:%S")
|
||||||
@@ -2135,7 +2156,7 @@ class BasicSample(BaseClass):
|
|||||||
if dlg.exec():
|
if dlg.exec():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
#Below are the custom sample types
|
# Below are the custom sample types
|
||||||
|
|
||||||
class WastewaterSample(BasicSample):
|
class WastewaterSample(BasicSample):
|
||||||
"""
|
"""
|
||||||
@@ -2235,6 +2256,7 @@ class WastewaterSample(BasicSample):
|
|||||||
searchables.append(dict(label=label, field=item))
|
searchables.append(dict(label=label, field=item))
|
||||||
return searchables
|
return searchables
|
||||||
|
|
||||||
|
|
||||||
class BacterialCultureSample(BasicSample):
|
class BacterialCultureSample(BasicSample):
|
||||||
"""
|
"""
|
||||||
base of bacterial culture sample
|
base of bacterial culture sample
|
||||||
|
|||||||
@@ -139,7 +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'])}")
|
# 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:
|
||||||
@@ -215,13 +215,13 @@ class InfoParser(object):
|
|||||||
new = location
|
new = location
|
||||||
new['name'] = k
|
new['name'] = k
|
||||||
relevant.append(new)
|
relevant.append(new)
|
||||||
logger.debug(f"relevant map for {sheet}: {pformat(relevant)}")
|
# logger.debug(f"relevant map for {sheet}: {pformat(relevant)}")
|
||||||
if not relevant:
|
if not relevant:
|
||||||
continue
|
continue
|
||||||
for item in relevant:
|
for item in relevant:
|
||||||
# NOTE: Get cell contents at this location
|
# NOTE: Get cell contents at this location
|
||||||
value = ws.cell(row=item['row'], column=item['column']).value
|
value = ws.cell(row=item['row'], column=item['column']).value
|
||||||
logger.debug(f"Value for {item['name']} = {value}")
|
# logger.debug(f"Value for {item['name']} = {value}")
|
||||||
match item['name']:
|
match item['name']:
|
||||||
case "submission_type":
|
case "submission_type":
|
||||||
value, missing = is_missing(value)
|
value, missing = is_missing(value)
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ class SheetWriter(object):
|
|||||||
template = self.submission_type.template_file
|
template = self.submission_type.template_file
|
||||||
workbook = load_workbook(BytesIO(template))
|
workbook = load_workbook(BytesIO(template))
|
||||||
missing_only = False
|
missing_only = False
|
||||||
self.workbook = workbook
|
# self.workbook = workbook
|
||||||
|
self.xl = workbook
|
||||||
self.write_info()
|
self.write_info()
|
||||||
self.write_reagents()
|
self.write_reagents()
|
||||||
self.write_samples()
|
self.write_samples()
|
||||||
@@ -62,23 +63,23 @@ class SheetWriter(object):
|
|||||||
def write_info(self):
|
def write_info(self):
|
||||||
disallowed = ['filepath', 'reagents', 'samples', 'equipment', 'controls']
|
disallowed = ['filepath', 'reagents', 'samples', 'equipment', 'controls']
|
||||||
info_dict = {k: v for k, v in self.sub.items() if k not in disallowed}
|
info_dict = {k: v for k, v in self.sub.items() if k not in disallowed}
|
||||||
writer = InfoWriter(xl=self.workbook, submission_type=self.submission_type, info_dict=info_dict)
|
writer = InfoWriter(xl=self.xl, submission_type=self.submission_type, info_dict=info_dict)
|
||||||
self.xl = writer.write_info()
|
self.xl = writer.write_info()
|
||||||
|
|
||||||
def write_reagents(self):
|
def write_reagents(self):
|
||||||
reagent_list = self.sub['reagents']
|
reagent_list = self.sub['reagents']
|
||||||
writer = ReagentWriter(xl=self.workbook, submission_type=self.submission_type,
|
writer = ReagentWriter(xl=self.xl, submission_type=self.submission_type,
|
||||||
extraction_kit=self.sub['extraction_kit'], reagent_list=reagent_list)
|
extraction_kit=self.sub['extraction_kit'], reagent_list=reagent_list)
|
||||||
self.xl = writer.write_reagents()
|
self.xl = writer.write_reagents()
|
||||||
|
|
||||||
def write_samples(self):
|
def write_samples(self):
|
||||||
sample_list = self.sub['samples']
|
sample_list = self.sub['samples']
|
||||||
writer = SampleWriter(xl=self.workbook, submission_type=self.submission_type, sample_list=sample_list)
|
writer = SampleWriter(xl=self.xl, submission_type=self.submission_type, sample_list=sample_list)
|
||||||
self.xl = writer.write_samples()
|
self.xl = writer.write_samples()
|
||||||
|
|
||||||
def write_equipment(self):
|
def write_equipment(self):
|
||||||
equipment_list = self.sub['equipment']
|
equipment_list = self.sub['equipment']
|
||||||
writer = EquipmentWriter(xl=self.workbook, submission_type=self.submission_type, equipment_list=equipment_list)
|
writer = EquipmentWriter(xl=self.xl, submission_type=self.submission_type, equipment_list=equipment_list)
|
||||||
self.xl = writer.write_equipment()
|
self.xl = writer.write_equipment()
|
||||||
|
|
||||||
|
|
||||||
@@ -93,18 +94,18 @@ class InfoWriter(object):
|
|||||||
self.submission_type = submission_type
|
self.submission_type = submission_type
|
||||||
self.sub_object = sub_object
|
self.sub_object = sub_object
|
||||||
self.xl = xl
|
self.xl = xl
|
||||||
map = submission_type.construct_info_map(mode='write')
|
info_map = submission_type.construct_info_map(mode='write')
|
||||||
self.info = self.reconcile_map(info_dict, map)
|
self.info = self.reconcile_map(info_dict, info_map)
|
||||||
# logger.debug(pformat(self.info))
|
# logger.debug(pformat(self.info))
|
||||||
|
|
||||||
def reconcile_map(self, info_dict: dict, map: dict) -> dict:
|
def reconcile_map(self, info_dict: dict, info_map: dict) -> dict:
|
||||||
output = {}
|
output = {}
|
||||||
for k, v in info_dict.items():
|
for k, v in info_dict.items():
|
||||||
if v is None:
|
if v is None:
|
||||||
continue
|
continue
|
||||||
dicto = {}
|
dicto = {}
|
||||||
try:
|
try:
|
||||||
dicto['locations'] = map[k]
|
dicto['locations'] = info_map[k]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# continue
|
# continue
|
||||||
pass
|
pass
|
||||||
@@ -126,7 +127,7 @@ class InfoWriter(object):
|
|||||||
logger.error(f"No locations for {k}, skipping")
|
logger.error(f"No locations for {k}, skipping")
|
||||||
continue
|
continue
|
||||||
for loc in locations:
|
for loc in locations:
|
||||||
# logger.debug(f"Writing {k} to {loc['sheet']}, row: {loc['row']}, column: {loc['column']}")
|
logger.debug(f"Writing {k} to {loc['sheet']}, row: {loc['row']}, column: {loc['column']}")
|
||||||
sheet = self.xl[loc['sheet']]
|
sheet = self.xl[loc['sheet']]
|
||||||
sheet.cell(row=loc['row'], column=loc['column'], value=v['value'])
|
sheet.cell(row=loc['row'], column=loc['column'], value=v['value'])
|
||||||
return self.sub_object.custom_info_writer(self.xl, info=self.info)
|
return self.sub_object.custom_info_writer(self.xl, info=self.info)
|
||||||
@@ -141,14 +142,14 @@ class ReagentWriter(object):
|
|||||||
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)
|
kit_type = KitType.query(name=extraction_kit)
|
||||||
map = kit_type.construct_xl_map_for_use(submission_type)
|
reagent_map = kit_type.construct_xl_map_for_use(submission_type)
|
||||||
self.reagents = self.reconcile_map(reagent_list=reagent_list, map=map)
|
self.reagents = self.reconcile_map(reagent_list=reagent_list, reagent_map=reagent_map)
|
||||||
|
|
||||||
def reconcile_map(self, reagent_list, map) -> List[dict]:
|
def reconcile_map(self, reagent_list, reagent_map) -> List[dict]:
|
||||||
output = []
|
output = []
|
||||||
for reagent in reagent_list:
|
for reagent in reagent_list:
|
||||||
try:
|
try:
|
||||||
mp_info = map[reagent['role']]
|
mp_info = reagent_map[reagent['role']]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
continue
|
continue
|
||||||
placeholder = copy(reagent)
|
placeholder = copy(reagent)
|
||||||
@@ -182,7 +183,7 @@ class SampleWriter(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
|
||||||
self.map = submission_type.construct_sample_map()['lookup_table']
|
self.sample_map = submission_type.construct_sample_map()['lookup_table']
|
||||||
self.samples = self.reconcile_map(sample_list)
|
self.samples = self.reconcile_map(sample_list)
|
||||||
|
|
||||||
def reconcile_map(self, sample_list: list):
|
def reconcile_map(self, sample_list: list):
|
||||||
@@ -200,10 +201,10 @@ class SampleWriter(object):
|
|||||||
return sorted(output, key=lambda k: k['submission_rank'])
|
return sorted(output, key=lambda k: k['submission_rank'])
|
||||||
|
|
||||||
def write_samples(self):
|
def write_samples(self):
|
||||||
sheet = self.xl[self.map['sheet']]
|
sheet = self.xl[self.sample_map['sheet']]
|
||||||
columns = self.map['sample_columns']
|
columns = self.sample_map['sample_columns']
|
||||||
for ii, sample in enumerate(self.samples):
|
for ii, sample in enumerate(self.samples):
|
||||||
row = self.map['start_row'] + (sample['submission_rank'] - 1)
|
row = self.sample_map['start_row'] + (sample['submission_rank'] - 1)
|
||||||
for k, v in sample.items():
|
for k, v in sample.items():
|
||||||
try:
|
try:
|
||||||
column = columns[k]
|
column = columns[k]
|
||||||
@@ -220,13 +221,15 @@ class EquipmentWriter(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
|
||||||
map = self.submission_type.construct_equipment_map()
|
equipment_map = self.submission_type.construct_equipment_map()
|
||||||
self.equipment = self.reconcile_map(equipment_list=equipment_list, map=map)
|
self.equipment = self.reconcile_map(equipment_list=equipment_list, equipment_map=equipment_map)
|
||||||
|
|
||||||
def reconcile_map(self, equipment_list: list, map: list):
|
def reconcile_map(self, equipment_list: list, equipment_map: list):
|
||||||
output = []
|
output = []
|
||||||
|
if equipment_list is None:
|
||||||
|
return output
|
||||||
for ii, equipment in enumerate(equipment_list, start=1):
|
for ii, equipment in enumerate(equipment_list, start=1):
|
||||||
mp_info = map[equipment['role']]
|
mp_info = equipment_map[equipment['role']]
|
||||||
# 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 == {}:
|
||||||
|
|||||||
@@ -573,10 +573,22 @@ class PydSubmission(BaseModel, extra='allow'):
|
|||||||
@field_validator("contact")
|
@field_validator("contact")
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_contact_from_org(cls, value, values):
|
def get_contact_from_org(cls, value, values):
|
||||||
|
logger.debug(f"Checking on value: {value}")
|
||||||
|
match value:
|
||||||
|
case dict():
|
||||||
|
if isinstance(value['value'], tuple):
|
||||||
|
value['value'] = value['value'][0]
|
||||||
|
case tuple():
|
||||||
|
value = dict(value=value[0], missing=False)
|
||||||
|
case _:
|
||||||
|
value = dict(value=value, missing=False)
|
||||||
check = Contact.query(name=value['value'])
|
check = Contact.query(name=value['value'])
|
||||||
if check is None:
|
if check is None:
|
||||||
org = Organization.query(name=values.data['submitting_lab']['value'])
|
org = Organization.query(name=values.data['submitting_lab']['value'])
|
||||||
contact = org.contacts[0].name
|
contact = org.contacts[0].name
|
||||||
|
logger.debug(f"Pulled: {contact}")
|
||||||
|
if isinstance(contact, tuple):
|
||||||
|
contact = contact[0]
|
||||||
return dict(value=contact, missing=True)
|
return dict(value=contact, missing=True)
|
||||||
else:
|
else:
|
||||||
return value
|
return value
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ class GelBox(QDialog):
|
|||||||
layout.addWidget(QLabel("DNA Core Submission Number"),0,1)
|
layout.addWidget(QLabel("DNA Core Submission Number"),0,1)
|
||||||
self.core_number = QLineEdit()
|
self.core_number = QLineEdit()
|
||||||
layout.addWidget(self.core_number, 0,2)
|
layout.addWidget(self.core_number, 0,2)
|
||||||
|
layout.addWidget(QLabel("Gel Barcode"),0,3)
|
||||||
|
self.gel_barcode = QLineEdit()
|
||||||
|
layout.addWidget(self.gel_barcode, 0, 4)
|
||||||
# setting this layout to the widget
|
# setting this layout to the widget
|
||||||
# plot window goes on right side, spanning 3 rows
|
# plot window goes on right side, spanning 3 rows
|
||||||
layout.addWidget(self.imv, 1, 1,20,20)
|
layout.addWidget(self.imv, 1, 1,20,20)
|
||||||
@@ -80,8 +83,9 @@ class GelBox(QDialog):
|
|||||||
Tuple[str, str|Path, list]: output values
|
Tuple[str, str|Path, list]: output values
|
||||||
"""
|
"""
|
||||||
dna_core_submission_number = self.core_number.text()
|
dna_core_submission_number = self.core_number.text()
|
||||||
|
gel_barcode = self.gel_barcode.text()
|
||||||
values, comment = self.form.parse_form()
|
values, comment = self.form.parse_form()
|
||||||
return dna_core_submission_number, self.img_path, values, comment
|
return dna_core_submission_number, gel_barcode, self.img_path, values, comment
|
||||||
|
|
||||||
class ControlsForm(QWidget):
|
class ControlsForm(QWidget):
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ from pathlib import Path
|
|||||||
from . import select_open_file, select_save_file
|
from . import select_open_file, select_save_file
|
||||||
import logging, difflib, inspect
|
import logging, difflib, inspect
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tools import Report, Result, check_not_nan, workbook_2_csv
|
from tools import Report, Result, check_not_nan, workbook_2_csv, main_form_style
|
||||||
from backend.excel.parser import SheetParser
|
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,
|
||||||
ReagentRole, KitTypeReagentRoleAssociation
|
ReagentRole, KitTypeReagentRoleAssociation, BasicSubmission
|
||||||
)
|
)
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from .pop_ups import QuestionAsker, AlertPop
|
from .pop_ups import QuestionAsker, AlertPop
|
||||||
@@ -149,6 +149,8 @@ class SubmissionFormContainer(QWidget):
|
|||||||
|
|
||||||
class SubmissionFormWidget(QWidget):
|
class SubmissionFormWidget(QWidget):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, parent: QWidget, submission: PydSubmission) -> None:
|
def __init__(self, parent: QWidget, submission: PydSubmission) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
# self.report = Report()
|
# self.report = Report()
|
||||||
@@ -169,15 +171,16 @@ class SubmissionFormWidget(QWidget):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
logger.error(f"Couldn't get attribute from pyd: {k}")
|
logger.error(f"Couldn't get attribute from pyd: {k}")
|
||||||
value = dict(value=None, missing=True)
|
value = dict(value=None, missing=True)
|
||||||
add_widget = self.create_widget(key=k, value=value, submission_type=self.pyd.submission_type['value'])
|
add_widget = self.create_widget(key=k, value=value, submission_type=self.pyd.submission_type['value'], sub_obj=st)
|
||||||
if add_widget != None:
|
if add_widget != None:
|
||||||
self.layout.addWidget(add_widget)
|
self.layout.addWidget(add_widget)
|
||||||
if k == "extraction_kit":
|
if k == "extraction_kit":
|
||||||
add_widget.input.currentTextChanged.connect(self.scrape_reagents)
|
add_widget.input.currentTextChanged.connect(self.scrape_reagents)
|
||||||
|
self.setStyleSheet(main_form_style)
|
||||||
self.scrape_reagents(self.pyd.extraction_kit)
|
self.scrape_reagents(self.pyd.extraction_kit)
|
||||||
|
|
||||||
def create_widget(self, key: str, value: dict | PydReagent, submission_type: str | None = None,
|
def create_widget(self, key: str, value: dict | PydReagent, submission_type: str | None = None,
|
||||||
extraction_kit: str | None = None) -> "self.InfoItem":
|
extraction_kit: str | None = None, sub_obj:BasicSubmission|None=None) -> "self.InfoItem":
|
||||||
"""
|
"""
|
||||||
Make an InfoItem widget to hold a field
|
Make an InfoItem widget to hold a field
|
||||||
|
|
||||||
@@ -197,7 +200,7 @@ class SubmissionFormWidget(QWidget):
|
|||||||
else:
|
else:
|
||||||
widget = None
|
widget = None
|
||||||
case _:
|
case _:
|
||||||
widget = self.InfoItem(self, key=key, value=value, submission_type=submission_type)
|
widget = self.InfoItem(self, key=key, value=value, submission_type=submission_type, sub_obj=sub_obj)
|
||||||
return widget
|
return widget
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -395,11 +398,11 @@ class SubmissionFormWidget(QWidget):
|
|||||||
|
|
||||||
class InfoItem(QWidget):
|
class InfoItem(QWidget):
|
||||||
|
|
||||||
def __init__(self, parent: QWidget, key: str, value: dict, submission_type: str | None = None) -> None:
|
def __init__(self, parent: QWidget, key: str, value: dict, submission_type: str | None = None, sub_obj:BasicSubmission|None=None) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
layout = QVBoxLayout()
|
layout = QVBoxLayout()
|
||||||
self.label = self.ParsedQLabel(key=key, value=value)
|
self.label = self.ParsedQLabel(key=key, value=value)
|
||||||
self.input: QWidget = self.set_widget(parent=self, key=key, value=value, submission_type=submission_type)
|
self.input: QWidget = self.set_widget(parent=self, key=key, value=value, submission_type=submission_type, sub_obj=sub_obj)
|
||||||
self.setObjectName(key)
|
self.setObjectName(key)
|
||||||
try:
|
try:
|
||||||
self.missing: bool = value['missing']
|
self.missing: bool = value['missing']
|
||||||
@@ -436,7 +439,7 @@ class SubmissionFormWidget(QWidget):
|
|||||||
return None, None
|
return None, None
|
||||||
return self.input.objectName(), dict(value=value, missing=self.missing)
|
return self.input.objectName(), dict(value=value, missing=self.missing)
|
||||||
|
|
||||||
def set_widget(self, parent: QWidget, key: str, value: dict, submission_type: str | None = None) -> QWidget:
|
def set_widget(self, parent: QWidget, key: str, value: dict, submission_type: str | None = None, sub_obj:BasicSubmission|None=None) -> QWidget:
|
||||||
"""
|
"""
|
||||||
Creates form widget
|
Creates form widget
|
||||||
|
|
||||||
@@ -449,6 +452,8 @@ class SubmissionFormWidget(QWidget):
|
|||||||
Returns:
|
Returns:
|
||||||
QWidget: Form object
|
QWidget: Form object
|
||||||
"""
|
"""
|
||||||
|
if sub_obj is None:
|
||||||
|
sub_obj = SubmissionType.query(name=submission_type).get_submission_class()
|
||||||
try:
|
try:
|
||||||
value = value['value']
|
value = value['value']
|
||||||
except (TypeError, KeyError):
|
except (TypeError, KeyError):
|
||||||
@@ -488,15 +493,15 @@ class SubmissionFormWidget(QWidget):
|
|||||||
logger.error(f"Couldn't find {obj.prsr.sub['extraction_kit']}")
|
logger.error(f"Couldn't find {obj.prsr.sub['extraction_kit']}")
|
||||||
obj.ext_kit = uses[0]
|
obj.ext_kit = uses[0]
|
||||||
add_widget.addItems(uses)
|
add_widget.addItems(uses)
|
||||||
case 'submitted_date':
|
# case 'submitted_date':
|
||||||
# NOTE: uses base calendar
|
# # NOTE: uses base calendar
|
||||||
add_widget = QDateEdit(calendarPopup=True)
|
# add_widget = QDateEdit(calendarPopup=True)
|
||||||
# NOTE: sets submitted date based on date found in excel sheet
|
# # NOTE: sets submitted date based on date found in excel sheet
|
||||||
try:
|
# try:
|
||||||
add_widget.setDate(value)
|
# add_widget.setDate(value)
|
||||||
# NOTE: if not found, use today
|
# # NOTE: if not found, use today
|
||||||
except:
|
# except:
|
||||||
add_widget.setDate(date.today())
|
# add_widget.setDate(date.today())
|
||||||
case 'submission_category':
|
case 'submission_category':
|
||||||
add_widget = QComboBox()
|
add_widget = QComboBox()
|
||||||
cats = ['Diagnostic', "Surveillance", "Research"]
|
cats = ['Diagnostic', "Surveillance", "Research"]
|
||||||
@@ -507,6 +512,15 @@ class SubmissionFormWidget(QWidget):
|
|||||||
cats.insert(0, cats.pop(cats.index(submission_type)))
|
cats.insert(0, cats.pop(cats.index(submission_type)))
|
||||||
add_widget.addItems(cats)
|
add_widget.addItems(cats)
|
||||||
case _:
|
case _:
|
||||||
|
if key in sub_obj.timestamps():
|
||||||
|
add_widget = QDateEdit(calendarPopup=True)
|
||||||
|
# NOTE: sets submitted date based on date found in excel sheet
|
||||||
|
try:
|
||||||
|
add_widget.setDate(value)
|
||||||
|
# NOTE: if not found, use today
|
||||||
|
except:
|
||||||
|
add_widget.setDate(date.today())
|
||||||
|
else:
|
||||||
# NOTE: anything else gets added in as a line edit
|
# NOTE: anything else gets added in as a line edit
|
||||||
add_widget = QLineEdit()
|
add_widget = QLineEdit()
|
||||||
# logger.debug(f"Setting widget text to {str(value).replace('_', ' ')}")
|
# logger.debug(f"Setting widget text to {str(value).replace('_', ' ')}")
|
||||||
@@ -514,6 +528,7 @@ class SubmissionFormWidget(QWidget):
|
|||||||
if add_widget is not None:
|
if add_widget is not None:
|
||||||
add_widget.setObjectName(key)
|
add_widget.setObjectName(key)
|
||||||
add_widget.setParent(parent)
|
add_widget.setParent(parent)
|
||||||
|
# add_widget.setStyleSheet(main_form_style)
|
||||||
return add_widget
|
return add_widget
|
||||||
|
|
||||||
def update_missing(self):
|
def update_missing(self):
|
||||||
@@ -569,6 +584,7 @@ class SubmissionFormWidget(QWidget):
|
|||||||
self.label = self.ReagentParsedLabel(reagent=reagent)
|
self.label = self.ReagentParsedLabel(reagent=reagent)
|
||||||
layout.addWidget(self.label)
|
layout.addWidget(self.label)
|
||||||
self.lot = self.ReagentLot(reagent=reagent, extraction_kit=extraction_kit)
|
self.lot = self.ReagentLot(reagent=reagent, extraction_kit=extraction_kit)
|
||||||
|
# self.lot.setStyleSheet(main_form_style)
|
||||||
layout.addWidget(self.lot)
|
layout.addWidget(self.lot)
|
||||||
# NOTE: Remove spacing between reagents
|
# NOTE: Remove spacing between reagents
|
||||||
layout.setContentsMargins(0, 0, 0, 0)
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
@@ -615,7 +631,7 @@ class SubmissionFormWidget(QWidget):
|
|||||||
Set widget status to updated
|
Set widget status to updated
|
||||||
"""
|
"""
|
||||||
self.missing = True
|
self.missing = True
|
||||||
self.label.updated(self.reagent.type)
|
self.label.updated(self.reagent.role)
|
||||||
|
|
||||||
class ReagentParsedLabel(QLabel):
|
class ReagentParsedLabel(QLabel):
|
||||||
|
|
||||||
@@ -692,4 +708,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 }")
|
# self.setStyleSheet(main_form_style)
|
||||||
|
|||||||
@@ -43,6 +43,13 @@ LOGDIR = main_aux_dir.joinpath("logs")
|
|||||||
row_map = {1: "A", 2: "B", 3: "C", 4: "D", 5: "E", 6: "F", 7: "G", 8: "H"}
|
row_map = {1: "A", 2: "B", 3: "C", 4: "D", 5: "E", 6: "F", 7: "G", 8: "H"}
|
||||||
row_keys = {v: k for k, v in row_map.items()}
|
row_keys = {v: k for k, v in row_map.items()}
|
||||||
|
|
||||||
|
main_form_style = '''
|
||||||
|
QComboBox:!editable, QDateEdit {
|
||||||
|
background-color:light gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
def check_not_nan(cell_contents) -> bool:
|
def check_not_nan(cell_contents) -> bool:
|
||||||
"""
|
"""
|
||||||
@@ -329,6 +336,19 @@ def get_config(settings_path: Path | str | None = None) -> Settings:
|
|||||||
return Settings(**settings)
|
return Settings(**settings)
|
||||||
|
|
||||||
|
|
||||||
|
def check_if_app() -> bool:
|
||||||
|
"""
|
||||||
|
Checks if the program is running from pyinstaller compiled
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if running from pyinstaller. Else False.
|
||||||
|
"""
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
# Logging formatters
|
# Logging formatters
|
||||||
|
|
||||||
class GroupWriteRotatingFileHandler(handlers.RotatingFileHandler):
|
class GroupWriteRotatingFileHandler(handlers.RotatingFileHandler):
|
||||||
@@ -362,22 +382,22 @@ class CustomFormatter(logging.Formatter):
|
|||||||
BOLD = '\033[1m'
|
BOLD = '\033[1m'
|
||||||
UNDERLINE = '\033[4m'
|
UNDERLINE = '\033[4m'
|
||||||
|
|
||||||
format = "%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s"
|
log_format = "%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s"
|
||||||
|
|
||||||
FORMATS = {
|
FORMATS = {
|
||||||
logging.DEBUG: bcolors.ENDC + format + bcolors.ENDC,
|
logging.DEBUG: bcolors.ENDC + log_format + bcolors.ENDC,
|
||||||
logging.INFO: bcolors.ENDC + format + bcolors.ENDC,
|
logging.INFO: bcolors.ENDC + log_format + bcolors.ENDC,
|
||||||
logging.WARNING: bcolors.WARNING + format + bcolors.ENDC,
|
logging.WARNING: bcolors.WARNING + log_format + bcolors.ENDC,
|
||||||
logging.ERROR: bcolors.FAIL + format + bcolors.ENDC,
|
logging.ERROR: bcolors.FAIL + log_format + bcolors.ENDC,
|
||||||
logging.CRITICAL: bcolors.FAIL + format + bcolors.ENDC
|
logging.CRITICAL: bcolors.FAIL + log_format + bcolors.ENDC
|
||||||
}
|
}
|
||||||
|
|
||||||
def format(self, record):
|
def format(self, record):
|
||||||
|
if check_if_app():
|
||||||
|
log_fmt = self.log_format
|
||||||
|
else:
|
||||||
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)
|
||||||
|
|
||||||
|
|
||||||
@@ -490,19 +510,6 @@ def jinja_template_loading() -> Environment:
|
|||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
def check_if_app() -> bool:
|
|
||||||
"""
|
|
||||||
Checks if the program is running from pyinstaller compiled
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: True if running from pyinstaller. Else False.
|
|
||||||
"""
|
|
||||||
if getattr(sys, 'frozen', False):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def convert_well_to_row_column(input_str: str) -> Tuple[int, int]:
|
def convert_well_to_row_column(input_str: str) -> Tuple[int, int]:
|
||||||
"""
|
"""
|
||||||
Converts typical alphanumeric (i.e. "A2") to row, column
|
Converts typical alphanumeric (i.e. "A2") to row, column
|
||||||
@@ -591,7 +598,7 @@ class Report(BaseModel):
|
|||||||
logger.info(f"Adding {res} from {result} 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)} for <Result> entry into <Report>")
|
||||||
|
|
||||||
def is_empty(self):
|
def is_empty(self):
|
||||||
return bool(self.results)
|
return bool(self.results)
|
||||||
|
|||||||
Reference in New Issue
Block a user