Everything working pretty well.
This commit is contained in:
1
TODO.md
1
TODO.md
@@ -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)
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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]:
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
|
||||||
@@ -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.
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -642,6 +642,13 @@ 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]
|
||||||
|
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_item['value'] = v['value']
|
||||||
new_info.append(new_item)
|
new_info.append(new_item)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user