post documentation and code clean-up.
This commit is contained in:
@@ -1,3 +1,7 @@
|
|||||||
|
## 202407.01
|
||||||
|
|
||||||
|
- Better documentation.
|
||||||
|
|
||||||
## 202406.04
|
## 202406.04
|
||||||
|
|
||||||
- Exported submission details will now be in docx format.
|
- 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.
|
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.
|
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.
|
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'.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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:
|
## 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.
|
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:
|
## Generating a report:
|
||||||
|
|
||||||
@@ -31,20 +68,22 @@
|
|||||||
3. Use the file dialog to select a location to save the report.
|
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.
|
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:
|
## 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)
|
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.
|
2. Click on the "Controls" tab.
|
||||||
3. Range of dates for controls can be selected from the date pickers at the top.
|
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.
|
1. 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)
|
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).
|
4. Analysis type and subtype can be set using the drop down menus. (Only kraken has a subtype so far).
|
||||||
|
|
||||||
## Adding new Kit:
|
## 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".
|
1. Click "Monthly" -> "Link PCR Logs".
|
||||||
2. Chose the .csv file taken from the PCR table runlogs folder.
|
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.
|
All database related operations.
|
||||||
'''
|
"""
|
||||||
from sqlalchemy import event
|
from sqlalchemy import event
|
||||||
from sqlalchemy.engine import Engine
|
from sqlalchemy.engine import Engine
|
||||||
|
|
||||||
|
|
||||||
@event.listens_for(Engine, "connect")
|
@event.listens_for(Engine, "connect")
|
||||||
def set_sqlite_pragma(dbapi_connection, connection_record):
|
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.execute("PRAGMA foreign_keys=ON")
|
||||||
cursor.close()
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
from .models import *
|
from .models import *
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
'''
|
"""
|
||||||
Contains all models for sqlalchemy
|
Contains all models for sqlalchemy
|
||||||
'''
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import sys, logging
|
import sys, logging
|
||||||
from sqlalchemy import Column, INTEGER, String, JSON
|
from sqlalchemy import Column, INTEGER, String, JSON
|
||||||
@@ -27,6 +27,7 @@ class BaseClass(Base):
|
|||||||
|
|
||||||
__table_args__ = {'extend_existing': True} #: Will only add new columns
|
__table_args__ = {'extend_existing': True} #: Will only add new columns
|
||||||
|
|
||||||
|
@classmethod
|
||||||
@declared_attr
|
@declared_attr
|
||||||
def __tablename__(cls) -> str:
|
def __tablename__(cls) -> str:
|
||||||
"""
|
"""
|
||||||
@@ -37,6 +38,7 @@ class BaseClass(Base):
|
|||||||
"""
|
"""
|
||||||
return f"_{cls.__name__.lower()}"
|
return f"_{cls.__name__.lower()}"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
@declared_attr
|
@declared_attr
|
||||||
def __database_session__(cls) -> Session:
|
def __database_session__(cls) -> Session:
|
||||||
"""
|
"""
|
||||||
@@ -51,6 +53,7 @@ class BaseClass(Base):
|
|||||||
from test_settings import ctx
|
from test_settings import ctx
|
||||||
return ctx.database_session
|
return ctx.database_session
|
||||||
|
|
||||||
|
@classmethod
|
||||||
@declared_attr
|
@declared_attr
|
||||||
def __directory_path__(cls) -> Path:
|
def __directory_path__(cls) -> Path:
|
||||||
"""
|
"""
|
||||||
@@ -65,6 +68,7 @@ class BaseClass(Base):
|
|||||||
from test_settings import ctx
|
from test_settings import ctx
|
||||||
return ctx.directory_path
|
return ctx.directory_path
|
||||||
|
|
||||||
|
@classmethod
|
||||||
@declared_attr
|
@declared_attr
|
||||||
def __backup_path__(cls) -> Path:
|
def __backup_path__(cls) -> Path:
|
||||||
"""
|
"""
|
||||||
@@ -192,3 +196,4 @@ from .controls import *
|
|||||||
from .organizations import *
|
from .organizations import *
|
||||||
from .kits import *
|
from .kits import *
|
||||||
from .submissions 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
|
from datetime import date
|
||||||
import logging, re
|
import logging, re
|
||||||
from tools import check_authorization, setup_lookup, Report, Result
|
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 pandas import ExcelFile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from . import Base, BaseClass, Organization
|
from . import Base, BaseClass, Organization
|
||||||
@@ -126,8 +126,9 @@ class KitType(BaseClass):
|
|||||||
cascade="all, delete-orphan",
|
cascade="all, delete-orphan",
|
||||||
) #: Relation to SubmissionType
|
) #: Relation to SubmissionType
|
||||||
|
|
||||||
used_for = association_proxy("kit_submissiontype_associations",
|
used_for = association_proxy("kit_submissiontype_associations", "submission_type",
|
||||||
"submission_type") #: Association proxy to SubmissionTypeKitTypeAssociation
|
creator=lambda ST: SubmissionTypeKitTypeAssociation(
|
||||||
|
submission_type=ST)) #: Association proxy to SubmissionTypeKitTypeAssociation
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
"""
|
"""
|
||||||
@@ -375,8 +376,9 @@ class Reagent(BaseClass):
|
|||||||
cascade="all, delete-orphan",
|
cascade="all, delete-orphan",
|
||||||
) #: Relation to SubmissionSampleAssociation
|
) #: Relation to SubmissionSampleAssociation
|
||||||
|
|
||||||
submissions = association_proxy("reagent_submission_associations",
|
submissions = association_proxy("reagent_submission_associations", "submission",
|
||||||
"submission") #: Association proxy to SubmissionSampleAssociation.samples
|
creator=lambda sub: SubmissionReagentAssociation(
|
||||||
|
submission=sub)) #: Association proxy to SubmissionSampleAssociation.samples
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if self.name is not None:
|
if self.name is not None:
|
||||||
@@ -597,7 +599,9 @@ class SubmissionType(BaseClass):
|
|||||||
cascade="all, delete-orphan",
|
cascade="all, delete-orphan",
|
||||||
) #: Association of kittypes
|
) #: 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(
|
submissiontype_equipmentrole_associations = relationship(
|
||||||
"SubmissionTypeEquipmentRoleAssociation",
|
"SubmissionTypeEquipmentRoleAssociation",
|
||||||
@@ -605,8 +609,8 @@ class SubmissionType(BaseClass):
|
|||||||
cascade="all, delete-orphan"
|
cascade="all, delete-orphan"
|
||||||
) #: Association of equipmentroles
|
) #: Association of equipmentroles
|
||||||
|
|
||||||
equipment = association_proxy("submissiontype_equipmentrole_associations",
|
equipment = association_proxy("submissiontype_equipmentrole_associations", "equipment_role",
|
||||||
"equipment_role") #: Proxy of equipmentrole associations
|
creator=lambda eq: SubmissionTypeEquipmentRoleAssociation(equipment_role=eq)) #: Proxy of equipmentrole associations
|
||||||
|
|
||||||
submissiontype_kit_rt_associations = relationship(
|
submissiontype_kit_rt_associations = relationship(
|
||||||
"KitTypeReagentRoleAssociation",
|
"KitTypeReagentRoleAssociation",
|
||||||
@@ -827,12 +831,13 @@ class SubmissionTypeKitTypeAssociation(BaseClass):
|
|||||||
submission_type = relationship(SubmissionType,
|
submission_type = relationship(SubmissionType,
|
||||||
back_populates="submissiontype_kit_associations") #: joined submission type
|
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.kit_type = kit_type
|
||||||
self.submission_type = submission_type
|
self.submission_type = submission_type
|
||||||
self.mutable_cost_column = 0.00
|
self.mutable_cost_column = mutable_cost_column
|
||||||
self.mutable_cost_sample = 0.00
|
self.mutable_cost_sample = mutable_cost_sample
|
||||||
self.constant_cost = 0.00
|
self.constant_cost = constant_cost
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
"""
|
"""
|
||||||
@@ -1539,11 +1544,11 @@ class Process(BaseClass):
|
|||||||
pass
|
pass
|
||||||
return cls.execute_query(query=query, limit=limit)
|
return cls.execute_query(query=query, limit=limit)
|
||||||
|
|
||||||
|
|
||||||
@check_authorization
|
@check_authorization
|
||||||
def save(self):
|
def save(self):
|
||||||
super().save()
|
super().save()
|
||||||
|
|
||||||
|
|
||||||
class TipRole(BaseClass):
|
class TipRole(BaseClass):
|
||||||
"""
|
"""
|
||||||
An abstract role that a tip fills during a process
|
An abstract role that a tip fills during a process
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class BasicSubmission(BaseClass):
|
|||||||
) #: Relation to SubmissionSampleAssociation
|
) #: Relation to SubmissionSampleAssociation
|
||||||
|
|
||||||
samples = association_proxy("submission_sample_associations",
|
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(
|
submission_reagent_associations = relationship(
|
||||||
"SubmissionReagentAssociation",
|
"SubmissionReagentAssociation",
|
||||||
@@ -853,14 +853,14 @@ class BasicSubmission(BaseClass):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def custom_sample_autofill_row(cls, sample, worksheet: Worksheet) -> int:
|
def custom_sample_autofill_row(cls, sample, worksheet: Worksheet) -> int:
|
||||||
"""
|
"""
|
||||||
_summary_
|
Updates row information
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
sample (_type_): _description_
|
sample (_type_): _description_
|
||||||
worksheet (Workbook): _description_
|
worksheet (Workbook): _description_
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: _description_
|
int: New row number
|
||||||
"""
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -1307,7 +1307,6 @@ class BacterialCulture(BasicSubmission):
|
|||||||
row = idx.index.to_list()[0]
|
row = idx.index.to_list()[0]
|
||||||
return row + 1
|
return row + 1
|
||||||
|
|
||||||
|
|
||||||
class Wastewater(BasicSubmission):
|
class Wastewater(BasicSubmission):
|
||||||
"""
|
"""
|
||||||
derivative submission type from 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
|
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.")
|
# logger.debug(f"Converting {self} to dict.")
|
||||||
# start = time()
|
|
||||||
sample = {}
|
sample = {}
|
||||||
sample['submitter_id'] = self.submitter_id
|
sample['submitter_id'] = self.submitter_id
|
||||||
sample['sample_type'] = self.sample_type
|
sample['sample_type'] = self.sample_type
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ class App(QMainWindow):
|
|||||||
helpMenu = menuBar.addMenu("&Help")
|
helpMenu = menuBar.addMenu("&Help")
|
||||||
helpMenu.addAction(self.helpAction)
|
helpMenu.addAction(self.helpAction)
|
||||||
helpMenu.addAction(self.docsAction)
|
helpMenu.addAction(self.docsAction)
|
||||||
|
helpMenu.addAction(self.githubAction)
|
||||||
fileMenu.addAction(self.importAction)
|
fileMenu.addAction(self.importAction)
|
||||||
methodsMenu.addAction(self.searchLog)
|
methodsMenu.addAction(self.searchLog)
|
||||||
methodsMenu.addAction(self.searchSample)
|
methodsMenu.addAction(self.searchSample)
|
||||||
@@ -103,6 +104,7 @@ class App(QMainWindow):
|
|||||||
self.docsAction = QAction("&Docs", self)
|
self.docsAction = QAction("&Docs", self)
|
||||||
self.searchLog = QAction("Search Log", self)
|
self.searchLog = QAction("Search Log", self)
|
||||||
self.searchSample = QAction("Search Sample", self)
|
self.searchSample = QAction("Search Sample", self)
|
||||||
|
self.githubAction = QAction("Github", self)
|
||||||
|
|
||||||
def _connectActions(self):
|
def _connectActions(self):
|
||||||
"""
|
"""
|
||||||
@@ -118,6 +120,7 @@ class App(QMainWindow):
|
|||||||
self.docsAction.triggered.connect(self.openDocs)
|
self.docsAction.triggered.connect(self.openDocs)
|
||||||
self.searchLog.triggered.connect(self.runSearch)
|
self.searchLog.triggered.connect(self.runSearch)
|
||||||
self.searchSample.triggered.connect(self.runSampleSearch)
|
self.searchSample.triggered.connect(self.runSampleSearch)
|
||||||
|
self.githubAction.triggered.connect(self.openGithub)
|
||||||
|
|
||||||
def showAbout(self):
|
def showAbout(self):
|
||||||
"""
|
"""
|
||||||
@@ -138,6 +141,14 @@ class App(QMainWindow):
|
|||||||
# logger.debug(f"Attempting to open {url}")
|
# logger.debug(f"Attempting to open {url}")
|
||||||
webbrowser.get('windows-default').open(f"file://{url.__str__()}")
|
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):
|
def result_reporter(self):
|
||||||
"""
|
"""
|
||||||
Report any anomolous results - if any - to the user
|
Report any anomolous results - if any - to the user
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class EquipmentUsage(QDialog):
|
|||||||
def __init__(self, parent, submission: BasicSubmission) -> QDialog:
|
def __init__(self, parent, submission: BasicSubmission) -> QDialog:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.submission = submission
|
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.used_equipment = self.submission.get_used_equipment()
|
||||||
self.kit = self.submission.extraction_kit
|
self.kit = self.submission.extraction_kit
|
||||||
# logger.debug(f"Existing equipment: {self.used_equipment}")
|
# logger.debug(f"Existing equipment: {self.used_equipment}")
|
||||||
|
|||||||
Reference in New Issue
Block a user