WuttaWeb 0.20.1__tar.gz → 0.20.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/CHANGELOG.md +16 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/PKG-INFO +1 -1
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/pyproject.toml +1 -1
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/grids/base.py +45 -5
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/base.mako +1 -1
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/grids/vue_template.mako +6 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/master/index.mako +5 -1
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/views/master.py +31 -10
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/grids/test_base.py +27 -1
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/views/test_master.py +23 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/.gitignore +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/COPYING.txt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/README.md +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/Makefile +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/_static/.keepme +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.app.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.auth.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.cli.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.cli.webapp.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.conf.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.db.continuum.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.db.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.db.sess.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.emails.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.forms.base.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.forms.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.forms.schema.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.forms.widgets.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.grids.base.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.grids.filters.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.grids.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.handler.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.helpers.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.menus.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.progress.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.static.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.subscribers.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.util.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.views.auth.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.views.base.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.views.batch.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.views.common.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.views.email.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.views.essential.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.views.master.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.views.people.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.views.progress.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.views.reports.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.views.roles.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.views.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.views.settings.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.views.upgrades.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/api/wuttaweb.views.users.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/conf.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/glossary.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/index.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/make.bat +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/narr/cli/builtin.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/narr/cli/index.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/narr/templates/base.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/narr/templates/index.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/narr/templates/lookup.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/docs/narr/templates/overview.rst +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/__init__.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/_version.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/app.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/auth.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/cli/__init__.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/cli/webapp.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/conf.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/db/__init__.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/db/continuum.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/db/sess.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/email-templates/feedback.html.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/email-templates/feedback.txt.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/emails.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/forms/__init__.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/forms/base.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/forms/schema.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/forms/widgets.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/grids/__init__.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/grids/filters.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/handler.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/helpers.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/menus.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/progress.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/static/__init__.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/static/img/favicon.ico +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/static/img/logo.png +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/static/img/testing.png +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/subscribers.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/appinfo/index.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/auth/change_password.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/auth/login.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/base_meta.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/batch/view.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/configure.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/deform/dateinput.pt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/deform/datetimeinput.pt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/deform/moneyinput.pt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/deform/password.pt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/deform/permissions.pt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/deform/readonly/checkbox.pt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/deform/readonly/email_recips.pt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/deform/readonly/filedownload.pt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/deform/select.pt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/deform/textarea.pt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/deform/textinput.pt +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/email/settings/view.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/forbidden.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/form.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/grids/table_element.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/home.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/master/configure.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/master/create.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/master/delete.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/master/edit.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/master/form.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/master/view.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/notfound.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/page.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/people/view_profile.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/progress.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/reports/view.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/setup.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/upgrade.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/upgrades/configure.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/upgrades/view.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/templates/wutta-components.mako +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/testing.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/util.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/views/__init__.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/views/auth.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/views/base.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/views/batch.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/views/common.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/views/email.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/views/essential.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/views/people.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/views/progress.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/views/reports.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/views/roles.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/views/settings.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/views/upgrades.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/src/wuttaweb/views/users.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tasks.py +0 -0
- {wuttaweb-0.20.1/tests/views → wuttaweb-0.20.3/tests}/__init__.py +0 -0
- {wuttaweb-0.20.1/tests/grids → wuttaweb-0.20.3/tests/cli}/__init__.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/cli/test_webapp.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/db/__init__.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/db/test_continuum.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/forms/test_base.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/forms/test_schema.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/forms/test_widgets.py +0 -0
- {wuttaweb-0.20.1/tests/cli → wuttaweb-0.20.3/tests/grids}/__init__.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/grids/test_filters.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/libcache/bb_oruga.js +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/libcache/bb_oruga_bulma.css +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/libcache/bb_oruga_bulma.js +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/libcache/bb_vue.js +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/libcache/bb_vue_fontawesome.js +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/libcache/buefy.css +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/libcache/buefy.js +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/libcache/fontawesome.js +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/libcache/vue.js +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/libcache/vue_resource.js +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/test_app.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/test_auth.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/test_emails.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/test_handler.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/test_helpers.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/test_menus.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/test_progress.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/test_static.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/test_subscribers.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/test_util.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/util.py +0 -0
- {wuttaweb-0.20.1/tests → wuttaweb-0.20.3/tests/views}/__init__.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/views/test___init__.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/views/test_auth.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/views/test_base.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/views/test_batch.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/views/test_common.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/views/test_email.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/views/test_essential.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/views/test_people.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/views/test_progress.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/views/test_reports.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/views/test_roles.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/views/test_settings.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/views/test_upgrades.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tests/views/test_users.py +0 -0
- {wuttaweb-0.20.1 → wuttaweb-0.20.3}/tox.ini +0 -0
|
@@ -5,6 +5,22 @@ All notable changes to wuttaweb will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## v0.20.3 (2025-01-14)
|
|
9
|
+
|
|
10
|
+
### Fix
|
|
11
|
+
|
|
12
|
+
- add `render_grid_tag()` as separate def block for index templates
|
|
13
|
+
- add `click_handler` attr for GridAction
|
|
14
|
+
|
|
15
|
+
## v0.20.2 (2025-01-14)
|
|
16
|
+
|
|
17
|
+
### Fix
|
|
18
|
+
|
|
19
|
+
- improve support for composite `model_key` in MasterView
|
|
20
|
+
- let content header text be a bit longer
|
|
21
|
+
- add optional `target` attr for GridAction
|
|
22
|
+
- add `render_date()` method for grids
|
|
23
|
+
|
|
8
24
|
## v0.20.1 (2025-01-13)
|
|
9
25
|
|
|
10
26
|
### Fix
|
|
@@ -602,6 +602,7 @@ class Grid:
|
|
|
602
602
|
* ``'batch_id'`` -> :meth:`render_batch_id()`
|
|
603
603
|
* ``'boolean'`` -> :meth:`render_boolean()`
|
|
604
604
|
* ``'currency'`` -> :meth:`render_currency()`
|
|
605
|
+
* ``'date'`` -> :meth:`render_date()`
|
|
605
606
|
* ``'datetime'`` -> :meth:`render_datetime()`
|
|
606
607
|
* ``'quantity'`` -> :meth:`render_quantity()`
|
|
607
608
|
|
|
@@ -611,6 +612,7 @@ class Grid:
|
|
|
611
612
|
'batch_id': self.render_batch_id,
|
|
612
613
|
'boolean': self.render_boolean,
|
|
613
614
|
'currency': self.render_currency,
|
|
615
|
+
'date': self.render_date,
|
|
614
616
|
'datetime': self.render_datetime,
|
|
615
617
|
'quantity': self.render_quantity,
|
|
616
618
|
}
|
|
@@ -631,11 +633,13 @@ class Grid:
|
|
|
631
633
|
data type implies a default renderer. This is only possible
|
|
632
634
|
if :attr:`model_class` is set to a SQLAlchemy mapped class.
|
|
633
635
|
|
|
634
|
-
This only looks for a
|
|
636
|
+
This only looks for a few data types, and configures as
|
|
635
637
|
follows:
|
|
636
638
|
|
|
637
639
|
* :class:`sqlalchemy:sqlalchemy.types.Boolean` ->
|
|
638
640
|
:meth:`render_boolean()`
|
|
641
|
+
* :class:`sqlalchemy:sqlalchemy.types.Date` ->
|
|
642
|
+
:meth:`render_date()`
|
|
639
643
|
* :class:`sqlalchemy:sqlalchemy.types.DateTime` ->
|
|
640
644
|
:meth:`render_datetime()`
|
|
641
645
|
"""
|
|
@@ -651,7 +655,9 @@ class Grid:
|
|
|
651
655
|
prop = getattr(attr, 'prop', None)
|
|
652
656
|
if prop and isinstance(prop, orm.ColumnProperty):
|
|
653
657
|
column = prop.columns[0]
|
|
654
|
-
if isinstance(column.type, sa.
|
|
658
|
+
if isinstance(column.type, sa.Date):
|
|
659
|
+
self.set_renderer(key, self.render_date)
|
|
660
|
+
elif isinstance(column.type, sa.DateTime):
|
|
655
661
|
self.set_renderer(key, self.render_datetime)
|
|
656
662
|
elif isinstance(column.type, sa.Boolean):
|
|
657
663
|
self.set_renderer(key, self.render_boolean)
|
|
@@ -1820,7 +1826,7 @@ class Grid:
|
|
|
1820
1826
|
This may be used automatically per
|
|
1821
1827
|
:meth:`set_default_renderers()` or you can use it explicitly::
|
|
1822
1828
|
|
|
1823
|
-
grid.set_renderer('foo',
|
|
1829
|
+
grid.set_renderer('foo', 'boolean')
|
|
1824
1830
|
"""
|
|
1825
1831
|
return self.app.render_boolean(value)
|
|
1826
1832
|
|
|
@@ -1839,6 +1845,22 @@ class Grid:
|
|
|
1839
1845
|
"""
|
|
1840
1846
|
return self.app.render_currency(value, **kwargs)
|
|
1841
1847
|
|
|
1848
|
+
def render_date(self, obj, key, value):
|
|
1849
|
+
"""
|
|
1850
|
+
Column renderer for :class:`python:datetime.date` values.
|
|
1851
|
+
|
|
1852
|
+
This calls
|
|
1853
|
+
:meth:`~wuttjamaican:wuttjamaican.app.AppHandler.render_date()`
|
|
1854
|
+
for the return value.
|
|
1855
|
+
|
|
1856
|
+
This may be used automatically per
|
|
1857
|
+
:meth:`set_default_renderers()` or you can use it explicitly::
|
|
1858
|
+
|
|
1859
|
+
grid.set_renderer('foo', 'date')
|
|
1860
|
+
"""
|
|
1861
|
+
dt = getattr(obj, key)
|
|
1862
|
+
return self.app.render_date(dt)
|
|
1863
|
+
|
|
1842
1864
|
def render_datetime(self, obj, key, value):
|
|
1843
1865
|
"""
|
|
1844
1866
|
Column renderer for :class:`python:datetime.datetime` values.
|
|
@@ -1850,7 +1872,7 @@ class Grid:
|
|
|
1850
1872
|
This may be used automatically per
|
|
1851
1873
|
:meth:`set_default_renderers()` or you can use it explicitly::
|
|
1852
1874
|
|
|
1853
|
-
grid.set_renderer('foo',
|
|
1875
|
+
grid.set_renderer('foo', 'datetime')
|
|
1854
1876
|
"""
|
|
1855
1877
|
dt = getattr(obj, key)
|
|
1856
1878
|
return self.app.render_datetime(dt)
|
|
@@ -1865,7 +1887,7 @@ class Grid:
|
|
|
1865
1887
|
|
|
1866
1888
|
This is not used automatically but you can use it explicitly::
|
|
1867
1889
|
|
|
1868
|
-
grid.set_renderer('foo',
|
|
1890
|
+
grid.set_renderer('foo', 'quantity')
|
|
1869
1891
|
"""
|
|
1870
1892
|
return self.app.render_quantity(value)
|
|
1871
1893
|
|
|
@@ -2258,6 +2280,20 @@ class GridAction:
|
|
|
2258
2280
|
|
|
2259
2281
|
See also :meth:`get_url()`.
|
|
2260
2282
|
|
|
2283
|
+
.. attribute:: target
|
|
2284
|
+
|
|
2285
|
+
Optional ``target`` attribute for the ``<a>`` tag.
|
|
2286
|
+
|
|
2287
|
+
.. attribute:: click_handler
|
|
2288
|
+
|
|
2289
|
+
Optional JS click handler for the action. This value will be
|
|
2290
|
+
rendered as-is within the final grid template, hence the JS
|
|
2291
|
+
string must be callable code. Note that ``props.row`` will be
|
|
2292
|
+
available in the calling context, so a couple of examples:
|
|
2293
|
+
|
|
2294
|
+
* ``deleteThisThing(props.row)``
|
|
2295
|
+
* ``$emit('do-something', props.row)``
|
|
2296
|
+
|
|
2261
2297
|
.. attribute:: icon
|
|
2262
2298
|
|
|
2263
2299
|
Name of icon to be shown for the action link.
|
|
@@ -2275,6 +2311,8 @@ class GridAction:
|
|
|
2275
2311
|
key,
|
|
2276
2312
|
label=None,
|
|
2277
2313
|
url=None,
|
|
2314
|
+
target=None,
|
|
2315
|
+
click_handler=None,
|
|
2278
2316
|
icon=None,
|
|
2279
2317
|
link_class=None,
|
|
2280
2318
|
):
|
|
@@ -2283,6 +2321,8 @@ class GridAction:
|
|
|
2283
2321
|
self.app = self.config.get_app()
|
|
2284
2322
|
self.key = key
|
|
2285
2323
|
self.url = url
|
|
2324
|
+
self.target = target
|
|
2325
|
+
self.click_handler = click_handler
|
|
2286
2326
|
self.label = label or self.app.make_title(key)
|
|
2287
2327
|
self.icon = icon or key
|
|
2288
2328
|
self.link_class = link_class or ''
|
|
@@ -180,6 +180,12 @@
|
|
|
180
180
|
% for action in grid.actions:
|
|
181
181
|
<a v-if="props.row._action_url_${action.key}"
|
|
182
182
|
:href="props.row._action_url_${action.key}"
|
|
183
|
+
% if action.target:
|
|
184
|
+
target="${action.target}"
|
|
185
|
+
% endif
|
|
186
|
+
% if action.click_handler:
|
|
187
|
+
@click.prevent="${action.click_handler}"
|
|
188
|
+
% endif
|
|
183
189
|
class="${action.link_class}">
|
|
184
190
|
${action.render_icon_and_label()}
|
|
185
191
|
</a>
|
|
@@ -8,10 +8,14 @@
|
|
|
8
8
|
|
|
9
9
|
<%def name="page_content()">
|
|
10
10
|
% if grid is not Undefined:
|
|
11
|
-
${
|
|
11
|
+
${self.render_grid_tag()}
|
|
12
12
|
% endif
|
|
13
13
|
</%def>
|
|
14
14
|
|
|
15
|
+
<%def name="render_grid_tag()">
|
|
16
|
+
${grid.render_vue_tag()}
|
|
17
|
+
</%def>
|
|
18
|
+
|
|
15
19
|
<%def name="render_vue_templates()">
|
|
16
20
|
${parent.render_vue_templates()}
|
|
17
21
|
${self.render_vue_template_grid()}
|
|
@@ -2176,6 +2176,28 @@ class MasterView(View):
|
|
|
2176
2176
|
"""
|
|
2177
2177
|
return str(instance) or "(no title)"
|
|
2178
2178
|
|
|
2179
|
+
def get_action_route_kwargs(self, obj):
|
|
2180
|
+
"""
|
|
2181
|
+
Get a dict of route kwargs for the given object.
|
|
2182
|
+
|
|
2183
|
+
This is called from :meth:`get_action_url()` and must return
|
|
2184
|
+
kwargs suitable for use with ``request.route_url()``.
|
|
2185
|
+
|
|
2186
|
+
In practice this should return a dict which has keys for each
|
|
2187
|
+
field from :meth:`get_model_key()` and values which come from
|
|
2188
|
+
the object.
|
|
2189
|
+
|
|
2190
|
+
:param obj: Model instance object.
|
|
2191
|
+
|
|
2192
|
+
:returns: The dict of route kwargs for the object.
|
|
2193
|
+
"""
|
|
2194
|
+
try:
|
|
2195
|
+
return dict([(key, obj[key])
|
|
2196
|
+
for key in self.get_model_key()])
|
|
2197
|
+
except TypeError:
|
|
2198
|
+
return dict([(key, getattr(obj, key))
|
|
2199
|
+
for key in self.get_model_key()])
|
|
2200
|
+
|
|
2179
2201
|
def get_action_url(self, action, obj, **kwargs):
|
|
2180
2202
|
"""
|
|
2181
2203
|
Generate an "action" URL for the given model instance.
|
|
@@ -2183,22 +2205,21 @@ class MasterView(View):
|
|
|
2183
2205
|
This is a shortcut which generates a route name based on
|
|
2184
2206
|
:meth:`get_route_prefix()` and the ``action`` param.
|
|
2185
2207
|
|
|
2186
|
-
It
|
|
2187
|
-
|
|
2208
|
+
It calls :meth:`get_action_route_kwargs()` and then passes
|
|
2209
|
+
those along with route name to ``request.route_url()``, and
|
|
2210
|
+
returns the result.
|
|
2188
2211
|
|
|
2189
2212
|
:param action: String name for the action, which corresponds
|
|
2190
2213
|
to part of some named route, e.g. ``'view'`` or ``'edit'``.
|
|
2191
2214
|
|
|
2192
2215
|
:param obj: Model instance object.
|
|
2216
|
+
|
|
2217
|
+
:param \**kwargs: Additional kwargs to be passed to
|
|
2218
|
+
``request.route_url()``, if needed.
|
|
2193
2219
|
"""
|
|
2194
|
-
|
|
2195
|
-
try:
|
|
2196
|
-
kw = dict([(key, obj[key])
|
|
2197
|
-
for key in self.get_model_key()])
|
|
2198
|
-
except TypeError:
|
|
2199
|
-
kw = dict([(key, getattr(obj, key))
|
|
2200
|
-
for key in self.get_model_key()])
|
|
2220
|
+
kw = self.get_action_route_kwargs(obj)
|
|
2201
2221
|
kw.update(kwargs)
|
|
2222
|
+
route_prefix = self.get_route_prefix()
|
|
2202
2223
|
return self.request.route_url(f'{route_prefix}.{action}', **kw)
|
|
2203
2224
|
|
|
2204
2225
|
def get_action_url_view(self, obj, i):
|
|
@@ -2729,7 +2750,7 @@ class MasterView(View):
|
|
|
2729
2750
|
inspector = sa.inspect(model_class)
|
|
2730
2751
|
keys = [col.name for col in inspector.primary_key]
|
|
2731
2752
|
return tuple([prop.key for prop in inspector.column_attrs
|
|
2732
|
-
if [col.name for col in prop.columns]
|
|
2753
|
+
if all([col.name in keys for col in prop.columns])])
|
|
2733
2754
|
|
|
2734
2755
|
raise AttributeError(f"you must define model_key for view class: {cls}")
|
|
2735
2756
|
|
|
@@ -213,7 +213,7 @@ class TestGrid(WebTestCase):
|
|
|
213
213
|
obj = MagicMock(foo=42.00)
|
|
214
214
|
self.assertEqual(grid.renderers['foo'](obj, 'foo', 42.00), '42')
|
|
215
215
|
|
|
216
|
-
def
|
|
216
|
+
def test_set_default_renderers(self):
|
|
217
217
|
model = self.app.model
|
|
218
218
|
|
|
219
219
|
# no defaults for "plain" schema
|
|
@@ -249,6 +249,18 @@ class TestGrid(WebTestCase):
|
|
|
249
249
|
self.assertIn('executing', grid.renderers)
|
|
250
250
|
self.assertIs(grid.renderers['executing'], myrender)
|
|
251
251
|
|
|
252
|
+
# nb. as of writing we have no Date columns in default schema,
|
|
253
|
+
# so must invent one to test that type
|
|
254
|
+
class SomeFoolery(model.Base):
|
|
255
|
+
__tablename__ = 'somefoolery'
|
|
256
|
+
id = sa.Column(sa.Integer(), primary_key=True)
|
|
257
|
+
created = sa.Column(sa.Date())
|
|
258
|
+
|
|
259
|
+
# renderer set for date mapped field
|
|
260
|
+
grid = self.make_grid(model_class=SomeFoolery)
|
|
261
|
+
self.assertIn('created', grid.renderers)
|
|
262
|
+
self.assertIsNot(grid.renderers['created'], myrender)
|
|
263
|
+
|
|
252
264
|
def test_linked_columns(self):
|
|
253
265
|
grid = self.make_grid(columns=['foo', 'bar'])
|
|
254
266
|
self.assertEqual(grid.linked_columns, [])
|
|
@@ -1415,6 +1427,20 @@ class TestGrid(WebTestCase):
|
|
|
1415
1427
|
# zero is *not* empty string (with this renderer)
|
|
1416
1428
|
self.assertEqual(grid.render_quantity(obj, 'foo', 0), "0")
|
|
1417
1429
|
|
|
1430
|
+
def test_render_date(self):
|
|
1431
|
+
grid = self.make_grid(columns=['foo', 'bar'])
|
|
1432
|
+
|
|
1433
|
+
# null
|
|
1434
|
+
obj = MagicMock(dt=None)
|
|
1435
|
+
result = grid.render_date(obj, 'dt', None)
|
|
1436
|
+
self.assertIsNone(result)
|
|
1437
|
+
|
|
1438
|
+
# typical
|
|
1439
|
+
dt = datetime.date(2025, 1, 13)
|
|
1440
|
+
obj = MagicMock(dt=dt)
|
|
1441
|
+
result = grid.render_date(obj, 'dt', str(dt))
|
|
1442
|
+
self.assertEqual(result, '2025-01-13')
|
|
1443
|
+
|
|
1418
1444
|
def test_render_datetime(self):
|
|
1419
1445
|
grid = self.make_grid(columns=['foo', 'bar'])
|
|
1420
1446
|
|
|
@@ -750,6 +750,29 @@ class TestMasterView(WebTestCase):
|
|
|
750
750
|
self.request.matchdict = {'name': 'blarg'}
|
|
751
751
|
self.assertRaises(HTTPNotFound, view.get_instance, session=self.session)
|
|
752
752
|
|
|
753
|
+
def test_get_action_route_kwargs(self):
|
|
754
|
+
model = self.app.model
|
|
755
|
+
with patch.object(mod.MasterView, 'model_class', new=model.Setting, create=True):
|
|
756
|
+
view = self.make_view()
|
|
757
|
+
|
|
758
|
+
# dict object
|
|
759
|
+
setting = {'name': 'foo', 'value': 'bar'}
|
|
760
|
+
kw = view.get_action_route_kwargs(setting)
|
|
761
|
+
self.assertEqual(kw, {'name': 'foo'})
|
|
762
|
+
|
|
763
|
+
# mapped object
|
|
764
|
+
setting = model.Setting(name='foo', value='bar')
|
|
765
|
+
kw = view.get_action_route_kwargs(setting)
|
|
766
|
+
self.assertEqual(kw, {'name': 'foo'})
|
|
767
|
+
|
|
768
|
+
# non-standard object
|
|
769
|
+
class MySetting:
|
|
770
|
+
def __init__(self, **kw):
|
|
771
|
+
self.__dict__.update(kw)
|
|
772
|
+
setting = MySetting(name='foo', value='bar')
|
|
773
|
+
kw = view.get_action_route_kwargs(setting)
|
|
774
|
+
self.assertEqual(kw, {'name': 'foo'})
|
|
775
|
+
|
|
753
776
|
def test_get_action_url_for_dict(self):
|
|
754
777
|
model = self.app.model
|
|
755
778
|
setting = {'name': 'foo', 'value': 'bar'}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|