Everything working pretty well.

This commit is contained in:
Landon Wark
2024-02-20 14:36:58 -06:00
parent a7e915995e
commit 1e711149f1
14 changed files with 62 additions and 37 deletions

View File

@@ -1,3 +1,4 @@
- [ ] Create Tips ... *sigh*.
- [x] Create platemap image from html for export to pdf. - [x] Create platemap image from html for export to pdf.
- [x] Move plate map maker to submission. - [x] Move plate map maker to submission.
- [x] Finish Equipment Parser (add in regex to id asset_number) - [x] Finish Equipment Parser (add in regex to id asset_number)

View File

@@ -4,7 +4,7 @@ from pathlib import Path
# Version of the realpython-reader package # Version of the realpython-reader package
__project__ = "submissions" __project__ = "submissions"
__version__ = "202402.2b" __version__ = "202402.3b"
__author__ = {"name":"Landon Wark", "email":"Landon.Wark@phac-aspc.gc.ca"} __author__ = {"name":"Landon Wark", "email":"Landon.Wark@phac-aspc.gc.ca"}
__copyright__ = "2022-2024, Government of Canada" __copyright__ = "2022-2024, Government of Canada"

View File

@@ -4,10 +4,10 @@ All kit and reagent related models
from __future__ import annotations from __future__ import annotations
from sqlalchemy import Column, String, TIMESTAMP, JSON, INTEGER, ForeignKey, Interval, Table, FLOAT, BLOB from sqlalchemy import Column, String, TIMESTAMP, JSON, INTEGER, ForeignKey, Interval, Table, FLOAT, BLOB
from sqlalchemy.orm import relationship, validates, Query from sqlalchemy.orm import relationship, validates, Query
from sqlalchemy.ext.associationproxy import association_proxy, AssociationProxy 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, Settings from tools import check_authorization, setup_lookup, Report, Result
from typing import List from typing import List
from pandas import ExcelFile from pandas import ExcelFile
from pathlib import Path from pathlib import Path
@@ -921,23 +921,32 @@ class SubmissionReagentAssociation(BaseClass):
from . import BasicSubmission from . import BasicSubmission
query: Query = cls.__database_session__.query(cls) query: Query = cls.__database_session__.query(cls)
match reagent: match reagent:
case Reagent(): case Reagent() | str():
# logger.debug(f"Lookup SubmissionReagentAssociation by reagent Reagent {reagent}") # logger.debug(f"Lookup SubmissionReagentAssociation by reagent Reagent {reagent}")
if isinstance(reagent, str):
reagent = Reagent.query(lot_number=reagent)
query = query.filter(cls.reagent==reagent) query = query.filter(cls.reagent==reagent)
case str(): # case str():
# logger.debug(f"Lookup SubmissionReagentAssociation by reagent str {reagent}") # logger.debug(f"Lookup SubmissionReagentAssociation by reagent str {reagent}")
query = query.join(Reagent).filter(Reagent.lot==reagent)
# query = query.filter(cls.reagent==reagent)
# logger.debug(f"Result: {query.all()}")
case _: case _:
pass pass
match submission: match submission:
case BasicSubmission(): case BasicSubmission() | str():
if isinstance(submission, str):
submission = BasicSubmission.query(rsl_number=submission)
# logger.debug(f"Lookup SubmissionReagentAssociation by submission BasicSubmission {submission}") # logger.debug(f"Lookup SubmissionReagentAssociation by submission BasicSubmission {submission}")
query = query.filter(cls.submission==submission) query = query.filter(cls.submission==submission)
case str(): # case str():
# logger.debug(f"Lookup SubmissionReagentAssociation by submission str {submission}") # logger.debug(f"Lookup SubmissionReagentAssociation by submission str {submission}")
query = query.join(BasicSubmission).filter(BasicSubmission.rsl_plate_num==submission) # submission = BasicSubmission.query(rsl_number=submission)
# query = query.filter(cls.submission==submission)
# logger.debug(f"Result: {query.all()}")
case int(): case int():
# logger.debug(f"Lookup SubmissionReagentAssociation by submission id {submission}") # logger.debug(f"Lookup SubmissionReagentAssociation by submission id {submission}")
submission = BasicSubmission.query(id=submission)
query = query.join(BasicSubmission).filter(BasicSubmission.id==submission) query = query.join(BasicSubmission).filter(BasicSubmission.id==submission)
case _: case _:
pass pass

