post documentation and code clean-up.
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
## 202407.01
|
||||
|
||||
- Better documentation.
|
||||
|
||||
## 202406.04
|
||||
|
||||
- Exported submission details will now be in docx format.
|
||||
|
||||
61
README.md
61
README.md
@@ -9,20 +9,57 @@
|
||||
1. Ensure a properly formatted Submission Excel form has been filled out.
|
||||
a. The program can fill in reagent fields and some other information automatically, but should be checked for accuracy afterwards.
|
||||
2. Click on 'File' in the menu bar, followed by 'Import Submission' and use the file dialog to locate the form.
|
||||
1. The excel file may also be dragged into the grey area on the left hand side of the screen from Windows File Explorer. If so, skip step 3.
|
||||
3. Click 'Ok'.
|
||||
4. Most of the fields in the form should be automatically filled in from the form area to the left of the screen.
|
||||
5. You may need to maximize the app to ensure you can see all the info.
|
||||
6. Any fields that are not automatically filled in can be filled in manually from the drop down menus.
|
||||
a. Any reagent lots not found in the drop downs can be typed in manually.
|
||||
1. Any reagent lots not found in the drop downs can be typed in manually.
|
||||
7. Once you are certain all the information is correct, click 'Submit' at the bottom of the form.
|
||||
8. Add in any new reagents the app doesn't have in the database.
|
||||
9. Once the new run shows up at the bottom of the Submissions, everything is fine.
|
||||
10. In case of any mistakes, the run can be overwritten by a reimport.
|
||||
|
||||
## Adding Equipment to a Run:
|
||||
|
||||
1. Right click on the run in the Submissions Table to access the context menu.
|
||||
2. Click on “Add Equipment”.
|
||||
3. Select equipment used for each equipment role from the drop down menu.
|
||||
1. Any tips associated with a liquid handler will also be available in a separate drop down menu.
|
||||
5. Select (or input) the process used on with the equipment.
|
||||
1. Equipment that is not necessary may disabled using the check boxes to the left of each entry.
|
||||
|
||||
## Importing PCR results (Wastewater only):
|
||||
|
||||
This is meant to import .xslx files created from the Design & Analysis Software
|
||||
1. Click on 'File' -> 'Import PCR Results'.
|
||||
2. Use the file dialog to locate the .xlsx file you want to import.
|
||||
3. Click 'Okay'.
|
||||
|
||||
## Using the Gel Box (Wastewater Artic only):
|
||||
|
||||
1. Right click on the run in the Submissions Table to access the context menu.
|
||||
2. Click on “Gel Box”.
|
||||
3. Choose the .jpg file exported from the Egel reader.
|
||||
4. Click “Okay”.
|
||||
5. If none exists, eEnter the DNA Core Submission Number and gel barcode at the top of the window.
|
||||
6. Use the histogram slide on the right side of the window to adjust the image contrast.
|
||||
7. Use the mouse scroll to zoom in on relevant areas of the image.
|
||||
8. Enter the control status in the grid at the bottom of the window.
|
||||
9. Add any relevant comments.
|
||||
10. Click “Okay”.
|
||||
|
||||
## Check existing Run:
|
||||
|
||||
1. Details of existing runs can be checked by double clicking on the row of interest in the summary sheet on the right of the 'Submissions' tab.
|
||||
2. All information available on the run should be available in the resulting text window. This information can be exported by clicking 'Export PDF' at the top.
|
||||
2. All information available on the run should be available in the resulting text window.
|
||||
1. This information can be exported by clicking 'Export DOCX' at the top.
|
||||
|
||||
## Signing Off on a run:
|
||||
|
||||
1. Open the “Submission Details” window (see 7.6 above).
|
||||
2. Scroll down to bottom of the details window.
|
||||
3. If the current user is authorized a button marked “Sign Off” will appear at the bottom of the page. Click it.
|
||||
|
||||
## Generating a report:
|
||||
|
||||
@@ -31,20 +68,22 @@
|
||||
3. Use the file dialog to select a location to save the report.
|
||||
a. Both an excel sheet and a pdf should be generated containing summary information for submissions made by each client lab.
|
||||
|
||||
## Importing PCR results:
|
||||
## Exporting a run as an Excel file:
|
||||
|
||||
1. Right click on the run in the Submissions Table to access the context menu.
|
||||
2. Select “Export” from the context menu.
|
||||
3. Select the folder and input the filename in the “Save File” dialog.
|
||||
4. Click “Okay”.
|
||||
5. Ensure the resulting Excel workbook contains all the relevant information.
|
||||
|
||||
This is meant to import .xslx files created from the Design & Analysis Software
|
||||
1. Click on 'File' -> 'Import PCR Results'.
|
||||
2. Use the file dialog to locate the .xlsx file you want to import.
|
||||
3. Click 'Okay'.
|
||||
|
||||
## Checking Controls:
|
||||
|
||||
1. Controls for bacterial runs are now incorporated directly into the submissions database using webview. (Admittedly this performance is not as good as with a browser, so you will have to triage your data)
|
||||
2. Click on the "Controls" tab.
|
||||
3. Range of dates for controls can be selected from the date pickers at the top.
|
||||
a. If start date is set after end date, the start date will default back to 3 months before end date.
|
||||
b. Recommendation is to use less than 6 month date range keeping in mind that higher data density will affect performance (with kraken being the worst so far)
|
||||
1. If start date is set after end date, the start date will default back to 3 months before end date.
|
||||
2. Recommendation is to use less than 6 month date range keeping in mind that higher data density will affect performance (with kraken being the worst so far)
|
||||
4. Analysis type and subtype can be set using the drop down menus. (Only kraken has a subtype so far).
|
||||
|
||||
## Adding new Kit:
|
||||
@@ -69,7 +108,3 @@ This is meant to import .xslx files created from the Design & Analysis Software
|
||||
1. Click "Monthly" -> "Link PCR Logs".
|
||||
2. Chose the .csv file taken from the PCR table runlogs folder.
|
||||
|
||||
## Hitpicking:
|
||||
1. Select all submissions you wish to hitpick using "Ctrl + click". All must have PCR results.
|
||||
2. Right click on the last sample and select "Hitpick" from the contex menu.
|
||||
3. Select location to save csv file.
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
'''
|
||||
"""
|
||||
All database related operations.
|
||||
'''
|
||||
"""
|
||||
from sqlalchemy import event
|
||||
from sqlalchemy.engine import Engine
|
||||
|
||||
|
||||
@event.listens_for(Engine, "connect")
|
||||
def set_sqlite_pragma(dbapi_connection, connection_record):
|
||||
"""
|
||||
@@ -19,4 +20,5 @@ def set_sqlite_pragma(dbapi_connection, connection_record):
|
||||
cursor.execute("PRAGMA foreign_keys=ON")
|
||||
cursor.close()
|
||||
|
||||
from .models import *
|
||||
|
||||
from .models import *
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'''
|
||||
"""
|
||||
Contains all models for sqlalchemy
|
||||
'''
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import sys, logging
|
||||
from sqlalchemy import Column, INTEGER, String, JSON
|
||||
@@ -27,6 +27,7 @@ class BaseClass(Base):
|
||||
|
||||
__table_args__ = {'extend_existing': True} #: Will only add new columns
|
||||
|
||||
@classmethod
|
||||
@declared_attr
|
||||
def __tablename__(cls) -> str:
|
||||
"""
|
||||
@@ -37,6 +38,7 @@ class BaseClass(Base):
|
||||
"""
|
||||
return f"_{cls.__name__.lower()}"
|
||||
|
||||
@classmethod
|
||||
@declared_attr
|
||||
def __database_session__(cls) -> Session:
|
||||
"""
|
||||
@@ -51,6 +53,7 @@ class BaseClass(Base):
|
||||
from test_settings import ctx
|
||||
return ctx.database_session
|
||||
|
||||
@classmethod
|
||||
@declared_attr
|
||||
def __directory_path__(cls) -> Path:
|
||||
"""
|
||||
@@ -65,6 +68,7 @@ class BaseClass(Base):
|
||||
from test_settings import ctx
|
||||
return ctx.directory_path
|
||||
|
||||
@classmethod
|
||||
@declared_attr
|
||||
def __backup_path__(cls) -> Path:
|
||||
"""
|
||||
@@ -192,3 +196,4 @@ from .controls import *
|
||||
from .organizations import *
|
||||
from .kits import *
|
||||
from .submissions import *
|
||||
BasicSubmission.reagents.creator = lambda reg: SubmissionReagentAssociation(reagent=reg)
|
||||
|
||||
@@ -8,7 +8,7 @@ from sqlalchemy.ext.associationproxy import association_proxy
|
||||
from datetime import date
|
||||
import logging, re
|
||||
from tools import check_authorization, setup_lookup, Report, Result
|
||||
from typing import List, Literal, Any
|
||||
from typing import List, Literal
|
||||
from pandas import ExcelFile
|
||||
from pathlib import Path
|
||||
from . import Base, BaseClass, Organization
|
||||
@@ -126,8 +126,9 @@ class KitType(BaseClass):
|
||||
cascade="all, delete-orphan",
|
||||
) #: Relation to SubmissionType
|
||||
|
||||
used_for = association_proxy("kit_submissiontype_associations",
|
||||
"submission_type") #: Association proxy to SubmissionTypeKitTypeAssociation
|
||||
used_for = association_proxy("kit_submissiontype_associations", "submission_type",
|
||||
creator=lambda ST: SubmissionTypeKitTypeAssociation(
|
||||
submission_type=ST)) #: Association proxy to SubmissionTypeKitTypeAssociation
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""
|
||||
@@ -375,8 +376,9 @@ class Reagent(BaseClass):
|
||||
cascade="all, delete-orphan",
|
||||
) #: Relation to SubmissionSampleAssociation
|
||||
|
||||
submissions = association_proxy("reagent_submission_associations",
|
||||
"submission") #: Association proxy to SubmissionSampleAssociation.samples
|
||||
submissions = association_proxy("reagent_submission_associations", "submission",
|
||||
creator=lambda sub: SubmissionReagentAssociation(
|
||||
submission=sub)) #: Association proxy to SubmissionSampleAssociation.samples
|
||||
|
||||
def __repr__(self):
|
||||
if self.name is not None:
|
||||
@@ -597,7 +599,9 @@ class SubmissionType(BaseClass):
|
||||
cascade="all, delete-orphan",
|
||||
) #: Association of kittypes
|
||||
|
||||
kit_types = association_proxy("submissiontype_kit_associations", "kit_type") #: Proxy of kittype association
|
||||
kit_types = association_proxy("submissiontype_kit_associations", "kit_type",
|
||||
creator=lambda kit: SubmissionTypeKitTypeAssociation(
|
||||
kit_type=kit)) #: Proxy of kittype association
|
||||
|
||||
submissiontype_equipmentrole_associations = relationship(
|
||||
"SubmissionTypeEquipmentRoleAssociation",
|
||||
@@ -605,8 +609,8 @@ class SubmissionType(BaseClass):
|
||||
cascade="all, delete-orphan"
|
||||
) #: Association of equipmentroles
|
||||
|
||||
equipment = association_proxy("submissiontype_equipmentrole_associations",
|
||||
"equipment_role") #: Proxy of equipmentrole associations
|
||||
equipment = association_proxy("submissiontype_equipmentrole_associations", "equipment_role",
|
||||
creator=lambda eq: SubmissionTypeEquipmentRoleAssociation(equipment_role=eq)) #: Proxy of equipmentrole associations
|
||||
|
||||
submissiontype_kit_rt_associations = relationship(
|
||||
"KitTypeReagentRoleAssociation",
|
||||
@@ -710,7 +714,7 @@ class SubmissionType(BaseClass):
|
||||
|
||||
Returns:
|
||||
dict: Tip locations in the excel sheet.
|
||||
"""
|
||||
"""
|
||||
output = {}
|
||||
for item in self.submissiontype_tiprole_associations:
|
||||
tmap = item.uses
|
||||
@@ -827,12 +831,13 @@ class SubmissionTypeKitTypeAssociation(BaseClass):
|
||||
submission_type = relationship(SubmissionType,
|
||||
back_populates="submissiontype_kit_associations") #: joined submission type
|
||||
|
||||
def __init__(self, kit_type=None, submission_type=None):
|
||||
def __init__(self, kit_type=None, submission_type=None,
|
||||
mutable_cost_column: int = 0.00, mutable_cost_sample: int = 0.00, constant_cost: int = 0.00):
|
||||
self.kit_type = kit_type
|
||||
self.submission_type = submission_type
|
||||
self.mutable_cost_column = 0.00
|
||||
self.mutable_cost_sample = 0.00
|
||||
self.constant_cost = 0.00
|
||||
self.mutable_cost_column = mutable_cost_column
|
||||
self.mutable_cost_sample = mutable_cost_sample
|
||||
self.constant_cost = constant_cost
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""
|
||||
@@ -1539,15 +1544,15 @@ class Process(BaseClass):
|
||||
pass
|
||||
return cls.execute_query(query=query, limit=limit)
|
||||
|
||||
|
||||
@check_authorization
|
||||
def save(self):
|
||||
super().save()
|
||||
|
||||
|
||||
class TipRole(BaseClass):
|
||||
"""
|
||||
An abstract role that a tip fills during a process
|
||||
"""
|
||||
"""
|
||||
id = Column(INTEGER, primary_key=True) #: primary key
|
||||
name = Column(String(64)) #: name of reagent type
|
||||
instances = relationship("Tips", back_populates="role",
|
||||
@@ -1564,7 +1569,7 @@ class TipRole(BaseClass):
|
||||
|
||||
def __repr__(self):
|
||||
return f"<TipRole({self.name})>"
|
||||
|
||||
|
||||
@check_authorization
|
||||
def save(self):
|
||||
super().save()
|
||||
@@ -1573,7 +1578,7 @@ class TipRole(BaseClass):
|
||||
class Tips(BaseClass):
|
||||
"""
|
||||
A concrete instance of tips.
|
||||
"""
|
||||
"""
|
||||
id = Column(INTEGER, primary_key=True) #: primary key
|
||||
role = relationship("TipRole", back_populates="instances",
|
||||
secondary=tiproles_tips) #: joined parent reagent type
|
||||
@@ -1606,7 +1611,7 @@ class Tips(BaseClass):
|
||||
|
||||
Returns:
|
||||
Tips | List[Tips]: Tips matching criteria
|
||||
"""
|
||||
"""
|
||||
query = cls.__database_session__.query(cls)
|
||||
match name:
|
||||
case str():
|
||||
@@ -1622,7 +1627,7 @@ class Tips(BaseClass):
|
||||
case _:
|
||||
pass
|
||||
return cls.execute_query(query=query, limit=limit)
|
||||
|
||||
|
||||
@check_authorization
|
||||
def save(self):
|
||||
super().save()
|
||||
@@ -1642,7 +1647,7 @@ class SubmissionTypeTipRoleAssociation(BaseClass):
|
||||
back_populates="submissiontype_tiprole_associations") #: associated submission
|
||||
tip_role = relationship(TipRole,
|
||||
back_populates="tiprole_submissiontype_associations") #: associated equipment
|
||||
|
||||
|
||||
@check_authorization
|
||||
def save(self):
|
||||
super().save()
|
||||
@@ -1651,7 +1656,7 @@ class SubmissionTypeTipRoleAssociation(BaseClass):
|
||||
class SubmissionTipsAssociation(BaseClass):
|
||||
"""
|
||||
Association between a concrete submission instance and concrete tips
|
||||
"""
|
||||
"""
|
||||
tip_id = Column(INTEGER, ForeignKey("_tips.id"), primary_key=True) #: id of associated equipment
|
||||
submission_id = Column(INTEGER, ForeignKey("_basicsubmission.id"), primary_key=True) #: id of associated submission
|
||||
submission = relationship("BasicSubmission",
|
||||
@@ -1668,5 +1673,5 @@ class SubmissionTipsAssociation(BaseClass):
|
||||
|
||||
Returns:
|
||||
dict: Values of this object
|
||||
"""
|
||||
"""
|
||||
return dict(role=self.role_name, name=self.tips.name, lot=self.tips.lot)
|
||||
|
||||
@@ -79,7 +79,7 @@ class BasicSubmission(BaseClass):
|
||||
) #: Relation to SubmissionSampleAssociation
|
||||
|
||||
samples = association_proxy("submission_sample_associations",
|
||||
"sample") #: Association proxy to SubmissionSampleAssociation.samples
|
||||
"sample", creator=lambda sample: SubmissionSampleAssociation(sample=sample)) #: Association proxy to SubmissionSampleAssociation.samples
|
||||
|
||||
submission_reagent_associations = relationship(
|
||||
"SubmissionReagentAssociation",
|
||||
@@ -853,14 +853,14 @@ class BasicSubmission(BaseClass):
|
||||
@classmethod
|
||||
def custom_sample_autofill_row(cls, sample, worksheet: Worksheet) -> int:
|
||||
"""
|
||||
_summary_
|
||||
Updates row information
|
||||
|
||||
Args:
|
||||
sample (_type_): _description_
|
||||
worksheet (Workbook): _description_
|
||||
|
||||
Returns:
|
||||
int: _description_
|
||||
int: New row number
|
||||
"""
|
||||
return None
|
||||
|
||||
@@ -1307,7 +1307,6 @@ class BacterialCulture(BasicSubmission):
|
||||
row = idx.index.to_list()[0]
|
||||
return row + 1
|
||||
|
||||
|
||||
class Wastewater(BasicSubmission):
|
||||
"""
|
||||
derivative submission type from BasicSubmission
|
||||
@@ -2053,7 +2052,6 @@ class BasicSample(BaseClass):
|
||||
dict: well location and name (sample id, organism) NOTE: keys must sync with WWSample to_sub_dict above
|
||||
"""
|
||||
# logger.debug(f"Converting {self} to dict.")
|
||||
# start = time()
|
||||
sample = {}
|
||||
sample['submitter_id'] = self.submitter_id
|
||||
sample['sample_type'] = self.sample_type
|
||||
|
||||
@@ -69,6 +69,7 @@ class App(QMainWindow):
|
||||
helpMenu = menuBar.addMenu("&Help")
|
||||
helpMenu.addAction(self.helpAction)
|
||||
helpMenu.addAction(self.docsAction)
|
||||
helpMenu.addAction(self.githubAction)
|
||||
fileMenu.addAction(self.importAction)
|
||||
methodsMenu.addAction(self.searchLog)
|
||||
methodsMenu.addAction(self.searchSample)
|
||||
@@ -103,6 +104,7 @@ class App(QMainWindow):
|
||||
self.docsAction = QAction("&Docs", self)
|
||||
self.searchLog = QAction("Search Log", self)
|
||||
self.searchSample = QAction("Search Sample", self)
|
||||
self.githubAction = QAction("Github", self)
|
||||
|
||||
def _connectActions(self):
|
||||
"""
|
||||
@@ -118,6 +120,7 @@ class App(QMainWindow):
|
||||
self.docsAction.triggered.connect(self.openDocs)
|
||||
self.searchLog.triggered.connect(self.runSearch)
|
||||
self.searchSample.triggered.connect(self.runSampleSearch)
|
||||
self.githubAction.triggered.connect(self.openGithub)
|
||||
|
||||
def showAbout(self):
|
||||
"""
|
||||
@@ -138,6 +141,14 @@ class App(QMainWindow):
|
||||
# logger.debug(f"Attempting to open {url}")
|
||||
webbrowser.get('windows-default').open(f"file://{url.__str__()}")
|
||||
|
||||
def openGithub(self):
|
||||
"""
|
||||
Opens the instructions html page
|
||||
"""
|
||||
url = "https://github.com/landowark/submissions"
|
||||
webbrowser.get('windows-default').open(url)
|
||||
|
||||
|
||||
def result_reporter(self):
|
||||
"""
|
||||
Report any anomolous results - if any - to the user
|
||||
|
||||
@@ -18,7 +18,7 @@ class EquipmentUsage(QDialog):
|
||||
def __init__(self, parent, submission: BasicSubmission) -> QDialog:
|
||||
super().__init__(parent)
|
||||
self.submission = submission
|
||||
self.setWindowTitle("Equipment Checklist")
|
||||
self.setWindowTitle(f"Equipment Checklist - {submission.rsl_plate_num}")
|
||||
self.used_equipment = self.submission.get_used_equipment()
|
||||
self.kit = self.submission.extraction_kit
|
||||
# logger.debug(f"Existing equipment: {self.used_equipment}")
|
||||
|
||||
Reference in New Issue
Block a user