From c7b6c90eac49d6b8582108c4f4a21f536d050b00 Mon Sep 17 00:00:00 2001 From: lwark Date: Fri, 7 Jun 2024 14:15:19 -0500 Subject: [PATCH] Renaming ReagentType to ReagentRole --- .../backend/db/models/organizations.py | 80 ++++++++++--------- .../backend/db/models/submissions.py | 56 +++++++++---- src/submissions/backend/excel/parser.py | 6 +- src/submissions/backend/excel/writer.py | 47 ++++++----- src/submissions/backend/validators/pydant.py | 12 +++ .../frontend/widgets/gel_checker.py | 6 +- .../frontend/widgets/submission_widget.py | 64 +++++++++------ src/submissions/tools.py | 55 +++++++------ 8 files changed, 197 insertions(+), 129 deletions(-) diff --git a/src/submissions/backend/db/models/organizations.py b/src/submissions/backend/db/models/organizations.py index 41fa635..7c70f5b 100644 --- a/src/submissions/backend/db/models/organizations.py +++ b/src/submissions/backend/db/models/organizations.py @@ -13,24 +13,27 @@ logger = logging.getLogger(f"submissions.{__name__}") # table containing organization/contact relationship orgs_contacts = Table( - "_orgs_contacts", - Base.metadata, - Column("org_id", INTEGER, ForeignKey("_organization.id")), - Column("contact_id", INTEGER, ForeignKey("_contact.id")), - # __table_args__ = {'extend_existing': True} - extend_existing = True - ) + "_orgs_contacts", + Base.metadata, + Column("org_id", INTEGER, ForeignKey("_organization.id")), + Column("contact_id", INTEGER, ForeignKey("_contact.id")), + # __table_args__ = {'extend_existing': True} + extend_existing=True +) + class Organization(BaseClass): """ Base of organization """ - - id = Column(INTEGER, primary_key=True) #: primary key - name = Column(String(64)) #: organization name - submissions = relationship("BasicSubmission", back_populates="submitting_lab") #: submissions this organization has submitted - cost_centre = Column(String()) #: cost centre used by org for payment - contacts = relationship("Contact", back_populates="organization", secondary=orgs_contacts) #: contacts involved with this org + + id = Column(INTEGER, primary_key=True) #: primary key + name = Column(String(64)) #: organization name + submissions = relationship("BasicSubmission", + back_populates="submitting_lab") #: submissions this organization has submitted + cost_centre = Column(String()) #: cost centre used by org for payment + contacts = relationship("Contact", back_populates="organization", + secondary=orgs_contacts) #: contacts involved with this org def __repr__(self) -> str: return f"" @@ -38,10 +41,10 @@ class Organization(BaseClass): @classmethod @setup_lookup def query(cls, - id:int|None=None, - name:str|None=None, - limit:int=0, - ) -> Organization|List[Organization]: + id: int | None = None, + name: str | None = None, + limit: int = 0, + ) -> Organization | List[Organization]: """ Lookup organizations in the database by a number of parameters. @@ -51,11 +54,11 @@ class Organization(BaseClass): Returns: Organization|List[Organization]: - """ + """ query: Query = cls.__database_session__.query(cls) match id: case int(): - query = query.filter(cls.id==id) + query = query.filter(cls.id == id) limit = 1 case _: pass @@ -73,33 +76,35 @@ class Organization(BaseClass): def save(self): super().save() + class Contact(BaseClass): """ Base of Contact """ - - id = Column(INTEGER, primary_key=True) #: primary key - name = Column(String(64)) #: contact name - email = Column(String(64)) #: contact email - phone = Column(String(32)) #: contact phone number - organization = relationship("Organization", back_populates="contacts", uselist=True, secondary=orgs_contacts) #: relationship to joined organization - submissions = relationship("BasicSubmission", back_populates="contact") #: submissions this contact has submitted + + id = Column(INTEGER, primary_key=True) #: primary key + name = Column(String(64)) #: contact name + email = Column(String(64)) #: contact email + phone = Column(String(32)) #: contact phone number + organization = relationship("Organization", back_populates="contacts", uselist=True, + secondary=orgs_contacts) #: relationship to joined organization + submissions = relationship("BasicSubmission", back_populates="contact") #: submissions this contact has submitted def __repr__(self) -> str: """ Returns: str: Representation of this Contact - """ + """ return f"" @classmethod @setup_lookup - def query(cls, - name:str|None=None, - email:str|None=None, - phone:str|None=None, - limit:int=0, - ) -> Contact|List[Contact]: + def query(cls, + name: str | None = None, + email: str | None = None, + phone: str | None = None, + limit: int = 0, + ) -> Contact | List[Contact]: """ Lookup contacts in the database by a number of parameters. @@ -111,29 +116,28 @@ class Contact(BaseClass): Returns: Contact|List[Contact]: Contact(s) of interest. - """ + """ # super().query(session) query: Query = cls.__database_session__.query(cls) match name: case str(): # logger.debug(f"Looking up contact with name: {name}") - query = query.filter(cls.name==name) + query = query.filter(cls.name == name) limit = 1 case _: pass match email: case str(): # logger.debug(f"Looking up contact with email: {name}") - query = query.filter(cls.email==email) + query = query.filter(cls.email == email) limit = 1 case _: pass match phone: case str(): # logger.debug(f"Looking up contact with phone: {name}") - query = query.filter(cls.phone==phone) + query = query.filter(cls.phone == phone) limit = 1 case _: pass return cls.execute_query(query=query, limit=limit) - \ No newline at end of file diff --git a/src/submissions/backend/db/models/submissions.py b/src/submissions/backend/db/models/submissions.py index 687c42a..1ed2a7c 100644 --- a/src/submissions/backend/db/models/submissions.py +++ b/src/submissions/backend/db/models/submissions.py @@ -248,6 +248,7 @@ class BasicSubmission(BaseClass): ext_info = self.extraction_info except TypeError: ext_info = None + output = { "id": self.id, "plate_number": self.rsl_plate_num, @@ -257,8 +258,7 @@ class BasicSubmission(BaseClass): "submitting_lab": sub_lab, "sample_count": self.sample_count, "extraction_kit": ext_kit, - "cost": self.run_cost, - + "cost": self.run_cost } if report: return output @@ -299,6 +299,15 @@ class BasicSubmission(BaseClass): except Exception as e: logger.error(f"Error setting comment: {self.comment}, {e}") comments = None + try: + contact = self.contact.name + except AttributeError as e: + logger.error(f"Problem setting contact: {e}") + contact = "NA" + try: + contact_phone = self.contact.phone + except AttributeError: + contact_phone = "NA" output["submission_category"] = self.submission_category output["technician"] = self.technician output["reagents"] = reagents @@ -308,9 +317,9 @@ class BasicSubmission(BaseClass): output["equipment"] = equipment output["cost_centre"] = cost_centre output["signed_by"] = self.signed_by - output["contact"] = self.contact.name - output["contact_phone"] = self.contact.phone - + # logger.debug(f"Setting contact to: {contact} of type: {type(contact)}") + output["contact"] = contact + output["contact_phone"] = contact_phone return output def calculate_column_count(self) -> int: @@ -431,7 +440,7 @@ class BasicSubmission(BaseClass): excluded = ['controls', 'extraction_info', 'pcr_info', 'comment', 'comments', 'samples', 'reagents', 'equipment', 'gel_info', 'gel_image', 'dna_core_submission_number', 'gel_controls', 'source_plates', 'pcr_technician', 'ext_technician', 'artic_technician', 'cost_centre', - 'signed_by'] + 'signed_by', 'artic_date', 'gel_barcode', 'gel_date', 'ngs_date', 'contact_phone', 'contact'] for item in excluded: try: df = df.drop(item, axis=1) @@ -544,7 +553,6 @@ class BasicSubmission(BaseClass): # logger.debug("To dict complete") new_dict = {} for key, value in dicto.items(): - # start = time() # logger.debug(f"Checking {key}") missing = value is None or value in ['', 'None'] match key: @@ -1403,6 +1411,10 @@ class WastewaterArtic(BasicSubmission): gel_info = Column(JSON) #: unstructured data from gel. gel_controls = Column(JSON) #: locations of controls on the gel source_plates = Column(JSON) #: wastewater plates that samples come from + artic_date = Column(TIMESTAMP) #: Date Artic Performed + ngs_date = Column(TIMESTAMP) #: Date submission received + gel_date = Column(TIMESTAMP) #: Date submission received + gel_barcode = Column(String(16)) __mapper_args__ = dict(polymorphic_identity="Wastewater Artic", polymorphic_load="inline", @@ -1418,12 +1430,18 @@ class WastewaterArtic(BasicSubmission): output = super().to_dict(full_data=full_data, backup=backup, report=report) if self.artic_technician in [None, "None"]: output['artic_technician'] = self.technician + else: + output['artic_technician'] = self.artic_technician if report: return output output['gel_info'] = self.gel_info output['gel_image'] = self.gel_image output['dna_core_submission_number'] = self.dna_core_submission_number output['source_plates'] = self.source_plates + output['artic_date'] = self.artic_date or self.submitted_date + output['ngs_date'] = self.ngs_date or self.submitted_date + output['gel_date'] = self.gel_date or self.submitted_date + output['gel_barcode'] = self.gel_barcode return output @classmethod @@ -1756,19 +1774,22 @@ class WastewaterArtic(BasicSubmission): output.append(dicto) continue for item in self.source_plates: - old_plate = WastewaterAssociation.query(submission=item['plate'], sample=assoc.sample, limit=1) + if assoc.sample.id is None: + old_plate = None + else: + old_plate = WastewaterAssociation.query(submission=item['plate'], sample=assoc.sample, limit=1) if old_plate is not None: set_plate = old_plate.submission.rsl_plate_num - logger.debug(dicto['WW Processing Num']) - if dicto['WW Processing Num'].startswith("NTC"): - dicto['Well'] = dicto['WW Processing Num'] + # logger.debug(f"Dictionary: {pformat(dicto)}") + if dicto['ww_processing_num'].startswith("NTC"): + dicto['well'] = dicto['ww_processing_num'] else: - dicto['Well'] = f"{row_map[old_plate.row]}{old_plate.column}" + dicto['well'] = f"{row_map[old_plate.row]}{old_plate.column}" break - elif dicto['WW Processing Num'].startswith("NTC"): - dicto['Well'] = dicto['WW Processing Num'] + elif dicto['ww_processing_num'].startswith("NTC"): + dicto['well'] = dicto['ww_processing_num'] dicto['plate_name'] = set_plate - logger.debug(f"Here is our raw sample: {pformat(dicto)}") + # logger.debug(f"Here is our raw sample: {pformat(dicto)}") output.append(dicto) return output @@ -1801,7 +1822,7 @@ class WastewaterArtic(BasicSubmission): fname = select_open_file(obj=obj, file_extension="jpg") dlg = GelBox(parent=obj, img_path=fname, submission=self) if dlg.exec(): - self.dna_core_submission_number, img_path, output, comment = dlg.parse_form() + self.dna_core_submission_number, self.gel_barcode, img_path, output, comment = dlg.parse_form() self.gel_image = img_path.name self.gel_info = output dt = datetime.strftime(datetime.now(), "%Y-%m-%d %H:%M:%S") @@ -2135,7 +2156,7 @@ class BasicSample(BaseClass): if dlg.exec(): pass -#Below are the custom sample types +# Below are the custom sample types class WastewaterSample(BasicSample): """ @@ -2235,6 +2256,7 @@ class WastewaterSample(BasicSample): searchables.append(dict(label=label, field=item)) return searchables + class BacterialCultureSample(BasicSample): """ base of bacterial culture sample diff --git a/src/submissions/backend/excel/parser.py b/src/submissions/backend/excel/parser.py index fa2a126..c9b8429 100644 --- a/src/submissions/backend/excel/parser.py +++ b/src/submissions/backend/excel/parser.py @@ -139,7 +139,7 @@ class SheetParser(object): # logger.debug(f"Submission dictionary coming into 'to_pydantic':\n{pformat(self.sub)}") pyd_dict = copy(self.sub) pyd_dict['samples'] = [PydSample(**sample) for sample in self.sub['samples']] - logger.debug(f"Reagents: {pformat(self.sub['reagents'])}") + # logger.debug(f"Reagents: {pformat(self.sub['reagents'])}") pyd_dict['reagents'] = [PydReagent(**reagent) for reagent in self.sub['reagents']] # logger.debug(f"Equipment: {self.sub['equipment']}") try: @@ -215,13 +215,13 @@ class InfoParser(object): new = location new['name'] = k relevant.append(new) - logger.debug(f"relevant map for {sheet}: {pformat(relevant)}") + # logger.debug(f"relevant map for {sheet}: {pformat(relevant)}") if not relevant: continue for item in relevant: # NOTE: Get cell contents at this location value = ws.cell(row=item['row'], column=item['column']).value - logger.debug(f"Value for {item['name']} = {value}") + # logger.debug(f"Value for {item['name']} = {value}") match item['name']: case "submission_type": value, missing = is_missing(value) diff --git a/src/submissions/backend/excel/writer.py b/src/submissions/backend/excel/writer.py index 90bd85e..30c86d1 100644 --- a/src/submissions/backend/excel/writer.py +++ b/src/submissions/backend/excel/writer.py @@ -53,7 +53,8 @@ class SheetWriter(object): template = self.submission_type.template_file workbook = load_workbook(BytesIO(template)) missing_only = False - self.workbook = workbook + # self.workbook = workbook + self.xl = workbook self.write_info() self.write_reagents() self.write_samples() @@ -62,23 +63,23 @@ class SheetWriter(object): def write_info(self): disallowed = ['filepath', 'reagents', 'samples', 'equipment', 'controls'] info_dict = {k: v for k, v in self.sub.items() if k not in disallowed} - writer = InfoWriter(xl=self.workbook, submission_type=self.submission_type, info_dict=info_dict) + writer = InfoWriter(xl=self.xl, submission_type=self.submission_type, info_dict=info_dict) self.xl = writer.write_info() def write_reagents(self): reagent_list = self.sub['reagents'] - writer = ReagentWriter(xl=self.workbook, submission_type=self.submission_type, + writer = ReagentWriter(xl=self.xl, submission_type=self.submission_type, extraction_kit=self.sub['extraction_kit'], reagent_list=reagent_list) self.xl = writer.write_reagents() def write_samples(self): sample_list = self.sub['samples'] - writer = SampleWriter(xl=self.workbook, submission_type=self.submission_type, sample_list=sample_list) + writer = SampleWriter(xl=self.xl, submission_type=self.submission_type, sample_list=sample_list) self.xl = writer.write_samples() def write_equipment(self): equipment_list = self.sub['equipment'] - writer = EquipmentWriter(xl=self.workbook, submission_type=self.submission_type, equipment_list=equipment_list) + writer = EquipmentWriter(xl=self.xl, submission_type=self.submission_type, equipment_list=equipment_list) self.xl = writer.write_equipment() @@ -93,18 +94,18 @@ class InfoWriter(object): self.submission_type = submission_type self.sub_object = sub_object self.xl = xl - map = submission_type.construct_info_map(mode='write') - self.info = self.reconcile_map(info_dict, map) + info_map = submission_type.construct_info_map(mode='write') + self.info = self.reconcile_map(info_dict, info_map) # logger.debug(pformat(self.info)) - def reconcile_map(self, info_dict: dict, map: dict) -> dict: + def reconcile_map(self, info_dict: dict, info_map: dict) -> dict: output = {} for k, v in info_dict.items(): if v is None: continue dicto = {} try: - dicto['locations'] = map[k] + dicto['locations'] = info_map[k] except KeyError: # continue pass @@ -126,7 +127,7 @@ class InfoWriter(object): logger.error(f"No locations for {k}, skipping") continue for loc in locations: - # logger.debug(f"Writing {k} to {loc['sheet']}, row: {loc['row']}, column: {loc['column']}") + logger.debug(f"Writing {k} to {loc['sheet']}, row: {loc['row']}, column: {loc['column']}") sheet = self.xl[loc['sheet']] sheet.cell(row=loc['row'], column=loc['column'], value=v['value']) return self.sub_object.custom_info_writer(self.xl, info=self.info) @@ -141,14 +142,14 @@ class ReagentWriter(object): submission_type = SubmissionType.query(name=submission_type) if isinstance(extraction_kit, str): kit_type = KitType.query(name=extraction_kit) - map = kit_type.construct_xl_map_for_use(submission_type) - self.reagents = self.reconcile_map(reagent_list=reagent_list, map=map) + reagent_map = kit_type.construct_xl_map_for_use(submission_type) + self.reagents = self.reconcile_map(reagent_list=reagent_list, reagent_map=reagent_map) - def reconcile_map(self, reagent_list, map) -> List[dict]: + def reconcile_map(self, reagent_list, reagent_map) -> List[dict]: output = [] for reagent in reagent_list: try: - mp_info = map[reagent['role']] + mp_info = reagent_map[reagent['role']] except KeyError: continue placeholder = copy(reagent) @@ -182,7 +183,7 @@ class SampleWriter(object): submission_type = SubmissionType.query(name=submission_type) self.submission_type = submission_type self.xl = xl - self.map = submission_type.construct_sample_map()['lookup_table'] + self.sample_map = submission_type.construct_sample_map()['lookup_table'] self.samples = self.reconcile_map(sample_list) def reconcile_map(self, sample_list: list): @@ -200,10 +201,10 @@ class SampleWriter(object): return sorted(output, key=lambda k: k['submission_rank']) def write_samples(self): - sheet = self.xl[self.map['sheet']] - columns = self.map['sample_columns'] + sheet = self.xl[self.sample_map['sheet']] + columns = self.sample_map['sample_columns'] for ii, sample in enumerate(self.samples): - row = self.map['start_row'] + (sample['submission_rank'] - 1) + row = self.sample_map['start_row'] + (sample['submission_rank'] - 1) for k, v in sample.items(): try: column = columns[k] @@ -220,13 +221,15 @@ class EquipmentWriter(object): submission_type = SubmissionType.query(name=submission_type) self.submission_type = submission_type self.xl = xl - map = self.submission_type.construct_equipment_map() - self.equipment = self.reconcile_map(equipment_list=equipment_list, map=map) + equipment_map = self.submission_type.construct_equipment_map() + self.equipment = self.reconcile_map(equipment_list=equipment_list, equipment_map=equipment_map) - def reconcile_map(self, equipment_list: list, map: list): + def reconcile_map(self, equipment_list: list, equipment_map: list): output = [] + if equipment_list is None: + return output for ii, equipment in enumerate(equipment_list, start=1): - mp_info = map[equipment['role']] + mp_info = equipment_map[equipment['role']] # logger.debug(f"{equipment['role']} map: {mp_info}") placeholder = copy(equipment) if mp_info == {}: diff --git a/src/submissions/backend/validators/pydant.py b/src/submissions/backend/validators/pydant.py index ae0ef1d..11b6e61 100644 --- a/src/submissions/backend/validators/pydant.py +++ b/src/submissions/backend/validators/pydant.py @@ -573,10 +573,22 @@ class PydSubmission(BaseModel, extra='allow'): @field_validator("contact") @classmethod def get_contact_from_org(cls, value, values): + logger.debug(f"Checking on value: {value}") + match value: + case dict(): + if isinstance(value['value'], tuple): + value['value'] = value['value'][0] + case tuple(): + value = dict(value=value[0], missing=False) + case _: + value = dict(value=value, missing=False) check = Contact.query(name=value['value']) if check is None: org = Organization.query(name=values.data['submitting_lab']['value']) contact = org.contacts[0].name + logger.debug(f"Pulled: {contact}") + if isinstance(contact, tuple): + contact = contact[0] return dict(value=contact, missing=True) else: return value diff --git a/src/submissions/frontend/widgets/gel_checker.py b/src/submissions/frontend/widgets/gel_checker.py index 7c32f2a..c5e62a6 100644 --- a/src/submissions/frontend/widgets/gel_checker.py +++ b/src/submissions/frontend/widgets/gel_checker.py @@ -55,6 +55,9 @@ class GelBox(QDialog): layout.addWidget(QLabel("DNA Core Submission Number"),0,1) self.core_number = QLineEdit() layout.addWidget(self.core_number, 0,2) + layout.addWidget(QLabel("Gel Barcode"),0,3) + self.gel_barcode = QLineEdit() + layout.addWidget(self.gel_barcode, 0, 4) # setting this layout to the widget # plot window goes on right side, spanning 3 rows layout.addWidget(self.imv, 1, 1,20,20) @@ -80,8 +83,9 @@ class GelBox(QDialog): Tuple[str, str|Path, list]: output values """ dna_core_submission_number = self.core_number.text() + gel_barcode = self.gel_barcode.text() values, comment = self.form.parse_form() - return dna_core_submission_number, self.img_path, values, comment + return dna_core_submission_number, gel_barcode, self.img_path, values, comment class ControlsForm(QWidget): diff --git a/src/submissions/frontend/widgets/submission_widget.py b/src/submissions/frontend/widgets/submission_widget.py index 99453af..6d18446 100644 --- a/src/submissions/frontend/widgets/submission_widget.py +++ b/src/submissions/frontend/widgets/submission_widget.py @@ -9,12 +9,12 @@ from pathlib import Path from . import select_open_file, select_save_file import logging, difflib, inspect from pathlib import Path -from tools import Report, Result, check_not_nan, workbook_2_csv +from tools import Report, Result, check_not_nan, workbook_2_csv, main_form_style from backend.excel.parser import SheetParser from backend.validators import PydSubmission, PydReagent from backend.db import ( KitType, Organization, SubmissionType, Reagent, - ReagentRole, KitTypeReagentRoleAssociation + ReagentRole, KitTypeReagentRoleAssociation, BasicSubmission ) from pprint import pformat from .pop_ups import QuestionAsker, AlertPop @@ -149,6 +149,8 @@ class SubmissionFormContainer(QWidget): class SubmissionFormWidget(QWidget): + + def __init__(self, parent: QWidget, submission: PydSubmission) -> None: super().__init__(parent) # self.report = Report() @@ -169,15 +171,16 @@ class SubmissionFormWidget(QWidget): except AttributeError: logger.error(f"Couldn't get attribute from pyd: {k}") value = dict(value=None, missing=True) - add_widget = self.create_widget(key=k, value=value, submission_type=self.pyd.submission_type['value']) + add_widget = self.create_widget(key=k, value=value, submission_type=self.pyd.submission_type['value'], sub_obj=st) if add_widget != None: self.layout.addWidget(add_widget) if k == "extraction_kit": add_widget.input.currentTextChanged.connect(self.scrape_reagents) + self.setStyleSheet(main_form_style) self.scrape_reagents(self.pyd.extraction_kit) def create_widget(self, key: str, value: dict | PydReagent, submission_type: str | None = None, - extraction_kit: str | None = None) -> "self.InfoItem": + extraction_kit: str | None = None, sub_obj:BasicSubmission|None=None) -> "self.InfoItem": """ Make an InfoItem widget to hold a field @@ -197,13 +200,13 @@ class SubmissionFormWidget(QWidget): else: widget = None case _: - widget = self.InfoItem(self, key=key, value=value, submission_type=submission_type) + widget = self.InfoItem(self, key=key, value=value, submission_type=submission_type, sub_obj=sub_obj) return widget return None def scrape_reagents(self, *args, **kwargs): #extraction_kit:str, caller:str|None=None): """ - Extracted scrape reagents function that will run when + Extracted scrape reagents function that will run when form 'extraction_kit' widget is updated. Args: @@ -395,11 +398,11 @@ class SubmissionFormWidget(QWidget): class InfoItem(QWidget): - def __init__(self, parent: QWidget, key: str, value: dict, submission_type: str | None = None) -> None: + def __init__(self, parent: QWidget, key: str, value: dict, submission_type: str | None = None, sub_obj:BasicSubmission|None=None) -> None: super().__init__(parent) layout = QVBoxLayout() self.label = self.ParsedQLabel(key=key, value=value) - self.input: QWidget = self.set_widget(parent=self, key=key, value=value, submission_type=submission_type) + self.input: QWidget = self.set_widget(parent=self, key=key, value=value, submission_type=submission_type, sub_obj=sub_obj) self.setObjectName(key) try: self.missing: bool = value['missing'] @@ -436,7 +439,7 @@ class SubmissionFormWidget(QWidget): return None, None return self.input.objectName(), dict(value=value, missing=self.missing) - def set_widget(self, parent: QWidget, key: str, value: dict, submission_type: str | None = None) -> QWidget: + def set_widget(self, parent: QWidget, key: str, value: dict, submission_type: str | None = None, sub_obj:BasicSubmission|None=None) -> QWidget: """ Creates form widget @@ -449,6 +452,8 @@ class SubmissionFormWidget(QWidget): Returns: QWidget: Form object """ + if sub_obj is None: + sub_obj = SubmissionType.query(name=submission_type).get_submission_class() try: value = value['value'] except (TypeError, KeyError): @@ -488,15 +493,15 @@ class SubmissionFormWidget(QWidget): logger.error(f"Couldn't find {obj.prsr.sub['extraction_kit']}") obj.ext_kit = uses[0] add_widget.addItems(uses) - case 'submitted_date': - # NOTE: uses base calendar - add_widget = QDateEdit(calendarPopup=True) - # NOTE: sets submitted date based on date found in excel sheet - try: - add_widget.setDate(value) - # NOTE: if not found, use today - except: - add_widget.setDate(date.today()) + # case 'submitted_date': + # # NOTE: uses base calendar + # add_widget = QDateEdit(calendarPopup=True) + # # NOTE: sets submitted date based on date found in excel sheet + # try: + # add_widget.setDate(value) + # # NOTE: if not found, use today + # except: + # add_widget.setDate(date.today()) case 'submission_category': add_widget = QComboBox() cats = ['Diagnostic', "Surveillance", "Research"] @@ -507,13 +512,23 @@ class SubmissionFormWidget(QWidget): cats.insert(0, cats.pop(cats.index(submission_type))) add_widget.addItems(cats) case _: - # NOTE: anything else gets added in as a line edit - add_widget = QLineEdit() - # logger.debug(f"Setting widget text to {str(value).replace('_', ' ')}") - add_widget.setText(str(value).replace("_", " ")) + if key in sub_obj.timestamps(): + add_widget = QDateEdit(calendarPopup=True) + # NOTE: sets submitted date based on date found in excel sheet + try: + add_widget.setDate(value) + # NOTE: if not found, use today + except: + add_widget.setDate(date.today()) + else: + # NOTE: anything else gets added in as a line edit + add_widget = QLineEdit() + # logger.debug(f"Setting widget text to {str(value).replace('_', ' ')}") + add_widget.setText(str(value).replace("_", " ")) if add_widget is not None: add_widget.setObjectName(key) add_widget.setParent(parent) + # add_widget.setStyleSheet(main_form_style) return add_widget def update_missing(self): @@ -569,6 +584,7 @@ class SubmissionFormWidget(QWidget): self.label = self.ReagentParsedLabel(reagent=reagent) layout.addWidget(self.label) self.lot = self.ReagentLot(reagent=reagent, extraction_kit=extraction_kit) + # self.lot.setStyleSheet(main_form_style) layout.addWidget(self.lot) # NOTE: Remove spacing between reagents layout.setContentsMargins(0, 0, 0, 0) @@ -615,7 +631,7 @@ class SubmissionFormWidget(QWidget): Set widget status to updated """ self.missing = True - self.label.updated(self.reagent.type) + self.label.updated(self.reagent.role) class ReagentParsedLabel(QLabel): @@ -692,4 +708,4 @@ class SubmissionFormWidget(QWidget): # logger.debug(f"New relevant reagents: {relevant_reagents}") self.setObjectName(f"lot_{reagent.role}") self.addItems(relevant_reagents) - self.setStyleSheet("{ background-color: white }") + # self.setStyleSheet(main_form_style) diff --git a/src/submissions/tools.py b/src/submissions/tools.py index 0e32d6e..7df34df 100644 --- a/src/submissions/tools.py +++ b/src/submissions/tools.py @@ -43,6 +43,13 @@ LOGDIR = main_aux_dir.joinpath("logs") row_map = {1: "A", 2: "B", 3: "C", 4: "D", 5: "E", 6: "F", 7: "G", 8: "H"} row_keys = {v: k for k, v in row_map.items()} +main_form_style = ''' + QComboBox:!editable, QDateEdit { + background-color:light gray; + } + + ''' + def check_not_nan(cell_contents) -> bool: """ @@ -329,6 +336,19 @@ def get_config(settings_path: Path | str | None = None) -> Settings: return Settings(**settings) +def check_if_app() -> bool: + """ + Checks if the program is running from pyinstaller compiled + + Returns: + bool: True if running from pyinstaller. Else False. + """ + if getattr(sys, 'frozen', False): + return True + else: + return False + + # Logging formatters class GroupWriteRotatingFileHandler(handlers.RotatingFileHandler): @@ -362,23 +382,23 @@ class CustomFormatter(logging.Formatter): BOLD = '\033[1m' UNDERLINE = '\033[4m' - format = "%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s" + log_format = "%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s" FORMATS = { - logging.DEBUG: bcolors.ENDC + format + bcolors.ENDC, - logging.INFO: bcolors.ENDC + format + bcolors.ENDC, - logging.WARNING: bcolors.WARNING + format + bcolors.ENDC, - logging.ERROR: bcolors.FAIL + format + bcolors.ENDC, - logging.CRITICAL: bcolors.FAIL + format + bcolors.ENDC + logging.DEBUG: bcolors.ENDC + log_format + bcolors.ENDC, + logging.INFO: bcolors.ENDC + log_format + bcolors.ENDC, + logging.WARNING: bcolors.WARNING + log_format + bcolors.ENDC, + logging.ERROR: bcolors.FAIL + log_format + bcolors.ENDC, + logging.CRITICAL: bcolors.FAIL + log_format + bcolors.ENDC } def format(self, record): - log_fmt = self.FORMATS.get(record.levelno) - formatter = logging.Formatter(log_fmt) if check_if_app(): - return record + log_fmt = self.log_format else: - return formatter.format(record) + log_fmt = self.FORMATS.get(record.levelno) + formatter = logging.Formatter(log_fmt) + return formatter.format(record) class StreamToLogger(object): @@ -490,19 +510,6 @@ def jinja_template_loading() -> Environment: return env -def check_if_app() -> bool: - """ - Checks if the program is running from pyinstaller compiled - - Returns: - bool: True if running from pyinstaller. Else False. - """ - if getattr(sys, 'frozen', False): - return True - else: - return False - - def convert_well_to_row_column(input_str: str) -> Tuple[int, int]: """ Converts typical alphanumeric (i.e. "A2") to row, column @@ -591,7 +598,7 @@ class Report(BaseModel): logger.info(f"Adding {res} from {result} to results.") self.results.append(res) case _: - logger.error(f"Unknown variable type: {type(result)}") + logger.error(f"Unknown variable type: {type(result)} for entry into ") def is_empty(self): return bool(self.results)