From b466cb61d2ce7cfaa4c81b20e7e37cb704ef1683 Mon Sep 17 00:00:00 2001 From: Landon Wark Date: Wed, 6 Mar 2024 15:04:43 -0600 Subject: [PATCH] Improved previous sub finding. --- CHANGELOG.md | 4 + alembic.ini | 6 +- ...dding_source_plates_to_artic_submission.py | 48 +++++ src/submissions/__init__.py | 2 +- .../backend/db/models/submissions.py | 190 ++++++++++++------ src/submissions/backend/validators/pydant.py | 1 + .../frontend/widgets/gel_checker.py | 19 +- .../frontend/widgets/submission_details.py | 35 +++- .../frontend/widgets/submission_table.py | 1 + .../templates/basicsample_details.html | 54 +++++ .../templates/basicsubmission_details.html | 14 +- src/submissions/templates/plate_map.html | 12 +- src/submissions/templates/tooltip.html | 8 +- src/submissions/tools.py | 66 +++--- 14 files changed, 343 insertions(+), 117 deletions(-) create mode 100644 alembic/versions/fabf697c721d_adding_source_plates_to_artic_submission.py create mode 100644 src/submissions/templates/basicsample_details.html diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d3e3a7..21fb47c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 202402.04 + +- Addition of comments to gel box. + ## 202402.01 - Addition of gel box for Artic quality control. diff --git a/alembic.ini b/alembic.ini index f157eb0..6e04d96 100644 --- a/alembic.ini +++ b/alembic.ini @@ -55,9 +55,9 @@ version_path_separator = os # Use os.pathsep. Default configuration used for ne # are written from script.py.mako # output_encoding = utf-8 -sqlalchemy.url = sqlite:///L:\Robotics Laboratory Support\Submissions\submissions.db -; sqlalchemy.url = sqlite:///C:\Users\lwark\Documents\Archives\Submissions_app_backups\DB_backups\submissions-new.db -; sqlalchemy.url = sqlite:///C:\Users\lwark\Documents\python\submissions\tests\test_assets\submissions-test.db +; sqlalchemy.url = sqlite:///L:\Robotics Laboratory Support\Submissions\submissions.db +; sqlalchemy.url = sqlite:///C:\Users\lwark\Documents\Archives\Submissions_app_backups\DB_backups\submissions-demo.db +sqlalchemy.url = sqlite:///C:\Users\lwark\Documents\python\submissions\tests\test_assets\submissions-test.db [post_write_hooks] diff --git a/alembic/versions/fabf697c721d_adding_source_plates_to_artic_submission.py b/alembic/versions/fabf697c721d_adding_source_plates_to_artic_submission.py new file mode 100644 index 0000000..c52320c --- /dev/null +++ b/alembic/versions/fabf697c721d_adding_source_plates_to_artic_submission.py @@ -0,0 +1,48 @@ +"""adding source plates to Artic submission + +Revision ID: fabf697c721d +Revises: 70426df72f80 +Create Date: 2024-03-06 11:01:34.794411 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'fabf697c721d' +down_revision = '70426df72f80' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + # with op.batch_alter_table('_submissionsampleassociation', schema=None) as batch_op: + # batch_op.create_unique_constraint("ssa_unique", ['id']) + + with op.batch_alter_table('_wastewaterartic', schema=None) as batch_op: + batch_op.add_column(sa.Column('source_plates', sa.JSON(), nullable=True)) + + with op.batch_alter_table('_wastewaterassociation', schema=None) as batch_op: + batch_op.alter_column('id', + existing_type=sa.INTEGER(), + nullable=False) + + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('_wastewaterassociation', schema=None) as batch_op: + batch_op.alter_column('id', + existing_type=sa.INTEGER(), + nullable=True) + + with op.batch_alter_table('_wastewaterartic', schema=None) as batch_op: + batch_op.drop_column('source_plates') + + # with op.batch_alter_table('_submissionsampleassociation', schema=None) as batch_op: + # batch_op.drop_constraint("ssa_unique", type_='unique') + + # ### end Alembic commands ### diff --git a/src/submissions/__init__.py b/src/submissions/__init__.py index 0fe4d00..7189c9b 100644 --- a/src/submissions/__init__.py +++ b/src/submissions/__init__.py @@ -4,7 +4,7 @@ from pathlib import Path # Version of the realpython-reader package __project__ = "submissions" -__version__ = "202402.4b" +__version__ = "202403.1b" __author__ = {"name":"Landon Wark", "email":"Landon.Wark@phac-aspc.gc.ca"} __copyright__ = "2022-2024, Government of Canada" diff --git a/src/submissions/backend/db/models/submissions.py b/src/submissions/backend/db/models/submissions.py index 5204cb8..b916bb4 100644 --- a/src/submissions/backend/db/models/submissions.py +++ b/src/submissions/backend/db/models/submissions.py @@ -276,7 +276,7 @@ class BasicSubmission(BaseClass): sample_list = self.hitpick_plate() # logger.debug("Setting background colours") for sample in sample_list: - if sample['positive']: + if sample['Positive']: sample['background_color'] = "#f10f07" else: if "colour" in sample.keys(): @@ -288,7 +288,7 @@ class BasicSubmission(BaseClass): for column in range(1, plate_columns+1): for row in range(1, plate_rows+1): try: - well = [item for item in sample_list if item['row'] == row and item['column']==column][0] + well = [item for item in sample_list if item['Row'] == row and item['Column']==column][0] except IndexError: well = dict(name="", row=row, column=column, background_color="#ffffff") output_samples.append(well) @@ -429,7 +429,8 @@ class BasicSubmission(BaseClass): case "reagents": new_dict[key] = [PydReagent(**reagent) for reagent in value] case "samples": - new_dict[key] = [PydSample(**sample) for sample in dicto['samples']] + # samples = {k.lower().replace(" ", "_"):v for k,v in dicto['samples'].items()} + new_dict[key] = [PydSample(**{k.lower().replace(" ", "_"):v for k,v in sample.items()}) for sample in dicto['samples']] case "equipment": try: new_dict[key] = [PydEquipment(**equipment) for equipment in dicto['equipment']] @@ -1293,6 +1294,7 @@ class WastewaterArtic(BasicSubmission): pcr_info = Column(JSON) #: unstructured output from pcr table logger or user(Artic) gel_image = Column(String(64)) #: file name of gel image in zip file gel_info = Column(JSON) #: unstructured data from gel. + source_plates = Column(JSON) #: wastewater plates that samples come from __mapper_args__ = dict(polymorphic_identity="Wastewater Artic", polymorphic_load="inline", @@ -1328,12 +1330,33 @@ class WastewaterArtic(BasicSubmission): output['gel_info'] = self.gel_info output['gel_image'] = self.gel_image output['dna_core_submission_number'] = self.dna_core_submission_number + output['source_plates'] = self.source_plates return output @classmethod def get_abbreviation(cls) -> str: return "AR" + @classmethod + def parse_info(cls, input_dict:dict, xl:pd.ExcelFile|None=None) -> dict: + """ + Update submission dictionary with type specific information + + Args: + input_dict (dict): Input sample dictionary + xl (pd.ExcelFile): original xl workbook, used for child classes mostly + + Returns: + dict: Updated sample dictionary + """ + 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])) + input_dict['source_plates'] = plates + return input_dict + @classmethod def parse_samples(cls, input_dict: dict) -> dict: """ @@ -1364,8 +1387,11 @@ class WastewaterArtic(BasicSubmission): Returns: str: output name - """ + """ + # Remove letters. processed = re.sub(r"[A-Z]", "", input_str) + # Remove trailing '-' if any + processed = processed.strip("-") try: en_num = re.search(r"\-\d{1}$", processed).group() processed = rreplace(processed, en_num, "") @@ -1507,18 +1533,22 @@ class WastewaterArtic(BasicSubmission): worksheet = input_excel["First Strand List"] samples = cls.query(rsl_number=info['rsl_plate_num']['value']).submission_sample_associations samples = sorted(samples, key=attrgetter('column', 'row')) - source_plates = [] - first_samples = [] - for sample in samples: - sample = sample.sample - try: - assoc = [item.submission.rsl_plate_num for item in sample.sample_submission_associations if item.submission.submission_type_name=="Wastewater"][-1] - except IndexError: - logger.error(f"Association not found for {sample}") - continue - if assoc not in source_plates: - source_plates.append(assoc) - first_samples.append(sample.ww_processing_num) + try: + source_plates = [item['plate'] for item in info['source_plates']] + first_samples = [item['start_sample'] for item in info['source_plates']] + except: + source_plates = [] + first_samples = [] + for sample in samples: + sample = sample.sample + try: + assoc = [item.submission.rsl_plate_num for item in sample.sample_submission_associations if item.submission.submission_type_name=="Wastewater"][-1] + except IndexError: + logger.error(f"Association not found for {sample}") + continue + if assoc not in source_plates: + source_plates.append(assoc) + first_samples.append(sample.ww_processing_num) # Pad list to length of 3 source_plates += ['None'] * (3 - len(source_plates)) first_samples += [''] * (3 - len(first_samples)) @@ -1573,7 +1603,7 @@ class WastewaterArtic(BasicSubmission): Tuple[dict, Template]: (Updated dictionary, Template to be rendered) """ base_dict, template = super().get_details_template(base_dict=base_dict) - base_dict['excluded'] += ['gel_info', 'gel_image', 'headers', "dna_core_submission_number"] + base_dict['excluded'] += ['gel_info', 'gel_image', 'headers', "dna_core_submission_number", "source_plates"] base_dict['DNA Core ID'] = base_dict['dna_core_submission_number'] check = 'gel_info' in base_dict.keys() and base_dict['gel_info'] != None if check: @@ -1598,20 +1628,20 @@ class WastewaterArtic(BasicSubmission): List[dict]: Updated dictionaries """ logger.debug(f"Hello from {self.__class__.__name__} dictionary sample adjuster.") - if backup: - output = [] - for assoc in self.submission_sample_associations: - dicto = assoc.to_sub_dict() - old_sub = assoc.sample.get_previous_ww_submission(current_artic_submission=self) - try: - dicto['plate_name'] = old_sub.rsl_plate_num - except AttributeError: - dicto['plate_name'] = "" - old_assoc = WastewaterAssociation.query(submission=old_sub, sample=assoc.sample, limit=1) - dicto['well'] = f"{row_map[old_assoc.row]}{old_assoc.column}" - output.append(dicto) - else: - output = super().adjust_to_dict_samples(backup=False) + # if backup: + output = [] + for assoc in self.submission_sample_associations: + dicto = assoc.to_sub_dict() + old_sub = assoc.sample.get_previous_ww_submission(current_artic_submission=self) + try: + dicto['plate_name'] = old_sub.rsl_plate_num + except AttributeError: + dicto['plate_name'] = "" + old_assoc = WastewaterAssociation.query(submission=old_sub, sample=assoc.sample, limit=1) + dicto['well'] = f"{row_map[old_assoc.row]}{old_assoc.column}" + output.append(dicto) + # else: + # output = super().adjust_to_dict_samples(backup=False) return output def custom_context_events(self) -> dict: @@ -1637,9 +1667,15 @@ class WastewaterArtic(BasicSubmission): fname = select_open_file(obj=obj, file_extension="jpg") dlg = GelBox(parent=obj, img_path=fname) if dlg.exec(): - self.dna_core_submission_number, img_path, output = dlg.parse_form() + self.dna_core_submission_number, img_path, output, comment = dlg.parse_form() self.gel_image = img_path.name self.gel_info = output + dt = datetime.strftime(datetime.now(), "%Y-%m-%d %H:%M:%S") + com = dict(text=comment, name=getuser(), time=dt) + if self.comment is not None: + self.comment.append(com) + else: + self.comment = [com] logger.debug(pformat(self.gel_info)) with ZipFile(self.__directory_path__.joinpath("submission_imgs.zip"), 'a') as zipf: # Add a file located at the source_path to the destination within the zip @@ -1703,7 +1739,7 @@ class BasicSample(BaseClass): except AttributeError: return f" dict: + def to_sub_dict(self, full_data:bool=False) -> dict: """ gui friendly dictionary, extends parent method. @@ -1712,8 +1748,10 @@ class BasicSample(BaseClass): """ # logger.debug(f"Converting {self} to dict.") sample = {} - sample['submitter_id'] = self.submitter_id - sample['sample_type'] = self.sample_type + 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] return sample def set_attribute(self, name:str, value): @@ -1797,6 +1835,41 @@ class BasicSample(BaseClass): """ return input_dict + @classmethod + def get_details_template(cls, base_dict:dict) -> Tuple[dict, Template]: + """ + Get the details jinja template for the correct class + + Args: + base_dict (dict): incoming dictionary of Submission fields + + Returns: + Tuple(dict, Template): (Updated dictionary, Template to be rendered) + """ + base_dict['excluded'] = ['submissions', 'excluded'] + env = jinja_template_loading() + temp_name = f"{cls.__name__.lower()}_details.html" + logger.debug(f"Returning template: {temp_name}") + try: + template = env.get_template(temp_name) + except TemplateNotFound as e: + logger.error(f"Couldn't find template {e}") + 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, @@ -1896,18 +1969,18 @@ class WastewaterSample(BasicSample): polymorphic_load="inline", inherit_condition=(id == BasicSample.id)) - def to_sub_dict(self) -> dict: + def to_sub_dict(self, full_data:bool=False) -> dict: """ gui friendly dictionary, extends parent method. Returns: dict: well location and name (sample id, organism) NOTE: keys must sync with WWSample to_sub_dict above """ - sample = super().to_sub_dict() - sample['ww_processing_num'] = self.ww_processing_num - sample['sample_location'] = self.sample_location - sample['received_date'] = self.received_date - sample['collection_date'] = self.collection_date + sample = super().to_sub_dict(full_data=full_data) + sample['WW Processing Number'] = self.ww_processing_num + sample['Sample Location'] = self.sample_location + sample['Received Date'] = self.received_date + sample['Collection Date'] = self.collection_date return sample @classmethod @@ -1944,9 +2017,14 @@ class WastewaterSample(BasicSample): def get_previous_ww_submission(self, current_artic_submission:WastewaterArtic): # assocs = [assoc for assoc in self.sample_submission_associations if assoc.submission.submission_type_name=="Wastewater"] - subs = self.submissions[:self.submissions.index(current_artic_submission)] - subs = [sub for sub in subs if sub.submission_type_name=="Wastewater"] - logger.debug(f"Submissions up to current artic submission: {subs}") + # subs = self.submissions[:self.submissions.index(current_artic_submission)] + try: + plates = [item['plate'] for item in current_artic_submission.source_plates] + except TypeError as e: + logger.error(f"source_plates must not be present") + plates = [item.rsl_plate_num for item in self.submissions[:self.submissions.index(current_artic_submission)]] + subs = [sub for sub in self.submissions if sub.rsl_plate_num in plates] + logger.debug(f"Submissions: {subs}") try: return subs[-1] except IndexError: @@ -1964,17 +2042,17 @@ class BacterialCultureSample(BasicSample): polymorphic_load="inline", inherit_condition=(id == BasicSample.id)) - def to_sub_dict(self) -> dict: + def to_sub_dict(self, full_data:bool=False) -> dict: """ gui friendly dictionary, extends parent method. Returns: dict: well location and name (sample id, organism) NOTE: keys must sync with WWSample to_sub_dict above """ - sample = super().to_sub_dict() - sample['name'] = self.submitter_id - sample['organism'] = self.organism - sample['concentration'] = self.concentration + sample = super().to_sub_dict(full_data=full_data) + sample['Name'] = self.submitter_id + sample['Organism'] = self.organism + sample['Concentration'] = self.concentration if self.control != None: sample['colour'] = [0,128,0] sample['tooltip'] = f"Control: {self.control.controltype.name} - {self.control.controltype.targets}" @@ -2038,16 +2116,16 @@ class SubmissionSampleAssociation(BaseClass): # Get sample info # logger.debug(f"Running {self.__repr__()}") sample = self.sample.to_sub_dict() - sample['name'] = self.sample.submitter_id - sample['row'] = self.row - sample['column'] = self.column + sample['Name'] = self.sample.submitter_id + sample['Row'] = self.row + sample['Column'] = self.column try: - sample['well'] = f"{row_map[self.row]}{self.column}" + sample['Well'] = f"{row_map[self.row]}{self.column}" except KeyError as e: logger.error(f"Unable to find row {self.row} in row_map.") - sample['well'] = None - sample['plate_name'] = self.submission.rsl_plate_num - sample['positive'] = False + sample['Well'] = None + sample['Plate Name'] = self.submission.rsl_plate_num + sample['Positive'] = False return sample def to_hitpick(self) -> dict|None: diff --git a/src/submissions/backend/validators/pydant.py b/src/submissions/backend/validators/pydant.py index d66a0eb..763fe50 100644 --- a/src/submissions/backend/validators/pydant.py +++ b/src/submissions/backend/validators/pydant.py @@ -225,6 +225,7 @@ class PydEquipment(BaseModel, extra='ignore'): @classmethod def make_empty_list(cls, value): # logger.debug(f"Pydantic value: {value}") + value = convert_nans_to_nones(value) if value == None: value = [''] if len(value)==0: diff --git a/src/submissions/frontend/widgets/gel_checker.py b/src/submissions/frontend/widgets/gel_checker.py index a3b06df..fb64851 100644 --- a/src/submissions/frontend/widgets/gel_checker.py +++ b/src/submissions/frontend/widgets/gel_checker.py @@ -2,7 +2,8 @@ Gel box for artic quality control """ from PyQt6.QtWidgets import (QWidget, QDialog, QGridLayout, - QLabel, QLineEdit, QDialogButtonBox + QLabel, QLineEdit, QDialogButtonBox, + QTextEdit ) import numpy as np import pyqtgraph as pg @@ -44,7 +45,8 @@ class GelBox(QDialog): # 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)#, xvals=np.linspace(1., 3., data.shape[0])) + self.imv.setImage(img, scale=None)#, xvals=np.linspace(1., 3., data.shape[0])) + layout = QGridLayout() layout.addWidget(QLabel("DNA Core Submission Number"),0,1) self.core_number = QLineEdit() @@ -59,7 +61,7 @@ class GelBox(QDialog): self.buttonBox = QDialogButtonBox(QBtn) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) - layout.addWidget(self.buttonBox, 22, 5, 1, 1)#, alignment=Qt.AlignmentFlag.AlignTop) + layout.addWidget(self.buttonBox, 23, 1, 1, 1)#, alignment=Qt.AlignmentFlag.AlignTop) self.setLayout(layout) def parse_form(self) -> Tuple[str, str|Path, list]: @@ -70,14 +72,14 @@ class GelBox(QDialog): Tuple[str, str|Path, list]: output values """ dna_core_submission_number = self.core_number.text() - return dna_core_submission_number, self.img_path, self.form.parse_form() + values, comment = self.form.parse_form() + return dna_core_submission_number, self.img_path, values, comment class ControlsForm(QWidget): def __init__(self, parent) -> None: super().__init__(parent) self.layout = QGridLayout() - columns = [] rows = [] for iii, item in enumerate(["Negative Control Key", "Description", "Results - 65 C", "Results - 63 C", "Results - Spike"]): @@ -98,6 +100,11 @@ class ControlsForm(QWidget): widge.setText("Neg") widge.setObjectName(f"{rows[iii]} : {columns[jjj]}") self.layout.addWidget(widge, iii+1, jjj+2, 1, 1) + self.layout.addWidget(QLabel("Comments:"), 0,5,1,1) + self.comment_field = QTextEdit(self) + self.comment_field.setFixedHeight(50) + self.layout.addWidget(self.comment_field, 1,5,4,1) + self.setLayout(self.layout) def parse_form(self) -> List[dict]: @@ -118,4 +125,4 @@ class ControlsForm(QWidget): if label[0] not in [item['name'] for item in output]: output.append(dicto) logger.debug(pformat(output)) - return output + return output, self.comment_field.toPlainText() diff --git a/src/submissions/frontend/widgets/submission_details.py b/src/submissions/frontend/widgets/submission_details.py index 934aa3a..441e59c 100644 --- a/src/submissions/frontend/widgets/submission_details.py +++ b/src/submissions/frontend/widgets/submission_details.py @@ -1,8 +1,10 @@ from PyQt6.QtWidgets import (QDialog, QScrollArea, QPushButton, QVBoxLayout, QMessageBox, QDialogButtonBox, QTextEdit) from PyQt6.QtWebEngineWidgets import QWebEngineView -from PyQt6.QtCore import Qt -from backend.db.models import BasicSubmission +from PyQt6.QtWebChannel import QWebChannel +from PyQt6.QtCore import Qt, pyqtSlot + +from backend.db.models import BasicSubmission, BasicSample from tools import check_if_app from .functions import select_save_file from io import BytesIO @@ -31,7 +33,7 @@ class SubmissionDetails(QDialog): self.app = parent.parent().parent().parent().parent().parent().parent() except AttributeError: self.app = None - self.setWindowTitle("Submission Details") + self.setWindowTitle(f"Submission Details - {sub.rsl_plate_num}") # create scrollable interior interior = QScrollArea() interior.setParent(self) @@ -46,19 +48,34 @@ class SubmissionDetails(QDialog): 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) - webview = QWebEngineView() - webview.setMinimumSize(900, 500) - webview.setMaximumSize(900, 500) - webview.setHtml(self.html) + self.webview = QWebEngineView(parent=self) + self.webview.setMinimumSize(900, 500) + self.webview.setMaximumSize(900, 500) + self.webview.setHtml(self.html) self.layout = QVBoxLayout() interior.resize(900, 500) - interior.setWidget(webview) + interior.setWidget(self.webview) self.setFixedSize(900, 500) # button to export a pdf version btn = QPushButton("Export PDF") btn.setParent(self) btn.setFixedWidth(900) btn.clicked.connect(self.export) + # setup channel + self.channel = QWebChannel() + self.channel.registerObject('backend', self) + 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}") + 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) def export(self): """ @@ -130,4 +147,4 @@ class SubmissionComment(QDialog): full_comment = [{"name":commenter, "time": dt, "text": comment}] logger.debug(f"Full comment: {full_comment}") return full_comment - \ No newline at end of file + diff --git a/src/submissions/frontend/widgets/submission_table.py b/src/submissions/frontend/widgets/submission_table.py index 9031aa7..26ce7c0 100644 --- a/src/submissions/frontend/widgets/submission_table.py +++ b/src/submissions/frontend/widgets/submission_table.py @@ -103,6 +103,7 @@ class SubmissionsSheet(QTableView): Args: event (_type_): the item of interest """ + # logger.debug(event().__dict__) id = self.selectionModel().currentIndex() id = id.sibling(id.row(),0).data() submission = BasicSubmission.query(id=id) diff --git a/src/submissions/templates/basicsample_details.html b/src/submissions/templates/basicsample_details.html new file mode 100644 index 0000000..b12a3fa --- /dev/null +++ b/src/submissions/templates/basicsample_details.html @@ -0,0 +1,54 @@ + + + + {% block head %} + + Sample Details for {{ sample['Submitter ID'] }} + + {% endblock %} + + + {% block body %} +

