Writer beginnings
This commit is contained in:
@@ -18,7 +18,10 @@ 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 frontend import select_save_file
|
||||||
from tools import report_result, list_sort_dict
|
from tools import report_result, list_sort_dict
|
||||||
|
from backend.excel import writers
|
||||||
|
|
||||||
# NOTE: Load testing environment
|
# NOTE: Load testing environment
|
||||||
if 'pytest' in sys.modules:
|
if 'pytest' in sys.modules:
|
||||||
@@ -241,6 +244,7 @@ class BaseClass(Base):
|
|||||||
allowed = [k for k, v in cls.__dict__.items() if isinstance(v, InstrumentedAttribute) or isinstance(v, hybrid_property)]
|
allowed = [k for k, v in cls.__dict__.items() if isinstance(v, InstrumentedAttribute) or isinstance(v, hybrid_property)]
|
||||||
# and not isinstance(v.property, _RelationshipDeclared)]
|
# and not isinstance(v.property, _RelationshipDeclared)]
|
||||||
sanitized_kwargs = {k: v for k, v in kwargs.items() if k in allowed}
|
sanitized_kwargs = {k: v for k, v in kwargs.items() if k in allowed}
|
||||||
|
outside_kwargs = {k: v for k, v in kwargs.items() if k not in allowed}
|
||||||
logger.debug(f"Sanitized kwargs: {sanitized_kwargs}")
|
logger.debug(f"Sanitized kwargs: {sanitized_kwargs}")
|
||||||
instance = cls.query(**sanitized_kwargs)
|
instance = cls.query(**sanitized_kwargs)
|
||||||
if not instance or isinstance(instance, list):
|
if not instance or isinstance(instance, list):
|
||||||
@@ -258,6 +262,7 @@ class BaseClass(Base):
|
|||||||
setattr(instance, k, v.to_sql())
|
setattr(instance, k, v.to_sql())
|
||||||
else:
|
else:
|
||||||
logger.error(f"Could not set {k} due to {e}")
|
logger.error(f"Could not set {k} due to {e}")
|
||||||
|
instance._misc_info.update(outside_kwargs)
|
||||||
logger.info(f"Instance from query or create: {instance}, new: {new}")
|
logger.info(f"Instance from query or create: {instance}, new: {new}")
|
||||||
return instance, new
|
return instance, new
|
||||||
|
|
||||||
@@ -309,10 +314,16 @@ class BaseClass(Base):
|
|||||||
check = False
|
check = False
|
||||||
if check:
|
if check:
|
||||||
logger.debug("Got uselist")
|
logger.debug("Got uselist")
|
||||||
query = query.filter(attr.contains(v))
|
try:
|
||||||
|
query = query.filter(attr.contains(v))
|
||||||
|
except ArgumentError:
|
||||||
|
continue
|
||||||
else:
|
else:
|
||||||
logger.debug("Single item.")
|
logger.debug("Single item.")
|
||||||
query = query.filter(attr == v)
|
try:
|
||||||
|
query = query.filter(attr == v)
|
||||||
|
except ArgumentError:
|
||||||
|
continue
|
||||||
if k in singles:
|
if k in singles:
|
||||||
logger.warning(f"{k} is in singles. Returning only one value.")
|
logger.warning(f"{k} is in singles. Returning only one value.")
|
||||||
limit = 1
|
limit = 1
|
||||||
@@ -496,7 +507,10 @@ class BaseClass(Base):
|
|||||||
value = json.dumps(value)
|
value = json.dumps(value)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
value = str(value)
|
value = str(value)
|
||||||
self._misc_info.update({key: value})
|
try:
|
||||||
|
self._misc_info.update({key: value})
|
||||||
|
except AttributeError:
|
||||||
|
self._misc_info = {key: value}
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
field_type = getattr(self.__class__, key)
|
field_type = getattr(self.__class__, key)
|
||||||
@@ -623,6 +637,17 @@ class BaseClass(Base):
|
|||||||
if dlg.exec():
|
if dlg.exec():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def export(self, obj, output_filepath: str|Path|None=None):
|
||||||
|
if not hasattr(self, "template_file"):
|
||||||
|
logger.error(f"Export not implemented for {self.__class__.__name__}")
|
||||||
|
return
|
||||||
|
pyd = self.to_pydantic()
|
||||||
|
if not output_filepath:
|
||||||
|
output_filepath = select_save_file(obj=obj, default_name=pyd.construct_filename(), extension="xlsx")
|
||||||
|
Writer = getattr(writers, f"{self.__class__.__name__}Writer")
|
||||||
|
writer = Writer(output_filepath=output_filepath, pydant_obj=pyd, range_dict=self.range_dict)
|
||||||
|
workbook = writer
|
||||||
|
|
||||||
|
|
||||||
class LogMixin(Base):
|
class LogMixin(Base):
|
||||||
tracking_exclusion: ClassVar = ['artic_technician', 'clientsubmissionsampleassociation',
|
tracking_exclusion: ClassVar = ['artic_technician', 'clientsubmissionsampleassociation',
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ from operator import itemgetter
|
|||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
from sqlalchemy.ext.hybrid import hybrid_property
|
from sqlalchemy.ext.hybrid import hybrid_property
|
||||||
|
|
||||||
|
from frontend import select_save_file
|
||||||
from . import Base, BaseClass, Reagent, SubmissionType, KitType, ClientLab, Contact, LogMixin, Procedure, kittype_procedure
|
from . import Base, BaseClass, Reagent, SubmissionType, KitType, ClientLab, Contact, LogMixin, Procedure, kittype_procedure
|
||||||
from sqlalchemy import Column, String, TIMESTAMP, INTEGER, ForeignKey, JSON, FLOAT, case, func, Table, Sequence
|
from sqlalchemy import Column, String, TIMESTAMP, INTEGER, ForeignKey, JSON, FLOAT, case, func, Table, Sequence
|
||||||
from sqlalchemy.orm import relationship, validates, Query
|
from sqlalchemy.orm import relationship, validates, Query
|
||||||
@@ -158,6 +160,14 @@ class ClientSubmission(BaseClass, LogMixin):
|
|||||||
offset = None
|
offset = None
|
||||||
return cls.execute_query(query=query, limit=limit, offset=offset, **kwargs)
|
return cls.execute_query(query=query, limit=limit, offset=offset, **kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def template_file(self):
|
||||||
|
return self.submissiontype.template_file
|
||||||
|
|
||||||
|
@property
|
||||||
|
def range_dict(self):
|
||||||
|
return self.submissiontype.info_map
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def submissions_to_df(cls, submissiontype: str | None = None, limit: int = 0,
|
def submissions_to_df(cls, submissiontype: str | None = None, limit: int = 0,
|
||||||
chronologic: bool = True, page: int = 1, page_size: int = 250) -> pd.DataFrame:
|
chronologic: bool = True, page: int = 1, page_size: int = 250) -> pd.DataFrame:
|
||||||
@@ -318,12 +328,12 @@ class ClientSubmission(BaseClass, LogMixin):
|
|||||||
logger.debug(f"Sample: {sample.id}")
|
logger.debug(f"Sample: {sample.id}")
|
||||||
if sample not in run.sample:
|
if sample not in run.sample:
|
||||||
assoc = run.add_sample(sample)
|
assoc = run.add_sample(sample)
|
||||||
assoc.save()
|
# assoc.save()
|
||||||
|
run.save()
|
||||||
else:
|
else:
|
||||||
logger.warning("Run cancelled.")
|
logger.warning("Run cancelled.")
|
||||||
obj.set_data()
|
obj.set_data()
|
||||||
|
|
||||||
|
|
||||||
def edit(self, obj):
|
def edit(self, obj):
|
||||||
logger.debug("Edit")
|
logger.debug("Edit")
|
||||||
|
|
||||||
@@ -352,6 +362,9 @@ class ClientSubmission(BaseClass, LogMixin):
|
|||||||
output['expanded'] = ["clientlab", "contact", "submissiontype"]
|
output['expanded'] = ["clientlab", "contact", "submissiontype"]
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
def to_pydantic(self, filepath: Path|str|None=None, **kwargs):
|
||||||
|
output = super().to_pydantic(filepath=filepath, **kwargs)
|
||||||
|
return output
|
||||||
|
|
||||||
class Run(BaseClass, LogMixin):
|
class Run(BaseClass, LogMixin):
|
||||||
"""
|
"""
|
||||||
@@ -1266,6 +1279,15 @@ class Run(BaseClass, LogMixin):
|
|||||||
self.set_attribute(key='comment', value=comment)
|
self.set_attribute(key='comment', value=comment)
|
||||||
self.save(original=False)
|
self.save(original=False)
|
||||||
|
|
||||||
|
def export(self, obj, output_filepath: str|Path|None=None):
|
||||||
|
|
||||||
|
clientsubmission_pyd = self.clientsubmission.to_pydantic()
|
||||||
|
if not output_filepath:
|
||||||
|
output_filepath = select_save_file(obj=obj, default_name=clientsubmission_pyd.construct_filename(), extension="xlsx")
|
||||||
|
Writer = getattr(writers, "ClientSubmissionWriter")
|
||||||
|
writer = Writer(output_filepath=output_filepath, pydant_obj=pyd, range_dict=self.range_dict)
|
||||||
|
workbook = writer.
|
||||||
|
|
||||||
def backup(self, obj=None, fname: Path | None = None, full_backup: bool = False):
|
def backup(self, obj=None, fname: Path | None = None, full_backup: bool = False):
|
||||||
"""
|
"""
|
||||||
Exports xlsx info files for this instance.
|
Exports xlsx info files for this instance.
|
||||||
@@ -1890,8 +1912,8 @@ class RunSampleAssociation(BaseClass):
|
|||||||
# id = Column(INTEGER, unique=True, nullable=False) #: id to be used for inheriting purposes
|
# id = Column(INTEGER, unique=True, nullable=False) #: id to be used for inheriting purposes
|
||||||
sample_id = Column(INTEGER, ForeignKey("_sample.id"), primary_key=True) #: id of associated sample
|
sample_id = Column(INTEGER, ForeignKey("_sample.id"), primary_key=True) #: id of associated sample
|
||||||
run_id = Column(INTEGER, ForeignKey("_run.id"), primary_key=True) #: id of associated procedure
|
run_id = Column(INTEGER, ForeignKey("_run.id"), primary_key=True) #: id of associated procedure
|
||||||
row = Column(INTEGER) #: row on the 96 well plate
|
# row = Column(INTEGER) #: row on the 96 well plate
|
||||||
column = Column(INTEGER) #: column on the 96 well plate
|
# column = Column(INTEGER) #: column on the 96 well plate
|
||||||
# misc_info = Column(JSON)
|
# misc_info = Column(JSON)
|
||||||
|
|
||||||
# NOTE: reference to the Submission object
|
# NOTE: reference to the Submission object
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ from __future__ import annotations
|
|||||||
import logging, re
|
import logging, re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Generator, Tuple, TYPE_CHECKING
|
from typing import Generator, Tuple, TYPE_CHECKING
|
||||||
|
|
||||||
|
from openpyxl.reader.excel import load_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:
|
||||||
@@ -78,8 +80,6 @@ class DefaultKEYVALUEParser(DefaultParser):
|
|||||||
sheet="Sample List"
|
sheet="Sample List"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def parsed_info(self) -> Generator[Tuple, None, None]:
|
def parsed_info(self) -> Generator[Tuple, None, None]:
|
||||||
for item in self.range_dict:
|
for item in self.range_dict:
|
||||||
@@ -91,7 +91,10 @@ class DefaultKEYVALUEParser(DefaultParser):
|
|||||||
key = re.sub(r"\(.*\)", "", key)
|
key = re.sub(r"\(.*\)", "", key)
|
||||||
key = key.lower().replace(":", "").strip().replace(" ", "_")
|
key = key.lower().replace(":", "").strip().replace(" ", "_")
|
||||||
value = item['worksheet'].cell(row, item['value_column']).value
|
value = item['worksheet'].cell(row, item['value_column']).value
|
||||||
value = dict(value=value, missing=False if value else True)
|
missing = False if value else True
|
||||||
|
location_map = dict(row=row, key_column=item['key_column'], value_column=item['value_column'], sheet=item['sheet'])
|
||||||
|
value = dict(value=value, location=location_map, missing=missing)
|
||||||
|
logger.debug(f"Yieldings {value} for {key}")
|
||||||
yield key, value
|
yield key, value
|
||||||
|
|
||||||
|
|
||||||
@@ -126,5 +129,5 @@ class DefaultTABLEParser(DefaultParser):
|
|||||||
def to_pydantic(self, **kwargs):
|
def to_pydantic(self, **kwargs):
|
||||||
return [self._pyd_object(**output) for output in self.parsed_info]
|
return [self._pyd_object(**output) for output in self.parsed_info]
|
||||||
|
|
||||||
from .clientsubmission_parser import *
|
from .clientsubmission_parser import ClientSubmissionSampleParser, ClientSubmissionInfoParser
|
||||||
from backend.excel.parsers.results_parsers.pcr_results_parser import PCRInfoParser, PCRSampleParser
|
from backend.excel.parsers.results_parsers.pcr_results_parser import PCRInfoParser, PCRSampleParser
|
||||||
|
|||||||
@@ -96,8 +96,6 @@ class ClientSubmissionInfoParser(DefaultKEYVALUEParser, SubmissionTyperMixin):
|
|||||||
# TODO: check if run with name already exists
|
# TODO: check if run with name already exists
|
||||||
add_run = QuestionAsker(title="Add Run?", message="We've detected a sheet corresponding to an associated procedure type.\nWould you like to add a new run?")
|
add_run = QuestionAsker(title="Add Run?", message="We've detected a sheet corresponding to an associated procedure type.\nWould you like to add a new run?")
|
||||||
if add_run.accepted:
|
if add_run.accepted:
|
||||||
|
|
||||||
|
|
||||||
# NOTE: recruit parser.
|
# NOTE: recruit parser.
|
||||||
try:
|
try:
|
||||||
manager = getattr(procedure_managers, name)
|
manager = getattr(procedure_managers, name)
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ logger = logging.getLogger(f"procedure.{__name__}")
|
|||||||
|
|
||||||
class PydBaseClass(BaseModel, extra='allow', validate_assignment=True):
|
class PydBaseClass(BaseModel, extra='allow', validate_assignment=True):
|
||||||
_sql_object: ClassVar = None
|
_sql_object: ClassVar = None
|
||||||
|
# _misc_info: dict|None = None
|
||||||
|
|
||||||
@model_validator(mode="before")
|
@model_validator(mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -67,6 +68,10 @@ class PydBaseClass(BaseModel, extra='allow', validate_assignment=True):
|
|||||||
def __init__(self, **data):
|
def __init__(self, **data):
|
||||||
# NOTE: Grab the sql model for validation purposes.
|
# NOTE: Grab the sql model for validation purposes.
|
||||||
self.__class__._sql_object = getattr(models, self.__class__.__name__.replace("Pyd", ""))
|
self.__class__._sql_object = getattr(models, self.__class__.__name__.replace("Pyd", ""))
|
||||||
|
# try:
|
||||||
|
# self.template_file = self.__class__._sql_object.template_file
|
||||||
|
# except AttributeError:
|
||||||
|
# pass
|
||||||
super().__init__(**data)
|
super().__init__(**data)
|
||||||
|
|
||||||
def filter_field(self, key: str) -> Any:
|
def filter_field(self, key: str) -> Any:
|
||||||
@@ -105,6 +110,12 @@ class PydBaseClass(BaseModel, extra='allow', validate_assignment=True):
|
|||||||
output = {k: getattr(self, k) for k in fields}
|
output = {k: getattr(self, k) for k in fields}
|
||||||
else:
|
else:
|
||||||
output = {k: self.filter_field(k) for k in fields}
|
output = {k: self.filter_field(k) for k in fields}
|
||||||
|
if hasattr(self, "misc_info") and "info_placement" in self.misc_info:
|
||||||
|
for k, v in output.items():
|
||||||
|
try:
|
||||||
|
output[k]['location'] = [item['location'] for item in self.misc_info['info_placement'] if item['name'] == k]
|
||||||
|
except (TypeError, KeyError):
|
||||||
|
continue
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def to_sql(self):
|
def to_sql(self):
|
||||||
@@ -301,6 +312,7 @@ class PydSample(PydBaseClass):
|
|||||||
sql._misc_info["submission_rank"] = self.submission_rank
|
sql._misc_info["submission_rank"] = self.submission_rank
|
||||||
return sql
|
return sql
|
||||||
|
|
||||||
|
|
||||||
class PydTips(BaseModel):
|
class PydTips(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
lot: str | None = Field(default=None)
|
lot: str | None = Field(default=None)
|
||||||
@@ -1662,7 +1674,7 @@ class PydProcedure(PydBaseClass, arbitrary_types_allowed=True):
|
|||||||
class PydClientSubmission(PydBaseClass):
|
class PydClientSubmission(PydBaseClass):
|
||||||
# sql_object: ClassVar = ClientSubmission
|
# sql_object: ClassVar = ClientSubmission
|
||||||
|
|
||||||
filepath: Path
|
filepath: Path | None = Field(default=None)
|
||||||
submissiontype: dict | None
|
submissiontype: dict | None
|
||||||
submitted_date: dict | None = Field(default=dict(value=date.today(), missing=True), validate_default=True)
|
submitted_date: dict | None = Field(default=dict(value=date.today(), missing=True), validate_default=True)
|
||||||
clientlab: dict | None
|
clientlab: dict | None
|
||||||
@@ -1673,6 +1685,39 @@ class PydClientSubmission(PydBaseClass):
|
|||||||
contact: dict | None = Field(default=dict(value=None, missing=True), validate_default=True)
|
contact: dict | None = Field(default=dict(value=None, missing=True), validate_default=True)
|
||||||
submitter_plate_id: dict | None = Field(default=dict(value=None, missing=True), validate_default=True)
|
submitter_plate_id: dict | None = Field(default=dict(value=None, missing=True), validate_default=True)
|
||||||
|
|
||||||
|
@field_validator("submitted_date", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def enforce_submitted_date(cls, value):
|
||||||
|
match value:
|
||||||
|
case str():
|
||||||
|
value = dict(value=datetime.strptime(value, "%Y-%m-%d %H:%M:%S"), missing=False)
|
||||||
|
case date() | datetime():
|
||||||
|
value = dict(value=value, missing=False)
|
||||||
|
case _:
|
||||||
|
pass
|
||||||
|
return value
|
||||||
|
|
||||||
|
@field_validator("submitter_plate_id", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def enforce_submitter_plate_id(cls, value):
|
||||||
|
if isinstance(value, str):
|
||||||
|
value = dict(value=value, missing=False)
|
||||||
|
return value
|
||||||
|
|
||||||
|
@field_validator("submission_category", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def enforce_submission_category_id(cls, value):
|
||||||
|
if isinstance(value, str):
|
||||||
|
value = dict(value=value, missing=False)
|
||||||
|
return value
|
||||||
|
|
||||||
|
@field_validator("sample_count", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def enforce_sample_count(cls, value):
|
||||||
|
if isinstance(value, str) or isinstance(value, int):
|
||||||
|
value = dict(value=value, missing=False)
|
||||||
|
return value
|
||||||
|
|
||||||
@field_validator("sample_count")
|
@field_validator("sample_count")
|
||||||
@classmethod
|
@classmethod
|
||||||
def enforce_integer(cls, value):
|
def enforce_integer(cls, value):
|
||||||
@@ -1700,7 +1745,7 @@ class PydClientSubmission(PydBaseClass):
|
|||||||
except TypeError:
|
except TypeError:
|
||||||
check = True
|
check = True
|
||||||
if check:
|
if check:
|
||||||
return dict(value=date.today(), missing=True)
|
value.update(dict(value=date.today(), missing=True))
|
||||||
else:
|
else:
|
||||||
match value['value']:
|
match value['value']:
|
||||||
case str():
|
case str():
|
||||||
@@ -1735,6 +1780,29 @@ class PydClientSubmission(PydBaseClass):
|
|||||||
from frontend.widgets.submission_widget import ClientSubmissionFormWidget
|
from frontend.widgets.submission_widget import ClientSubmissionFormWidget
|
||||||
return ClientSubmissionFormWidget(parent=parent, clientsubmission=self, samples=samples, disable=disable)
|
return ClientSubmissionFormWidget(parent=parent, clientsubmission=self, samples=samples, disable=disable)
|
||||||
|
|
||||||
|
def to_sql(self):
|
||||||
|
sql = super().to_sql()
|
||||||
|
if "info_placement" not in sql._misc_info:
|
||||||
|
sql._misc_info['info_placement'] = []
|
||||||
|
info_placement = []
|
||||||
|
for k in list(self.model_fields.keys()) + list(self.model_extra.keys()):
|
||||||
|
attribute = getattr(self, k)
|
||||||
|
match k:
|
||||||
|
case "filepath":
|
||||||
|
sql._misc_info[k] = attribute.__str__()
|
||||||
|
continue
|
||||||
|
case _:
|
||||||
|
pass
|
||||||
|
logger.debug(f"Setting {k} to {attribute}")
|
||||||
|
if isinstance(attribute, dict):
|
||||||
|
if "location" in attribute:
|
||||||
|
info_placement.append(dict(name=k, location=attribute['location']))
|
||||||
|
else:
|
||||||
|
info_placement.append(dict(name=k, location=None))
|
||||||
|
max_row = max([value['location']['row'] for value in info_placement if value])
|
||||||
|
sql._misc_info['info_placement'] = info_placement
|
||||||
|
return sql
|
||||||
|
|
||||||
|
|
||||||
class PydResults(PydBaseClass, arbitrary_types_allowed=True):
|
class PydResults(PydBaseClass, arbitrary_types_allowed=True):
|
||||||
results: dict = Field(default={})
|
results: dict = Field(default={})
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ class SampleChecker(QDialog):
|
|||||||
self.channel = QWebChannel()
|
self.channel = QWebChannel()
|
||||||
self.channel.registerObject('backend', self)
|
self.channel.registerObject('backend', self)
|
||||||
# NOTE: Used to maintain javascript functions.
|
# NOTE: Used to maintain javascript functions.
|
||||||
template = env.get_template("sample_checker.html")
|
# template = env.get_template("sample_checker.html")
|
||||||
template_path = Path(template.environment.loader.__getattribute__("searchpath")[0])
|
# template_path = Path(template.environment.loader.__getattribute__("searchpath")[0])
|
||||||
# with open(template_path.joinpath("css", "styles.css"), "r") as f:
|
# with open(template_path.joinpath("css", "styles.css"), "r") as f:
|
||||||
# css = [f.read()]
|
# css = [f.read()]
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -472,6 +472,10 @@ class SubmissionFormWidget(QWidget):
|
|||||||
self.missing: bool = value['missing']
|
self.missing: bool = value['missing']
|
||||||
except (TypeError, KeyError):
|
except (TypeError, KeyError):
|
||||||
self.missing: bool = True
|
self.missing: bool = True
|
||||||
|
try:
|
||||||
|
self.location: dict|None = value['location']
|
||||||
|
except (TypeError, KeyError):
|
||||||
|
self.location: dict|None = None
|
||||||
if self.input is not None:
|
if self.input is not None:
|
||||||
layout.addWidget(self.label)
|
layout.addWidget(self.label)
|
||||||
layout.addWidget(self.input)
|
layout.addWidget(self.input)
|
||||||
@@ -501,7 +505,7 @@ class SubmissionFormWidget(QWidget):
|
|||||||
value = self.input.date().toPyDate()
|
value = self.input.date().toPyDate()
|
||||||
case _:
|
case _:
|
||||||
return None, None
|
return None, None
|
||||||
return self.input.objectName(), dict(value=value, missing=self.missing)
|
return self.input.objectName(), dict(value=value, missing=self.missing, location=self.location)
|
||||||
|
|
||||||
def set_widget(self, parent: QWidget, key: str, value: dict,
|
def set_widget(self, parent: QWidget, key: str, value: dict,
|
||||||
submission_type: str | SubmissionType | None = None,
|
submission_type: str | SubmissionType | None = None,
|
||||||
@@ -518,6 +522,7 @@ class SubmissionFormWidget(QWidget):
|
|||||||
Returns:
|
Returns:
|
||||||
QWidget: Form object
|
QWidget: Form object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(submission_type, str):
|
if isinstance(submission_type, str):
|
||||||
submission_type = SubmissionType.query(name=submission_type)
|
submission_type = SubmissionType.query(name=submission_type)
|
||||||
if sub_obj is None:
|
if sub_obj is None:
|
||||||
@@ -843,6 +848,7 @@ class ClientSubmissionFormWidget(SubmissionFormWidget):
|
|||||||
value = getattr(self, item)
|
value = getattr(self, item)
|
||||||
info[item] = value
|
info[item] = value
|
||||||
for k, v in info.items():
|
for k, v in info.items():
|
||||||
|
logger.debug(f"Setting pyd {k} to {v}")
|
||||||
self.pyd.__setattr__(k, v)
|
self.pyd.__setattr__(k, v)
|
||||||
report.add_result(report)
|
report.add_result(report)
|
||||||
return report
|
return report
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
{% extends "basicsubmission_details.html" %}
|
|
||||||
|
|
||||||
<head>
|
|
||||||
{% block head %}
|
|
||||||
{{ super() }}
|
|
||||||
{% endblock %}
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
{% block body %}
|
|
||||||
{{ super() }}
|
|
||||||
{% if sub['controls'] %}
|
|
||||||
<h3><u>Attached Controls:</u></h3>
|
|
||||||
{% for item in sub['controls'] %}
|
|
||||||
<p> <b>{{ item['name'] }}:</b> {{ item['type'] }} (Targets: {{ item['targets'] }})</p>
|
|
||||||
{% if item['kraken'] %}
|
|
||||||
<p> {{ item['name'] }} Top 10 Kraken Results:</p>
|
|
||||||
<p>{% for genera in item['kraken'] %}
|
|
||||||
{{ genera['name'] }}: {{ genera['kraken_count'] }} ({{ genera['kraken_percent'] }})<br>
|
|
||||||
{% endfor %}</p>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
{% block signing_button %}
|
|
||||||
{{ super() }}
|
|
||||||
{% endblock %}
|
|
||||||
</body>
|
|
||||||
@@ -10,12 +10,8 @@
|
|||||||
<h2><u>Equipment Details for {{ equipment['name'] }}</u></h2>
|
<h2><u>Equipment Details for {{ equipment['name'] }}</u></h2>
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
<p>{% for key, value in equipment.items() if key not in equipment['excluded'] %}
|
<p>{% for key, value in equipment.items() if key not in equipment['excluded'] %}
|
||||||
<!-- <b>{{ key | replace("_", " ") | title }}: </b>{% if permission and key in reagent['editable']%}<input type={% if key=='expiry' %}"date"{% else %}"text"{% endif %} id="{{ key }}" name="{{ key }}" value="{{ value }}">{% else %}{{ value }}{% endif %}<br>-->
|
|
||||||
<b>{{ key | replace("_", " ") | title }}: </b>{{ value }}<br>
|
<b>{{ key | replace("_", " ") | title }}: </b>{{ value }}<br>
|
||||||
{% endfor %}</p>
|
{% endfor %}</p>
|
||||||
<!-- {% if permission %}-->
|
|
||||||
<!-- <button type="button" id="save_btn">Save</button>-->
|
|
||||||
<!-- {% endif %}-->
|
|
||||||
{% if equipment['submissions'] %}<h2>Submissions:</h2>
|
{% if equipment['submissions'] %}<h2>Submissions:</h2>
|
||||||
{% for submission in equipment['submissions'] %}
|
{% for submission in equipment['submissions'] %}
|
||||||
<p><b><a class="data-link" id="{{ submission['plate'] }}">{{ submission['plate'] }}:</a></b> <a class="data-link process" id="{{ submission['process'] }}">{{ submission['process'] }}</a></p>
|
<p><b><a class="data-link" id="{{ submission['plate'] }}">{{ submission['plate'] }}:</a></b> <a class="data-link process" id="{{ submission['process'] }}">{{ submission['process'] }}</a></p>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Sample name: {{ fields['submitter_id'] }}<br>
|
Sample name: {{ fields['submitter_id'] }}<br>
|
||||||
{% if fields['organism'] %}Organism: {{ fields['organism'] }}<br>{% endif %}
|
{% if fields['organism'] %}Organism: {{ fields['organism'] }}<br>{% endif %}
|
||||||
{% if fields['concentration'] %}Concentration: {{ fields['concentration'] }}<br>{% endif %}
|
{% if fields['concentration'] %}Concentration: {{ fields['concentration'] }}<br>{% endif %}
|
||||||
Well: {{ fields['well'] }}<!--{{ fields['column'] }}-->
|
Well: {{ fields['well'] }}
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
{% extends "basicsubmission_details.html" %}
|
|
||||||
|
|
||||||
<head>
|
|
||||||
{% block head %}
|
|
||||||
{{ super() }}
|
|
||||||
{% endblock %}
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
{% block body %}
|
|
||||||
{{ super() }}
|
|
||||||
{% if sub['pcr_info'] %}
|
|
||||||
{% for entry in sub['pcr_info'] %}
|
|
||||||
{% if 'comment' not in entry.keys() %}
|
|
||||||
<h3><u>qPCR Momentum Status:</u></h3>
|
|
||||||
{% else %}
|
|
||||||
<h3><u>qPCR Status:</u></h3>
|
|
||||||
{% endif %}
|
|
||||||
<p>{% for key, value in entry.items() if key != 'imported_by'%}
|
|
||||||
{% if "column" in key %}
|
|
||||||
<b>{{ key|replace('_', ' ')|title() }}:</b> {{ value }}uL<br>
|
|
||||||
{% else %}
|
|
||||||
<b>{{ key|replace('_', ' ')|title() }}:</b> {{ value }}<br>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}</p>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% if sub['origin_plate'] %}
|
|
||||||
<br/>
|
|
||||||
<h3><u>24 Well Plate:</u></h3>
|
|
||||||
{{ sub['origin_plate'] }}
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
{% block signing_button %}
|
|
||||||
{{ super() }}
|
|
||||||
{% endblock %}
|
|
||||||
</body>
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
{% extends "basicsubmission_details.html" %}
|
|
||||||
|
|
||||||
<head>
|
|
||||||
{% block head %}
|
|
||||||
{{ super() }}
|
|
||||||
{% endblock %}
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
{% block body %}
|
|
||||||
{{ super() }}
|
|
||||||
{% if sub['gel_info'] %}
|
|
||||||
<br/>
|
|
||||||
<h3><u>Gel Box:</u></h3>
|
|
||||||
{% if sub['gel_image_actual'] %}
|
|
||||||
<br/>
|
|
||||||
<img align='left' height="400px" width="600px" src="data:image/jpeg;base64,{{ sub['gel_image_actual'] | safe }}">
|
|
||||||
{% endif %}
|
|
||||||
<br/>
|
|
||||||
<table style="width:100%; border: 1px solid black; border-collapse: collapse;">
|
|
||||||
<tr>
|
|
||||||
{% for header in sub['headers'] %}
|
|
||||||
<th>{{ header }}</th>
|
|
||||||
{% endfor %}
|
|
||||||
</tr>
|
|
||||||
{% for field in sub['gel_info'] %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ field['name'] }}</td>
|
|
||||||
{% for item in field['values'] %}
|
|
||||||
<td>{{ item['value'] }}</td>
|
|
||||||
{% endfor %}
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
<br/>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
{% block signing_button %}
|
|
||||||
{{ super() }}
|
|
||||||
{% endblock %}
|
|
||||||
</body>
|
|
||||||
Reference in New Issue
Block a user