Beginning prelim code cleanup.

This commit is contained in:
lwark
2025-03-19 13:50:37 -05:00
parent 942c46e8b2
commit ea24a8ffd4
10 changed files with 529 additions and 110 deletions

View File

@@ -46,7 +46,7 @@ class BaseClass(Base):
"""
Abstract class to pass ctx values to all SQLAlchemy objects.
"""
__abstract__ = True #: NOTE: Will not be added to DB
__abstract__ = True #: NOTE: Will not be added to DB as a table
__table_args__ = {'extend_existing': True} #: Will only add new columns
@@ -54,6 +54,7 @@ class BaseClass(Base):
omni_removes = ["id", 'submissions', "omnigui_class_dict", "omnigui_instance_dict"]
omni_sort = ["name"]
omni_inheritable = []
searchables = []
@classproperty
def skip_on_edit(cls):
@@ -416,7 +417,10 @@ class BaseClass(Base):
else:
value = existing + [value]
else:
value = [value]
if isinstance(value, list):
value = value
else:
value = [value]
value = list(set(value))
logger.debug(f"Final value for {key}: {value}")
return super().__setattr__(key, value)

View File

@@ -858,7 +858,7 @@ class SubmissionType(BaseClass):
id = Column(INTEGER, primary_key=True) #: primary key
name = Column(String(128), unique=True) #: name of submission type
info_map = Column(JSON) #: Where basic information is found in the excel workbook corresponding to this type.
info_map = Column(JSON) #: Where parsable information is found in the excel workbook corresponding to this type.
defaults = Column(JSON) #: Basic information about this submission type
instances = relationship("BasicSubmission", backref="submission_type") #: Concrete instances of this type.
template_file = Column(BLOB) #: Blank form for this type stored as binary.
@@ -866,6 +866,200 @@ class SubmissionType(BaseClass):
secondary=submissiontypes_processes) #: Relation to equipment processes used for this type.
sample_map = Column(JSON) #: Where sample information is found in the excel sheet corresponding to this type.
"""
Example info_map (Bacterial Culture)
NOTE: read locations will be appended to write locations.
{
"comment": {
"read": [
{
"column": 2,
"row": 34,
"sheet": "Sample List"
}
],
"write": []
},
"contact": {
"read": [
{
"column": 2,
"row": 4,
"sheet": "Sample List"
}
],
"write": []
},
"contact_phone": {
"read": [],
"write": [
{
"column": 2,
"row": 5,
"sheet": "Sample List"
}
]
},
"cost_centre": {
"read": [
{
"column": 2,
"row": 6,
"sheet": "Sample List"
}
],
"write": []
},
"custom": {},
"extraction_kit": {
"read": [
{
"column": 4,
"row": 5,
"sheet": "Sample List"
}
],
"write": []
},
"rsl_plate_num": {
"read": [
{
"column": 2,
"row": 13,
"sheet": "Sample List"
}
],
"write": []
},
"sample_count": {
"read": [
{
"column": 4,
"row": 4,
"sheet": "Sample List"
}
],
"write": []
},
"signed_by": {
"read": [],
"write": [
{
"column": 2,
"row": 15,
"sheet": "Sample List"
}
]
},
"submission_category": {
"read": [
{
"column": 4,
"row": 6,
"sheet": "Sample List"
}
],
"write": []
},
"submission_type": {
"read": [
{
"column": 4,
"row": 3,
"sheet": "Sample List"
}
],
"write": []
},
"submitted_date": {
"read": [
{
"column": 2,
"row": 3,
"sheet": "Sample List"
}
],
"write": []
},
"submitter_plate_num": {
"read": [
{
"column": 2,
"row": 2,
"sheet": "Sample List"
}
],
"write": []
},
"submitting_lab": {
"read": [
{
"column": 4,
"row": 2,
"sheet": "Sample List"
}
],
"write": []
},
"technician": {
"read": [
{
"column": 2,
"row": 14,
"sheet": "Sample List"
}
],
"write": []
}
}
"""
"""
Example defaults (for Bacterial Culture)
{
"abbreviation": "BC",
"details_ignore": [
"controls"
],
"form_ignore": [
"controls",
"cost_centre"
],
"regex": "(?P<Bacterial_Culture>RSL(?:-|_)?BC(?:-|_)?20\\d{2}-?\\d{2}-?\\d{2}(?:(_|-)?\\d?([^_0123456789\\sA-QS-Z]|$)?R?\\d?)?)",
"sample_type": "Bacterial Culture Sample",
"turnaround_time": 3
}
"""
"""
Example sample_map (Bacterial Culture)
{
"lookup_table": {
"end_row": 132,
"merge_on_id": "submitter_id",
"sample_columns": {
"column": 6,
"concentration": 4,
"organism": 3,
"row": 5,
"submitter_id": 2
},
"sheet": "Sample List",
"start_row": 37
},
"plate_map": {
"end_column": 13,
"end_row": 14,
"sheet": "Plate Map",
"start_column": 2,
"start_row": 7
}
}
"""
submissiontype_kit_associations = relationship(
"SubmissionTypeKitTypeAssociation",
back_populates="submission_type",
@@ -1218,6 +1412,11 @@ class SubmissionType(BaseClass):
sample_map=self.sample_map
)
@classproperty
def info_map_json_edit_fields(cls):
dicto = dict()
return dicto
class SubmissionTypeKitTypeAssociation(BaseClass):
"""
@@ -1519,7 +1718,7 @@ class KitTypeReagentRoleAssociation(BaseClass):
case _:
pass
setattr(instance, k, v)
logger.info(f"Instance from query or create: {instance.__dict__}")
logger.info(f"Instance from query or create: {instance.__dict__}\nis new: {new}")
# sys.exit()
return instance, new
@@ -2196,6 +2395,7 @@ class Process(BaseClass):
id = Column(INTEGER, primary_key=True) #: Process id, primary key
name = Column(String(64), unique=True) #: Process name
# version = Column(String(32))
submission_types = relationship("SubmissionType", back_populates='processes',
secondary=submissiontypes_processes) #: relation to SubmissionType
equipment = relationship("Equipment", back_populates='processes',
@@ -2209,6 +2409,7 @@ class Process(BaseClass):
tip_roles = relationship("TipRole", back_populates='processes',
secondary=process_tiprole) #: relation to KitType
def __repr__(self) -> str:
"""
Returns:
@@ -2308,6 +2509,7 @@ class Process(BaseClass):
return OmniProcess(
instance_object=self,
name=self.name,
# version=self.version,
submission_types=[item.to_omni() for item in self.submission_types],
equipment_roles=[item.to_omni() for item in self.equipment_roles],
tip_roles=[item.to_omni() for item in self.tip_roles]

View File

@@ -32,7 +32,6 @@ from jinja2 import Template
from PIL import Image
logger = logging.getLogger(f"submissions.{__name__}")
@@ -460,7 +459,7 @@ class BasicSubmission(BaseClass, LogMixin):
"""
rows = range(1, plate_rows + 1)
columns = range(1, plate_columns + 1)
logger.debug(f"sample list for plate map: {pformat(sample_list)}")
# logger.debug(f"sample list for plate map: {pformat(sample_list)}")
# NOTE: An overly complicated list comprehension create a list of sample locations
# NOTE: next will return a blank cell if no value found for row/column
output_samples = [next((item for item in sample_list if item['row'] == row and item['column'] == column),
@@ -1340,8 +1339,7 @@ class BasicSubmission(BaseClass, LogMixin):
for equip in equipment:
logger.debug(f"Parsed equipment: {equip}")
_, assoc = equip.to_sql(submission=self)
logger.debug(f"Got equipment association: {assoc.__dict__}")
logger.debug(f"Got equipment association: {assoc} for {equip}")
try:
assoc.save()
except AttributeError as e:
@@ -1488,6 +1486,55 @@ class BacterialCulture(BasicSubmission):
input_dict = super().custom_info_parser(input_dict=input_dict, xl=xl, custom_fields=custom_fields)
return input_dict
def custom_context_events(self) -> dict:
"""
Sets context events for main widget
Returns:
dict: Context menu items for this instance.
"""
events = super().custom_context_events()
events['Import Concentration'] = self.import_concentration
return events
@report_result
def import_concentration(self, obj) -> Report:
from frontend.widgets import select_open_file
from backend.excel.parser import ConcentrationParser
report = Report()
fname = select_open_file(obj=obj, file_extension="xlsx")
if not fname:
report.add_result(Result(msg="No file selected, cancelling.", status="Warning"))
return report
parser = ConcentrationParser(filepath=fname, submission=self)
conc_samples = [sample for sample in parser.samples]
for sample in self.samples:
logger.debug(f"Sample {sample.submitter_id}")
try:
# NOTE: Fix for ENs which have no rsl_number...
sample_dict = next(item for item in conc_samples if item['submitter_id'] == sample.submitter_id)
except StopIteration:
continue
logger.debug(f"Sample {sample.submitter_id} conc. = {sample_dict['concentration']}")
if sample_dict['concentration']:
sample.concentration = sample_dict['concentration']
else:
continue
sample.save()
# logger.debug(conc_samples)
return report
@classmethod
def parse_concentration(cls, xl: Workbook, rsl_plate_num: str) -> Generator[dict, None, None]:
lookup_table = cls.get_submission_type().sample_map['lookup_table']
logger.debug(lookup_table)
main_sheet = xl[lookup_table['sheet']]
for row in main_sheet.iter_rows(min_row=lookup_table['start_row'], max_row=lookup_table['end_row']):
idx = row[0].row
sample = dict(submitter_id=main_sheet.cell(row=idx, column=lookup_table['sample_columns']['submitter_id']).value)
sample['concentration'] = main_sheet.cell(row=idx, column=lookup_table['sample_columns']['concentration']).value
yield sample
class Wastewater(BasicSubmission):
"""
@@ -2598,7 +2645,7 @@ class BasicSample(BaseClass, LogMixin):
self.show_details(obj)
# Below are the custom sample types
# NOTE: Below are the custom sample types
class WastewaterSample(BasicSample):
"""