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/") 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)