diff --git a/src/wuttaweb/grids/base.py b/src/wuttaweb/grids/base.py index 63829a4..313dd67 100644 --- a/src/wuttaweb/grids/base.py +++ b/src/wuttaweb/grids/base.py @@ -2,7 +2,7 @@ ################################################################################ # # wuttaweb -- Web App for Wutta Framework -# Copyright © 2024-2025 Lance Edgar +# Copyright © 2024-2026 Lance Edgar # # This file is part of Wutta Framework. # @@ -30,6 +30,12 @@ import logging import warnings from collections import namedtuple, OrderedDict +try: + from enum import EnumType +except ImportError: # pragma: no cover + # nb. python < 3.11 + from enum import EnumMeta as EnumType + import sqlalchemy as sa from sqlalchemy import orm @@ -763,7 +769,7 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth :param key: Name of column. - :param enum: Instance of :class:`python:enum.Enum`. + :param enum: Instance of :class:`python:enum.Enum`, or a dict. """ self.enums[key] = enum self.set_renderer(key, self.render_enum, enum=enum) @@ -2050,7 +2056,7 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth See also :meth:`set_enum()`. :param enum: Enum class for the field. This should be an - instance of :class:`~python:enum.Enum`. + instance of :class:`~python:enum.Enum` or else a dict. To use this feature for your grid:: @@ -2061,12 +2067,26 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth TWO = 2 THREE = 3 - grid.set_enum('my_enum_field', MyEnum) + grid.set_enum("my_enum_field", MyEnum) + + Or, perhaps more common:: + + myenum = { + 1: "ONE", + 2: "TWO", + 3: "THREE", + } + + grid.set_enum("my_enum_field", myenum) """ if enum: - raw_value = obj[key] - if raw_value: - return raw_value.value + + if isinstance(enum, EnumType): + if raw_value := obj[key]: + return raw_value.value + + if isinstance(enum, dict): + return enum.get(value, value) return value diff --git a/src/wuttaweb/templates/batch/view.mako b/src/wuttaweb/templates/batch/view.mako index 1305079..0f2c0b9 100644 --- a/src/wuttaweb/templates/batch/view.mako +++ b/src/wuttaweb/templates/batch/view.mako @@ -7,86 +7,88 @@ <%def name="tool_panel_execution()"> - - % if batch.executed: - -

- Batch was executed
- ${app.render_time_ago(batch.executed)}
- by ${batch.executed_by} -

-
- % elif why_not_execute: - -

- Batch cannot be executed: -

-

- ${why_not_execute} -

-
- % else: - % if master.has_perm('execute'): - + % if master.executable: + + % if batch.executed: +

- Batch can be executed + Batch was executed
+ ${app.render_time_ago(batch.executed)}
+ by ${batch.executed_by}

- - Execute Batch - - - - -
- - % else: + % elif why_not_execute:

- Batch may be executed,
- but you do not have permission. + Batch cannot be executed: +

+

+ ${why_not_execute}

+ % else: + % if master.has_perm('execute'): + +

+ Batch can be executed +

+ + Execute Batch + + + + + +
+ + % else: + +

+ Batch may be executed,
+ but you do not have permission. +

