diff --git a/TODO.md b/TODO.md index 4595830..674a6f9 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,4 @@ +- [x] Fix cropping of gel image. - [ ] Create Tips ... *sigh*. - [x] Create platemap image from html for export to pdf. - [x] Move plate map maker to submission. diff --git a/requirements.txt b/requirements.txt index 8c6ed38..d5a61cf 100644 Binary files a/requirements.txt and b/requirements.txt differ diff --git a/src/submissions/backend/db/models/submissions.py b/src/submissions/backend/db/models/submissions.py index b916bb4..cfac895 100644 --- a/src/submissions/backend/db/models/submissions.py +++ b/src/submissions/backend/db/models/submissions.py @@ -9,7 +9,7 @@ from tempfile import TemporaryDirectory from reportlab.graphics.barcode import createBarcodeImageInMemory from reportlab.graphics.shapes import Drawing from reportlab.lib.units import mm -from operator import attrgetter +from operator import attrgetter, itemgetter from pprint import pformat from . import Reagent, SubmissionType, KitType, Organization from sqlalchemy import Column, String, TIMESTAMP, INTEGER, ForeignKey, JSON, FLOAT, case @@ -1349,11 +1349,13 @@ class WastewaterArtic(BasicSubmission): Returns: dict: Updated sample dictionary """ + from backend.validators import RSLNamer input_dict = super().parse_info(input_dict) df = xl.parse("First Strand List", header=None) plates = [] for row in [8,9,10]: - plates.append(dict(plate=df.iat[row-1, 2], start_sample=df.iat[row-1, 3])) + plate_name = RSLNamer(df.iat[row-1, 2]).parsed_name + plates.append(dict(plate=plate_name, start_sample=df.iat[row-1, 3])) input_dict['source_plates'] = plates return input_dict @@ -1373,6 +1375,16 @@ class WastewaterArtic(BasicSubmission): # Because generate_sample_object needs the submitter_id and the artic has the "({origin well})" # at the end, this has to be done here. No moving to sqlalchemy object :( input_dict['submitter_id'] = re.sub(r"\s\(.+\)\s?$", "", str(input_dict['submitter_id'])).strip() + try: + input_dict['ww_processing_num'] = input_dict['sample_name_(lims)'] + del input_dict['sample_name_(lims)'] + except KeyError: + logger.error(f"Unable to set ww_processing_num for sample {input_dict['submitter_id']}") + try: + input_dict['ww_full_sample_id'] = input_dict['sample_name_(ww)'] + del input_dict['sample_name_(ww)'] + except KeyError: + logger.error(f"Unable to set ww_processing_num for sample {input_dict['submitter_id']}") if "ENC" in input_dict['submitter_id']: input_dict['submitter_id'] = cls.en_adapter(input_str=input_dict['submitter_id']) return input_dict @@ -1642,6 +1654,7 @@ class WastewaterArtic(BasicSubmission): output.append(dicto) # else: # output = super().adjust_to_dict_samples(backup=False) + return output def custom_context_events(self) -> dict: @@ -1751,7 +1764,7 @@ class BasicSample(BaseClass): sample['Submitter ID'] = self.submitter_id sample['Sample Type'] = self.sample_type if full_data: - sample['submissions'] = [item.to_sub_dict() for item in self.sample_submission_associations] + sample['submissions'] = sorted([item.to_sub_dict() for item in self.sample_submission_associations], key=itemgetter('submitted_date')) return sample def set_attribute(self, name:str, value): @@ -1857,19 +1870,6 @@ class BasicSample(BaseClass): template = env.get_template("basicsample_details.html") return base_dict, template - def show_details(self, obj): - """ - Creates Widget for showing sample details. - - Args: - obj (_type_): parent widget - """ - logger.debug("Hello from details") - from frontend.widgets.sample_details import SampleDetails - dlg = SampleDetails(parent=obj, samp=self) - if dlg.exec(): - pass - @classmethod @setup_lookup def query(cls, @@ -2126,6 +2126,7 @@ class SubmissionSampleAssociation(BaseClass): sample['Well'] = None sample['Plate Name'] = self.submission.rsl_plate_num sample['Positive'] = False + sample['submitted_date'] = self.submission.submitted_date return sample def to_hitpick(self) -> dict|None: diff --git a/src/submissions/frontend/widgets/gel_checker.py b/src/submissions/frontend/widgets/gel_checker.py index fb64851..724b392 100644 --- a/src/submissions/frontend/widgets/gel_checker.py +++ b/src/submissions/frontend/widgets/gel_checker.py @@ -44,9 +44,11 @@ class GelBox(QDialog): pg.setConfigOptions(antialias=True) # creating image view object self.imv = pg.ImageView() - img = np.array(Image.open(self.img_path).rotate(-90).transpose(Image.FLIP_LEFT_RIGHT)) - self.imv.setImage(img, scale=None)#, xvals=np.linspace(1., 3., data.shape[0])) - + # Create image. + # For some reason, ImageView wants to flip the image, so we have to rotate and flip the array first. + # Using the Image.rotate function results in cropped image. + img = np.flip(np.rot90(np.array(Image.open(self.img_path)),1),0) + self.imv.setImage(img) layout = QGridLayout() layout.addWidget(QLabel("DNA Core Submission Number"),0,1) self.core_number = QLineEdit() diff --git a/src/submissions/frontend/widgets/submission_details.py b/src/submissions/frontend/widgets/submission_details.py index 441e59c..c4ac06c 100644 --- a/src/submissions/frontend/widgets/submission_details.py +++ b/src/submissions/frontend/widgets/submission_details.py @@ -37,21 +37,21 @@ class SubmissionDetails(QDialog): # create scrollable interior interior = QScrollArea() interior.setParent(self) - self.base_dict = sub.to_dict(full_data=True) - logger.debug(f"Submission details data:\n{pformat({k:v for k,v in self.base_dict.items() if k != 'samples'})}") - # don't want id - del self.base_dict['id'] - logger.debug(f"Creating barcode.") - if not check_if_app(): - self.base_dict['barcode'] = base64.b64encode(sub.make_plate_barcode(width=120, height=30)).decode('utf-8') - logger.debug(f"Making platemap...") - self.base_dict['platemap'] = sub.make_plate_map() - self.base_dict, self.template = sub.get_details_template(base_dict=self.base_dict) - self.html = self.template.render(sub=self.base_dict) + # self.base_dict = sub.to_dict(full_data=True) + # logger.debug(f"Submission details data:\n{pformat({k:v for k,v in self.base_dict.items() if k != 'samples'})}") + # # don't want id + # del self.base_dict['id'] + # logger.debug(f"Creating barcode.") + # if not check_if_app(): + # self.base_dict['barcode'] = base64.b64encode(sub.make_plate_barcode(width=120, height=30)).decode('utf-8') + # logger.debug(f"Making platemap...") + # self.base_dict['platemap'] = sub.make_plate_map() + # self.base_dict, self.template = sub.get_details_template(base_dict=self.base_dict) + # self.html = self.template.render(sub=self.base_dict) self.webview = QWebEngineView(parent=self) self.webview.setMinimumSize(900, 500) self.webview.setMaximumSize(900, 500) - self.webview.setHtml(self.html) + # self.webview.setHtml(self.html) self.layout = QVBoxLayout() interior.resize(900, 500) interior.setWidget(self.webview) @@ -64,18 +64,46 @@ class SubmissionDetails(QDialog): # setup channel self.channel = QWebChannel() self.channel.registerObject('backend', self) + self.submission_details(submission=sub) self.webview.page().setWebChannel(self.channel) @pyqtSlot(str) - def sample_details(self, sample): - # print(f"{string} is in row {row}, column {column}") - # self.webview.setHtml(f"

{sample}") + def sample_details(self, sample:str): + """ + Changes details view to summary of Sample + + Args: + sample (str): Submitter Id of the sample. + """ sample = BasicSample.query(submitter_id=sample) base_dict = sample.to_sub_dict(full_data=True) base_dict, template = sample.get_details_template(base_dict=base_dict) html = template.render(sample=base_dict) self.webview.setHtml(html) - # sample.show_details(obj=self) + + @pyqtSlot(str) + def submission_details(self, submission:str|BasicSubmission): + """ + Sets details view to summary of Submission. + + Args: + submission (str | BasicSubmission): Submission of interest. + """ + logger.debug(f"Details for: {submission}") + if isinstance(submission, str): + submission = BasicSubmission.query(rsl_number=submission) + self.base_dict = submission.to_dict(full_data=True) + logger.debug(f"Submission details data:\n{pformat({k:v for k,v in self.base_dict.items() if k != 'samples'})}") + # don't want id + del self.base_dict['id'] + logger.debug(f"Creating barcode.") + if not check_if_app(): + self.base_dict['barcode'] = base64.b64encode(submission.make_plate_barcode(width=120, height=30)).decode('utf-8') + logger.debug(f"Making platemap...") + self.base_dict['platemap'] = submission.make_plate_map() + self.base_dict, self.template = submission.get_details_template(base_dict=self.base_dict) + self.html = self.template.render(sub=self.base_dict) + self.webview.setHtml(self.html) def export(self): """ diff --git a/src/submissions/frontend/widgets/submission_widget.py b/src/submissions/frontend/widgets/submission_widget.py index 3a234ca..54016b9 100644 --- a/src/submissions/frontend/widgets/submission_widget.py +++ b/src/submissions/frontend/widgets/submission_widget.py @@ -395,7 +395,7 @@ class SubmissionFormWidget(QWidget): def __init__(self, parent: QWidget, **kwargs) -> None: super().__init__(parent) - self.ignore = ['filepath', 'samples', 'reagents', 'csv', 'ctx', 'comment', 'equipment'] + self.ignore = ['filepath', 'samples', 'reagents', 'csv', 'ctx', 'comment', 'equipment', 'source_plates'] self.recover = ['filepath', 'samples', 'csv', 'comment', 'equipment'] layout = QVBoxLayout() for k, v in kwargs.items(): diff --git a/src/submissions/templates/basicsample_details.html b/src/submissions/templates/basicsample_details.html index b12a3fa..a917286 100644 --- a/src/submissions/templates/basicsample_details.html +++ b/src/submissions/templates/basicsample_details.html @@ -46,9 +46,20 @@ {% endfor %}

{% if sample['submissions'] %}

Submissions:

{% for submission in sample['submissions'] %} -

{{ submission['Plate Name'] }}: {{ submission['Well'] }}

+

{{ submission['Plate Name'] }}: {{ submission['Well'] }}

{% endfor %} {% endif %} {% endblock %} +