Added ability to not import reagents on first import.
This commit is contained in:
@@ -1,3 +1,11 @@
|
|||||||
|
## 202412.03
|
||||||
|
|
||||||
|
- Automated truncating of object names longer than 64 chars going into _auditlog
|
||||||
|
- Writer will now blank out the lookup table before writing to ensure removal of extraneous help info.
|
||||||
|
- Added support for running startup and teardown scripts.
|
||||||
|
- Created startup script to pull irida controls from secondary database.
|
||||||
|
- Added ability to not import reagents on first import.
|
||||||
|
|
||||||
## 202412.02
|
## 202412.02
|
||||||
|
|
||||||
- Addition of turnaround time tracking
|
- Addition of turnaround time tracking
|
||||||
|
|||||||
@@ -1,17 +1,50 @@
|
|||||||
import sys, os
|
import sys, os
|
||||||
from tools import ctx, setup_logger, check_if_app
|
from tools import ctx, setup_logger, check_if_app
|
||||||
|
from backend import scripts
|
||||||
|
|
||||||
# environment variable must be set to enable qtwebengine in network path
|
# environment variable must be set to enable qtwebengine in network path
|
||||||
if check_if_app():
|
if check_if_app():
|
||||||
os.environ['QTWEBENGINE_DISABLE_SANDBOX'] = "1"
|
os.environ['QTWEBENGINE_DISABLE_SANDBOX'] = "1"
|
||||||
|
|
||||||
# setup custom logger
|
# setup custom logger
|
||||||
logger = setup_logger(verbosity=3)
|
logger = setup_logger(verbosity=3)
|
||||||
# create settings object
|
|
||||||
|
|
||||||
from PyQt6.QtWidgets import QApplication
|
from PyQt6.QtWidgets import QApplication
|
||||||
from frontend.widgets.app import App
|
from frontend.widgets.app import App
|
||||||
|
|
||||||
|
|
||||||
|
def run_startup():
|
||||||
|
try:
|
||||||
|
startup_scripts = ctx.startup_scripts
|
||||||
|
except AttributeError as e:
|
||||||
|
logger.error(f"Couldn't get startup scripts due to {e}")
|
||||||
|
return
|
||||||
|
for script in startup_scripts:
|
||||||
|
try:
|
||||||
|
func = getattr(scripts, script)
|
||||||
|
except AttributeError as e:
|
||||||
|
logger.error(f"Couldn't run startup script {script} due to {e}")
|
||||||
|
continue
|
||||||
|
func(ctx)
|
||||||
|
|
||||||
|
|
||||||
|
def run_teardown():
|
||||||
|
try:
|
||||||
|
teardown_scripts = ctx.teardown_scripts
|
||||||
|
except AttributeError as e:
|
||||||
|
logger.error(f"Couldn't get teardown scripts due to {e}")
|
||||||
|
return
|
||||||
|
for script in teardown_scripts:
|
||||||
|
try:
|
||||||
|
func = getattr(scripts, script)
|
||||||
|
except AttributeError as e:
|
||||||
|
logger.error(f"Couldn't run teardown script {script} due to {e}")
|
||||||
|
continue
|
||||||
|
func(ctx)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
run_startup()
|
||||||
app = QApplication(['', '--no-sandbox'])
|
app = QApplication(['', '--no-sandbox'])
|
||||||
ex = App(ctx=ctx)
|
ex = App(ctx=ctx)
|
||||||
sys.exit(app.exec())
|
app.exec()
|
||||||
|
sys.exit(run_teardown())
|
||||||
|
|||||||
@@ -37,10 +37,11 @@ from .models import *
|
|||||||
|
|
||||||
|
|
||||||
def update_log(mapper, connection, target):
|
def update_log(mapper, connection, target):
|
||||||
logger.debug("\n\nBefore update\n\n")
|
# logger.debug("\n\nBefore update\n\n")
|
||||||
state = inspect(target)
|
state = inspect(target)
|
||||||
# logger.debug(state)
|
# logger.debug(state)
|
||||||
update = dict(user=getuser(), time=datetime.now(), object=str(state.object), changes=[])
|
object_name = state.object.truncated_name()
|
||||||
|
update = dict(user=getuser(), time=datetime.now(), object=object_name, changes=[])
|
||||||
# logger.debug(update)
|
# logger.debug(update)
|
||||||
for attr in state.attrs:
|
for attr in state.attrs:
|
||||||
hist = attr.load_history()
|
hist = attr.load_history()
|
||||||
@@ -49,8 +50,10 @@ def update_log(mapper, connection, target):
|
|||||||
if attr.key == "custom":
|
if attr.key == "custom":
|
||||||
continue
|
continue
|
||||||
added = [str(item) for item in hist.added]
|
added = [str(item) for item in hist.added]
|
||||||
if attr.key in ['submission_sample_associations', 'submission_reagent_associations']:
|
if attr.key in ['artic_technician', 'submission_sample_associations', 'submission_reagent_associations',
|
||||||
added = ['Numbers truncated for space purposes.']
|
'submission_equipment_associations', 'submission_tips_associations', 'contact_id', 'gel_info',
|
||||||
|
'gel_controls', 'source_plates']:
|
||||||
|
continue
|
||||||
deleted = [str(item) for item in hist.deleted]
|
deleted = [str(item) for item in hist.deleted]
|
||||||
change = dict(field=attr.key, added=added, deleted=deleted)
|
change = dict(field=attr.key, added=added, deleted=deleted)
|
||||||
# logger.debug(f"Adding: {pformat(change)}")
|
# logger.debug(f"Adding: {pformat(change)}")
|
||||||
|
|||||||
@@ -25,6 +25,16 @@ logger = logging.getLogger(f"submissions.{__name__}")
|
|||||||
class LogMixin(Base):
|
class LogMixin(Base):
|
||||||
__abstract__ = True
|
__abstract__ = True
|
||||||
|
|
||||||
|
def truncated_name(self):
|
||||||
|
name = str(self)
|
||||||
|
if len(name) > 64:
|
||||||
|
name = name.replace("<", "").replace(">", "")
|
||||||
|
if len(name) > 64:
|
||||||
|
name = name.replace("agent", "")
|
||||||
|
if len(name) > 64:
|
||||||
|
name = f"...{name[-61:]}"
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
class BaseClass(Base):
|
class BaseClass(Base):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -539,7 +539,7 @@ class IridaControl(Control):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
consolidate = False
|
consolidate = False
|
||||||
report = Report()
|
report = Report()
|
||||||
logger.debug(f"settings: {pformat(chart_settings)}")
|
# logger.debug(f"settings: {pformat(chart_settings)}")
|
||||||
controls = cls.query(subtype=chart_settings['sub_type'], start_date=chart_settings['start_date'],
|
controls = cls.query(subtype=chart_settings['sub_type'], start_date=chart_settings['start_date'],
|
||||||
end_date=chart_settings['end_date'])
|
end_date=chart_settings['end_date'])
|
||||||
# logger.debug(f"Controls found: {controls}")
|
# logger.debug(f"Controls found: {controls}")
|
||||||
|
|||||||
@@ -427,9 +427,10 @@ class Reagent(BaseClass, LogMixin):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if self.name:
|
if self.name:
|
||||||
return f"<Reagent({self.name}-{self.lot})>"
|
name = f"<Reagent({self.name}-{self.lot})>"
|
||||||
else:
|
else:
|
||||||
return f"<Reagent({self.role.name}-{self.lot})>"
|
name = f"<Reagent({self.role.name}-{self.lot})>"
|
||||||
|
return name
|
||||||
|
|
||||||
def to_sub_dict(self, extraction_kit: KitType = None, full_data: bool = False, **kwargs) -> dict:
|
def to_sub_dict(self, extraction_kit: KitType = None, full_data: bool = False, **kwargs) -> dict:
|
||||||
"""
|
"""
|
||||||
@@ -1347,7 +1348,7 @@ class SubmissionReagentAssociation(BaseClass):
|
|||||||
return PydReagent(**self.to_sub_dict(extraction_kit=extraction_kit))
|
return PydReagent(**self.to_sub_dict(extraction_kit=extraction_kit))
|
||||||
|
|
||||||
|
|
||||||
class Equipment(BaseClass):
|
class Equipment(BaseClass, LogMixin):
|
||||||
"""
|
"""
|
||||||
A concrete instance of equipment
|
A concrete instance of equipment
|
||||||
"""
|
"""
|
||||||
@@ -1851,7 +1852,7 @@ class TipRole(BaseClass):
|
|||||||
super().save()
|
super().save()
|
||||||
|
|
||||||
|
|
||||||
class Tips(BaseClass):
|
class Tips(BaseClass, LogMixin):
|
||||||
"""
|
"""
|
||||||
A concrete instance of tips.
|
A concrete instance of tips.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -174,7 +174,8 @@ class BasicSubmission(BaseClass, LogMixin):
|
|||||||
'platemap', 'export_map', 'equipment', 'tips', 'custom'],
|
'platemap', 'export_map', 'equipment', 'tips', 'custom'],
|
||||||
# NOTE: Fields not placed in ui form
|
# NOTE: Fields not placed in ui form
|
||||||
form_ignore=['reagents', 'ctx', 'id', 'cost', 'extraction_info', 'signed_by', 'comment', 'namer',
|
form_ignore=['reagents', 'ctx', 'id', 'cost', 'extraction_info', 'signed_by', 'comment', 'namer',
|
||||||
'submission_object', "tips", 'contact_phone', 'custom', 'cost_centre'] + recover,
|
'submission_object', "tips", 'contact_phone', 'custom', 'cost_centre', 'completed_date',
|
||||||
|
'controls'] + recover,
|
||||||
# NOTE: Fields not placed in ui form to be moved to pydantic
|
# NOTE: Fields not placed in ui form to be moved to pydantic
|
||||||
form_recover=recover
|
form_recover=recover
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ contains writer objects for pushing values to submission sheet templates.
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
from datetime import date
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from typing import List, Generator, Tuple
|
from typing import List, Generator, Tuple
|
||||||
@@ -214,6 +215,10 @@ class ReagentWriter(object):
|
|||||||
Returns:
|
Returns:
|
||||||
List[dict]: merged dictionary
|
List[dict]: merged dictionary
|
||||||
"""
|
"""
|
||||||
|
filled_roles = [item['role'] for item in reagent_list]
|
||||||
|
for map_obj in reagent_map.keys():
|
||||||
|
if map_obj not in filled_roles:
|
||||||
|
reagent_list.append(dict(name="Not Applicable", role=map_obj, lot="Not Applicable", expiry="Not Applicable"))
|
||||||
for reagent in reagent_list:
|
for reagent in reagent_list:
|
||||||
try:
|
try:
|
||||||
mp_info = reagent_map[reagent['role']]
|
mp_info = reagent_map[reagent['role']]
|
||||||
@@ -268,6 +273,7 @@ class SampleWriter(object):
|
|||||||
# NOTE: exclude any samples without a submission rank.
|
# NOTE: exclude any samples without a submission rank.
|
||||||
samples = [item for item in self.reconcile_map(sample_list) if item['submission_rank'] > 0]
|
samples = [item for item in self.reconcile_map(sample_list) if item['submission_rank'] > 0]
|
||||||
self.samples = sorted(samples, key=itemgetter('submission_rank'))
|
self.samples = sorted(samples, key=itemgetter('submission_rank'))
|
||||||
|
self.blank_lookup_table()
|
||||||
|
|
||||||
def reconcile_map(self, sample_list: list) -> Generator[dict, None, None]:
|
def reconcile_map(self, sample_list: list) -> Generator[dict, None, None]:
|
||||||
"""
|
"""
|
||||||
@@ -291,6 +297,16 @@ class SampleWriter(object):
|
|||||||
new[k] = v
|
new[k] = v
|
||||||
yield new
|
yield new
|
||||||
|
|
||||||
|
def blank_lookup_table(self):
|
||||||
|
"""
|
||||||
|
Blanks out columns in the lookup table to ensure help values are removed before writing.
|
||||||
|
"""
|
||||||
|
sheet = self.xl[self.sample_map['sheet']]
|
||||||
|
for row in range(self.sample_map['start_row'], self.sample_map['end_row'] + 1):
|
||||||
|
for column in self.sample_map['sample_columns'].values():
|
||||||
|
if sheet.cell(row, column).data_type != 'f':
|
||||||
|
sheet.cell(row=row, column=column, value="")
|
||||||
|
|
||||||
def write_samples(self) -> Workbook:
|
def write_samples(self) -> Workbook:
|
||||||
"""
|
"""
|
||||||
Performs writing operations.
|
Performs writing operations.
|
||||||
|
|||||||
7
src/submissions/backend/scripts/__init__.py
Normal file
7
src/submissions/backend/scripts/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from .irida import import_irida
|
||||||
|
|
||||||
|
def hello(ctx):
|
||||||
|
print("\n\nHello!\n\n")
|
||||||
|
|
||||||
|
def goodbye(ctx):
|
||||||
|
print("\n\nGoodbye\n\n")
|
||||||
56
src/submissions/backend/scripts/irida.py
Normal file
56
src/submissions/backend/scripts/irida.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import logging, sqlite3, json
|
||||||
|
from pprint import pformat, pprint
|
||||||
|
from datetime import datetime
|
||||||
|
from tools import Settings
|
||||||
|
from backend import BasicSample
|
||||||
|
from backend.db import IridaControl, ControlType
|
||||||
|
|
||||||
|
logger = logging.getLogger(f"submissions.{__name__}")
|
||||||
|
|
||||||
|
def import_irida(ctx:Settings):
|
||||||
|
"""
|
||||||
|
Grabs Irida controls from secondary database.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ctx (Settings): Settings inherited from app.
|
||||||
|
|
||||||
|
"""
|
||||||
|
ct = ControlType.query(name="Irida Control")
|
||||||
|
existing_controls = [item.name for item in IridaControl.query()]
|
||||||
|
prm_list = ", ".join([f"'{thing}'" for thing in existing_controls])
|
||||||
|
ctrl_db_path = ctx.directory_path.joinpath("submissions_parser_output", "submissions.db")
|
||||||
|
# print(f"Incoming settings: {pformat(ctx)}")
|
||||||
|
try:
|
||||||
|
conn = sqlite3.connect(ctrl_db_path)
|
||||||
|
except AttributeError as e:
|
||||||
|
print(f"Error, could not import from irida due to {e}")
|
||||||
|
return
|
||||||
|
sql = f"SELECT name, submitted_date, submission_id, contains, matches, kraken, subtype, refseq_version, " \
|
||||||
|
f"kraken2_version, kraken2_db_version, sample_id FROM _iridacontrol INNER JOIN _control on _control.id " \
|
||||||
|
f"= _iridacontrol.id WHERE _control.name NOT IN ({prm_list})"
|
||||||
|
cursor = conn.execute(sql)
|
||||||
|
records = [dict(name=row[0], submitted_date=row[1], submission_id=row[2], contains=row[3], matches=row[4], kraken=row[5],
|
||||||
|
subtype=row[6], refseq_version=row[7], kraken2_version=row[8], kraken2_db_version=row[9],
|
||||||
|
sample_id=row[10]) for row in cursor]
|
||||||
|
# incoming_controls = set(item['name'] for item in records)
|
||||||
|
# relevant = list(incoming_controls - existing_controls)
|
||||||
|
for record in records:
|
||||||
|
instance = IridaControl.query(name=record['name'])
|
||||||
|
if instance:
|
||||||
|
logger.warning(f"Irida Control {instance.name} already exists, skipping.")
|
||||||
|
continue
|
||||||
|
record['contains'] = json.loads(record['contains'])
|
||||||
|
assert isinstance(record['contains'], dict)
|
||||||
|
record['matches'] = json.loads(record['matches'])
|
||||||
|
assert isinstance(record['matches'], dict)
|
||||||
|
record['kraken'] = json.loads(record['kraken'])
|
||||||
|
assert isinstance(record['kraken'], dict)
|
||||||
|
record['submitted_date'] = datetime.strptime(record['submitted_date'], "%Y-%m-%d %H:%M:%S.%f")
|
||||||
|
assert isinstance(record['submitted_date'], datetime)
|
||||||
|
instance = IridaControl(controltype=ct, **record)
|
||||||
|
sample = BasicSample.query(submitter_id=instance.name)
|
||||||
|
if sample:
|
||||||
|
instance.sample = sample
|
||||||
|
instance.submission = sample.submissions[0]
|
||||||
|
# pprint(instance.__dict__)
|
||||||
|
instance.save()
|
||||||
@@ -27,7 +27,7 @@ from .turnaround import TurnaroundTime
|
|||||||
from .omni_search import SearchBox
|
from .omni_search import SearchBox
|
||||||
|
|
||||||
logger = logging.getLogger(f'submissions.{__name__}')
|
logger = logging.getLogger(f'submissions.{__name__}')
|
||||||
logger.info("Hello, I am a logger")
|
# logger.info("Hello, I am a logger")
|
||||||
|
|
||||||
|
|
||||||
class App(QMainWindow):
|
class App(QMainWindow):
|
||||||
|
|||||||
@@ -64,9 +64,6 @@ class SubmissionDetails(QDialog):
|
|||||||
self.reagent_details(reagent=sub)
|
self.reagent_details(reagent=sub)
|
||||||
self.webview.page().setWebChannel(self.channel)
|
self.webview.page().setWebChannel(self.channel)
|
||||||
|
|
||||||
# def back_function(self):
|
|
||||||
# self.webview.back()
|
|
||||||
|
|
||||||
def activate_export(self):
|
def activate_export(self):
|
||||||
title = self.webview.title()
|
title = self.webview.title()
|
||||||
self.setWindowTitle(title)
|
self.setWindowTitle(title)
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ Contains all submission related frontend functions
|
|||||||
'''
|
'''
|
||||||
from PyQt6.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
QWidget, QPushButton, QVBoxLayout,
|
QWidget, QPushButton, QVBoxLayout,
|
||||||
QComboBox, QDateEdit, QLineEdit, QLabel
|
QComboBox, QDateEdit, QLineEdit, QLabel, QCheckBox, QBoxLayout, QHBoxLayout, QGridLayout
|
||||||
)
|
)
|
||||||
from PyQt6.QtCore import pyqtSignal, Qt
|
from PyQt6.QtCore import pyqtSignal, Qt, QSignalBlocker
|
||||||
from . import select_open_file, select_save_file
|
from . import select_open_file, select_save_file
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -228,9 +228,26 @@ class SubmissionFormWidget(QWidget):
|
|||||||
# if k == "extraction_kit":
|
# if k == "extraction_kit":
|
||||||
if k in self.__class__.update_reagent_fields:
|
if k in self.__class__.update_reagent_fields:
|
||||||
add_widget.input.currentTextChanged.connect(self.scrape_reagents)
|
add_widget.input.currentTextChanged.connect(self.scrape_reagents)
|
||||||
|
self.disabler = self.DisableReagents(self)
|
||||||
|
self.disabler.checkbox.setChecked(True)
|
||||||
|
self.layout.addWidget(self.disabler)
|
||||||
|
self.disabler.checkbox.checkStateChanged.connect(self.disable_reagents)
|
||||||
self.setStyleSheet(main_form_style)
|
self.setStyleSheet(main_form_style)
|
||||||
self.scrape_reagents(self.extraction_kit)
|
self.scrape_reagents(self.extraction_kit)
|
||||||
|
|
||||||
|
def disable_reagents(self):
|
||||||
|
for reagent in self.findChildren(self.ReagentFormWidget):
|
||||||
|
# if self.disabler.checkbox.isChecked():
|
||||||
|
# # reagent.setVisible(True)
|
||||||
|
# # with QSignalBlocker(self.disabler.checkbox) as b:
|
||||||
|
# reagent.flip_check()
|
||||||
|
# else:
|
||||||
|
# # reagent.setVisible(False)
|
||||||
|
# # with QSignalBlocker(self.disabler.checkbox) as b:
|
||||||
|
# reagent.check.setChecked(False)
|
||||||
|
reagent.flip_check(self.disabler.checkbox.isChecked())
|
||||||
|
|
||||||
|
|
||||||
def create_widget(self, key: str, value: dict | PydReagent, submission_type: str | SubmissionType | None = None,
|
def create_widget(self, key: str, value: dict | PydReagent, submission_type: str | SubmissionType | None = None,
|
||||||
extraction_kit: str | None = None, sub_obj: BasicSubmission | None = None,
|
extraction_kit: str | None = None, sub_obj: BasicSubmission | None = None,
|
||||||
disable: bool = False) -> "self.InfoItem":
|
disable: bool = False) -> "self.InfoItem":
|
||||||
@@ -350,8 +367,9 @@ class SubmissionFormWidget(QWidget):
|
|||||||
report.add_result(result)
|
report.add_result(result)
|
||||||
# logger.debug(f"Submission: {pformat(self.pyd)}")
|
# logger.debug(f"Submission: {pformat(self.pyd)}")
|
||||||
# logger.debug("Checking kit integrity...")
|
# logger.debug("Checking kit integrity...")
|
||||||
_, result = self.pyd.check_kit_integrity()
|
if self.disabler.checkbox.isChecked():
|
||||||
report.add_result(result)
|
_, result = self.pyd.check_kit_integrity()
|
||||||
|
report.add_result(result)
|
||||||
if len(result.results) > 0:
|
if len(result.results) > 0:
|
||||||
return
|
return
|
||||||
# logger.debug(f"PYD before transformation into SQL:\n\n{self.pyd}\n\n")
|
# logger.debug(f"PYD before transformation into SQL:\n\n{self.pyd}\n\n")
|
||||||
@@ -665,11 +683,15 @@ class SubmissionFormWidget(QWidget):
|
|||||||
self.app = self.parent().parent().parent().parent().parent().parent().parent().parent()
|
self.app = self.parent().parent().parent().parent().parent().parent().parent().parent()
|
||||||
self.reagent = reagent
|
self.reagent = reagent
|
||||||
self.extraction_kit = extraction_kit
|
self.extraction_kit = extraction_kit
|
||||||
layout = QVBoxLayout()
|
layout = QGridLayout()
|
||||||
|
self.check = QCheckBox()
|
||||||
|
self.check.setChecked(True)
|
||||||
|
self.check.checkStateChanged.connect(self.disable)
|
||||||
|
layout.addWidget(self.check, 0, 0, 1, 1)
|
||||||
self.label = self.ReagentParsedLabel(reagent=reagent)
|
self.label = self.ReagentParsedLabel(reagent=reagent)
|
||||||
layout.addWidget(self.label)
|
layout.addWidget(self.label, 0, 1, 1, 9)
|
||||||
self.lot = self.ReagentLot(scrollWidget=parent, reagent=reagent, extraction_kit=extraction_kit)
|
self.lot = self.ReagentLot(scrollWidget=parent, reagent=reagent, extraction_kit=extraction_kit)
|
||||||
layout.addWidget(self.lot)
|
layout.addWidget(self.lot, 1, 0, 1, 10)
|
||||||
# NOTE: Remove spacing between reagents
|
# NOTE: Remove spacing between reagents
|
||||||
layout.setContentsMargins(0, 0, 0, 0)
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
@@ -678,6 +700,20 @@ class SubmissionFormWidget(QWidget):
|
|||||||
# NOTE: If changed set self.missing to True and update self.label
|
# NOTE: If changed set self.missing to True and update self.label
|
||||||
self.lot.currentTextChanged.connect(self.updated)
|
self.lot.currentTextChanged.connect(self.updated)
|
||||||
|
|
||||||
|
def flip_check(self, checked:bool):
|
||||||
|
with QSignalBlocker(self.check) as b:
|
||||||
|
self.check.setChecked(checked)
|
||||||
|
self.lot.setEnabled(checked)
|
||||||
|
self.label.setEnabled(checked)
|
||||||
|
|
||||||
|
def disable(self):
|
||||||
|
self.lot.setEnabled(self.check.isChecked())
|
||||||
|
self.label.setEnabled(self.check.isChecked())
|
||||||
|
if not any([item.lot.isEnabled() for item in self.parent().findChildren(self.__class__)]):
|
||||||
|
self.parent().disabler.checkbox.setChecked(False)
|
||||||
|
else:
|
||||||
|
self.parent().disabler.checkbox.setChecked(True)
|
||||||
|
|
||||||
def parse_form(self) -> Tuple[PydReagent | None, Report]:
|
def parse_form(self) -> Tuple[PydReagent | None, Report]:
|
||||||
"""
|
"""
|
||||||
Pulls form info into PydReagent
|
Pulls form info into PydReagent
|
||||||
@@ -686,6 +722,8 @@ class SubmissionFormWidget(QWidget):
|
|||||||
Tuple[PydReagent, dict]: PydReagent and Report(?)
|
Tuple[PydReagent, dict]: PydReagent and Report(?)
|
||||||
"""
|
"""
|
||||||
report = Report()
|
report = Report()
|
||||||
|
if not self.lot.isEnabled():
|
||||||
|
return None, report
|
||||||
lot = self.lot.currentText()
|
lot = self.lot.currentText()
|
||||||
# logger.debug(f"Using this lot for the reagent {self.reagent}: {lot}")
|
# logger.debug(f"Using this lot for the reagent {self.reagent}: {lot}")
|
||||||
wanted_reagent = Reagent.query(lot=lot, role=self.reagent.role)
|
wanted_reagent = Reagent.query(lot=lot, role=self.reagent.role)
|
||||||
@@ -786,3 +824,15 @@ class SubmissionFormWidget(QWidget):
|
|||||||
self.setObjectName(f"lot_{reagent.role}")
|
self.setObjectName(f"lot_{reagent.role}")
|
||||||
self.addItems(relevant_reagents)
|
self.addItems(relevant_reagents)
|
||||||
self.setToolTip(f"Enter lot number for the reagent used for {reagent.role}")
|
self.setToolTip(f"Enter lot number for the reagent used for {reagent.role}")
|
||||||
|
|
||||||
|
class DisableReagents(QWidget):
|
||||||
|
|
||||||
|
def __init__(self, parent: QWidget):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.app = self.parent().parent().parent().parent().parent().parent().parent().parent()
|
||||||
|
layout = QHBoxLayout()
|
||||||
|
self.label = QLabel("Import Reagents")
|
||||||
|
self.checkbox = QCheckBox()
|
||||||
|
layout.addWidget(self.label)
|
||||||
|
layout.addWidget(self.checkbox)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|||||||
@@ -17,12 +17,12 @@
|
|||||||
{% if sub['custom'] %}{% for key, value in sub['custom'].items() %}
|
{% if sub['custom'] %}{% for key, value in sub['custom'].items() %}
|
||||||
<b>{{ key | replace("_", " ") | title }}: </b>{{ value }}<br>
|
<b>{{ key | replace("_", " ") | title }}: </b>{{ value }}<br>
|
||||||
{% endfor %}{% endif %}</p>
|
{% endfor %}{% endif %}</p>
|
||||||
|
{% if sub['reagents'] %}
|
||||||
<h3><u>Reagents:</u></h3>
|
<h3><u>Reagents:</u></h3>
|
||||||
<p>{% for item in sub['reagents'] %}
|
<p>{% for item in sub['reagents'] %}
|
||||||
<b>{{ item['role'] }}</b>: <a class="data-link reagent" id="{{ item['lot'] }}">{{ item['lot'] }} (EXP: {{ item['expiry'] }})</a><br>
|
<b>{{ item['role'] }}</b>: <a class="data-link reagent" id="{{ item['lot'] }}">{{ item['lot'] }} (EXP: {{ item['expiry'] }})</a><br>
|
||||||
{% endfor %}</p>
|
{% endfor %}</p>
|
||||||
|
{% endif %}
|
||||||
{% if sub['equipment'] %}
|
{% if sub['equipment'] %}
|
||||||
<h3><u>Equipment:</u></h3>
|
<h3><u>Equipment:</u></h3>
|
||||||
<p>{% for item in sub['equipment'] %}
|
<p>{% for item in sub['equipment'] %}
|
||||||
|
|||||||
@@ -418,13 +418,13 @@ class Settings(BaseSettings, extra="allow"):
|
|||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.set_from_db()
|
self.set_from_db()
|
||||||
pprint(f"User settings:\n{self.__dict__}")
|
# pprint(f"User settings:\n{self.__dict__}")
|
||||||
|
|
||||||
def set_from_db(self):
|
def set_from_db(self):
|
||||||
if 'pytest' in sys.modules:
|
if 'pytest' in sys.modules:
|
||||||
output = dict(power_users=['lwark', 'styson', 'ruwang'])
|
output = dict(power_users=['lwark', 'styson', 'ruwang'])
|
||||||
else:
|
else:
|
||||||
print(f"Hello from database settings getter.")
|
# print(f"Hello from database settings getter.")
|
||||||
# print(self.__dict__)
|
# print(self.__dict__)
|
||||||
session = self.database_session
|
session = self.database_session
|
||||||
metadata = MetaData()
|
metadata = MetaData()
|
||||||
|
|||||||
Reference in New Issue
Block a user