View File

@@ -338,7 +338,7 @@ class BasicSubmission(BaseClass):
logger.debug(f"Got {len(subs)} submissions.") logger.debug(f"Got {len(subs)} submissions.")
df = pd.DataFrame.from_records(subs) df = pd.DataFrame.from_records(subs)
# Exclude sub information # Exclude sub information
for item in ['controls', 'extraction_info', 'pcr_info', 'comment', 'comments', 'samples', 'reagents', 'equipment']: for item in ['controls', 'extraction_info', 'pcr_info', 'comment', 'comments', 'samples', 'reagents', 'equipment', 'gel_info', 'gel_image', 'dna_core_submission_number']:
try: try:
df = df.drop(item, axis=1) df = df.drop(item, axis=1)
except: except:
@@ -1068,7 +1068,21 @@ class BacterialCulture(BasicSubmission):
outstr = re.sub(r"BC(\d{6})", r"BC-\1", outstr, flags=re.IGNORECASE) outstr = re.sub(r"BC(\d{6})", r"BC-\1", outstr, flags=re.IGNORECASE)
except (AttributeError, TypeError) as e: except (AttributeError, TypeError) as e:
outstr = RSLNamer.construct_new_plate_name(data=data) outstr = RSLNamer.construct_new_plate_name(data=data)
return outstr try:
plate_number = re.search(r"(?:(-|_)\d)(?!\d)", outstr).group().strip("_").strip("-")
# logger.debug(f"Plate number is: {plate_number}")
except AttributeError as e:
plate_number = "1"
outstr = re.sub(r"(\d{8})(-|_)?\d?(R\d?)?", rf"\1-{plate_number}\3", outstr)
# logger.debug(f"After addition of plate number the plate name is: {outstr}")
try:
repeat = re.search(r"-\dR(?P<repeat>\d)?", outstr).groupdict()['repeat']
if repeat == None:
repeat = "1"
except AttributeError as e:
repeat = ""
return re.sub(r"(-\dR)\d?", rf"\1 {repeat}", outstr).replace(" ", "")
# return outstr
@classmethod @classmethod
def get_regex(cls) -> str: def get_regex(cls) -> str:
@@ -1078,7 +1092,7 @@ class BacterialCulture(BasicSubmission):
Returns: Returns:
str: string for regex construction str: string for regex construction
""" """
return "(?P<Bacterial_Culture>RSL(?:-|_)?BC(?:-|_)?20\d{2}-?\d{2}-?\d{2}(?:(_|-)?\d?([^_0123456789\s]|$)?R?\d?)?)" return "(?P<Bacterial_Culture>RSL(?:-|_)?BC(?:-|_)?20\d{2}-?\d{2}-?\d{2}(?:(_|-)?\d?([^_0123456789\sA-QS-Z]|$)?R?\d?)?)"
@classmethod @classmethod
def filename_template(cls): def filename_template(cls):
@@ -1244,7 +1258,7 @@ class Wastewater(BasicSubmission):
Returns: Returns:
str: String for regex construction str: String for regex construction
""" """
return "(?P<Wastewater>RSL(?:-|_)?WW(?:-|_)?20\d{2}-?\d{2}-?\d{2}(?:(_|-)?\d?([^_0123456789\s]|$)?R?\d?)?)" return "(?P<Wastewater>RSL(?:-|_)?WW(?:-|_)?20\d{2}-?\d{2}-?\d{2}(?:(_|-)?\d?([^_0123456789\sA-QS-Z]|$)?R?\d?)?)"
@classmethod @classmethod
def adjust_autofill_samples(cls, samples: List[Any]) -> List[Any]: def adjust_autofill_samples(cls, samples: List[Any]) -> List[Any]:

