Pydantic switchover debugging.
This commit is contained in:
@@ -52,7 +52,12 @@ def import_irida(ctx: Settings):
|
|||||||
sample = new_session.query(BasicSample).filter(BasicSample.submitter_id == instance.name).first()
|
sample = new_session.query(BasicSample).filter(BasicSample.submitter_id == instance.name).first()
|
||||||
if sample:
|
if sample:
|
||||||
instance.sample = sample
|
instance.sample = sample
|
||||||
instance.submission = sample.submissions[0]
|
try:
|
||||||
|
instance.submission = sample.submissions[0]
|
||||||
|
except IndexError:
|
||||||
|
logger.error(f"Could not get sample for {sample}")
|
||||||
|
instance.submission = None
|
||||||
|
# instance.submission = sample.submission[0]
|
||||||
new_session.add(instance)
|
new_session.add(instance)
|
||||||
new_session.commit()
|
new_session.commit()
|
||||||
new_session.close()
|
new_session.close()
|
||||||
|
|||||||
@@ -397,8 +397,8 @@ class BaseClass(Base):
|
|||||||
"""
|
"""
|
||||||
Custom dunder method to handle potential list relationship issues.
|
Custom dunder method to handle potential list relationship issues.
|
||||||
"""
|
"""
|
||||||
if key != "_sa_instance_state":
|
# if key != "_sa_instance_state":
|
||||||
logger.debug(f"Attempting to set {key} to {pformat(value)}")
|
# logger.debug(f"Attempting to set {key} to {pformat(value)}")
|
||||||
try:
|
try:
|
||||||
field_type = getattr(self.__class__, key)
|
field_type = getattr(self.__class__, key)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@@ -407,21 +407,25 @@ class BaseClass(Base):
|
|||||||
logger.debug(f"{key} is an InstrumentedAttribute.")
|
logger.debug(f"{key} is an InstrumentedAttribute.")
|
||||||
match field_type.property:
|
match field_type.property:
|
||||||
case ColumnProperty():
|
case ColumnProperty():
|
||||||
logger.debug(f"Setting ColumnProperty to {value}")
|
# logger.debug(f"Setting ColumnProperty to {value}")
|
||||||
return super().__setattr__(key, value)
|
return super().__setattr__(key, value)
|
||||||
case _RelationshipDeclared():
|
case _RelationshipDeclared():
|
||||||
logger.debug(f"Setting _RelationshipDeclared to {value}")
|
logger.debug(f"{self.__class__.__name__} Setting _RelationshipDeclared for {key} to {value}")
|
||||||
if field_type.property.uselist:
|
if field_type.property.uselist:
|
||||||
logger.debug(f"Setting with uselist")
|
logger.debug(f"Setting with uselist")
|
||||||
existing = self.__getattribute__(key)
|
existing = self.__getattribute__(key)
|
||||||
|
# NOTE: This is causing problems with removal of items from lists. Have to overhaul it.
|
||||||
if existing is not None:
|
if existing is not None:
|
||||||
|
logger.debug(f"{key} Existing: {existing}, incoming: {value}")
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
value = existing + value
|
# value = existing + value
|
||||||
|
value = value
|
||||||
else:
|
else:
|
||||||
value = existing + [value]
|
value = existing + [value]
|
||||||
else:
|
else:
|
||||||
value = [value]
|
value = [value]
|
||||||
value = list(set(value))
|
value = list(set(value))
|
||||||
|
logger.debug(f"Final value for {key}: {value}")
|
||||||
return super().__setattr__(key, value)
|
return super().__setattr__(key, value)
|
||||||
else:
|
else:
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
@@ -429,6 +433,7 @@ class BaseClass(Base):
|
|||||||
value = value[0]
|
value = value[0]
|
||||||
else:
|
else:
|
||||||
raise ValueError("Object is too long to parse a single value.")
|
raise ValueError("Object is too long to parse a single value.")
|
||||||
|
# value = value
|
||||||
return super().__setattr__(key, value)
|
return super().__setattr__(key, value)
|
||||||
case _:
|
case _:
|
||||||
return super().__setattr__(key, value)
|
return super().__setattr__(key, value)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ All kit and reagent related models
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import json, zipfile, yaml, logging, re
|
import json, zipfile, yaml, logging, re
|
||||||
|
import sys
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from sqlalchemy import Column, String, TIMESTAMP, JSON, INTEGER, ForeignKey, Interval, Table, FLOAT, BLOB
|
from sqlalchemy import Column, String, TIMESTAMP, JSON, INTEGER, ForeignKey, Interval, Table, FLOAT, BLOB
|
||||||
from sqlalchemy.orm import relationship, validates, Query
|
from sqlalchemy.orm import relationship, validates, Query
|
||||||
@@ -231,6 +232,7 @@ class KitType(BaseClass):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def query_or_create(cls, **kwargs) -> Tuple[KitType, bool]:
|
def query_or_create(cls, **kwargs) -> Tuple[KitType, bool]:
|
||||||
from backend.validators.pydant import PydKitType
|
from backend.validators.pydant import PydKitType
|
||||||
|
from backend.validators.omni_gui_objects import BaseOmni
|
||||||
new = False
|
new = False
|
||||||
disallowed = ['expiry']
|
disallowed = ['expiry']
|
||||||
sanitized_kwargs = {k: v for k, v in kwargs.items() if k not in disallowed}
|
sanitized_kwargs = {k: v for k, v in kwargs.items() if k not in disallowed}
|
||||||
@@ -1074,7 +1076,7 @@ class SubmissionType(BaseClass):
|
|||||||
new = True
|
new = True
|
||||||
for k, v in sanitized_kwargs.items():
|
for k, v in sanitized_kwargs.items():
|
||||||
setattr(instance, k, v)
|
setattr(instance, k, v)
|
||||||
logger.info(f"Instance from query or create: {instance}")
|
logger.info(f"Instance from submissiontype query or create: {instance}")
|
||||||
return instance, new
|
return instance, new
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -1298,7 +1300,7 @@ class SubmissionTypeKitTypeAssociation(BaseClass):
|
|||||||
new = True
|
new = True
|
||||||
for k, v in sanitized_kwargs.items():
|
for k, v in sanitized_kwargs.items():
|
||||||
setattr(instance, k, v)
|
setattr(instance, k, v)
|
||||||
logger.info(f"Instance from query or create: {instance}")
|
logger.info(f"Instance from SubmissionTypeKitTypeAssociation query or create: {instance}")
|
||||||
return instance, new
|
return instance, new
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -1428,6 +1430,22 @@ class KitTypeReagentRoleAssociation(BaseClass):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
return "Blank KitTypeReagentRole"
|
return "Blank KitTypeReagentRole"
|
||||||
|
|
||||||
|
@hybrid_property
|
||||||
|
def submissiontype(self):
|
||||||
|
return self.submission_type
|
||||||
|
|
||||||
|
@submissiontype.setter
|
||||||
|
def submissiontype(self, value):
|
||||||
|
self.submission_type = value
|
||||||
|
|
||||||
|
@hybrid_property
|
||||||
|
def kittype(self):
|
||||||
|
return self.kit_type
|
||||||
|
|
||||||
|
@kittype.setter
|
||||||
|
def kittype(self, value):
|
||||||
|
self.kit_type = value
|
||||||
|
|
||||||
@validates('required')
|
@validates('required')
|
||||||
def validate_required(self, key, value):
|
def validate_required(self, key, value):
|
||||||
"""
|
"""
|
||||||
@@ -1478,8 +1496,31 @@ class KitTypeReagentRoleAssociation(BaseClass):
|
|||||||
instance = cls()
|
instance = cls()
|
||||||
new = True
|
new = True
|
||||||
for k, v in sanitized_kwargs.items():
|
for k, v in sanitized_kwargs.items():
|
||||||
|
logger.debug(f"Key: {k} has value: {v}")
|
||||||
|
match k:
|
||||||
|
case "kittype" | "kit_type":
|
||||||
|
k = "kit_type"
|
||||||
|
if isinstance(v, str):
|
||||||
|
v = KitType.query(name=v)
|
||||||
|
else:
|
||||||
|
v = v.instance_object
|
||||||
|
case "submissiontype" | "submission_type":
|
||||||
|
k = "submission_type"
|
||||||
|
if isinstance(v, str):
|
||||||
|
v = SubmissionType.query(name=v)
|
||||||
|
else:
|
||||||
|
v = v.instance_object
|
||||||
|
case "reagentrole" | "reagent_role":
|
||||||
|
k = "reagent_role"
|
||||||
|
if isinstance(v, str):
|
||||||
|
v = ReagentRole.query(name=v)
|
||||||
|
else:
|
||||||
|
v = v.instance_object
|
||||||
|
case _:
|
||||||
|
pass
|
||||||
setattr(instance, k, v)
|
setattr(instance, k, v)
|
||||||
logger.info(f"Instance from query or create: {instance}")
|
logger.info(f"Instance from query or create: {instance.__dict__}")
|
||||||
|
# sys.exit()
|
||||||
return instance, new
|
return instance, new
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -460,6 +460,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)}")
|
||||||
# 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),
|
||||||
@@ -1536,6 +1537,7 @@ class Wastewater(BasicSubmission):
|
|||||||
continue
|
continue
|
||||||
thing['tooltip'] = f"Sample Name: {thing['name']}\nWell: {thing['sample_location']}"
|
thing['tooltip'] = f"Sample Name: {thing['name']}\nWell: {thing['sample_location']}"
|
||||||
dummy_samples.append(thing)
|
dummy_samples.append(thing)
|
||||||
|
logger.debug(f"Dummy samples for 24 well: {pformat(dummy_samples)}")
|
||||||
output['origin_plate'] = self.__class__.make_plate_map(sample_list=dummy_samples, plate_rows=4,
|
output['origin_plate'] = self.__class__.make_plate_map(sample_list=dummy_samples, plate_rows=4,
|
||||||
plate_columns=6)
|
plate_columns=6)
|
||||||
# logger.debug(f"PCR info: {output['pcr_info']}")
|
# logger.debug(f"PCR info: {output['pcr_info']}")
|
||||||
|
|||||||
579
src/submissions/backend/validators/omni_gui_objects.py
Normal file
579
src/submissions/backend/validators/omni_gui_objects.py
Normal file
@@ -0,0 +1,579 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from pydantic import BaseModel, field_validator, Field
|
||||||
|
from typing import List, ClassVar
|
||||||
|
from backend.db.models import *
|
||||||
|
from sqlalchemy.orm.properties import ColumnProperty
|
||||||
|
from sqlalchemy.orm.relationships import _RelationshipDeclared
|
||||||
|
|
||||||
|
logger = logging.getLogger(f"submissions.{__name__}")
|
||||||
|
|
||||||
|
|
||||||
|
class BaseOmni(BaseModel):
|
||||||
|
|
||||||
|
instance_object: Any | None = Field(default=None)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
try:
|
||||||
|
return f"<{self.__class__.__name__}({self.name})>"
|
||||||
|
except AttributeError:
|
||||||
|
return f"<{self.__class__.__name__}(NO NAME)>"
|
||||||
|
|
||||||
|
@classproperty
|
||||||
|
def aliases(cls):
|
||||||
|
return cls.class_object.aliases
|
||||||
|
|
||||||
|
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():
|
||||||
|
# print(getattr(self.__class__, key).property)
|
||||||
|
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:
|
||||||
|
logger.debug(f"Check if {self_value.__class__} is subclass of {self.__class__}")
|
||||||
|
check = issubclass(self_value.__class__, self.__class__)
|
||||||
|
except TypeError as e:
|
||||||
|
logger.error(f"Couldn't check if {self_value.__class__} is subclass of {self.__class__} due to {e}")
|
||||||
|
check = False
|
||||||
|
if check:
|
||||||
|
logger.debug(f"Checking for subclass name.")
|
||||||
|
self_value = self_value.name
|
||||||
|
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 __setattr__(self, key, value):
|
||||||
|
try:
|
||||||
|
class_value = getattr(self.class_object, key)
|
||||||
|
except AttributeError:
|
||||||
|
return super().__setattr__(key, value)
|
||||||
|
try:
|
||||||
|
new_key = class_value.impl.key
|
||||||
|
except AttributeError:
|
||||||
|
new_key = None
|
||||||
|
logger.debug(f"Class value before new key: {class_value.property}")
|
||||||
|
if new_key and new_key != key:
|
||||||
|
class_value = getattr(self.class_object, new_key)
|
||||||
|
logger.debug(f"Class value after new key: {class_value.property}")
|
||||||
|
if isinstance(class_value, InstrumentedAttribute):
|
||||||
|
logger.debug(f"{key} is an InstrumentedAttribute with class_value.property: {class_value.property}.")
|
||||||
|
match class_value.property:
|
||||||
|
case ColumnProperty():
|
||||||
|
logger.debug(f"Setting ColumnProperty to {value}")
|
||||||
|
return super().__setattr__(key, value)
|
||||||
|
case _RelationshipDeclared():
|
||||||
|
logger.debug(f" {self.__class__.__name__} Setting _RelationshipDeclared for {key} to {value}")
|
||||||
|
if class_value.property.uselist:
|
||||||
|
logger.debug(f"Setting {key} with uselist")
|
||||||
|
existing = self.__getattribute__(key)
|
||||||
|
if existing is not None:
|
||||||
|
# NOTE: Getting some really weird duplicates for OmniSubmissionTypeKitTypeAssociation here.
|
||||||
|
logger.debug(f"Existing: {existing}, incoming: {value}")
|
||||||
|
if isinstance(value, list):
|
||||||
|
if value != existing:
|
||||||
|
value = existing + value
|
||||||
|
else:
|
||||||
|
value = existing
|
||||||
|
else:
|
||||||
|
if issubclass(value.__class__, self.__class__):
|
||||||
|
value = value.to_sql()
|
||||||
|
value = existing + [value]
|
||||||
|
else:
|
||||||
|
if issubclass(value.__class__, self.__class__):
|
||||||
|
value = value.to_sql()
|
||||||
|
value = [value]
|
||||||
|
# value = list(set(value))
|
||||||
|
logger.debug(f"Final value for {key}: {value}")
|
||||||
|
return super().__setattr__(key, value)
|
||||||
|
else:
|
||||||
|
if isinstance(value, list):
|
||||||
|
if len(value) == 1:
|
||||||
|
value = value[0]
|
||||||
|
else:
|
||||||
|
raise ValueError("Object is too long to parse a single value.")
|
||||||
|
return super().__setattr__(key, value)
|
||||||
|
case _:
|
||||||
|
return super().__setattr__(key, value)
|
||||||
|
else:
|
||||||
|
return super().__setattr__(key, value)
|
||||||
|
|
||||||
|
|
||||||
|
class OmniSubmissionType(BaseOmni):
|
||||||
|
class_object: ClassVar[Any] = SubmissionType
|
||||||
|
|
||||||
|
name: str = Field(default="", description="property")
|
||||||
|
info_map: dict = Field(default={}, description="property")
|
||||||
|
defaults: dict = Field(default={}, description="property")
|
||||||
|
template_file: bytes = Field(default=bytes(), description="property")
|
||||||
|
sample_map: dict = Field(default={}, description="property")
|
||||||
|
|
||||||
|
@field_validator("name", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def rescue_name_none(cls, value):
|
||||||
|
if not value:
|
||||||
|
return ""
|
||||||
|
return value
|
||||||
|
|
||||||
|
@field_validator("sample_map", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def rescue_sample_map_none(cls, value):
|
||||||
|
if not value:
|
||||||
|
return {}
|
||||||
|
return value
|
||||||
|
|
||||||
|
@field_validator("defaults", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def rescue_defaults_none(cls, value):
|
||||||
|
if not value:
|
||||||
|
return {}
|
||||||
|
return value
|
||||||
|
|
||||||
|
@field_validator("info_map", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def rescue_info_map_none(cls, value):
|
||||||
|
if not value:
|
||||||
|
return {}
|
||||||
|
return value
|
||||||
|
|
||||||
|
def __init__(self, instance_object: Any, **data):
|
||||||
|
super().__init__(**data)
|
||||||
|
self.instance_object = instance_object
|
||||||
|
|
||||||
|
def to_dataframe_dict(self):
|
||||||
|
return dict(
|
||||||
|
name=self.name
|
||||||
|
)
|
||||||
|
|
||||||
|
def to_sql(self):
|
||||||
|
instance, new = self.class_object.query_or_create(name=self.name)
|
||||||
|
instance.info_map = self.info_map
|
||||||
|
instance.defaults = self.defaults
|
||||||
|
instance.sample_map = self.sample_map
|
||||||
|
if self.template_file:
|
||||||
|
instance.template_file = self.template_file
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class OmniReagentRole(BaseOmni):
|
||||||
|
class_object: ClassVar[Any] = ReagentRole
|
||||||
|
|
||||||
|
name: str = Field(default="", description="property")
|
||||||
|
eol_ext: timedelta = Field(default=timedelta(days=0), description="property")
|
||||||
|
|
||||||
|
@field_validator("name", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def rescue_name_none(cls, value):
|
||||||
|
if not value:
|
||||||
|
return ""
|
||||||
|
return value
|
||||||
|
|
||||||
|
@field_validator("eol_ext", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def rescue_eol_ext(cls, value):
|
||||||
|
if not value:
|
||||||
|
value = timedelta(days=0)
|
||||||
|
return value
|
||||||
|
|
||||||
|
def __init__(self, instance_object: Any, **data):
|
||||||
|
super().__init__(**data)
|
||||||
|
self.instance_object = instance_object
|
||||||
|
|
||||||
|
def to_dataframe_dict(self):
|
||||||
|
return dict(
|
||||||
|
name=self.name
|
||||||
|
)
|
||||||
|
|
||||||
|
def to_sql(self):
|
||||||
|
instance, new = self.class_object.query_or_create(name=self.name)
|
||||||
|
if new:
|
||||||
|
instance.eol_ext = self.eol_ext
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class OmniSubmissionTypeKitTypeAssociation(BaseOmni):
|
||||||
|
class_object: ClassVar[Any] = SubmissionTypeKitTypeAssociation
|
||||||
|
|
||||||
|
submissiontype: str | OmniSubmissionType = Field(default="", description="relationship", title="SubmissionType")
|
||||||
|
kittype: str | OmniKitType = Field(default="", description="relationship", title="KitType")
|
||||||
|
mutable_cost_column: float = Field(default=0.0, description="property")
|
||||||
|
mutable_cost_sample: float = Field(default=0.0, description="property")
|
||||||
|
constant_cost: float = Field(default=0.0, description="property")
|
||||||
|
# processes: List[OmniProcess] | List[str] = Field(default=[], description="relationship", title="Process")
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
if isinstance(self.submissiontype, str):
|
||||||
|
submissiontype = self.submissiontype
|
||||||
|
else:
|
||||||
|
submissiontype = self.submissiontype.name
|
||||||
|
if isinstance(self.kittype, str):
|
||||||
|
kittype = self.kittype
|
||||||
|
else:
|
||||||
|
kittype = self.kittype.name
|
||||||
|
try:
|
||||||
|
return f"<{self.__class__.__name__}({submissiontype}&{kittype})>"
|
||||||
|
except AttributeError:
|
||||||
|
return f"<{self.__class__.__name__}(NO NAME)>"
|
||||||
|
|
||||||
|
@field_validator("submissiontype", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def rescue_submissiontype_none(cls, value):
|
||||||
|
if not value:
|
||||||
|
return ""
|
||||||
|
return value
|
||||||
|
|
||||||
|
@field_validator("kittype", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def rescue_kittype_none(cls, value):
|
||||||
|
if not value:
|
||||||
|
return ""
|
||||||
|
return value
|
||||||
|
|
||||||
|
@field_validator("kittype")
|
||||||
|
@classmethod
|
||||||
|
def no_list_please(cls, value):
|
||||||
|
if isinstance(value, list):
|
||||||
|
raise ValueError("List is not allowed for kittype.")
|
||||||
|
return value
|
||||||
|
|
||||||
|
def __init__(self, instance_object: Any, **data):
|
||||||
|
super().__init__(**data)
|
||||||
|
self.instance_object = instance_object
|
||||||
|
|
||||||
|
def to_dataframe_dict(self):
|
||||||
|
if isinstance(self.submissiontype, OmniSubmissionType):
|
||||||
|
submissiontype = self.submissiontype.name
|
||||||
|
else:
|
||||||
|
submissiontype = self.submissiontype
|
||||||
|
if isinstance(self.kittype, OmniKitType):
|
||||||
|
kittype = self.kittype.name
|
||||||
|
else:
|
||||||
|
kittype = self.kittype
|
||||||
|
return dict(
|
||||||
|
submissiontype=submissiontype,
|
||||||
|
kittype=kittype,
|
||||||
|
mutable_cost_column=self.mutable_cost_column,
|
||||||
|
mutable_cost_sample=self.mutable_cost_sample,
|
||||||
|
constant_cost=self.constant_cost
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def to_sql(self):
|
||||||
|
logger.debug(f"Self kittype: {self.submissiontype}")
|
||||||
|
if issubclass(self.submissiontype.__class__, BaseOmni):
|
||||||
|
submissiontype = SubmissionType.query(name=self.submissiontype.name)
|
||||||
|
else:
|
||||||
|
submissiontype = SubmissionType.query(name=self.submissiontype)
|
||||||
|
if issubclass(self.kittype.__class__, BaseOmni):
|
||||||
|
kittype = KitType.query(name=self.kittype.name)
|
||||||
|
else:
|
||||||
|
kittype = KitType.query(name=self.kittype)
|
||||||
|
# logger.debug(f"Self kittype: {self.kittype}")
|
||||||
|
# kittype = KitType.query(name=self.kittype)
|
||||||
|
logger.debug(f"Query or create with {kittype}, {submissiontype}")
|
||||||
|
instance, is_new = self.class_object.query_or_create(kittype=kittype, submissiontype=submissiontype)
|
||||||
|
instance.mutable_cost_column = self.mutable_cost_column
|
||||||
|
instance.mutable_cost_sample = self.mutable_cost_sample
|
||||||
|
instance.constant_cost = self.constant_cost
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class OmniKitTypeReagentRoleAssociation(BaseOmni):
|
||||||
|
class_object: ClassVar[Any] = KitTypeReagentRoleAssociation
|
||||||
|
|
||||||
|
reagent_role: str | OmniReagentRole = Field(default="", description="relationship", title="ReagentRole")
|
||||||
|
uses: dict = Field(default={}, description="property")
|
||||||
|
required: bool = Field(default=True, description="property")
|
||||||
|
submission_type: str | OmniSubmissionType = Field(default="", description="relationship", title="SubmissionType")
|
||||||
|
kit_type: str | OmniKitType = Field(default="", description="relationship", title="KitType")
|
||||||
|
|
||||||
|
|
||||||
|
@field_validator("uses", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def rescue_uses_none(cls, value):
|
||||||
|
if not value:
|
||||||
|
return {}
|
||||||
|
return value
|
||||||
|
|
||||||
|
def __init__(self, instance_object: Any, **data):
|
||||||
|
super().__init__(**data)
|
||||||
|
self.instance_object = instance_object
|
||||||
|
|
||||||
|
def to_dataframe_dict(self):
|
||||||
|
if isinstance(self.submission_type, OmniSubmissionType):
|
||||||
|
submission_type = self.submission_type.name
|
||||||
|
else:
|
||||||
|
submission_type = self.submission_type
|
||||||
|
if isinstance(self.kit_type, OmniKitType):
|
||||||
|
kit_type = self.kit_type.name
|
||||||
|
else:
|
||||||
|
kit_type = self.kit_type
|
||||||
|
# name = f"{kit_type} -> {self.reagent_role}"
|
||||||
|
# logger.debug(f"Using name: {name}")
|
||||||
|
if isinstance(self.reagent_role, OmniReagentRole):
|
||||||
|
reagent_role = self.reagent_role.name
|
||||||
|
else:
|
||||||
|
reagent_role = self.reagent_role
|
||||||
|
return dict(
|
||||||
|
reagent_role=reagent_role,
|
||||||
|
# name=self.reagent_role.name,
|
||||||
|
submission_type=submission_type,
|
||||||
|
kit_type=kit_type
|
||||||
|
)
|
||||||
|
|
||||||
|
def to_sql(self):
|
||||||
|
if isinstance(self.reagent_role, OmniReagentRole):
|
||||||
|
reagent_role = self.reagent_role.name
|
||||||
|
else:
|
||||||
|
reagent_role = self.reagent_role
|
||||||
|
instance, new = self.class_object.query_or_create(
|
||||||
|
reagentrole=reagent_role,
|
||||||
|
kittype=self.kit_type,
|
||||||
|
submissiontype=self.submission_type
|
||||||
|
)
|
||||||
|
if new:
|
||||||
|
reagent_role = self.reagent_role.to_sql()
|
||||||
|
instance.reagent_role = reagent_role
|
||||||
|
logger.debug(f"KTRRAssoc uses: {self.uses}")
|
||||||
|
instance.uses = self.uses
|
||||||
|
logger.debug(f"KitTypeReagentRoleAssociation: {pformat(instance.__dict__)}")
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class OmniEquipmentRole(BaseOmni):
|
||||||
|
class_object: ClassVar[Any] = EquipmentRole
|
||||||
|
|
||||||
|
name: str = Field(default="", description="property")
|
||||||
|
|
||||||
|
@field_validator("name", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def rescue_name_none(cls, value):
|
||||||
|
if not value:
|
||||||
|
return ""
|
||||||
|
return value
|
||||||
|
|
||||||
|
def __init__(self, instance_object: Any, **data):
|
||||||
|
super().__init__(**data)
|
||||||
|
self.instance_object = instance_object
|
||||||
|
|
||||||
|
def to_dataframe_dict(self):
|
||||||
|
return dict(
|
||||||
|
name=self.name
|
||||||
|
)
|
||||||
|
|
||||||
|
def to_sql(self):
|
||||||
|
instance, new = self.class_object.query_or_create(name=self.name)
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class OmniTips(BaseOmni):
|
||||||
|
class_object: ClassVar[Any] = Tips
|
||||||
|
|
||||||
|
name: str = Field(default="", description="property")
|
||||||
|
|
||||||
|
@field_validator("name", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def rescue_name_none(cls, value):
|
||||||
|
if not value:
|
||||||
|
return ""
|
||||||
|
return value
|
||||||
|
|
||||||
|
def __init__(self, instance_object: Any, **data):
|
||||||
|
super().__init__(**data)
|
||||||
|
self.instance_object = instance_object
|
||||||
|
|
||||||
|
def to_dataframe_dict(self):
|
||||||
|
return dict(
|
||||||
|
name=self.name
|
||||||
|
)
|
||||||
|
|
||||||
|
def to_sql(self):
|
||||||
|
instance, new = self.class_object.query_or_create(name=self.name)
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class OmniTipRole(BaseOmni):
|
||||||
|
class_object: ClassVar[Any] = TipRole
|
||||||
|
|
||||||
|
name: str = Field(default="", description="property")
|
||||||
|
tips: List[OmniTips] = Field(default=[], description="relationship", title="Tips")
|
||||||
|
|
||||||
|
@field_validator("name", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def rescue_name_none(cls, value):
|
||||||
|
if not value:
|
||||||
|
return ""
|
||||||
|
return value
|
||||||
|
|
||||||
|
def __init__(self, instance_object: Any, **data):
|
||||||
|
super().__init__(**data)
|
||||||
|
self.instance_object = instance_object
|
||||||
|
|
||||||
|
def to_dataframe_dict(self):
|
||||||
|
return dict(
|
||||||
|
name=self.name,
|
||||||
|
tips=[item.name for item in self.tips]
|
||||||
|
)
|
||||||
|
|
||||||
|
def to_sql(self):
|
||||||
|
instance, new = self.class_object.query_or_create(name=self.name)
|
||||||
|
for tips in self.tips:
|
||||||
|
tips.to_sql()
|
||||||
|
# if new_assoc not in instance.instances:
|
||||||
|
# instance.instances.append(new_assoc)
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class OmniProcess(BaseOmni):
|
||||||
|
class_object: ClassVar[Any] = Process
|
||||||
|
|
||||||
|
# NOTE: How am I going to figure out relatioinships without getting into recursion issues?
|
||||||
|
name: str = Field(default="", description="property") #: Process name
|
||||||
|
submission_types: List[OmniSubmissionType] | List[str] = Field(default=[], description="relationship",
|
||||||
|
title="SubmissionType")
|
||||||
|
equipment_roles: List[OmniEquipmentRole] | List[str] = Field(default=[], description="relationship",
|
||||||
|
title="EquipmentRole")
|
||||||
|
tip_roles: List[OmniTipRole] | List[str] = Field(default=[], description="relationship", title="TipRole")
|
||||||
|
|
||||||
|
def __init__(self, instance_object: Any, **data):
|
||||||
|
super().__init__(**data)
|
||||||
|
self.instance_object = instance_object
|
||||||
|
|
||||||
|
def to_dataframe_dict(self):
|
||||||
|
submissiontypes = [item.name for item in self.submission_types]
|
||||||
|
logger.debug(f"Submission Types: {submissiontypes}")
|
||||||
|
equipmentroles = [item.name for item in self.equipment_roles]
|
||||||
|
logger.debug(f"Equipment Roles: {equipmentroles}")
|
||||||
|
return dict(
|
||||||
|
name=self.name,
|
||||||
|
submission_types=submissiontypes,
|
||||||
|
equipment_roles=equipmentroles
|
||||||
|
)
|
||||||
|
|
||||||
|
@field_validator("name", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def rescue_name_none(cls, value):
|
||||||
|
if not value:
|
||||||
|
return ""
|
||||||
|
return value
|
||||||
|
|
||||||
|
def to_sql(self):
|
||||||
|
instance, new = self.class_object.query_or_create(name=self.name)
|
||||||
|
for st in self.submission_types:
|
||||||
|
new_assoc = st.to_sql()
|
||||||
|
if new_assoc not in instance.submission_types:
|
||||||
|
instance.submission_types.append(new_assoc)
|
||||||
|
for er in self.equipment_roles:
|
||||||
|
new_assoc = er.to_sql()
|
||||||
|
if new_assoc not in instance.equipment_roles:
|
||||||
|
instance.equipment_roles.append(new_assoc)
|
||||||
|
for tr in self.tip_roles:
|
||||||
|
new_assoc = tr.to_sql()
|
||||||
|
if new_assoc not in instance.tip_roles:
|
||||||
|
instance.tip_roles.append(new_assoc)
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class OmniKitType(BaseOmni):
|
||||||
|
class_object: ClassVar[Any] = KitType
|
||||||
|
|
||||||
|
name: str = Field(default="", description="property")
|
||||||
|
kit_submissiontype_associations: List[OmniSubmissionTypeKitTypeAssociation] | List[str] = Field(default=[],
|
||||||
|
description="relationship",
|
||||||
|
title="SubmissionTypeKitTypeAssociation")
|
||||||
|
kit_reagentrole_associations: List[OmniKitTypeReagentRoleAssociation] | List[str] = Field(default=[],
|
||||||
|
description="relationship",
|
||||||
|
title="KitTypeReagentRoleAssociation")
|
||||||
|
processes: List[OmniProcess] | List[str] = Field(default=[], description="relationship", title="Process")
|
||||||
|
|
||||||
|
@field_validator("name", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def rescue_name_none(cls, value):
|
||||||
|
if not value:
|
||||||
|
return ""
|
||||||
|
return value
|
||||||
|
|
||||||
|
def __init__(self, instance_object: Any, **data):
|
||||||
|
super().__init__(**data)
|
||||||
|
self.instance_object = instance_object
|
||||||
|
|
||||||
|
def to_dataframe_dict(self):
|
||||||
|
return dict(
|
||||||
|
name=self.name
|
||||||
|
)
|
||||||
|
|
||||||
|
def to_sql(self) -> KitType:
|
||||||
|
kit, is_new = KitType.query_or_create(name=self.name)
|
||||||
|
if is_new:
|
||||||
|
logger.debug(f"New kit made: {kit}")
|
||||||
|
else:
|
||||||
|
logger.debug(f"Kit retrieved: {kit}")
|
||||||
|
new_rr = []
|
||||||
|
for rr_assoc in self.kit_reagentrole_associations:
|
||||||
|
new_assoc = rr_assoc.to_sql()
|
||||||
|
if new_assoc not in new_rr:
|
||||||
|
logger.debug(f"Adding {new_assoc} to kit_reagentrole_associations")
|
||||||
|
new_rr.append(new_assoc)
|
||||||
|
logger.debug(f"Setting kit_reagentrole_associations to {new_rr}")
|
||||||
|
kit.kit_reagentrole_associations = new_rr
|
||||||
|
new_st = []
|
||||||
|
for st_assoc in self.kit_submissiontype_associations:
|
||||||
|
new_assoc = st_assoc.to_sql()
|
||||||
|
if new_assoc not in new_st:
|
||||||
|
new_st.append(new_assoc)
|
||||||
|
kit.kit_submissiontype_associations = new_st
|
||||||
|
new_processes = []
|
||||||
|
for process in self.processes:
|
||||||
|
new_process = process.to_sql()
|
||||||
|
if new_process not in new_processes:
|
||||||
|
new_processes.append(new_process)
|
||||||
|
kit.processes = new_processes
|
||||||
|
logger.debug(f"Kit: {pformat(kit.__dict__)}")
|
||||||
|
for item in kit.kit_reagentrole_associations:
|
||||||
|
logger.debug(f"KTRRassoc: {item.__dict__}")
|
||||||
|
return kit
|
||||||
@@ -242,6 +242,7 @@ class App(QMainWindow):
|
|||||||
from frontend.widgets.omni_manager_pydant import ManagerWindow as ManagerWindowPyd
|
from frontend.widgets.omni_manager_pydant import ManagerWindow as ManagerWindowPyd
|
||||||
dlg = ManagerWindowPyd(parent=self, object_type=KitType, extras=[], add_edit='edit', managers=set())
|
dlg = ManagerWindowPyd(parent=self, object_type=KitType, extras=[], add_edit='edit', managers=set())
|
||||||
if dlg.exec():
|
if dlg.exec():
|
||||||
|
logger.debug("\n\nBeginning parsing\n\n")
|
||||||
output = dlg.parse_form()
|
output = dlg.parse_form()
|
||||||
# assert isinstance(output, KitType)
|
# assert isinstance(output, KitType)
|
||||||
# output.save()
|
# output.save()
|
||||||
@@ -249,6 +250,11 @@ class App(QMainWindow):
|
|||||||
# output.to_sql()
|
# output.to_sql()
|
||||||
with open(f"{output.name}.obj", "wb") as f:
|
with open(f"{output.name}.obj", "wb") as f:
|
||||||
pickle.dump(output, f)
|
pickle.dump(output, f)
|
||||||
|
logger.debug("\n\nBeginning transformation\n\n")
|
||||||
|
sql = output.to_sql()
|
||||||
|
with open(f"{output.name}.sql", "wb") as f:
|
||||||
|
pickle.dump(sql, f)
|
||||||
|
sql.save()
|
||||||
|
|
||||||
|
|
||||||
class AddSubForm(QWidget):
|
class AddSubForm(QWidget):
|
||||||
|
|||||||
680
src/submissions/frontend/widgets/omni_manager_pydant.py
Normal file
680
src/submissions/frontend/widgets/omni_manager_pydant.py
Normal file
@@ -0,0 +1,680 @@
|
|||||||
|
"""
|
||||||
|
Provides a screen for managing all attributes of a database object.
|
||||||
|
"""
|
||||||
|
import json, logging
|
||||||
|
import sys
|
||||||
|
from json.decoder import JSONDecodeError
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from pprint import pformat
|
||||||
|
from typing import Any, List, Literal
|
||||||
|
from PyQt6.QtCore import QSortFilterProxyModel, Qt
|
||||||
|
from PyQt6.QtGui import QAction, QCursor
|
||||||
|
from PyQt6.QtWidgets import (
|
||||||
|
QLabel, QDialog,
|
||||||
|
QTableView, QWidget, QLineEdit, QGridLayout, QComboBox, QPushButton, QDialogButtonBox, QDateEdit, QMenu,
|
||||||
|
QDoubleSpinBox, QSpinBox, QCheckBox, QTextEdit, QVBoxLayout, QHBoxLayout
|
||||||
|
)
|
||||||
|
from pandas import DataFrame
|
||||||
|
from backend import db
|
||||||
|
from tools import check_object_in_manager
|
||||||
|
from .omni_search import SearchBox
|
||||||
|
from frontend.widgets.submission_table import pandasModel
|
||||||
|
|
||||||
|
logger = logging.getLogger(f"submissions.{__name__}")
|
||||||
|
|
||||||
|
|
||||||
|
class ManagerWindow(QDialog):
|
||||||
|
"""
|
||||||
|
Initially this is a window to manage Organization Contacts, but hope to abstract it more later.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, parent,
|
||||||
|
extras: List[str],
|
||||||
|
instance: Any | None = None,
|
||||||
|
object_type: Any | None = None,
|
||||||
|
manager: Any | None = None,
|
||||||
|
add_edit: Literal['add', 'edit'] = 'edit',
|
||||||
|
**kwargs):
|
||||||
|
super().__init__(parent)
|
||||||
|
# NOTE: Should I pass in an instance?
|
||||||
|
self.instance = instance
|
||||||
|
logger.debug(f"Setting instance: {self.instance}")
|
||||||
|
if not self.instance:
|
||||||
|
self.class_object = self.original_type = object_type
|
||||||
|
else:
|
||||||
|
self.class_object = self.original_type = self.instance.__class__
|
||||||
|
self.add_edit = add_edit
|
||||||
|
if manager is None:
|
||||||
|
try:
|
||||||
|
self.manager = self.parent().omni_object
|
||||||
|
except AttributeError:
|
||||||
|
self.manager = None
|
||||||
|
else:
|
||||||
|
self.manager = manager
|
||||||
|
# logger.debug(f"Manager: {manager}")
|
||||||
|
self.extras = extras
|
||||||
|
self.context = kwargs
|
||||||
|
self.layout = QGridLayout(self)
|
||||||
|
QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
|
||||||
|
self.buttonBox = QDialogButtonBox(QBtn)
|
||||||
|
self.buttonBox.accepted.connect(self.accept)
|
||||||
|
self.buttonBox.rejected.connect(self.reject)
|
||||||
|
self.setMinimumSize(600, 600)
|
||||||
|
sub_classes = ["Any"] + [cls.__name__ for cls in self.class_object.__subclasses__()]
|
||||||
|
if len(sub_classes) > 1:
|
||||||
|
self.sub_class = QComboBox(self)
|
||||||
|
self.sub_class.setObjectName("sub_class")
|
||||||
|
self.sub_class.addItems(sub_classes)
|
||||||
|
self.sub_class.currentTextChanged.connect(self.update_options)
|
||||||
|
self.sub_class.setEditable(False)
|
||||||
|
self.sub_class.setMinimumWidth(self.minimumWidth())
|
||||||
|
self.layout.addWidget(self.sub_class, 0, 0)
|
||||||
|
else:
|
||||||
|
self.sub_class = None
|
||||||
|
self.update_instance(initial=True)
|
||||||
|
if self.add_edit == "edit":
|
||||||
|
self.options = QComboBox(self)
|
||||||
|
self.options.setObjectName("options")
|
||||||
|
self.update_options()
|
||||||
|
else:
|
||||||
|
self.update_data()
|
||||||
|
self.setLayout(self.layout)
|
||||||
|
self.setWindowTitle(f"Manage {self.class_object.__name__} - Manager: {self.manager}")
|
||||||
|
|
||||||
|
def update_options(self) -> None:
|
||||||
|
"""
|
||||||
|
Changes form inputs based on sample type
|
||||||
|
"""
|
||||||
|
# logger.debug(f"Instance: {self.instance}")
|
||||||
|
if self.sub_class:
|
||||||
|
self.class_object = getattr(db, self.sub_class.currentText())
|
||||||
|
# logger.debug(f"From update options, managers: {self.managers}")
|
||||||
|
try:
|
||||||
|
query_kwargs = {self.parent().instance.query_alias: self.parent().instance}
|
||||||
|
except AttributeError as e:
|
||||||
|
# logger.debug(f"Couldn't set query kwargs due to: {e}")
|
||||||
|
query_kwargs = {}
|
||||||
|
# logger.debug(f"Query kwargs: {query_kwargs}")
|
||||||
|
# logger.debug(f"self.class_object: {self.class_object}")
|
||||||
|
options = [item.name for item in self.class_object.query(**query_kwargs)]
|
||||||
|
if self.instance:
|
||||||
|
try:
|
||||||
|
inserter = options.pop(options.index(self.instance.name))
|
||||||
|
except ValueError:
|
||||||
|
inserter = self.instance.name
|
||||||
|
options.insert(0, inserter)
|
||||||
|
self.options.clear()
|
||||||
|
self.options.addItems(options)
|
||||||
|
self.options.setEditable(False)
|
||||||
|
self.options.setMinimumWidth(self.minimumWidth())
|
||||||
|
self.layout.addWidget(self.options, 1, 0, 1, 1)
|
||||||
|
# if len(options) > 0:
|
||||||
|
self.add_button = QPushButton("Add New")
|
||||||
|
self.layout.addWidget(self.add_button, 1, 1, 1, 1)
|
||||||
|
self.add_button.clicked.connect(self.add_new)
|
||||||
|
self.options.currentTextChanged.connect(self.update_instance)
|
||||||
|
# logger.debug(f"Instance: {self.instance}")
|
||||||
|
self.update_data()
|
||||||
|
|
||||||
|
def update_instance(self, initial: bool = False):
|
||||||
|
if self.add_edit == "edit" or initial:
|
||||||
|
try:
|
||||||
|
# logger.debug(f"Querying with {self.options.currentText()}")
|
||||||
|
self.instance = self.class_object.query(name=self.options.currentText(), limit=1)
|
||||||
|
except AttributeError:
|
||||||
|
# self.instance = None
|
||||||
|
pass
|
||||||
|
# logger.debug(f"Instance: {self.instance}")
|
||||||
|
if not self.instance:
|
||||||
|
logger.warning(f"Instance not found, creating blank instance.")
|
||||||
|
self.instance = self.class_object()
|
||||||
|
# logger.debug(f"self.instance: {self.instance}")
|
||||||
|
if issubclass(self.instance.__class__, db.BaseClass):
|
||||||
|
self.omni_object = self.instance.to_omni(expand=True)
|
||||||
|
else:
|
||||||
|
self.omni_object = self.instance
|
||||||
|
logger.debug(f"Created omni_object: {self.omni_object.__dict__}")
|
||||||
|
self.update_data()
|
||||||
|
|
||||||
|
def update_data(self) -> None:
|
||||||
|
"""
|
||||||
|
Performs updating of widgets on first run and after options change.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
# NOTE: Remove all old widgets.
|
||||||
|
deletes = [item for item in self.findChildren(EditProperty)] + \
|
||||||
|
[item for item in self.findChildren(EditRelationship)] + \
|
||||||
|
[item for item in self.findChildren(QDialogButtonBox)]
|
||||||
|
for item in deletes:
|
||||||
|
item.setParent(None)
|
||||||
|
fields = self.omni_object.__class__.model_fields
|
||||||
|
for key, info in fields.items():
|
||||||
|
# logger.debug(f"Attempting to set {key}, {info} widget")
|
||||||
|
try:
|
||||||
|
value = getattr(self.omni_object, key)
|
||||||
|
except AttributeError:
|
||||||
|
value = None
|
||||||
|
match info.description:
|
||||||
|
# NOTE: ColumnProperties will be directly edited.
|
||||||
|
case "property":
|
||||||
|
# NOTE: field.property.expression.type gives db column type eg. STRING or TIMESTAMP
|
||||||
|
# logger.debug(f"Creating property widget with value: {value}")
|
||||||
|
widget = EditProperty(self, key=key, column_type=info, value=value)
|
||||||
|
# NOTE: RelationshipDeclareds will be given a list of existing related objects.
|
||||||
|
case "relationship":
|
||||||
|
# NOTE: field.comparator.class_object.class_ gives the relationship class
|
||||||
|
# logger.debug(f"Creating relationship widget with value: {value}")
|
||||||
|
widget = EditRelationship(self, key=key, class_object=info.title, value=value)
|
||||||
|
case _:
|
||||||
|
continue
|
||||||
|
if widget:
|
||||||
|
self.layout.addWidget(widget, self.layout.rowCount(), 0, 1, 2)
|
||||||
|
# NOTE: Add OK|Cancel to bottom of dialog.
|
||||||
|
self.layout.addWidget(self.buttonBox, self.layout.rowCount(), 0, 1, 2)
|
||||||
|
|
||||||
|
def parse_form(self) -> Any:
|
||||||
|
"""
|
||||||
|
Returns the instance associated with this window.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Any: The instance with updated fields.
|
||||||
|
"""
|
||||||
|
# TODO: Need Relationship property here too?
|
||||||
|
results = [item.parse_form() for item in self.findChildren(EditProperty)]
|
||||||
|
for result in results:
|
||||||
|
logger.debug(f"Incoming property result: {result}")
|
||||||
|
setattr(self.omni_object, result['field'], result['value'])
|
||||||
|
# NOTE: Getting 'None' back here.
|
||||||
|
logger.debug(f"Set result: {getattr(self.instance, result['field'])}")
|
||||||
|
results = [item.parse_form() for item in self.findChildren(EditRelationship)]
|
||||||
|
for result in results:
|
||||||
|
logger.debug(f"Incoming relationship result: {result}")
|
||||||
|
# if not getattr(self.omni_object, result['field']):
|
||||||
|
setattr(self.omni_object, result['field'], result['value'])
|
||||||
|
# logger.debug(f"Set result: {getattr(self.omni_object, result['field'])}")
|
||||||
|
# logger.debug(f"Instance coming from parsed form: {self.omni_object.__dict__}")
|
||||||
|
return self.omni_object
|
||||||
|
|
||||||
|
def add_new(self):
|
||||||
|
new_instance = self.class_object()
|
||||||
|
self.instance = new_instance
|
||||||
|
self.update_options()
|
||||||
|
|
||||||
|
|
||||||
|
class EditProperty(QWidget):
|
||||||
|
|
||||||
|
def __init__(self, parent: ManagerWindow, key: str, column_type: Any, value):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.label = QLabel(key.title().replace("_", " "))
|
||||||
|
self.layout = QGridLayout()
|
||||||
|
self.layout.addWidget(self.label, 0, 0, 1, 1)
|
||||||
|
self.setObjectName(key)
|
||||||
|
# logger.debug(f"Column type for {key}: {type(column_type.default)}")
|
||||||
|
match column_type.default:
|
||||||
|
case str():
|
||||||
|
self.widget = QLineEdit(self)
|
||||||
|
self.widget.setText(value)
|
||||||
|
case bool():
|
||||||
|
if isinstance(column_type.default, bool):
|
||||||
|
self.widget = QCheckBox()
|
||||||
|
self.widget.setChecked(value)
|
||||||
|
else:
|
||||||
|
if value is None:
|
||||||
|
value = 0
|
||||||
|
self.widget = QSpinBox()
|
||||||
|
self.widget.setMaximum(1)
|
||||||
|
self.widget.setValue(value)
|
||||||
|
case float():
|
||||||
|
if not value:
|
||||||
|
value = 0.0
|
||||||
|
self.widget = QDoubleSpinBox()
|
||||||
|
self.widget.setMaximum(999.99)
|
||||||
|
self.widget.setValue(value)
|
||||||
|
case datetime():
|
||||||
|
self.widget = QDateEdit(self)
|
||||||
|
self.widget.setDate(value)
|
||||||
|
case timedelta():
|
||||||
|
self.widget = QSpinBox()
|
||||||
|
self.widget.setMaximum(9999)
|
||||||
|
self.widget.setToolTip("This time interval is measured in days.")
|
||||||
|
self.widget.setValue(value.days)
|
||||||
|
case dict():
|
||||||
|
self.widget = JsonEditButton(parent=self, key=key, value=value)
|
||||||
|
# self.widget.viewButton.clicked.connect(lambda: self.parent().toggle_textedit(self.widget))
|
||||||
|
# self.widget.addButton.clicked.connect(lambda: self.parent().add_to_json(self.widget))
|
||||||
|
case bytes():
|
||||||
|
self.widget = QLabel("BLOB Under construction")
|
||||||
|
case _:
|
||||||
|
self.widget = None
|
||||||
|
self.layout.addWidget(self.widget, 0, 1, 1, 3)
|
||||||
|
self.setLayout(self.layout)
|
||||||
|
|
||||||
|
def parse_form(self):
|
||||||
|
# logger.debug(f"Parsing widget {self.objectName()}: {type(self.widget)}")
|
||||||
|
match self.widget:
|
||||||
|
case QLineEdit():
|
||||||
|
value = self.widget.text()
|
||||||
|
case QDateEdit():
|
||||||
|
value = self.widget.date()
|
||||||
|
case QSpinBox() | QDoubleSpinBox():
|
||||||
|
value = self.widget.value()
|
||||||
|
case QCheckBox():
|
||||||
|
value = self.widget.isChecked()
|
||||||
|
case JsonEditButton():
|
||||||
|
value = self.widget.data
|
||||||
|
case _:
|
||||||
|
value = None
|
||||||
|
return dict(field=self.objectName(), value=value)
|
||||||
|
|
||||||
|
|
||||||
|
class EditRelationship(QWidget):
|
||||||
|
|
||||||
|
def __init__(self, parent, key: str, class_object: Any, value):
|
||||||
|
from backend.db import models
|
||||||
|
super().__init__(parent)
|
||||||
|
self.class_object = getattr(models, class_object)
|
||||||
|
logger.debug(f"Class object: {self.class_object}")
|
||||||
|
self.setParent(parent)
|
||||||
|
# logger.debug(f"Edit relationship class_object: {self.class_object}")
|
||||||
|
self.label = QLabel(key.title().replace("_", " "))
|
||||||
|
self.setObjectName(key) #: key is the name of the relationship this represents
|
||||||
|
logger.debug(f"Checking relationship for {self.parent().class_object}: {key}")
|
||||||
|
# try:
|
||||||
|
# self.relationship = getattr(self.parent().instance.__class__, key)
|
||||||
|
self.relationship = getattr(self.parent().class_object, key)
|
||||||
|
#: relationship object for type differentiation
|
||||||
|
# except AttributeError:
|
||||||
|
# logger.warning(f"Could not get relationship for: {key}.")
|
||||||
|
# self.relationship = None
|
||||||
|
self.widget = QTableView()
|
||||||
|
self.add_button = QPushButton("Add New")
|
||||||
|
self.add_button.clicked.connect(self.add_new)
|
||||||
|
self.existing_button = QPushButton("Add Existing")
|
||||||
|
self.existing_button.clicked.connect(self.add_existing)
|
||||||
|
# self.existing_button.setEnabled(self.class_object.level == 1)
|
||||||
|
if not isinstance(value, list):
|
||||||
|
if value not in [None, ""]:
|
||||||
|
value = [value]
|
||||||
|
else:
|
||||||
|
value = []
|
||||||
|
self.data = value
|
||||||
|
logger.debug(f"Set data: {self.data}")
|
||||||
|
# self.update_buttons()
|
||||||
|
logger.debug(f"Parent manager: {self.parent().manager}")
|
||||||
|
checked_manager, is_primary = check_object_in_manager(self.parent().manager, self.objectName())
|
||||||
|
if checked_manager:
|
||||||
|
logger.debug(f"Checked manager for {self.objectName()}: {checked_manager}")
|
||||||
|
# logger.debug(f"Omni will inherit: {self.class_object.omni_inheritable} from {self.parent().class_object}")
|
||||||
|
# if checked_manager is not None:# and not self.data:# and self.objectName() in self.parent().class_object.omni_inheritable:
|
||||||
|
# # logger.debug(f"Setting {checked_manager} in self.data")
|
||||||
|
# # if isinstance(checked_manager, InstrumentedList):
|
||||||
|
# # self.data = [item.to_omni() for item in checked_manager]
|
||||||
|
# # else:
|
||||||
|
# # self.data = [checked_manager.to_omni()]
|
||||||
|
# self.data = [checked_manager]
|
||||||
|
if not self.data:
|
||||||
|
self.data = [checked_manager]
|
||||||
|
try:
|
||||||
|
logger.debug(f"Relationship {key} uses list: {self.relationship.property.uselist}")
|
||||||
|
check = not self.relationship.property.uselist and len(self.data) >= 1
|
||||||
|
except AttributeError:
|
||||||
|
check = True
|
||||||
|
if check:
|
||||||
|
self.add_button.setEnabled(False)
|
||||||
|
self.existing_button.setEnabled(False)
|
||||||
|
if is_primary:
|
||||||
|
self.widget.setEnabled(False)
|
||||||
|
else:
|
||||||
|
self.add_button.setEnabled(True)
|
||||||
|
self.existing_button.setEnabled(True)
|
||||||
|
if is_primary:
|
||||||
|
self.widget.setEnabled(True)
|
||||||
|
self.layout = QGridLayout()
|
||||||
|
self.layout.addWidget(self.label, 0, 0, 1, 5)
|
||||||
|
self.layout.addWidget(self.widget, 1, 0, 1, 8)
|
||||||
|
self.layout.addWidget(self.add_button, 0, 6, 1, 1, alignment=Qt.AlignmentFlag.AlignRight)
|
||||||
|
self.layout.addWidget(self.existing_button, 0, 7, 1, 1, alignment=Qt.AlignmentFlag.AlignRight)
|
||||||
|
self.setLayout(self.layout)
|
||||||
|
self.set_data()
|
||||||
|
|
||||||
|
def update_buttons(self):
|
||||||
|
if not self.relationship.property.uselist and len(self.data) >= 1:
|
||||||
|
# logger.debug(f"Property {self.relationship} doesn't use list and data is of length: {len(self.data)}")
|
||||||
|
self.add_button.setEnabled(False)
|
||||||
|
self.existing_button.setEnabled(False)
|
||||||
|
else:
|
||||||
|
self.add_button.setEnabled(True)
|
||||||
|
self.existing_button.setEnabled(True)
|
||||||
|
|
||||||
|
def parse_row(self, x):
|
||||||
|
context = {item: x.sibling(x.row(), self.df.columns.get_loc(item)).data() for item in self.df.columns}
|
||||||
|
try:
|
||||||
|
object = self.class_object.query(**context)
|
||||||
|
except KeyError:
|
||||||
|
object = None
|
||||||
|
self.widget.doubleClicked.disconnect()
|
||||||
|
self.add_edit(instance=object)
|
||||||
|
|
||||||
|
def add_new(self, instance: Any = None, add_edit: Literal["add", "edit"] = "add"):
|
||||||
|
if add_edit == "edit":
|
||||||
|
logger.debug(f"Editing instance: {instance.__dict__}")
|
||||||
|
# NOTE: if an existing instance is not being edited, create a new instance
|
||||||
|
if not instance:
|
||||||
|
# logger.debug(f"Creating new instance of {self.class_object}")
|
||||||
|
instance = self.class_object()
|
||||||
|
# logger.debug(f"Creating manager window for {instance}")
|
||||||
|
manager = self.parent().manager
|
||||||
|
# logger.debug(f"Managers going into add new: {managers}")
|
||||||
|
# dlg = ManagerWindow(self.parent(), object_type=instance.__class__, extras=[], manager=manager, add_edit="add")
|
||||||
|
dlg = ManagerWindow(self.parent(), instance=instance, extras=[], manager=manager, add_edit=add_edit)
|
||||||
|
if dlg.exec():
|
||||||
|
new_instance = dlg.parse_form()
|
||||||
|
logger.debug(f"New instance: {pformat(new_instance.__dict__)}")
|
||||||
|
# NOTE: Somewhere between this and the next logger, I'm losing the uses data.
|
||||||
|
if add_edit == "add":
|
||||||
|
self.parent().omni_object.__setattr__(self.objectName(), new_instance)
|
||||||
|
else:
|
||||||
|
instance.__dict__.update(new_instance.__dict__)
|
||||||
|
logger.debug(f"Final instance: {pformat(instance.__dict__)}")
|
||||||
|
# self.parent().instance.save()
|
||||||
|
self.parent().update_data()
|
||||||
|
|
||||||
|
def add_existing(self):
|
||||||
|
dlg = SearchBox(self, object_type=self.class_object, returnable=True, extras=[])
|
||||||
|
if dlg.exec():
|
||||||
|
rows = dlg.return_selected_rows()
|
||||||
|
for row in rows:
|
||||||
|
# logger.debug(f"Querying with {row}")
|
||||||
|
instance = self.class_object.query(**row)
|
||||||
|
# NOTE: My custom __setattr__ should take care of any list problems.
|
||||||
|
if isinstance(instance, list):
|
||||||
|
instance = instance[0]
|
||||||
|
self.parent().omni_object.__setattr__(self.objectName(), instance.to_omni())
|
||||||
|
# self.parent().instance.save()
|
||||||
|
self.parent().update_data()
|
||||||
|
|
||||||
|
def set_data(self) -> None:
|
||||||
|
"""
|
||||||
|
sets data in model
|
||||||
|
"""
|
||||||
|
# logger.debug(f"Self.data: {self.data}")
|
||||||
|
try:
|
||||||
|
# records = [{k: v['instance_attr'] for k, v in item.omnigui_instance_dict.items()} for item in self.data]
|
||||||
|
records = [item.to_dataframe_dict() for item in self.data]
|
||||||
|
except AttributeError:
|
||||||
|
records = []
|
||||||
|
# logger.debug(f"Records: {records}")
|
||||||
|
self.df = DataFrame.from_records(records)
|
||||||
|
try:
|
||||||
|
self.columns_of_interest = [dict(name=item, column=self.df.columns.get_loc(item)) for item in self.extras]
|
||||||
|
except (KeyError, AttributeError):
|
||||||
|
self.columns_of_interest = []
|
||||||
|
try:
|
||||||
|
self.df['id'] = self.df['id'].apply(str)
|
||||||
|
self.df['id'] = self.df['id'].str.zfill(4)
|
||||||
|
except KeyError as e:
|
||||||
|
logger.error(f"Could not alter id to string due to KeyError: {e}")
|
||||||
|
proxy_model = QSortFilterProxyModel()
|
||||||
|
proxy_model.setSourceModel(pandasModel(self.df))
|
||||||
|
self.widget.setModel(proxy_model)
|
||||||
|
self.widget.resizeColumnsToContents()
|
||||||
|
self.widget.resizeRowsToContents()
|
||||||
|
self.widget.setSortingEnabled(True)
|
||||||
|
self.widget.doubleClicked.connect(self.parse_row)
|
||||||
|
# self.update_buttons()
|
||||||
|
|
||||||
|
def contextMenuEvent(self, event):
|
||||||
|
"""
|
||||||
|
Creates actions for right click menu events.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event (_type_): the item of interest
|
||||||
|
"""
|
||||||
|
if not self.widget.isEnabled():
|
||||||
|
logger.warning(f"{self.objectName()} is disabled.")
|
||||||
|
return
|
||||||
|
id = self.widget.selectionModel().currentIndex()
|
||||||
|
logger.debug(f"Row id: {id.row()}")
|
||||||
|
# NOTE: the overly complicated {column_name: row_value} dictionary construction
|
||||||
|
row_data = {self.df.columns[column]: self.widget.model().index(id.row(), column).data() for column in
|
||||||
|
range(self.widget.model().columnCount())}
|
||||||
|
# logger.debug(f"Row data: {row_data}")
|
||||||
|
logger.debug(f"Attempting to grab {self.objectName()} from {self.parent().omni_object}")
|
||||||
|
object = getattr(self.parent().omni_object, self.objectName())
|
||||||
|
logger.debug(f"Initial object: {object}")
|
||||||
|
if isinstance(object, list):
|
||||||
|
try:
|
||||||
|
object = next((item for item in object if item.check_all_attributes(attributes=row_data)))
|
||||||
|
except StopIteration:
|
||||||
|
logger.warning(f"Failed to find all attributes equal, getting row {id.row()}")
|
||||||
|
object = object[id.row()]
|
||||||
|
object.instance_object = object.to_sql()
|
||||||
|
logger.debug(f"Object of interest: {pformat(object.__dict__)}")
|
||||||
|
self.menu = QMenu(self)
|
||||||
|
try:
|
||||||
|
remove_action = QAction(f"Remove {object.name}", self)
|
||||||
|
except AttributeError:
|
||||||
|
remove_action = QAction(f"Remove object", self)
|
||||||
|
remove_action.triggered.connect(lambda: self.remove_item(object=object))
|
||||||
|
self.menu.addAction(remove_action)
|
||||||
|
try:
|
||||||
|
edit_action = QAction(f"Edit {object.name}", self)
|
||||||
|
except AttributeError:
|
||||||
|
edit_action = QAction(f"Edit object", self)
|
||||||
|
edit_action.triggered.connect(lambda: self.add_new(instance=object.instance_object, add_edit="edit"))
|
||||||
|
self.menu.addAction(edit_action)
|
||||||
|
self.menu.popup(QCursor.pos())
|
||||||
|
|
||||||
|
def remove_item(self, object):
|
||||||
|
logger.debug(f"Attempting to remove {object} from {self.parent().instance.__dict__}")
|
||||||
|
# editor = getattr(self.parent().instance, self.objectName().lower())
|
||||||
|
editor = getattr(self.parent().omni_object, self.objectName().lower())
|
||||||
|
logger.debug(f"Editor: {editor}")
|
||||||
|
# if object == self.parent().manager:
|
||||||
|
# logger.error(f"Can't remove manager object.")
|
||||||
|
# return
|
||||||
|
# logger.debug(f"Object: {object}")
|
||||||
|
# try:
|
||||||
|
# self.data.remove(object)
|
||||||
|
# except (AttributeError, ValueError) as e:
|
||||||
|
# logger.error(f"Couldn't remove object from self.data due to: {e}")
|
||||||
|
# self.data = []
|
||||||
|
try:
|
||||||
|
# logger.debug(f"Using remove technique")
|
||||||
|
editor.remove(object)
|
||||||
|
except AttributeError as e:
|
||||||
|
logger.error(f"Remove failed using set to None for {self.objectName().lower()}.")
|
||||||
|
setattr(self.parent().omni_object, self.objectName().lower(), None)
|
||||||
|
except ValueError as e:
|
||||||
|
logger.error(f"Remove failed for {self.objectName().lower()} due to {e}.")
|
||||||
|
logger.debug(f"Setting {self.objectName()} to {editor}")
|
||||||
|
setattr(self.parent().omni_object, self.objectName().lower(), editor)
|
||||||
|
logger.debug(f"After set: {getattr(self.parent().omni_object, self.objectName().lower())}")
|
||||||
|
# self.parent().instance.save()
|
||||||
|
# self.parent().update_data()
|
||||||
|
self.set_data()
|
||||||
|
self.update_buttons()
|
||||||
|
|
||||||
|
def parse_form(self):
|
||||||
|
# logger.debug(f"Returning parsed form data from {self.objectName()}: {self.data}")
|
||||||
|
try:
|
||||||
|
check = self.relationship.property.uselist
|
||||||
|
except AttributeError:
|
||||||
|
check = False
|
||||||
|
if check and isinstance(self.data, list):
|
||||||
|
output_data = self.data[0]
|
||||||
|
else:
|
||||||
|
output_data = self.data
|
||||||
|
return dict(field=self.objectName(), value=output_data)
|
||||||
|
|
||||||
|
|
||||||
|
class JsonEditButton(QWidget):
|
||||||
|
|
||||||
|
def __init__(self, parent, key: str, value: str = ""):
|
||||||
|
super().__init__(parent)
|
||||||
|
logger.debug(f"Setting jsonedit data to: {value}")
|
||||||
|
self.data = value
|
||||||
|
self.setParent(parent)
|
||||||
|
self.setObjectName(key)
|
||||||
|
self.addButton = QPushButton("Add Entry", parent=self)
|
||||||
|
self.addButton.clicked.connect(self.add_to_json)
|
||||||
|
self.viewButton = QPushButton("View >>>", parent=self)
|
||||||
|
self.viewButton.clicked.connect(self.toggle_textedit)
|
||||||
|
self.layout = QGridLayout()
|
||||||
|
self.layout.addWidget(self.addButton, 0, 0)
|
||||||
|
self.layout.addWidget(self.viewButton, 0, 1)
|
||||||
|
self.setLayout(self.layout)
|
||||||
|
self.edit_box = LargeTextEdit(parent=self, key=key)
|
||||||
|
self.parent().parent().layout.addWidget(self.edit_box, 1, self.parent().parent().layout.columnCount(),
|
||||||
|
self.parent().parent().layout.rowCount() - 1, 1)
|
||||||
|
self.edit_box.setVisible(False)
|
||||||
|
self.edit_box.widget.textChanged.connect(self.set_json_to_text)
|
||||||
|
|
||||||
|
def set_json_to_text(self):
|
||||||
|
logger.debug(self.edit_box.widget.toPlainText())
|
||||||
|
text = self.edit_box.widget.toPlainText()
|
||||||
|
try:
|
||||||
|
jsoner = json.loads(text)
|
||||||
|
except JSONDecodeError:
|
||||||
|
jsoner = None
|
||||||
|
if jsoner:
|
||||||
|
self.data = jsoner
|
||||||
|
|
||||||
|
def add_to_json(self):
|
||||||
|
jsonedit = JsonEditScreen(parent=self, parameter=self.objectName())
|
||||||
|
if jsonedit.exec():
|
||||||
|
data = jsonedit.parse_form()
|
||||||
|
# logger.debug(f"Data: {pformat(data)}")
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
def toggle_textedit(self):
|
||||||
|
self.edit_box.setVisible(not self.edit_box.isVisible())
|
||||||
|
# data = getattr(self.omni_object, name)
|
||||||
|
# logger.debug(f"Data: {data}")
|
||||||
|
data = json.dumps(self.data, indent=4)
|
||||||
|
self.edit_box.widget.setText(data)
|
||||||
|
|
||||||
|
|
||||||
|
class JsonEditScreen(QDialog):
|
||||||
|
|
||||||
|
def __init__(self, parent, parameter: str):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.class_obj = parent.parent().parent().class_object
|
||||||
|
self.layout = QGridLayout()
|
||||||
|
# logger.debug(f"Parameter: {parameter}")
|
||||||
|
self.setWindowTitle(parameter)
|
||||||
|
try:
|
||||||
|
self.json_field = getattr(self.class_obj, f"{parameter}_json_edit_fields")
|
||||||
|
except AttributeError:
|
||||||
|
self.json_field = self.class_obj.json_edit_fields
|
||||||
|
match self.json_field:
|
||||||
|
case dict():
|
||||||
|
for key, value in self.json_field.items():
|
||||||
|
# logger.debug(f"Key: {key}, Value: {value}")
|
||||||
|
row = self.layout.rowCount()
|
||||||
|
self.layout.addWidget(QLabel(key), row, 0)
|
||||||
|
match value:
|
||||||
|
case "int":
|
||||||
|
self.widget = QSpinBox()
|
||||||
|
case "str":
|
||||||
|
self.widget = QLineEdit()
|
||||||
|
case dict():
|
||||||
|
self.widget = DictionaryJsonSubEdit(parent=self, key=key, dic=value)
|
||||||
|
case _:
|
||||||
|
continue
|
||||||
|
self.widget.setObjectName(key)
|
||||||
|
self.layout.addWidget(self.widget, row, 1)
|
||||||
|
QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
|
||||||
|
self.buttonBox = QDialogButtonBox(QBtn)
|
||||||
|
self.buttonBox.accepted.connect(self.accept)
|
||||||
|
self.buttonBox.rejected.connect(self.reject)
|
||||||
|
self.layout.addWidget(self.buttonBox, self.layout.rowCount(), 0, 1, 2)
|
||||||
|
self.setLayout(self.layout)
|
||||||
|
|
||||||
|
def parse_form(self):
|
||||||
|
widgets = [item for item in self.findChildren(QWidget) if item.objectName() in self.json_field.keys()]
|
||||||
|
# logger.debug(f"Widgets: {widgets}")
|
||||||
|
# logger.debug(type(self.json_field))
|
||||||
|
if isinstance(self.json_field, dict):
|
||||||
|
output = {}
|
||||||
|
elif isinstance(self.json_field, list):
|
||||||
|
output = []
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Inappropriate data type: {type(self.json_field)}")
|
||||||
|
for widget in widgets:
|
||||||
|
# logger.debug(f"JsonEditScreen Widget: {widget}")
|
||||||
|
key = widget.objectName()
|
||||||
|
match widget:
|
||||||
|
case QSpinBox():
|
||||||
|
value = widget.value()
|
||||||
|
case QLineEdit():
|
||||||
|
value = widget.text()
|
||||||
|
case DictionaryJsonSubEdit():
|
||||||
|
value = widget.parse_form()
|
||||||
|
case _:
|
||||||
|
continue
|
||||||
|
if isinstance(self.json_field, dict):
|
||||||
|
output[key] = value
|
||||||
|
elif isinstance(self.json_field, list):
|
||||||
|
if isinstance(value, list):
|
||||||
|
output += value
|
||||||
|
else:
|
||||||
|
output.append(value)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Inappropriate data type: {type(self.json_field)}")
|
||||||
|
# output[key] = value
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
class DictionaryJsonSubEdit(QWidget):
|
||||||
|
|
||||||
|
def __init__(self, parent, key, dic: dict):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.layout = QHBoxLayout()
|
||||||
|
self.setObjectName(key)
|
||||||
|
self.data = dic
|
||||||
|
for key, value in self.data.items():
|
||||||
|
self.layout.addWidget(QLabel(key))
|
||||||
|
match value:
|
||||||
|
case "int":
|
||||||
|
self.widget = QSpinBox()
|
||||||
|
case "str":
|
||||||
|
self.widget = QLineEdit()
|
||||||
|
case dict():
|
||||||
|
self.widget = DictionaryJsonSubEdit(parent, key=key, dic=value)
|
||||||
|
self.widget.setObjectName(key)
|
||||||
|
self.layout.addWidget(self.widget)
|
||||||
|
self.setLayout(self.layout)
|
||||||
|
|
||||||
|
def parse_form(self):
|
||||||
|
widgets = [item for item in self.findChildren(QWidget) if item.objectName() in self.data.keys()]
|
||||||
|
# logger.debug(f"Widgets: {widgets}")
|
||||||
|
output = {}
|
||||||
|
for widget in widgets:
|
||||||
|
# logger.debug(f"DictionaryJsonSubEdit Widget: {widget}")
|
||||||
|
key = widget.objectName()
|
||||||
|
match widget:
|
||||||
|
case QSpinBox():
|
||||||
|
value = widget.value()
|
||||||
|
case QLineEdit():
|
||||||
|
value = widget.text()
|
||||||
|
case DictionaryJsonSubEdit():
|
||||||
|
value = widget.parse_form()
|
||||||
|
case _:
|
||||||
|
continue
|
||||||
|
output[key] = value
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
class LargeTextEdit(QWidget):
|
||||||
|
|
||||||
|
def __init__(self, parent, key: str):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setParent(parent)
|
||||||
|
self.setObjectName(key)
|
||||||
|
self.widget = QTextEdit()
|
||||||
|
self.layout = QVBoxLayout()
|
||||||
|
self.layout.addWidget(self.widget)
|
||||||
|
self.setLayout(self.layout)
|
||||||
Reference in New Issue
Block a user