From 816a0a45f85588b88b0f5091cce3d0feff1e9223 Mon Sep 17 00:00:00 2001 From: lwark Date: Fri, 1 Nov 2024 13:16:16 -0500 Subject: [PATCH] Update to add SQL Server support. --- CHANGELOG.md | 3 +- src/submissions/backend/db/__init__.py | 12 +- src/submissions/backend/db/models/__init__.py | 15 +- src/submissions/backend/db/models/controls.py | 149 +++++++++++++++--- src/submissions/backend/db/models/kits.py | 15 +- .../backend/db/models/submissions.py | 15 +- src/submissions/backend/excel/parser.py | 15 +- .../backend/validators/__init__.py | 10 +- src/submissions/backend/validators/pydant.py | 6 +- src/submissions/frontend/widgets/app.py | 2 +- .../frontend/widgets/controls_chart.py | 3 +- .../frontend/widgets/submission_details.py | 2 +- submissions.spec | 2 +- 13 files changed, 182 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a51282..1977cf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ -## 202410.05 +## 202411.01 - Code clean up. +- Improved flexibility of Irida chart for subtyping. ## 202410.03 diff --git a/src/submissions/backend/db/__init__.py b/src/submissions/backend/db/__init__.py index e8c9d98..1bd444d 100644 --- a/src/submissions/backend/db/__init__.py +++ b/src/submissions/backend/db/__init__.py @@ -18,8 +18,18 @@ def set_sqlite_pragma(dbapi_connection, connection_record): connection_record (_type_): _description_ """ cursor = dbapi_connection.cursor() + # print(ctx.database_schema) if ctx.database_schema == "sqlite": - cursor.execute("PRAGMA foreign_keys=ON") + execution_phrase = "PRAGMA foreign_keys=ON" + # cursor.execute(execution_phrase) + elif ctx.database_schema == "mssql+pyodbc": + execution_phrase = "SET IDENTITY_INSERT dbo._wastewater ON;" + else: + print("Nothing to execute, returning") + cursor.close() + return + print(f"Executing {execution_phrase} in sql.") + cursor.execute(execution_phrase) cursor.close() diff --git a/src/submissions/backend/db/models/__init__.py b/src/submissions/backend/db/models/__init__.py index f110a9a..004779b 100644 --- a/src/submissions/backend/db/models/__init__.py +++ b/src/submissions/backend/db/models/__init__.py @@ -94,20 +94,7 @@ class BaseClass(Base): Returns: dict | list | str: Output of key:value dict or single (list, str) desired variable """ - # if issubclass(cls, BaseClass) and cls.__name__ != "BaseClass": singles = list(set(cls.singles + BaseClass.singles)) - # else: - # singles = cls.singles - # output = dict(singles=singles) - # output = {} - # for k, v in dicto.items(): - # if len(args) > 0 and k not in args: - # # logger.debug(f"{k} not selected as being of interest.") - # continue - # else: - # output[k] = v - # if len(args) == 1: - # return output[args[0]] return dict(singles=singles) @classmethod @@ -197,10 +184,10 @@ class ConfigItem(BaseClass): ConfigItem|List[ConfigItem]: Config item(s) """ query = cls.__database_session__.query(cls) - # config_items = [item for item in config_items if item.key in args] match len(args): case 0: config_items = query.all() + # NOTE: If only one item sought, don't use a list, just return it. case 1: config_items = query.filter(cls.key == args[0]).first() case _: diff --git a/src/submissions/backend/db/models/controls.py b/src/submissions/backend/db/models/controls.py index dc5ab91..cb31e85 100644 --- a/src/submissions/backend/db/models/controls.py +++ b/src/submissions/backend/db/models/controls.py @@ -66,18 +66,18 @@ class ControlType(BaseClass): Returns: List[str]: list of subtypes available """ - # NOTE: Get first instance since all should have same subtypes - # NOTE: Get mode of instance if not self.instances: return + # NOTE: Get first instance since all should have same subtypes + # NOTE: Get mode of instance jsoner = getattr(self.instances[0], mode) try: # NOTE: Pick genera (all should have same subtypes) genera = list(jsoner.keys())[0] except IndexError: return [] - # NOTE: remove items that don't have relevant data - subtypes = [item for item in jsoner[genera] if "_hashes" not in item and "_ratio" not in item] + # NOTE subtypes now created for all modes, but ignored for all but allowed_for_subtyping later in the ControlsChart + subtypes = sorted(list(jsoner[genera].keys()), reverse=True) return subtypes def get_instance_class(self) -> Control: @@ -98,7 +98,7 @@ class ControlType(BaseClass): Generator[str, None, None]: Control types that have targets """ ct = cls.query(name=control_type).targets - return (item for item in ct.keys() if ct[item]) + return (k for k, v in ct.items() if v) @classmethod def build_positive_regex(cls, control_type:str) -> Pattern: @@ -141,6 +141,98 @@ class Control(BaseClass): def __repr__(self) -> str: return f"<{self.controltype_name}({self.name})>" + # @classmethod + # @setup_lookup + # def query(cls, + # submission_type: str | None = None, + # subtype: str | None = None, + # start_date: date | str | int | None = None, + # end_date: date | str | int | None = None, + # control_name: str | None = None, + # limit: int = 0, **kwargs + # ) -> Control | List[Control]: + # """ + # Lookup control objects in the database based on a number of parameters. + # + # Args: + # submission_type (str | None, optional): Control archetype. Defaults to None. + # start_date (date | str | int | None, optional): Beginning date to search by. Defaults to 2023-01-01 if end_date not None. + # end_date (date | str | int | None, optional): End date to search by. Defaults to today if start_date not None. + # control_name (str | None, optional): Name of control. Defaults to None. + # limit (int, optional): Maximum number of results to return (0 = all). Defaults to 0. + # + # Returns: + # PCRControl|List[PCRControl]: Control object of interest. + # """ + # from backend.db import SubmissionType + # query: Query = cls.__database_session__.query(cls) + # match submission_type: + # case str(): + # from backend import BasicSubmission, SubmissionType + # # logger.debug(f"Lookup controls by SubmissionType str: {submission_type}") + # query = query.join(BasicSubmission).join(SubmissionType).filter(SubmissionType.name == submission_type) + # case SubmissionType(): + # from backend import BasicSubmission + # # logger.debug(f"Lookup controls by SubmissionType: {submission_type}") + # query = query.join(BasicSubmission).filter(BasicSubmission.submission_type_name == submission_type.name) + # case _: + # pass + # # NOTE: by control type + # match subtype: + # case str(): + # if cls.__name__ == "Control": + # raise ValueError(f"Cannot query base class Control with subtype.") + # elif cls.__name__ == "IridaControl": + # query = query.filter(cls.subtype == subtype) + # else: + # try: + # query = query.filter(cls.subtype == subtype) + # except AttributeError as e: + # logger.error(e) + # case _: + # pass + # # NOTE: by date range + # if start_date is not None and end_date is None: + # logger.warning(f"Start date with no end date, using today.") + # end_date = date.today() + # if end_date is not None and start_date is None: + # logger.warning(f"End date with no start date, using 90 days ago.") + # # start_date = date(2023, 1, 1) + # start_date = date.today() - timedelta(days=90) + # if start_date is not None: + # match start_date: + # case date(): + # # logger.debug(f"Lookup control by start date({start_date})") + # start_date = start_date.strftime("%Y-%m-%d") + # case int(): + # # logger.debug(f"Lookup control by ordinal start date {start_date}") + # start_date = datetime.fromordinal( + # datetime(1900, 1, 1).toordinal() + start_date - 2).date().strftime("%Y-%m-%d") + # case _: + # # logger.debug(f"Lookup control with parsed start date {start_date}") + # start_date = parse(start_date).strftime("%Y-%m-%d") + # match end_date: + # case date(): + # # logger.debug(f"Lookup control by end date({end_date})") + # end_date = end_date.strftime("%Y-%m-%d") + # case int(): + # # logger.debug(f"Lookup control by ordinal end date {end_date}") + # end_date = datetime.fromordinal(datetime(1900, 1, 1).toordinal() + end_date - 2).date().strftime( + # "%Y-%m-%d") + # case _: + # # logger.debug(f"Lookup control with parsed end date {end_date}") + # end_date = parse(end_date).strftime("%Y-%m-%d") + # # logger.debug(f"Looking up BasicSubmissions from start date: {start_date} and end date: {end_date}") + # query = query.filter(cls.submitted_date.between(start_date, end_date)) + # match control_name: + # case str(): + # # logger.debug(f"Lookup control by name {control_name}") + # query = query.filter(cls.name.startswith(control_name)) + # limit = 1 + # case _: + # pass + # return cls.execute_query(query=query, limit=limit) + @classmethod def find_polymorphic_subclass(cls, polymorphic_identity: str | ControlType | None = None, attrs: dict | None = None) -> Control: @@ -155,10 +247,9 @@ class Control(BaseClass): Control: Subclass of interest. """ if isinstance(polymorphic_identity, dict): - # logger.debug(f"Controlling for dict value") polymorphic_identity = polymorphic_identity['value'] - if isinstance(polymorphic_identity, ControlType): - polymorphic_identity = polymorphic_identity.name + # if isinstance(polymorphic_identity, ControlType): + # polymorphic_identity = polymorphic_identity.name model = cls match polymorphic_identity: case str(): @@ -167,6 +258,12 @@ class Control(BaseClass): except Exception as e: logger.error( f"Could not get polymorph {polymorphic_identity} of {cls} due to {e}, falling back to BasicSubmission") + case ControlType(): + try: + model = cls.__mapper__.polymorphic_map[polymorphic_identity.name].class_ + except Exception as e: + logger.error( + f"Could not get polymorph {polymorphic_identity} of {cls} due to {e}, falling back to BasicSubmission") case _: pass if attrs and any([not hasattr(cls, attr) for attr in attrs.keys()]): @@ -228,7 +325,7 @@ class PCRControl(Control): @classmethod @setup_lookup def query(cls, - sub_type: str | None = None, + submission_type: str | None = None, start_date: date | str | int | None = None, end_date: date | str | int | None = None, control_name: str | None = None, @@ -238,7 +335,7 @@ class PCRControl(Control): Lookup control objects in the database based on a number of parameters. Args: - sub_type (str | None, optional): Control archetype. Defaults to None. + submission_type (str | None, optional): Control archetype. Defaults to None. start_date (date | str | int | None, optional): Beginning date to search by. Defaults to 2023-01-01 if end_date not None. end_date (date | str | int | None, optional): End date to search by. Defaults to today if start_date not None. control_name (str | None, optional): Name of control. Defaults to None. @@ -254,8 +351,9 @@ class PCRControl(Control): logger.warning(f"Start date with no end date, using today.") end_date = date.today() if end_date is not None and start_date is None: - logger.warning(f"End date with no start date, using Jan 1, 2023") - start_date = date(2023, 1, 1) + logger.warning(f"End date with no start date, using 90 days ago.") + # start_date = date(2023, 1, 1) + start_date = date.today() - timedelta(days=90) if start_date is not None: match start_date: case date(): @@ -281,15 +379,15 @@ class PCRControl(Control): end_date = parse(end_date).strftime("%Y-%m-%d") # logger.debug(f"Looking up BasicSubmissions from start date: {start_date} and end date: {end_date}") query = query.filter(cls.submitted_date.between(start_date, end_date)) - match sub_type: + match submission_type: case str(): from backend import BasicSubmission, SubmissionType - # logger.debug(f"Lookup controls by SubmissionType str: {sub_type}") - query = query.join(BasicSubmission).join(SubmissionType).filter(SubmissionType.name == sub_type) + # logger.debug(f"Lookup controls by SubmissionType str: {submission_type}") + query = query.join(BasicSubmission).join(SubmissionType).filter(SubmissionType.name == submission_type) case SubmissionType(): from backend import BasicSubmission - # logger.debug(f"Lookup controls by SubmissionType: {sub_type}") - query = query.join(BasicSubmission).filter(BasicSubmission.submission_type_name==sub_type.name) + # logger.debug(f"Lookup controls by SubmissionType: {submission_type}") + query = query.join(BasicSubmission).filter(BasicSubmission.submission_type_name==submission_type.name) case _: pass match control_name: @@ -319,7 +417,8 @@ class PCRControl(Control): parent.mode_typer.clear() parent.mode_typer.setEnabled(False) report = Report() - controls = cls.query(sub_type=chart_settings['sub_type'], start_date=chart_settings['start_date'], + logger.debug(f"Chart settings: {pformat(chart_settings)}") + controls = cls.query(submission_type=chart_settings['sub_type'], start_date=chart_settings['start_date'], end_date=chart_settings['end_date']) data = [control.to_sub_dict() for control in controls] df = DataFrame.from_records(data) @@ -332,11 +431,14 @@ class PCRControl(Control): class IridaControl(Control): + + subtyping_allowed = ['kraken'] + id = Column(INTEGER, ForeignKey('_control.id'), primary_key=True) contains = Column(JSON) #: unstructured hashes in contains.tsv for each organism matches = Column(JSON) #: unstructured hashes in matches.tsv for each organism kraken = Column(JSON) #: unstructured output from kraken_report - sub_type = Column(String(16), nullable=False) #: EN-NOS, MCS-NOS, etc + subtype = Column(String(16), nullable=False) #: EN-NOS, MCS-NOS, etc refseq_version = Column(String(16)) #: version of refseq used in fastq parsing kraken2_version = Column(String(16)) #: version of kraken2 used in fastq parsing kraken2_db_version = Column(String(32)) #: folder name of kraken2 db @@ -488,16 +590,17 @@ class IridaControl(Control): # NOTE: by control type match sub_type: case str(): - query = query.filter(cls.sub_type == sub_type) + query = query.filter(cls.subtype == sub_type) case _: pass - # NOTE: by date range + # NOTE: If one date exists, we need the other one to exist as well. if start_date is not None and end_date is None: logger.warning(f"Start date with no end date, using today.") end_date = date.today() if end_date is not None and start_date is None: - logger.warning(f"End date with no start date, using Jan 1, 2023") - start_date = date(2023, 1, 1) + logger.warning(f"End date with no start date, using 90 days ago.") + # start_date = date(2023, 1, 1) + start_date = date.today() - timedelta(days=90) if start_date is not None: match start_date: case date(): diff --git a/src/submissions/backend/db/models/kits.py b/src/submissions/backend/db/models/kits.py index 9191513..d8b3e20 100644 --- a/src/submissions/backend/db/models/kits.py +++ b/src/submissions/backend/db/models/kits.py @@ -2,16 +2,12 @@ All kit and reagent related models """ from __future__ import annotations -import datetime -import json -import sys +import datetime, json, zipfile, yaml, logging, re from pprint import pformat -import yaml from sqlalchemy import Column, String, TIMESTAMP, JSON, INTEGER, ForeignKey, Interval, Table, FLOAT, BLOB from sqlalchemy.orm import relationship, validates, Query from sqlalchemy.ext.associationproxy import association_proxy from datetime import date -import logging, re from tools import check_authorization, setup_lookup, Report, Result, check_regex_match, yaml_regex_creator from typing import List, Literal, Generator, Any from pandas import ExcelFile @@ -688,16 +684,17 @@ class SubmissionType(BaseClass): return f"" def get_template_file_sheets(self) -> List[str]: + logger.debug(f"Submission type to get sheets for: {self.name}") """ Gets names of sheet in the stored blank form. Returns: List[str]: List of sheet names """ - # print(f"Getting template file from {self.__database_session__.get_bind()}") - if "pytest" in sys.modules: - return ExcelFile("C:\\Users\lwark\Documents\python\submissions\mytests\\test_assets\RSL-AR-20240513-1.xlsx").sheet_names - return ExcelFile(BytesIO(self.template_file), engine="openpyxl").sheet_names + try: + return ExcelFile(BytesIO(self.template_file), engine="openpyxl").sheet_names + except zipfile.BadZipfile: + return [] def set_template_file(self, filepath: Path | str): """ diff --git a/src/submissions/backend/db/models/submissions.py b/src/submissions/backend/db/models/submissions.py index 239a58b..570dfe1 100644 --- a/src/submissions/backend/db/models/submissions.py +++ b/src/submissions/backend/db/models/submissions.py @@ -4,6 +4,7 @@ Models for the main submission and sample types. from __future__ import annotations import sys import types +import zipfile from copy import deepcopy from getpass import getuser import logging, uuid, tempfile, re, base64 @@ -733,6 +734,7 @@ class BasicSubmission(BaseClass): return model # Child class custom functions + @classmethod def custom_info_parser(cls, input_dict: dict, xl: Workbook | None = None, custom_fields: dict = {}) -> dict: """ @@ -1170,7 +1172,8 @@ class BasicSubmission(BaseClass): # logger.debug(f"Retrieved instance: {instance}") if instance is None: used_class = cls.find_polymorphic_subclass(attrs=kwargs, polymorphic_identity=submission_type) - instance = used_class(**kwargs) + # instance = used_class(**kwargs) + instance = used_class(**sanitized_kwargs) match submission_type: case str(): submission_type = SubmissionType.query(name=submission_type) @@ -1216,7 +1219,10 @@ class BasicSubmission(BaseClass): fname = self.__backup_path__.joinpath(f"{self.rsl_plate_num}-backup({date.today().strftime('%Y%m%d')})") msg = QuestionAsker(title="Delete?", message=f"Are you sure you want to delete {self.rsl_plate_num}?\n") if msg.exec(): - self.backup(fname=fname, full_backup=True) + try: + self.backup(fname=fname, full_backup=True) + except zipfile.BadZipfile: + logger.error("Couldn't open zipfile for writing.") self.__database_session__.delete(self) try: self.__database_session__.commit() @@ -1419,7 +1425,7 @@ class Wastewater(BasicSubmission): """ derivative submission type from BasicSubmission """ - id = Column(INTEGER, ForeignKey('_basicsubmission.id'), primary_key=True) + id = Column(INTEGER, ForeignKey('_basicsubmission.id'), primary_key=True, autoincrement=False) ext_technician = Column(String(64)) #: Name of technician doing extraction pcr_technician = Column(String(64)) #: Name of technician doing pcr pcr_info = Column(JSON) #: unstructured output from pcr table logger or user(Artic) @@ -2279,7 +2285,7 @@ class BasicSample(BaseClass): try: model = cls.__mapper__.polymorphic_map[polymorphic_identity].class_ except Exception as e: - logger.error(f"Could not get polymorph {polymorphic_identity} of {cls} due to {e}") + logger.error(f"Could not get polymorph {polymorphic_identity} of {cls} due to {e}, using {cls}") model = cls logger.info(f"Recruiting model: {model}") return model @@ -2856,6 +2862,7 @@ class SubmissionSampleAssociation(BaseClass): SubmissionSampleAssociation: Queried or new association. """ # logger.debug(f"Attempting create or query with {kwargs}") + disallowed = ['id'] match submission: case BasicSubmission(): pass diff --git a/src/submissions/backend/excel/parser.py b/src/submissions/backend/excel/parser.py index 6b6fd76..efa5824 100644 --- a/src/submissions/backend/excel/parser.py +++ b/src/submissions/backend/excel/parser.py @@ -123,8 +123,8 @@ class SheetParser(object): """ Enforce that the parser has an extraction kit """ - from frontend.widgets.pop_ups import ObjectSelector if 'extraction_kit' not in self.sub.keys() or not check_not_nan(self.sub['extraction_kit']['value']): + from frontend.widgets.pop_ups import ObjectSelector dlg = ObjectSelector(title="Kit Needed", message="At minimum a kit is needed. Please select one.", obj_type=KitType) if dlg.exec(): @@ -199,6 +199,7 @@ class InfoParser(object): if k == "custom": continue if isinstance(v, str): + logger.debug(f"Found string for {k}, setting value to {v}") dicto[k] = dict(value=v, missing=False) continue # logger.debug(f"Looking for {k} in self.map") @@ -270,13 +271,13 @@ class ReagentParser(object): if isinstance(extraction_kit, dict): extraction_kit = extraction_kit['value'] self.kit_object = KitType.query(name=extraction_kit) - # logger.debug(f"Got extraction kit object: {self.kit_object}") + logger.debug(f"Got extraction kit object: {self.kit_object}") self.map = self.fetch_kit_info_map(submission_type=submission_type) # logger.debug(f"Reagent Parser map: {self.map}") self.xl = xl @report_result - def fetch_kit_info_map(self, submission_type: str) -> Tuple[Report, dict]: + def fetch_kit_info_map(self, submission_type: str|SubmissionType) -> Tuple[Report, dict]: """ Gets location of kit reagents from database @@ -296,8 +297,13 @@ class ReagentParser(object): pass # logger.debug(f"Reagent map: {pformat(reagent_map)}") if not reagent_map.keys(): + try: + ext_kit_loc = self.submission_type_obj.info_map['extraction_kit']['read'][0] + location_string = f"Sheet: {ext_kit_loc['sheet']}, Row: {ext_kit_loc['row']}, Column: {ext_kit_loc['column']}?" + except: + location_string = "" report.add_result(Result(owner=__name__, code=0, msg=f"No kit map found for {self.kit_object.name}.\n\n" - f"Are you sure you used the right kit?", + f"Are you sure you put the right kit in:\n\n{location_string}?", status="Critical")) return report, reagent_map @@ -409,7 +415,6 @@ class SampleParser(object): """ invalids = [0, "0", "EMPTY"] smap = self.sample_info_map['plate_map'] - print(smap) ws = self.xl[smap['sheet']] plate_map_samples = [] for ii, row in enumerate(range(smap['start_row'], smap['end_row'] + 1), start=1): diff --git a/src/submissions/backend/validators/__init__.py b/src/submissions/backend/validators/__init__.py index aaa579b..c390287 100644 --- a/src/submissions/backend/validators/__init__.py +++ b/src/submissions/backend/validators/__init__.py @@ -52,7 +52,7 @@ class RSLNamer(object): str: parsed submission type """ def st_from_path(filename:Path) -> str: - logger.info(f"Using path method for {filename}.") + # logger.info(f"Using path method for {filename}.") if filename.exists(): wb = load_workbook(filename) try: @@ -60,7 +60,7 @@ class RSLNamer(object): categories = wb.properties.category.split(";") submission_type = next(item.strip().title() for item in categories) except (StopIteration, AttributeError): - sts = {item.name: item.get_template_file_sheets() for item in SubmissionType.query()} + sts = {item.name: item.get_template_file_sheets() for item in SubmissionType.query() if item.template_file} try: submission_type = next(k.title() for k,v in sts.items() if wb.sheetnames==v) except StopIteration: @@ -70,8 +70,10 @@ class RSLNamer(object): submission_type = cls.retrieve_submission_type(filename=filename.stem.__str__()) return submission_type def st_from_str(filename:str) -> str: + if filename.startswith("tmp"): + return "Bacterial Culture" regex = BasicSubmission.construct_regex() - logger.info(f"Using string method for {filename}.") + # logger.info(f"Using string method for {filename}.") # logger.debug(f"Using regex: {regex}") m = regex.search(filename) try: @@ -95,7 +97,7 @@ class RSLNamer(object): check = True if check: if "pytest" in sys.modules: - return "Bacterial Culture" + raise ValueError("Submission Type came back as None.") # logger.debug("Final option, ask the user for submission type") from frontend.widgets import ObjectSelector dlg = ObjectSelector(title="Couldn't parse submission type.", diff --git a/src/submissions/backend/validators/pydant.py b/src/submissions/backend/validators/pydant.py index 4c7f339..00aa016 100644 --- a/src/submissions/backend/validators/pydant.py +++ b/src/submissions/backend/validators/pydant.py @@ -349,7 +349,7 @@ class PydEquipment(BaseModel, extra='ignore'): """ if isinstance(submission, str): # logger.debug(f"Got string, querying {submission}") - submission = BasicSubmission.query(rsl_number=submission) + submission = BasicSubmission.query(rsl_plate_num=submission) equipment = Equipment.query(asset_number=self.asset_number) if equipment is None: logger.error("No equipment found. Returning None.") @@ -361,7 +361,8 @@ class PydEquipment(BaseModel, extra='ignore'): role=self.role, limit=1) except TypeError as e: logger.error(f"Couldn't get association due to {e}, returning...") - return equipment, None + # return equipment, None + assoc = None if assoc is None: assoc = SubmissionEquipmentAssociation(submission=submission, equipment=equipment) process = Process.query(name=self.processes[0]) @@ -829,6 +830,7 @@ class PydSubmission(BaseModel, extra='allow'): equip, association = equip.toSQL(submission=instance) if association is not None: instance.submission_equipment_associations.append(association) + logger.debug(f"Equipment associations:\n\n{pformat(instance.submission_equipment_associations)}") case "tips": for tips in self.tips: if tips is None: diff --git a/src/submissions/frontend/widgets/app.py b/src/submissions/frontend/widgets/app.py index d057583..e6cefa0 100644 --- a/src/submissions/frontend/widgets/app.py +++ b/src/submissions/frontend/widgets/app.py @@ -230,7 +230,7 @@ class App(QMainWindow): st = SubmissionType.import_from_json(filepath=fname) if st: # NOTE: Do not delete the print statement below. - print(pformat(st.to_export_dict())) + # print(pformat(st.to_export_dict())) choice = input("Save the above submission type? [y/N]: ") if choice.lower() == "y": pass diff --git a/src/submissions/frontend/widgets/controls_chart.py b/src/submissions/frontend/widgets/controls_chart.py index 962b302..d5483a9 100644 --- a/src/submissions/frontend/widgets/controls_chart.py +++ b/src/submissions/frontend/widgets/controls_chart.py @@ -103,7 +103,8 @@ class ControlsViewer(QWidget): sub_types = self.archetype.get_modes(mode=self.mode) except AttributeError: sub_types = [] - if sub_types: + # NOTE: added in allowed to have subtypes in case additions made in future. + if sub_types and self.mode.lower() in self.archetype.get_instance_class().subtyping_allowed: # NOTE: block signal that will rerun controls getter and update mode_sub_typer with QSignalBlocker(self.mode_sub_typer) as blocker: self.mode_sub_typer.addItems(sub_types) diff --git a/src/submissions/frontend/widgets/submission_details.py b/src/submissions/frontend/widgets/submission_details.py index d638867..53bc10a 100644 --- a/src/submissions/frontend/widgets/submission_details.py +++ b/src/submissions/frontend/widgets/submission_details.py @@ -167,7 +167,7 @@ class SubmissionDetails(QDialog): Renders submission to html, then creates and saves .pdf file to user selected file. """ fname = select_save_file(obj=self, default_name=self.export_plate, extension="pdf") - save_pdf(obj=self, filename=fname) + save_pdf(obj=self.webview, filename=fname) class SubmissionComment(QDialog): """ diff --git a/submissions.spec b/submissions.spec index 09fbb01..7f95b62 100644 --- a/submissions.spec +++ b/submissions.spec @@ -37,7 +37,7 @@ a = Analysis( ("src\\submissions\\resources\\*", "files\\resources"), ("alembic.ini", "files"), ], - hiddenimports=[], + hiddenimports=["pyodbc"], hookspath=[], hooksconfig={}, runtime_hooks=[],