Updated to proper json-ing.

This commit is contained in:
Landon Wark
2024-04-15 12:31:41 -05:00
parent 7c46578d21
commit 4e09913102
4 changed files with 29 additions and 21 deletions

View File

@@ -1,7 +1,8 @@
- [ ] Appending of qPCR results to WW not saving. Find out why. - [x] Update controls to NestedMutableJson
- [x] Appending of qPCR results to WW not saving. Find out why.
- Possibly due to immutable JSON? But... it's worked before... Right? - Possibly due to immutable JSON? But... it's worked before... Right?
- Based on research, if a top-level JSON field is not changed, SQLalchemy will not detect changes. - Based on research, if a top-level JSON field is not changed, SQLalchemy will not detect changes.
- May have to use a special class: [link](https://docs.sqlalchemy.org/en/14/orm/extensions/mutable.html#establishing-mutability-on-scalar-column-values) - Using sqlalchemy-json module seems to have helped.
- [ ] Add Bead basher and Assit to DB. - [ ] Add Bead basher and Assit to DB.
- [x] Artic not creating right plate name. - [x] Artic not creating right plate name.
- [ ] Merge BasicSubmission.find_subclasses and BasicSubmission.find_polymorphic_subclass - [ ] Merge BasicSubmission.find_subclasses and BasicSubmission.find_polymorphic_subclass

Binary file not shown.

View File

@@ -4,7 +4,8 @@ All control related models.
from __future__ import annotations from __future__ import annotations
from sqlalchemy import Column, String, TIMESTAMP, JSON, INTEGER, ForeignKey from sqlalchemy import Column, String, TIMESTAMP, JSON, INTEGER, ForeignKey
from sqlalchemy.orm import relationship, Query from sqlalchemy.orm import relationship, Query
import logging, json from sqlalchemy_json import NestedMutableJson
import logging
from operator import itemgetter from operator import itemgetter
from . import BaseClass from . import BaseClass
from tools import setup_lookup from tools import setup_lookup
@@ -60,9 +61,10 @@ class ControlType(BaseClass):
List[str]: list of subtypes available List[str]: list of subtypes available
""" """
# Get first instance since all should have same subtypes # Get first instance since all should have same subtypes
outs = self.instances[0] # outs = self.instances[0]
# Get mode of instance # Get mode of instance
jsoner = json.loads(getattr(outs, mode)) # jsoner = json.loads(getattr(outs, mode))
jsoner = getattr(self.instances[0], mode)
logger.debug(f"JSON out: {jsoner.keys()}") logger.debug(f"JSON out: {jsoner.keys()}")
try: try:
# Pick genera (all should have same subtypes) # Pick genera (all should have same subtypes)
@@ -82,9 +84,9 @@ class Control(BaseClass):
controltype = relationship("ControlType", back_populates="instances", foreign_keys=[parent_id]) #: reference to parent control type controltype = relationship("ControlType", back_populates="instances", foreign_keys=[parent_id]) #: reference to parent control type
name = Column(String(255), unique=True) #: Sample ID name = Column(String(255), unique=True) #: Sample ID
submitted_date = Column(TIMESTAMP) #: Date submitted to Robotics submitted_date = Column(TIMESTAMP) #: Date submitted to Robotics
contains = Column(JSON) #: unstructured hashes in contains.tsv for each organism contains = Column(NestedMutableJson) #: unstructured hashes in contains.tsv for each organism
matches = Column(JSON) #: unstructured hashes in matches.tsv for each organism matches = Column(NestedMutableJson) #: unstructured hashes in matches.tsv for each organism
kraken = Column(JSON) #: unstructured output from kraken_report kraken = Column(NestedMutableJson) #: unstructured output from kraken_report
submission_id = Column(INTEGER, ForeignKey("_basicsubmission.id")) #: parent submission id submission_id = Column(INTEGER, ForeignKey("_basicsubmission.id")) #: parent submission id
submission = relationship("BacterialCulture", back_populates="controls", foreign_keys=[submission_id]) #: parent submission submission = relationship("BacterialCulture", back_populates="controls", foreign_keys=[submission_id]) #: parent submission
refseq_version = Column(String(16)) #: version of refseq used in fastq parsing refseq_version = Column(String(16)) #: version of refseq used in fastq parsing
@@ -109,7 +111,8 @@ class Control(BaseClass):
""" """
# logger.debug("loading json string into dict") # logger.debug("loading json string into dict")
try: try:
kraken = json.loads(self.kraken) # kraken = json.loads(self.kraken)
kraken = self.kraken
except TypeError: except TypeError:
kraken = {} kraken = {}
# logger.debug("calculating kraken count total to use in percentage") # logger.debug("calculating kraken count total to use in percentage")
@@ -147,7 +150,8 @@ class Control(BaseClass):
output = [] output = []
# logger.debug("load json string for mode (i.e. contains, matches, kraken2)") # logger.debug("load json string for mode (i.e. contains, matches, kraken2)")
try: try:
data = json.loads(getattr(self, mode)) # data = json.loads(getattr(self, mode))
data = self.__getattribute__(mode)
except TypeError: except TypeError:
data = {} data = {}
logger.debug(f"Length of data: {len(data)}") logger.debug(f"Length of data: {len(data)}")

View File

@@ -11,22 +11,24 @@ from reportlab.graphics.shapes import Drawing
from reportlab.lib.units import mm from reportlab.lib.units import mm
from operator import attrgetter, itemgetter from operator import attrgetter, itemgetter
from pprint import pformat from pprint import pformat
from . import Reagent, SubmissionType, KitType, Organization from . import BaseClass, Reagent, SubmissionType, KitType, Organization
# MutableDict and JSONEncodedDict are custom classes designed to get around JSON columns not being updated.
# See: https://docs.sqlalchemy.org/en/14/orm/extensions/mutable.html#establishing-mutability-on-scalar-column-values
from sqlalchemy import Column, String, TIMESTAMP, INTEGER, ForeignKey, JSON, FLOAT, case from sqlalchemy import Column, String, TIMESTAMP, INTEGER, ForeignKey, JSON, FLOAT, case
from sqlalchemy.orm import relationship, validates, Query from sqlalchemy.orm import relationship, validates, Query
from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy_json import NestedMutableJson
from sqlalchemy.exc import OperationalError as AlcOperationalError, IntegrityError as AlcIntegrityError, StatementError
from sqlite3 import OperationalError as SQLOperationalError, IntegrityError as SQLIntegrityError
import pandas as pd import pandas as pd
from openpyxl import Workbook from openpyxl import Workbook
from openpyxl.worksheet.worksheet import Worksheet from openpyxl.worksheet.worksheet import Worksheet
from openpyxl.drawing.image import Image as OpenpyxlImage from openpyxl.drawing.image import Image as OpenpyxlImage
from . import BaseClass
from tools import check_not_nan, row_map, setup_lookup, jinja_template_loading, rreplace from tools import check_not_nan, row_map, setup_lookup, jinja_template_loading, rreplace
from datetime import datetime, date from datetime import datetime, date
from typing import List, Any, Tuple from typing import List, Any, Tuple
from dateutil.parser import parse from dateutil.parser import parse
from dateutil.parser._parser import ParserError from dateutil.parser._parser import ParserError
from sqlalchemy.exc import OperationalError as AlcOperationalError, IntegrityError as AlcIntegrityError, StatementError
from sqlite3 import OperationalError as SQLOperationalError, IntegrityError as SQLIntegrityError
from pathlib import Path from pathlib import Path
from jinja2.exceptions import TemplateNotFound from jinja2.exceptions import TemplateNotFound
from jinja2 import Template from jinja2 import Template
@@ -52,10 +54,10 @@ class BasicSubmission(BaseClass):
# Move this into custom types? # 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("_reagent.id", ondelete="SET NULL", name="fk_BS_reagents_id")) #: id of used reagents reagents_id = Column(String, ForeignKey("_reagent.id", ondelete="SET NULL", name="fk_BS_reagents_id")) #: id of used reagents
extraction_info = Column(JSON) #: unstructured output from the extraction table logger. extraction_info = Column(NestedMutableJson) #: unstructured output from the extraction table logger.
run_cost = Column(FLOAT(2)) #: total cost of running the plate. Set from constant and mutable kit costs at time of creation. run_cost = Column(FLOAT(2)) #: total cost of running the plate. Set from constant and mutable kit costs at time of creation.
uploaded_by = Column(String(32)) #: user name of person who submitted the submission to the database. uploaded_by = Column(String(32)) #: user name of person who submitted the submission to the database.
comment = Column(JSON) #: user notes comment = Column(NestedMutableJson) #: user notes
submission_category = Column(String(64)) #: ["Research", "Diagnostic", "Surveillance", "Validation"], else defaults to submission_type_name submission_category = Column(String(64)) #: ["Research", "Diagnostic", "Surveillance", "Validation"], else defaults to submission_type_name
submission_sample_associations = relationship( submission_sample_associations = relationship(
@@ -1141,7 +1143,8 @@ class Wastewater(BasicSubmission):
id = Column(INTEGER, ForeignKey('_basicsubmission.id'), primary_key=True) id = Column(INTEGER, ForeignKey('_basicsubmission.id'), primary_key=True)
ext_technician = Column(String(64)) #: Name of technician doing extraction ext_technician = Column(String(64)) #: Name of technician doing extraction
pcr_technician = Column(String(64)) #: Name of technician doing pcr pcr_technician = Column(String(64)) #: Name of technician doing pcr
pcr_info = Column(JSON) #: unstructured output from pcr table logger or user(Artic) # pcr_info = Column(JSON) #: unstructured output from pcr table logger or user(Artic)
pcr_info = Column(NestedMutableJson)
__mapper_args__ = __mapper_args__ = dict(polymorphic_identity="Wastewater", __mapper_args__ = __mapper_args__ = dict(polymorphic_identity="Wastewater",
polymorphic_load="inline", polymorphic_load="inline",
@@ -1331,10 +1334,10 @@ class WastewaterArtic(BasicSubmission):
id = Column(INTEGER, ForeignKey('_basicsubmission.id'), primary_key=True) id = Column(INTEGER, ForeignKey('_basicsubmission.id'), primary_key=True)
artic_technician = Column(String(64)) #: Name of technician performing artic artic_technician = Column(String(64)) #: Name of technician performing artic
dna_core_submission_number = Column(String(64)) #: Number used by core as id dna_core_submission_number = Column(String(64)) #: Number used by core as id
pcr_info = Column(JSON) #: unstructured output from pcr table logger or user(Artic) pcr_info = Column(NestedMutableJson) #: unstructured output from pcr table logger or user(Artic)
gel_image = Column(String(64)) #: file name of gel image in zip file gel_image = Column(String(64)) #: file name of gel image in zip file
gel_info = Column(JSON) #: unstructured data from gel. gel_info = Column(NestedMutableJson) #: unstructured data from gel.
source_plates = Column(JSON) #: wastewater plates that samples come from source_plates = Column(NestedMutableJson) #: wastewater plates that samples come from
__mapper_args__ = dict(polymorphic_identity="Wastewater Artic", __mapper_args__ = dict(polymorphic_identity="Wastewater Artic",
polymorphic_load="inline", polymorphic_load="inline",
@@ -2339,7 +2342,7 @@ class WastewaterAssociation(SubmissionSampleAssociation):
ct_n2 = Column(FLOAT(2)) #: AKA ct for N2 ct_n2 = Column(FLOAT(2)) #: AKA ct for N2
n1_status = Column(String(32)) #: positive or negative for N1 n1_status = Column(String(32)) #: positive or negative for N1
n2_status = Column(String(32)) #: positive or negative for N2 n2_status = Column(String(32)) #: positive or negative for N2
pcr_results = Column(JSON) #: imported PCR status from QuantStudio pcr_results = Column(NestedMutableJson) #: imported PCR status from QuantStudio
__mapper_args__ = dict(polymorphic_identity="Wastewater Association", __mapper_args__ = dict(polymorphic_identity="Wastewater Association",
polymorphic_load="inline", polymorphic_load="inline",