inventory/inventory/routes/listing.py

149 lines
No EOL
5.8 KiB
Python

from flask import Blueprint, render_template, abort, request
import crudkit
from crudkit.api._cursor import decode_cursor, encode_cursor
from crudkit.ui.fragments import render_table, register_template_globals
bp_listing = Blueprint("listing", __name__)
def init_listing_routes(app):
# Make helpers available in all templates
register_template_globals(app)
@bp_listing.get("/listing/<model>")
def show_list(model):
if model.lower() not in {"inventory", "user", "worklog"}:
abort(404)
cls = crudkit.crud.get_model(model)
if cls is None:
abort(404)
# read query args
limit = request.args.get("limit", None)
limit = int(limit) if (limit is not None and str(limit).isdigit()) else 15
sort = request.args.get("sort")
fields_qs = request.args.get("fields")
cursor = request.args.get("cursor")
key, _desc, backward = decode_cursor(cursor)
# base spec per model
spec = {}
columns = []
row_classes = []
if model.lower() == 'inventory':
spec = {"fields": [
"label",
"name",
"barcode",
"serial",
"brand.name",
"model",
"device_type.description",
"condition",
"owner.label",
"location.label",
]}
columns = [
{"field": "label"},
{"field": "name"},
{"field": "barcode", "label": "Barcode #"},
{"field": "serial", "label": "Serial #"},
{"field": "brand.name", "label": "Brand"},
{"field": "model"},
{"field": "device_type.description", "label": "Device Type"},
{"field": "condition"},
{"field": "owner.label", "label": "Contact",
"link": {"endpoint": "entry.entry", "params": {"id": "{owner.id}", "model": "user"}}},
{"field": "location.label", "label": "Room"},
]
elif model.lower() == 'user':
spec = {"fields": [
"label",
"last_name",
"first_name",
"supervisor.label",
"robot.overlord",
"staff",
"active",
], "sort": "first_name,last_name"} # default for users
columns = [
{"field": "label", "label": "Full Name"},
{"field": "last_name"},
{"field": "first_name"},
{"field": "supervisor.label", "label": "Supervisor",
"link": {"endpoint": "entry.entry", "params": {"id": "{supervisor.id}", "model": "user"}}},
{"field": "staff", "format": "yesno"},
{"field": "active", "format": "yesno"},
]
row_classes = [
{"when": {"field": "active", "is": False}, "class": "table-secondary"},
{"when": {"all": [
{"field": "staff", "is": False},
{"field": "active", "is": True}
]}, "class": "table-success"},
]
elif model.lower() == 'worklog':
spec = {"fields": [
"work_item.label",
"contact.label",
"start_time",
"end_time",
"complete",
]}
columns = [
{"field": "work_item.label", "label": "Work Item",
"link": {"endpoint": "entry.entry", "params": {"id": "{work_item.id}", "model": "inventory"}}},
{"field": "contact.label", "label": "Contact",
"link": {"endpoint": "entry.entry", "params": {"id": "{contact.id}", "model": "user"}}},
{"field": "start_time", "format": "datetime"},
{"field": "end_time", "format": "datetime"},
{"field": "complete", "format": "yesno"},
]
row_classes = [
{"when": {"field": "complete", "is": True}, "class": "table-success"},
{"when": {"field": "complete", "is": False}, "class": "table-danger"}
]
# Build params to feed CRUDService (flat dict; parse_filters expects flat keys)
params = dict(spec)
# overlay fields from query (?fields=...)
if fields_qs:
params["fields"] = [p.strip() for p in fields_qs.split(",") if p.strip()]
# overlay sort from query (?sort=...)
if sort:
params["sort"] = sort
# limit semantics: 0 means "unlimited" in your service layer
params["limit"] = limit
# forward *all other* query params as filters (flat), excluding known control keys
CONTROL_KEYS = {"limit", "cursor", "sort", "fields"}
for k, v in request.args.items():
if k in CONTROL_KEYS:
continue
if v is None or v == "":
continue
params[k] = v
service = crudkit.crud.get_service(cls)
window = service.seek_window(params, key=key, backward=backward, include_total=True)
table = render_table(window.items, columns=columns,
opts={"object_class": model, "row_classes": row_classes})
pagination_ctx = {
"limit": window.limit,
"total": window.total,
"next_cursor": encode_cursor(window.last_key, list(window.order.desc), backward=False),
"prev_cursor": encode_cursor(window.first_key, list(window.order.desc), backward=True),
"sort": params.get("sort") # expose current sort to the template
}
return render_template("listing.html", model=model, table=table, pagination=pagination_ctx)
app.register_blueprint(bp_listing)