diff --git a/src/submissions/backend/db/models/__init__.py b/src/submissions/backend/db/models/__init__.py
index e980c45..1a5304e 100644
--- a/src/submissions/backend/db/models/__init__.py
+++ b/src/submissions/backend/db/models/__init__.py
@@ -13,6 +13,7 @@ from sqlalchemy import Column, INTEGER, String, JSON
from sqlalchemy.ext.associationproxy import AssociationProxy
from sqlalchemy.orm import DeclarativeMeta, declarative_base, Query, Session, InstrumentedAttribute, ColumnProperty
from sqlalchemy.ext.declarative import declared_attr
+from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.exc import ArgumentError
from typing import Any, List, ClassVar
from pathlib import Path
@@ -237,10 +238,10 @@ class BaseClass(Base):
@classmethod
def query_or_create(cls, **kwargs) -> Tuple[Any, bool]:
new = False
- allowed = [k for k, v in cls.__dict__.items() if isinstance(v, InstrumentedAttribute)]
+ allowed = [k for k, v in cls.__dict__.items() if isinstance(v, InstrumentedAttribute) or isinstance(v, hybrid_property)]
# and not isinstance(v.property, _RelationshipDeclared)]
sanitized_kwargs = {k: v for k, v in kwargs.items() if k in allowed}
- # logger.debug(f"Sanitized kwargs: {sanitized_kwargs}")
+ logger.debug(f"Sanitized kwargs: {sanitized_kwargs}")
instance = cls.query(**sanitized_kwargs)
if not instance or isinstance(instance, list):
instance = cls()
@@ -273,7 +274,7 @@ class BaseClass(Base):
return cls.execute_query(**kwargs)
@classmethod
- def execute_query(cls, query: Query = None, model=None, limit: int = 0, **kwargs) -> Any | List[Any]:
+ def execute_query(cls, query: Query = None, model=None, limit: int = 0, offset:int|None=None, **kwargs) -> Any | List[Any]:
"""
Execute sqlalchemy query with relevant defaults.
@@ -291,22 +292,32 @@ class BaseClass(Base):
# logger.debug(f"Model: {model}")
if query is None:
query: Query = cls.__database_session__.query(cls)
+ else:
+ logger.debug(f"Incoming query: {query}")
singles = cls.get_default_info('singles')
for k, v in kwargs.items():
-
- logger.info(f"Using key: {k} with value: {v}")
+ logger.info(f"Using key: {k} with value: {v} against {cls}")
try:
attr = getattr(cls, k)
- # NOTE: account for attrs that use list.
- if attr.property.uselist:
- query = query.filter(attr.contains(v))
- else:
- query = query.filter(attr == v)
except (ArgumentError, AttributeError) as e:
- logger.error(f"Attribute {k} unavailable due to:\n\t{e}\nSkipping.")
+ logger.error(f"Attribute {k} unavailable due to:\n\t{e}\n.")
+ continue
+ # NOTE: account for attrs that use list.
+ try:
+ check = attr.property.uselist
+ except AttributeError:
+ check = False
+ if check:
+ logger.debug("Got uselist")
+ query = query.filter(attr.contains(v))
+ else:
+ logger.debug("Single item.")
+ query = query.filter(attr == v)
if k in singles:
logger.warning(f"{k} is in singles. Returning only one value.")
limit = 1
+ if offset:
+ query.offset(offset)
with query.session.no_autoflush:
match limit:
case 0:
@@ -476,13 +487,13 @@ class BaseClass(Base):
# logger.debug(f"Attempting to set: {key} to {value}")
if key.startswith("_"):
return super().__setattr__(key, value)
- try:
- check = not hasattr(self, key)
- except:
- return
+ # try:
+ check = not hasattr(self, key)
+ # except:
+ # return
if check:
try:
- json.dumps(value)
+ value = json.dumps(value)
except TypeError:
value = str(value)
self._misc_info.update({key: value})
@@ -612,6 +623,7 @@ class BaseClass(Base):
if dlg.exec():
pass
+
class LogMixin(Base):
tracking_exclusion: ClassVar = ['artic_technician', 'clientsubmissionsampleassociation',
'submission_reagent_associations', 'submission_equipment_associations',
diff --git a/src/submissions/backend/db/models/submissions.py b/src/submissions/backend/db/models/submissions.py
index 0e8e563..f93dc88 100644
--- a/src/submissions/backend/db/models/submissions.py
+++ b/src/submissions/backend/db/models/submissions.py
@@ -149,12 +149,14 @@ class ClientSubmission(BaseClass, LogMixin):
pass
# query = query.order_by(cls.submitted_date.desc())
# NOTE: Split query results into pages of size {page_size}
- if page_size > 0:
- query = query.limit(page_size)
+ if page_size > 0 and limit == 0:
+ limit = page_size
page = page - 1
if page is not None:
- query = query.offset(page * page_size)
- return cls.execute_query(query=query, limit=limit, **kwargs)
+ offset = page * page_size
+ else:
+ offset = None
+ return cls.execute_query(query=query, limit=limit, offset=offset, **kwargs)
@classmethod
def submissions_to_df(cls, submissiontype: str | None = None, limit: int = 0,
@@ -269,7 +271,9 @@ class ClientSubmission(BaseClass, LogMixin):
try:
assert isinstance(sample, Sample)
except AssertionError:
+ logger.warning(f"Converting {sample} to sql.")
sample = sample.to_sql()
+ logger.debug(sample.__dict__)
try:
row = sample._misc_info['row']
except (KeyError, AttributeError):
@@ -278,10 +282,12 @@ class ClientSubmission(BaseClass, LogMixin):
column = sample._misc_info['column']
except KeyError:
column = 0
+ logger.debug(f"Sample: {sample}")
+ submission_rank = sample._misc_info['submission_rank']
assoc = ClientSubmissionSampleAssociation(
sample=sample,
submission=self,
- submission_rank=sample._misc_info['submission_rank'],
+ submission_rank=submission_rank,
row=row,
column=column
)
@@ -310,8 +316,9 @@ class ClientSubmission(BaseClass, LogMixin):
for sample in active_samples:
sample = sample.to_sql()
logger.debug(f"Sample: {sample.id}")
- assoc = run.add_sample(sample)
- assoc.save()
+ if sample not in run.sample:
+ assoc = run.add_sample(sample)
+ assoc.save()
else:
logger.warning("Run cancelled.")
obj.set_data()
diff --git a/src/submissions/backend/excel/parsers/__init__.py b/src/submissions/backend/excel/parsers/__init__.py
index 0e0048a..7e5db59 100644
--- a/src/submissions/backend/excel/parsers/__init__.py
+++ b/src/submissions/backend/excel/parsers/__init__.py
@@ -43,9 +43,10 @@ class DefaultParser(object):
"""
self.proceduretype = proceduretype
try:
- self._pyd_object = getattr(pydant, f"Pyd{self.__class__.__name__.replace('Parser', '')}")
- except AttributeError:
- self._pyd_object = pydant.PydResults
+ self._pyd_object = getattr(pydant, f"Pyd{self.__class__.__name__.replace('Parser', '').replace('Info', '')}")
+ except AttributeError as e:
+ logger.error(f"Couldn't get pyd object: Pyd{self.__class__.__name__.replace('Parser', '').replace('Info', '')}")
+ self._pyd_object = getattr(pydant, self.__class__.pyd_name)
self.workbook = load_workbook(self.filepath, data_only=True)
if not range_dict:
self.range_dict = self.__class__.default_range_dict
@@ -118,7 +119,7 @@ class DefaultTABLEParser(DefaultParser):
if isinstance(key, str):
key = key.lower().replace(" ", "_")
key = re.sub(r"_(\(.*\)|#)", "", key)
- logger.debug(f"Row {ii} values: {key}: {value}")
+ # logger.debug(f"Row {ii} values: {key}: {value}")
output[key] = value
yield output
@@ -126,4 +127,4 @@ class DefaultTABLEParser(DefaultParser):
return [self._pyd_object(**output) for output in self.parsed_info]
from .clientsubmission_parser import *
-from backend.excel.parsers.results_parsers.pcr_results_parser import *
+from backend.excel.parsers.results_parsers.pcr_results_parser import PCRInfoParser, PCRSampleParser
diff --git a/src/submissions/backend/excel/parsers/clientsubmission_parser.py b/src/submissions/backend/excel/parsers/clientsubmission_parser.py
index 7c82922..ac96e9e 100644
--- a/src/submissions/backend/excel/parsers/clientsubmission_parser.py
+++ b/src/submissions/backend/excel/parsers/clientsubmission_parser.py
@@ -74,6 +74,8 @@ class ClientSubmissionInfoParser(DefaultKEYVALUEParser, SubmissionTyperMixin):
Object for retrieving submitter info from "sample list" sheet
"""
+ pyd_name = "PydClientSubmission"
+
default_range_dict = [dict(
start_row=2,
end_row=18,
@@ -110,6 +112,8 @@ class ClientSubmissionSampleParser(DefaultTABLEParser, SubmissionTyperMixin):
Object for retrieving submitter samples from "sample list" sheet
"""
+ pyd_name = "PydSample"
+
default_range_dict = [dict(
header_row=19,
end_row=115,
@@ -126,7 +130,7 @@ class ClientSubmissionSampleParser(DefaultTABLEParser, SubmissionTyperMixin):
def parsed_info(self) -> Generator[dict, None, None]:
output = super().parsed_info
for ii, sample in enumerate(output):
- logger.debug(f"Parsed info sample: {sample}")
+ # logger.debug(f"Parsed info sample: {sample}")
if isinstance(sample["row"], str) and sample["row"].lower() in ascii_lowercase[0:8]:
try:
sample["row"] = row_keys[sample["row"]]
diff --git a/src/submissions/backend/validators/pydant.py b/src/submissions/backend/validators/pydant.py
index 35b09a8..36bbdcf 100644
--- a/src/submissions/backend/validators/pydant.py
+++ b/src/submissions/backend/validators/pydant.py
@@ -290,6 +290,16 @@ class PydSample(PydBaseClass):
value = row_keys[value]
return value
+ def improved_dict(self, dictionaries: bool = True) -> dict:
+ output = super().improved_dict(dictionaries=dictionaries)
+ output['name'] = self.sample_id
+ del output['sampletype']
+ return output
+
+ def to_sql(self):
+ sql = super().to_sql()
+ sql._misc_info["submission_rank"] = self.submission_rank
+ return sql
class PydTips(BaseModel):
name: str
diff --git a/src/submissions/frontend/widgets/procedure_creation.py b/src/submissions/frontend/widgets/procedure_creation.py
index 2172ffb..656004d 100644
--- a/src/submissions/frontend/widgets/procedure_creation.py
+++ b/src/submissions/frontend/widgets/procedure_creation.py
@@ -90,7 +90,7 @@ class ProcedureCreation(QDialog):
plate_map=self.plate_map,
edit=self.edit
)
- with open("web.html", "w") as f:
+ with open("procedure_creation_rendered.html", "w") as f:
f.write(html)
self.webview.setHtml(html)
diff --git a/src/submissions/frontend/widgets/sample_checker.py b/src/submissions/frontend/widgets/sample_checker.py
index 39d5959..62e7669 100644
--- a/src/submissions/frontend/widgets/sample_checker.py
+++ b/src/submissions/frontend/widgets/sample_checker.py
@@ -5,11 +5,9 @@ from PyQt6.QtCore import Qt, pyqtSlot
from PyQt6.QtWebChannel import QWebChannel
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QGridLayout
-
from backend.db.models import ClientSubmission
from backend.validators import PydSample, RSLNamer
-from tools import get_application_from_parent, jinja_template_loading
-
+from tools import get_application_from_parent, jinja_template_loading, render_details_template
env = jinja_template_loading()
@@ -24,6 +22,7 @@ class SampleChecker(QDialog):
self.rsl_plate_number = RSLNamer.construct_new_plate_name(clientsubmission.to_dict())
else:
self.rsl_plate_number = clientsubmission
+ logger.debug(f"RSL Plate number: {self.rsl_plate_number}")
self.samples = samples
self.setWindowTitle(title)
self.app = get_application_from_parent(parent)
@@ -38,22 +37,27 @@ class SampleChecker(QDialog):
# NOTE: Used to maintain javascript functions.
template = env.get_template("sample_checker.html")
template_path = Path(template.environment.loader.__getattribute__("searchpath")[0])
- with open(template_path.joinpath("css", "styles.css"), "r") as f:
- css = f.read()
+ # with open(template_path.joinpath("css", "styles.css"), "r") as f:
+ # css = [f.read()]
try:
samples = self.formatted_list
except AttributeError as e:
logger.error(f"Problem getting sample list: {e}")
samples = []
- html = template.render(samples=samples, css=css, rsl_plate_number=self.rsl_plate_number)
+ # html = template.render(samples=samples, css=css, rsl_plate_number=self.rsl_plate_number)
+ html = render_details_template(template_name="sample_checker", samples=samples, rsl_plate_number=self.rsl_plate_number)
self.webview.setHtml(html)
+ self.webview.page().setWebChannel(self.channel)
QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
self.buttonBox = QDialogButtonBox(QBtn)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
self.layout.addWidget(self.buttonBox, 11, 9, 1, 1, alignment=Qt.AlignmentFlag.AlignRight)
self.setLayout(self.layout)
- self.webview.page().setWebChannel(self.channel)
+
+ with open("sample_checker_rendered.html", "w") as f:
+ f.write(html)
+ logger.debug(f"HTML sample checker written!")
@pyqtSlot(str, str, str)
def text_changed(self, submission_rank: str, key: str, new_value: str):
@@ -65,8 +69,8 @@ class SampleChecker(QDialog):
return
item.__setattr__(key, new_value)
- @pyqtSlot(str, bool)
- def enable_sample(self, submission_rank: str, enabled: bool):
+ @pyqtSlot(int, bool)
+ def enable_sample(self, submission_rank: int, enabled: bool):
logger.debug(f"Name: {submission_rank}, Enabled: {enabled}")
try:
item = next((sample for sample in self.samples if int(submission_rank) == sample.submission_rank))
diff --git a/src/submissions/frontend/widgets/submission_table.py b/src/submissions/frontend/widgets/submission_table.py
index f80b90c..2541cc6 100644
--- a/src/submissions/frontend/widgets/submission_table.py
+++ b/src/submissions/frontend/widgets/submission_table.py
@@ -325,8 +325,9 @@ class SubmissionsTree(QTreeView):
"""
indexes = self.selectedIndexes()
dicto = next((item.data(1) for item in indexes if item.data(1)))
+ logger.debug(f"Dicto: {pformat(dicto)}")
query_obj = dicto['item_type'].query(name=dicto['query_str'], limit=1)
- logger.debug(query_obj)
+ logger.debug(f"Querying: {query_obj}")
# NOTE: Convert to data in id column (i.e. column 0)
# id = id.sibling(id.row(), 0).data()
# logger.debug(id.model().query_group_object(id.row()))
diff --git a/src/submissions/frontend/widgets/submission_widget.py b/src/submissions/frontend/widgets/submission_widget.py
index dcd5286..2a4163f 100644
--- a/src/submissions/frontend/widgets/submission_widget.py
+++ b/src/submissions/frontend/widgets/submission_widget.py
@@ -141,6 +141,11 @@ class SubmissionFormContainer(QWidget):
checker = SampleChecker(self, "Sample Checker", self.pydsamples)
if checker.exec():
# logger.debug(pformat(self.pydclientsubmission.sample))
+ try:
+ assert isinstance(self.pydclientsubmission, PydClientSubmission)
+ except AssertionError as e:
+ logger.error(f"Got wrong type for {self.pydclientsubmission}: {type(self.pydclientsubmission)}")
+ raise e
self.form = self.pydclientsubmission.to_form(parent=self)
self.form.samples = self.pydsamples
self.layout().addWidget(self.form)
diff --git a/src/submissions/templates/details.html b/src/submissions/templates/details.html
index 7145c36..ef608a3 100644
--- a/src/submissions/templates/details.html
+++ b/src/submissions/templates/details.html
@@ -24,23 +24,23 @@
{% block script %}
{% if not child %}
-
-{% endif %}
+
+
+
+
+
+
+
+
+
+
+
+
+
{% for j in js%}
- {% endblock %}
+
+
+
+ {{ super() }}
+ {% endblock %}