Bug fixes.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
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 threading import Thread
|
||||||
# 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"
|
||||||
@@ -23,12 +23,12 @@ def run_startup():
|
|||||||
for script in startup_scripts:
|
for script in startup_scripts:
|
||||||
try:
|
try:
|
||||||
func = getattr(scripts, script)
|
func = getattr(scripts, script)
|
||||||
# func = modules[script]
|
|
||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
logger.error(f"Couldn't run startup script {script} due to {e}")
|
logger.error(f"Couldn't run startup script {script} due to {e}")
|
||||||
continue
|
continue
|
||||||
logger.info(f"Running startup script: {func.__name__}")
|
logger.info(f"Running startup script: {func.__name__}")
|
||||||
func.script(ctx)
|
thread = Thread(target=func.script, args=(ctx, ))
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
|
||||||
def run_teardown():
|
def run_teardown():
|
||||||
@@ -45,7 +45,8 @@ def run_teardown():
|
|||||||
logger.error(f"Couldn't run teardown script {script} due to {e}")
|
logger.error(f"Couldn't run teardown script {script} due to {e}")
|
||||||
continue
|
continue
|
||||||
logger.info(f"Running teardown script: {func.__name__}")
|
logger.info(f"Running teardown script: {func.__name__}")
|
||||||
func.script(ctx)
|
thread = Thread(target=func.script, args=(ctx,))
|
||||||
|
thread.start()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
run_startup()
|
run_startup()
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ from .controls import *
|
|||||||
from .organizations import *
|
from .organizations import *
|
||||||
from .kits import *
|
from .kits import *
|
||||||
from .submissions import *
|
from .submissions import *
|
||||||
from .audit import AuditLog
|
from .audit import *
|
||||||
|
|
||||||
# NOTE: Add a creator to the submission for reagent association. Assigned here due to circular import constraints.
|
# NOTE: Add a creator to the submission for reagent association. Assigned here due to circular import constraints.
|
||||||
# https://docs.sqlalchemy.org/en/20/orm/extensions/associationproxy.html#sqlalchemy.ext.associationproxy.association_proxy.params.creator
|
# https://docs.sqlalchemy.org/en/20/orm/extensions/associationproxy.html#sqlalchemy.ext.associationproxy.association_proxy.params.creator
|
||||||
|
|||||||
@@ -332,7 +332,6 @@ class KitType(BaseClass):
|
|||||||
return new_kit
|
return new_kit
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ReagentRole(BaseClass):
|
class ReagentRole(BaseClass):
|
||||||
"""
|
"""
|
||||||
Base of reagent type abstract
|
Base of reagent type abstract
|
||||||
|
|||||||
@@ -1265,7 +1265,9 @@ class BasicSubmission(BaseClass, LogMixin):
|
|||||||
logger.error(f"Couldn't save association with {equip} due to {e}")
|
logger.error(f"Couldn't save association with {equip} due to {e}")
|
||||||
if equip.tips:
|
if equip.tips:
|
||||||
for tips in equip.tips:
|
for tips in equip.tips:
|
||||||
|
logger.debug(f"Attempting to add tips assoc: {tips} (pydantic)")
|
||||||
tassoc = tips.to_sql(submission=self)
|
tassoc = tips.to_sql(submission=self)
|
||||||
|
logger.debug(f"Attempting to add tips assoc: {tips.__dict__} (sql)")
|
||||||
if tassoc not in self.submission_tips_associations:
|
if tassoc not in self.submission_tips_associations:
|
||||||
tassoc.save()
|
tassoc.save()
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
Contains pandas and openpyxl convenience functions for interacting with excel workbooks
|
Contains pandas and openpyxl convenience functions for interacting with excel workbooks
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from .reports import *
|
|
||||||
from .parser import *
|
from .parser import *
|
||||||
|
from .reports import *
|
||||||
from .writer import *
|
from .writer import *
|
||||||
|
|||||||
@@ -4,20 +4,24 @@ from datetime import datetime
|
|||||||
from tools import Settings
|
from tools import Settings
|
||||||
from backend import BasicSample
|
from backend import BasicSample
|
||||||
from backend.db import IridaControl, ControlType
|
from backend.db import IridaControl, ControlType
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
logger = logging.getLogger(f"submissions.{__name__}")
|
logger = logging.getLogger(f"submissions.{__name__}")
|
||||||
|
|
||||||
|
|
||||||
def script(ctx:Settings):
|
def script(ctx: Settings):
|
||||||
"""
|
"""
|
||||||
Grabs Irida controls from secondary database.
|
Grabs Irida controls from secondary database.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
ctx (Settings): Settings inherited from app.
|
ctx (Settings): Settings inherited from app.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ct = ControlType.query(name="Irida Control")
|
# NOTE: Because the main session will be busy in another thread, this requires a new session.
|
||||||
existing_controls = [item.name for item in IridaControl.query()]
|
new_session = Session(ctx.database_session.get_bind())
|
||||||
|
# ct = ControlType.query(name="Irida Control")
|
||||||
|
ct = new_session.query(ControlType).filter(ControlType.name == "Irida Control").first()
|
||||||
|
# existing_controls = [item.name for item in IridaControl.query()]
|
||||||
|
existing_controls = [item.name for item in new_session.query(IridaControl)]
|
||||||
prm_list = ", ".join([f"'{thing}'" for thing in existing_controls])
|
prm_list = ", ".join([f"'{thing}'" for thing in existing_controls])
|
||||||
ctrl_db_path = ctx.directory_path.joinpath("submissions_parser_output", "submissions.db")
|
ctrl_db_path = ctx.directory_path.joinpath("submissions_parser_output", "submissions.db")
|
||||||
try:
|
try:
|
||||||
@@ -25,29 +29,39 @@ def script(ctx:Settings):
|
|||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
logger.error(f"Error, could not import from irida due to {e}")
|
logger.error(f"Error, could not import from irida due to {e}")
|
||||||
return
|
return
|
||||||
sql = f"SELECT name, submitted_date, submission_id, contains, matches, kraken, subtype, refseq_version, " \
|
sql = "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 " \
|
"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})"
|
f"= _iridacontrol.id WHERE _control.name NOT IN ({prm_list})"
|
||||||
cursor = conn.execute(sql)
|
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],
|
records = [
|
||||||
subtype=row[6], refseq_version=row[7], kraken2_version=row[8], kraken2_db_version=row[9],
|
dict(name=row[0], submitted_date=row[1], submission_id=row[2], contains=row[3], matches=row[4], kraken=row[5],
|
||||||
sample_id=row[10]) for row in cursor]
|
subtype=row[6], refseq_version=row[7], kraken2_version=row[8], kraken2_db_version=row[9],
|
||||||
|
sample_id=row[10]) for row in cursor]
|
||||||
for record in records:
|
for record in records:
|
||||||
instance = IridaControl.query(name=record['name'])
|
# instance = IridaControl.query(name=record['name'])
|
||||||
|
instance = new_session.query(IridaControl).filter(IridaControl.name == record['name']).first()
|
||||||
if instance:
|
if instance:
|
||||||
logger.warning(f"Irida Control {instance.name} already exists, skipping.")
|
logger.warning(f"Irida Control {instance.name} already exists, skipping.")
|
||||||
continue
|
continue
|
||||||
record['contains'] = json.loads(record['contains'])
|
for thing in ['contains', 'matches', 'kraken']:
|
||||||
assert isinstance(record['contains'], dict)
|
if record[thing]:
|
||||||
record['matches'] = json.loads(record['matches'])
|
record[thing] = json.loads(record[thing])
|
||||||
assert isinstance(record['matches'], dict)
|
assert isinstance(record[thing], dict)
|
||||||
record['kraken'] = json.loads(record['kraken'])
|
else:
|
||||||
assert isinstance(record['kraken'], dict)
|
record[thing] = {}
|
||||||
|
# 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")
|
record['submitted_date'] = datetime.strptime(record['submitted_date'], "%Y-%m-%d %H:%M:%S.%f")
|
||||||
assert isinstance(record['submitted_date'], datetime)
|
assert isinstance(record['submitted_date'], datetime)
|
||||||
instance = IridaControl(controltype=ct, **record)
|
instance = IridaControl(controltype=ct, **record)
|
||||||
sample = BasicSample.query(submitter_id=instance.name)
|
# sample = BasicSample.query(submitter_id=instance.name)
|
||||||
|
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]
|
instance.submission = sample.submissions[0]
|
||||||
instance.save()
|
# instance.save()
|
||||||
|
new_session.add(instance)
|
||||||
|
new_session.commit()
|
||||||
|
new_session.close()
|
||||||
|
|||||||
@@ -295,7 +295,8 @@ class PydTips(BaseModel):
|
|||||||
Returns:
|
Returns:
|
||||||
SubmissionTipsAssociation: Association between queried tips and submission
|
SubmissionTipsAssociation: Association between queried tips and submission
|
||||||
"""
|
"""
|
||||||
tips = Tips.query(name=self.name, lot=self.lot, limit=1)
|
tips = Tips.query(name=self.name, limit=1)
|
||||||
|
logger.debug(f"Tips query has yielded: {tips}")
|
||||||
assoc = SubmissionTipsAssociation.query(tip_id=tips.id, submission_id=submission.id, role=self.role, limit=1)
|
assoc = SubmissionTipsAssociation.query(tip_id=tips.id, submission_id=submission.id, role=self.role, limit=1)
|
||||||
if assoc is None:
|
if assoc is None:
|
||||||
assoc = SubmissionTipsAssociation(submission=submission, tips=tips, role_name=self.role)
|
assoc = SubmissionTipsAssociation(submission=submission, tips=tips, role_name=self.role)
|
||||||
@@ -900,17 +901,20 @@ class PydSubmission(BaseModel, extra='allow'):
|
|||||||
return render
|
return render
|
||||||
|
|
||||||
# @report_result
|
# @report_result
|
||||||
def check_kit_integrity(self, extraction_kit: str | dict | None = None) -> Tuple[List[PydReagent], Report]:
|
def check_kit_integrity(self, extraction_kit: str | dict | None = None, exempt:List[PydReagent]=[]) -> Tuple[
|
||||||
|
List[PydReagent], Report]:
|
||||||
"""
|
"""
|
||||||
Ensures all reagents expected in kit are listed in Submission
|
Ensures all reagents expected in kit are listed in Submission
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
reagenttypes (list | None, optional): List to check against complete list. Defaults to None.
|
extraction_kit (str | dict | None, optional): kit to be checked. Defaults to None.
|
||||||
|
exempt (List[PydReagent], optional): List of reagents that don't need to be checked. Defaults to []
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Report: Result object containing a message and any missing components.
|
Tuple[List[PydReagent], Report]: List of reagents and Result object containing a message and any missing components.
|
||||||
"""
|
"""
|
||||||
report = Report()
|
report = Report()
|
||||||
|
# logger.debug(f"The following reagents are exempt from the kit integrity check:\n{exempt}")
|
||||||
if isinstance(extraction_kit, str):
|
if isinstance(extraction_kit, str):
|
||||||
extraction_kit = dict(value=extraction_kit)
|
extraction_kit = dict(value=extraction_kit)
|
||||||
if extraction_kit is not None and extraction_kit != self.extraction_kit['value']:
|
if extraction_kit is not None and extraction_kit != self.extraction_kit['value']:
|
||||||
@@ -922,7 +926,8 @@ class PydSubmission(BaseModel, extra='allow'):
|
|||||||
expected_check = [item.role for item in ext_kit_rtypes]
|
expected_check = [item.role for item in ext_kit_rtypes]
|
||||||
output_reagents = [rt for rt in self.reagents if rt.role in expected_check]
|
output_reagents = [rt for rt in self.reagents if rt.role in expected_check]
|
||||||
missing_check = [item.role for item in output_reagents]
|
missing_check = [item.role for item in output_reagents]
|
||||||
missing_reagents = [rt for rt in ext_kit_rtypes if rt.role not in missing_check]
|
missing_reagents = [rt for rt in ext_kit_rtypes if rt.role not in missing_check and rt.role not in exempt]
|
||||||
|
# logger.debug(f"Missing reagents: {missing_reagents}")
|
||||||
missing_reagents += [rt for rt in output_reagents if rt.missing]
|
missing_reagents += [rt for rt in output_reagents if rt.missing]
|
||||||
output_reagents += [rt for rt in missing_reagents if rt not in output_reagents]
|
output_reagents += [rt for rt in missing_reagents if rt not in output_reagents]
|
||||||
# NOTE: if lists are equal return no problem
|
# NOTE: if lists are equal return no problem
|
||||||
@@ -930,8 +935,8 @@ class PydSubmission(BaseModel, extra='allow'):
|
|||||||
result = None
|
result = None
|
||||||
else:
|
else:
|
||||||
result = Result(
|
result = Result(
|
||||||
msg=f"The excel sheet you are importing is missing some reagents expected by the kit.\n\nIt looks like you are missing: {[item.role.upper() for item in missing_reagents]}\n\nAlternatively, you may have set the wrong extraction kit.\n\nThe program will populate lists using existing reagents.\n\nPlease make sure you check the lots carefully!",
|
msg=f"The excel sheet you are importing is missing some reagents expected by the kit.\n\nIt looks like you are missing: {[item.role.upper() for item in missing_reagents]}\n\nAlternatively, you may have set the wrong extraction kit.\n\nThe program will populate lists using existing reagents.\n\nPlease make sure you check the lots carefully!",
|
||||||
status="Warning")
|
status="Warning")
|
||||||
report.add_result(result)
|
report.add_result(result)
|
||||||
return output_reagents, report
|
return output_reagents, report
|
||||||
|
|
||||||
|
|||||||
@@ -2,15 +2,17 @@
|
|||||||
Contains all custom generated PyQT6 derivative widgets.
|
Contains all custom generated PyQT6 derivative widgets.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from .app import App
|
||||||
|
from .controls_chart import *
|
||||||
|
from .equipment_usage import *
|
||||||
from .functions import *
|
from .functions import *
|
||||||
|
from .gel_checker import *
|
||||||
|
from .info_tab import *
|
||||||
from .misc import *
|
from .misc import *
|
||||||
|
from .omni_search import *
|
||||||
from .pop_ups import *
|
from .pop_ups import *
|
||||||
|
from .submission_details import *
|
||||||
from .submission_table import *
|
from .submission_table import *
|
||||||
from .submission_widget import *
|
from .submission_widget import *
|
||||||
from .controls_chart import *
|
from .summary import *
|
||||||
from .submission_details import *
|
from .turnaround import *
|
||||||
from .equipment_usage import *
|
|
||||||
from .gel_checker import *
|
|
||||||
from .summary import Summary
|
|
||||||
from .turnaround import TurnaroundTime
|
|
||||||
from .app import App
|
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ class RoleComboBox(QWidget):
|
|||||||
PydEquipment|None: PydEquipment matching form
|
PydEquipment|None: PydEquipment matching form
|
||||||
"""
|
"""
|
||||||
eq = Equipment.query(name=self.box.currentText())
|
eq = Equipment.query(name=self.box.currentText())
|
||||||
tips = [PydTips(name=item.currentText(), role=item.objectName().lstrip("tips").lstrip("_")) for item in
|
tips = [PydTips(name=item.currentText(), role=item.objectName().lstrip("tips").lstrip("_"), lot="") for item in
|
||||||
self.findChildren(QComboBox) if item.objectName().startswith("tips")]
|
self.findChildren(QComboBox) if item.objectName().startswith("tips")]
|
||||||
try:
|
try:
|
||||||
return PydEquipment(
|
return PydEquipment(
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from PyQt6.QtWidgets import (
|
|||||||
QComboBox, QDateEdit, QLineEdit, QLabel, QCheckBox, QHBoxLayout, QGridLayout
|
QComboBox, QDateEdit, QLineEdit, QLabel, QCheckBox, QHBoxLayout, QGridLayout
|
||||||
)
|
)
|
||||||
from PyQt6.QtCore import pyqtSignal, Qt, QSignalBlocker
|
from PyQt6.QtCore import pyqtSignal, Qt, QSignalBlocker
|
||||||
from . import select_open_file, select_save_file
|
from .functions import select_open_file, select_save_file
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tools import Report, Result, check_not_nan, main_form_style, report_result
|
from tools import Report, Result, check_not_nan, main_form_style, report_result
|
||||||
@@ -338,11 +338,15 @@ class SubmissionFormWidget(QWidget):
|
|||||||
report = Report()
|
report = Report()
|
||||||
result = self.parse_form()
|
result = self.parse_form()
|
||||||
report.add_result(result)
|
report.add_result(result)
|
||||||
|
# allow = not all([item.lot.isEnabled() for item in self.findChildren(self.ReagentFormWidget)])
|
||||||
|
exempt = [item.reagent.role for item in self.findChildren(self.ReagentFormWidget) if not item.lot.isEnabled()]
|
||||||
|
# if allow:
|
||||||
|
# logger.warning(f"Some reagents are disabled, allowing incomplete kit.")
|
||||||
if self.disabler.checkbox.isChecked():
|
if self.disabler.checkbox.isChecked():
|
||||||
_, result = self.pyd.check_kit_integrity()
|
_, result = self.pyd.check_kit_integrity(exempt=exempt)
|
||||||
report.add_result(result)
|
report.add_result(result)
|
||||||
if len(result.results) > 0:
|
if len(result.results) > 0:
|
||||||
return
|
return report
|
||||||
base_submission, result = self.pyd.to_sql()
|
base_submission, result = self.pyd.to_sql()
|
||||||
# NOTE: check output message for issues
|
# NOTE: check output message for issues
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user