+
+ % endif % endif - % endif -
+
+ % endif <%def name="modify_vue_vars()"> diff --git a/src/wuttaweb/views/batch.py b/src/wuttaweb/views/batch.py index ce306ea..89ffe6f 100644 --- a/src/wuttaweb/views/batch.py +++ b/src/wuttaweb/views/batch.py @@ -2,7 +2,7 @@ ################################################################################ # # wuttaweb -- Web App for Wutta Framework -# Copyright © 2024-2025 Lance Edgar +# Copyright © 2024-2026 Lance Edgar # # This file is part of Wutta Framework. # @@ -32,7 +32,7 @@ import markdown from sqlalchemy import orm from wuttaweb.views import MasterView -from wuttaweb.forms.schema import UserRef +from wuttaweb.forms.schema import UserRef, WuttaDictEnum from wuttaweb.forms.widgets import BatchIdWidget @@ -147,6 +147,9 @@ class BatchMasterView(MasterView): # description g.set_link("description") + # status_code + g.set_enum("status_code", self.model_class.STATUS) + def render_batch_id( # pylint: disable=empty-docstring,unused-argument self, batch, key, value ): @@ -191,6 +194,7 @@ class BatchMasterView(MasterView): if self.creating: f.remove("status_code") else: + f.set_node("status_code", WuttaDictEnum(self.request, batch.STATUS)) f.set_readonly("status_code") # created @@ -441,7 +445,7 @@ class BatchMasterView(MasterView): g.set_renderer("status_code", self.render_row_status) # tool button - create row - if not batch.executed and self.has_perm("create_row"): + if self.rows_creatable and not batch.executed and self.has_perm("create_row"): button = self.make_button( f"New {self.get_row_model_title()}", primary=True, diff --git a/src/wuttaweb/views/master.py b/src/wuttaweb/views/master.py index 2875f7c..c10914d 100644 --- a/src/wuttaweb/views/master.py +++ b/src/wuttaweb/views/master.py @@ -2,7 +2,7 @@ ################################################################################ # # wuttaweb -- Web App for Wutta Framework -# Copyright © 2024-2025 Lance Edgar +# Copyright © 2024-2026 Lance Edgar # # This file is part of Wutta Framework. # @@ -3514,6 +3514,11 @@ class MasterView(View): # pylint: disable=too-many-public-methods if hasattr(cls, "model_title"): return cls.model_title + if model_class := cls.get_model_class(): + if hasattr(model_class, "__wutta_hint__"): + if model_title := model_class.__wutta_hint__.get("model_title"): + return model_title + return cls.get_model_name() @classmethod @@ -3532,6 +3537,13 @@ class MasterView(View): # pylint: disable=too-many-public-methods if hasattr(cls, "model_title_plural"): return cls.model_title_plural + if model_class := cls.get_model_class(): + if hasattr(model_class, "__wutta_hint__"): + if model_title_plural := model_class.__wutta_hint__.get( + "model_title_plural" + ): + return model_title_plural + model_title = cls.get_model_title() return f"{model_title}s" diff --git a/src/wuttaweb/views/people.py b/src/wuttaweb/views/people.py index b726772..a4ec254 100644 --- a/src/wuttaweb/views/people.py +++ b/src/wuttaweb/views/people.py @@ -96,7 +96,9 @@ class PersonView(MasterView): # pylint: disable=abstract-method f.remove("full_name") # users - if self.viewing: + if self.creating or self.editing: + f.remove("users") + elif self.viewing: f.set_grid("users", self.make_users_grid(person)) def make_users_grid(self, person): diff --git a/tests/grids/test_base.py b/tests/grids/test_base.py index 5174335..4d9872d 100644 --- a/tests/grids/test_base.py +++ b/tests/grids/test_base.py @@ -1602,15 +1602,30 @@ class TestGrid(WebTestCase): grid = self.make_grid(columns=["foo", "bar"]) obj = {"status": None} - # null + # true enum, null value = grid.render_enum(obj, "status", None, enum=enum.UpgradeStatus) self.assertIsNone(value) - # normal + # true enum, normal value obj["status"] = enum.UpgradeStatus.SUCCESS value = grid.render_enum(obj, "status", "SUCCESS", enum=enum.UpgradeStatus) self.assertEqual(value, "success") + # dict enum + statuses = { + enum.UpgradeStatus.SUCCESS.name: "success", + enum.UpgradeStatus.FAILURE.name: "failure", + } + + # dict enum, null + value = grid.render_enum(obj, "status", None, enum=statuses) + self.assertIsNone(value) + + # true enum, normal value + obj["status"] = enum.UpgradeStatus.SUCCESS.value + value = grid.render_enum(obj, "status", "SUCCESS", enum=statuses) + self.assertEqual(value, "success") + def test_render_percent(self): grid = self.make_grid(columns=["foo", "bar"]) obj = MagicMock() diff --git a/tests/views/test_batch.py b/tests/views/test_batch.py index 92c0a4b..317f731 100644 --- a/tests/views/test_batch.py +++ b/tests/views/test_batch.py @@ -442,6 +442,7 @@ class TestBatchMasterView(WebTestCase): mod.BatchMasterView, model_class=MockBatch, route_prefix="mock_batches", + rows_creatable=True, create=True, ): with patch.object(