Mid clean-up.
This commit is contained in:
6
TODO.md
6
TODO.md
@@ -1,4 +1,6 @@
|
||||
- [ ] Find a way to merge AddEdit with ReagentAdder
|
||||
- [ ] Stop displacing date on Irida controls and just do what Turnaround time does.
|
||||
- [ ] Get Manager window working for KitType, maybe SubmissionType
|
||||
- [x] Find a way to merge AddEdit with ReagentAdder
|
||||
- [x] Find a way to merge omni_search and sample_search
|
||||
- [x] Allow parsing of custom fields to a json 'custom' field in _basicsubmissions
|
||||
- [x] Upgrade to generators when returning lists.
|
||||
@@ -13,7 +15,7 @@
|
||||
- [x] Fix Artic RSLNamer
|
||||
- [x] Put "Not applicable" reagents in to_dict() method.
|
||||
- Currently in to_pydantic().
|
||||
- [x] Critical: Convert Json lits to dicts so I can have them update properly without using crashy Sqlalchemy-json
|
||||
- [x] Critical: Convert Json list to dicts so I can have them update properly without using crashy Sqlalchemy-json
|
||||
- Was actually not necessary.
|
||||
- [x] Fix Parsed/Missing mix ups.
|
||||
- [x] Have sample parser check for controls and add to reagents?
|
||||
|
||||
@@ -4,6 +4,7 @@ Contains all models for sqlalchemy
|
||||
from __future__ import annotations
|
||||
import sys, logging
|
||||
from pandas import DataFrame
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import Column, INTEGER, String, JSON
|
||||
from sqlalchemy.orm import DeclarativeMeta, declarative_base, Query, Session
|
||||
from sqlalchemy.ext.declarative import declared_attr
|
||||
@@ -17,6 +18,7 @@ from tools import report_result
|
||||
if 'pytest' in sys.modules:
|
||||
sys.path.append(Path(__file__).parents[4].absolute().joinpath("tests").__str__())
|
||||
|
||||
# NOTE: For inheriting in LogMixin
|
||||
Base: DeclarativeMeta = declarative_base()
|
||||
|
||||
logger = logging.getLogger(f"submissions.{__name__}")
|
||||
@@ -31,6 +33,7 @@ class LogMixin(Base):
|
||||
if len(name) > 64:
|
||||
name = name.replace("<", "").replace(">", "")
|
||||
if len(name) > 64:
|
||||
# NOTE: As if re'agent'
|
||||
name = name.replace("agent", "")
|
||||
if len(name) > 64:
|
||||
name = f"...{name[-61:]}"
|
||||
@@ -116,7 +119,7 @@ class BaseClass(Base):
|
||||
return dict(singles=singles)
|
||||
|
||||
@classmethod
|
||||
def find_regular_subclass(cls, name: str | None = None) -> Any:
|
||||
def find_regular_subclass(cls, name: str = "") -> Any:
|
||||
"""
|
||||
|
||||
Args:
|
||||
@@ -126,8 +129,9 @@ class BaseClass(Base):
|
||||
Any: Subclass of this object
|
||||
|
||||
"""
|
||||
if not name:
|
||||
return cls
|
||||
# if not name:
|
||||
# logger.warning("You need to include a name of what you're looking for.")
|
||||
# return cls
|
||||
if " " in name:
|
||||
search = name.title().replace(" ", "")
|
||||
else:
|
||||
@@ -171,7 +175,7 @@ class BaseClass(Base):
|
||||
try:
|
||||
records = [obj.to_sub_dict(**kwargs) for obj in objects]
|
||||
except AttributeError:
|
||||
records = [obj.to_dict() for obj in objects]
|
||||
records = [obj.to_omnigui_dict() for obj in objects]
|
||||
return DataFrame.from_records(records)
|
||||
|
||||
@classmethod
|
||||
@@ -190,7 +194,7 @@ class BaseClass(Base):
|
||||
Execute sqlalchemy query with relevant defaults.
|
||||
|
||||
Args:
|
||||
model (Any, optional): model to be queried. Defaults to None
|
||||
model (Any, optional): model to be queried, allows for plugging in. Defaults to None
|
||||
query (Query, optional): input query object. Defaults to None
|
||||
limit (int): Maximum number of results. (0 = all). Defaults to 0
|
||||
|
||||
@@ -237,13 +241,28 @@ class BaseClass(Base):
|
||||
report.add_result(Result(msg=e, status="Critical"))
|
||||
return report
|
||||
|
||||
def to_dict(self):
|
||||
def to_omnigui_dict(self) -> dict:
|
||||
"""
|
||||
For getting any object in an omni-thing friendly output.
|
||||
|
||||
Returns:
|
||||
dict: Dictionary of object minus _sa_instance_state with id at the front.
|
||||
"""
|
||||
dicto = {k: v for k, v in self.__dict__.items() if k not in ["_sa_instance_state"]}
|
||||
dicto = {'id': dicto.pop('id'), **dicto}
|
||||
try:
|
||||
dicto = {'id': dicto.pop('id'), **dicto}
|
||||
except KeyError:
|
||||
pass
|
||||
return dicto
|
||||
|
||||
@classmethod
|
||||
def get_pydantic_model(cls):
|
||||
def get_pydantic_model(cls) -> BaseModel:
|
||||
"""
|
||||
Gets the pydantic model corresponding to this object.
|
||||
|
||||
Returns:
|
||||
Pydantic model with name "Pyd{cls.__name__}"
|
||||
"""
|
||||
from backend.validators import pydant
|
||||
try:
|
||||
model = getattr(pydant, f"Pyd{cls.__name__}")
|
||||
@@ -252,7 +271,13 @@ class BaseClass(Base):
|
||||
return model
|
||||
|
||||
@classproperty
|
||||
def add_edit_tooltips(self):
|
||||
def add_edit_tooltips(self) -> dict:
|
||||
"""
|
||||
Gets tooltips for Omni-add-edit
|
||||
|
||||
Returns:
|
||||
dict: custom dictionary for this class.
|
||||
"""
|
||||
return dict()
|
||||
|
||||
|
||||
@@ -270,7 +295,7 @@ class ConfigItem(BaseClass):
|
||||
@classmethod
|
||||
def get_config_items(cls, *args) -> ConfigItem | List[ConfigItem]:
|
||||
"""
|
||||
Get desired config items from database
|
||||
Get desired config items, or all from database
|
||||
|
||||
Returns:
|
||||
ConfigItem|List[ConfigItem]: Config item(s)
|
||||
@@ -283,6 +308,7 @@ class ConfigItem(BaseClass):
|
||||
case 1:
|
||||
config_items = query.filter(cls.key == args[0]).first()
|
||||
case _:
|
||||
# NOTE: All items whose key field is in args.
|
||||
config_items = query.filter(cls.key.in_(args)).all()
|
||||
return config_items
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class AuditLog(Base):
|
||||
changes = Column(JSON)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.user} @ {self.time}>"
|
||||
return f"<{self.object}: {self.user} @ {self.time}>"
|
||||
|
||||
@classmethod
|
||||
def query(cls, start_date: date | str | int | None = None, end_date: date | str | int | None = None) -> List["AuditLog"]:
|
||||
|
||||
@@ -255,6 +255,7 @@ class Control(BaseClass):
|
||||
f"Could not get polymorph {polymorphic_identity} of {cls} due to {e}, falling back to BasicSubmission")
|
||||
case _:
|
||||
pass
|
||||
# NOTE: if attrs passed in and this cls doesn't have all attributes in attr
|
||||
if attrs and any([not hasattr(cls, attr) for attr in attrs.keys()]):
|
||||
# NOTE: looks for first model that has all included kwargs
|
||||
try:
|
||||
@@ -272,6 +273,9 @@ class Control(BaseClass):
|
||||
|
||||
Args:
|
||||
parent (QWidget): chart holding widget to add buttons to.
|
||||
|
||||
Returns:
|
||||
None: Child methods will return things.
|
||||
"""
|
||||
return None
|
||||
|
||||
@@ -284,7 +288,7 @@ class Control(BaseClass):
|
||||
chart_settings (dict): settings passed down from chart widget
|
||||
ctx (Settings): settings passed down from gui
|
||||
"""
|
||||
return None
|
||||
return Report(), None
|
||||
|
||||
def delete(self):
|
||||
self.__database_session__.delete(self)
|
||||
@@ -315,8 +319,14 @@ class PCRControl(Control):
|
||||
Returns:
|
||||
dict: Output dict of name, ct, subtype, target, reagent_lot and submitted_date
|
||||
"""
|
||||
return dict(name=self.name, ct=self.ct, subtype=self.subtype, target=self.target, reagent_lot=self.reagent_lot,
|
||||
submitted_date=self.submitted_date.date())
|
||||
return dict(
|
||||
name=self.name,
|
||||
ct=self.ct,
|
||||
subtype=self.subtype,
|
||||
target=self.target,
|
||||
reagent_lot=self.reagent_lot,
|
||||
submitted_date=self.submitted_date.date()
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@report_result
|
||||
@@ -403,9 +413,9 @@ class IridaControl(Control):
|
||||
kraken = self.kraken
|
||||
except TypeError:
|
||||
kraken = {}
|
||||
kraken_cnt_total = sum([kraken[item]['kraken_count'] for item in kraken])
|
||||
kraken_cnt_total = sum([item['kraken_count'] for item in kraken.values()])
|
||||
new_kraken = [dict(name=item, kraken_count=kraken[item]['kraken_count'],
|
||||
kraken_percent="{0:.0%}".format(kraken[item]['kraken_count'] / kraken_cnt_total),
|
||||
kraken_percent=f"{kraken[item]['kraken_count'] / kraken_cnt_total:0.2%}",
|
||||
target=item in self.controltype.targets)
|
||||
for item in kraken]
|
||||
new_kraken = sorted(new_kraken, key=itemgetter('kraken_count'), reverse=True)
|
||||
@@ -479,6 +489,7 @@ class IridaControl(Control):
|
||||
@classmethod
|
||||
def make_parent_buttons(cls, parent: QWidget) -> None:
|
||||
"""
|
||||
Creates buttons for controlling
|
||||
|
||||
Args:
|
||||
parent (QWidget): chart holding widget to add buttons to.
|
||||
@@ -486,6 +497,7 @@ class IridaControl(Control):
|
||||
"""
|
||||
super().make_parent_buttons(parent=parent)
|
||||
rows = parent.layout.rowCount() - 2
|
||||
# NOTE: check box for consolidating off-target items
|
||||
checker = QCheckBox(parent)
|
||||
checker.setChecked(True)
|
||||
checker.setObjectName("irida_check")
|
||||
@@ -703,6 +715,12 @@ class IridaControl(Control):
|
||||
df = df[df.name not in exclude]
|
||||
return df
|
||||
|
||||
def to_pydantic(self):
|
||||
def to_pydantic(self) -> "PydIridaControl":
|
||||
"""
|
||||
Constructs a pydantic version of this object.
|
||||
|
||||
Returns:
|
||||
PydIridaControl: This object as a pydantic model.
|
||||
"""
|
||||
from backend.validators import PydIridaControl
|
||||
return PydIridaControl(**self.__dict__)
|
||||
|
||||
@@ -129,8 +129,10 @@ class KitType(BaseClass):
|
||||
"""
|
||||
return f"<KitType({self.name})>"
|
||||
|
||||
def get_reagents(self, required: bool = False, submission_type: str | SubmissionType | None = None) -> Generator[
|
||||
ReagentRole, None, None]:
|
||||
def get_reagents(self,
|
||||
required: bool = False,
|
||||
submission_type: str | SubmissionType | None = None
|
||||
) -> Generator[ReagentRole, None, None]:
|
||||
"""
|
||||
Return ReagentTypes linked to kit through KitTypeReagentTypeAssociation.
|
||||
|
||||
@@ -192,13 +194,13 @@ class KitType(BaseClass):
|
||||
Lookup a list of or single KitType.
|
||||
|
||||
Args:
|
||||
name (str, optional): Name of desired kit (returns single instance). Defaults to None.
|
||||
used_for (str | Submissiontype | None, optional): Submission type the kit is used for. Defaults to None.
|
||||
id (int | None, optional): Kit id in the database. Defaults to None.
|
||||
limit (int, optional): Maximum number of results to return (0 = all). Defaults to 0.
|
||||
name (str, optional): Name of desired kit (returns single instance). Defaults to None.
|
||||
used_for (str | Submissiontype | None, optional): Submission type the kit is used for. Defaults to None.
|
||||
id (int | None, optional): Kit id in the database. Defaults to None.
|
||||
limit (int, optional): Maximum number of results to return (0 = all). Defaults to 0.
|
||||
|
||||
Returns:
|
||||
KitType|List[KitType]: KitType(s) of interest.
|
||||
KitType|List[KitType]: KitType(s) of interest.
|
||||
"""
|
||||
query: Query = cls.__database_session__.query(cls)
|
||||
match used_for:
|
||||
@@ -240,23 +242,23 @@ class KitType(BaseClass):
|
||||
dict: Dictionary containing relevant info for SubmissionType construction
|
||||
"""
|
||||
base_dict = dict(name=self.name, reagent_roles=[], equipment_roles=[])
|
||||
for k, v in self.construct_xl_map_for_use(submission_type=submission_type):
|
||||
for key, value in self.construct_xl_map_for_use(submission_type=submission_type):
|
||||
try:
|
||||
assoc = next(item for item in self.kit_reagentrole_associations if item.reagent_role.name == k)
|
||||
assoc = next(item for item in self.kit_reagentrole_associations if item.reagent_role.name == key)
|
||||
except StopIteration as e:
|
||||
continue
|
||||
for kk, vv in assoc.to_export_dict().items():
|
||||
v[kk] = vv
|
||||
base_dict['reagent_roles'].append(v)
|
||||
for k, v in submission_type.construct_field_map("equipment"):
|
||||
value[kk] = vv
|
||||
base_dict['reagent_roles'].append(value)
|
||||
for key, value in submission_type.construct_field_map("equipment"):
|
||||
try:
|
||||
assoc = next(item for item in submission_type.submissiontype_equipmentrole_associations if
|
||||
item.equipment_role.name == k)
|
||||
item.equipment_role.name == key)
|
||||
except StopIteration:
|
||||
continue
|
||||
for kk, vv in assoc.to_export_dict(extraction_kit=self).items():
|
||||
v[kk] = vv
|
||||
base_dict['equipment_roles'].append(v)
|
||||
value[kk] = vv
|
||||
base_dict['equipment_roles'].append(value)
|
||||
return base_dict
|
||||
|
||||
@classmethod
|
||||
@@ -402,6 +404,7 @@ class ReagentRole(BaseClass):
|
||||
case _:
|
||||
pass
|
||||
assert reagent.role
|
||||
# NOTE: Get all roles common to the reagent and the kit.
|
||||
result = set(kit_type.reagent_roles).intersection(reagent.role)
|
||||
return next((item for item in result), None)
|
||||
match name:
|
||||
@@ -500,7 +503,7 @@ class Reagent(BaseClass, LogMixin):
|
||||
except (TypeError, AttributeError) as e:
|
||||
place_holder = date.today()
|
||||
logger.error(f"We got a type error setting {self.lot} expiry: {e}. setting to today for testing")
|
||||
# NOTE: The notation for not having an expiry is 1970.1.1
|
||||
# NOTE: The notation for not having an expiry is 1970.01.01
|
||||
if self.expiry.year == 1970:
|
||||
place_holder = "NA"
|
||||
else:
|
||||
@@ -555,7 +558,7 @@ class Reagent(BaseClass, LogMixin):
|
||||
instance = PydReagent(**kwargs)
|
||||
new = True
|
||||
instance, _ = instance.toSQL()
|
||||
logger.debug(f"Instance: {instance}")
|
||||
logger.info(f"Instance from query or create: {instance}")
|
||||
return instance, new
|
||||
|
||||
@classmethod
|
||||
@@ -609,33 +612,70 @@ class Reagent(BaseClass, LogMixin):
|
||||
pass
|
||||
return cls.execute_query(query=query, limit=limit)
|
||||
|
||||
def set_attribute(self, key, value):
|
||||
match key:
|
||||
case "lot":
|
||||
value = value.upper()
|
||||
case "role":
|
||||
match value:
|
||||
case ReagentRole():
|
||||
role = value
|
||||
case str():
|
||||
role = ReagentRole.query(name=value, limit=1)
|
||||
case _:
|
||||
return
|
||||
if role and role not in self.role:
|
||||
self.role.append(role)
|
||||
return
|
||||
case "comment":
|
||||
return
|
||||
case "expiry":
|
||||
if isinstance(value, str):
|
||||
value = date(year=1970, month=1, day=1)
|
||||
# NOTE: if min time is used, any reagent set to expire today (Bac postive control, eg) will have expired at midnight and therefore be flagged.
|
||||
# NOTE: Make expiry at date given, plus maximum time = end of day
|
||||
value = datetime.combine(value, datetime.max.time())
|
||||
value = value.replace(tzinfo=timezone)
|
||||
case _:
|
||||
pass
|
||||
logger.debug(f"Role to be set to: {value}")
|
||||
try:
|
||||
self.__setattr__(key, value)
|
||||
except AttributeError as e:
|
||||
logger.error(f"Could not set {key} due to {e}")
|
||||
|
||||
|
||||
@check_authorization
|
||||
def edit_from_search(self, obj, **kwargs):
|
||||
from frontend.widgets.misc import AddReagentForm
|
||||
from frontend.widgets.omni_add_edit import AddEdit
|
||||
role = ReagentRole.query(kwargs['role'])
|
||||
if role:
|
||||
role_name = role.name
|
||||
else:
|
||||
role_name = None
|
||||
dlg = AddReagentForm(reagent_lot=self.lot, reagent_role=role_name, expiry=self.expiry, reagent_name=self.name)
|
||||
# dlg = AddReagentForm(reagent_lot=self.lot, reagent_role=role_name, expiry=self.expiry, reagent_name=self.name)
|
||||
dlg = AddEdit(parent=None, instance=self)
|
||||
if dlg.exec():
|
||||
vars = dlg.parse_form()
|
||||
for key, value in vars.items():
|
||||
match key:
|
||||
case "expiry":
|
||||
if isinstance(value, str):
|
||||
field_value = datetime.strptime(value, "%Y-%m-%d")
|
||||
elif isinstance(value, date):
|
||||
field_value = datetime.combine(value, datetime.max.time())
|
||||
else:
|
||||
field_value = value
|
||||
field_value.replace(tzinfo=timezone)
|
||||
case "role":
|
||||
continue
|
||||
case _:
|
||||
field_value = value
|
||||
self.__setattr__(key, field_value)
|
||||
pyd = dlg.parse_form()
|
||||
for field in pyd.model_fields:
|
||||
self.set_attribute(field, pyd.__getattribute__(field))
|
||||
# for key, value in vars.items():
|
||||
# match key:
|
||||
# case "expiry":
|
||||
# if isinstance(value, str):
|
||||
# field_value = datetime.strptime(value, "%Y-%m-%d")
|
||||
# elif isinstance(value, date):
|
||||
# field_value = datetime.combine(value, datetime.max.time())
|
||||
# else:
|
||||
# field_value = value
|
||||
# field_value.replace(tzinfo=timezone)
|
||||
# case "role":
|
||||
# continue
|
||||
# case _:
|
||||
# field_value = value
|
||||
# self.__setattr__(key, field_value)
|
||||
self.save()
|
||||
# print(self.__dict__)
|
||||
|
||||
@classproperty
|
||||
def add_edit_tooltips(self):
|
||||
@@ -767,7 +807,7 @@ class SubmissionType(BaseClass):
|
||||
Grabs the default excel template file.
|
||||
|
||||
Returns:
|
||||
bytes: The excel sheet.
|
||||
bytes: The Excel sheet.
|
||||
"""
|
||||
submission_type = cls.query(name="Bacterial Culture")
|
||||
return submission_type.template_file
|
||||
@@ -787,13 +827,13 @@ class SubmissionType(BaseClass):
|
||||
def set_template_file(self, filepath: Path | str):
|
||||
"""
|
||||
|
||||
Sets the binary store to an excel file.
|
||||
Sets the binary store to an Excel file.
|
||||
|
||||
Args:
|
||||
filepath (Path | str): Path to the template file.
|
||||
|
||||
Raises:
|
||||
ValueError: Raised if file is not excel file.
|
||||
ValueError: Raised if file is not Excel file.
|
||||
"""
|
||||
if isinstance(filepath, str):
|
||||
filepath = Path(filepath)
|
||||
|
||||
@@ -375,7 +375,10 @@ class BasicSubmission(BaseClass, LogMixin):
|
||||
output["contact_phone"] = contact_phone
|
||||
output["custom"] = custom
|
||||
output["controls"] = controls
|
||||
output["completed_date"] = self.completed_date
|
||||
try:
|
||||
output["completed_date"] = self.completed_date.strftime("%Y-%m-%d")
|
||||
except AttributeError:
|
||||
output["completed_date"] = self.completed_date
|
||||
return output
|
||||
|
||||
def calculate_column_count(self) -> int:
|
||||
|
||||
@@ -3,6 +3,7 @@ contains writer objects for pushing values to submission sheet templates.
|
||||
"""
|
||||
import logging
|
||||
from copy import copy
|
||||
from datetime import datetime
|
||||
from operator import itemgetter
|
||||
from pprint import pformat
|
||||
from typing import List, Generator, Tuple
|
||||
|
||||
@@ -48,7 +48,7 @@ class PydReagent(BaseModel):
|
||||
def rescue_type_with_lookup(cls, value, values):
|
||||
if value is None and values.data['lot'] is not None:
|
||||
try:
|
||||
return Reagent.query(lot=values.data['lot'].name)
|
||||
return Reagent.query(lot=values.data['lot']).name
|
||||
except AttributeError:
|
||||
return value
|
||||
return value
|
||||
@@ -133,28 +133,8 @@ class PydReagent(BaseModel):
|
||||
for key, value in self.__dict__.items():
|
||||
if isinstance(value, dict):
|
||||
value = value['value']
|
||||
# NOTE: set fields based on keys in dictionary
|
||||
match key:
|
||||
case "lot":
|
||||
reagent.lot = value.upper()
|
||||
case "role":
|
||||
reagent_role = ReagentRole.query(name=value)
|
||||
if reagent_role is not None:
|
||||
reagent.role.append(reagent_role)
|
||||
case "comment":
|
||||
continue
|
||||
case "expiry":
|
||||
if isinstance(value, str):
|
||||
value = date(year=1970, month=1, day=1)
|
||||
# NOTE: if min time is used, any reagent set to expire today (Bac postive control, eg) will have expired at midnight and therefore be flagged.
|
||||
# NOTE: Make expiry at date given, plus now time + 1 hour
|
||||
value = datetime.combine(value, datetime.max.time())
|
||||
reagent.expiry = value.replace(tzinfo=timezone)
|
||||
case _:
|
||||
try:
|
||||
reagent.__setattr__(key, value)
|
||||
except AttributeError:
|
||||
logger.error(f"Couldn't set {key} to {value}")
|
||||
# NOTE: reagent method sets fields based on keys in dictionary
|
||||
reagent.set_attribute(key, value)
|
||||
if submission is not None and reagent not in submission.reagents:
|
||||
assoc = SubmissionReagentAssociation(reagent=reagent, submission=submission)
|
||||
assoc.comments = self.comment
|
||||
@@ -830,7 +810,7 @@ class PydSubmission(BaseModel, extra='allow'):
|
||||
case item if item in instance.timestamps():
|
||||
logger.warning(f"Incoming timestamp key: {item}, with value: {value}")
|
||||
if isinstance(value, date):
|
||||
value = datetime.combine(value, datetime.max.time())
|
||||
value = datetime.combine(value, datetime.now().time())
|
||||
value = value.replace(tzinfo=timezone)
|
||||
elif isinstance(value, str):
|
||||
value: datetime = datetime.strptime(value, "%Y-%m-%d")
|
||||
|
||||
@@ -12,7 +12,7 @@ from PyQt6.QtGui import QAction
|
||||
from pathlib import Path
|
||||
from markdown import markdown
|
||||
from __init__ import project_path
|
||||
from backend import SubmissionType, Reagent, BasicSample, Organization
|
||||
from backend import SubmissionType, Reagent, BasicSample, Organization, KitType
|
||||
from tools import check_if_app, Settings, Report, jinja_template_loading, check_authorization, page_size, is_power_user
|
||||
from .functions import select_save_file, select_open_file
|
||||
# from datetime import date
|
||||
@@ -84,6 +84,7 @@ class App(QMainWindow):
|
||||
maintenanceMenu.addAction(self.joinPCRAction)
|
||||
editMenu.addAction(self.editReagentAction)
|
||||
editMenu.addAction(self.manageOrgsAction)
|
||||
# editMenu.addAction(self.manageKitsAction)
|
||||
if not is_power_user():
|
||||
editMenu.setEnabled(False)
|
||||
|
||||
@@ -111,6 +112,7 @@ class App(QMainWindow):
|
||||
self.yamlImportAction = QAction("Import Type Template", self)
|
||||
self.editReagentAction = QAction("Edit Reagent", self)
|
||||
self.manageOrgsAction = QAction("Manage Clients", self)
|
||||
self.manageKitsAction = QAction("Manage Kits", self)
|
||||
|
||||
def _connectActions(self):
|
||||
"""
|
||||
@@ -129,6 +131,7 @@ class App(QMainWindow):
|
||||
self.table_widget.pager.current_page.textChanged.connect(self.update_data)
|
||||
self.editReagentAction.triggered.connect(self.edit_reagent)
|
||||
self.manageOrgsAction.triggered.connect(self.manage_orgs)
|
||||
self.manageKitsAction.triggered.connect(self.manage_kits)
|
||||
|
||||
def showAbout(self):
|
||||
"""
|
||||
@@ -219,6 +222,11 @@ class App(QMainWindow):
|
||||
new_org = dlg.parse_form()
|
||||
# logger.debug(new_org.__dict__)
|
||||
|
||||
def manage_kits(self):
|
||||
dlg = ManagerWindow(parent=self, object_type=KitType, extras=[])
|
||||
if dlg.exec():
|
||||
print(dlg.parse_form())
|
||||
|
||||
class AddSubForm(QWidget):
|
||||
|
||||
def __init__(self, parent: QWidget):
|
||||
|
||||
@@ -11,7 +11,7 @@ import logging
|
||||
|
||||
from sqlalchemy.orm.relationships import _RelationshipDeclared
|
||||
|
||||
from tools import Report, Result
|
||||
from tools import Report, Result, report_result
|
||||
|
||||
logger = logging.getLogger(f"submissions.{__name__}")
|
||||
|
||||
@@ -23,7 +23,7 @@ class AddEdit(QDialog):
|
||||
self.instance = instance
|
||||
self.object_type = instance.__class__
|
||||
self.layout = QGridLayout(self)
|
||||
logger.debug(f"Manager: {manager}")
|
||||
# logger.debug(f"Manager: {manager}")
|
||||
QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
|
||||
self.buttonBox = QDialogButtonBox(QBtn)
|
||||
self.buttonBox.accepted.connect(self.accept)
|
||||
@@ -36,7 +36,7 @@ class AddEdit(QDialog):
|
||||
fields = {'name': fields.pop('name'), **fields}
|
||||
except KeyError:
|
||||
pass
|
||||
logger.debug(pformat(fields, indent=4))
|
||||
# logger.debug(pformat(fields, indent=4))
|
||||
height_counter = 0
|
||||
for key, field in fields.items():
|
||||
try:
|
||||
@@ -47,7 +47,7 @@ class AddEdit(QDialog):
|
||||
logger.debug(f"{key} property: {type(field['class_attr'].property)}")
|
||||
# widget = EditProperty(self, key=key, column_type=field.property.expression.type,
|
||||
# value=getattr(self.instance, key))
|
||||
logger.debug(f"Column type: {field}, Value: {value}")
|
||||
# logger.debug(f"Column type: {field}, Value: {value}")
|
||||
widget = EditProperty(self, key=key, column_type=field, value=value)
|
||||
except AttributeError as e:
|
||||
logger.error(f"Problem setting widget {key}: {e}")
|
||||
@@ -60,6 +60,7 @@ class AddEdit(QDialog):
|
||||
self.setMinimumSize(600, 50 * height_counter)
|
||||
self.setLayout(self.layout)
|
||||
|
||||
@report_result
|
||||
def parse_form(self) -> Tuple[BaseModel, Report]:
|
||||
report = Report()
|
||||
parsed = {result[0].strip(":"): result[1] for result in [item.parse_form() for item in self.findChildren(EditProperty)] if result[0]}
|
||||
|
||||
@@ -188,6 +188,8 @@ class EditRelationship(QWidget):
|
||||
dlg = AddEdit(self, instance=instance, manager=self.parent().object_type.__name__.lower())
|
||||
if dlg.exec():
|
||||
new_instance = dlg.parse_form()
|
||||
new_instance, result = new_instance.toSQL()
|
||||
logger.debug(f"New instance: {new_instance}")
|
||||
addition = getattr(self.parent().instance, self.objectName())
|
||||
if isinstance(addition, InstrumentedList):
|
||||
addition.append(new_instance)
|
||||
@@ -211,7 +213,7 @@ class EditRelationship(QWidget):
|
||||
sets data in model
|
||||
"""
|
||||
# logger.debug(self.data)
|
||||
self.data = DataFrame.from_records([item.to_dict() for item in self.data])
|
||||
self.data = DataFrame.from_records([item.to_omnigui_dict() for item in self.data])
|
||||
try:
|
||||
self.columns_of_interest = [dict(name=item, column=self.data.columns.get_loc(item)) for item in self.extras]
|
||||
except (KeyError, AttributeError):
|
||||
|
||||
@@ -68,7 +68,11 @@ class SearchBox(QDialog):
|
||||
self.object_type = self.original_type
|
||||
else:
|
||||
self.object_type = self.original_type.find_regular_subclass(self.sub_class.currentText())
|
||||
for iii, searchable in enumerate(self.object_type.searchables):
|
||||
try:
|
||||
search_fields = self.object_type.searchables
|
||||
except AttributeError:
|
||||
search_fields = []
|
||||
for iii, searchable in enumerate(search_fields):
|
||||
widget = FieldSearch(parent=self, label=searchable, field_name=searchable)
|
||||
widget.setObjectName(searchable)
|
||||
self.layout.addWidget(widget, 1 + iii, 0)
|
||||
@@ -142,7 +146,10 @@ class SearchResults(QTableView):
|
||||
self.context = kwargs
|
||||
self.parent = parent
|
||||
self.object_type = object_type
|
||||
self.extras = extras + self.object_type.searchables
|
||||
try:
|
||||
self.extras = extras + self.object_type.searchables
|
||||
except AttributeError:
|
||||
self.extras = extras
|
||||
|
||||
def setData(self, df: DataFrame) -> None:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user