Add edit page, dialog based item page. Consider switching to popups.
This commit is contained in:
parent
2a8de79adb
commit
27169ff8a0
@ -1,10 +1,6 @@
|
||||
FROM python:3.11-slim
|
||||
FROM python:3.13-slim
|
||||
|
||||
# Get runtime dependencies
|
||||
# glx for OpenCV, ghostscript for datasheet PDF rendering, zbar for barcode scanning, git for cloning repos
|
||||
#RUN apt-get update && apt-get install -y libgl1-mesa-glx ghostscript libzbar0 git && apt-get clean && rm -rf /var/lib/apt/lists
|
||||
COPY requirements.txt ./
|
||||
#COPY config-server.yml config.yml
|
||||
RUN pip3 install -r requirements.txt
|
||||
COPY *.py *.txt *.toml ./
|
||||
COPY inventory ./inventory
|
||||
|
@ -24,7 +24,7 @@ class office(Model):
|
||||
|
||||
class location(Model):
|
||||
name = CharField()
|
||||
locationid = AutoField()
|
||||
locationid = CharField(unique=True, primary_key=True)
|
||||
description = CharField(null=True)
|
||||
|
||||
parent = ForeignKeyField('self', null=True, backref="sublocations")
|
||||
@ -42,6 +42,7 @@ class item(Model):
|
||||
description = CharField(null=True)
|
||||
serial = CharField(null=True)
|
||||
checkout = BooleanField(default=False)
|
||||
checkout_loc = ForeignKeyField(location, backref="items_checkedout_here", null=True)
|
||||
checkout_user = ForeignKeyField(user, backref="items_held", null=True)
|
||||
checkout_start = DateTimeField(null=True)
|
||||
checkout_end = DateTimeField(null=True)
|
||||
@ -70,7 +71,13 @@ class component(Model):
|
||||
|
||||
def init():
|
||||
print("Connecting to database...")
|
||||
import time
|
||||
while True:
|
||||
try:
|
||||
db.connect()
|
||||
break
|
||||
except:
|
||||
time.sleep(1)
|
||||
print("Checking & creating tables...")
|
||||
db.create_tables([location, office, item, component, user])
|
||||
print("Database initialized.")
|
||||
@ -81,6 +88,10 @@ def init():
|
||||
#print(add)
|
||||
#print(type(add))
|
||||
for itm in add:
|
||||
try:
|
||||
itm["location"] = item.select().where(item.barcode==itm["barcode"])[0].loc.name
|
||||
except:
|
||||
pass
|
||||
print(itm)
|
||||
#print(type(itm))
|
||||
search.add_document(itm)
|
||||
@ -115,17 +126,65 @@ def search_item(query, filters: dict={}):
|
||||
def find_item(barcode):
|
||||
return search.get_barcode(barcode)
|
||||
|
||||
def create_item(fullname, serial, officename, barcode, location=None, description=None, manufacturer=None, mac=None, fwver=None):
|
||||
def find_item_location(barcode):
|
||||
try:
|
||||
return item.select().where(item.barcode==barcode).loc
|
||||
except:
|
||||
return False
|
||||
|
||||
def create_item(fullname, serial, officename, barcode, locationid=None, description=None, manufacturer=None, mac=None, fwver=None):
|
||||
try:
|
||||
off = office(name=officename)
|
||||
off.save(force_insert=True)
|
||||
except IntegrityError:
|
||||
pass
|
||||
try:
|
||||
loc = get_location_id(locationid)
|
||||
if loc == False:
|
||||
loc = None
|
||||
else:
|
||||
print("Found location: " + loc.name)
|
||||
off = office.select().where(office.name == officename)[0]
|
||||
itm = item(office=off, barcode=barcode, fullname=fullname, description=description, loc=location, serial=serial, mac=mac, fwver=fwver, manufacturer=manufacturer)
|
||||
itm = item(office=off, barcode=barcode, fullname=fullname, description=description, loc=loc, serial=serial, mac=mac, fwver=fwver, manufacturer=manufacturer)
|
||||
itm.save(force_insert=True)
|
||||
search.add_document(item.select().where(item.barcode==barcode).dicts()[0])
|
||||
itmdict= item.select().where(item.barcode==barcode).dicts()[0]
|
||||
try:
|
||||
itmdict["location"] = loc.name
|
||||
#print(locationid)
|
||||
#print(itmdict["location"])
|
||||
except:
|
||||
pass
|
||||
search.add_document(itmdict)
|
||||
print("item: " + itm.fullname)
|
||||
return itm
|
||||
except IntegrityError:
|
||||
print("Duplicate item " + fullname)
|
||||
return False
|
||||
|
||||
|
||||
def update_item(fullname, serial, officename, barcode, locationid=None, description=None, manufacturer=None, mac=None, fwver=None):
|
||||
try:
|
||||
off = office(name=officename)
|
||||
off.save(force_insert=True)
|
||||
except IntegrityError:
|
||||
pass
|
||||
try:
|
||||
loc = get_location_id(locationid)
|
||||
if loc == False:
|
||||
loc = None
|
||||
else:
|
||||
print("Found location: " + loc.name)
|
||||
off = office.select().where(office.name == officename)[0]
|
||||
itm = item(office=off, barcode=barcode, fullname=fullname, description=description, loc=loc, serial=serial, mac=mac, fwver=fwver, manufacturer=manufacturer)
|
||||
itm.save()
|
||||
itmdict= item.select().where(item.barcode==barcode).dicts()[0]
|
||||
try:
|
||||
itmdict["location"] = loc.name
|
||||
#print(locationid)
|
||||
#print(itmdict["location"])
|
||||
except:
|
||||
pass
|
||||
search.add_document(itmdict)
|
||||
print("item: " + itm.fullname)
|
||||
return itm
|
||||
except IntegrityError:
|
||||
@ -196,7 +255,7 @@ def checkout(user, barcode, loc=None):
|
||||
if itm:
|
||||
itm.checkout = True
|
||||
itm.checkout_user = user
|
||||
itm.loc = loc
|
||||
itm.checkout_loc = loc
|
||||
itm.save()
|
||||
return itm
|
||||
else:
|
||||
@ -207,6 +266,7 @@ def checkin(user, barcode, loc=None):
|
||||
if itm:
|
||||
itm.checkout = False
|
||||
itm.last_user = user
|
||||
if loc is not None:
|
||||
itm.loc = loc
|
||||
itm.save()
|
||||
return itm
|
||||
@ -214,12 +274,13 @@ def checkin(user, barcode, loc=None):
|
||||
return False
|
||||
|
||||
|
||||
def create_location(name, parent=None):
|
||||
if parent is not None:
|
||||
loc = location(name=name, parent=parent)
|
||||
loc.save()
|
||||
def create_location(name, barcode, parent=None, description=None):
|
||||
try:
|
||||
loc = location(name=name, locationid=barcode, parent=parent, description=description)
|
||||
loc.save(force_insert=True)
|
||||
print(loc.name, loc.locationid)
|
||||
return loc
|
||||
else:
|
||||
except:
|
||||
return False
|
||||
|
||||
def _find_parent(loc, parent):
|
||||
@ -248,6 +309,21 @@ def get_location(name, parent=None):
|
||||
except:
|
||||
return False
|
||||
|
||||
def get_location_id(barcode):
|
||||
try:
|
||||
print("str" + barcode + "str")
|
||||
if len(barcode) > 0:
|
||||
query = location.select()
|
||||
for loc in query:
|
||||
print(loc.name, loc.locationid)
|
||||
if loc.locationid == barcode:
|
||||
return loc
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def get_user(name):
|
||||
query = user.select().where(user.username == name)
|
||||
if len(query) == 1:
|
||||
|
@ -35,26 +35,31 @@ app = rio.App(
|
||||
comps.Settings(),
|
||||
],
|
||||
pages=[
|
||||
rio.Page(
|
||||
rio.ComponentPage(
|
||||
name="Home",
|
||||
page_url='',
|
||||
url_segment='',
|
||||
build=pages.BrowsePage,
|
||||
),
|
||||
|
||||
rio.Page(
|
||||
name="AboutPage",
|
||||
page_url='about-page',
|
||||
build=pages.AboutPage,
|
||||
rio.ComponentPage(
|
||||
name="SettingsPage",
|
||||
url_segment='settings-page',
|
||||
build=pages.SettingsPage,
|
||||
),
|
||||
|
||||
rio.Page(
|
||||
rio.ComponentPage(
|
||||
name="AddPage",
|
||||
page_url='add',
|
||||
url_segment='add',
|
||||
build=pages.AddPage,
|
||||
),
|
||||
rio.Page(
|
||||
rio.ComponentPage(
|
||||
name="AddLocationPage",
|
||||
url_segment='addlocation',
|
||||
build=pages.AddLocationPage,
|
||||
),
|
||||
rio.ComponentPage(
|
||||
name="ItemPage",
|
||||
page_url='item',
|
||||
url_segment='item',
|
||||
build=pages.ItemPage,
|
||||
),
|
||||
],
|
||||
|
@ -16,7 +16,6 @@ class Footer(rio.Component):
|
||||
def build(self) -> rio.Component:
|
||||
return rio.Card(
|
||||
content=rio.Column(
|
||||
rio.Icon("rio/logo:fill", width=5, height=5),
|
||||
rio.Text("Buzzwordz Inc."),
|
||||
rio.Text(
|
||||
"Hyper Dyper Website",
|
||||
|
@ -60,7 +60,7 @@ class Navbar(rio.Component):
|
||||
# you've passed the app during creation. Since multiple pages can be
|
||||
# active at a time (e.g. /foo/bar/baz), this is a list.
|
||||
active_page = self.session.active_page_instances[0]
|
||||
active_page_url_segment = active_page.page_url
|
||||
active_page_url_segment = active_page.url_segment
|
||||
|
||||
# The navbar should appear above all other components. This is easily
|
||||
# done by using a `rio.Overlay` component.
|
||||
@ -80,7 +80,7 @@ class Navbar(rio.Component):
|
||||
style=(
|
||||
"major"
|
||||
if active_page_url_segment == ""
|
||||
else "plain"
|
||||
else "plain-text"
|
||||
),
|
||||
),
|
||||
"/",
|
||||
@ -121,23 +121,35 @@ class Navbar(rio.Component):
|
||||
style=(
|
||||
"major"
|
||||
if active_page_url_segment == "add"
|
||||
else "plain"
|
||||
else "plain-text"
|
||||
),
|
||||
),
|
||||
"/add",
|
||||
),
|
||||
rio.Link(
|
||||
rio.Button(
|
||||
"Locations",
|
||||
icon="material/news",
|
||||
style=(
|
||||
"major"
|
||||
if active_page_url_segment == "addlocation"
|
||||
else "plain-text"
|
||||
),
|
||||
),
|
||||
"/addlocation",
|
||||
),
|
||||
# Same game, different button
|
||||
rio.Link(
|
||||
rio.Button(
|
||||
"About",
|
||||
"Settings",
|
||||
icon="material/info",
|
||||
style=(
|
||||
"major"
|
||||
if active_page_url_segment == "about-page"
|
||||
else "plain"
|
||||
if active_page_url_segment == "settings-page"
|
||||
else "plain-text"
|
||||
),
|
||||
),
|
||||
"/about-page",
|
||||
"/settings-page",
|
||||
),
|
||||
spacing=1,
|
||||
margin=1,
|
||||
|
@ -1,6 +1,7 @@
|
||||
from .root_page import RootPage
|
||||
from .about_page import AboutPage
|
||||
from .settings_page import SettingsPage
|
||||
from .add_page import AddPage
|
||||
from .add_location import AddLocationPage
|
||||
from .browse_page import BrowsePage
|
||||
from .login_page import LoginPage
|
||||
from .item_page import ItemPage
|
||||
|
@ -1,63 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import KW_ONLY, field
|
||||
from typing import * # type: ignore
|
||||
|
||||
import rio
|
||||
|
||||
from .. import components as comps
|
||||
|
||||
class AboutPage(rio.Component):
|
||||
"""
|
||||
A sample page, which displays a humorous description of the company.
|
||||
"""
|
||||
|
||||
def build(self) -> rio.Component:
|
||||
return rio.Markdown(
|
||||
"""
|
||||
# About Us
|
||||
|
||||
Welcome to Buzzwordz Inc.! Unleashing Synergistic Paradigms for Unprecedented
|
||||
Excellence since the day after yesterday.
|
||||
|
||||
## About Our Company
|
||||
|
||||
At buzzwordz, we are all talk and no action. Our mission is to be the vanguards
|
||||
of industry-leading solutions, leveraging bleeding-edge technologies to catapult
|
||||
your business into the stratosphere of success. Our unparalleled team of ninjas,
|
||||
gurus, and rockstars is dedicated to disrupting the status quo and actualizing
|
||||
your wildest business dreams. We live, breathe, and eat operational excellence
|
||||
and groundbreaking innovation.
|
||||
|
||||
## Synergistic Consulting
|
||||
|
||||
Unlock your business's quantum potential with our bespoke, game-changing
|
||||
strategies. Our consulting services synergize cross-functional paradigms to
|
||||
create a holistic ecosystem of perpetual growth and exponential ROI. Did I
|
||||
mention paradigm-shifts? We've got those too.
|
||||
|
||||
## Agile Hyper-Development
|
||||
|
||||
We turn moonshot ideas into reality with our agile, ninja-level development
|
||||
techniques. Our team of coding wizards crafts robust, scalable, and future-proof
|
||||
solutions that redefine industry standards. 24/7 Proactive Hyper-Support
|
||||
|
||||
Experience next-gen support that anticipates your needs before you do. Our
|
||||
omnipresent customer happiness engineers ensure seamless integration,
|
||||
frictionless operation, and infinite satisfaction, day and night.
|
||||
|
||||
Embark on a journey of transformational growth and stratospheric success. Don't
|
||||
delay, give us your money today.
|
||||
|
||||
Phone: (123) 456-7890
|
||||
|
||||
Email: info@yourwebsite.com
|
||||
|
||||
Address: 123 Main Street, City, Country
|
||||
""",
|
||||
width=60,
|
||||
margin_bottom=4,
|
||||
align_x=0.5,
|
||||
align_y=0,
|
||||
)
|
||||
|
118
inventory/pages/add_location.py
Normal file
118
inventory/pages/add_location.py
Normal file
@ -0,0 +1,118 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import KW_ONLY, field
|
||||
from typing import * # type: ignore
|
||||
|
||||
import rio
|
||||
import datetime
|
||||
from mac_vendor_lookup import AsyncMacLookup
|
||||
|
||||
from db_classes import *
|
||||
from .. import components as comps
|
||||
import asyncio
|
||||
|
||||
class AddLocationPage(rio.Component):
|
||||
code: str = ""
|
||||
popup_message: str = ""
|
||||
popup_show: bool = False
|
||||
popup_color: str = 'warning'
|
||||
description: str = ""
|
||||
name: str = ""
|
||||
parent_code: str = ""
|
||||
parent: str = ""
|
||||
|
||||
@rio.event.periodic(1)
|
||||
def set_office_init(self):
|
||||
self.office = self.session[comps.Settings].office
|
||||
#print("Populated:", self.office)
|
||||
|
||||
async def _update_location(self, event: rio.TextInputChangeEvent):
|
||||
print("Checking " + self.parent)
|
||||
if get_location_id(self.parent) != False:
|
||||
self.parent_code = self.parent
|
||||
print("Found location " + get_location_id(self.parent).name)
|
||||
self.parent = get_location_id(self.parent).name
|
||||
|
||||
async def add_part(self):
|
||||
if self.code == "":
|
||||
# FAIL
|
||||
self.popup_message = "\n Missing barcode! \n\n"
|
||||
self.popup_show = True
|
||||
self.popup_color = 'danger'
|
||||
await asyncio.sleep(1)
|
||||
self.popup_show = False
|
||||
else:
|
||||
# OK, add part
|
||||
if get_location_id(self.parent_code) != False:
|
||||
self.parent = get_location_id(self.parent_code)
|
||||
else:
|
||||
self.parent = None
|
||||
if create_location(name=self.name, barcode=self.code, parent=self.parent, description=self.description) == False:
|
||||
self.popup_message = "\n Duplicate barcode! \n\n"
|
||||
self.popup_show = True
|
||||
self.popup_color = 'warning'
|
||||
await asyncio.sleep(2)
|
||||
self.popup_show = False
|
||||
else:
|
||||
self.popup_message = "\n Part added! \n\n"
|
||||
self.popup_show = True
|
||||
self.popup_color = 'success'
|
||||
self.name: str = ""
|
||||
self.code: str = ""
|
||||
self.description: str = ""
|
||||
self.parent_code: str = ""
|
||||
self.parent: str = ""
|
||||
await asyncio.sleep(2)
|
||||
self.popup_show = False
|
||||
|
||||
|
||||
async def _add_part_enter(self, event: rio.TextInputConfirmEvent):
|
||||
await self.add_part()
|
||||
|
||||
async def _add_part_button(self):
|
||||
await self.add_part()
|
||||
|
||||
def build(self) -> rio.Component:
|
||||
return rio.Column(
|
||||
rio.Popup(
|
||||
anchor=rio.Text(
|
||||
text="Add a part below:",
|
||||
style='heading1',
|
||||
align_x = 0.5
|
||||
),
|
||||
color=self.bind().popup_color,
|
||||
is_open=self.bind().popup_show,
|
||||
content=rio.Text(
|
||||
text=self.bind().popup_message,
|
||||
),
|
||||
|
||||
),
|
||||
rio.TextInput(
|
||||
label="Barcode",
|
||||
text=self.bind().code
|
||||
),
|
||||
rio.TextInput(
|
||||
label="Parent Location (optional)",
|
||||
text=self.bind().parent,
|
||||
on_change=self._update_location
|
||||
),
|
||||
rio.TextInput(
|
||||
label="Location Name",
|
||||
text=self.bind().name
|
||||
),
|
||||
rio.MultiLineTextInput(
|
||||
label="Description (optional)",
|
||||
text=self.bind().description
|
||||
),
|
||||
rio.Button(
|
||||
content="Add",
|
||||
on_press=self._add_part_button
|
||||
),
|
||||
|
||||
spacing=2,
|
||||
min_width=60,
|
||||
margin_bottom=4,
|
||||
align_x=0.5,
|
||||
align_y=0,
|
||||
)
|
||||
|
@ -10,7 +10,14 @@ from mac_vendor_lookup import AsyncMacLookup
|
||||
from db_classes import *
|
||||
from .. import components as comps
|
||||
|
||||
import asyncio
|
||||
|
||||
class AddPage(rio.Component):
|
||||
|
||||
"""
|
||||
A set of fields for adding/editing items.
|
||||
"""
|
||||
|
||||
partnum: str = ""
|
||||
mac: str = ""
|
||||
serial: str = ""
|
||||
@ -27,6 +34,9 @@ class AddPage(rio.Component):
|
||||
manu: str = ""
|
||||
manufield: str = ""
|
||||
office: str = ""
|
||||
description: str = ""
|
||||
location: str = ""
|
||||
location_code: str = ""
|
||||
|
||||
@rio.event.periodic(1)
|
||||
def set_office_init(self):
|
||||
@ -60,12 +70,16 @@ class AddPage(rio.Component):
|
||||
self.popup_message = "\n Missing barcode! \n\n"
|
||||
self.popup_show = True
|
||||
self.popup_color = 'danger'
|
||||
await asyncio.sleep(2)
|
||||
self.popup_show = False
|
||||
else:
|
||||
# OK, add part
|
||||
if create_item(self.partnum, self.serial, self.office, self.code, location=None, description=None, manufacturer=self.manu, mac=self.mac, fwver=self.fwver) == False:
|
||||
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
|
||||
self.popup_color = 'warning'
|
||||
await asyncio.sleep(2)
|
||||
self.popup_show = False
|
||||
else:
|
||||
self.popup_message = "\n Part added! \n\n"
|
||||
self.popup_show = True
|
||||
@ -79,6 +93,11 @@ class AddPage(rio.Component):
|
||||
self.macvendor: str = ""
|
||||
self.manu: str = ""
|
||||
self.manufield: str = ""
|
||||
self.description: str = ""
|
||||
self.location: str = ""
|
||||
self.location_code: str = ""
|
||||
await asyncio.sleep(2)
|
||||
self.popup_show = False
|
||||
|
||||
|
||||
async def _add_part_enter(self, event: rio.TextInputConfirmEvent):
|
||||
@ -107,8 +126,16 @@ class AddPage(rio.Component):
|
||||
async def _update_mac(self, event: rio.TextInputChangeEvent):
|
||||
await self.check_mac(event.text)
|
||||
|
||||
async def _update_location(self, event: rio.TextInputChangeEvent):
|
||||
print("Checking " + self.location)
|
||||
if get_location_id(self.location) != False:
|
||||
self.location_code = self.location
|
||||
print("Found location " + get_location_id(self.location).name)
|
||||
self.location = get_location_id(self.location).name
|
||||
|
||||
|
||||
def _update_partnum(self, event: rio.TextInputChangeEvent):
|
||||
def __find_hm_header(txt):
|
||||
def __find_hm_header_static(txt):
|
||||
searchlist = ["RSPS", "RSPE", "RSP", "RSB", "LRS", "RS", "OS", "RED", "MSP", "MSM", "MS", "MM", "EESX", "EES", "OZD", "OBR"]
|
||||
for header in searchlist:
|
||||
if txt.find(header) >= 0:
|
||||
@ -129,6 +156,8 @@ class AddPage(rio.Component):
|
||||
|
||||
if dash and acount <= 5 and ncount <= 5:
|
||||
return acount+ncount
|
||||
elif dash and acount >5 and acount <= 10 and ncount == 0:
|
||||
return acount+ncount
|
||||
return -1
|
||||
|
||||
def __find_hm_fwver(txt):
|
||||
@ -143,7 +172,7 @@ class AddPage(rio.Component):
|
||||
if txt.find("BRS") == 0:
|
||||
a -= 1
|
||||
if txt[a] in ['S', 'A']:
|
||||
return txt[a]
|
||||
return '2' + txt[a]
|
||||
a = __find_hm_fwver(txt)
|
||||
if txt.find("GRS") == 0:
|
||||
a -= 4
|
||||
@ -162,7 +191,7 @@ class AddPage(rio.Component):
|
||||
print(txt,txt[a])
|
||||
|
||||
if txt[a] in ['P', 'E']:
|
||||
return txt[a]
|
||||
return '2' + txt[a]
|
||||
a = __find_hm_fwver(txt)
|
||||
if txt.find("EAGLE") == 0:
|
||||
a -= 2
|
||||
@ -179,7 +208,7 @@ class AddPage(rio.Component):
|
||||
if __find_hm_fwver(pn) >= 0:
|
||||
self.fwver = pn[__find_hm_fwver(pn):]
|
||||
if len(__find_hm_fwmode(pn)) > 0:
|
||||
self.fwver += " SWL-" + __find_hm_fwmode(pn)
|
||||
self.fwver += " SW-L" + __find_hm_fwmode(pn)
|
||||
|
||||
def build(self) -> rio.Component:
|
||||
return rio.Column(
|
||||
@ -207,33 +236,39 @@ class AddPage(rio.Component):
|
||||
),
|
||||
rio.TextInput(
|
||||
label="Serial",
|
||||
text=self.bind().serial
|
||||
text=self.bind().serial,
|
||||
on_confirm=self._add_part_enter
|
||||
),
|
||||
rio.TextInput(
|
||||
label="MAC",
|
||||
text=self.bind().mac,
|
||||
on_change=self._update_mac
|
||||
on_change=self._update_mac,
|
||||
on_confirm=self._add_part_enter
|
||||
),
|
||||
rio.TextInput(
|
||||
label="Location (optional)",
|
||||
text=self.bind().location,
|
||||
on_change=self._update_location,
|
||||
on_confirm=self._add_part_enter
|
||||
),
|
||||
rio.TextInput(
|
||||
label="Manufacturer",
|
||||
text=self.bind().manufield
|
||||
text=self.bind().manufield,
|
||||
on_confirm=self._add_part_enter
|
||||
),
|
||||
rio.TextInput(
|
||||
label="FW Ver",
|
||||
text=self.bind().fwver,
|
||||
on_confirm=self._add_part_enter
|
||||
text=self.bind().fwver
|
||||
),
|
||||
rio.MultiLineTextInput(
|
||||
label="Description (optional)",
|
||||
text=self.bind().description
|
||||
),
|
||||
rio.Row(
|
||||
# rio.DateInput(
|
||||
# label="Timestamp",
|
||||
# value=self.bind().date
|
||||
# ),
|
||||
rio.TextInput(
|
||||
#text=
|
||||
label="Timestamp",
|
||||
is_sensitive=False,
|
||||
text = self.bind().time_start,
|
||||
#on_change=self._set_time
|
||||
)
|
||||
),
|
||||
rio.Button(
|
||||
content="Add",
|
||||
@ -241,7 +276,7 @@ class AddPage(rio.Component):
|
||||
),
|
||||
|
||||
spacing=2,
|
||||
width=60,
|
||||
min_width=60,
|
||||
margin_bottom=4,
|
||||
align_x=0.5,
|
||||
align_y=0,
|
||||
|
@ -6,15 +6,36 @@ from typing import * # type: ignore
|
||||
import rio
|
||||
|
||||
from .. import components as comps
|
||||
from .add_page import AddPage
|
||||
|
||||
from db_classes import *
|
||||
import functools
|
||||
import asyncio
|
||||
|
||||
class BrowsePage(rio.Component):
|
||||
searchtext: str = ""
|
||||
items: dict = {}
|
||||
items: list = []
|
||||
office: str = ""
|
||||
filters: dict = {}
|
||||
|
||||
popup_message: str = ""
|
||||
popup_show: bool = False
|
||||
popup_color: str = 'success'
|
||||
|
||||
|
||||
elocation: str = ""
|
||||
elocation_code: str = ""
|
||||
epartnum: str = ""
|
||||
emac: str = ""
|
||||
eserial: str = ""
|
||||
efwver: str = ""
|
||||
ecode: str = ""
|
||||
emacvendor: str = ""
|
||||
emanu: str = ""
|
||||
emanufield: str = ""
|
||||
eoffice: str = ""
|
||||
edescription: str = ""
|
||||
|
||||
@rio.event.on_populate
|
||||
async def _search(self, query=searchtext):
|
||||
self.office = self.session[comps.Settings].office
|
||||
@ -35,24 +56,331 @@ class BrowsePage(rio.Component):
|
||||
async def _search_trigger(self, event: rio.TextInputChangeEvent):
|
||||
await self._search(event.text)
|
||||
|
||||
def click_item(self, code):
|
||||
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)
|
||||
|
||||
try:
|
||||
loc = itm["location"]
|
||||
except:
|
||||
loc = ""
|
||||
if itm["checkout"]:
|
||||
checkout = itm["checkout_user"] + " - " + loc
|
||||
else:
|
||||
checkout = loc
|
||||
|
||||
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)))
|
||||
|
||||
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_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()
|
||||
|
||||
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()
|
||||
|
||||
# Return the selected value
|
||||
return result
|
||||
|
||||
|
||||
async def _create_dialog_edit(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
|
||||
except:
|
||||
self.elocation: str = ""
|
||||
self.elocation_code: str = ""
|
||||
|
||||
self.epartnum: str = itm["fullname"]
|
||||
self.emac: str = itm["mac"]
|
||||
self.eserial: str = itm["serial"]
|
||||
self.efwver: str = itm["fwver"]
|
||||
self.ecode: str = code
|
||||
self.popup_message: str = ""
|
||||
self.popup_show: bool = False
|
||||
self.popup_color: str = 'warning'
|
||||
self.emacvendor: str = ""
|
||||
self.emanu: str = itm["manufacturer"]
|
||||
self.emanufield: str = itm["manufacturer"]
|
||||
self.eoffice: str = itm["office"]
|
||||
self.edescription: str = itm["description"]
|
||||
|
||||
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"
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
def click_item_page(self, code):
|
||||
self.session[comps.Settings].selected_item = code
|
||||
self.session.attach(self.session[comps.Settings])
|
||||
self.session.navigate_to("/item")
|
||||
|
||||
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)
|
||||
|
||||
def build(self) -> rio.Component:
|
||||
searchview: rio.ListView = rio.ListView(height='grow')
|
||||
searchview: rio.ListView = rio.ListView(grow_y=True)
|
||||
for item in self.items:
|
||||
if item["loc"] is not None:
|
||||
loc = item["loc"]["name"]
|
||||
else:
|
||||
try:
|
||||
loc = item["location"]
|
||||
except:
|
||||
loc = ""
|
||||
if item["checkout"]:
|
||||
checkout = item["checkout_user"] + " - " + loc
|
||||
else:
|
||||
checkout = loc
|
||||
searchview.add(rio.SimpleListItem(text=item["fullname"],secondary_text=(item["manufacturer"] + " - Serial: " + item["serial"] + "\n" + checkout), on_press=functools.partial(
|
||||
self.click_item,
|
||||
self.click_item_dialog,
|
||||
code=item["barcode"])))
|
||||
return rio.Column(
|
||||
rio.Row(
|
||||
@ -63,8 +391,21 @@ class BrowsePage(rio.Component):
|
||||
)
|
||||
),
|
||||
searchview,
|
||||
rio.Popup(
|
||||
anchor=rio.Text(
|
||||
text="",
|
||||
style='heading1',
|
||||
align_x = 0.5
|
||||
),
|
||||
color=self.popup_color,
|
||||
is_open=self.popup_show,
|
||||
content=rio.Text(
|
||||
text=self.popup_message,
|
||||
),
|
||||
|
||||
),
|
||||
spacing=2,
|
||||
width=60,
|
||||
min_width=60,
|
||||
align_x=0.5,
|
||||
align_y=0,
|
||||
)
|
||||
|
@ -92,7 +92,7 @@ class ItemPage(rio.Component):
|
||||
align_x = 0.5,
|
||||
),
|
||||
spacing=2,
|
||||
width=60,
|
||||
min_width=60,
|
||||
align_x=0.5,
|
||||
align_y=0,
|
||||
)
|
||||
|
@ -27,11 +27,11 @@ class RootPage(rio.Component):
|
||||
# of all other components.
|
||||
comps.Navbar(),
|
||||
# Add some empty space so the navbar doesn't cover the content.
|
||||
rio.Spacer(height=10),
|
||||
rio.Spacer(min_height=10),
|
||||
# The page view will display the content of the current page.
|
||||
rio.PageView(
|
||||
# Make sure the page view takes up all available space.
|
||||
height="grow",
|
||||
grow_y=True
|
||||
),
|
||||
# The footer is also common to all pages, so place it here.
|
||||
comps.Footer(),
|
||||
|
26
inventory/pages/settings_page.py
Normal file
26
inventory/pages/settings_page.py
Normal file
@ -0,0 +1,26 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import KW_ONLY, field
|
||||
from typing import * # type: ignore
|
||||
|
||||
import rio
|
||||
|
||||
from .. import components as comps
|
||||
|
||||
class SettingsPage(rio.Component):
|
||||
"""
|
||||
A sample page, which displays a humorous description of the company.
|
||||
"""
|
||||
|
||||
def build(self) -> rio.Component:
|
||||
return rio.Markdown(
|
||||
"""
|
||||
Belden Inventory manager v0.5
|
||||
WIP
|
||||
""",
|
||||
min_width=60,
|
||||
margin_bottom=4,
|
||||
align_x=0.5,
|
||||
align_y=0,
|
||||
)
|
||||
|
@ -1,6 +1,5 @@
|
||||
peewee
|
||||
pymysql
|
||||
flask
|
||||
rio-ui
|
||||
meilisearch
|
||||
rio-ui==0.10.4
|
||||
meilisearch #==0.31.5
|
||||
mac-vendor-lookup
|
@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""Interactions with the Meilisearch API for adding and searching cables."""
|
||||
import sys
|
||||
|
||||
from meilisearch import Client
|
||||
from meilisearch.task import TaskInfo
|
||||
from meilisearch.errors import MeilisearchApiError
|
||||
@ -45,6 +47,8 @@ class InventorySearch:
|
||||
# make a variable to easily reference the index
|
||||
self.idxref = self.client.index(self.index)
|
||||
time.sleep(0.05)
|
||||
# disable typos, we have serial numbers and such that should be exact match
|
||||
self.idxref.update_typo_tolerance({'enabled': False})
|
||||
# update filterable attributes if needed
|
||||
self.idxref.update_distinct_attribute('barcode')
|
||||
self.update_filterables(filterable_attrs)
|
||||
@ -109,4 +113,4 @@ class InventorySearch:
|
||||
|
||||
# entrypoint
|
||||
if __name__ == "__main__":
|
||||
jbs = InventorySearch()
|
||||
ivs = InventorySearch()
|
||||
|
Loading…
x
Reference in New Issue
Block a user