Database updates, scraping samples from excel sheets
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
"""initial commit
|
"""initial commit
|
||||||
|
|
||||||
Revision ID: 4cba0c1ffe03
|
Revision ID: 03da9270e51f
|
||||||
Revises:
|
Revises:
|
||||||
Create Date: 2023-01-18 08:59:34.382715
|
Create Date: 2023-01-19 09:01:03.022482
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from alembic import op
|
from alembic import op
|
||||||
@@ -10,7 +10,7 @@ import sqlalchemy as sa
|
|||||||
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = '4cba0c1ffe03'
|
revision = '03da9270e51f'
|
||||||
down_revision = None
|
down_revision = None
|
||||||
branch_labels = None
|
branch_labels = None
|
||||||
depends_on = None
|
depends_on = None
|
||||||
@@ -50,22 +50,6 @@ def upgrade() -> None:
|
|||||||
sa.ForeignKeyConstraint(['kit_id'], ['_kits.id'], name='fk_RT_kits_id', ondelete='SET NULL', use_alter=True),
|
sa.ForeignKeyConstraint(['kit_id'], ['_kits.id'], name='fk_RT_kits_id', ondelete='SET NULL', use_alter=True),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint('id')
|
||||||
)
|
)
|
||||||
op.create_table('_ww_samples',
|
|
||||||
sa.Column('id', sa.INTEGER(), nullable=False),
|
|
||||||
sa.Column('ww_processing_num', sa.String(length=64), nullable=True),
|
|
||||||
sa.Column('ww_sample_full_id', sa.String(length=64), nullable=True),
|
|
||||||
sa.Column('rsl_number', sa.String(length=64), nullable=True),
|
|
||||||
sa.Column('collection_date', sa.TIMESTAMP(), nullable=True),
|
|
||||||
sa.Column('testing_type', sa.String(length=64), nullable=True),
|
|
||||||
sa.Column('site_status', sa.String(length=64), nullable=True),
|
|
||||||
sa.Column('notes', sa.String(length=2000), nullable=True),
|
|
||||||
sa.Column('ct_n1', sa.FLOAT(precision=2), nullable=True),
|
|
||||||
sa.Column('ct_n2', sa.FLOAT(precision=2), nullable=True),
|
|
||||||
sa.Column('seq_submitted', sa.BOOLEAN(), nullable=True),
|
|
||||||
sa.Column('ww_seq_run_id', sa.String(length=64), nullable=True),
|
|
||||||
sa.Column('sample_type', sa.String(length=8), nullable=True),
|
|
||||||
sa.PrimaryKeyConstraint('id')
|
|
||||||
)
|
|
||||||
op.create_table('_control_samples',
|
op.create_table('_control_samples',
|
||||||
sa.Column('id', sa.INTEGER(), nullable=False),
|
sa.Column('id', sa.INTEGER(), nullable=False),
|
||||||
sa.Column('parent_id', sa.String(), nullable=True),
|
sa.Column('parent_id', sa.String(), nullable=True),
|
||||||
@@ -119,35 +103,63 @@ def upgrade() -> None:
|
|||||||
sa.Column('technician', sa.String(length=64), nullable=True),
|
sa.Column('technician', sa.String(length=64), nullable=True),
|
||||||
sa.Column('reagents_id', sa.String(), nullable=True),
|
sa.Column('reagents_id', sa.String(), nullable=True),
|
||||||
sa.Column('control_id', sa.INTEGER(), nullable=True),
|
sa.Column('control_id', sa.INTEGER(), nullable=True),
|
||||||
sa.Column('sample_id', sa.String(), nullable=True),
|
|
||||||
sa.ForeignKeyConstraint(['control_id'], ['_control_samples.id'], name='fk_BC_control_id', ondelete='SET NULL'),
|
sa.ForeignKeyConstraint(['control_id'], ['_control_samples.id'], name='fk_BC_control_id', ondelete='SET NULL'),
|
||||||
sa.ForeignKeyConstraint(['extraction_kit_id'], ['_kits.id'], ondelete='SET NULL'),
|
sa.ForeignKeyConstraint(['extraction_kit_id'], ['_kits.id'], name='fk_BS_extkit_id', ondelete='SET NULL'),
|
||||||
sa.ForeignKeyConstraint(['reagents_id'], ['_reagents.id'], name='fk_BS_reagents_id', ondelete='SET NULL'),
|
sa.ForeignKeyConstraint(['reagents_id'], ['_reagents.id'], name='fk_BS_reagents_id', ondelete='SET NULL'),
|
||||||
sa.ForeignKeyConstraint(['sample_id'], ['_ww_samples.id'], name='fk_WW_sample_id', ondelete='SET NULL'),
|
sa.ForeignKeyConstraint(['submitting_lab_id'], ['_organizations.id'], name='fk_BS_sublab_id', ondelete='SET NULL'),
|
||||||
sa.ForeignKeyConstraint(['submitting_lab_id'], ['_organizations.id'], ondelete='SET NULL'),
|
|
||||||
sa.PrimaryKeyConstraint('id'),
|
sa.PrimaryKeyConstraint('id'),
|
||||||
sa.UniqueConstraint('rsl_plate_num'),
|
sa.UniqueConstraint('rsl_plate_num'),
|
||||||
sa.UniqueConstraint('submitter_plate_num')
|
sa.UniqueConstraint('submitter_plate_num')
|
||||||
)
|
)
|
||||||
|
op.create_table('_bc_samples',
|
||||||
|
sa.Column('id', sa.INTEGER(), nullable=False),
|
||||||
|
sa.Column('well_number', sa.String(length=8), nullable=True),
|
||||||
|
sa.Column('sample_id', sa.String(length=64), nullable=False),
|
||||||
|
sa.Column('organism', sa.String(length=64), nullable=True),
|
||||||
|
sa.Column('concentration', sa.String(length=16), nullable=True),
|
||||||
|
sa.Column('rsl_plate_id', sa.INTEGER(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['rsl_plate_id'], ['_submissions.id'], name='fk_BCS_sample_id', ondelete='SET NULL'),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
op.create_table('_reagents_submissions',
|
op.create_table('_reagents_submissions',
|
||||||
sa.Column('reagent_id', sa.INTEGER(), nullable=True),
|
sa.Column('reagent_id', sa.INTEGER(), nullable=True),
|
||||||
sa.Column('submission_id', sa.INTEGER(), nullable=True),
|
sa.Column('submission_id', sa.INTEGER(), nullable=True),
|
||||||
sa.ForeignKeyConstraint(['reagent_id'], ['_reagents.id'], ),
|
sa.ForeignKeyConstraint(['reagent_id'], ['_reagents.id'], ),
|
||||||
sa.ForeignKeyConstraint(['submission_id'], ['_submissions.id'], )
|
sa.ForeignKeyConstraint(['submission_id'], ['_submissions.id'], )
|
||||||
)
|
)
|
||||||
|
op.create_table('_ww_samples',
|
||||||
|
sa.Column('id', sa.INTEGER(), nullable=False),
|
||||||
|
sa.Column('ww_processing_num', sa.String(length=64), nullable=True),
|
||||||
|
sa.Column('ww_sample_full_id', sa.String(length=64), nullable=False),
|
||||||
|
sa.Column('rsl_number', sa.String(length=64), nullable=True),
|
||||||
|
sa.Column('rsl_plate_id', sa.INTEGER(), nullable=True),
|
||||||
|
sa.Column('collection_date', sa.TIMESTAMP(), nullable=True),
|
||||||
|
sa.Column('testing_type', sa.String(length=64), nullable=True),
|
||||||
|
sa.Column('site_status', sa.String(length=64), nullable=True),
|
||||||
|
sa.Column('notes', sa.String(length=2000), nullable=True),
|
||||||
|
sa.Column('ct_n1', sa.FLOAT(precision=2), nullable=True),
|
||||||
|
sa.Column('ct_n2', sa.FLOAT(precision=2), nullable=True),
|
||||||
|
sa.Column('seq_submitted', sa.BOOLEAN(), nullable=True),
|
||||||
|
sa.Column('ww_seq_run_id', sa.String(length=64), nullable=True),
|
||||||
|
sa.Column('sample_type', sa.String(length=8), nullable=True),
|
||||||
|
sa.Column('well_number', sa.String(length=8), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['rsl_plate_id'], ['_submissions.id'], name='fk_WWS_sample_id', ondelete='SET NULL'),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_table('_ww_samples')
|
||||||
op.drop_table('_reagents_submissions')
|
op.drop_table('_reagents_submissions')
|
||||||
|
op.drop_table('_bc_samples')
|
||||||
op.drop_table('_submissions')
|
op.drop_table('_submissions')
|
||||||
op.drop_table('_orgs_contacts')
|
op.drop_table('_orgs_contacts')
|
||||||
op.drop_table('_reagentstypes_kittypes')
|
op.drop_table('_reagentstypes_kittypes')
|
||||||
op.drop_table('_reagents')
|
op.drop_table('_reagents')
|
||||||
op.drop_table('_organizations')
|
op.drop_table('_organizations')
|
||||||
op.drop_table('_control_samples')
|
op.drop_table('_control_samples')
|
||||||
op.drop_table('_ww_samples')
|
|
||||||
op.drop_table('_reagent_types')
|
op.drop_table('_reagent_types')
|
||||||
op.drop_table('_kits')
|
op.drop_table('_kits')
|
||||||
op.drop_table('_control_types')
|
op.drop_table('_control_types')
|
||||||
@@ -16,6 +16,12 @@ def get_kits_by_use( ctx:dict, kittype_str:str|None) -> list:
|
|||||||
|
|
||||||
|
|
||||||
def store_submission(ctx:dict, base_submission:models.BasicSubmission) -> None:
|
def store_submission(ctx:dict, base_submission:models.BasicSubmission) -> None:
|
||||||
|
for sample in base_submission.samples:
|
||||||
|
sample.rsl_plate = base_submission
|
||||||
|
try:
|
||||||
|
ctx['database_session'].add(sample)
|
||||||
|
except IntegrityError:
|
||||||
|
continue
|
||||||
ctx['database_session'].add(base_submission)
|
ctx['database_session'].add(base_submission)
|
||||||
try:
|
try:
|
||||||
ctx['database_session'].commit()
|
ctx['database_session'].commit()
|
||||||
@@ -53,6 +59,11 @@ def construct_submission_info(ctx:dict, info_dict:dict) -> models.BasicSubmissio
|
|||||||
# Because of unique constraint, the submitter plate number cannot be None, so...
|
# Because of unique constraint, the submitter plate number cannot be None, so...
|
||||||
if info_dict[item] == None:
|
if info_dict[item] == None:
|
||||||
info_dict[item] = uuid.uuid4().hex.upper()
|
info_dict[item] = uuid.uuid4().hex.upper()
|
||||||
|
field_value = info_dict[item]
|
||||||
|
# case "samples":
|
||||||
|
# for sample in info_dict[item]:
|
||||||
|
# instance.samples.append(sample)
|
||||||
|
# continue
|
||||||
case _:
|
case _:
|
||||||
field_value = info_dict[item]
|
field_value = info_dict[item]
|
||||||
try:
|
try:
|
||||||
@@ -60,6 +71,7 @@ def construct_submission_info(ctx:dict, info_dict:dict) -> models.BasicSubmissio
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
print(f"Could not set attribute: {item} to {info_dict[item]}")
|
print(f"Could not set attribute: {item} to {info_dict[item]}")
|
||||||
continue
|
continue
|
||||||
|
# print(instance.__dict__)
|
||||||
return instance
|
return instance
|
||||||
# looked_up = []
|
# looked_up = []
|
||||||
# for reagent in reagents:
|
# for reagent in reagents:
|
||||||
@@ -184,7 +196,11 @@ def create_kit_from_yaml(ctx:dict, exp:dict) -> None:
|
|||||||
Args:
|
Args:
|
||||||
ctx (dict): Context dictionary passed down from frontend
|
ctx (dict): Context dictionary passed down from frontend
|
||||||
exp (dict): Experiment dictionary created from yaml file
|
exp (dict): Experiment dictionary created from yaml file
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
|
exp['password'].decode()
|
||||||
|
except (UnicodeDecodeError, AttributeError):
|
||||||
|
exp['password'] = exp['password'].encode()
|
||||||
if base64.b64encode(exp['password']) != b'cnNsX3N1Ym1pNTVpb25z':
|
if base64.b64encode(exp['password']) != b'cnNsX3N1Ym1pNTVpb25z':
|
||||||
print(f"Not the correct password.")
|
print(f"Not the correct password.")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -8,4 +8,4 @@ from .controls import Control, ControlType
|
|||||||
from .kits import KitType, ReagentType, Reagent
|
from .kits import KitType, ReagentType, Reagent
|
||||||
from .submissions import BasicSubmission, BacterialCulture, Wastewater
|
from .submissions import BasicSubmission, BacterialCulture, Wastewater
|
||||||
from .organizations import Organization, Contact
|
from .organizations import Organization, Contact
|
||||||
from .samples import Sample
|
from .samples import WWSample, BCSample
|
||||||
@@ -3,15 +3,16 @@ from sqlalchemy import Column, String, TIMESTAMP, text, JSON, INTEGER, ForeignKe
|
|||||||
from sqlalchemy.orm import relationship, relationships
|
from sqlalchemy.orm import relationship, relationships
|
||||||
|
|
||||||
|
|
||||||
class Sample(Base):
|
class WWSample(Base):
|
||||||
|
|
||||||
__tablename__ = "_ww_samples"
|
__tablename__ = "_ww_samples"
|
||||||
|
|
||||||
id = Column(INTEGER, primary_key=True) #: primary key
|
id = Column(INTEGER, primary_key=True) #: primary key
|
||||||
ww_processing_num = Column(String(64))
|
ww_processing_num = Column(String(64))
|
||||||
ww_sample_full_id = Column(String(64))
|
ww_sample_full_id = Column(String(64), nullable=False)
|
||||||
rsl_number = Column(String(64))
|
rsl_number = Column(String(64))
|
||||||
rsl_plate = relationship("Wastewater", back_populates="samples")
|
rsl_plate = relationship("Wastewater", back_populates="samples")
|
||||||
|
rsl_plate_id = Column(INTEGER, ForeignKey("_submissions.id", ondelete="SET NULL", name="fk_WWS_sample_id"))
|
||||||
collection_date = Column(TIMESTAMP) #: Date submission received
|
collection_date = Column(TIMESTAMP) #: Date submission received
|
||||||
testing_type = Column(String(64))
|
testing_type = Column(String(64))
|
||||||
site_status = Column(String(64))
|
site_status = Column(String(64))
|
||||||
@@ -21,7 +22,35 @@ class Sample(Base):
|
|||||||
seq_submitted = Column(BOOLEAN())
|
seq_submitted = Column(BOOLEAN())
|
||||||
ww_seq_run_id = Column(String(64))
|
ww_seq_run_id = Column(String(64))
|
||||||
sample_type = Column(String(8))
|
sample_type = Column(String(8))
|
||||||
|
well_number = Column(String(8))
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
return f"{self.well_number}: {self.ww_sample_full_id}"
|
||||||
|
|
||||||
|
def to_sub_dict(self):
|
||||||
|
return {
|
||||||
|
"well": self.well_number,
|
||||||
|
"name": self.ww_sample_full_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BCSample(Base):
|
||||||
|
|
||||||
|
__tablename__ = "_bc_samples"
|
||||||
|
|
||||||
|
id = Column(INTEGER, primary_key=True) #: primary key
|
||||||
|
well_number = Column(String(8))
|
||||||
|
sample_id = Column(String(64), nullable=False)
|
||||||
|
organism = Column(String(64))
|
||||||
|
concentration = Column(String(16))
|
||||||
|
rsl_plate_id = Column(INTEGER, ForeignKey("_submissions.id", ondelete="SET NULL", name="fk_BCS_sample_id"))
|
||||||
|
rsl_plate = relationship("BacterialCulture", back_populates="samples")
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
return f"{self.well_number}: {self.sample_id} - {self.organism}"
|
||||||
|
|
||||||
|
def to_sub_dict(self):
|
||||||
|
return {
|
||||||
|
"well": self.well_number,
|
||||||
|
"name": f"{self.sample_id} - ({self.organism})",
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ class BasicSubmission(Base):
|
|||||||
submitter_plate_num = Column(String(127), unique=True) #: The number given to the submission by the submitting lab
|
submitter_plate_num = Column(String(127), unique=True) #: The number given to the submission by the submitting lab
|
||||||
submitted_date = Column(TIMESTAMP) #: Date submission received
|
submitted_date = Column(TIMESTAMP) #: Date submission received
|
||||||
submitting_lab = relationship("Organization", back_populates="submissions") #: client
|
submitting_lab = relationship("Organization", back_populates="submissions") #: client
|
||||||
submitting_lab_id = Column(INTEGER, ForeignKey("_organizations.id", ondelete="SET NULL"))
|
submitting_lab_id = Column(INTEGER, ForeignKey("_organizations.id", ondelete="SET NULL", name="fk_BS_sublab_id"))
|
||||||
sample_count = Column(INTEGER) #: Number of samples in the submission
|
sample_count = Column(INTEGER) #: Number of samples in the submission
|
||||||
extraction_kit = relationship("KitType", back_populates="submissions") #: The extraction kit used
|
extraction_kit = relationship("KitType", back_populates="submissions") #: The extraction kit used
|
||||||
extraction_kit_id = Column(INTEGER, ForeignKey("_kits.id", ondelete="SET NULL"))
|
extraction_kit_id = Column(INTEGER, ForeignKey("_kits.id", ondelete="SET NULL", name="fk_BS_extkit_id"))
|
||||||
submission_type = Column(String(32))
|
submission_type = Column(String(32))
|
||||||
technician = Column(String(64))
|
technician = Column(String(64))
|
||||||
# Move this into custom types?
|
# Move this into custom types?
|
||||||
@@ -94,10 +94,12 @@ class BasicSubmission(Base):
|
|||||||
class BacterialCulture(BasicSubmission):
|
class BacterialCulture(BasicSubmission):
|
||||||
control = relationship("Control", back_populates="submissions") #: A control sample added to submission
|
control = relationship("Control", back_populates="submissions") #: A control sample added to submission
|
||||||
control_id = Column(INTEGER, ForeignKey("_control_samples.id", ondelete="SET NULL", name="fk_BC_control_id"))
|
control_id = Column(INTEGER, ForeignKey("_control_samples.id", ondelete="SET NULL", name="fk_BC_control_id"))
|
||||||
|
samples = relationship("BCSample", back_populates="rsl_plate", uselist=True)
|
||||||
|
# bc_sample_id = Column(INTEGER, ForeignKey("_bc_samples.id", ondelete="SET NULL", name="fk_BC_sample_id"))
|
||||||
__mapper_args__ = {"polymorphic_identity": "bacterial_culture", "polymorphic_load": "inline"}
|
__mapper_args__ = {"polymorphic_identity": "bacterial_culture", "polymorphic_load": "inline"}
|
||||||
|
|
||||||
|
|
||||||
class Wastewater(BasicSubmission):
|
class Wastewater(BasicSubmission):
|
||||||
samples = relationship("Sample", back_populates="rsl_plate")
|
samples = relationship("WWSample", back_populates="rsl_plate", uselist=True)
|
||||||
sample_id = Column(String, ForeignKey("_ww_samples.id", ondelete="SET NULL", name="fk_WW_sample_id"))
|
# ww_sample_id = Column(String, ForeignKey("_ww_samples.id", ondelete="SET NULL", name="fk_WW_sample_id"))
|
||||||
__mapper_args__ = {"polymorphic_identity": "wastewater", "polymorphic_load": "inline"}
|
__mapper_args__ = {"polymorphic_identity": "wastewater", "polymorphic_load": "inline"}
|
||||||
0
src/submissions/backend/excel/__init__.py
Normal file
0
src/submissions/backend/excel/__init__.py
Normal file
@@ -1,9 +1,12 @@
|
|||||||
import pandas as pd
|
import pandas as pd
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from backend.db.models.samples import WWSample, BCSample
|
||||||
import logging
|
import logging
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import re
|
import re
|
||||||
|
import numpy as np
|
||||||
|
from datetime import date
|
||||||
|
import uuid
|
||||||
|
|
||||||
logger = logging.getLogger(f"submissions.{__name__}")
|
logger = logging.getLogger(f"submissions.{__name__}")
|
||||||
|
|
||||||
@@ -21,8 +24,8 @@ class SheetParser(object):
|
|||||||
self.xl = None
|
self.xl = None
|
||||||
self.sub = OrderedDict()
|
self.sub = OrderedDict()
|
||||||
self.sub['submission_type'] = self._type_decider()
|
self.sub['submission_type'] = self._type_decider()
|
||||||
parse = getattr(self, f"_parse_{self.sub['submission_type'].lower()}")
|
parse_sub = getattr(self, f"_parse_{self.sub['submission_type'].lower()}")
|
||||||
parse()
|
parse_sub()
|
||||||
|
|
||||||
def _type_decider(self):
|
def _type_decider(self):
|
||||||
try:
|
try:
|
||||||
@@ -46,6 +49,7 @@ class SheetParser(object):
|
|||||||
self.sub['submitting_lab'] = submission_info.iloc[0][3]
|
self.sub['submitting_lab'] = submission_info.iloc[0][3]
|
||||||
self.sub['sample_count'] = str(submission_info.iloc[2][3])
|
self.sub['sample_count'] = str(submission_info.iloc[2][3])
|
||||||
self.sub['extraction_kit'] = submission_info.iloc[3][3]
|
self.sub['extraction_kit'] = submission_info.iloc[3][3]
|
||||||
|
|
||||||
return submission_info
|
return submission_info
|
||||||
|
|
||||||
|
|
||||||
@@ -71,7 +75,10 @@ class SheetParser(object):
|
|||||||
self.sub['lot_ethanol'] = submission_info.iloc[10][6]
|
self.sub['lot_ethanol'] = submission_info.iloc[10][6]
|
||||||
self.sub['lot_positive_control'] = submission_info.iloc[103][1]
|
self.sub['lot_positive_control'] = submission_info.iloc[103][1]
|
||||||
self.sub['lot_plate'] = submission_info.iloc[12][6]
|
self.sub['lot_plate'] = submission_info.iloc[12][6]
|
||||||
|
sample_parser = SampleParser(submission_info.iloc[15:111])
|
||||||
|
sample_parse = getattr(sample_parser, f"parse_{self.sub['submission_type'].lower()}_samples")
|
||||||
|
self.sub['samples'] = sample_parse()
|
||||||
|
|
||||||
|
|
||||||
def _parse_wastewater(self):
|
def _parse_wastewater(self):
|
||||||
# submission_info = self.xl.parse("WW Submissions (ENTER HERE)")
|
# submission_info = self.xl.parse("WW Submissions (ENTER HERE)")
|
||||||
@@ -102,6 +109,9 @@ class SheetParser(object):
|
|||||||
self.sub['lot_pre_mix_2'] = qprc_info.iloc[2][14]
|
self.sub['lot_pre_mix_2'] = qprc_info.iloc[2][14]
|
||||||
self.sub['lot_positive_control'] = qprc_info.iloc[3][14]
|
self.sub['lot_positive_control'] = qprc_info.iloc[3][14]
|
||||||
self.sub['lot_ddh2o'] = qprc_info.iloc[4][14]
|
self.sub['lot_ddh2o'] = qprc_info.iloc[4][14]
|
||||||
|
sample_parser = SampleParser(submission_info.iloc[16:40])
|
||||||
|
sample_parse = getattr(sample_parser, f"parse_{self.sub['submission_type'].lower()}_samples")
|
||||||
|
self.sub['samples'] = sample_parse()
|
||||||
# tech = str(submission_info.iloc[11][1])
|
# tech = str(submission_info.iloc[11][1])
|
||||||
# if tech == "nan":
|
# if tech == "nan":
|
||||||
# tech = "Unknown"
|
# tech = "Unknown"
|
||||||
@@ -119,4 +129,60 @@ class SheetParser(object):
|
|||||||
# self.sub['lot_isopropanol'] = submission_info.iloc[9][6]
|
# self.sub['lot_isopropanol'] = submission_info.iloc[9][6]
|
||||||
# self.sub['lot_ethanol'] = submission_info.iloc[10][6]
|
# self.sub['lot_ethanol'] = submission_info.iloc[10][6]
|
||||||
# self.sub['lot_positive_control'] = None #submission_info.iloc[103][1]
|
# self.sub['lot_positive_control'] = None #submission_info.iloc[103][1]
|
||||||
# self.sub['lot_plate'] = submission_info.iloc[12][6]
|
# self.sub['lot_plate'] = submission_info.iloc[12][6]
|
||||||
|
|
||||||
|
|
||||||
|
class SampleParser(object):
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, df:pd.DataFrame) -> None:
|
||||||
|
self.samples = df.to_dict("records")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_bacterial_culture_samples(self) -> list[BCSample]:
|
||||||
|
new_list = []
|
||||||
|
for sample in self.samples:
|
||||||
|
new = BCSample()
|
||||||
|
new.well_number = sample['This section to be filled in completely by submittor']
|
||||||
|
new.sample_id = sample['Unnamed: 1']
|
||||||
|
new.organism = sample['Unnamed: 2']
|
||||||
|
new.concentration = sample['Unnamed: 3']
|
||||||
|
print(f"Sample object: {new.sample_id} = {type(new.sample_id)}")
|
||||||
|
try:
|
||||||
|
not_a_nan = not np.isnan(new.sample_id)
|
||||||
|
except TypeError:
|
||||||
|
not_a_nan = True
|
||||||
|
if not_a_nan:
|
||||||
|
new_list.append(new)
|
||||||
|
return new_list
|
||||||
|
|
||||||
|
|
||||||
|
def parse_wastewater_samples(self) -> list[WWSample]:
|
||||||
|
new_list = []
|
||||||
|
for sample in self.samples:
|
||||||
|
new = WWSample()
|
||||||
|
new.ww_processing_num = sample['Unnamed: 2']
|
||||||
|
try:
|
||||||
|
not_a_nan = not np.isnan(sample['Unnamed: 3'])
|
||||||
|
except TypeError:
|
||||||
|
not_a_nan = True
|
||||||
|
if not_a_nan:
|
||||||
|
new.ww_sample_full_id = sample['Unnamed: 3']
|
||||||
|
else:
|
||||||
|
new.ww_sample_full_id = uuid.uuid4().hex.upper()
|
||||||
|
new.rsl_number = sample['Unnamed: 9']
|
||||||
|
try:
|
||||||
|
not_a_nan = not np.isnan(sample['Unnamed: 5'])
|
||||||
|
except TypeError:
|
||||||
|
not_a_nan = True
|
||||||
|
if not_a_nan:
|
||||||
|
new.collection_date = sample['Unnamed: 5']
|
||||||
|
else:
|
||||||
|
new.collection_date = date.today()
|
||||||
|
new.testing_type = sample['Unnamed: 6']
|
||||||
|
new.site_status = sample['Unnamed: 7']
|
||||||
|
new.notes = str(sample['Unnamed: 8'])
|
||||||
|
new.well_number = sample['Unnamed: 1']
|
||||||
|
new_list.append(new)
|
||||||
|
return new_list
|
||||||
|
|
||||||
@@ -90,6 +90,7 @@ class App(QMainWindow):
|
|||||||
|
|
||||||
def importSubmission(self):
|
def importSubmission(self):
|
||||||
logger.debug(self.ctx)
|
logger.debug(self.ctx)
|
||||||
|
self.samples = []
|
||||||
home_dir = str(Path(self.ctx["directory_path"]))
|
home_dir = str(Path(self.ctx["directory_path"]))
|
||||||
fname = Path(QFileDialog.getOpenFileName(self, 'Open file', home_dir)[0])
|
fname = Path(QFileDialog.getOpenFileName(self, 'Open file', home_dir)[0])
|
||||||
logger.debug(f"Attempting to parse file: {fname}")
|
logger.debug(f"Attempting to parse file: {fname}")
|
||||||
@@ -107,27 +108,31 @@ class App(QMainWindow):
|
|||||||
(?P<extraction_kit>^extraction_kit$) |
|
(?P<extraction_kit>^extraction_kit$) |
|
||||||
(?P<submitted_date>^submitted_date$) |
|
(?P<submitted_date>^submitted_date$) |
|
||||||
(?P<submitting_lab>)^submitting_lab$ |
|
(?P<submitting_lab>)^submitting_lab$ |
|
||||||
|
(?P<samples>)^samples$ |
|
||||||
(?P<reagent>^lot_.*$)
|
(?P<reagent>^lot_.*$)
|
||||||
|
|
||||||
""", re.VERBOSE)
|
""", re.VERBOSE)
|
||||||
for item in prsr.sub:
|
for item in prsr.sub:
|
||||||
logger.debug(f"Item: {item}")
|
logger.debug(f"Item: {item}")
|
||||||
self.table_widget.formlayout.addWidget(QLabel(item.replace("_", " ").title()))
|
|
||||||
try:
|
try:
|
||||||
mo = variable_parser.fullmatch(item).lastgroup
|
mo = variable_parser.fullmatch(item).lastgroup
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
mo = "other"
|
mo = "other"
|
||||||
|
print(f"Mo: {mo}")
|
||||||
match mo:
|
match mo:
|
||||||
case 'submitting_lab':
|
case 'submitting_lab':
|
||||||
|
self.table_widget.formlayout.addWidget(QLabel(item.replace("_", " ").title()))
|
||||||
print(f"{item}: {prsr.sub[item]}")
|
print(f"{item}: {prsr.sub[item]}")
|
||||||
add_widget = QComboBox()
|
add_widget = QComboBox()
|
||||||
labs = [item.__str__() for item in lookup_all_orgs(ctx=self.ctx)]
|
labs = [item.__str__() for item in lookup_all_orgs(ctx=self.ctx)]
|
||||||
try:
|
try:
|
||||||
labs = difflib.get_close_matches(prsr.sub[item], labs, len(labs), 0)
|
labs = difflib.get_close_matches(prsr.sub[item], labs, len(labs), 0)
|
||||||
except TypeError:
|
except (TypeError, ValueError):
|
||||||
pass
|
pass
|
||||||
add_widget.addItems(labs)
|
add_widget.addItems(labs)
|
||||||
case 'extraction_kit':
|
case 'extraction_kit':
|
||||||
|
self.table_widget.formlayout.addWidget(QLabel(item.replace("_", " ").title()))
|
||||||
if prsr.sub[item] == 'nan':
|
if prsr.sub[item] == 'nan':
|
||||||
msg = QMessageBox()
|
msg = QMessageBox()
|
||||||
# msg.setIcon(QMessageBox.critical)
|
# msg.setIcon(QMessageBox.critical)
|
||||||
@@ -143,10 +148,12 @@ class App(QMainWindow):
|
|||||||
else:
|
else:
|
||||||
add_widget.addItems(['bacterial_culture'])
|
add_widget.addItems(['bacterial_culture'])
|
||||||
case 'submitted_date':
|
case 'submitted_date':
|
||||||
|
self.table_widget.formlayout.addWidget(QLabel(item.replace("_", " ").title()))
|
||||||
add_widget = QDateEdit(calendarPopup=True)
|
add_widget = QDateEdit(calendarPopup=True)
|
||||||
# add_widget.setDateTime(QDateTime.date(prsr.sub[item]))
|
# add_widget.setDateTime(QDateTime.date(prsr.sub[item]))
|
||||||
add_widget.setDate(prsr.sub[item])
|
add_widget.setDate(prsr.sub[item])
|
||||||
case 'reagent':
|
case 'reagent':
|
||||||
|
self.table_widget.formlayout.addWidget(QLabel(item.replace("_", " ").title()))
|
||||||
add_widget = QComboBox()
|
add_widget = QComboBox()
|
||||||
add_widget.setEditable(True)
|
add_widget.setEditable(True)
|
||||||
# Ensure that all reagenttypes have a name that matches the items in the excel parser
|
# Ensure that all reagenttypes have a name that matches the items in the excel parser
|
||||||
@@ -169,7 +176,12 @@ class App(QMainWindow):
|
|||||||
relevant_reagents.insert(0, str(prsr.sub[item]))
|
relevant_reagents.insert(0, str(prsr.sub[item]))
|
||||||
logger.debug(f"Relevant reagents: {relevant_reagents}")
|
logger.debug(f"Relevant reagents: {relevant_reagents}")
|
||||||
add_widget.addItems(relevant_reagents)
|
add_widget.addItems(relevant_reagents)
|
||||||
|
# TODO: make samples not appear in frame.
|
||||||
|
case 'samples':
|
||||||
|
print(f"{item}: {prsr.sub[item]}")
|
||||||
|
self.samples = prsr.sub[item]
|
||||||
case _:
|
case _:
|
||||||
|
self.table_widget.formlayout.addWidget(QLabel(item.replace("_", " ").title()))
|
||||||
add_widget = QLineEdit()
|
add_widget = QLineEdit()
|
||||||
add_widget.setText(str(prsr.sub[item]).replace("_", " "))
|
add_widget.setText(str(prsr.sub[item]).replace("_", " "))
|
||||||
self.table_widget.formlayout.addWidget(add_widget)
|
self.table_widget.formlayout.addWidget(add_widget)
|
||||||
@@ -215,6 +227,7 @@ class App(QMainWindow):
|
|||||||
if wanted_reagent != None:
|
if wanted_reagent != None:
|
||||||
parsed_reagents.append(wanted_reagent)
|
parsed_reagents.append(wanted_reagent)
|
||||||
logger.debug(info)
|
logger.debug(info)
|
||||||
|
info['samples'] = self.samples
|
||||||
base_submission = construct_submission_info(ctx=self.ctx, info_dict=info)
|
base_submission = construct_submission_info(ctx=self.ctx, info_dict=info)
|
||||||
for reagent in parsed_reagents:
|
for reagent in parsed_reagents:
|
||||||
base_submission.reagents.append(reagent)
|
base_submission.reagents.append(reagent)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from PyQt6.QtWidgets import (
|
|||||||
QDialogButtonBox, QDateEdit, QTableView,
|
QDialogButtonBox, QDateEdit, QTableView,
|
||||||
QTextEdit, QSizePolicy, QWidget,
|
QTextEdit, QSizePolicy, QWidget,
|
||||||
QGridLayout, QPushButton, QSpinBox,
|
QGridLayout, QPushButton, QSpinBox,
|
||||||
QScrollBar
|
QScrollBar, QScrollArea
|
||||||
)
|
)
|
||||||
from PyQt6.QtCore import Qt, QDate, QAbstractTableModel
|
from PyQt6.QtCore import Qt, QDate, QAbstractTableModel
|
||||||
from PyQt6.QtGui import QFontMetrics
|
from PyQt6.QtGui import QFontMetrics
|
||||||
@@ -135,6 +135,7 @@ class SubmissionsSheet(QTableView):
|
|||||||
# print(index)
|
# print(index)
|
||||||
value=index.sibling(index.row(),0).data()
|
value=index.sibling(index.row(),0).data()
|
||||||
dlg = SubmissionDetails(ctx=self.ctx, id=value)
|
dlg = SubmissionDetails(ctx=self.ctx, id=value)
|
||||||
|
# dlg.show()
|
||||||
if dlg.exec():
|
if dlg.exec():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -146,32 +147,42 @@ class SubmissionDetails(QDialog):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.setWindowTitle("Submission Details")
|
self.setWindowTitle("Submission Details")
|
||||||
|
interior = QScrollArea()
|
||||||
|
interior.setParent(self)
|
||||||
data = lookup_submission_by_id(ctx=ctx, id=id)
|
data = lookup_submission_by_id(ctx=ctx, id=id)
|
||||||
base_dict = data.to_dict()
|
base_dict = data.to_dict()
|
||||||
base_dict['reagents'] = [item.to_sub_dict() for item in data.reagents]
|
base_dict['reagents'] = [item.to_sub_dict() for item in data.reagents]
|
||||||
|
base_dict['samples'] = [item.to_sub_dict() for item in data.samples]
|
||||||
template = env.get_template("submission_details.txt")
|
template = env.get_template("submission_details.txt")
|
||||||
text = template.render(sub=base_dict)
|
text = template.render(sub=base_dict)
|
||||||
txt_field = QTextEdit(self)
|
txt_editor = QTextEdit(self)
|
||||||
txt_field.setReadOnly(True)
|
|
||||||
txt_field.document().setPlainText(text)
|
txt_editor.setReadOnly(True)
|
||||||
|
txt_editor.document().setPlainText(text)
|
||||||
font = txt_field.document().defaultFont()
|
|
||||||
|
font = txt_editor.document().defaultFont()
|
||||||
fontMetrics = QFontMetrics(font)
|
fontMetrics = QFontMetrics(font)
|
||||||
textSize = fontMetrics.size(0, txt_field.toPlainText())
|
textSize = fontMetrics.size(0, txt_editor.toPlainText())
|
||||||
|
|
||||||
w = textSize.width() + 10
|
w = textSize.width() + 10
|
||||||
h = textSize.height() + 10
|
h = textSize.height() + 10
|
||||||
txt_field.setMinimumSize(w, h)
|
txt_editor.setMinimumSize(w, h)
|
||||||
txt_field.setMaximumSize(w, h)
|
txt_editor.setMaximumSize(w, h)
|
||||||
txt_field.resize(w, h)
|
txt_editor.resize(w, h)
|
||||||
|
interior.resize(w,900)
|
||||||
# txt_field.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.MinimumExpanding)
|
# txt_field.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.MinimumExpanding)
|
||||||
QBtn = QDialogButtonBox.StandardButton.Ok
|
# QBtn = QDialogButtonBox.StandardButton.Ok
|
||||||
# self.buttonBox = QDialogButtonBox(QBtn)
|
# self.buttonBox = QDialogButtonBox(QBtn)
|
||||||
# self.buttonBox.accepted.connect(self.accept)
|
# self.buttonBox.accepted.connect(self.accept)
|
||||||
txt_field.setText(text)
|
txt_editor.setText(text)
|
||||||
|
# txt_editor.verticalScrollBar()
|
||||||
|
interior.setWidget(txt_editor)
|
||||||
self.layout = QVBoxLayout()
|
self.layout = QVBoxLayout()
|
||||||
self.layout.addWidget(txt_field)
|
self.setFixedSize(w, 900)
|
||||||
|
# self.layout.addWidget(txt_editor)
|
||||||
# self.layout.addStretch()
|
# self.layout.addStretch()
|
||||||
|
self.layout.addWidget(interior)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ReportDatePicker(QDialog):
|
class ReportDatePicker(QDialog):
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
|
|
||||||
|
|
||||||
{% for key, value in sub.items() if key != 'reagents' %}
|
{% for key, value in sub.items() if key != 'reagents' and key != 'samples' %}
|
||||||
{{ key }}: {{ value }}
|
{{ key }}: {{ value }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
Reagents:
|
Reagents:
|
||||||
{% for item in sub['reagents'] %}
|
{% for item in sub['reagents'] %}
|
||||||
{{ item['type'] }}: {{ item['lot'] }} (EXP: {{ item['expiry'] }})
|
{{ item['type'] }}: {{ item['lot'] }} (EXP: {{ item['expiry'] }})
|
||||||
|
{% endfor %}
|
||||||
|
Samples:
|
||||||
|
{% for item in sub['samples'] %}
|
||||||
|
{{ item['well'] }}: {{ item['name'] }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
Reference in New Issue
Block a user