Sample Details for {{ sample['Submitter ID'] }}

+

{% for key, value in sample.items() if key not in sample['excluded'] %} +     {{ key }}: {{ value }}
+ {% endfor %}

+ {% if sample['submissions'] %}

Submissions:

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

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

+ {% endfor %} + {% endif %} + {% endblock %} + + diff --git a/src/submissions/templates/basicsubmission_details.html b/src/submissions/templates/basicsubmission_details.html index b484429..4917fd1 100644 --- a/src/submissions/templates/basicsubmission_details.html +++ b/src/submissions/templates/basicsubmission_details.html @@ -35,6 +35,7 @@ } Submission Details for {{ sub['Plate Number'] }} + {% endblock %} @@ -57,7 +58,7 @@ {% if sub['samples'] %}

Samples:

{% for item in sub['samples'] %} -     {{ item['well'] }}: {% if item['organism'] %} {{ item['name'] }} - ({{ item['organism']|replace('\n\t', '
        ') }}){% else %} {{ item['name']|replace('\n\t', '
        ') }}{% endif %}
+     {{ item['Well'] }}: {% if item['Organism'] %} {{ item['Name'] }} - ({{ item['Organism']|replace('\n\t', '
        ') }}){% else %} {{ item['Name']|replace('\n\t', '
        ') }}{% endif %}
{% endfor %}

{% endif %} {% if sub['controls'] %} @@ -116,4 +117,15 @@ {% endif %} {% endblock %} + diff --git a/src/submissions/templates/plate_map.html b/src/submissions/templates/plate_map.html index dd16ab7..5d1dfb3 100644 --- a/src/submissions/templates/plate_map.html +++ b/src/submissions/templates/plate_map.html @@ -1,15 +1,15 @@