View File

@@ -601,5 +601,3 @@ class PCRParser(object):
self.pcr['plugin'] = df.iloc[19][1] self.pcr['plugin'] = df.iloc[19][1]
self.pcr['exported_on'] = df.iloc[20][1] self.pcr['exported_on'] = df.iloc[20][1]
self.pcr['imported_by'] = getuser() self.pcr['imported_by'] = getuser()

View File

@@ -32,7 +32,6 @@ def make_report_xlsx(records:list[dict]) -> Tuple[DataFrame, DataFrame]:
df = df.sort_values(['Submitting Lab', "Submitted Date"]) df = df.sort_values(['Submitting Lab', "Submitted Date"])
return df, df2 return df, df2
def make_report_html(df:DataFrame, start_date:date, end_date:date) -> str: def make_report_html(df:DataFrame, start_date:date, end_date:date) -> str:
""" """
@@ -74,7 +73,6 @@ def make_report_html(df:DataFrame, start_date:date, end_date:date) -> str:
html = temp.render(input=dicto) html = temp.render(input=dicto)
return html return html
def convert_data_list_to_df(input:list[dict], subtype:str|None=None) -> DataFrame: def convert_data_list_to_df(input:list[dict], subtype:str|None=None) -> DataFrame:
""" """
Convert list of control records to dataframe Convert list of control records to dataframe
@@ -104,7 +102,6 @@ def convert_data_list_to_df(input:list[dict], subtype:str|None=None) -> DataFram
df = df_column_renamer(df=df) df = df_column_renamer(df=df)
return df return df
def df_column_renamer(df:DataFrame) -> DataFrame: def df_column_renamer(df:DataFrame) -> DataFrame:
""" """
Ad hoc function I created to clarify some fields Ad hoc function I created to clarify some fields
@@ -123,7 +120,6 @@ def df_column_renamer(df:DataFrame) -> DataFrame:
"kraken_percent":"kraken2_read_percent_(top_50)" "kraken_percent":"kraken2_read_percent_(top_50)"
}) })
def displace_date(df:DataFrame) -> DataFrame: def displace_date(df:DataFrame) -> DataFrame:
""" """
This function serves to split samples that were submitted on the same date by incrementing dates. This function serves to split samples that were submitted on the same date by incrementing dates.
@@ -182,7 +178,6 @@ def check_date(df:DataFrame, item:dict, previous_dates:list) -> Tuple[DataFrame,
df, previous_dates = check_date(df, item, previous_dates) df, previous_dates = check_date(df, item, previous_dates)
return df, previous_dates return df, previous_dates
def get_unique_values_in_df_column(df: DataFrame, column_name: str) -> list: def get_unique_values_in_df_column(df: DataFrame, column_name: str) -> list:
""" """
get all unique values in a dataframe column by name get all unique values in a dataframe column by name
@@ -196,7 +191,6 @@ def get_unique_values_in_df_column(df: DataFrame, column_name: str) -> list:
""" """
return sorted(df[column_name].unique()) return sorted(df[column_name].unique())
def drop_reruns_from_df(ctx:Settings, df: DataFrame) -> DataFrame: def drop_reruns_from_df(ctx:Settings, df: DataFrame) -> DataFrame:
""" """
Removes semi-duplicates from dataframe after finding sequencing repeats. Removes semi-duplicates from dataframe after finding sequencing repeats.

View File

@@ -133,10 +133,10 @@ class RSLNamer(object):
else: else:
today = data['submitted_date'] today = data['submitted_date']
else: else:
today = re.search(r"\d{4}(_|-)?\d{2}(_|-)?\d{2}", data['rsl_plate_num'])
try: try:
today = re.search(r"\d{4}(_|-)?\d{2}(_|-)?\d{2}", data['rsl_plate_num'])
today = parse(today.group()) today = parse(today.group())
except AttributeError: except (AttributeError, KeyError):
today = datetime.now() today = datetime.now()
if "rsl_plate_num" in data.keys(): if "rsl_plate_num" in data.keys():
plate_number = data['rsl_plate_num'].split("-")[-1][0] plate_number = data['rsl_plate_num'].split("-")[-1][0]

View File

@@ -642,7 +642,14 @@ class PydSubmission(BaseModel, extra='allow'):
new_item = {} new_item = {}
new_item['type'] = k new_item['type'] = k
new_item['location'] = excel_map['info'][k] new_item['location'] = excel_map['info'][k]
new_item['value'] = v['value'] match k:
case "comment":
if v['value'] is not None:
new_item['value'] = "--".join([comment['text'] for comment in v['value']])
else:
new_item['value'] = None
case _:
new_item['value'] = v['value']
new_info.append(new_item) new_info.append(new_item)
except KeyError: except KeyError:
logger.error(f"Unable to fill in {k}, not found in relevant info.") logger.error(f"Unable to fill in {k}, not found in relevant info.")

View File

@@ -10,4 +10,8 @@ from .submission_table import *
from .submission_widget import * from .submission_widget import *
from .controls_chart import * from .controls_chart import *
from .kit_creator import * from .kit_creator import *
from .submission_details import *
from .equipment_usage import *
from .gel_checker import *
from .submission_type_creator import *
from .app import App from .app import App

View File

@@ -80,7 +80,6 @@ class EquipmentUsage(QDialog):
for object in self.parent().findChildren(QCheckBox): for object in self.parent().findChildren(QCheckBox):
object.setChecked(self.check.isChecked()) object.setChecked(self.check.isChecked())
# TODO: Figure out how this is working again
class RoleComboBox(QWidget): class RoleComboBox(QWidget):
def __init__(self, parent, role:PydEquipmentRole, used:list) -> None: def __init__(self, parent, role:PydEquipmentRole, used:list) -> None:

View File

@@ -1,12 +1,12 @@
""" """
Gel box for artic quality control Gel box for artic quality control
""" """
from PyQt6.QtWidgets import * from PyQt6.QtWidgets import (QWidget, QDialog, QGridLayout,
from PyQt6.QtWidgets import QWidget QLabel, QLineEdit, QDialogButtonBox
)
import numpy as np import numpy as np
import pyqtgraph as pg import pyqtgraph as pg
from PyQt6.QtGui import * from PyQt6.QtGui import QIcon
from PyQt6.QtCore import *
from PIL import Image from PIL import Image
import numpy as np import numpy as np
import logging import logging

View File

@@ -60,7 +60,6 @@ class SubmissionDetails(QDialog):
btn.setFixedWidth(900) btn.setFixedWidth(900)
btn.clicked.connect(self.export) btn.clicked.connect(self.export)
def export(self): def export(self):
""" """
Renders submission to html, then creates and saves .pdf file to user selected file. Renders submission to html, then creates and saves .pdf file to user selected file.

View File

@@ -9,7 +9,7 @@ import pandas as pd
from jinja2 import Environment, FileSystemLoader from jinja2 import Environment, FileSystemLoader
from logging import handlers from logging import handlers
from pathlib import Path from pathlib import Path
from sqlalchemy.orm import Query, Session from sqlalchemy.orm import Session
from sqlalchemy import create_engine from sqlalchemy import create_engine
from pydantic import field_validator, BaseModel, Field from pydantic import field_validator, BaseModel, Field
from pydantic_settings import BaseSettings, SettingsConfigDict from pydantic_settings import BaseSettings, SettingsConfigDict