Beginning prelim code cleanup.
This commit is contained in:
@@ -1,3 +1,12 @@
|
|||||||
|
# 202503.04
|
||||||
|
|
||||||
|
- Kit editor debugging.
|
||||||
|
- Fixed missing search bar in Edit Reagent.
|
||||||
|
|
||||||
|
# 202503.03
|
||||||
|
|
||||||
|
- Kit editor pre-release.
|
||||||
|
|
||||||
# 202501.02
|
# 202501.02
|
||||||
|
|
||||||
- Fixed bug where Wastewater ENs were not receiving rsl_number and therefore not getting PCR data.
|
- Fixed bug where Wastewater ENs were not receiving rsl_number and therefore not getting PCR data.
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class BaseClass(Base):
|
|||||||
"""
|
"""
|
||||||
Abstract class to pass ctx values to all SQLAlchemy objects.
|
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
|
__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_removes = ["id", 'submissions', "omnigui_class_dict", "omnigui_instance_dict"]
|
||||||
omni_sort = ["name"]
|
omni_sort = ["name"]
|
||||||
omni_inheritable = []
|
omni_inheritable = []
|
||||||
|
searchables = []
|
||||||
|
|
||||||
@classproperty
|
@classproperty
|
||||||
def skip_on_edit(cls):
|
def skip_on_edit(cls):
|
||||||
@@ -416,7 +417,10 @@ class BaseClass(Base):
|
|||||||
else:
|
else:
|
||||||
value = existing + [value]
|
value = existing + [value]
|
||||||
else:
|
else:
|
||||||
value = [value]
|
if isinstance(value, list):
|
||||||
|
value = value
|
||||||
|
else:
|
||||||
|
value = [value]
|
||||||
value = list(set(value))
|
value = list(set(value))
|
||||||
logger.debug(f"Final value for {key}: {value}")
|
logger.debug(f"Final value for {key}: {value}")
|
||||||
return super().__setattr__(key, value)
|
return super().__setattr__(key, value)
|
||||||
|
|||||||
@@ -858,7 +858,7 @@ class SubmissionType(BaseClass):
|
|||||||
|
|
||||||
id = Column(INTEGER, primary_key=True) #: primary key
|
id = Column(INTEGER, primary_key=True) #: primary key
|
||||||
name = Column(String(128), unique=True) #: name of submission type
|
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
|
defaults = Column(JSON) #: Basic information about this submission type
|
||||||
instances = relationship("BasicSubmission", backref="submission_type") #: Concrete instances of this type.
|
instances = relationship("BasicSubmission", backref="submission_type") #: Concrete instances of this type.
|
||||||
template_file = Column(BLOB) #: Blank form for this type stored as binary.
|
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.
|
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.
|
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(
|
submissiontype_kit_associations = relationship(
|
||||||
"SubmissionTypeKitTypeAssociation",
|
"SubmissionTypeKitTypeAssociation",
|
||||||
back_populates="submission_type",
|
back_populates="submission_type",
|
||||||
@@ -1218,6 +1412,11 @@ class SubmissionType(BaseClass):
|
|||||||
sample_map=self.sample_map
|
sample_map=self.sample_map
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classproperty
|
||||||
|
def info_map_json_edit_fields(cls):
|
||||||
|
dicto = dict()
|
||||||
|
return dicto
|
||||||
|
|
||||||
|
|
||||||
class SubmissionTypeKitTypeAssociation(BaseClass):
|
class SubmissionTypeKitTypeAssociation(BaseClass):
|
||||||
"""
|
"""
|
||||||
@@ -1519,7 +1718,7 @@ class KitTypeReagentRoleAssociation(BaseClass):
|
|||||||
case _:
|
case _:
|
||||||
pass
|
pass
|
||||||
setattr(instance, k, v)
|
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()
|
# sys.exit()
|
||||||
return instance, new
|
return instance, new
|
||||||
|
|
||||||
@@ -2196,6 +2395,7 @@ class Process(BaseClass):
|
|||||||
|
|
||||||
id = Column(INTEGER, primary_key=True) #: Process id, primary key
|
id = Column(INTEGER, primary_key=True) #: Process id, primary key
|
||||||
name = Column(String(64), unique=True) #: Process name
|
name = Column(String(64), unique=True) #: Process name
|
||||||
|
# version = Column(String(32))
|
||||||
submission_types = relationship("SubmissionType", back_populates='processes',
|
submission_types = relationship("SubmissionType", back_populates='processes',
|
||||||
secondary=submissiontypes_processes) #: relation to SubmissionType
|
secondary=submissiontypes_processes) #: relation to SubmissionType
|
||||||
equipment = relationship("Equipment", back_populates='processes',
|
equipment = relationship("Equipment", back_populates='processes',
|
||||||
@@ -2209,6 +2409,7 @@ class Process(BaseClass):
|
|||||||
tip_roles = relationship("TipRole", back_populates='processes',
|
tip_roles = relationship("TipRole", back_populates='processes',
|
||||||
secondary=process_tiprole) #: relation to KitType
|
secondary=process_tiprole) #: relation to KitType
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
"""
|
"""
|
||||||
Returns:
|
Returns:
|
||||||
@@ -2308,6 +2509,7 @@ class Process(BaseClass):
|
|||||||
return OmniProcess(
|
return OmniProcess(
|
||||||
instance_object=self,
|
instance_object=self,
|
||||||
name=self.name,
|
name=self.name,
|
||||||
|
# version=self.version,
|
||||||
submission_types=[item.to_omni() for item in self.submission_types],
|
submission_types=[item.to_omni() for item in self.submission_types],
|
||||||
equipment_roles=[item.to_omni() for item in self.equipment_roles],
|
equipment_roles=[item.to_omni() for item in self.equipment_roles],
|
||||||
tip_roles=[item.to_omni() for item in self.tip_roles]
|
tip_roles=[item.to_omni() for item in self.tip_roles]
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ from jinja2 import Template
|
|||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(f"submissions.{__name__}")
|
logger = logging.getLogger(f"submissions.{__name__}")
|
||||||
|
|
||||||
|
|
||||||
@@ -460,7 +459,7 @@ class BasicSubmission(BaseClass, LogMixin):
|
|||||||
"""
|
"""
|
||||||
rows = range(1, plate_rows + 1)
|
rows = range(1, plate_rows + 1)
|
||||||
columns = range(1, plate_columns + 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: 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
|
# 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),
|
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:
|
for equip in equipment:
|
||||||
logger.debug(f"Parsed equipment: {equip}")
|
logger.debug(f"Parsed equipment: {equip}")
|
||||||
_, assoc = equip.to_sql(submission=self)
|
_, assoc = equip.to_sql(submission=self)
|
||||||
logger.debug(f"Got equipment association: {assoc.__dict__}")
|
logger.debug(f"Got equipment association: {assoc} for {equip}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
assoc.save()
|
assoc.save()
|
||||||
except AttributeError as e:
|
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)
|
input_dict = super().custom_info_parser(input_dict=input_dict, xl=xl, custom_fields=custom_fields)
|
||||||
return input_dict
|
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):
|
class Wastewater(BasicSubmission):
|
||||||
"""
|
"""
|
||||||
@@ -2598,7 +2645,7 @@ class BasicSample(BaseClass, LogMixin):
|
|||||||
self.show_details(obj)
|
self.show_details(obj)
|
||||||
|
|
||||||
|
|
||||||
# Below are the custom sample types
|
# NOTE: Below are the custom sample types
|
||||||
|
|
||||||
class WastewaterSample(BasicSample):
|
class WastewaterSample(BasicSample):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -677,10 +677,7 @@ class PCRParser(object):
|
|||||||
def pcr_info(self) -> dict:
|
def pcr_info(self) -> dict:
|
||||||
"""
|
"""
|
||||||
Parse general info rows for all types of PCR results
|
Parse general info rows for all types of PCR results
|
||||||
|
"""
|
||||||
Args:
|
|
||||||
sheet_name (str): Name of sheet in excel workbook that holds info.
|
|
||||||
"""
|
|
||||||
info_map = self.submission_obj.get_submission_type().sample_map['pcr_general_info']
|
info_map = self.submission_obj.get_submission_type().sample_map['pcr_general_info']
|
||||||
sheet = self.xl[info_map['sheet']]
|
sheet = self.xl[info_map['sheet']]
|
||||||
iter_rows = sheet.iter_rows(min_row=info_map['start_row'], max_row=info_map['end_row'])
|
iter_rows = sheet.iter_rows(min_row=info_map['start_row'], max_row=info_map['end_row'])
|
||||||
@@ -695,3 +692,27 @@ class PCRParser(object):
|
|||||||
pcr[key] = value
|
pcr[key] = value
|
||||||
pcr['imported_by'] = getuser()
|
pcr['imported_by'] = getuser()
|
||||||
return pcr
|
return pcr
|
||||||
|
|
||||||
|
|
||||||
|
class ConcentrationParser(object):
|
||||||
|
|
||||||
|
def __init__(self, filepath: Path | None = None, submission: BasicSubmission | None = None) -> None:
|
||||||
|
if filepath is None:
|
||||||
|
logger.error('No filepath given.')
|
||||||
|
self.xl = None
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
self.xl = load_workbook(filepath)
|
||||||
|
except ValueError as e:
|
||||||
|
logger.error(f'Incorrect value: {e}')
|
||||||
|
self.xl = None
|
||||||
|
except PermissionError:
|
||||||
|
logger.error(f"Couldn't get permissions for {filepath.__str__()}. Operation might have been cancelled.")
|
||||||
|
return None
|
||||||
|
if submission is None:
|
||||||
|
self.submission_obj = BacterialCulture
|
||||||
|
rsl_plate_num = None
|
||||||
|
else:
|
||||||
|
self.submission_obj = submission
|
||||||
|
rsl_plate_num = self.submission_obj.rsl_plate_num
|
||||||
|
self.samples = self.submission_obj.parse_concentration(xl=self.xl, rsl_plate_num=rsl_plate_num)
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ logger = logging.getLogger(f"submissions.{__name__}")
|
|||||||
|
|
||||||
|
|
||||||
class BaseOmni(BaseModel):
|
class BaseOmni(BaseModel):
|
||||||
|
|
||||||
instance_object: Any | None = Field(default=None)
|
instance_object: Any | None = Field(default=None)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@@ -23,67 +22,107 @@ class BaseOmni(BaseModel):
|
|||||||
def aliases(cls):
|
def aliases(cls):
|
||||||
return cls.class_object.aliases
|
return cls.class_object.aliases
|
||||||
|
|
||||||
|
# NOTE: Okay, this will not work for editing, since by definition not all attributes will line up.
|
||||||
|
# def check_all_attributes(self, attributes: dict) -> bool:
|
||||||
|
# """
|
||||||
|
# Checks this instance against a dictionary of attributes to determine if they are a match.
|
||||||
|
#
|
||||||
|
# Args:
|
||||||
|
# attributes (dict): A dictionary of attributes to be check for equivalence
|
||||||
|
#
|
||||||
|
# Returns:
|
||||||
|
# bool: If a single unequivocal value is found will be false, else true.
|
||||||
|
# """
|
||||||
|
# logger.debug(f"Incoming attributes: {attributes}")
|
||||||
|
# for key, value in attributes.items():
|
||||||
|
# logger.debug(f"Comparing value class: {value.__class__} to omni class")
|
||||||
|
# if isinstance(value, str):
|
||||||
|
# try:
|
||||||
|
# check = value.lower() == "none"
|
||||||
|
# except AttributeError:
|
||||||
|
# continue
|
||||||
|
# if check:
|
||||||
|
# value = None
|
||||||
|
# logger.debug(f"Attempting to grab attribute: {key}")
|
||||||
|
# try:
|
||||||
|
# self_value = getattr(self, key)
|
||||||
|
# class_attr = getattr(self.class_object, key)
|
||||||
|
# except AttributeError:
|
||||||
|
# continue
|
||||||
|
# try:
|
||||||
|
# logger.debug(f"Check if {self_value.__class__} is subclass of {BaseOmni}")
|
||||||
|
# check = issubclass(self_value.__class__, BaseOmni)
|
||||||
|
# except TypeError as e:
|
||||||
|
# logger.error(f"Couldn't check if {self_value.__class__} is subclass of {BaseOmni} due to {e}")
|
||||||
|
# check = False
|
||||||
|
# if check:
|
||||||
|
# logger.debug(f"Checking for subclass name.")
|
||||||
|
# self_value = self_value.name
|
||||||
|
# try:
|
||||||
|
# logger.debug(f"Check if {value.__class__} is subclass of {BaseOmni}")
|
||||||
|
# check = issubclass(value.__class__, BaseOmni)
|
||||||
|
# except TypeError as e:
|
||||||
|
# logger.error(f"Couldn't check if {value.__class__} is subclass of {BaseOmni} due to {e}")
|
||||||
|
# check = False
|
||||||
|
# if check:
|
||||||
|
# logger.debug(f"Checking for subclass name.")
|
||||||
|
# value = value.name
|
||||||
|
# logger.debug(f"Self value: {self_value}, class attr: {class_attr} of type: {type(class_attr)}")
|
||||||
|
# if isinstance(class_attr, property):
|
||||||
|
# filter = "property"
|
||||||
|
# else:
|
||||||
|
# filter = class_attr.property
|
||||||
|
# match filter:
|
||||||
|
# case ColumnProperty():
|
||||||
|
# match class_attr.type:
|
||||||
|
# case INTEGER():
|
||||||
|
# if value.lower() == "true":
|
||||||
|
# value = 1
|
||||||
|
# elif value.lower() == "false":
|
||||||
|
# value = 0
|
||||||
|
# else:
|
||||||
|
# value = int(value)
|
||||||
|
# case FLOAT():
|
||||||
|
# value = float(value)
|
||||||
|
# case "property":
|
||||||
|
# pass
|
||||||
|
# case _RelationshipDeclared():
|
||||||
|
# logger.debug(f"Checking relationship value: {self_value}")
|
||||||
|
# try:
|
||||||
|
# self_value = self_value.name
|
||||||
|
# except AttributeError:
|
||||||
|
# pass
|
||||||
|
# if class_attr.property.uselist:
|
||||||
|
# self_value = self_value.__str__()
|
||||||
|
# logger.debug(
|
||||||
|
# f"Checking self_value {self_value} of type {type(self_value)} against attribute {value} of type {type(value)}")
|
||||||
|
# if self_value != value:
|
||||||
|
# output = False
|
||||||
|
# logger.debug(f"Value {key} is False, returning.")
|
||||||
|
# return output
|
||||||
|
# return True
|
||||||
|
|
||||||
def check_all_attributes(self, attributes: dict) -> bool:
|
def check_all_attributes(self, attributes: dict) -> bool:
|
||||||
"""
|
|
||||||
Checks this instance against a dictionary of attributes to determine if they are a match.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
attributes (dict): A dictionary of attributes to be check for equivalence
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: If a single unequivocal value is found will be false, else true.
|
|
||||||
"""
|
|
||||||
logger.debug(f"Incoming attributes: {attributes}")
|
logger.debug(f"Incoming attributes: {attributes}")
|
||||||
|
attributes = {k : v for k, v in attributes.items() if k in self.list_searchables.keys()}
|
||||||
for key, value in attributes.items():
|
for key, value in attributes.items():
|
||||||
if value.lower() == "none":
|
|
||||||
value = None
|
|
||||||
logger.debug(f"Attempting to grab attribute: {key}")
|
|
||||||
self_value = getattr(self, key)
|
|
||||||
class_attr = getattr(self.class_object, key)
|
|
||||||
# logger.debug(f"Self value: {self_value}, class attr: {class_attr} of type: {type(class_attr)}")
|
|
||||||
if isinstance(class_attr, property):
|
|
||||||
filter = "property"
|
|
||||||
else:
|
|
||||||
filter = class_attr.property
|
|
||||||
match filter:
|
|
||||||
case ColumnProperty():
|
|
||||||
match class_attr.type:
|
|
||||||
case INTEGER():
|
|
||||||
if value.lower() == "true":
|
|
||||||
value = 1
|
|
||||||
elif value.lower() == "false":
|
|
||||||
value = 0
|
|
||||||
else:
|
|
||||||
value = int(value)
|
|
||||||
case FLOAT():
|
|
||||||
value = float(value)
|
|
||||||
case "property":
|
|
||||||
pass
|
|
||||||
case _RelationshipDeclared():
|
|
||||||
logger.debug(f"Checking {self_value}")
|
|
||||||
try:
|
|
||||||
self_value = self_value.name
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
if class_attr.property.uselist:
|
|
||||||
self_value = self_value.__str__()
|
|
||||||
try:
|
try:
|
||||||
logger.debug(f"Check if {self_value.__class__} is subclass of {self.__class__}")
|
logger.debug(f"Check if {value.__class__} is subclass of {BaseOmni}")
|
||||||
check = issubclass(self_value.__class__, self.__class__)
|
check = issubclass(value.__class__, BaseOmni)
|
||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
logger.error(f"Couldn't check if {self_value.__class__} is subclass of {self.__class__} due to {e}")
|
logger.error(f"Couldn't check if {value.__class__} is subclass of {BaseOmni} due to {e}")
|
||||||
check = False
|
check = False
|
||||||
if check:
|
if check:
|
||||||
logger.debug(f"Checking for subclass name.")
|
logger.debug(f"Checking for subclass name.")
|
||||||
self_value = self_value.name
|
value = value.name
|
||||||
logger.debug(
|
self_value = self.list_searchables[key]
|
||||||
f"Checking self_value {self_value} of type {type(self_value)} against attribute {value} of type {type(value)}")
|
if value != self_value:
|
||||||
if self_value != value:
|
logger.debug(f"Value {key} is False, these are not the same object.")
|
||||||
output = False
|
return False
|
||||||
logger.debug(f"Value {key} is False, returning.")
|
logger.debug("Everything checks out, these are the same object.")
|
||||||
return output
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def __setattr__(self, key, value):
|
def __setattr__(self, key, value):
|
||||||
try:
|
try:
|
||||||
class_value = getattr(self.class_object, key)
|
class_value = getattr(self.class_object, key)
|
||||||
@@ -176,6 +215,13 @@ class OmniSubmissionType(BaseOmni):
|
|||||||
return {}
|
return {}
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
@field_validator("template_file", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def provide_blank_template_file(cls, value):
|
||||||
|
if value is None:
|
||||||
|
value = bytes()
|
||||||
|
return value
|
||||||
|
|
||||||
def __init__(self, instance_object: Any, **data):
|
def __init__(self, instance_object: Any, **data):
|
||||||
super().__init__(**data)
|
super().__init__(**data)
|
||||||
self.instance_object = instance_object
|
self.instance_object = instance_object
|
||||||
@@ -296,7 +342,6 @@ class OmniSubmissionTypeKitTypeAssociation(BaseOmni):
|
|||||||
constant_cost=self.constant_cost
|
constant_cost=self.constant_cost
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_sql(self):
|
def to_sql(self):
|
||||||
logger.debug(f"Self kittype: {self.submissiontype}")
|
logger.debug(f"Self kittype: {self.submissiontype}")
|
||||||
if issubclass(self.submissiontype.__class__, BaseOmni):
|
if issubclass(self.submissiontype.__class__, BaseOmni):
|
||||||
@@ -315,6 +360,18 @@ class OmniSubmissionTypeKitTypeAssociation(BaseOmni):
|
|||||||
instance.constant_cost = self.constant_cost
|
instance.constant_cost = self.constant_cost
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
@property
|
||||||
|
def list_searchables(self):
|
||||||
|
if isinstance(self.kittype, OmniKitType):
|
||||||
|
kit = self.kittype.name
|
||||||
|
else:
|
||||||
|
kit = self.kittype
|
||||||
|
if isinstance(self.submissiontype, OmniSubmissionType):
|
||||||
|
subtype = self.submissiontype.name
|
||||||
|
else:
|
||||||
|
subtype = self.submissiontype
|
||||||
|
return dict(kittype=kit, submissiontype=subtype)
|
||||||
|
|
||||||
|
|
||||||
class OmniKitTypeReagentRoleAssociation(BaseOmni):
|
class OmniKitTypeReagentRoleAssociation(BaseOmni):
|
||||||
class_object: ClassVar[Any] = KitTypeReagentRoleAssociation
|
class_object: ClassVar[Any] = KitTypeReagentRoleAssociation
|
||||||
@@ -325,6 +382,11 @@ class OmniKitTypeReagentRoleAssociation(BaseOmni):
|
|||||||
submission_type: str | OmniSubmissionType = Field(default="", description="relationship", title="SubmissionType")
|
submission_type: str | OmniSubmissionType = Field(default="", description="relationship", title="SubmissionType")
|
||||||
kit_type: str | OmniKitType = Field(default="", description="relationship", title="KitType")
|
kit_type: str | OmniKitType = Field(default="", description="relationship", title="KitType")
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
try:
|
||||||
|
return f"<OmniKitTypeReagentRoleAssociation({self.kit_type.name}&{self.reagent_role.name})>"
|
||||||
|
except AttributeError:
|
||||||
|
return f"<OmniKitTypeReagentRoleAssociation(NO NAME)>"
|
||||||
|
|
||||||
@field_validator("uses", mode="before")
|
@field_validator("uses", mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -362,19 +424,46 @@ class OmniKitTypeReagentRoleAssociation(BaseOmni):
|
|||||||
reagent_role = self.reagent_role.name
|
reagent_role = self.reagent_role.name
|
||||||
else:
|
else:
|
||||||
reagent_role = self.reagent_role
|
reagent_role = self.reagent_role
|
||||||
|
if issubclass(self.submission_type.__class__, BaseOmni):
|
||||||
|
submissiontype = self.submission_type.name
|
||||||
|
else:
|
||||||
|
submissiontype = self.submission_type
|
||||||
|
if issubclass(self.kit_type.__class__, BaseOmni):
|
||||||
|
kittype = self.kit_type.name
|
||||||
|
else:
|
||||||
|
kittype = self.kit_type
|
||||||
instance, new = self.class_object.query_or_create(
|
instance, new = self.class_object.query_or_create(
|
||||||
reagentrole=reagent_role,
|
reagentrole=reagent_role,
|
||||||
kittype=self.kit_type,
|
kittype=kittype,
|
||||||
submissiontype=self.submission_type
|
submissiontype=submissiontype
|
||||||
)
|
)
|
||||||
|
logger.debug(f"KitTypeReagentRoleAssociation coming out of query_or_create: {instance.__dict__}\nnew: {new}")
|
||||||
if new:
|
if new:
|
||||||
|
logger.warning(f"This is a new instance: {instance.__dict__}")
|
||||||
reagent_role = self.reagent_role.to_sql()
|
reagent_role = self.reagent_role.to_sql()
|
||||||
instance.reagent_role = reagent_role
|
instance.reagent_role = reagent_role
|
||||||
logger.debug(f"KTRRAssoc uses: {self.uses}")
|
logger.debug(f"KTRRAssoc uses: {self.uses}")
|
||||||
instance.uses = self.uses
|
instance.uses = self.uses
|
||||||
|
instance.required = int(self.required)
|
||||||
logger.debug(f"KitTypeReagentRoleAssociation: {pformat(instance.__dict__)}")
|
logger.debug(f"KitTypeReagentRoleAssociation: {pformat(instance.__dict__)}")
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
@property
|
||||||
|
def list_searchables(self):
|
||||||
|
if isinstance(self.kit_type, OmniKitType):
|
||||||
|
kit = self.kit_type.name
|
||||||
|
else:
|
||||||
|
kit = self.kit_type
|
||||||
|
if isinstance(self.submission_type, OmniSubmissionType):
|
||||||
|
subtype = self.submission_type.name
|
||||||
|
else:
|
||||||
|
subtype = self.submission_type
|
||||||
|
if isinstance(self.reagent_role, OmniReagentRole):
|
||||||
|
reagentrole = self.reagent_role.name
|
||||||
|
else:
|
||||||
|
reagentrole = self.reagent_role
|
||||||
|
return dict(kit_type=kit, submission_type=subtype, reagent_role=reagentrole)
|
||||||
|
|
||||||
|
|
||||||
class OmniEquipmentRole(BaseOmni):
|
class OmniEquipmentRole(BaseOmni):
|
||||||
class_object: ClassVar[Any] = EquipmentRole
|
class_object: ClassVar[Any] = EquipmentRole
|
||||||
@@ -463,6 +552,7 @@ class OmniProcess(BaseOmni):
|
|||||||
|
|
||||||
# NOTE: How am I going to figure out relatioinships without getting into recursion issues?
|
# NOTE: How am I going to figure out relatioinships without getting into recursion issues?
|
||||||
name: str = Field(default="", description="property") #: Process name
|
name: str = Field(default="", description="property") #: Process name
|
||||||
|
# version: str = Field(default="", description="property") #: Version (string to account for "in_use" or whatever)
|
||||||
submission_types: List[OmniSubmissionType] | List[str] = Field(default=[], description="relationship",
|
submission_types: List[OmniSubmissionType] | List[str] = Field(default=[], description="relationship",
|
||||||
title="SubmissionType")
|
title="SubmissionType")
|
||||||
equipment_roles: List[OmniEquipmentRole] | List[str] = Field(default=[], description="relationship",
|
equipment_roles: List[OmniEquipmentRole] | List[str] = Field(default=[], description="relationship",
|
||||||
@@ -491,6 +581,13 @@ class OmniProcess(BaseOmni):
|
|||||||
return ""
|
return ""
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
# @field_validator("version", mode="before")
|
||||||
|
# @classmethod
|
||||||
|
# def rescue_name_none(cls, value):
|
||||||
|
# if not value:
|
||||||
|
# return "1"
|
||||||
|
# return value
|
||||||
|
|
||||||
def to_sql(self):
|
def to_sql(self):
|
||||||
instance, new = self.class_object.query_or_create(name=self.name)
|
instance, new = self.class_object.query_or_create(name=self.name)
|
||||||
for st in self.submission_types:
|
for st in self.submission_types:
|
||||||
@@ -507,17 +604,21 @@ class OmniProcess(BaseOmni):
|
|||||||
instance.tip_roles.append(new_assoc)
|
instance.tip_roles.append(new_assoc)
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
@property
|
||||||
|
def list_searchables(self):
|
||||||
|
return dict(name=self.name)
|
||||||
|
|
||||||
|
|
||||||
class OmniKitType(BaseOmni):
|
class OmniKitType(BaseOmni):
|
||||||
class_object: ClassVar[Any] = KitType
|
class_object: ClassVar[Any] = KitType
|
||||||
|
|
||||||
name: str = Field(default="", description="property")
|
name: str = Field(default="", description="property")
|
||||||
kit_submissiontype_associations: List[OmniSubmissionTypeKitTypeAssociation] | List[str] = Field(default=[],
|
kit_submissiontype_associations: List[OmniSubmissionTypeKitTypeAssociation] | List[str] = Field(default=[],
|
||||||
description="relationship",
|
description="relationship",
|
||||||
title="SubmissionTypeKitTypeAssociation")
|
title="SubmissionTypeKitTypeAssociation")
|
||||||
kit_reagentrole_associations: List[OmniKitTypeReagentRoleAssociation] | List[str] = Field(default=[],
|
kit_reagentrole_associations: List[OmniKitTypeReagentRoleAssociation] | List[str] = Field(default=[],
|
||||||
description="relationship",
|
description="relationship",
|
||||||
title="KitTypeReagentRoleAssociation")
|
title="KitTypeReagentRoleAssociation")
|
||||||
processes: List[OmniProcess] | List[str] = Field(default=[], description="relationship", title="Process")
|
processes: List[OmniProcess] | List[str] = Field(default=[], description="relationship", title="Process")
|
||||||
|
|
||||||
@field_validator("name", mode="before")
|
@field_validator("name", mode="before")
|
||||||
@@ -548,8 +649,9 @@ class OmniKitType(BaseOmni):
|
|||||||
if new_assoc not in new_rr:
|
if new_assoc not in new_rr:
|
||||||
logger.debug(f"Adding {new_assoc} to kit_reagentrole_associations")
|
logger.debug(f"Adding {new_assoc} to kit_reagentrole_associations")
|
||||||
new_rr.append(new_assoc)
|
new_rr.append(new_assoc)
|
||||||
logger.debug(f"Setting kit_reagentrole_associations to {new_rr}")
|
logger.debug(f"Setting kit_reagentrole_associations to {pformat([item.__dict__ for item in new_rr])}")
|
||||||
kit.kit_reagentrole_associations = new_rr
|
kit.kit_reagentrole_associations = new_rr
|
||||||
|
# sys.exit()
|
||||||
new_st = []
|
new_st = []
|
||||||
for st_assoc in self.kit_submissiontype_associations:
|
for st_assoc in self.kit_submissiontype_associations:
|
||||||
new_assoc = st_assoc.to_sql()
|
new_assoc = st_assoc.to_sql()
|
||||||
|
|||||||
@@ -1244,6 +1244,7 @@ class PydIridaControl(BaseModel, extra='ignore'):
|
|||||||
|
|
||||||
class PydProcess(BaseModel, extra="allow"):
|
class PydProcess(BaseModel, extra="allow"):
|
||||||
name: str
|
name: str
|
||||||
|
version: str = Field(default="1")
|
||||||
submission_types: List[str]
|
submission_types: List[str]
|
||||||
equipment: List[str]
|
equipment: List[str]
|
||||||
equipment_roles: List[str]
|
equipment_roles: List[str]
|
||||||
|
|||||||
@@ -19,10 +19,6 @@ class TurnaroundChart(CustomFigure):
|
|||||||
months: int = 6):
|
months: int = 6):
|
||||||
super().__init__(df=df, modes=modes, settings=settings)
|
super().__init__(df=df, modes=modes, settings=settings)
|
||||||
self.df = df
|
self.df = df
|
||||||
# try:
|
|
||||||
# months = int(settings['months'])
|
|
||||||
# except KeyError:
|
|
||||||
# months = 6
|
|
||||||
self.construct_chart()
|
self.construct_chart()
|
||||||
if threshold:
|
if threshold:
|
||||||
self.add_hline(y=threshold)
|
self.add_hline(y=threshold)
|
||||||
@@ -31,20 +27,26 @@ class TurnaroundChart(CustomFigure):
|
|||||||
def construct_chart(self, df: pd.DataFrame | None = None):
|
def construct_chart(self, df: pd.DataFrame | None = None):
|
||||||
if df:
|
if df:
|
||||||
self.df = df
|
self.df = df
|
||||||
self.df = self.df[self.df.days.notnull()]
|
|
||||||
self.df = self.df.sort_values(['submitted_date', 'name'], ascending=[True, True]).reset_index(drop=True)
|
|
||||||
self.df = self.df.reset_index().rename(columns={"index": "idx"})
|
|
||||||
try:
|
try:
|
||||||
|
self.df = self.df[self.df.days.notnull()]
|
||||||
|
self.df = self.df.sort_values(['submitted_date', 'name'], ascending=[True, True]).reset_index(drop=True)
|
||||||
|
self.df = self.df.reset_index().rename(columns={"index": "idx"})
|
||||||
scatter = px.scatter(data_frame=self.df, x='idx', y="days",
|
scatter = px.scatter(data_frame=self.df, x='idx', y="days",
|
||||||
hover_data=["name", "submitted_date", "completed_date", "days"],
|
hover_data=["name", "submitted_date", "completed_date", "days"],
|
||||||
color="acceptable", color_discrete_map={True: "green", False: "red"}
|
color="acceptable", color_discrete_map={True: "green", False: "red"}
|
||||||
)
|
)
|
||||||
except ValueError:
|
except (ValueError, AttributeError):
|
||||||
scatter = px.scatter()
|
scatter = px.scatter()
|
||||||
self.add_traces(scatter.data)
|
self.add_traces(scatter.data)
|
||||||
self.update_traces(marker={'size': 15})
|
self.update_traces(marker={'size': 15})
|
||||||
tickvals = self.df['idx'].tolist()
|
try:
|
||||||
ticklabels = self.df['name'].tolist()
|
tickvals = self.df['idx'].tolist()
|
||||||
|
except KeyError:
|
||||||
|
tickvals = []
|
||||||
|
try:
|
||||||
|
ticklabels = self.df['name'].tolist()
|
||||||
|
except KeyError:
|
||||||
|
ticklabels = []
|
||||||
self.update_layout(
|
self.update_layout(
|
||||||
xaxis=dict(
|
xaxis=dict(
|
||||||
tickmode='array',
|
tickmode='array',
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class ManagerWindow(QDialog):
|
|||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
# NOTE: Should I pass in an instance?
|
# NOTE: Should I pass in an instance?
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
logger.debug(f"Setting instance: {self.instance}")
|
# logger.debug(f"Setting instance: {self.instance}")
|
||||||
if not self.instance:
|
if not self.instance:
|
||||||
self.class_object = self.original_type = object_type
|
self.class_object = self.original_type = object_type
|
||||||
else:
|
else:
|
||||||
@@ -131,7 +131,7 @@ class ManagerWindow(QDialog):
|
|||||||
self.omni_object = self.instance.to_omni(expand=True)
|
self.omni_object = self.instance.to_omni(expand=True)
|
||||||
else:
|
else:
|
||||||
self.omni_object = self.instance
|
self.omni_object = self.instance
|
||||||
logger.debug(f"Created omni_object: {self.omni_object.__dict__}")
|
# logger.debug(f"Created omni_object: {self.omni_object.__dict__}")
|
||||||
self.update_data()
|
self.update_data()
|
||||||
|
|
||||||
def update_data(self) -> None:
|
def update_data(self) -> None:
|
||||||
@@ -154,6 +154,7 @@ class ManagerWindow(QDialog):
|
|||||||
value = getattr(self.omni_object, key)
|
value = getattr(self.omni_object, key)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
value = None
|
value = None
|
||||||
|
# logger.debug(f"Got value {value} for key {key}")
|
||||||
match info.description:
|
match info.description:
|
||||||
# NOTE: ColumnProperties will be directly edited.
|
# NOTE: ColumnProperties will be directly edited.
|
||||||
case "property":
|
case "property":
|
||||||
@@ -163,7 +164,11 @@ class ManagerWindow(QDialog):
|
|||||||
# NOTE: RelationshipDeclareds will be given a list of existing related objects.
|
# NOTE: RelationshipDeclareds will be given a list of existing related objects.
|
||||||
case "relationship":
|
case "relationship":
|
||||||
# NOTE: field.comparator.class_object.class_ gives the relationship class
|
# NOTE: field.comparator.class_object.class_ gives the relationship class
|
||||||
# logger.debug(f"Creating relationship widget with value: {value}")
|
# try:
|
||||||
|
# logger.debug(
|
||||||
|
# f"Creating relationship widget with value: {[pformat(item.__dict__) for item in value]}")
|
||||||
|
# except AttributeError:
|
||||||
|
# logger.debug(f"Creating relationship widget with value: {value}")
|
||||||
widget = EditRelationship(self, key=key, class_object=info.title, value=value)
|
widget = EditRelationship(self, key=key, class_object=info.title, value=value)
|
||||||
case _:
|
case _:
|
||||||
continue
|
continue
|
||||||
@@ -182,13 +187,13 @@ class ManagerWindow(QDialog):
|
|||||||
# TODO: Need Relationship property here too?
|
# TODO: Need Relationship property here too?
|
||||||
results = [item.parse_form() for item in self.findChildren(EditProperty)]
|
results = [item.parse_form() for item in self.findChildren(EditProperty)]
|
||||||
for result in results:
|
for result in results:
|
||||||
logger.debug(f"Incoming property result: {result}")
|
# logger.debug(f"Incoming property result: {result}")
|
||||||
setattr(self.omni_object, result['field'], result['value'])
|
setattr(self.omni_object, result['field'], result['value'])
|
||||||
# NOTE: Getting 'None' back here.
|
# NOTE: Getting 'None' back here.
|
||||||
logger.debug(f"Set result: {getattr(self.instance, result['field'])}")
|
# logger.debug(f"Set result: {getattr(self.instance, result['field'])}")
|
||||||
results = [item.parse_form() for item in self.findChildren(EditRelationship)]
|
results = [item.parse_form() for item in self.findChildren(EditRelationship)]
|
||||||
for result in results:
|
for result in results:
|
||||||
logger.debug(f"Incoming relationship result: {result}")
|
# logger.debug(f"Incoming relationship result: {result}")
|
||||||
setattr(self.omni_object, result['field'], result['value'])
|
setattr(self.omni_object, result['field'], result['value'])
|
||||||
# logger.debug(f"Set result: {getattr(self.omni_object, result['field'])}")
|
# logger.debug(f"Set result: {getattr(self.omni_object, result['field'])}")
|
||||||
# logger.debug(f"Instance coming from parsed form: {self.omni_object.__dict__}")
|
# logger.debug(f"Instance coming from parsed form: {self.omni_object.__dict__}")
|
||||||
@@ -270,12 +275,12 @@ class EditRelationship(QWidget):
|
|||||||
from backend.db import models
|
from backend.db import models
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.class_object = getattr(models, class_object)
|
self.class_object = getattr(models, class_object)
|
||||||
logger.debug(f"Class object: {self.class_object}")
|
# logger.debug(f"Class object: {self.class_object}")
|
||||||
self.setParent(parent)
|
self.setParent(parent)
|
||||||
# logger.debug(f"Edit relationship class_object: {self.class_object}")
|
# logger.debug(f"Edit relationship class_object: {self.class_object}")
|
||||||
self.label = QLabel(key.title().replace("_", " "))
|
self.label = QLabel(key.title().replace("_", " "))
|
||||||
self.setObjectName(key) #: key is the name of the relationship this represents
|
self.setObjectName(key) #: key is the name of the relationship this represents
|
||||||
logger.debug(f"Checking relationship for {self.parent().class_object}: {key}")
|
# logger.debug(f"Checking relationship for {self.parent().class_object}: {key}")
|
||||||
self.relationship = getattr(self.parent().class_object, key)
|
self.relationship = getattr(self.parent().class_object, key)
|
||||||
self.widget = QTableView()
|
self.widget = QTableView()
|
||||||
self.add_button = QPushButton("Add New")
|
self.add_button = QPushButton("Add New")
|
||||||
@@ -288,15 +293,15 @@ class EditRelationship(QWidget):
|
|||||||
else:
|
else:
|
||||||
value = []
|
value = []
|
||||||
self.data = value
|
self.data = value
|
||||||
logger.debug(f"Set data: {self.data}")
|
# logger.debug(f"Set data: {self.data}")
|
||||||
# self.update_buttons()
|
# self.update_buttons()
|
||||||
logger.debug(f"Parent manager: {self.parent().manager}")
|
# logger.debug(f"Parent manager: {self.parent().manager}")
|
||||||
checked_manager, is_primary = check_object_in_manager(self.parent().manager, self.objectName())
|
checked_manager, is_primary = check_object_in_manager(self.parent().manager, self.objectName())
|
||||||
if checked_manager:
|
if checked_manager:
|
||||||
if not self.data:
|
if not self.data:
|
||||||
self.data = [checked_manager]
|
self.data = [checked_manager]
|
||||||
try:
|
try:
|
||||||
logger.debug(f"Relationship {key} uses list: {self.relationship.property.uselist}")
|
# logger.debug(f"Relationship {key} uses list: {self.relationship.property.uselist}")
|
||||||
check = not self.relationship.property.uselist and len(self.data) >= 1
|
check = not self.relationship.property.uselist and len(self.data) >= 1
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
check = True
|
check = True
|
||||||
@@ -336,9 +341,9 @@ class EditRelationship(QWidget):
|
|||||||
self.widget.doubleClicked.disconnect()
|
self.widget.doubleClicked.disconnect()
|
||||||
self.add_edit(instance=object)
|
self.add_edit(instance=object)
|
||||||
|
|
||||||
def add_new(self, instance: Any = None, add_edit: Literal["add", "edit"] = "add"):
|
def add_new(self, instance: Any = None, add_edit: Literal["add", "edit"] = "add", index: int | None = None):
|
||||||
if add_edit == "edit":
|
if add_edit == "edit":
|
||||||
logger.debug(f"Editing instance: {instance.__dict__}")
|
logger.debug(f"\n\nEditing instance: {instance.__dict__}\n\n")
|
||||||
# NOTE: if an existing instance is not being edited, create a new instance
|
# NOTE: if an existing instance is not being edited, create a new instance
|
||||||
if not instance:
|
if not instance:
|
||||||
# logger.debug(f"Creating new instance of {self.class_object}")
|
# logger.debug(f"Creating new instance of {self.class_object}")
|
||||||
@@ -352,10 +357,27 @@ class EditRelationship(QWidget):
|
|||||||
logger.debug(f"New instance: {pformat(new_instance.__dict__)}")
|
logger.debug(f"New instance: {pformat(new_instance.__dict__)}")
|
||||||
# NOTE: Somewhere between this and the next logger, I'm losing the uses data.
|
# NOTE: Somewhere between this and the next logger, I'm losing the uses data.
|
||||||
if add_edit == "add":
|
if add_edit == "add":
|
||||||
|
logger.debug("Setting as new object")
|
||||||
self.parent().omni_object.__setattr__(self.objectName(), new_instance)
|
self.parent().omni_object.__setattr__(self.objectName(), new_instance)
|
||||||
else:
|
else:
|
||||||
instance.__dict__.update(new_instance.__dict__)
|
logger.debug("Updating dictionary")
|
||||||
logger.debug(f"Final instance: {pformat(instance.__dict__)}")
|
obj = getattr(self.parent().omni_object, self.objectName())
|
||||||
|
if isinstance(obj, list):
|
||||||
|
logger.debug(f"This is a list")
|
||||||
|
# obj = obj[index]
|
||||||
|
try:
|
||||||
|
# NOTE: Okay, this will not work for editing, since by definition not all attributes will line up.
|
||||||
|
# NOTE: Set items to search by in the Omni object itself?
|
||||||
|
obj = next((item for item in obj if item.check_all_attributes(new_instance.__dict__)))
|
||||||
|
except StopIteration:
|
||||||
|
logger.error(f"Couldn't find object in list.")
|
||||||
|
return
|
||||||
|
logger.debug(f"Updating \n{pformat(obj)} with \n{pformat(new_instance.__dict__)}")
|
||||||
|
obj.__dict__.update(new_instance.__dict__)
|
||||||
|
# # self.parent().omni_object.__setattr__(self.objectName(), obj)
|
||||||
|
# # instance.__dict__.update(new_instance.__dict__)
|
||||||
|
logger.debug(f"Final instance: {pformat(self.parent().omni_object.__dict__)}")
|
||||||
|
# NOTE: somewhere in the update_data I'm losing changes.
|
||||||
self.parent().update_data()
|
self.parent().update_data()
|
||||||
|
|
||||||
def add_existing(self):
|
def add_existing(self):
|
||||||
@@ -415,9 +437,9 @@ class EditRelationship(QWidget):
|
|||||||
row_data = {self.df.columns[column]: self.widget.model().index(id.row(), column).data() for column in
|
row_data = {self.df.columns[column]: self.widget.model().index(id.row(), column).data() for column in
|
||||||
range(self.widget.model().columnCount())}
|
range(self.widget.model().columnCount())}
|
||||||
# logger.debug(f"Row data: {row_data}")
|
# logger.debug(f"Row data: {row_data}")
|
||||||
logger.debug(f"Attempting to grab {self.objectName()} from {self.parent().omni_object}")
|
# logger.debug(f"Attempting to grab {self.objectName()} from {self.parent().omni_object}")
|
||||||
object = getattr(self.parent().omni_object, self.objectName())
|
object = getattr(self.parent().omni_object, self.objectName())
|
||||||
logger.debug(f"Initial object: {object}")
|
# logger.debug(f"Initial object: {object}")
|
||||||
if isinstance(object, list):
|
if isinstance(object, list):
|
||||||
try:
|
try:
|
||||||
object = next((item for item in object if item.check_all_attributes(attributes=row_data)))
|
object = next((item for item in object if item.check_all_attributes(attributes=row_data)))
|
||||||
@@ -437,14 +459,15 @@ class EditRelationship(QWidget):
|
|||||||
edit_action = QAction(f"Edit {object.name}", self)
|
edit_action = QAction(f"Edit {object.name}", self)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
edit_action = QAction(f"Edit object", self)
|
edit_action = QAction(f"Edit object", self)
|
||||||
edit_action.triggered.connect(lambda: self.add_new(instance=object.instance_object, add_edit="edit"))
|
edit_action.triggered.connect(
|
||||||
|
lambda: self.add_new(instance=object.instance_object, add_edit="edit", index=id.row()))
|
||||||
self.menu.addAction(edit_action)
|
self.menu.addAction(edit_action)
|
||||||
self.menu.popup(QCursor.pos())
|
self.menu.popup(QCursor.pos())
|
||||||
|
|
||||||
def remove_item(self, object):
|
def remove_item(self, object):
|
||||||
logger.debug(f"Attempting to remove {object} from {self.parent().instance.__dict__}")
|
# logger.debug(f"Attempting to remove {object} from {self.parent().instance.__dict__}")
|
||||||
editor = getattr(self.parent().omni_object, self.objectName().lower())
|
editor = getattr(self.parent().omni_object, self.objectName().lower())
|
||||||
logger.debug(f"Editor: {editor}")
|
# logger.debug(f"Editor: {editor}")
|
||||||
try:
|
try:
|
||||||
# logger.debug(f"Using remove technique")
|
# logger.debug(f"Using remove technique")
|
||||||
editor.remove(object)
|
editor.remove(object)
|
||||||
@@ -453,9 +476,9 @@ class EditRelationship(QWidget):
|
|||||||
setattr(self.parent().omni_object, self.objectName().lower(), None)
|
setattr(self.parent().omni_object, self.objectName().lower(), None)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logger.error(f"Remove failed for {self.objectName().lower()} due to {e}.")
|
logger.error(f"Remove failed for {self.objectName().lower()} due to {e}.")
|
||||||
logger.debug(f"Setting {self.objectName()} to {editor}")
|
# logger.debug(f"Setting {self.objectName()} to {editor}")
|
||||||
setattr(self.parent().omni_object, self.objectName().lower(), editor)
|
setattr(self.parent().omni_object, self.objectName().lower(), editor)
|
||||||
logger.debug(f"After set: {getattr(self.parent().omni_object, self.objectName().lower())}")
|
# logger.debug(f"After set: {getattr(self.parent().omni_object, self.objectName().lower())}")
|
||||||
self.set_data()
|
self.set_data()
|
||||||
self.update_buttons()
|
self.update_buttons()
|
||||||
|
|
||||||
@@ -466,7 +489,10 @@ class EditRelationship(QWidget):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
check = False
|
check = False
|
||||||
if check and isinstance(self.data, list):
|
if check and isinstance(self.data, list):
|
||||||
output_data = self.data[0]
|
try:
|
||||||
|
output_data = self.data[0]
|
||||||
|
except IndexError:
|
||||||
|
output_data = []
|
||||||
else:
|
else:
|
||||||
output_data = self.data
|
output_data = self.data
|
||||||
return dict(field=self.objectName(), value=output_data)
|
return dict(field=self.objectName(), value=output_data)
|
||||||
@@ -476,7 +502,7 @@ class JsonEditButton(QWidget):
|
|||||||
|
|
||||||
def __init__(self, parent, key: str, value: str = ""):
|
def __init__(self, parent, key: str, value: str = ""):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
logger.debug(f"Setting jsonedit data to: {value}")
|
# logger.debug(f"Setting jsonedit data to: {value}")
|
||||||
self.data = value
|
self.data = value
|
||||||
self.setParent(parent)
|
self.setParent(parent)
|
||||||
self.setObjectName(key)
|
self.setObjectName(key)
|
||||||
@@ -495,7 +521,7 @@ class JsonEditButton(QWidget):
|
|||||||
self.edit_box.widget.textChanged.connect(self.set_json_to_text)
|
self.edit_box.widget.textChanged.connect(self.set_json_to_text)
|
||||||
|
|
||||||
def set_json_to_text(self):
|
def set_json_to_text(self):
|
||||||
logger.debug(self.edit_box.widget.toPlainText())
|
# logger.debug(self.edit_box.widget.toPlainText())
|
||||||
text = self.edit_box.widget.toPlainText()
|
text = self.edit_box.widget.toPlainText()
|
||||||
try:
|
try:
|
||||||
jsoner = json.loads(text)
|
jsoner = json.loads(text)
|
||||||
@@ -529,7 +555,11 @@ class JsonEditScreen(QDialog):
|
|||||||
try:
|
try:
|
||||||
self.json_field = getattr(self.class_obj, f"{parameter}_json_edit_fields")
|
self.json_field = getattr(self.class_obj, f"{parameter}_json_edit_fields")
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
self.json_field = self.class_obj.json_edit_fields
|
try:
|
||||||
|
self.json_field = self.class_obj.json_edit_fields
|
||||||
|
except AttributeError:
|
||||||
|
logger.error(f"No json fields to edit.")
|
||||||
|
return
|
||||||
match self.json_field:
|
match self.json_field:
|
||||||
case dict():
|
case dict():
|
||||||
for key, value in self.json_field.items():
|
for key, value in self.json_field.items():
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ class SearchBox(QDialog):
|
|||||||
Changes form inputs based on sample type
|
Changes form inputs based on sample type
|
||||||
"""
|
"""
|
||||||
search_fields = []
|
search_fields = []
|
||||||
|
# search_fields = self.object_type.searchables
|
||||||
logger.debug(f"Search fields: {search_fields}")
|
logger.debug(f"Search fields: {search_fields}")
|
||||||
deletes = [item for item in self.findChildren(FieldSearch)]
|
deletes = [item for item in self.findChildren(FieldSearch)]
|
||||||
for item in deletes:
|
for item in deletes:
|
||||||
@@ -68,7 +69,7 @@ class SearchBox(QDialog):
|
|||||||
if not self.sub_class:
|
if not self.sub_class:
|
||||||
logger.warning(f"No subclass selected.")
|
logger.warning(f"No subclass selected.")
|
||||||
self.update_data()
|
self.update_data()
|
||||||
return
|
# return
|
||||||
else:
|
else:
|
||||||
if self.sub_class.currentText() == "Any":
|
if self.sub_class.currentText() == "Any":
|
||||||
self.object_type = self.original_type
|
self.object_type = self.original_type
|
||||||
|
|||||||
Reference in New Issue
Block a user