From b1c817432ba35b91467bd7da77a69c58bffd7c81 Mon Sep 17 00:00:00 2001 From: Cole Deck Date: Fri, 20 Dec 2024 23:41:19 +0000 Subject: [PATCH] Replace item dialogs with Popups for better integration --- db_classes.py | 11 +- inventory/pages/add_page.py | 2 + inventory/pages/browse_page.py | 504 +++++++++++++++------------------ search.py | 2 +- 4 files changed, 245 insertions(+), 274 deletions(-) diff --git a/db_classes.py b/db_classes.py index 5b89a00..d0b9328 100644 --- a/db_classes.py +++ b/db_classes.py @@ -82,7 +82,7 @@ def init(): db.create_tables([location, office, item, component, user]) print("Database initialized.") global search - print("Creating cache index...") + print("Creating cache index... ", end='', flush=True) search = ivs() add = item.select().dicts() #print(add) @@ -92,9 +92,10 @@ def init(): itm["location"] = item.select().where(item.barcode==itm["barcode"])[0].loc.name except: pass - print(itm) + #print(itm) #print(type(itm)) search.add_document(itm) + print(len(add)) print("Cache build complete.") def search_item(query, filters: dict={}): @@ -128,7 +129,7 @@ def find_item(barcode): def find_item_location(barcode): try: - return item.select().where(item.barcode==barcode).loc + return item.select().where(item.barcode==barcode)[0].loc except: return False @@ -311,11 +312,11 @@ def get_location(name, parent=None): def get_location_id(barcode): try: - print("str" + barcode + "str") + #print("str" + barcode + "str") if len(barcode) > 0: query = location.select() for loc in query: - print(loc.name, loc.locationid) + #print(loc.name, loc.locationid) if loc.locationid == barcode: return loc return False diff --git a/inventory/pages/add_page.py b/inventory/pages/add_page.py index 7e9a8da..97e6a66 100644 --- a/inventory/pages/add_page.py +++ b/inventory/pages/add_page.py @@ -74,6 +74,8 @@ class AddPage(rio.Component): self.popup_show = False else: # OK, add part + if self.location == "": + self.location_code = "" if create_item(self.partnum, self.serial, self.office, self.code, locationid=self.location_code, description=self.description, manufacturer=self.manu, mac=self.mac, fwver=self.fwver) == False: self.popup_message = "\n Duplicate barcode! \n\n" self.popup_show = True diff --git a/inventory/pages/browse_page.py b/inventory/pages/browse_page.py index a2bd854..817b2d0 100644 --- a/inventory/pages/browse_page.py +++ b/inventory/pages/browse_page.py @@ -35,6 +35,19 @@ class BrowsePage(rio.Component): emanufield: str = "" eoffice: str = "" edescription: str = "" + info_show = False + edit_show = False + + iname: str ="" + imanu: str ="" + iserial: str ="" + imac: str ="" + ifw: str ="" + iloc: str ="" + icheckouts: str ="" + icheckout_times: str ="" + ibarcode: str ="" + idesc: str ="" @rio.event.on_populate async def _search(self, query=searchtext): @@ -56,103 +69,74 @@ class BrowsePage(rio.Component): async def _search_trigger(self, event: rio.TextInputChangeEvent): await self._search(event.text) - async def _create_dialog_info(self, code: str) -> str | None: - async def copy_info(text: str): - await self.session.set_clipboard(text) - # TODO: show "Copied!" popup - self.popup_message = "\n Copied! \n\n" - self.popup_color = 'success' - self.popup_show = True - await asyncio.sleep(1.5) - self.popup_show = False - - def build_dialog_info() -> rio.Component: - # Build the dialog - - itm: dict = find_item(code) + async def copy_info(self, text: str): + self.popup_message = "\n Copied! \n\n" + self.popup_color = 'success' + await self.session.set_clipboard(text) + self.popup_show = True + await asyncio.sleep(1.5) + self.popup_show = False + async def _open_info_popup(self, code: str, skip_display=False) -> str | None: + itm: dict = find_item(code) - try: - loc = itm["location"] - except: - loc = "" - if itm["checkout"]: - checkout = itm["checkout_user"] + " - " + loc - else: - checkout = loc + try: + self.iloc = itm["location"] + except: + self.iloc = "" + if itm["checkout"]: + checkout = itm["checkout_user"] + " - " + self.iloc + else: + checkout = self.iloc + self.iname=str(itm["fullname"]) + self.imanu=str(itm["manufacturer"]) + self.iserial=str(itm["serial"]) + self.imac=str(itm["mac"]) + self.ifw=str(itm["fwver"]) + self.iloc=str(checkout) + self.icheckouts=str(itm["checkout"]) + self.icheckout_times=str(itm["checkout_start"]) + " to " + str(itm["checkout_end"]) + #office=str(itm["office"]) + self.ibarcode=str(itm["barcode"]) + self.idesc=str(itm["description"]) + + if not skip_display: + self.edit_show = False + self.info_show = True + - details: rio.ListView = rio.ListView(grow_y=True, min_width=40) - # for key, val in itm.items(): - # details.add(rio.SimpleListItem(text=key,secondary_text=val)) - #functools.partial(copy_info, text=item["barcode"]) - name=str(itm["fullname"]) - manu=str(itm["manufacturer"]) - serial=str(itm["serial"]) - mac=str(itm["mac"]) - fw=str(itm["fwver"]) - loc=str(checkout) - checkouts=str(itm["checkout"]) - checkout_times=str(itm["checkout_start"]) + " to " + str(itm["checkout_end"]) - #office=str(itm["office"]) - barcode=str(itm["barcode"]) - desc=str(itm["description"]) - details.add(rio.SimpleListItem(text=name, on_press=functools.partial(copy_info, text=name))) - details.add(rio.SimpleListItem(text=manu, on_press=functools.partial(copy_info, text=manu))) - details.add(rio.SimpleListItem(text="Serial",secondary_text=serial, on_press=functools.partial(copy_info, text=serial))) - details.add(rio.SimpleListItem(text="MAC",secondary_text=mac, on_press=functools.partial(copy_info, text=mac))) - details.add(rio.SimpleListItem(text="FW Version",secondary_text=fw, on_press=functools.partial(copy_info, text=fw))) - details.add(rio.SimpleListItem(text="Location",secondary_text=loc, on_press=functools.partial(copy_info, text=loc))) - details.add(rio.SimpleListItem(text="Checked out?",secondary_text=checkouts, on_press=functools.partial(copy_info, text=checkouts))) - details.add(rio.SimpleListItem(text="Checkout start/end",secondary_text=checkout_times, on_press=functools.partial(copy_info, text=checkout_times))) - #details.add(rio.SimpleListItem(text="Office",secondary_text=office, on_press=functools.partial(copy_info, text=office))) - details.add(rio.SimpleListItem(text="Barcode",secondary_text=barcode, on_press=functools.partial(copy_info, text=barcode))) - details.add(rio.SimpleListItem(text="Description",secondary_text=desc, on_press=functools.partial(copy_info, text=desc))) + async def _close_info_popup(self): + self.edit_show = False + self.info_show = False + await self.force_refresh() - return rio.Card( - rio.Column( - details, - rio.Row( - rio.Button( - content="Close", - on_press=_close_dialog_info - ), - rio.Button( - content="Edit", - on_press=functools.partial(self._create_dialog_edit, code=code) - ), - spacing=2, - margin=2 - ), - spacing=1, - margin=2, - ), - align_x=0.5, - align_y=0.5, - ) + async def _close_edit_popup(self): + await self._open_info_popup(self.ibarcode) + await self.force_refresh() - async def _close_dialog_info() -> None: - # This function will be called whenever the user selects an - # Item. It simply closes the dialog with the selected value. - await dialog.close() + async def _save_edit_popup(self): + self.edit_show = False + self.info_show = True + await self.force_refresh() + await self._add_part_button() + await asyncio.sleep(1) + await self._open_info_popup(self.ibarcode, skip_display=True) + await self._search() + await asyncio.sleep(1) + self.popup_show = False + await self._open_info_popup(self.ibarcode, skip_display=True) + await self._search() + - dialog = await self.session.show_custom_dialog( - build=build_dialog_info, - # Prevent the user from interacting with the rest of the app - # while the dialog is open - modal=True, - # Don't close the dialog if the user clicks outside of it - user_closeable=True, - ) - # Wait for the user to select an option - result = await dialog.wait_for_close() + async def _delete_edit_popup(self): + self.edit_show = False + self.info_show = False + # TODO: delete here + await self.force_refresh() - # Return the selected value - return result - - async def _create_dialog_edit(self, code: str) -> str | None: + async def _open_edit_popup(self, code: str) -> str | None: itm: dict = find_item(code) - from mac_vendor_lookup import AsyncMacLookup try: self.elocation: str = itm["location"] self.elocation_code: str = find_item_location(code).locationid @@ -173,189 +157,46 @@ class BrowsePage(rio.Component): self.emanufield: str = itm["manufacturer"] self.eoffice: str = itm["office"] self.edescription: str = itm["description"] + self.info_show = False + self.edit_show = True - async def check_mac(mac): - print("Checking", mac) - self.emac = mac - try: - macvendor = await AsyncMacLookup().lookup(mac) - if self.emanufield == "" or self.emanufield == self.emacvendor: # blank or set by MAC already - self.emanu = macvendor - self.emanufield = macvendor - else: - self.emanu = self.emanufield - self.emacvendor = macvendor - #print(macvendor) - except: - pass - # not valid MAC? - - async def check_all(): - await check_mac(self.emac) - # check part number - # lookup in PL_Export_rel - - async def add_part(): - await check_all() - if self.ecode == "": - # FAIL - self.popup_message = "\n Missing barcode! \n\n" + async def add_part(self): + if self.ecode == "": + # FAIL + self.popup_message = "\n Missing barcode! \n\n" + self.popup_show = True + self.popup_color = 'danger' + + else: + # OK, add part + if self.elocation == "": + self.elocation_code = "" + if update_item(self.epartnum, self.eserial, self.office, self.ecode, locationid=self.elocation_code, description=self.edescription, manufacturer=self.emanu, mac=self.emac, fwver=self.efwver) == False: + self.popup_message = "\n Unable to update! \n\n" self.popup_show = True - self.popup_color = 'danger' - - else: - # OK, add part - if update_item(self.epartnum, self.eserial, self.office, self.ecode, locationid=self.elocation_code, description=self.edescription, manufacturer=self.emanu, mac=self.emac, fwver=self.efwver) == False: - self.popup_message = "\n Unable to update! \n\n" - self.popup_show = True - self.popup_color = 'warning' - else: - self.popup_message = "\n Part updated! \n\n" - self.popup_show = True - self.popup_color = 'success' - #self.ename: str = "" - self.epartnum: str = "" - self.emac: str = "" - self.eserial: str = "" - self.efwver: str = "" - self.ecode: str = "" - self.emacvendor: str = "" - self.emanu: str = "" - self.emanufield: str = "" - self.edescription: str = "" - self.elocation: str = "" - self.elocation_code: str = "" - - - async def _add_part_enter(event: rio.TextInputConfirmEvent): - await add_part() - - async def _add_part_button(): - await add_part() - - async def _update_mac(event: rio.TextInputChangeEvent): - await check_mac(event.text) - - async def _update_location(event: rio.TextInputChangeEvent): - print("Checking " + event.text) - self.elocation = event.text - if get_location_id(event.text) != False: - self.elocation_code = event.text - print("Found location " + get_location_id(event.text).name) - self.elocation = get_location_id(event.text).name + self.popup_color = 'warning' + else: + self.popup_message = "\n Part updated! \n\n" + self.popup_show = True + self.popup_color = 'success' + #self.ename: str = "" + self.epartnum = "" + self.emac = "" + self.eserial = "" + self.efwver = "" + self.ecode = "" + self.emacvendor = "" + self.emanu = "" + self.emanufield = "" + self.edescription = "" + self.elocation = "" + self.elocation_code = "" - async def _update_epartnum(event: rio.TextInputChangeEvent): - self.epartnum = event.text - - async def _update_eserial(event: rio.TextInputChangeEvent): - self.eserial = event.text - - async def _update_emanufield(event: rio.TextInputChangeEvent): - self.emanufield = event.text - self.emanu = event.text - async def _update_efwver(event: rio.TextInputChangeEvent): - self.efwver = event.text - async def _update_edescription(event: rio.TextInputChangeEvent): - self.edescription = event.text - def build_dialog_edit() -> rio.Component: - # Build the dialog - return rio.Card( - rio.Column( - rio.TextInput( - label="Barcode", - text=self.ecode, - is_sensitive=False - ), - rio.TextInput( - label="Full part number", - text=self.epartnum, - on_change=_update_epartnum - ), - rio.TextInput( - label="Serial", - text=self.eserial, - on_change=_update_eserial - ), - rio.TextInput( - label="MAC", - text=self.emac, - on_change=_update_mac, - ), - rio.TextInput( - label="Location (optional)", - text=self.elocation, - on_change=_update_location, - ), - rio.TextInput( - label="Manufacturer", - text=self.emanufield, - on_change=_update_emanufield - ), - rio.TextInput( - label="FW Ver", - text=self.efwver, - on_change=_update_efwver - ), - rio.MultiLineTextInput( - label="Description (optional)", - text=self.edescription, - on_change=_update_edescription - ), - - rio.Row( - rio.Button( - content="Cancel", - on_press=_close_dialog_edit, - color='warning' - ), - rio.Button( - content="Delete", - on_press=_delete_dialog_edit, - color='danger' - ), - rio.Button( - content="Save", - on_press=_save_dialog_edit - ), - spacing=2, - margin=2 - ), - spacing=1, - margin=2 - ), - align_x=0.5, - align_y=0.5 - ) - - async def _close_dialog_edit() -> None: - await dialog.close() - - async def _save_dialog_edit() -> None: - await dialog.close() - await _add_part_button() - await asyncio.sleep(2) - self.popup_show = False - - async def _delete_dialog_edit() -> None: - await dialog.close() - - dialog = await self.session.show_custom_dialog( - build=build_dialog_edit, - # Prevent the user from interacting with the rest of the app - # while the dialog is open - modal=True, - # Don't close the dialog if the user clicks outside of it - user_closeable=False, - ) - - # Wait for the user to select an option - result = await dialog.wait_for_close() - - # Return the selected value - return result + async def _add_part_button(self): + await self.add_part() def click_item_page(self, code): self.session[comps.Settings].selected_item = code @@ -364,9 +205,14 @@ class BrowsePage(rio.Component): async def click_item_dialog(self, code): self.session[comps.Settings].selected_item = code - #self.session.attach(self.session[comps.Settings]) - #self.session.navigate_to("/item") - ret = await self._create_dialog_info(code) + await self._open_info_popup(code) + + async def _update_elocation(self, event: rio.TextInputChangeEvent): + print("Checking " + self.elocation) + if get_location_id(self.elocation) != False: + self.elocation_code = self.elocation + print("Found location " + get_location_id(self.elocation).name) + self.elocation = get_location_id(self.elocation).name def build(self) -> rio.Component: searchview: rio.ListView = rio.ListView(grow_y=True) @@ -382,6 +228,22 @@ class BrowsePage(rio.Component): searchview.add(rio.SimpleListItem(text=item["fullname"],secondary_text=(item["manufacturer"] + " - Serial: " + item["serial"] + "\n" + checkout), on_press=functools.partial( self.click_item_dialog, code=item["barcode"]))) + + details = rio.ListView(grow_y=True, min_width=40) + + details.add(rio.SimpleListItem(text=self.iname, on_press=functools.partial(self.copy_info, text=self.iname))) + details.add(rio.SimpleListItem(text=self.imanu, on_press=functools.partial(self.copy_info, text=self.imanu))) + details.add(rio.SimpleListItem(text="Serial",secondary_text=self.iserial, on_press=functools.partial(self.copy_info, text=self.iserial))) + details.add(rio.SimpleListItem(text="MAC",secondary_text=self.imac, on_press=functools.partial(self.copy_info, text=self.imac))) + details.add(rio.SimpleListItem(text="FW Version",secondary_text=self.ifw, on_press=functools.partial(self.copy_info, text=self.ifw))) + details.add(rio.SimpleListItem(text="Location",secondary_text=self.iloc, on_press=functools.partial(self.copy_info, text=self.iloc))) + details.add(rio.SimpleListItem(text="Checked out?",secondary_text=self.icheckouts, on_press=functools.partial(self.copy_info, text=self.icheckouts))) + details.add(rio.SimpleListItem(text="Checkout start/end",secondary_text=self.icheckout_times, on_press=functools.partial(self.copy_info, text=self.icheckout_times))) + #details.add(rio.SimpleListItem(text="Office",secondary_text=office, on_press=functools.partial(copy_info, text=office))) + details.add(rio.SimpleListItem(text="Barcode",secondary_text=self.ibarcode, on_press=functools.partial(self.copy_info, text=self.ibarcode))) + details.add(rio.SimpleListItem(text="Description",secondary_text=self.idesc, on_press=functools.partial(self.copy_info, text=self.idesc))) + + return rio.Column( rio.Row( rio.TextInput( @@ -404,6 +266,112 @@ class BrowsePage(rio.Component): ), ), + rio.Popup( + anchor=rio.Text( + text="", + style='heading1', + align_x = 0.5 + ), + #color=self.popup_color, + is_open=self.info_show, + min_width=40, + position='fullscreen', + color=rio.Color.TRANSPARENT, + content=rio.Card( + rio.Column( + details, + rio.Row( + rio.Button( + content="Close", + on_press=self._close_info_popup + ), + rio.Button( + content="Edit", + on_press=functools.partial(self._open_edit_popup, code=self.ibarcode) + ), + spacing=2, + margin=2 + ), + spacing=1, + margin=2, + ), + align_x=0.5, + align_y=0.5, + ) + ), + rio.Popup( + anchor=rio.Text( + text="", + style='heading1', + align_x = 0.5 + ), + #color=self.popup_color, + is_open=self.edit_show, + min_width=40, + position='fullscreen', + color=rio.Color.TRANSPARENT, + content=rio.Card( + rio.Column( + rio.TextInput( + label="Barcode", + text=self.bind().ecode, + is_sensitive=False + ), + rio.TextInput( + label="Full part number", + text=self.bind().epartnum, + ), + rio.TextInput( + label="Serial", + text=self.bind().eserial, + ), + rio.TextInput( + label="MAC", + text=self.bind().emac, + ), + rio.TextInput( + label="Location", + text=self.bind().elocation, + on_change=self._update_elocation, + ), + rio.TextInput( + label="Manufacturer", + text=self.bind().emanu, + ), + rio.TextInput( + label="FW Ver", + text=self.bind().efwver + ), + rio.MultiLineTextInput( + label="Description", + text=self.bind().edescription + ), + + rio.Row( + rio.Button( + content="Cancel", + on_press=self._close_edit_popup, + color='warning' + ), + rio.Button( + content="Delete", + on_press=self._delete_edit_popup, + color='danger' + ), + rio.Button( + content="Save", + on_press=self._save_edit_popup + ), + spacing=2, + margin=2 + ), + spacing=1, + margin=2 + ), + align_x=0.5, + align_y=0.5 + ) + ), spacing=2, min_width=60, align_x=0.5, diff --git a/search.py b/search.py index 9e6be66..261ab8a 100644 --- a/search.py +++ b/search.py @@ -89,7 +89,7 @@ class InventorySearch: :param filters: A meilisearch compatible filter statement. :returns: The search results dict. Actual results are in a list under "hits", but there are other nice values that are useful in the root element.""" if filters: - q = self.idxref.search(query, {"filter": filters}) + q = self.idxref.search(query, {"filter": filters, "limit": 1000}) else: q = self.idxref.search(query) return q