Compare commits
2 Commits
6f1202d3ba
...
4d70d751ca
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d70d751ca | ||
|
|
39d20bbc22 |
11
CHANGELOG.md
11
CHANGELOG.md
@@ -1,10 +1,15 @@
|
|||||||
|
# 202509.04
|
||||||
|
|
||||||
|
- Qubit results parsing complete.
|
||||||
|
|
||||||
# 202509.03
|
# 202509.03
|
||||||
|
|
||||||
- Sortable headers in treeview.
|
- Sortable headers in treeview.
|
||||||
|
- Added gitea remote.
|
||||||
|
|
||||||
# 202509.02
|
# 202509.02
|
||||||
|
|
||||||
- First Useable updated version.
|
- First usable updated version.
|
||||||
|
|
||||||
# 202504.04
|
# 202504.04
|
||||||
|
|
||||||
@@ -12,7 +17,7 @@
|
|||||||
|
|
||||||
# 202504.03
|
# 202504.03
|
||||||
|
|
||||||
- Split Concentration controls on the chart so they are individually selectable.
|
- Split Concentration controls on the chart, so they are individually selectable.
|
||||||
|
|
||||||
# 202504.02
|
# 202504.02
|
||||||
|
|
||||||
@@ -315,7 +320,7 @@
|
|||||||
|
|
||||||
## 202307.03
|
## 202307.03
|
||||||
|
|
||||||
- Auto-filling of some empty cells in Excel file.
|
- Autofilling of some empty cells in Excel file.
|
||||||
- Better pydantic validations of missing data.
|
- Better pydantic validations of missing data.
|
||||||
|
|
||||||
## 202307.02
|
## 202307.02
|
||||||
|
|||||||
4
TODO.md
4
TODO.md
@@ -1,5 +1,5 @@
|
|||||||
- [ ] Add in database objects for rsl_run (submission -> run), procedure (run -> procedure), many more things will likely be associated with procedure.
|
- [x] Add in database objects for rsl_run (submission -> run), procedure (run -> procedure), many more things will likely be associated with procedure.
|
||||||
- [ ] Add in database object for client submission.
|
- [x] Add in database object for client submission.
|
||||||
- [ ] Add arbitrary pipette addition to equipment UI.
|
- [ ] Add arbitrary pipette addition to equipment UI.
|
||||||
- [ ] transfer details template rendering fully into sql objects
|
- [ ] transfer details template rendering fully into sql objects
|
||||||
- [x] Add in connecting links for tips.
|
- [x] Add in connecting links for tips.
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ from sqlalchemy.exc import ArgumentError
|
|||||||
from typing import Any, List, ClassVar
|
from typing import Any, List, ClassVar
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from sqlalchemy.orm.relationships import _RelationshipDeclared
|
from sqlalchemy.orm.relationships import _RelationshipDeclared
|
||||||
from tools import report_result, list_sort_dict, jinja_template_loading, Report, Result, ctx
|
from tools import report_result, list_sort_dict, jinja_template_loading, Report, Alert, ctx
|
||||||
|
|
||||||
# NOTE: Load testing environment
|
# NOTE: Load testing environment
|
||||||
if 'pytest' in sys.modules:
|
if 'pytest' in sys.modules:
|
||||||
@@ -364,7 +364,7 @@ class BaseClass(Base):
|
|||||||
logger.error(f"Error message: {type(e)}")
|
logger.error(f"Error message: {type(e)}")
|
||||||
logger.error(pformat(self.__dict__))
|
logger.error(pformat(self.__dict__))
|
||||||
self.__database_session__.rollback()
|
self.__database_session__.rollback()
|
||||||
report.add_result(Result(msg=e, status="Critical"))
|
report.add_result(Alert(msg=e, status="Critical"))
|
||||||
return report
|
return report
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ All kittype and reagent related models
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import zipfile, logging, re, numpy as np
|
import zipfile, logging, re, numpy as np
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
from pathlib import Path
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from sqlalchemy import Column, String, TIMESTAMP, JSON, INTEGER, ForeignKey, Interval, Table, FLOAT, func
|
from sqlalchemy import Column, String, TIMESTAMP, JSON, INTEGER, ForeignKey, Interval, Table, FLOAT, func
|
||||||
from sqlalchemy.ext.hybrid import hybrid_property
|
from sqlalchemy.ext.hybrid import hybrid_property
|
||||||
from sqlalchemy.orm import relationship, validates, Query, declared_attr
|
from sqlalchemy.orm import relationship, validates, Query, declared_attr
|
||||||
from sqlalchemy.ext.associationproxy import association_proxy
|
from sqlalchemy.ext.associationproxy import association_proxy
|
||||||
from datetime import date, datetime, timedelta
|
from datetime import date, datetime, timedelta
|
||||||
from tools import check_authorization, setup_lookup, Report, Result, check_regex_match, timezone, \
|
from tools import check_authorization, setup_lookup, Report, Alert, check_regex_match, timezone, \
|
||||||
jinja_template_loading, flatten_list
|
jinja_template_loading, flatten_list
|
||||||
from typing import List, Literal, Generator, Any, Tuple, TYPE_CHECKING
|
from typing import List, Literal, Generator, Any, Tuple, TYPE_CHECKING
|
||||||
from . import BaseClass, ClientLab, LogMixin
|
from . import BaseClass, ClientLab, LogMixin
|
||||||
@@ -926,10 +927,13 @@ class Procedure(BaseClass):
|
|||||||
logger.info(f"Add Results! {resultstype_name}")
|
logger.info(f"Add Results! {resultstype_name}")
|
||||||
from backend.managers import results
|
from backend.managers import results
|
||||||
results_manager = getattr(results, f"{resultstype_name}Manager")
|
results_manager = getattr(results, f"{resultstype_name}Manager")
|
||||||
rs = results_manager(procedure=self, parent=obj)
|
rs = results_manager(procedure=self, parent=obj, fname=Path("C:\\Users\lwark\Documents\Submission_Forms\QubitData_18-09-2025_13-43-53.csv"))
|
||||||
procedure = rs.procedure_to_pydantic()
|
procedure = rs.procedure_to_pydantic()
|
||||||
samples = rs.samples_to_pydantic()
|
samples = rs.samples_to_pydantic()
|
||||||
procedure_sql = procedure.to_sql()
|
if procedure:
|
||||||
|
procedure_sql = procedure.to_sql()
|
||||||
|
else:
|
||||||
|
return
|
||||||
procedure_sql.save()
|
procedure_sql.save()
|
||||||
for sample in samples:
|
for sample in samples:
|
||||||
sample_sql = sample.to_sql()
|
sample_sql = sample.to_sql()
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ from sqlalchemy.orm.attributes import flag_modified
|
|||||||
from sqlalchemy.ext.associationproxy import association_proxy
|
from sqlalchemy.ext.associationproxy import association_proxy
|
||||||
from sqlalchemy.exc import OperationalError as AlcOperationalError, IntegrityError as AlcIntegrityError, StatementError
|
from sqlalchemy.exc import OperationalError as AlcOperationalError, IntegrityError as AlcIntegrityError, StatementError
|
||||||
from sqlite3 import OperationalError as SQLOperationalError, IntegrityError as SQLIntegrityError
|
from sqlite3 import OperationalError as SQLOperationalError, IntegrityError as SQLIntegrityError
|
||||||
from tools import setup_lookup, jinja_template_loading, create_holidays_for_year, check_dictionary_inclusion_equality, is_power_user
|
from tools import (setup_lookup, jinja_template_loading, create_holidays_for_year,
|
||||||
|
check_dictionary_inclusion_equality, is_power_user, row_map)
|
||||||
from datetime import datetime, date
|
from datetime import datetime, date
|
||||||
from typing import List, Literal, Generator, TYPE_CHECKING
|
from typing import List, Literal, Generator, TYPE_CHECKING
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -1865,6 +1866,16 @@ class ProcedureSampleAssociation(BaseClass):
|
|||||||
|
|
||||||
results = relationship("Results", back_populates="sampleprocedureassociation") #: associated results
|
results = relationship("Results", back_populates="sampleprocedureassociation") #: associated results
|
||||||
|
|
||||||
|
@property
|
||||||
|
def well(self):
|
||||||
|
if self.row > 0:
|
||||||
|
if self.column > 0:
|
||||||
|
return f"{row_map[self.row]}{self.column}"
|
||||||
|
else:
|
||||||
|
return self.row
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def query(cls, sample: Sample | str | None = None, procedure: Procedure | str | None = None, limit: int = 0,
|
def query(cls, sample: Sample | str | None = None, procedure: Procedure | str | None = None, limit: int = 0,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|||||||
@@ -2,11 +2,13 @@
|
|||||||
Default Parser archetypes.
|
Default Parser archetypes.
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import logging, re
|
import logging, re, csv
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from pprint import pformat
|
||||||
from typing import Generator, TYPE_CHECKING
|
from typing import Generator, TYPE_CHECKING
|
||||||
from openpyxl.cell import MergedCell
|
from openpyxl.cell import MergedCell
|
||||||
from openpyxl.reader.excel import load_workbook
|
from openpyxl.reader.excel import load_workbook
|
||||||
|
from openpyxl.workbook import Workbook
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
from backend.validators import pydant
|
from backend.validators import pydant
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -44,6 +46,8 @@ class DefaultParser(object):
|
|||||||
**kwargs ():
|
**kwargs ():
|
||||||
"""
|
"""
|
||||||
logger.info(f"\n\nHello from {self.__class__.__name__}\n\n")
|
logger.info(f"\n\nHello from {self.__class__.__name__}\n\n")
|
||||||
|
if isinstance(filepath, str):
|
||||||
|
filepath = Path(filepath)
|
||||||
self.filepath = filepath
|
self.filepath = filepath
|
||||||
self.proceduretype = proceduretype
|
self.proceduretype = proceduretype
|
||||||
try:
|
try:
|
||||||
@@ -58,13 +62,27 @@ class DefaultParser(object):
|
|||||||
self.sheet = sheet
|
self.sheet = sheet
|
||||||
if not start_row:
|
if not start_row:
|
||||||
start_row = self.__class__.start_row
|
start_row = self.__class__.start_row
|
||||||
self.workbook = load_workbook(self.filepath, data_only=True)
|
if self.filepath.suffix == ".xslx":
|
||||||
self.worksheet = self.workbook[self.sheet]
|
self.workbook = load_workbook(self.filepath, data_only=True)
|
||||||
|
self.worksheet = self.workbook[self.sheet]
|
||||||
|
elif self.filepath.suffix == ".csv":
|
||||||
|
self.workbook, self.worksheet = self.csv2xlsx(self.filepath)
|
||||||
self.start_row = self.delineate_start_row(start_row=start_row)
|
self.start_row = self.delineate_start_row(start_row=start_row)
|
||||||
self.end_row = self.delineate_end_row(start_row=self.start_row)
|
self.end_row = self.delineate_end_row(start_row=self.start_row)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def csv2xlsx(cls, filepath):
|
||||||
|
wb = Workbook()
|
||||||
|
ws = wb.active
|
||||||
|
with open(filepath, "r") as f:
|
||||||
|
reader = csv.reader(f, delimiter=",")
|
||||||
|
for row in reader:
|
||||||
|
ws.append(row)
|
||||||
|
return wb, ws
|
||||||
|
|
||||||
def to_pydantic(self):
|
def to_pydantic(self):
|
||||||
data = self.parsed_info
|
data = self.parsed_info
|
||||||
|
logger.debug(f"Data for {self.__class__.__name__}: {pformat(data)}")
|
||||||
data['filepath'] = self.filepath
|
data['filepath'] = self.filepath
|
||||||
return self._pyd_object(**data)
|
return self._pyd_object(**data)
|
||||||
|
|
||||||
@@ -85,7 +103,7 @@ class DefaultParser(object):
|
|||||||
for iii, row in enumerate(self.worksheet.iter_rows(min_row=start_row), start=start_row):
|
for iii, row in enumerate(self.worksheet.iter_rows(min_row=start_row), start=start_row):
|
||||||
if all([item.value is None for item in row]):
|
if all([item.value is None for item in row]):
|
||||||
return iii
|
return iii
|
||||||
return self.worksheet.max_row
|
return self.worksheet.max_row + 1
|
||||||
|
|
||||||
|
|
||||||
class DefaultKEYVALUEParser(DefaultParser):
|
class DefaultKEYVALUEParser(DefaultParser):
|
||||||
|
|||||||
@@ -12,12 +12,22 @@ logger = logging.getLogger(f"submissions.{__name__}")
|
|||||||
class DefaultResultsInfoParser(DefaultKEYVALUEParser):
|
class DefaultResultsInfoParser(DefaultKEYVALUEParser):
|
||||||
pyd_name = "PydResults"
|
pyd_name = "PydResults"
|
||||||
|
|
||||||
def __init__(self, filepath: Path | str, proceduretype: "ProcedureType" | None = None,
|
def __init__(self, filepath: Path | str, results_type: str, proceduretype: "ProcedureType" | None = None,
|
||||||
results_type: str | None = "PCR", *args, **kwargs):
|
*args, **kwargs):
|
||||||
if results_type:
|
if results_type:
|
||||||
self.results_type = results_type
|
self.results_type = results_type
|
||||||
sheet = proceduretype.allowed_result_methods[results_type]['info']['sheet']
|
try:
|
||||||
start_row = proceduretype.allowed_result_methods[results_type]['info']['start_row']
|
sheet = proceduretype.allowed_result_methods[results_type]['info']['sheet']
|
||||||
|
except KeyError:
|
||||||
|
sheet = 1
|
||||||
|
if "start_row" not in kwargs:
|
||||||
|
try:
|
||||||
|
start_row = proceduretype.allowed_result_methods[results_type]['info']['start_row']
|
||||||
|
except KeyError:
|
||||||
|
start_row = 1
|
||||||
|
else:
|
||||||
|
start_row = kwargs.pop('start_row')
|
||||||
|
# start_row = proceduretype.allowed_result_methods[results_type]['info']['start_row']
|
||||||
super().__init__(filepath=filepath, proceduretype=proceduretype, sheet=sheet, start_row=start_row, *args,
|
super().__init__(filepath=filepath, proceduretype=proceduretype, sheet=sheet, start_row=start_row, *args,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
@@ -25,14 +35,24 @@ class DefaultResultsInfoParser(DefaultKEYVALUEParser):
|
|||||||
class DefaultResultsSampleParser(DefaultTABLEParser):
|
class DefaultResultsSampleParser(DefaultTABLEParser):
|
||||||
pyd_name = "PydResults"
|
pyd_name = "PydResults"
|
||||||
|
|
||||||
def __init__(self, filepath: Path | str, proceduretype: "ProcedureType" | None = None,
|
def __init__(self, filepath: Path | str, results_type: str, proceduretype: "ProcedureType" | None = None,
|
||||||
results_type: str | None = "PCR", *args, **kwargs):
|
*args, **kwargs):
|
||||||
if results_type:
|
if results_type:
|
||||||
self.results_type = results_type
|
self.results_type = results_type
|
||||||
sheet = proceduretype.allowed_result_methods[results_type]['sample']['sheet']
|
try:
|
||||||
start_row = proceduretype.allowed_result_methods[results_type]['sample']['start_row']
|
sheet = proceduretype.allowed_result_methods[results_type]['sample']['sheet']
|
||||||
|
except KeyError:
|
||||||
|
sheet = 1
|
||||||
|
if "start_row" not in kwargs:
|
||||||
|
try:
|
||||||
|
start_row = proceduretype.allowed_result_methods[results_type]['sample']['start_row']
|
||||||
|
except KeyError:
|
||||||
|
start_row = 1
|
||||||
|
else:
|
||||||
|
start_row = kwargs.pop('start_row')
|
||||||
super().__init__(filepath=filepath, proceduretype=proceduretype, sheet=sheet, start_row=start_row, *args,
|
super().__init__(filepath=filepath, proceduretype=proceduretype, sheet=sheet, start_row=start_row, *args,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
|
|
||||||
from .pcr_results_parser import PCRInfoParser, PCRSampleParser
|
from .pcr_results_parser import PCRInfoParser, PCRSampleParser
|
||||||
|
from .qubit_results_parser import QubitInfoParser, QubitSampleParser
|
||||||
|
|||||||
@@ -17,16 +17,20 @@ logger = logging.getLogger(f"submission.{__name__}")
|
|||||||
|
|
||||||
class DefaultResultsManager(DefaultManager):
|
class DefaultResultsManager(DefaultManager):
|
||||||
|
|
||||||
def __init__(self, procedure: Procedure, parent, fname: Path | str | None = None):
|
def __init__(self, procedure: Procedure, parent, fname: Path | str | None = None, extension: str|None="xlsx"):
|
||||||
self.procedure = procedure
|
self.procedure = procedure
|
||||||
if not fname:
|
if not fname:
|
||||||
self.fname = select_open_file(file_extension="xlsx", obj=get_application_from_parent(parent))
|
fname = select_open_file(file_extension=extension, obj=get_application_from_parent(parent))
|
||||||
elif isinstance(fname, str):
|
elif isinstance(fname, str):
|
||||||
self.fname = Path(fname)
|
fname = Path(fname)
|
||||||
|
self.fname = fname
|
||||||
|
|
||||||
|
|
||||||
def procedure_to_pydantic(self) -> PydResults:
|
def procedure_to_pydantic(self) -> PydResults:
|
||||||
|
logger.debug(f"Info parser: {self.info_parser}")
|
||||||
info = self.info_parser.to_pydantic()
|
info = self.info_parser.to_pydantic()
|
||||||
info.parent = self.procedure
|
if info:
|
||||||
|
info.parent = self.procedure
|
||||||
return info
|
return info
|
||||||
|
|
||||||
def samples_to_pydantic(self) -> List[PydResults]:
|
def samples_to_pydantic(self) -> List[PydResults]:
|
||||||
@@ -34,3 +38,4 @@ class DefaultResultsManager(DefaultManager):
|
|||||||
return sample
|
return sample
|
||||||
|
|
||||||
from .pcr_results_manager import PCRManager
|
from .pcr_results_manager import PCRManager
|
||||||
|
from .qubit_results_manager import QubitManager
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from typing import List, Tuple, Literal, Generator
|
|||||||
from types import GeneratorType
|
from types import GeneratorType
|
||||||
from . import RSLNamer
|
from . import RSLNamer
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tools import check_not_nan, convert_nans_to_nones, Report, Result, timezone, sort_dict_by_list, row_keys, flatten_list
|
from tools import check_not_nan, convert_nans_to_nones, Report, Alert, timezone, sort_dict_by_list, row_keys, flatten_list
|
||||||
from backend.db import models
|
from backend.db import models
|
||||||
from backend.db.models import *
|
from backend.db.models import *
|
||||||
from sqlalchemy.orm.properties import ColumnProperty
|
from sqlalchemy.orm.properties import ColumnProperty
|
||||||
@@ -1397,14 +1397,14 @@ class PydRun(PydBaseClass): #, extra='allow'):
|
|||||||
Converts this instance into a backend.db.models.procedure.BasicRun instance
|
Converts this instance into a backend.db.models.procedure.BasicRun instance
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tuple[BasicRun, Result]: BasicRun instance, result object
|
Tuple[BasicRun, Alert]: BasicRun instance, result object
|
||||||
"""
|
"""
|
||||||
report = Report()
|
report = Report()
|
||||||
dicto = self.improved_dict()
|
dicto = self.improved_dict()
|
||||||
instance, result = Run.query_or_create(submissiontype=self.submission_type['value'],
|
instance, result = Run.query_or_create(submissiontype=self.submission_type['value'],
|
||||||
rsl_plate_number=self.rsl_plate_number['value'])
|
rsl_plate_number=self.rsl_plate_number['value'])
|
||||||
if instance is None:
|
if instance is None:
|
||||||
report.add_result(Result(msg="Overwrite Cancelled."))
|
report.add_result(Alert(msg="Overwrite Cancelled."))
|
||||||
return None, report
|
return None, report
|
||||||
report.add_result(result)
|
report.add_result(result)
|
||||||
self.handle_duplicate_samples()
|
self.handle_duplicate_samples()
|
||||||
@@ -1585,7 +1585,7 @@ class PydRun(PydBaseClass): #, extra='allow'):
|
|||||||
expired.append(f"{reagent.role}, {reagent.lot}: {reagent.expiry.date()} + {role_eol.days}")
|
expired.append(f"{reagent.role}, {reagent.lot}: {reagent.expiry.date()} + {role_eol.days}")
|
||||||
if expired:
|
if expired:
|
||||||
output = '\n'.join(expired)
|
output = '\n'.join(expired)
|
||||||
result = Result(status="Warning",
|
result = Alert(status="Warning",
|
||||||
msg=f"The following reagents are expired:\n\n{output}"
|
msg=f"The following reagents are expired:\n\n{output}"
|
||||||
)
|
)
|
||||||
report.add_result(result)
|
report.add_result(result)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from datetime import date
|
|||||||
from PyQt6.QtCore import QSignalBlocker
|
from PyQt6.QtCore import QSignalBlocker
|
||||||
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
||||||
from PyQt6.QtWidgets import QWidget, QGridLayout
|
from PyQt6.QtWidgets import QWidget, QGridLayout
|
||||||
from tools import Report, report_result, Result
|
from tools import Report, report_result, Alert
|
||||||
from .misc import StartEndDatePicker
|
from .misc import StartEndDatePicker
|
||||||
from .functions import select_save_file, save_pdf
|
from .functions import select_save_file, save_pdf
|
||||||
import logging
|
import logging
|
||||||
@@ -42,7 +42,7 @@ class InfoPane(QWidget):
|
|||||||
with QSignalBlocker(self.datepicker.start_date) as blocker:
|
with QSignalBlocker(self.datepicker.start_date) as blocker:
|
||||||
self.datepicker.start_date.setDate(lastmonth)
|
self.datepicker.start_date.setDate(lastmonth)
|
||||||
self.update_data()
|
self.update_data()
|
||||||
report.add_result(Result(owner=self.__str__(), msg=msg, status="Warning"))
|
report.add_result(Alert(owner=self.__str__(), msg=msg, status="Warning"))
|
||||||
return report
|
return report
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -109,9 +109,7 @@ class SubmissionsTree(QTreeView):
|
|||||||
sets data in model
|
sets data in model
|
||||||
"""
|
"""
|
||||||
self.clear()
|
self.clear()
|
||||||
self.data = [item.to_dict(full_data=True) for item in
|
self.data = [item.to_dict(full_data=True) for item in ClientSubmission.query(chronologic=True, page=page, page_size=page_size)]
|
||||||
# self.data = [item.details_dict() for item in
|
|
||||||
ClientSubmission.query(chronologic=True, page=page, page_size=page_size)]
|
|
||||||
root = self.model.invisibleRootItem()
|
root = self.model.invisibleRootItem()
|
||||||
for submission in self.data:
|
for submission in self.data:
|
||||||
group_str = f"{submission['submissiontype']}-{submission['submitter_plate_id']}-{submission['submitted_date']}"
|
group_str = f"{submission['submissiontype']}-{submission['submitter_plate_id']}-{submission['submitted_date']}"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from PyQt6.QtWidgets import (
|
|||||||
from PyQt6.QtCore import pyqtSignal, Qt, QSignalBlocker
|
from PyQt6.QtCore import pyqtSignal, Qt, QSignalBlocker
|
||||||
from .functions import select_open_file, select_save_file
|
from .functions import select_open_file, select_save_file
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tools import Report, Result, check_not_nan, main_form_style, report_result, get_application_from_parent
|
from tools import Report, Alert, check_not_nan, main_form_style, report_result, get_application_from_parent
|
||||||
from backend.validators import PydReagent, PydClientSubmission, PydSample
|
from backend.validators import PydReagent, PydClientSubmission, PydSample
|
||||||
from backend.db.models import (
|
from backend.db.models import (
|
||||||
ClientLab, SubmissionType, Reagent, ReagentLot,
|
ClientLab, SubmissionType, Reagent, ReagentLot,
|
||||||
@@ -116,7 +116,7 @@ class SubmissionFormContainer(QWidget):
|
|||||||
if isinstance(fname, bool) or fname is None:
|
if isinstance(fname, bool) or fname is None:
|
||||||
fname = select_open_file(self, file_extension="xlsx")
|
fname = select_open_file(self, file_extension="xlsx")
|
||||||
if not fname:
|
if not fname:
|
||||||
report.add_result(Result(msg=f"File {fname.__str__()} not found.", status="critical"))
|
report.add_result(Alert(msg=f"File {fname.__str__()} not found.", status="critical"))
|
||||||
return report
|
return report
|
||||||
# NOTE: create sheetparser using excel sheet and context from gui
|
# NOTE: create sheetparser using excel sheet and context from gui
|
||||||
self.clientsubmission_manager = DefaultClientSubmissionManager(parent=self, input_object=fname)
|
self.clientsubmission_manager = DefaultClientSubmissionManager(parent=self, input_object=fname)
|
||||||
@@ -133,7 +133,7 @@ class SubmissionFormContainer(QWidget):
|
|||||||
else:
|
else:
|
||||||
message = "Submission cancelled."
|
message = "Submission cancelled."
|
||||||
logger.warning(message)
|
logger.warning(message)
|
||||||
report.add_result(Result(msg=message, owner=self.__class__.__name__, status="Warning"))
|
report.add_result(Alert(msg=message, owner=self.__class__.__name__, status="Warning"))
|
||||||
return report
|
return report
|
||||||
|
|
||||||
@report_result
|
@report_result
|
||||||
@@ -157,7 +157,7 @@ class SubmissionFormContainer(QWidget):
|
|||||||
# NOTE: send reagent to db
|
# NOTE: send reagent to db
|
||||||
sqlobj = reagent.to_sql()
|
sqlobj = reagent.to_sql()
|
||||||
sqlobj.save()
|
sqlobj.save()
|
||||||
report.add_result(Result(owner=__name__, code=0, msg="New reagent created.", status="Information"))
|
report.add_result(Alert(owner=__name__, code=0, msg="New reagent created.", status="Information"))
|
||||||
return reagent, report
|
return reagent, report
|
||||||
|
|
||||||
|
|
||||||
@@ -386,7 +386,7 @@ class SubmissionFormWidget(QWidget):
|
|||||||
if reagent is not None:
|
if reagent is not None:
|
||||||
reagents.append(reagent)
|
reagents.append(reagent)
|
||||||
else:
|
else:
|
||||||
report.add_result(Result(msg="Failed integrity check", status="Critical"))
|
report.add_result(Alert(msg="Failed integrity check", status="Critical"))
|
||||||
return report
|
return report
|
||||||
case self.InfoItem():
|
case self.InfoItem():
|
||||||
field, value = widget.parse_form()
|
field, value = widget.parse_form()
|
||||||
@@ -779,7 +779,7 @@ class ClientSubmissionFormWidget(SubmissionFormWidget):
|
|||||||
if reagent is not None:
|
if reagent is not None:
|
||||||
reagents.append(reagent)
|
reagents.append(reagent)
|
||||||
else:
|
else:
|
||||||
report.add_result(Result(msg="Failed integrity check", status="Critical"))
|
report.add_result(Alert(msg="Failed integrity check", status="Critical"))
|
||||||
return report
|
return report
|
||||||
case self.InfoItem():
|
case self.InfoItem():
|
||||||
field, value = widget.parse_form()
|
field, value = widget.parse_form()
|
||||||
|
|||||||
@@ -104,8 +104,6 @@ div.gallery {
|
|||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.plate {
|
.plate {
|
||||||
display: inline-grid;
|
display: inline-grid;
|
||||||
grid-auto-flow: column;
|
grid-auto-flow: column;
|
||||||
@@ -189,3 +187,9 @@ ul.no-bullets {
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid-auto-flow: column;
|
grid-auto-flow: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.disable_section {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
{% block script %}
|
{% block script %}
|
||||||
{% if not child %}
|
{% if not child %}
|
||||||
|
|
||||||
{% for j in js%}
|
{% for j in js %}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
{{ j }}
|
{{ j }}
|
||||||
|
|||||||
@@ -458,7 +458,6 @@ def render_details_template(template_name: str, css_in: List[str] | str = [], js
|
|||||||
js_in = ["details"] + js_in
|
js_in = ["details"] + js_in
|
||||||
js_in = [html_folder.joinpath("js", f"{j}.js") for j in js_in]
|
js_in = [html_folder.joinpath("js", f"{j}.js") for j in js_in]
|
||||||
template = env.get_template(f"{template_name}.html")
|
template = env.get_template(f"{template_name}.html")
|
||||||
# template_path = Path(template.environment.loader.__getattribute__("searchpath")[0])
|
|
||||||
css_out = []
|
css_out = []
|
||||||
for css in css_in:
|
for css in css_in:
|
||||||
with open(css, "r") as f:
|
with open(css, "r") as f:
|
||||||
@@ -645,7 +644,7 @@ def get_application_from_parent(widget):
|
|||||||
return widget
|
return widget
|
||||||
|
|
||||||
|
|
||||||
class Result(BaseModel, arbitrary_types_allowed=True):
|
class Alert(BaseModel, arbitrary_types_allowed=True):
|
||||||
owner: str = Field(default="", validate_default=True)
|
owner: str = Field(default="", validate_default=True)
|
||||||
code: int = Field(default=0)
|
code: int = Field(default=0)
|
||||||
msg: str | Exception
|
msg: str | Exception
|
||||||
@@ -704,7 +703,7 @@ class Result(BaseModel, arbitrary_types_allowed=True):
|
|||||||
|
|
||||||
|
|
||||||
class Report(BaseModel):
|
class Report(BaseModel):
|
||||||
results: List[Result] = Field(default=[])
|
results: List[Alert] = Field(default=[])
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<Report(result_count:{len(self.results)})>"
|
return f"<Report(result_count:{len(self.results)})>"
|
||||||
@@ -717,10 +716,10 @@ class Report(BaseModel):
|
|||||||
Takes a result object or all results in another report and adds them to this one.
|
Takes a result object or all results in another report and adds them to this one.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
result (Result | Report | None): Results to be added.
|
result (Alert | Report | None): Results to be added.
|
||||||
"""
|
"""
|
||||||
match result:
|
match result:
|
||||||
case Result():
|
case Alert():
|
||||||
logger.info(f"Adding {result} to results.")
|
logger.info(f"Adding {result} to results.")
|
||||||
try:
|
try:
|
||||||
self.results.append(result)
|
self.results.append(result)
|
||||||
@@ -853,7 +852,7 @@ def check_authorization(func):
|
|||||||
logger.error(error_msg)
|
logger.error(error_msg)
|
||||||
report = Report()
|
report = Report()
|
||||||
report.add_result(
|
report.add_result(
|
||||||
Result(owner=func.__str__(), code=1, msg=error_msg, status="warning"))
|
Alert(owner=func.__str__(), code=1, msg=error_msg, status="warning"))
|
||||||
return report, kwargs
|
return report, kwargs
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
@@ -877,7 +876,7 @@ def under_development(func):
|
|||||||
logger.error(error_msg)
|
logger.error(error_msg)
|
||||||
report = Report()
|
report = Report()
|
||||||
report.add_result(
|
report.add_result(
|
||||||
Result(owner=func.__str__(), code=1, msg=error_msg,
|
Alert(owner=func.__str__(), code=1, msg=error_msg,
|
||||||
status="warning"))
|
status="warning"))
|
||||||
return report
|
return report
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|||||||
Reference in New Issue
Block a user