diff --git a/src/submissions/__main__.py b/src/submissions/__main__.py index ddd8b21..7925863 100644 --- a/src/submissions/__main__.py +++ b/src/submissions/__main__.py @@ -6,12 +6,11 @@ if getattr(sys, 'frozen', False): else : pass from configure import get_config, create_database_session, setup_logger +logger = setup_logger(verbosity=3) ctx = get_config(None) from PyQt6.QtWidgets import QApplication from frontend import App -logger = setup_logger(verbose=True) - ctx["database_session"] = create_database_session(Path(ctx['database'])) if __name__ == '__main__': diff --git a/src/submissions/backend/db/__init__.py b/src/submissions/backend/db/__init__.py index 725a6c5..d77c909 100644 --- a/src/submissions/backend/db/__init__.py +++ b/src/submissions/backend/db/__init__.py @@ -11,7 +11,7 @@ from sqlalchemy import JSON import json from dateutil.relativedelta import relativedelta -logger = logging.getLogger(__name__) +logger = logging.getLogger(f"submissions.{__name__}") def get_kits_by_use( ctx:dict, kittype_str:str|None) -> list: pass @@ -35,7 +35,7 @@ def store_submission(ctx:dict, base_submission:models.BasicSubmission) -> None: def store_reagent(ctx:dict, reagent:models.Reagent) -> None: - print(reagent.__dict__) + logger.debug(reagent.__dict__) ctx['database_session'].add(reagent) ctx['database_session'].commit() @@ -46,18 +46,18 @@ def construct_submission_info(ctx:dict, info_dict:dict) -> models.BasicSubmissio info_dict['submission_type'] = info_dict['submission_type'].replace(" ", "_").lower() instance = model() for item in info_dict: - print(f"Setting {item} to {info_dict[item]}") + logger.debug(f"Setting {item} to {info_dict[item]}") match item: case "extraction_kit": q_str = info_dict[item] - print(f"Looking up kit {q_str}") + logger.debug(f"Looking up kit {q_str}") field_value = lookup_kittype_by_name(ctx=ctx, name=q_str) - print(f"Got {field_value} for kit {q_str}") + logger.debug(f"Got {field_value} for kit {q_str}") case "submitting_lab": q_str = info_dict[item].replace(" ", "_").lower() - print(f"looking up organization: {q_str}") + logger.debug(f"looking up organization: {q_str}") field_value = lookup_org_by_name(ctx=ctx, name=q_str) - print(f"Got {field_value} for organization {q_str}") + logger.debug(f"Got {field_value} for organization {q_str}") case "submitter_plate_num": # Because of unique constraint, the submitter plate number cannot be None, so... if info_dict[item] == None: @@ -72,16 +72,16 @@ def construct_submission_info(ctx:dict, info_dict:dict) -> models.BasicSubmissio try: setattr(instance, item, field_value) except AttributeError: - print(f"Could not set attribute: {item} to {info_dict[item]}") + logger.debug(f"Could not set attribute: {item} to {info_dict[item]}") continue - # print(instance.__dict__) + # logger.debug(instance.__dict__) return instance # looked_up = [] # for reagent in reagents: # my_reagent = lookup_reagent(reagent) - # print(my_reagent) + # logger.debug(my_reagent) # looked_up.append(my_reagent) - # print(looked_up) + # logger.debug(looked_up) # instance.reagents = looked_up # ctx['database_session'].add(instance) # ctx['database_session'].commit() @@ -89,7 +89,7 @@ def construct_submission_info(ctx:dict, info_dict:dict) -> models.BasicSubmissio def construct_reagent(ctx:dict, info_dict:dict) -> models.Reagent: reagent = models.Reagent() for item in info_dict: - print(f"Reagent info item: {item}") + logger.debug(f"Reagent info item: {item}") match item: case "lot": reagent.lot = info_dict[item].upper() @@ -100,7 +100,7 @@ def construct_reagent(ctx:dict, info_dict:dict) -> models.Reagent: try: reagent.expiry = reagent.expiry + reagent.type.eol_ext except TypeError as e: - print(f"WE got a type error: {e}.") + logger.debug(f"WE got a type error: {e}.") except AttributeError: pass return reagent @@ -116,9 +116,9 @@ def get_all_reagenttype_names(ctx:dict) -> list[str]: return lookedup def lookup_reagenttype_by_name(ctx:dict, rt_name:str) -> models.ReagentType: - print(f"Looking up ReagentType by name: {rt_name}") + logger.debug(f"Looking up ReagentType by name: {rt_name}") lookedup = ctx['database_session'].query(models.ReagentType).filter(models.ReagentType.name==rt_name).first() - print(f"Found ReagentType: {lookedup}") + logger.debug(f"Found ReagentType: {lookedup}") return lookedup @@ -127,7 +127,7 @@ def lookup_kittype_by_use(ctx:dict, used_by:str) -> list[models.KitType]: return ctx['database_session'].query(models.KitType).filter(models.KitType.used_for.contains(used_by)) def lookup_kittype_by_name(ctx:dict, name:str) -> models.KitType: - print(f"Querying kittype: {name}") + logger.debug(f"Querying kittype: {name}") return ctx['database_session'].query(models.KitType).filter(models.KitType.name==name).first() @@ -154,11 +154,11 @@ def lookup_all_orgs(ctx:dict) -> list[models.Organization]: return ctx['database_session'].query(models.Organization).all() def lookup_org_by_name(ctx:dict, name:str|None) -> models.Organization: - print(f"Querying organization: {name}") + logger.debug(f"Querying organization: {name}") return ctx['database_session'].query(models.Organization).filter(models.Organization.name==name).first() def submissions_to_df(ctx:dict, type:str|None=None): - print(f"Type: {type}") + logger.debug(f"Type: {type}") subs = [item.to_dict() for item in lookup_all_submissions_by_type(ctx=ctx, type=type)] df = pd.DataFrame.from_records(subs) return df @@ -205,7 +205,7 @@ def create_kit_from_yaml(ctx:dict, exp:dict) -> None: except (UnicodeDecodeError, AttributeError): exp['password'] = exp['password'].encode() if base64.b64encode(exp['password']) != b'cnNsX3N1Ym1pNTVpb25z': - print(f"Not the correct password.") + logger.debug(f"Not the correct password.") return for type in exp: if type == "password": @@ -220,8 +220,8 @@ def create_kit_from_yaml(ctx:dict, exp:dict) -> None: rt = look_up rt.kits.append(kit) ctx['database_session'].add(rt) - print(rt.__dict__) - print(kit.__dict__) + logger.debug(rt.__dict__) + logger.debug(kit.__dict__) ctx['database_session'].add(kit) ctx['database_session'].commit() @@ -252,7 +252,7 @@ def get_all_controls_by_type(ctx:dict, con_type:str, start_date:date|None=None, list: Control instances. """ - # print(f"Using dates: {start_date} to {end_date}") + # logger.debug(f"Using dates: {start_date} to {end_date}") query = ctx['database_session'].query(models.ControlType).filter_by(name=con_type) try: output = query.first().instances @@ -261,7 +261,7 @@ def get_all_controls_by_type(ctx:dict, con_type:str, start_date:date|None=None, # Hacky solution to my not being able to get the sql query to work. if start_date != None and end_date != None: output = [item for item in output if item.submitted_date.date() > start_date and item.submitted_date.date() < end_date] - # print(f"Type {con_type}: {query.first()}") + # logger.debug(f"Type {con_type}: {query.first()}") return output @@ -271,7 +271,7 @@ def get_control_subtypes(ctx:dict, type:str, mode:str): except TypeError: return [] jsoner = json.loads(getattr(outs, mode)) - print(f"JSON out: {jsoner}") + logger.debug(f"JSON out: {jsoner}") try: genera = list(jsoner.keys())[0] except IndexError: diff --git a/src/submissions/backend/db/models/submissions.py b/src/submissions/backend/db/models/submissions.py index c73dd6f..fb98298 100644 --- a/src/submissions/backend/db/models/submissions.py +++ b/src/submissions/backend/db/models/submissions.py @@ -1,13 +1,12 @@ from . import Base -from sqlalchemy import Column, String, TIMESTAMP, text, JSON, INTEGER, ForeignKey, UniqueConstraint, Table -from sqlalchemy.orm import relationship, relationships +from sqlalchemy import Column, String, TIMESTAMP, INTEGER, ForeignKey, Table +from sqlalchemy.orm import relationship from datetime import datetime as dt reagents_submissions = Table("_reagents_submissions", Base.metadata, Column("reagent_id", INTEGER, ForeignKey("_reagents.id")), Column("submission_id", INTEGER, ForeignKey("_submissions.id"))) class BasicSubmission(Base): - # TODO: Figure out if I want seperate tables for different sample types. __tablename__ = "_submissions" id = Column(INTEGER, primary_key=True) #: primary key @@ -32,7 +31,6 @@ class BasicSubmission(Base): } def to_dict(self): - print(self.submitting_lab) try: sub_lab = self.submitting_lab.name except AttributeError: diff --git a/src/submissions/backend/excel/parser.py b/src/submissions/backend/excel/parser.py index fb7eb6e..f7ebe39 100644 --- a/src/submissions/backend/excel/parser.py +++ b/src/submissions/backend/excel/parser.py @@ -13,11 +13,14 @@ logger = logging.getLogger(f"submissions.{__name__}") class SheetParser(object): def __init__(self, filepath:Path|None = None, **kwargs): + logger.debug(f"Parsing {filepath.__str__()}") for kwarg in kwargs: setattr(self, f"_{kwarg}", kwargs[kwarg]) if filepath == None: + logger.debug(f"No filepath.") self.xl = None else: + try: self.xl = pd.ExcelFile(filepath.__str__()) except ValueError: @@ -76,6 +79,7 @@ class SheetParser(object): self.sub['lot_plate'] = submission_info.iloc[12][6] sample_parser = SampleParser(submission_info.iloc[15:111]) sample_parse = getattr(sample_parser, f"parse_{self.sub['submission_type'].lower()}_samples") + logger.debug(f"Parser result: {self.sub}") self.sub['samples'] = sample_parse() @@ -121,9 +125,10 @@ class SampleParser(object): new.sample_id = sample['Unnamed: 1'] new.organism = sample['Unnamed: 2'] new.concentration = sample['Unnamed: 3'] - # print(f"Sample object: {new.sample_id} = {type(new.sample_id)}") + # logger.debug(f"Sample object: {new.sample_id} = {type(new.sample_id)}") + logger.debug(f"Got sample_id: {new.sample_id}") try: - not_a_nan = not np.isnan(new.sample_id) and new.sample_id.lower() != 'blank' + not_a_nan = not np.isnan(new.sample_id) and str(new.sample_id).lower() != 'blank' except TypeError: not_a_nan = True if not_a_nan: diff --git a/src/submissions/backend/excel/reports.py b/src/submissions/backend/excel/reports.py index e9daad0..64bd1f9 100644 --- a/src/submissions/backend/excel/reports.py +++ b/src/submissions/backend/excel/reports.py @@ -3,6 +3,9 @@ from pandas import DataFrame import numpy as np from backend.db import models import json +import logging + +logger = logging.getLogger(f"submissions.{__name__}") def make_report_xlsx(records:list[dict]) -> DataFrame: df = DataFrame.from_records(records) @@ -10,7 +13,7 @@ def make_report_xlsx(records:list[dict]) -> DataFrame: # table = df.pivot_table(values="Cost", index=["Submitting Lab", "Extraction Kit"], columns=["Cost", "Sample Count"], aggfunc={'Cost':np.sum,'Sample Count':np.sum}) df2 = df.groupby(["Submitting Lab", "Extraction Kit"]).agg({'Cost': ['sum', 'count'], 'Sample Count':['sum']}) # df2['Cost'] = df2['Cost'].map('${:,.2f}'.format) - print(df2.columns) + logger.debug(df2.columns) # df2['Cost']['sum'] = df2['Cost']['sum'].apply('${:,.2f}'.format) df2.iloc[:, (df2.columns.get_level_values(1)=='sum') & (df2.columns.get_level_values(0)=='Cost')] = df2.iloc[:, (df2.columns.get_level_values(1)=='sum') & (df2.columns.get_level_values(0)=='Cost')].applymap('${:,.2f}'.format) return df2 @@ -27,7 +30,7 @@ def make_report_xlsx(records:list[dict]) -> DataFrame: # for ii in range(data_size): # new_dict = {} # for genus in sub_dict: -# print(genus) +# logger.debug(genus) # sub_name = list(sub_dict[genus].keys())[ii] # new_dict[genus] = sub_dict[genus][sub_name] # output.append({"date":dict_name, "name": sub_name, "data": new_dict}) @@ -55,10 +58,10 @@ def make_report_xlsx(records:list[dict]) -> DataFrame: # # col_dict = entry[col_name] # # series = pd.Series(data=col_dict.values(), index=col_dict.keys(), name=col_name) # # # df[col_name] = series.values -# # # print(df.index) +# # # logger.debug(df.index) # # series_list.append(series) # # df = DataFrame(series_list).T.fillna(0) -# # print(df) +# # logger.debug(df) # dfs['name'] = df # return dfs @@ -74,14 +77,14 @@ def convert_control_by_mode(ctx:dict, control:models.Control, mode:str): for key in data[genus]: _dict[key] = data[genus][key] output.append(_dict) - # print(output) + # logger.debug(output) return output def convert_data_list_to_df(ctx:dict, input:list[dict], subtype:str|None=None) -> DataFrame: df = DataFrame.from_records(input) safe = ['name', 'submitted_date', 'genus', 'target'] - print(df) + logger.debug(df) for column in df.columns: if "percent" in column: count_col = [item for item in df.columns if "count" in item][0] @@ -90,5 +93,5 @@ def convert_data_list_to_df(ctx:dict, input:list[dict], subtype:str|None=None) - if column not in safe: if subtype != None and column != subtype: del df[column] - # print(df) + # logger.debug(df) return df diff --git a/src/submissions/configure/__init__.py b/src/submissions/configure/__init__.py index f6cac7c..26e3054 100644 --- a/src/submissions/configure/__init__.py +++ b/src/submissions/configure/__init__.py @@ -1,5 +1,5 @@ import yaml -import sys, os, stat, platform +import sys, os, stat, platform, shutil import logging from logging import handlers from pathlib import Path @@ -8,17 +8,17 @@ from sqlalchemy.orm import Session from sqlalchemy import create_engine -logger = logging.getLogger(__name__) +logger = logging.getLogger(f"submissions.{__name__}") package_dir = Path(__file__).parents[2].resolve() logger.debug(f"Package dir: {package_dir}") -if platform.system == "Windows": - os_config_dir = "AppData" - logger.debug(f"Got platform Windows, config_dir: {os_config_dir}") +if platform.system() == "Windows": + os_config_dir = "AppData/local" + print(f"Got platform Windows, config_dir: {os_config_dir}") else: os_config_dir = ".config" - logger.debug(f"Got platform other, config_dir: {os_config_dir}") + print(f"Got platform other, config_dir: {os_config_dir}") main_aux_dir = Path.home().joinpath(f"{os_config_dir}/submissions") @@ -27,6 +27,8 @@ CONFIGDIR = main_aux_dir.joinpath("config") LOGDIR = main_aux_dir.joinpath("logs") + + class GroupWriteRotatingFileHandler(handlers.RotatingFileHandler): def doRollover(self): @@ -79,6 +81,16 @@ def get_config(settings_path: str|None) -> dict: ## register the tag handler yaml.add_constructor('!join', join) # if user hasn't defined config path in cli args + logger.debug(f"Making directory: {CONFIGDIR.__str__()}") + try: + CONFIGDIR.mkdir(parents=True) + except FileExistsError: + pass + logger.debug(f"Making directory: {LOGDIR.__str__()}") + try: + LOGDIR.mkdir(parents=True) + except FileExistsError: + pass if settings_path == None: # Check user .config/ozma directory # if Path.exists(Path.joinpath(CONFIGDIR, "config.yml")): @@ -94,6 +106,7 @@ def get_config(settings_path: str|None) -> dict: settings_path = Path(sys._MEIPASS).joinpath("files", "config.yml") else: settings_path = package_dir.joinpath('config.yml') + shutil.copyfile(settings_path.__str__(), CONFIGDIR.joinpath("config.yml").__str__()) else: if Path(settings_path).is_dir(): settings_path = settings_path.joinpath("config.yml") @@ -103,6 +116,7 @@ def get_config(settings_path: str|None) -> dict: logger.error("No config.yml file found. Using empty dictionary.") return {} logger.debug(f"Using {settings_path} for config file.") + with open(settings_path, "r") as stream: try: settings = yaml.load(stream, Loader=yaml.Loader) @@ -141,7 +155,7 @@ def create_database_session(database_path: Path|None) -> Session: return session -def setup_logger(verbose:bool=False): +def setup_logger(verbosity:int=3): """Set logger levels using settings. Args: @@ -161,34 +175,38 @@ def setup_logger(verbose:bool=False): fh.setLevel(logging.DEBUG) fh.name = "File" # create console handler with a higher log level - ch = logging.StreamHandler() - if verbose: - ch.setLevel(logging.DEBUG) - else: - ch.setLevel(logging.WARNING) + ch = logging.StreamHandler(stream=sys.stdout) + match verbosity: + case 3: + ch.setLevel(logging.DEBUG) + case 2: + ch.setLevel(logging.INFO) + case 1: + ch.setLevel(logging.WARNING) ch.name = "Stream" # create formatter and add it to the handlers formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') fh.setFormatter(formatter) ch.setFormatter(formatter) - ch.setLevel(logging.ERROR) + # ch.setLevel(logging.ERROR) # add the handlers to the logger logger.addHandler(fh) logger.addHandler(ch) - stderr_logger = logging.getLogger('STDERR') + # stderr_logger = logging.getLogger('STDERR') + return logger # sl = StreamToLogger(stderr_logger, logging.ERROR) # sys.stderr = sl -def set_logger_verbosity(verbosity): - """Does what it says. - """ - handler = [item for item in logger.parent.handlers if item.name == "Stream"][0] - match verbosity: - case 3: - handler.setLevel(logging.DEBUG) - case 2: - handler.setLevel(logging.INFO) - case 1: - handler.setLevel(logging.WARNING) +# def set_logger_verbosity(verbosity): +# """Does what it says. +# """ +# handler = [item for item in logger.parent.handlers if item.name == "Stream"][0] +# match verbosity: +# case 3: +# handler.setLevel(logging.DEBUG) +# case 2: +# handler.setLevel(logging.INFO) +# case 1: +# handler.setLevel(logging.WARNING) diff --git a/src/submissions/frontend/__init__.py b/src/submissions/frontend/__init__.py index 44dfaa6..6233cd9 100644 --- a/src/submissions/frontend/__init__.py +++ b/src/submissions/frontend/__init__.py @@ -4,7 +4,7 @@ from PyQt6.QtWidgets import ( QTabWidget, QWidget, QVBoxLayout, QPushButton, QMenuBar, QFileDialog, QLineEdit, QMessageBox, QComboBox, QDateEdit, QHBoxLayout, - QSpinBox + QSpinBox, QScrollArea ) from PyQt6.QtGui import QAction, QIcon from PyQt6.QtCore import QDateTime, QDate, QSignalBlocker @@ -108,8 +108,9 @@ class App(QMainWindow): prsr = SheetParser(fname, **self.ctx) except PermissionError: return - print(f"prsr.sub = {prsr.sub}") + logger.debug(f"prsr.sub = {prsr.sub}") # replace formlayout with tab1.layout + # self.form = self.table_widget.formlayout for item in self.table_widget.formlayout.parentWidget().findChildren(QWidget): item.setParent(None) variable_parser = re.compile(r""" @@ -128,11 +129,11 @@ class App(QMainWindow): mo = variable_parser.fullmatch(item).lastgroup except AttributeError: mo = "other" - print(f"Mo: {mo}") + logger.debug(f"Mo: {mo}") match mo: case 'submitting_lab': self.table_widget.formlayout.addWidget(QLabel(item.replace("_", " ").title())) - print(f"{item}: {prsr.sub[item]}") + logger.debug(f"{item}: {prsr.sub[item]}") add_widget = QComboBox() labs = [item.__str__() for item in lookup_all_orgs(ctx=self.ctx)] try: @@ -167,15 +168,15 @@ class App(QMainWindow): add_widget.setEditable(True) # Ensure that all reagenttypes have a name that matches the items in the excel parser query_var = item.replace("lot_", "") - print(f"Query for: {query_var}") + logger.debug(f"Query for: {query_var}") if isinstance(prsr.sub[item], numpy.float64): - print(f"{prsr.sub[item]} is a numpy float!") + logger.debug(f"{prsr.sub[item]} is a numpy float!") try: prsr.sub[item] = int(prsr.sub[item]) except ValueError: pass relevant_reagents = [item.__str__() for item in lookup_regent_by_type_name_and_kit_name(ctx=self.ctx, type_name=query_var, kit_name=prsr.sub['extraction_kit'])] - print(f"Relevant reagents: {relevant_reagents}") + logger.debug(f"Relevant reagents: {relevant_reagents}") if prsr.sub[item] not in relevant_reagents and prsr.sub[item] != 'nan': try: check = not numpy.isnan(prsr.sub[item]) @@ -187,7 +188,7 @@ class App(QMainWindow): add_widget.addItems(relevant_reagents) # TODO: make samples not appear in frame. case 'samples': - print(f"{item}: {prsr.sub[item]}") + logger.debug(f"{item}: {prsr.sub[item]}") self.samples = prsr.sub[item] case _: self.table_widget.formlayout.addWidget(QLabel(item.replace("_", " ").title())) @@ -216,28 +217,7 @@ class App(QMainWindow): html += '' self.table_widget.webengineview.setHtml(html) self.table_widget.webengineview.update() - # type = self.table_widget.control_typer.currentText() - # mode = self.table_widget.mode_typer.currentText() - # controls = get_all_controls_by_type(ctx=self.ctx, type=type) - # data = [] - # for control in controls: - # dicts = convert_control_by_mode(ctx=self.ctx, control=control, mode=mode) - # data.append(dicts) - # data = [item for sublist in data for item in sublist] - # # print(data) - # df = convert_data_list_to_df(ctx=self.ctx, input=data) - # fig = create_charts(ctx=self.ctx, df=df) - - # print(fig) - # html = '' - # html += plotly.offline.plot(fig, output_type='div', auto_open=True, image = 'png', image_filename='plot_image') - # html += '' - # html = plotly.io.to_html(fig) - # # print(html) - # # with open("C:\\Users\\lwark\\Desktop\\test.html", "w") as f: - # # f.write(html) - # self.table_widget.webengineview.setHtml(html) - # self.table_widget.webengineview.update() + def submit_new_sample(self): @@ -297,8 +277,8 @@ class App(QMainWindow): case QLineEdit(): # ad hoc check to prevent double reporting of qdatedit under lineedit for some reason if not isinstance(prev_item, QDateEdit) and not isinstance(prev_item, QComboBox) and not isinstance(prev_item, QSpinBox): - print(f"Previous: {prev_item}") - print(f"Item: {item}") + logger.debug(f"Previous: {prev_item}") + logger.debug(f"Item: {item}") values.append(item.text()) case QComboBox(): values.append(item.currentText()) @@ -346,7 +326,7 @@ class App(QMainWindow): except TypeError: pass if self.table_widget.datepicker.start_date.date() > self.table_widget.datepicker.end_date.date(): - print("that is not allowed!") + logger.warning("Start date after end date is not allowed!") # self.table_widget.datepicker.start_date.setDate(e_date) threemonthsago = self.table_widget.datepicker.end_date.date().addDays(-90) with QSignalBlocker(self.table_widget.datepicker.start_date) as blocker: @@ -372,12 +352,12 @@ class App(QMainWindow): def chart_maker(self): - print(f"Control getter context: \n\tControl type: {self.con_type}\n\tMode: {self.mode}\n\tStart Date: {self.start_date}\n\tEnd Date: {self.end_date}") + logger.debug(f"Control getter context: \n\tControl type: {self.con_type}\n\tMode: {self.mode}\n\tStart Date: {self.start_date}\n\tEnd Date: {self.end_date}") if self.table_widget.sub_typer.currentText() == "": self.subtype = None else: self.subtype = self.table_widget.sub_typer.currentText() - print(f"Subtype: {self.subtype}") + logger.debug(f"Subtype: {self.subtype}") controls = get_all_controls_by_type(ctx=self.ctx, con_type=self.con_type, start_date=self.start_date, end_date=self.end_date) if controls == None: return @@ -386,14 +366,14 @@ class App(QMainWindow): dicts = convert_control_by_mode(ctx=self.ctx, control=control, mode=self.mode) data.append(dicts) data = [item for sublist in data for item in sublist] - # print(data) + # logger.debug(data) df = convert_data_list_to_df(ctx=self.ctx, input=data, subtype=self.subtype) if self.subtype == None: title = self.mode else: title = f"{self.mode} - {self.subtype}" fig = create_charts(ctx=self.ctx, df=df, ytitle=title) - print(f"Updating figure...") + logger.debug(f"Updating figure...") html = '' if fig != None: html += plotly.offline.plot(fig, output_type='div', include_plotlyjs='cdn')#, image = 'png', auto_open=True, image_filename='plot_image') @@ -404,7 +384,7 @@ class App(QMainWindow): # f.write(html) self.table_widget.webengineview.setHtml(html) self.table_widget.webengineview.update() - print("Figure updated... I hope.") + logger.debug("Figure updated... I hope.") # def datechange(self): @@ -412,7 +392,7 @@ class App(QMainWindow): # s_date = self.table_widget.datepicker.start_date.date() # e_date = self.table_widget.datepicker.end_date.date() # if s_date > e_date: - # print("that is not allowed!") + # logger.debug("that is not allowed!") # # self.table_widget.datepicker.start_date.setDate(e_date) # threemonthsago = e_date.addDays(-90) # self.table_widget.datepicker.start_date.setDate(threemonthsago) @@ -448,6 +428,12 @@ class AddSubForm(QWidget): self.formlayout = QVBoxLayout(self) self.formwidget.setLayout(self.formlayout) self.formwidget.setFixedWidth(300) + + self.interior = QScrollArea() + self.interior.setWidgetResizable(True) + self.interior.setFixedWidth(325) + self.interior.setParent(self.tab1) + self.interior.setWidget(self.formwidget) self.sheetwidget = QWidget(self) self.sheetlayout = QVBoxLayout(self) @@ -455,22 +441,16 @@ class AddSubForm(QWidget): self.sub_wid = SubmissionsSheet(parent.ctx) self.sheetlayout.addWidget(self.sub_wid) - self.tab1.layout = QHBoxLayout(self) self.tab1.setLayout(self.tab1.layout) # self.tab1.layout.addLayout(self.formlayout) + + # self.tab1.layout.addWidget(self.formwidget) self.tab1.layout.addWidget(self.formwidget) self.tab1.layout.addWidget(self.sheetwidget) # self.tab1.layout.addLayout(self.sheetlayout) - # self.tab1.setWidgetResizable(True) - # self.tab1.setVerticalScrollBar(QScrollBar()) - # self.tab1.layout.addWidget(self.scroller) - # self.tab1.setWidget(self.scroller) - # self.tab1.setMinimumHeight(300) self.datepicker = ControlsDatePicker() self.webengineview = QWebEngineView() - # data = '''Hello World''' - # self.webengineview.setHtml(data) self.tab2.layout = QVBoxLayout(self) self.control_typer = QComboBox() con_types = get_all_Control_Types_names(ctx=parent.ctx) @@ -493,3 +473,5 @@ class AddSubForm(QWidget): self.tab3.setLayout(self.tab3.layout) self.layout.addWidget(self.tabs) self.setLayout(self.layout) + print(self.tab1.layout.parentWidget().findChildren(QScrollArea)) + diff --git a/src/submissions/frontend/custom_widgets/__init__.py b/src/submissions/frontend/custom_widgets/__init__.py index 7a21032..72cbe39 100644 --- a/src/submissions/frontend/custom_widgets/__init__.py +++ b/src/submissions/frontend/custom_widgets/__init__.py @@ -14,6 +14,9 @@ from jinja2 import Environment, FileSystemLoader import sys from pathlib import Path +import logging + +logger = logging.getLogger(f"submissions.{__name__}") if getattr(sys, 'frozen', False): loader_path = Path(sys._MEIPASS).joinpath("files", "templates") @@ -62,7 +65,7 @@ class AddReagentForm(QDialog): exp_input.setDate(QDate.currentDate()) type_input = QComboBox() type_input.addItems([item.replace("_", " ").title() for item in get_all_reagenttype_names(ctx=ctx)]) - print(f"Trying to find index of {reagent_type}") + logger.debug(f"Trying to find index of {reagent_type}") try: reagent_type = reagent_type.replace("_", " ").title() except AttributeError: @@ -132,7 +135,7 @@ class SubmissionsSheet(QTableView): def show_details(self, item): index=(self.selectionModel().currentIndex()) - # print(index) + # logger.debug(index) value=index.sibling(index.row(),0).data() dlg = SubmissionDetails(ctx=self.ctx, id=value) # dlg.show() @@ -245,7 +248,7 @@ class KitAdder(QWidget): def submit(self): labels, values, reagents = self.extract_form_info(self) info = {item[0]:item[1] for item in zip(labels, values)} - print(info) + logger.debug(info) # info['reagenttypes'] = reagents # del info['name'] # del info['extension_of_life_(months)'] @@ -257,7 +260,7 @@ class KitAdder(QWidget): yml_type[used]['kits'][info['kit_name']] = {} yml_type[used]['kits'][info['kit_name']]['cost'] = info['cost_per_run'] yml_type[used]['kits'][info['kit_name']]['reagenttypes'] = reagents - print(yml_type) + logger.debug(yml_type) create_kit_from_yaml(ctx=self.ctx, exp=yml_type) def extract_form_info(self, object): @@ -265,7 +268,7 @@ class KitAdder(QWidget): values = [] reagents = {} for item in object.findChildren(QWidget): - print(item.parentWidget()) + logger.debug(item.parentWidget()) # if not isinstance(item.parentWidget(), ReagentTypeForm): match item: case QLabel(): @@ -273,8 +276,8 @@ class KitAdder(QWidget): case QLineEdit(): # ad hoc check to prevent double reporting of qdatedit under lineedit for some reason if not isinstance(prev_item, QDateEdit) and not isinstance(prev_item, QComboBox) and not isinstance(prev_item, QSpinBox) and not isinstance(prev_item, QScrollBar): - print(f"Previous: {prev_item}") - print(f"Item: {item}, {item.text()}") + logger.debug(f"Previous: {prev_item}") + logger.debug(f"Item: {item}, {item.text()}") values.append(item.text()) case QComboBox(): values.append(item.currentText()) @@ -286,7 +289,7 @@ class KitAdder(QWidget): re_labels, re_values, _ = self.extract_form_info(item) reagent = {item[0]:item[1] for item in zip(re_labels, re_values)} - print(reagent) + logger.debug(reagent) # reagent = {reagent['name:']:{'eol':reagent['extension_of_life_(months):']}} reagents[reagent['name']] = {'eol_ext':int(reagent['extension_of_life_(months)'])} prev_item = item diff --git a/src/submissions/frontend/visualizations/charts.py b/src/submissions/frontend/visualizations/charts.py index 97e8d2c..19636ac 100644 --- a/src/submissions/frontend/visualizations/charts.py +++ b/src/submissions/frontend/visualizations/charts.py @@ -5,7 +5,7 @@ from plotly.graph_objects import Figure import logging from backend.excel import get_unique_values_in_df_column -logger = logging.getLogger("controls.tools.vis_functions") +logger = logging.getLogger(f"submissions.{__name__}") def create_charts(ctx:dict, df:pd.DataFrame, ytitle:str|None=None) -> Figure: @@ -160,7 +160,7 @@ def construct_chart(ctx:dict, df:pd.DataFrame, modes:list, ytitle:str|None=None) color_discrete_sequence=None else: color = "target" - print(get_unique_values_in_df_column(df, 'target')) + # print(get_unique_values_in_df_column(df, 'target')) match get_unique_values_in_df_column(df, 'target'): case ['Target']: color_discrete_sequence=["blue"]