Added in checkbox to use all samples in Concentrations tab (very slow).
This commit is contained in:
@@ -1,3 +1,7 @@
|
|||||||
|
# 202504.01
|
||||||
|
|
||||||
|
- Added in checkbox to use all samples in Concentrations tab (very slow).
|
||||||
|
|
||||||
# 202503.05
|
# 202503.05
|
||||||
|
|
||||||
- Created Sample verification before import.
|
- Created Sample verification before import.
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class BaseClass(Base):
|
|||||||
try:
|
try:
|
||||||
return f"<{self.__class__.__name__}({self.name})>"
|
return f"<{self.__class__.__name__}({self.name})>"
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return f"<{self.__class__.__name__}({self.__name__})>"
|
return f"<{self.__class__.__name__}(Unknown)>"
|
||||||
|
|
||||||
# @classproperty
|
# @classproperty
|
||||||
# def skip_on_edit(cls):
|
# def skip_on_edit(cls):
|
||||||
@@ -411,13 +411,13 @@ class BaseClass(Base):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
return super().__setattr__(key, value)
|
return super().__setattr__(key, value)
|
||||||
if isinstance(field_type, InstrumentedAttribute):
|
if isinstance(field_type, InstrumentedAttribute):
|
||||||
logger.debug(f"{key} is an InstrumentedAttribute.")
|
# logger.debug(f"{key} is an InstrumentedAttribute.")
|
||||||
match field_type.property:
|
match field_type.property:
|
||||||
case ColumnProperty():
|
case ColumnProperty():
|
||||||
# logger.debug(f"Setting ColumnProperty to {value}")
|
# logger.debug(f"Setting ColumnProperty to {value}")
|
||||||
return super().__setattr__(key, value)
|
return super().__setattr__(key, value)
|
||||||
case _RelationshipDeclared():
|
case _RelationshipDeclared():
|
||||||
logger.debug(f"{self.__class__.__name__} Setting _RelationshipDeclared for {key} to {value}")
|
# logger.debug(f"{self.__class__.__name__} Setting _RelationshipDeclared for {key} to {value}")
|
||||||
if field_type.property.uselist:
|
if field_type.property.uselist:
|
||||||
logger.debug(f"Setting with uselist")
|
logger.debug(f"Setting with uselist")
|
||||||
existing = self.__getattribute__(key)
|
existing = self.__getattribute__(key)
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
Models for the main submission and sample types.
|
Models for the main submission and sample types.
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pickle
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from getpass import getuser
|
from getpass import getuser
|
||||||
import logging, uuid, tempfile, re, base64, numpy as np, pandas as pd, types, sys
|
import logging, uuid, tempfile, re, base64, numpy as np, pandas as pd, types, sys
|
||||||
@@ -588,7 +590,39 @@ class BasicSubmission(BaseClass, LogMixin):
|
|||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
logger.error(f"Could not set {self} attribute {key} to {value} due to \n{e}")
|
logger.error(f"Could not set {self} attribute {key} to {value} due to \n{e}")
|
||||||
|
|
||||||
def update_subsampassoc(self, sample: BasicSample, input_dict: dict) -> SubmissionSampleAssociation:
|
# def update_subsampassoc(self, sample: BasicSample, input_dict: dict) -> SubmissionSampleAssociation:
|
||||||
|
# """
|
||||||
|
# Update a joined submission sample association.
|
||||||
|
#
|
||||||
|
# Args:
|
||||||
|
# sample (BasicSample): Associated sample.
|
||||||
|
# input_dict (dict): values to be updated
|
||||||
|
#
|
||||||
|
# Returns:
|
||||||
|
# SubmissionSampleAssociation: Updated association
|
||||||
|
# """
|
||||||
|
# try:
|
||||||
|
# logger.debug(f"Searching for sample {sample} at column {input_dict['column']} and row {input_dict['row']}")
|
||||||
|
# assoc = next((item for item in self.submission_sample_associations
|
||||||
|
# if item.sample == sample and
|
||||||
|
# item.row == input_dict['row'] and
|
||||||
|
# item.column == input_dict['column']))
|
||||||
|
# logger.debug(f"Found assoc {pformat(assoc.__dict__)}")
|
||||||
|
# except StopIteration:
|
||||||
|
# report = Report()
|
||||||
|
# report.add_result(
|
||||||
|
# Result(msg=f"Couldn't find submission sample association for {sample.submitter_id}", status="Warning"))
|
||||||
|
# return report
|
||||||
|
# for k, v in input_dict.items():
|
||||||
|
# try:
|
||||||
|
# # logger.debug(f"Setting assoc {assoc} with key {k} to value {v}")
|
||||||
|
# setattr(assoc, k, v)
|
||||||
|
# # NOTE: for some reason I don't think assoc.__setattr__(k, v) works here.
|
||||||
|
# except AttributeError:
|
||||||
|
# logger.error(f"Can't set {k} to {v}")
|
||||||
|
# return assoc
|
||||||
|
|
||||||
|
def update_subsampassoc(self, assoc: SubmissionSampleAssociation, input_dict: dict) -> SubmissionSampleAssociation:
|
||||||
"""
|
"""
|
||||||
Update a joined submission sample association.
|
Update a joined submission sample association.
|
||||||
|
|
||||||
@@ -599,19 +633,26 @@ class BasicSubmission(BaseClass, LogMixin):
|
|||||||
Returns:
|
Returns:
|
||||||
SubmissionSampleAssociation: Updated association
|
SubmissionSampleAssociation: Updated association
|
||||||
"""
|
"""
|
||||||
try:
|
# try:
|
||||||
assoc = next(item for item in self.submission_sample_associations if item.sample == sample)
|
# logger.debug(f"Searching for sample {sample} at column {input_dict['column']} and row {input_dict['row']}")
|
||||||
except StopIteration:
|
# assoc = next((item for item in self.submission_sample_associations
|
||||||
report = Report()
|
# if item.sample == sample and
|
||||||
report.add_result(
|
# item.row == input_dict['row'] and
|
||||||
Result(msg=f"Couldn't find submission sample association for {sample.submitter_id}", status="Warning"))
|
# item.column == input_dict['column']))
|
||||||
return report
|
# logger.debug(f"Found assoc {pformat(assoc.__dict__)}")
|
||||||
|
# except StopIteration:
|
||||||
|
# report = Report()
|
||||||
|
# report.add_result(
|
||||||
|
# Result(msg=f"Couldn't find submission sample association for {sample.submitter_id}", status="Warning"))
|
||||||
|
# return report
|
||||||
for k, v in input_dict.items():
|
for k, v in input_dict.items():
|
||||||
try:
|
try:
|
||||||
|
# logger.debug(f"Setting assoc {assoc} with key {k} to value {v}")
|
||||||
setattr(assoc, k, v)
|
setattr(assoc, k, v)
|
||||||
# NOTE: for some reason I don't think assoc.__setattr__(k, v) works here.
|
# NOTE: for some reason I don't think assoc.__setattr__(k, v) works here.
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
logger.error(f"Can't set {k} to {v}")
|
# logger.error(f"Can't set {k} to {v}")
|
||||||
|
pass
|
||||||
return assoc
|
return assoc
|
||||||
|
|
||||||
def update_reagentassoc(self, reagent: Reagent, role: str):
|
def update_reagentassoc(self, reagent: Reagent, role: str):
|
||||||
@@ -949,10 +990,12 @@ class BasicSubmission(BaseClass, LogMixin):
|
|||||||
pcr_sample_map = cls.get_submission_type().sample_map['pcr_samples']
|
pcr_sample_map = cls.get_submission_type().sample_map['pcr_samples']
|
||||||
main_sheet = xl[pcr_sample_map['main_sheet']]
|
main_sheet = xl[pcr_sample_map['main_sheet']]
|
||||||
fields = {k: v for k, v in pcr_sample_map.items() if k not in ['main_sheet', 'start_row']}
|
fields = {k: v for k, v in pcr_sample_map.items() if k not in ['main_sheet', 'start_row']}
|
||||||
|
logger.debug(f"Fields: {fields}")
|
||||||
for row in main_sheet.iter_rows(min_row=pcr_sample_map['start_row']):
|
for row in main_sheet.iter_rows(min_row=pcr_sample_map['start_row']):
|
||||||
idx = row[0].row
|
idx = row[0].row
|
||||||
sample = {}
|
sample = {}
|
||||||
for k, v in fields.items():
|
for k, v in fields.items():
|
||||||
|
# logger.debug(f"Checking key: {k} with value {v}")
|
||||||
sheet = xl[v['sheet']]
|
sheet = xl[v['sheet']]
|
||||||
sample[k] = sheet.cell(row=idx, column=v['column']).value
|
sample[k] = sheet.cell(row=idx, column=v['column']).value
|
||||||
yield sample
|
yield sample
|
||||||
@@ -1535,12 +1578,15 @@ class BacterialCulture(BasicSubmission):
|
|||||||
column=lookup_table['sample_columns']['concentration']).value
|
column=lookup_table['sample_columns']['concentration']).value
|
||||||
yield sample
|
yield sample
|
||||||
|
|
||||||
def get_provisional_controls(self):
|
def get_provisional_controls(self, controls_only: bool = True):
|
||||||
if self.controls:
|
if controls_only:
|
||||||
provs = (control.sample for control in self.controls)
|
if self.controls:
|
||||||
|
provs = (control.sample for control in self.controls)
|
||||||
|
else:
|
||||||
|
regex = re.compile(r"^(ATCC)|(MCS)|(EN)")
|
||||||
|
provs = (sample for sample in self.samples if bool(regex.match(sample.submitter_id)))
|
||||||
else:
|
else:
|
||||||
regex = re.compile(r"^(ATCC)|(MCS)|(EN)")
|
provs = self.samples
|
||||||
provs = (sample for sample in self.samples if bool(regex.match(sample.submitter_id)))
|
|
||||||
for prov in provs:
|
for prov in provs:
|
||||||
prov.submission = self.rsl_plate_num
|
prov.submission = self.rsl_plate_num
|
||||||
prov.submitted_date = self.submitted_date
|
prov.submitted_date = self.submitted_date
|
||||||
@@ -1668,7 +1714,7 @@ class Wastewater(BasicSubmission):
|
|||||||
# NOTE: Also, you can't change the size of a list while iterating it, so don't even think about it.
|
# NOTE: Also, you can't change the size of a list while iterating it, so don't even think about it.
|
||||||
output = []
|
output = []
|
||||||
for sample in samples:
|
for sample in samples:
|
||||||
# logger.debug(sample)
|
logger.debug(sample)
|
||||||
# NOTE: remove '-{target}' from controls
|
# NOTE: remove '-{target}' from controls
|
||||||
sample['sample'] = re.sub('-N\\d*$', '', sample['sample'])
|
sample['sample'] = re.sub('-N\\d*$', '', sample['sample'])
|
||||||
# NOTE: if sample is already in output skip
|
# NOTE: if sample is already in output skip
|
||||||
@@ -1679,7 +1725,7 @@ class Wastewater(BasicSubmission):
|
|||||||
# logger.debug(f"Sample ct: {sample['ct']}")
|
# logger.debug(f"Sample ct: {sample['ct']}")
|
||||||
sample[f"ct_{sample['target'].lower()}"] = sample['ct'] if isinstance(sample['ct'], float) else 0.0
|
sample[f"ct_{sample['target'].lower()}"] = sample['ct'] if isinstance(sample['ct'], float) else 0.0
|
||||||
# NOTE: Set assessment
|
# NOTE: Set assessment
|
||||||
logger.debug(f"Sample assessemnt: {sample['assessment']}")
|
# logger.debug(f"Sample assessemnt: {sample['assessment']}")
|
||||||
# sample[f"{sample['target'].lower()}_status"] = sample['assessment']
|
# sample[f"{sample['target'].lower()}_status"] = sample['assessment']
|
||||||
# NOTE: Get sample having other target
|
# NOTE: Get sample having other target
|
||||||
other_targets = [s for s in samples if re.sub('-N\\d*$', '', s['sample']) == sample['sample']]
|
other_targets = [s for s in samples if re.sub('-N\\d*$', '', s['sample']) == sample['sample']]
|
||||||
@@ -1694,6 +1740,11 @@ class Wastewater(BasicSubmission):
|
|||||||
del sample['assessment']
|
del sample['assessment']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
# logger.debug(sample)
|
||||||
|
row = int(row_keys[sample['well'][:1]])
|
||||||
|
column = int(sample['well'][1:])
|
||||||
|
sample['row'] = row
|
||||||
|
sample['column'] = column
|
||||||
output.append(sample)
|
output.append(sample)
|
||||||
# NOTE: And then convert back to list to keep fidelity with parent method.
|
# NOTE: And then convert back to list to keep fidelity with parent method.
|
||||||
for sample in output:
|
for sample in output:
|
||||||
@@ -1779,18 +1830,43 @@ class Wastewater(BasicSubmission):
|
|||||||
parser = PCRParser(filepath=fname, submission=self)
|
parser = PCRParser(filepath=fname, submission=self)
|
||||||
self.set_attribute("pcr_info", parser.pcr_info)
|
self.set_attribute("pcr_info", parser.pcr_info)
|
||||||
# NOTE: These are generators here, need to expand.
|
# NOTE: These are generators here, need to expand.
|
||||||
pcr_samples = [sample for sample in parser.samples]
|
pcr_samples = sorted([sample for sample in parser.samples], key=itemgetter('column'))
|
||||||
|
logger.debug(f"Samples from parser: {pformat(pcr_samples)}")
|
||||||
|
# NOTE: Samples from parser check out.
|
||||||
pcr_controls = [control for control in parser.controls]
|
pcr_controls = [control for control in parser.controls]
|
||||||
self.save(original=False)
|
self.save(original=False)
|
||||||
for sample in self.samples:
|
for assoc in self.submission_sample_associations:
|
||||||
|
logger.debug(f"Checking pcr_samples for {assoc.sample.rsl_number}, {assoc.sample.ww_full_sample_id} at "
|
||||||
|
f"column {assoc.column} and row {assoc.row}")
|
||||||
|
# NOTE: Associations of interest do exist in the submission, are not being found below
|
||||||
|
# Okay, I've found the problem, at last, the problem is that only one RSL number is saved for each sample,
|
||||||
|
# Even though each instance of say "25-YUL13-PU3-0320" has multiple RSL numbers in the excel sheet.
|
||||||
|
# so, yeah, the submitters need to make sure that there are unique values for each one.
|
||||||
try:
|
try:
|
||||||
# NOTE: Fix for ENs which have no rsl_number...
|
sample_dict = next(item for item in pcr_samples if item['sample'] == assoc.sample.rsl_number
|
||||||
sample_dict = next(item for item in pcr_samples if item['sample'] == sample.rsl_number)
|
and item['row'] == assoc.row and item['column'] == assoc.column)
|
||||||
|
logger.debug(
|
||||||
|
f"Found sample {sample_dict} at index {pcr_samples.index(sample_dict)}: {pcr_samples[pcr_samples.index(sample_dict)]}")
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
|
logger.error(f"Couldn't find {assoc} in the Parser samples")
|
||||||
continue
|
continue
|
||||||
assoc = self.update_subsampassoc(sample=sample, input_dict=sample_dict)
|
logger.debug(f"Length of pcr_samples: {len(pcr_samples)}")
|
||||||
|
assoc = self.update_subsampassoc(assoc=assoc, input_dict=sample_dict)
|
||||||
result = assoc.save()
|
result = assoc.save()
|
||||||
report.add_result(result)
|
if result:
|
||||||
|
report.add_result(result)
|
||||||
|
# for sample in self.samples:
|
||||||
|
# logger.debug(f"Checking pcr_samples for {sample.rsl_number}, {sample.ww_full_sample_id}")
|
||||||
|
# try:
|
||||||
|
# # NOTE: Fix for ENs which have no rsl_number...
|
||||||
|
# sample_dict = next(item for item in pcr_samples if item['sample'] == sample.rsl_number)
|
||||||
|
# logger.debug(f"Found sample {sample_dict} at index {pcr_samples.index(sample_dict)}: {pcr_samples[pcr_samples.index(sample_dict)]}")
|
||||||
|
# except StopIteration:
|
||||||
|
# logger.error(f"Couldn't find {sample} in the Parser samples")
|
||||||
|
# continue
|
||||||
|
# assoc = self.update_subsampassoc(sample=sample, input_dict=sample_dict)
|
||||||
|
# result = assoc.save()
|
||||||
|
# report.add_result(result)
|
||||||
controltype = ControlType.query(name="PCR Control")
|
controltype = ControlType.query(name="PCR Control")
|
||||||
submitted_date = datetime.strptime(" ".join(parser.pcr_info['run_start_date/time'].split(" ")[:-1]),
|
submitted_date = datetime.strptime(" ".join(parser.pcr_info['run_start_date/time'].split(" ")[:-1]),
|
||||||
"%Y-%m-%d %I:%M:%S %p")
|
"%Y-%m-%d %I:%M:%S %p")
|
||||||
@@ -1804,7 +1880,7 @@ class Wastewater(BasicSubmission):
|
|||||||
new_control.save()
|
new_control.save()
|
||||||
return report
|
return report
|
||||||
|
|
||||||
def update_subsampassoc(self, sample: BasicSample, input_dict: dict) -> SubmissionSampleAssociation:
|
def update_subsampassoc(self, assoc: SubmissionSampleAssociation, input_dict: dict) -> SubmissionSampleAssociation:
|
||||||
"""
|
"""
|
||||||
Updates a joined submission sample association by assigning ct values to n1 or n2 based on alphabetical sorting.
|
Updates a joined submission sample association by assigning ct values to n1 or n2 based on alphabetical sorting.
|
||||||
|
|
||||||
@@ -1815,13 +1891,22 @@ class Wastewater(BasicSubmission):
|
|||||||
Returns:
|
Returns:
|
||||||
SubmissionSampleAssociation: Updated association
|
SubmissionSampleAssociation: Updated association
|
||||||
"""
|
"""
|
||||||
assoc = super().update_subsampassoc(sample=sample, input_dict=input_dict)
|
# logger.debug(f"Input dict: {pformat(input_dict)}")
|
||||||
targets = {k: input_dict[k] for k in sorted(input_dict.keys()) if k.startswith("ct_")}
|
#
|
||||||
assert 0 < len(targets) <= 2
|
assoc = super().update_subsampassoc(assoc=assoc, input_dict=input_dict)
|
||||||
for i, v in enumerate(targets.values(), start=1):
|
# targets = {k: input_dict[k] for k in sorted(input_dict.keys()) if k.startswith("ct_")}
|
||||||
update_key = f"ct_n{i}"
|
# assert 0 < len(targets) <= 2
|
||||||
if getattr(assoc, update_key) is None:
|
# for k, v in targets.items():
|
||||||
setattr(assoc, update_key, v)
|
# # logger.debug(f"Setting sample {sample} with key {k} to value {v}")
|
||||||
|
# # update_key = f"ct_n{i}"
|
||||||
|
# current_value = getattr(assoc, k)
|
||||||
|
# logger.debug(f"Current value came back as: {current_value}")
|
||||||
|
# if current_value is None:
|
||||||
|
# setattr(assoc, k, v)
|
||||||
|
# else:
|
||||||
|
# logger.debug(f"Have a value already, {current_value}... skipping.")
|
||||||
|
if assoc.column == 3:
|
||||||
|
logger.debug(f"Final association for association {assoc}:\n{pformat(assoc.__dict__)}")
|
||||||
return assoc
|
return assoc
|
||||||
|
|
||||||
|
|
||||||
@@ -2105,14 +2190,20 @@ class WastewaterArtic(BasicSubmission):
|
|||||||
Returns:
|
Returns:
|
||||||
str: output name
|
str: output name
|
||||||
"""
|
"""
|
||||||
|
logger.debug(f"PBS adapter on {input_str}")
|
||||||
# NOTE: Remove letters.
|
# NOTE: Remove letters.
|
||||||
processed = input_str.replace("RSL", "")
|
processed = input_str.replace("RSL", "")
|
||||||
|
# logger.debug(processed)
|
||||||
# NOTE: Remove brackets at end
|
# NOTE: Remove brackets at end
|
||||||
processed = re.sub(r"\(.*\)$", "", processed).strip()
|
processed = re.sub(r"\(.*\)$", "", processed).strip()
|
||||||
|
logger.debug(processed)
|
||||||
|
processed = re.sub(r"-RPT", "", processed, flags=re.IGNORECASE)
|
||||||
# NOTE: Remove any non-R letters at end.
|
# NOTE: Remove any non-R letters at end.
|
||||||
processed = re.sub(r"[A-QS-Z]+\d*", "", processed)
|
processed = re.sub(r"[A-QS-Z]+\d*", "", processed)
|
||||||
|
logger.debug(processed)
|
||||||
# NOTE: Remove trailing '-' if any
|
# NOTE: Remove trailing '-' if any
|
||||||
processed = processed.strip("-")
|
processed = processed.strip("-")
|
||||||
|
logger.debug(processed)
|
||||||
try:
|
try:
|
||||||
plate_num = re.search(r"\-\d{1}R?\d?$", processed).group()
|
plate_num = re.search(r"\-\d{1}R?\d?$", processed).group()
|
||||||
processed = rreplace(processed, plate_num, "")
|
processed = rreplace(processed, plate_num, "")
|
||||||
@@ -2126,16 +2217,24 @@ class WastewaterArtic(BasicSubmission):
|
|||||||
repeat_num = None
|
repeat_num = None
|
||||||
if repeat_num is None and "R" in plate_num:
|
if repeat_num is None and "R" in plate_num:
|
||||||
repeat_num = "1"
|
repeat_num = "1"
|
||||||
plate_num = re.sub(r"R", rf"R{repeat_num}", plate_num)
|
try:
|
||||||
|
plate_num = re.sub(r"R", rf"R{repeat_num}", plate_num)
|
||||||
|
except AttributeError:
|
||||||
|
logger.error(f"Problem re-evaluating plate number for {processed}")
|
||||||
|
logger.debug(processed)
|
||||||
# NOTE: Remove any redundant -digits
|
# NOTE: Remove any redundant -digits
|
||||||
processed = re.sub(r"-\d$", "", processed)
|
processed = re.sub(r"-\d$", "", processed)
|
||||||
|
logger.debug(processed)
|
||||||
day = re.search(r"\d{2}$", processed).group()
|
day = re.search(r"\d{2}$", processed).group()
|
||||||
processed = rreplace(processed, day, "")
|
processed = rreplace(processed, day, "")
|
||||||
|
logger.debug(processed)
|
||||||
month = re.search(r"\d{2}$", processed).group()
|
month = re.search(r"\d{2}$", processed).group()
|
||||||
processed = rreplace(processed, month, "")
|
processed = rreplace(processed, month, "")
|
||||||
processed = processed.replace("--", "")
|
processed = processed.replace("--", "")
|
||||||
|
logger.debug(processed)
|
||||||
year = re.search(r'^(?:\d{2})?\d{2}', processed).group()
|
year = re.search(r'^(?:\d{2})?\d{2}', processed).group()
|
||||||
year = f"20{year}"
|
year = f"20{year}"
|
||||||
|
logger.debug(processed)
|
||||||
final_en_name = f"PBS{year}{month}{day}-{plate_num}"
|
final_en_name = f"PBS{year}{month}{day}-{plate_num}"
|
||||||
return final_en_name
|
return final_en_name
|
||||||
|
|
||||||
|
|||||||
@@ -196,20 +196,22 @@ class TurnaroundMaker(ReportArchetype):
|
|||||||
|
|
||||||
class ConcentrationMaker(ReportArchetype):
|
class ConcentrationMaker(ReportArchetype):
|
||||||
|
|
||||||
def __init__(self, start_date: date, end_date: date, submission_type: str = "Bacterial Culture"):
|
def __init__(self, start_date: date, end_date: date, submission_type: str = "Bacterial Culture",
|
||||||
|
controls_only: bool = True):
|
||||||
self.start_date = start_date
|
self.start_date = start_date
|
||||||
self.end_date = end_date
|
self.end_date = end_date
|
||||||
# NOTE: Set page size to zero to override limiting query size.
|
# NOTE: Set page size to zero to override limiting query size.
|
||||||
self.subs = BasicSubmission.query(start_date=start_date, end_date=end_date,
|
self.subs = BasicSubmission.query(start_date=start_date, end_date=end_date,
|
||||||
submission_type_name=submission_type, page_size=0)
|
submission_type_name=submission_type, page_size=0)
|
||||||
# self.known_controls = list(itertools.chain.from_iterable([sub.controls for sub in self.subs]))
|
# self.known_controls = list(itertools.chain.from_iterable([sub.controls for sub in self.subs]))
|
||||||
self.controls = list(itertools.chain.from_iterable([sub.get_provisional_controls() for sub in self.subs]))
|
self.samples = list(itertools.chain.from_iterable([sub.get_provisional_controls(controls_only=controls_only) for sub in self.subs]))
|
||||||
self.records = [self.build_record(control) for control in self.controls]
|
self.records = [self.build_record(sample) for sample in self.samples]
|
||||||
self.df = DataFrame.from_records(self.records)
|
self.df = DataFrame.from_records(self.records)
|
||||||
self.sheet_name = "Concentration"
|
self.sheet_name = "Concentration"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def build_record(cls, control) -> dict:
|
def build_record(cls, control) -> dict:
|
||||||
|
|
||||||
positive = not control.submitter_id.lower().startswith("en")
|
positive = not control.submitter_id.lower().startswith("en")
|
||||||
try:
|
try:
|
||||||
concentration = float(control.concentration)
|
concentration = float(control.concentration)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'''
|
"""
|
||||||
Contains all validators
|
Contains all validators
|
||||||
'''
|
"""
|
||||||
import logging, re
|
import logging, re
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|||||||
@@ -366,7 +366,10 @@ class OmniKitTypeReagentRoleAssociation(BaseOmni):
|
|||||||
logger.debug(f"KitTypeReagentRoleAssociation coming out of query_or_create: {instance.__dict__}\nnew: {new}")
|
logger.debug(f"KitTypeReagentRoleAssociation coming out of query_or_create: {instance.__dict__}\nnew: {new}")
|
||||||
if new:
|
if new:
|
||||||
logger.warning(f"This is a new instance: {instance.__dict__}")
|
logger.warning(f"This is a new instance: {instance.__dict__}")
|
||||||
reagent_role = self.reagent_role.to_sql()
|
try:
|
||||||
|
reagent_role = self.reagent_role.to_sql()
|
||||||
|
except AttributeError:
|
||||||
|
reagent_role = ReagentRole.query(name=self.reagent_role)
|
||||||
instance.reagent_role = reagent_role
|
instance.reagent_role = reagent_role
|
||||||
logger.debug(f"KTRRAssoc uses: {self.uses}")
|
logger.debug(f"KTRRAssoc uses: {self.uses}")
|
||||||
instance.uses = self.uses
|
instance.uses = self.uses
|
||||||
@@ -509,15 +512,24 @@ class OmniProcess(BaseOmni):
|
|||||||
def to_sql(self):
|
def to_sql(self):
|
||||||
instance, new = self.class_object.query_or_create(name=self.name)
|
instance, new = self.class_object.query_or_create(name=self.name)
|
||||||
for st in self.submission_types:
|
for st in self.submission_types:
|
||||||
new_assoc = st.to_sql()
|
try:
|
||||||
|
new_assoc = st.to_sql()
|
||||||
|
except AttributeError:
|
||||||
|
new_assoc = SubmissionType.query(name=st)
|
||||||
if new_assoc not in instance.submission_types:
|
if new_assoc not in instance.submission_types:
|
||||||
instance.submission_types.append(new_assoc)
|
instance.submission_types.append(new_assoc)
|
||||||
for er in self.equipment_roles:
|
for er in self.equipment_roles:
|
||||||
new_assoc = er.to_sql()
|
try:
|
||||||
|
new_assoc = er.to_sql()
|
||||||
|
except AttributeError:
|
||||||
|
new_assoc = EquipmentRole.query(name=er)
|
||||||
if new_assoc not in instance.equipment_roles:
|
if new_assoc not in instance.equipment_roles:
|
||||||
instance.equipment_roles.append(new_assoc)
|
instance.equipment_roles.append(new_assoc)
|
||||||
for tr in self.tip_roles:
|
for tr in self.tip_roles:
|
||||||
new_assoc = tr.to_sql()
|
try:
|
||||||
|
new_assoc = tr.to_sql()
|
||||||
|
except AttributeError:
|
||||||
|
new_assoc = TipRole.query(name=tr)
|
||||||
if new_assoc not in instance.tip_roles:
|
if new_assoc not in instance.tip_roles:
|
||||||
instance.tip_roles.append(new_assoc)
|
instance.tip_roles.append(new_assoc)
|
||||||
return instance
|
return instance
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Pane showing BC control concentrations summary.
|
Pane showing BC control concentrations summary.
|
||||||
"""
|
"""
|
||||||
from PyQt6.QtWidgets import QWidget, QPushButton
|
from PyQt6.QtWidgets import QWidget, QPushButton, QCheckBox, QLabel
|
||||||
from .info_tab import InfoPane
|
from .info_tab import InfoPane
|
||||||
from backend.excel.reports import ConcentrationMaker
|
from backend.excel.reports import ConcentrationMaker
|
||||||
from frontend.visualizations.concentrations_chart import ConcentrationsChart
|
from frontend.visualizations.concentrations_chart import ConcentrationsChart
|
||||||
@@ -20,6 +20,12 @@ class Concentrations(InfoPane):
|
|||||||
self.export_button = QPushButton("Save Data", parent=self)
|
self.export_button = QPushButton("Save Data", parent=self)
|
||||||
self.export_button.pressed.connect(self.save_excel)
|
self.export_button.pressed.connect(self.save_excel)
|
||||||
self.layout.addWidget(self.export_button, 0, 3, 1, 1)
|
self.layout.addWidget(self.export_button, 0, 3, 1, 1)
|
||||||
|
check_label = QLabel("Controls Only")
|
||||||
|
self.all_box = QCheckBox()
|
||||||
|
self.all_box.setChecked(True)
|
||||||
|
self.all_box.checkStateChanged.connect(self.update_data)
|
||||||
|
self.layout.addWidget(check_label, 1, 0, 1, 1)
|
||||||
|
self.layout.addWidget(self.all_box, 1, 1, 1, 1)
|
||||||
self.fig = None
|
self.fig = None
|
||||||
self.report_object = None
|
self.report_object = None
|
||||||
self.update_data()
|
self.update_data()
|
||||||
@@ -33,7 +39,8 @@ class Concentrations(InfoPane):
|
|||||||
"""
|
"""
|
||||||
super().update_data()
|
super().update_data()
|
||||||
months = self.diff_month(self.start_date, self.end_date)
|
months = self.diff_month(self.start_date, self.end_date)
|
||||||
chart_settings = dict(start_date=self.start_date, end_date=self.end_date)
|
# logger.debug(f"Box checked: {self.all_box.isChecked()}")
|
||||||
|
chart_settings = dict(start_date=self.start_date, end_date=self.end_date, controls_only=self.all_box.isChecked())
|
||||||
self.report_obj = ConcentrationMaker(**chart_settings)
|
self.report_obj = ConcentrationMaker(**chart_settings)
|
||||||
self.fig = ConcentrationsChart(df=self.report_obj.df, settings=chart_settings, modes=[], months=months)
|
self.fig = ConcentrationsChart(df=self.report_obj.df, settings=chart_settings, modes=[], months=months)
|
||||||
self.webview.setHtml(self.fig.html)
|
self.webview.setHtml(self.fig.html)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List
|
from typing import List, Generator
|
||||||
|
|
||||||
from PyQt6.QtCore import Qt, pyqtSlot
|
from PyQt6.QtCore import Qt, pyqtSlot
|
||||||
from PyQt6.QtWebChannel import QWebChannel
|
from PyQt6.QtWebChannel import QWebChannel
|
||||||
@@ -37,7 +37,7 @@ class SampleChecker(QDialog):
|
|||||||
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()
|
||||||
html = template.render(samples=pyd.sample_list, css=css)
|
html = template.render(samples=self.formatted_list, css=css)
|
||||||
self.webview.setHtml(html)
|
self.webview.setHtml(html)
|
||||||
QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
|
QBtn = QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
|
||||||
self.buttonBox = QDialogButtonBox(QBtn)
|
self.buttonBox = QDialogButtonBox(QBtn)
|
||||||
@@ -59,8 +59,19 @@ class SampleChecker(QDialog):
|
|||||||
item = next((sample for sample in self.pyd.samples if int(submission_rank) in sample.submission_rank))
|
item = next((sample for sample in self.pyd.samples if int(submission_rank) in sample.submission_rank))
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
logger.error(f"Unable to find sample {submission_rank}")
|
logger.error(f"Unable to find sample {submission_rank}")
|
||||||
|
return
|
||||||
item.__setattr__(key, value)
|
item.__setattr__(key, value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def formatted_list(self) -> List[dict]:
|
||||||
|
output = []
|
||||||
|
for sample in self.pyd.sample_list:
|
||||||
|
if sample['submitter_id'] in [item['submitter_id'] for item in output]:
|
||||||
|
sample['color'] = "red"
|
||||||
|
else:
|
||||||
|
sample['color'] = "black"
|
||||||
|
output.append(sample)
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ class SubmissionFormContainer(QWidget):
|
|||||||
# logger.debug(f"Samples: {pformat(self.pyd.samples)}")
|
# logger.debug(f"Samples: {pformat(self.pyd.samples)}")
|
||||||
checker = SampleChecker(self, "Sample Checker", self.pyd)
|
checker = SampleChecker(self, "Sample Checker", self.pyd)
|
||||||
if checker.exec():
|
if checker.exec():
|
||||||
logger.debug(pformat(self.pyd.samples))
|
# logger.debug(pformat(self.pyd.samples))
|
||||||
self.form = self.pyd.to_form(parent=self)
|
self.form = self.pyd.to_form(parent=self)
|
||||||
self.layout().addWidget(self.form)
|
self.layout().addWidget(self.form)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
  Submitter ID              Row           Column<br/>
|
  Submitter ID              Row           Column<br/>
|
||||||
{% for sample in samples %}
|
{% for sample in samples %}
|
||||||
{{ '%02d' % sample['submission_rank'] }}
|
{{ '%02d' % sample['submission_rank'] }}
|
||||||
<input type="text" id="{{ sample['submission_rank'] }}_id" name="submitter_id" value="{{ sample['submitter_id'] }}" size="40">
|
<input type="text" id="{{ sample['submission_rank'] }}_id" name="submitter_id" value="{{ sample['submitter_id'] }}" size="40" style="color:{{ sample['color'] }};">>
|
||||||
<input type="number" id="{{ sample['submission_rank'] }}_row" name="row" value="{{ sample['row'] }}" size="5", min="1">
|
<input type="number" id="{{ sample['submission_rank'] }}_row" name="row" value="{{ sample['row'] }}" size="5", min="1">
|
||||||
<input type="number" id="{{ sample['submission_rank'] }}_col" name="column" value="{{ sample['column'] }}" size="5", min="1">
|
<input type="number" id="{{ sample['submission_rank'] }}_col" name="column" value="{{ sample['column'] }}" size="5", min="1">
|
||||||
<br/>
|
<br/>
|
||||||
|
|||||||
Reference in New Issue
Block a user