WuttaWeb 0.19.1__tar.gz → 0.19.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.19.1 → wuttaweb-0.19.3}/CHANGELOG.md +18 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/PKG-INFO +2 -3
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/pyproject.toml +2 -3
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/forms/schema.py +47 -4
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/forms/widgets.py +5 -36
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/grids/base.py +23 -11
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/base.mako +1 -1
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/configure.mako +3 -3
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/wutta-components.mako +145 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/views/master.py +4 -1
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/forms/test_schema.py +49 -6
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/forms/test_widgets.py +0 -30
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/views/test_master.py +32 -0
- wuttaweb-0.19.1/tests/views/__init__.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/.gitignore +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/COPYING.txt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/README.md +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/Makefile +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/_static/.keepme +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.app.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.auth.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.cli.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.cli.webapp.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.conf.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.db.continuum.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.db.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.db.sess.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.emails.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.forms.base.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.forms.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.forms.schema.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.forms.widgets.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.grids.base.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.grids.filters.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.grids.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.handler.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.helpers.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.menus.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.progress.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.static.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.subscribers.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.util.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.views.auth.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.views.base.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.views.batch.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.views.common.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.views.email.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.views.essential.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.views.master.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.views.people.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.views.progress.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.views.roles.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.views.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.views.settings.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.views.upgrades.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/api/wuttaweb.views.users.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/conf.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/glossary.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/index.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/make.bat +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/narr/cli/builtin.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/narr/cli/index.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/narr/templates/base.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/narr/templates/index.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/narr/templates/lookup.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/docs/narr/templates/overview.rst +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/__init__.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/_version.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/app.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/auth.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/cli/__init__.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/cli/webapp.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/conf.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/db/__init__.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/db/continuum.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/db/sess.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/email-templates/feedback.html.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/email-templates/feedback.txt.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/emails.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/forms/__init__.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/forms/base.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/grids/__init__.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/grids/filters.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/handler.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/helpers.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/menus.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/progress.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/static/__init__.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/static/img/favicon.ico +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/static/img/logo.png +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/static/img/testing.png +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/subscribers.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/appinfo/index.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/auth/change_password.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/auth/login.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/base_meta.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/batch/view.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/deform/dateinput.pt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/deform/datetimeinput.pt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/deform/moneyinput.pt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/deform/password.pt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/deform/permissions.pt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/deform/readonly/checkbox.pt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/deform/readonly/email_recips.pt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/deform/readonly/filedownload.pt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/deform/select.pt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/deform/textarea.pt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/deform/textinput.pt +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/email/settings/view.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/forbidden.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/form.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/grids/table_element.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/grids/vue_template.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/home.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/master/configure.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/master/create.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/master/delete.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/master/edit.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/master/form.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/master/index.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/master/view.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/notfound.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/page.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/people/view_profile.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/progress.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/setup.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/upgrade.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/upgrades/configure.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/templates/upgrades/view.mako +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/testing.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/util.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/views/__init__.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/views/auth.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/views/base.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/views/batch.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/views/common.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/views/email.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/views/essential.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/views/people.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/views/progress.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/views/roles.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/views/settings.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/views/upgrades.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/src/wuttaweb/views/users.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tasks.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/__init__.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/cli/__init__.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/cli/test_webapp.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/db/__init__.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/db/test_continuum.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/forms/test_base.py +0 -0
- {wuttaweb-0.19.1/tests/forms → wuttaweb-0.19.3/tests/grids}/__init__.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/grids/test_base.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/grids/test_filters.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/libcache/bb_oruga.js +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/libcache/bb_oruga_bulma.css +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/libcache/bb_oruga_bulma.js +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/libcache/bb_vue.js +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/libcache/bb_vue_fontawesome.js +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/libcache/buefy.css +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/libcache/buefy.js +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/libcache/fontawesome.js +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/libcache/vue.js +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/libcache/vue_resource.js +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/test_app.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/test_auth.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/test_emails.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/test_handler.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/test_helpers.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/test_menus.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/test_progress.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/test_static.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/test_subscribers.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/test_util.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/util.py +0 -0
- {wuttaweb-0.19.1/tests/grids → wuttaweb-0.19.3/tests/views}/__init__.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/views/test___init__.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/views/test_auth.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/views/test_base.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/views/test_batch.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/views/test_common.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/views/test_email.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/views/test_essential.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/views/test_people.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/views/test_progress.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/views/test_roles.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/views/test_settings.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/views/test_upgrades.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tests/views/test_users.py +0 -0
- {wuttaweb-0.19.1 → wuttaweb-0.19.3}/tox.ini +0 -0
|
@@ -5,6 +5,24 @@ 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.19.3 (2025-01-09)
|
|
9
|
+
|
|
10
|
+
### Fix
|
|
11
|
+
|
|
12
|
+
- use `request.url` instead of `current_route_url()`
|
|
13
|
+
- add basic `<wutta-autocomplete>` component
|
|
14
|
+
- add `WuttaDictEnum` form schema type
|
|
15
|
+
|
|
16
|
+
## v0.19.2 (2025-01-07)
|
|
17
|
+
|
|
18
|
+
### Fix
|
|
19
|
+
|
|
20
|
+
- always use prop key for default grid filters
|
|
21
|
+
- avoid `request.current_route_url()` for user menu
|
|
22
|
+
- add `scale` kwarg for `WuttaMoney` schema type, widget
|
|
23
|
+
- make WuttaQuantity serialize w/ app handler, remove custom widget
|
|
24
|
+
- bugfix for bool simple settings with default value
|
|
25
|
+
|
|
8
26
|
## v0.19.1 (2025-01-06)
|
|
9
27
|
|
|
10
28
|
### Fix
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: WuttaWeb
|
|
3
|
-
Version: 0.19.
|
|
3
|
+
Version: 0.19.3
|
|
4
4
|
Summary: Web App for Wutta Framework
|
|
5
5
|
Project-URL: Homepage, https://wuttaproject.org/
|
|
6
6
|
Project-URL: Repository, https://forgejo.wuttaproject.org/wutta/wuttaweb
|
|
@@ -37,10 +37,9 @@ Requires-Dist: pyramid-fanstatic
|
|
|
37
37
|
Requires-Dist: pyramid-mako
|
|
38
38
|
Requires-Dist: pyramid-tm
|
|
39
39
|
Requires-Dist: pyramid>=2
|
|
40
|
-
Requires-Dist: sqlalchemy-utils
|
|
41
40
|
Requires-Dist: waitress
|
|
42
41
|
Requires-Dist: webhelpers2
|
|
43
|
-
Requires-Dist: wuttjamaican[db]>=0.19.
|
|
42
|
+
Requires-Dist: wuttjamaican[db]>=0.19.3
|
|
44
43
|
Requires-Dist: zope-sqlalchemy>=1.5
|
|
45
44
|
Provides-Extra: continuum
|
|
46
45
|
Requires-Dist: wutta-continuum; extra == 'continuum'
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "WuttaWeb"
|
|
9
|
-
version = "0.19.
|
|
9
|
+
version = "0.19.3"
|
|
10
10
|
description = "Web App for Wutta Framework"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
|
|
@@ -42,10 +42,9 @@ dependencies = [
|
|
|
42
42
|
"pyramid_fanstatic",
|
|
43
43
|
"pyramid_mako",
|
|
44
44
|
"pyramid_tm",
|
|
45
|
-
"SQLAlchemy-Utils",
|
|
46
45
|
"waitress",
|
|
47
46
|
"WebHelpers2",
|
|
48
|
-
"WuttJamaican[db]>=0.19.
|
|
47
|
+
"WuttJamaican[db]>=0.19.3",
|
|
49
48
|
"zope.sqlalchemy>=1.5",
|
|
50
49
|
]
|
|
51
50
|
|
|
@@ -155,6 +155,37 @@ class WuttaEnum(colander.Enum):
|
|
|
155
155
|
return widgets.SelectWidget(**kwargs)
|
|
156
156
|
|
|
157
157
|
|
|
158
|
+
class WuttaDictEnum(colander.String):
|
|
159
|
+
"""
|
|
160
|
+
Schema type for "pseudo-enum" fields which reference a dict for
|
|
161
|
+
known values instead of a true enum class.
|
|
162
|
+
|
|
163
|
+
This is primarily for use with "status" fields such as
|
|
164
|
+
:attr:`~wuttjamaican:wuttjamaican.db.model.batch.BatchRowMixin.status_code`.
|
|
165
|
+
|
|
166
|
+
This is a subclass of :class:`colander.String`, but adds a default
|
|
167
|
+
widget (``SelectWidget``) with enum choices.
|
|
168
|
+
|
|
169
|
+
:param request: Current :term:`request` object.
|
|
170
|
+
|
|
171
|
+
:param enum_dct: Dict with possible enum values and labels.
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
def __init__(self, request, enum_dct, *args, **kwargs):
|
|
175
|
+
super().__init__(*args, **kwargs)
|
|
176
|
+
self.request = request
|
|
177
|
+
self.config = self.request.wutta_config
|
|
178
|
+
self.app = self.config.get_app()
|
|
179
|
+
self.enum_dct = enum_dct
|
|
180
|
+
|
|
181
|
+
def widget_maker(self, **kwargs):
|
|
182
|
+
""" """
|
|
183
|
+
if 'values' not in kwargs:
|
|
184
|
+
kwargs['values'] = [(k, v) for k, v in self.enum_dct.items()]
|
|
185
|
+
|
|
186
|
+
return widgets.SelectWidget(**kwargs)
|
|
187
|
+
|
|
188
|
+
|
|
158
189
|
class WuttaMoney(colander.Money):
|
|
159
190
|
"""
|
|
160
191
|
Custom schema type for "money" fields.
|
|
@@ -164,9 +195,13 @@ class WuttaMoney(colander.Money):
|
|
|
164
195
|
by default.
|
|
165
196
|
|
|
166
197
|
:param request: Current :term:`request` object.
|
|
198
|
+
|
|
199
|
+
:param scale: If this kwarg is specified, it will be passed along
|
|
200
|
+
to the widget constructor.
|
|
167
201
|
"""
|
|
168
202
|
|
|
169
203
|
def __init__(self, request, *args, **kwargs):
|
|
204
|
+
self.scale = kwargs.pop('scale', None)
|
|
170
205
|
super().__init__(*args, **kwargs)
|
|
171
206
|
self.request = request
|
|
172
207
|
self.config = self.request.wutta_config
|
|
@@ -174,6 +209,8 @@ class WuttaMoney(colander.Money):
|
|
|
174
209
|
|
|
175
210
|
def widget_maker(self, **kwargs):
|
|
176
211
|
""" """
|
|
212
|
+
if self.scale:
|
|
213
|
+
kwargs.setdefault('scale', self.scale)
|
|
177
214
|
return widgets.WuttaMoneyInputWidget(self.request, **kwargs)
|
|
178
215
|
|
|
179
216
|
|
|
@@ -181,8 +218,9 @@ class WuttaQuantity(colander.Decimal):
|
|
|
181
218
|
"""
|
|
182
219
|
Custom schema type for "quantity" fields.
|
|
183
220
|
|
|
184
|
-
This is a subclass of :class:`colander:colander.Decimal` but
|
|
185
|
-
|
|
221
|
+
This is a subclass of :class:`colander:colander.Decimal` but will
|
|
222
|
+
serialize values via
|
|
223
|
+
:meth:`~wuttjamaican:wuttjamaican.app.AppHandler.render_quantity()`.
|
|
186
224
|
|
|
187
225
|
:param request: Current :term:`request` object.
|
|
188
226
|
"""
|
|
@@ -193,9 +231,14 @@ class WuttaQuantity(colander.Decimal):
|
|
|
193
231
|
self.config = self.request.wutta_config
|
|
194
232
|
self.app = self.config.get_app()
|
|
195
233
|
|
|
196
|
-
def
|
|
234
|
+
def serialize(self, node, appstruct):
|
|
197
235
|
""" """
|
|
198
|
-
|
|
236
|
+
if appstruct in (colander.null, None):
|
|
237
|
+
return colander.null
|
|
238
|
+
|
|
239
|
+
# nb. we render as quantity here to avoid values like 12.0000,
|
|
240
|
+
# so we just show value like 12 instead
|
|
241
|
+
return self.app.render_quantity(appstruct)
|
|
199
242
|
|
|
200
243
|
|
|
201
244
|
class WuttaSet(colander.Set):
|
|
@@ -206,45 +206,13 @@ class WuttaMoneyInputWidget(MoneyInputWidget):
|
|
|
206
206
|
* ``moneyinput``
|
|
207
207
|
|
|
208
208
|
:param request: Current :term:`request` object.
|
|
209
|
-
"""
|
|
210
|
-
|
|
211
|
-
def __init__(self, request, *args, **kwargs):
|
|
212
|
-
super().__init__(*args, **kwargs)
|
|
213
|
-
self.request = request
|
|
214
|
-
self.config = self.request.wutta_config
|
|
215
|
-
self.app = self.config.get_app()
|
|
216
|
-
|
|
217
|
-
def serialize(self, field, cstruct, **kw):
|
|
218
|
-
""" """
|
|
219
|
-
readonly = kw.get('readonly', self.readonly)
|
|
220
|
-
if readonly:
|
|
221
|
-
if cstruct in (colander.null, None):
|
|
222
|
-
return HTML.tag('span')
|
|
223
|
-
cstruct = decimal.Decimal(cstruct)
|
|
224
|
-
return HTML.tag('span', c=[self.app.render_currency(cstruct)])
|
|
225
|
-
|
|
226
|
-
return super().serialize(field, cstruct, **kw)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
class WuttaQuantityWidget(TextInputWidget):
|
|
230
|
-
"""
|
|
231
|
-
Custom widget for "quantity" fields. This is used by default for
|
|
232
|
-
:class:`~wuttaweb.forms.schema.WuttaQuantity` type nodes.
|
|
233
|
-
|
|
234
|
-
The main purpose of this widget is to leverage
|
|
235
|
-
:meth:`~wuttjamaican:wuttjamaican.app.AppHandler.render_quantity()`
|
|
236
|
-
for the readonly display.
|
|
237
209
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
Deform templates:
|
|
241
|
-
|
|
242
|
-
* ``textinput``
|
|
243
|
-
|
|
244
|
-
:param request: Current :term:`request` object.
|
|
210
|
+
:param scale: If this kwarg is specified, it will be passed along
|
|
211
|
+
to ``render_currency()`` call.
|
|
245
212
|
"""
|
|
246
213
|
|
|
247
214
|
def __init__(self, request, *args, **kwargs):
|
|
215
|
+
self.scale = kwargs.pop('scale', 2)
|
|
248
216
|
super().__init__(*args, **kwargs)
|
|
249
217
|
self.request = request
|
|
250
218
|
self.config = self.request.wutta_config
|
|
@@ -257,7 +225,8 @@ class WuttaQuantityWidget(TextInputWidget):
|
|
|
257
225
|
if cstruct in (colander.null, None):
|
|
258
226
|
return HTML.tag('span')
|
|
259
227
|
cstruct = decimal.Decimal(cstruct)
|
|
260
|
-
|
|
228
|
+
text = self.app.render_currency(cstruct, scale=self.scale)
|
|
229
|
+
return HTML.tag('span', c=[text])
|
|
261
230
|
|
|
262
231
|
return super().serialize(field, cstruct, **kw)
|
|
263
232
|
|
|
@@ -32,7 +32,6 @@ from collections import namedtuple, OrderedDict
|
|
|
32
32
|
|
|
33
33
|
import sqlalchemy as sa
|
|
34
34
|
from sqlalchemy import orm
|
|
35
|
-
from sqlalchemy_utils import get_columns
|
|
36
35
|
|
|
37
36
|
import paginate
|
|
38
37
|
from paginate_sqlalchemy import SqlalchemyOrmPage
|
|
@@ -1147,16 +1146,29 @@ class Grid:
|
|
|
1147
1146
|
filters = filters or {}
|
|
1148
1147
|
|
|
1149
1148
|
if self.model_class:
|
|
1150
|
-
|
|
1151
|
-
#
|
|
1152
|
-
#
|
|
1153
|
-
#
|
|
1154
|
-
# i
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1149
|
+
|
|
1150
|
+
# nb. i have found this confusing for some reason. some
|
|
1151
|
+
# things i've tried so far include:
|
|
1152
|
+
#
|
|
1153
|
+
# i first tried self.get_model_columns() but my notes say
|
|
1154
|
+
# that was too aggressive in many cases.
|
|
1155
|
+
#
|
|
1156
|
+
# then i tried using the *subset* of self.columns, just
|
|
1157
|
+
# the ones which correspond to a property on the model
|
|
1158
|
+
# class. but sometimes that skips filters we need.
|
|
1159
|
+
#
|
|
1160
|
+
# then i tried get_columns() from sa-utils to give the
|
|
1161
|
+
# "true" column list, but that fails when the underlying
|
|
1162
|
+
# column has different name than the prop/attr key.
|
|
1163
|
+
#
|
|
1164
|
+
# so now, we are looking directly at the sa mapper, for
|
|
1165
|
+
# all column attrs and then using the prop key.
|
|
1166
|
+
|
|
1167
|
+
inspector = sa.inspect(self.model_class)
|
|
1168
|
+
for prop in inspector.column_attrs:
|
|
1169
|
+
if prop.key not in filters:
|
|
1170
|
+
attr = getattr(self.model_class, prop.key)
|
|
1171
|
+
filters[prop.key] = self.make_filter(attr)
|
|
1160
1172
|
|
|
1161
1173
|
return filters
|
|
1162
1174
|
|
|
@@ -659,7 +659,7 @@
|
|
|
659
659
|
% if request.is_root:
|
|
660
660
|
${h.form(url('stop_root'), ref='stopBeingRootForm')}
|
|
661
661
|
${h.csrf_token(request)}
|
|
662
|
-
<input type="hidden" name="referrer" value="${request.
|
|
662
|
+
<input type="hidden" name="referrer" value="${request.url}" />
|
|
663
663
|
<a @click="stopBeingRoot()"
|
|
664
664
|
class="navbar-item has-background-danger has-text-white">
|
|
665
665
|
Stop being root
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<br />
|
|
19
19
|
${self.buttons_content()}
|
|
20
20
|
|
|
21
|
-
${h.form(request.
|
|
21
|
+
${h.form(request.url, enctype='multipart/form-data', ref='saveSettingsForm', **{'@submit': 'saveSettingsFormSubmit'})}
|
|
22
22
|
${h.csrf_token(request)}
|
|
23
23
|
<div class="wutta-form-wrapper">
|
|
24
24
|
${self.form_content()}
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
<b-button @click="purgeSettingsShowDialog = false">
|
|
54
54
|
Cancel
|
|
55
55
|
</b-button>
|
|
56
|
-
${h.form(request.
|
|
56
|
+
${h.form(request.url, **{'@submit': 'purgingSettings = true'})}
|
|
57
57
|
${h.csrf_token(request)}
|
|
58
58
|
${h.hidden('remove_settings', 'true')}
|
|
59
59
|
<b-button type="is-danger"
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
icon-left="save">
|
|
108
108
|
{{ savingSettings ? "Working, please wait..." : "Save All Settings" }}
|
|
109
109
|
</b-button>
|
|
110
|
-
<b-button tag="a" href="${request.
|
|
110
|
+
<b-button tag="a" href="${request.url}"
|
|
111
111
|
icon-pack="fas"
|
|
112
112
|
icon-left="undo"
|
|
113
113
|
@click="undoChanges = true"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
<%def name="make_wutta_components()">
|
|
3
3
|
${self.make_wutta_request_mixin()}
|
|
4
|
+
${self.make_wutta_autocomplete_component()}
|
|
4
5
|
${self.make_wutta_button_component()}
|
|
5
6
|
${self.make_wutta_datepicker_component()}
|
|
6
7
|
${self.make_wutta_timepicker_component()}
|
|
@@ -86,6 +87,150 @@
|
|
|
86
87
|
</script>
|
|
87
88
|
</%def>
|
|
88
89
|
|
|
90
|
+
<%def name="make_wutta_autocomplete_component()">
|
|
91
|
+
<script type="text/x-template" id="wutta-autocomplete-template">
|
|
92
|
+
<div>
|
|
93
|
+
<b-autocomplete ref="autocomplete"
|
|
94
|
+
v-show="!value"
|
|
95
|
+
v-model="entry"
|
|
96
|
+
:data="data"
|
|
97
|
+
@typing="getAsyncData"
|
|
98
|
+
@select="selectionMade"
|
|
99
|
+
keep-first>
|
|
100
|
+
<template slot-scope="props">
|
|
101
|
+
{{ props.option.label }}
|
|
102
|
+
</template>
|
|
103
|
+
</b-autocomplete>
|
|
104
|
+
<b-button v-if="value"
|
|
105
|
+
@click="clearValue(true, true)">
|
|
106
|
+
{{ recordLabel }} (click to change)
|
|
107
|
+
</b-button>
|
|
108
|
+
</div>
|
|
109
|
+
</script>
|
|
110
|
+
<script>
|
|
111
|
+
const WuttaAutocomplete = {
|
|
112
|
+
template: '#wutta-autocomplete-template',
|
|
113
|
+
|
|
114
|
+
props: {
|
|
115
|
+
|
|
116
|
+
// callers do not specify this directly but rather by way
|
|
117
|
+
// of the `v-model` directive. the component will emit
|
|
118
|
+
// `input` events when this value changes
|
|
119
|
+
value: String,
|
|
120
|
+
|
|
121
|
+
// caller must specify initial display string, if the
|
|
122
|
+
// (v-model) value is not empty when component loads
|
|
123
|
+
display: String,
|
|
124
|
+
|
|
125
|
+
// the url from which search results are obtained. the
|
|
126
|
+
// endpoint should expect a GET with single `term` param
|
|
127
|
+
// in query string, and return list of objects, each with
|
|
128
|
+
// (at least) `value` and `label` properties.
|
|
129
|
+
serviceUrl: String,
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
data() {
|
|
133
|
+
return {
|
|
134
|
+
|
|
135
|
+
// user search input
|
|
136
|
+
entry: null,
|
|
137
|
+
|
|
138
|
+
// display label for button, when value is set
|
|
139
|
+
recordLabel: this.display,
|
|
140
|
+
|
|
141
|
+
// this contains the latest search results; it will
|
|
142
|
+
// change over time as user types. when an option is
|
|
143
|
+
// selected, it will be an element from this list.
|
|
144
|
+
data: [],
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
watch: {
|
|
149
|
+
|
|
150
|
+
value(val) {
|
|
151
|
+
// reset ourself when model value is cleared
|
|
152
|
+
if (!val) {
|
|
153
|
+
this.clearValue()
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
methods: {
|
|
159
|
+
|
|
160
|
+
focus() {
|
|
161
|
+
this.$refs.autocomplete.focus()
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
// convenience for parent component to fetch current label
|
|
165
|
+
getLabel() {
|
|
166
|
+
return this.recordLabel
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
// fetch new search results from server. this is invoked
|
|
170
|
+
// when user types new input
|
|
171
|
+
getAsyncData(entry) {
|
|
172
|
+
|
|
173
|
+
// nb. skip search until we have at least 3 chars of input
|
|
174
|
+
if (entry.length < 3) {
|
|
175
|
+
this.data = []
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// search results become autocomplete options
|
|
180
|
+
this.$http.get(this.serviceUrl + '?term=' + encodeURIComponent(entry))
|
|
181
|
+
.then(({ data }) => {
|
|
182
|
+
this.data = data
|
|
183
|
+
})
|
|
184
|
+
.catch((error) => {
|
|
185
|
+
this.data = []
|
|
186
|
+
throw error
|
|
187
|
+
})
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
// handle selection change. this is invoked when user
|
|
191
|
+
// chooses an autocomplete option
|
|
192
|
+
selectionMade(option) {
|
|
193
|
+
|
|
194
|
+
// reset user input
|
|
195
|
+
this.entry = null
|
|
196
|
+
|
|
197
|
+
// nb. this method can be triggered when a selection
|
|
198
|
+
// is made *or cleared* - if the latter then we do not
|
|
199
|
+
// want to emit event for the empty value; that part
|
|
200
|
+
// is handled in clearValue()
|
|
201
|
+
if (option) {
|
|
202
|
+
this.recordLabel = option.label
|
|
203
|
+
this.$emit('input', option.value)
|
|
204
|
+
} else {
|
|
205
|
+
this.recordLabel = null
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
// clear the component value
|
|
210
|
+
clearValue(emit, focus) {
|
|
211
|
+
|
|
212
|
+
// clear autocomplete selection
|
|
213
|
+
this.$refs.autocomplete.setSelected(null)
|
|
214
|
+
|
|
215
|
+
// maybe emit event for new value
|
|
216
|
+
if (emit) {
|
|
217
|
+
this.$emit('input', null)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// maybe set focus to autocomplete
|
|
221
|
+
if (focus) {
|
|
222
|
+
this.$nextTick(function() {
|
|
223
|
+
this.focus()
|
|
224
|
+
})
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
}
|
|
230
|
+
Vue.component('wutta-autocomplete', WuttaAutocomplete)
|
|
231
|
+
</script>
|
|
232
|
+
</%def>
|
|
233
|
+
|
|
89
234
|
<%def name="make_wutta_button_component()">
|
|
90
235
|
<script type="text/x-template" id="wutta-button-template">
|
|
91
236
|
<b-button :type="type"
|
|
@@ -1178,7 +1178,7 @@ class MasterView(View):
|
|
|
1178
1178
|
self.request.session.flash("Settings have been saved.")
|
|
1179
1179
|
|
|
1180
1180
|
# reload configure page
|
|
1181
|
-
return self.redirect(self.request.
|
|
1181
|
+
return self.redirect(self.request.url)
|
|
1182
1182
|
|
|
1183
1183
|
# render configure page
|
|
1184
1184
|
context = self.configure_get_context()
|
|
@@ -1329,6 +1329,9 @@ class MasterView(View):
|
|
|
1329
1329
|
|
|
1330
1330
|
if name in data:
|
|
1331
1331
|
value = data[name]
|
|
1332
|
+
elif simple.get('type') is bool:
|
|
1333
|
+
# nb. bool false will be *missing* from data
|
|
1334
|
+
value = False
|
|
1332
1335
|
else:
|
|
1333
1336
|
value = simple.get('default')
|
|
1334
1337
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8; -*-
|
|
2
2
|
|
|
3
3
|
import datetime
|
|
4
|
+
import decimal
|
|
4
5
|
from unittest import TestCase
|
|
5
6
|
from unittest.mock import patch
|
|
6
7
|
|
|
@@ -15,7 +16,7 @@ from wuttaweb.forms import widgets
|
|
|
15
16
|
from wuttaweb.testing import DataTestCase, WebTestCase
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
class
|
|
19
|
+
class TestWuttaDateTime(TestCase):
|
|
19
20
|
|
|
20
21
|
def test_deserialize(self):
|
|
21
22
|
typ = mod.WuttaDateTime()
|
|
@@ -80,22 +81,64 @@ class TestWuttaEnum(WebTestCase):
|
|
|
80
81
|
self.assertIsInstance(widget, widgets.SelectWidget)
|
|
81
82
|
|
|
82
83
|
|
|
84
|
+
MOCK_STATUS_ONE = 1
|
|
85
|
+
MOCK_STATUS_TWO = 2
|
|
86
|
+
MOCK_STATUS = {
|
|
87
|
+
MOCK_STATUS_ONE: 'one',
|
|
88
|
+
MOCK_STATUS_TWO: 'two',
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
class TestWuttaDictEnum(WebTestCase):
|
|
92
|
+
|
|
93
|
+
def test_widget_maker(self):
|
|
94
|
+
typ = mod.WuttaDictEnum(self.request, MOCK_STATUS)
|
|
95
|
+
widget = typ.widget_maker()
|
|
96
|
+
self.assertIsInstance(widget, widgets.SelectWidget)
|
|
97
|
+
self.assertEqual(widget.values, [
|
|
98
|
+
(1, 'one'),
|
|
99
|
+
(2, 'two'),
|
|
100
|
+
])
|
|
101
|
+
|
|
102
|
+
|
|
83
103
|
class TestWuttaMoney(WebTestCase):
|
|
84
104
|
|
|
85
105
|
def test_widget_maker(self):
|
|
86
106
|
enum = self.app.enum
|
|
107
|
+
|
|
108
|
+
# default scale
|
|
87
109
|
typ = mod.WuttaMoney(self.request)
|
|
88
110
|
widget = typ.widget_maker()
|
|
89
111
|
self.assertIsInstance(widget, widgets.WuttaMoneyInputWidget)
|
|
112
|
+
self.assertEqual(widget.scale, 2)
|
|
113
|
+
|
|
114
|
+
# custom scale
|
|
115
|
+
typ = mod.WuttaMoney(self.request, scale=4)
|
|
116
|
+
widget = typ.widget_maker()
|
|
117
|
+
self.assertIsInstance(widget, widgets.WuttaMoneyInputWidget)
|
|
118
|
+
self.assertEqual(widget.scale, 4)
|
|
90
119
|
|
|
91
120
|
|
|
92
121
|
class TestWuttaQuantity(WebTestCase):
|
|
93
122
|
|
|
94
|
-
def
|
|
95
|
-
|
|
96
|
-
typ =
|
|
97
|
-
|
|
98
|
-
|
|
123
|
+
def test_serialize(self):
|
|
124
|
+
node = colander.SchemaNode(mod.WuttaQuantity(self.request))
|
|
125
|
+
typ = node.typ
|
|
126
|
+
|
|
127
|
+
# null
|
|
128
|
+
result = typ.serialize(node, colander.null)
|
|
129
|
+
self.assertIs(result, colander.null)
|
|
130
|
+
result = typ.serialize(node, None)
|
|
131
|
+
self.assertIs(result, colander.null)
|
|
132
|
+
|
|
133
|
+
# quantity
|
|
134
|
+
result = typ.serialize(node, 42)
|
|
135
|
+
self.assertEqual(result, '42')
|
|
136
|
+
result = typ.serialize(node, 42.00)
|
|
137
|
+
self.assertEqual(result, '42')
|
|
138
|
+
result = typ.serialize(node, decimal.Decimal('42.00'))
|
|
139
|
+
self.assertEqual(result, '42')
|
|
140
|
+
result = typ.serialize(node, 42.13)
|
|
141
|
+
self.assertEqual(result, '42.13')
|
|
99
142
|
|
|
100
143
|
|
|
101
144
|
class TestObjectRef(DataTestCase):
|
|
@@ -143,36 +143,6 @@ class TestWuttaMoneyInputWidget(WebTestCase):
|
|
|
143
143
|
self.assertEqual(result, '<span></span>')
|
|
144
144
|
|
|
145
145
|
|
|
146
|
-
class TestWuttaQuantityWidget(WebTestCase):
|
|
147
|
-
|
|
148
|
-
def make_field(self, node, **kwargs):
|
|
149
|
-
# TODO: not sure why default renderer is in use even though
|
|
150
|
-
# pyramid_deform was included in setup? but this works..
|
|
151
|
-
kwargs.setdefault('renderer', deform.Form.default_renderer)
|
|
152
|
-
return deform.Field(node, **kwargs)
|
|
153
|
-
|
|
154
|
-
def make_widget(self, **kwargs):
|
|
155
|
-
return mod.WuttaQuantityWidget(self.request, **kwargs)
|
|
156
|
-
|
|
157
|
-
def test_serialize(self):
|
|
158
|
-
node = colander.SchemaNode(schema.WuttaQuantity(self.request))
|
|
159
|
-
field = self.make_field(node)
|
|
160
|
-
widget = self.make_widget()
|
|
161
|
-
amount = decimal.Decimal('42.00')
|
|
162
|
-
|
|
163
|
-
# editable widget has normal text input
|
|
164
|
-
result = widget.serialize(field, str(amount))
|
|
165
|
-
self.assertIn('<b-input', result)
|
|
166
|
-
|
|
167
|
-
# readonly is rendered per app convention
|
|
168
|
-
result = widget.serialize(field, str(amount), readonly=True)
|
|
169
|
-
self.assertEqual(result, '<span>42</span>')
|
|
170
|
-
|
|
171
|
-
# readonly w/ null value
|
|
172
|
-
result = widget.serialize(field, None, readonly=True)
|
|
173
|
-
self.assertEqual(result, '<span></span>')
|
|
174
|
-
|
|
175
|
-
|
|
176
146
|
class TestFileDownloadWidget(WebTestCase):
|
|
177
147
|
|
|
178
148
|
def make_field(self, node, **kwargs):
|
|
@@ -1565,6 +1565,38 @@ class TestMasterView(WebTestCase):
|
|
|
1565
1565
|
count = self.session.query(model.Setting).count()
|
|
1566
1566
|
self.assertEqual(count, 0)
|
|
1567
1567
|
|
|
1568
|
+
def test_configure_gather_settings(self):
|
|
1569
|
+
view = self.make_view()
|
|
1570
|
+
|
|
1571
|
+
simple_settings = [
|
|
1572
|
+
{'name': 'wutta.app_title'},
|
|
1573
|
+
{'name': 'wutta.foo'},
|
|
1574
|
+
{'name': 'wutta.flag', 'type': bool, 'default': True},
|
|
1575
|
+
{'name': 'wutta.number', 'type': int, 'default': 42},
|
|
1576
|
+
{'name': 'wutta.value1', 'save_if_empty': True},
|
|
1577
|
+
{'name': 'wutta.value2', 'save_if_empty': False},
|
|
1578
|
+
{'name': 'wutta.value3', 'save_if_empty': False, 'default': 'baz'},
|
|
1579
|
+
]
|
|
1580
|
+
|
|
1581
|
+
data = {
|
|
1582
|
+
'wutta.app_title': 'Poser',
|
|
1583
|
+
'wutta.foo': 'bar',
|
|
1584
|
+
'wutta.number': 44,
|
|
1585
|
+
'wutta.value1': None,
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
with patch.object(view, 'configure_get_simple_settings', return_value=simple_settings):
|
|
1589
|
+
settings = view.configure_gather_settings(data)
|
|
1590
|
+
self.assertEqual(len(settings), 6)
|
|
1591
|
+
self.assertEqual(settings, [
|
|
1592
|
+
{'name': 'wutta.app_title', 'value': 'Poser'},
|
|
1593
|
+
{'name': 'wutta.foo', 'value': 'bar'},
|
|
1594
|
+
{'name': 'wutta.flag', 'value': 'false'},
|
|
1595
|
+
{'name': 'wutta.number', 'value': '44'},
|
|
1596
|
+
{'name': 'wutta.value1', 'value': ''},
|
|
1597
|
+
{'name': 'wutta.value3', 'value': 'baz'},
|
|
1598
|
+
])
|
|
1599
|
+
|
|
1568
1600
|
##############################
|
|
1569
1601
|
# row methods
|
|
1570
1602
|
##############################
|
|
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
|