Minor bug fixes.
This commit is contained in:
@@ -4,9 +4,8 @@ All control related models.
|
||||
from __future__ import annotations
|
||||
from sqlalchemy import Column, String, TIMESTAMP, JSON, INTEGER, ForeignKey
|
||||
from sqlalchemy.orm import relationship, Query
|
||||
import logging
|
||||
import logging, json
|
||||
from operator import itemgetter
|
||||
import json
|
||||
from . import BaseClass
|
||||
from tools import setup_lookup, query_return
|
||||
from datetime import date, datetime
|
||||
@@ -51,6 +50,26 @@ class ControlType(BaseClass):
|
||||
pass
|
||||
return query_return(query=query, limit=limit)
|
||||
|
||||
def get_subtypes(self, mode:str) -> List[str]:
|
||||
"""
|
||||
Get subtypes associated with this controltype
|
||||
|
||||
Args:
|
||||
mode (str): analysis mode name
|
||||
|
||||
Returns:
|
||||
List[str]: list of subtypes available
|
||||
"""
|
||||
outs = self.instances[0]
|
||||
jsoner = json.loads(getattr(outs, mode))
|
||||
logger.debug(f"JSON out: {jsoner.keys()}")
|
||||
try:
|
||||
genera = list(jsoner.keys())[0]
|
||||
except IndexError:
|
||||
return []
|
||||
subtypes = [item for item in jsoner[genera] if "_hashes" not in item and "_ratio" not in item]
|
||||
return subtypes
|
||||
|
||||
class Control(BaseClass):
|
||||
"""
|
||||
Base class of a control sample.
|
||||
@@ -249,4 +268,3 @@ class Control(BaseClass):
|
||||
def save(self):
|
||||
self.__database_session__.add(self)
|
||||
self.__database_session__.commit()
|
||||
|
||||
|
||||
@@ -183,15 +183,6 @@ class ReagentType(BaseClass):
|
||||
# creator function: https://stackoverflow.com/questions/11091491/keyerror-when-adding-objects-to-sqlalchemy-association-object/11116291#11116291
|
||||
kit_types = association_proxy("reagenttype_kit_associations", "kit_type", creator=lambda kit: KitTypeReagentTypeAssociation(kit_type=kit))
|
||||
|
||||
# def __str__(self) -> str:
|
||||
# """
|
||||
# string representing this object
|
||||
|
||||
# Returns:
|
||||
# str: string representing this object's name
|
||||
# """
|
||||
# return self.name
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ReagentType({self.name})>"
|
||||
|
||||
@@ -379,7 +370,17 @@ class Reagent(BaseClass):
|
||||
name = Column(String(64)) #: reagent name
|
||||
lot = Column(String(64)) #: lot number of reagent
|
||||
expiry = Column(TIMESTAMP) #: expiry date - extended by eol_ext of parent programmatically
|
||||
submissions = relationship("BasicSubmission", back_populates="reagents", uselist=True) #: submissions this reagent is used in
|
||||
# submissions = relationship("BasicSubmission", back_populates="reagents", uselist=True) #: submissions this reagent is used in
|
||||
|
||||
reagent_submission_associations = relationship(
|
||||
"SubmissionReagentAssociation",
|
||||
back_populates="reagent",
|
||||
cascade="all, delete-orphan",
|
||||
) #: Relation to SubmissionSampleAssociation
|
||||
# association proxy of "user_keyword_associations" collection
|
||||
# to "keyword" attribute
|
||||
submissions = association_proxy("reagent_submission_associations", "submission") #: Association proxy to SubmissionSampleAssociation.samples
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
if self.name != None:
|
||||
@@ -706,3 +707,69 @@ class SubmissionTypeKitTypeAssociation(BaseClass):
|
||||
query = query.join(KitType).filter(KitType.id==kit_type)
|
||||
limit = query.count()
|
||||
return query_return(query=query, limit=limit)
|
||||
|
||||
class SubmissionReagentAssociation(BaseClass):
|
||||
|
||||
__tablename__ = "_reagents_submissions"
|
||||
|
||||
reagent_id = Column(INTEGER, ForeignKey("_reagents.id"), primary_key=True) #: id of associated sample
|
||||
submission_id = Column(INTEGER, ForeignKey("_submissions.id"), primary_key=True)
|
||||
comments = Column(String(1024))
|
||||
|
||||
submission = relationship("BasicSubmission", back_populates="submission_reagent_associations") #: associated submission
|
||||
|
||||
reagent = relationship(Reagent, back_populates="reagent_submission_associations")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.submission.rsl_plate_num}&{self.reagent.lot}>"
|
||||
|
||||
def __init__(self, reagent=None, submission=None):
|
||||
self.reagent = reagent
|
||||
self.submission = submission
|
||||
self.comments = ""
|
||||
|
||||
@classmethod
|
||||
@setup_lookup
|
||||
def query(cls,
|
||||
submission:"BasicSubmission"|str|int|None=None,
|
||||
reagent:Reagent|str|None=None,
|
||||
limit:int=0) -> SubmissionReagentAssociation|List[SubmissionReagentAssociation]:
|
||||
"""
|
||||
Lookup SubmissionReagentAssociations of interest.
|
||||
|
||||
Args:
|
||||
submission (BasicSubmission" | str | int | None, optional): Identifier of joined submission. Defaults to None.
|
||||
reagent (Reagent | str | None, optional): Identifier of joined reagent. Defaults to None.
|
||||
limit (int, optional): Maximum number of results to return (0 = all). Defaults to 0.
|
||||
|
||||
Returns:
|
||||
SubmissionReagentAssociation|List[SubmissionReagentAssociation]: SubmissionReagentAssociation(s) of interest
|
||||
"""
|
||||
from . import BasicSubmission
|
||||
query: Query = cls.__database_session__.query(cls)
|
||||
match reagent:
|
||||
case Reagent():
|
||||
query = query.filter(cls.reagent==reagent)
|
||||
case str():
|
||||
# logger.debug(f"Filtering query with reagent: {reagent}")
|
||||
reagent = Reagent.query(lot_number=reagent)
|
||||
query = query.filter(cls.reagent==reagent)
|
||||
# logger.debug([item.reagent.lot for item in query.all()])
|
||||
# query = query.join(Reagent).filter(Reagent.lot==reagent)
|
||||
case _:
|
||||
pass
|
||||
# logger.debug(f"Result of query after reagent: {query.all()}")
|
||||
match submission:
|
||||
case BasicSubmission():
|
||||
query = query.filter(cls.submission==submission)
|
||||
case str():
|
||||
query = query.join(BasicSubmission).filter(BasicSubmission.rsl_plate_num==submission)
|
||||
case int():
|
||||
query = query.join(BasicSubmission).filter(BasicSubmission.id==submission)
|
||||
case _:
|
||||
pass
|
||||
# logger.debug(f"Result of query after submission: {query.all()}")
|
||||
# limit = query.count()
|
||||
return query_return(query=query, limit=limit)
|
||||
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@ from getpass import getuser
|
||||
import math, json, logging, uuid, tempfile, re, yaml
|
||||
from pprint import pformat
|
||||
from . import Reagent, SubmissionType, KitType, Organization
|
||||
from sqlalchemy import Column, String, TIMESTAMP, INTEGER, ForeignKey, Table, JSON, FLOAT, case
|
||||
from sqlalchemy import Column, String, TIMESTAMP, INTEGER, ForeignKey, JSON, FLOAT, case
|
||||
from sqlalchemy.orm import relationship, validates, Query
|
||||
from json.decoder import JSONDecodeError
|
||||
from sqlalchemy.ext.associationproxy import association_proxy
|
||||
import pandas as pd
|
||||
from openpyxl import Workbook
|
||||
from . import Base, BaseClass
|
||||
from . import BaseClass
|
||||
from tools import check_not_nan, row_map, query_return, setup_lookup
|
||||
from datetime import datetime, date
|
||||
from typing import List
|
||||
@@ -24,15 +24,6 @@ from pathlib import Path
|
||||
|
||||
logger = logging.getLogger(f"submissions.{__name__}")
|
||||
|
||||
# table containing reagents/submission relationships
|
||||
reagents_submissions = Table(
|
||||
"_reagents_submissions",
|
||||
Base.metadata,
|
||||
Column("reagent_id", INTEGER, ForeignKey("_reagents.id")),
|
||||
Column("submission_id", INTEGER, ForeignKey("_submissions.id")),
|
||||
extend_existing = True
|
||||
)
|
||||
|
||||
class BasicSubmission(BaseClass):
|
||||
"""
|
||||
Concrete of basic submission which polymorphs into BacterialCulture and Wastewater
|
||||
@@ -51,7 +42,7 @@ class BasicSubmission(BaseClass):
|
||||
submission_type_name = Column(String, ForeignKey("_submission_types.name", ondelete="SET NULL", name="fk_BS_subtype_name")) #: name of joined submission type
|
||||
technician = Column(String(64)) #: initials of processing tech(s)
|
||||
# Move this into custom types?
|
||||
reagents = relationship("Reagent", back_populates="submissions", secondary=reagents_submissions) #: relationship to reagents
|
||||
# reagents = relationship("Reagent", back_populates="submissions", secondary=reagents_submissions) #: relationship to reagents
|
||||
reagents_id = Column(String, ForeignKey("_reagents.id", ondelete="SET NULL", name="fk_BS_reagents_id")) #: id of used reagents
|
||||
extraction_info = Column(JSON) #: unstructured output from the extraction table logger.
|
||||
pcr_info = Column(JSON) #: unstructured output from pcr table logger or user(Artic)
|
||||
@@ -69,6 +60,15 @@ class BasicSubmission(BaseClass):
|
||||
# to "keyword" attribute
|
||||
samples = association_proxy("submission_sample_associations", "sample") #: Association proxy to SubmissionSampleAssociation.samples
|
||||
|
||||
submission_reagent_associations = relationship(
|
||||
"SubmissionReagentAssociation",
|
||||
back_populates="submission",
|
||||
cascade="all, delete-orphan",
|
||||
) #: Relation to SubmissionSampleAssociation
|
||||
# association proxy of "user_keyword_associations" collection
|
||||
# to "keyword" attribute
|
||||
reagents = association_proxy("submission_reagent_associations", "reagent") #: Association proxy to SubmissionSampleAssociation.samples
|
||||
|
||||
# Allows for subclassing into ex. BacterialCulture, Wastewater, etc.
|
||||
__mapper_args__ = {
|
||||
"polymorphic_identity": "Basic Submission",
|
||||
@@ -438,6 +438,22 @@ class BasicSubmission(BaseClass):
|
||||
"""
|
||||
return "{{ rsl_plate_num }}"
|
||||
|
||||
@classmethod
|
||||
def submissions_to_df(cls, submission_type:str|None=None, limit:int=0) -> pd.DataFrame:
|
||||
logger.debug(f"Querying Type: {submission_type}")
|
||||
logger.debug(f"Using limit: {limit}")
|
||||
# use lookup function to create list of dicts
|
||||
subs = [item.to_dict() for item in cls.query(submission_type=submission_type, limit=limit)]
|
||||
logger.debug(f"Got {len(subs)} submissions.")
|
||||
df = pd.DataFrame.from_records(subs)
|
||||
# Exclude sub information
|
||||
for item in ['controls', 'extraction_info', 'pcr_info', 'comment', 'comments', 'samples', 'reagents']:
|
||||
try:
|
||||
df = df.drop(item, axis=1)
|
||||
except:
|
||||
logger.warning(f"Couldn't drop '{item}' column from submissionsheet df.")
|
||||
return df
|
||||
|
||||
def set_attribute(self, key:str, value):
|
||||
"""
|
||||
Performs custom attribute setting based on values.
|
||||
@@ -479,6 +495,11 @@ class BasicSubmission(BaseClass):
|
||||
field_value = value
|
||||
case "ctx" | "csv" | "filepath":
|
||||
return
|
||||
case "comment":
|
||||
if value == "" or value == None or value == 'null':
|
||||
field_value = None
|
||||
else:
|
||||
field_value = dict(name="submitter", text=value, time=datetime.now())
|
||||
case _:
|
||||
field_value = value
|
||||
# insert into field
|
||||
@@ -595,7 +616,8 @@ class BasicSubmission(BaseClass):
|
||||
start_date:date|str|int|None=None,
|
||||
end_date:date|str|int|None=None,
|
||||
reagent:Reagent|str|None=None,
|
||||
chronologic:bool=False, limit:int=0,
|
||||
chronologic:bool=False,
|
||||
limit:int=0,
|
||||
**kwargs
|
||||
) -> BasicSubmission | List[BasicSubmission]:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user