WuttaWeb 0.17.1__tar.gz → 0.18.0__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.17.1 → wuttaweb-0.18.0}/CHANGELOG.md +14 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/PKG-INFO +4 -2
- wuttaweb-0.18.0/docs/api/wuttaweb.cli.rst +6 -0
- wuttaweb-0.18.0/docs/api/wuttaweb.cli.webapp.rst +6 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/conf.py +1 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/index.rst +3 -0
- wuttaweb-0.18.0/docs/narr/cli/builtin.rst +28 -0
- wuttaweb-0.18.0/docs/narr/cli/index.rst +14 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/pyproject.toml +7 -6
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/app.py +99 -24
- wuttaweb-0.18.0/src/wuttaweb/cli/__init__.py +28 -0
- wuttaweb-0.18.0/src/wuttaweb/cli/webapp.py +88 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/grids/base.py +1 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/grids/filters.py +98 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/grids/vue_template.mako +24 -23
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/wutta-components.mako +67 -2
- wuttaweb-0.18.0/tests/cli/test_webapp.py +104 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/grids/test_filters.py +156 -0
- wuttaweb-0.18.0/tests/test_app.py +131 -0
- wuttaweb-0.18.0/tests/views/__init__.py +0 -0
- wuttaweb-0.17.1/tests/test_app.py +0 -61
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/.gitignore +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/COPYING.txt +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/README.md +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/Makefile +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/_static/.keepme +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.app.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.auth.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.conf.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.db.continuum.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.db.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.db.sess.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.forms.base.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.forms.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.forms.schema.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.forms.widgets.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.grids.base.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.grids.filters.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.grids.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.handler.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.helpers.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.menus.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.progress.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.static.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.subscribers.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.util.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.auth.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.base.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.batch.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.common.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.essential.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.master.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.people.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.progress.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.roles.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.settings.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.upgrades.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.users.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/glossary.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/make.bat +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/narr/templates/base.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/narr/templates/index.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/narr/templates/lookup.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/docs/narr/templates/overview.rst +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/__init__.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/_version.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/auth.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/conf.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/db/__init__.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/db/continuum.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/db/sess.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/email/templates/feedback.html.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/email/templates/feedback.txt.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/forms/__init__.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/forms/base.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/forms/schema.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/forms/widgets.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/grids/__init__.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/handler.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/helpers.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/menus.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/progress.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/static/__init__.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/static/img/favicon.ico +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/static/img/logo.png +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/static/img/testing.png +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/subscribers.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/appinfo/index.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/auth/change_password.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/auth/login.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/base.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/base_meta.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/batch/view.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/configure.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/dateinput.pt +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/datetimeinput.pt +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/moneyinput.pt +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/password.pt +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/permissions.pt +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/readonly/checkbox.pt +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/readonly/filedownload.pt +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/select.pt +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/textarea.pt +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/textinput.pt +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/forbidden.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/form.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/grids/table_element.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/home.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/master/configure.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/master/create.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/master/delete.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/master/edit.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/master/form.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/master/index.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/master/view.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/notfound.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/page.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/people/view_profile.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/progress.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/setup.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/upgrade.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/upgrades/configure.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/templates/upgrades/view.mako +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/util.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/views/__init__.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/views/auth.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/views/base.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/views/batch.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/views/common.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/views/essential.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/views/master.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/views/people.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/views/progress.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/views/roles.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/views/settings.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/views/upgrades.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/src/wuttaweb/views/users.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tasks.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/__init__.py +0 -0
- {wuttaweb-0.17.1/tests/db → wuttaweb-0.18.0/tests/cli}/__init__.py +0 -0
- {wuttaweb-0.17.1/tests/forms → wuttaweb-0.18.0/tests/db}/__init__.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/db/test_continuum.py +0 -0
- {wuttaweb-0.17.1/tests/grids → wuttaweb-0.18.0/tests/forms}/__init__.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/forms/test_base.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/forms/test_schema.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/forms/test_widgets.py +0 -0
- {wuttaweb-0.17.1/tests/views → wuttaweb-0.18.0/tests/grids}/__init__.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/grids/test_base.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/libcache/bb_oruga.js +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/libcache/bb_oruga_bulma.css +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/libcache/bb_oruga_bulma.js +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/libcache/bb_vue.js +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/libcache/bb_vue_fontawesome.js +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/libcache/buefy.css +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/libcache/buefy.js +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/libcache/fontawesome.js +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/libcache/vue.js +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/libcache/vue_resource.js +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/test_auth.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/test_handler.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/test_helpers.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/test_menus.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/test_progress.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/test_static.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/test_subscribers.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/test_util.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/util.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/views/test___init__.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/views/test_auth.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/views/test_base.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/views/test_batch.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/views/test_common.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/views/test_master.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/views/test_people.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/views/test_progress.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/views/test_roles.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/views/test_settings.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/views/test_upgrades.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tests/views/test_users.py +0 -0
- {wuttaweb-0.17.1 → wuttaweb-0.18.0}/tox.ini +0 -0
|
@@ -5,6 +5,20 @@ 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.18.0 (2024-12-18)
|
|
9
|
+
|
|
10
|
+
### Feat
|
|
11
|
+
|
|
12
|
+
- add basic support for running in ASGI context
|
|
13
|
+
- add support for running via uvicorn; `wutta webapp` command
|
|
14
|
+
|
|
15
|
+
## v0.17.2 (2024-12-17)
|
|
16
|
+
|
|
17
|
+
### Fix
|
|
18
|
+
|
|
19
|
+
- add basic support for grid filters for Date fields
|
|
20
|
+
- fix style bug for grid "add filter" autocomplete
|
|
21
|
+
|
|
8
22
|
## v0.17.1 (2024-12-16)
|
|
9
23
|
|
|
10
24
|
### Fix
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: WuttaWeb
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.18.0
|
|
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
|
|
@@ -25,6 +25,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
25
25
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
26
26
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
27
|
Requires-Python: >=3.8
|
|
28
|
+
Requires-Dist: asgiref
|
|
28
29
|
Requires-Dist: colanderalchemy
|
|
29
30
|
Requires-Dist: humanize
|
|
30
31
|
Requires-Dist: markdown
|
|
@@ -38,13 +39,14 @@ Requires-Dist: pyramid-tm
|
|
|
38
39
|
Requires-Dist: pyramid>=2
|
|
39
40
|
Requires-Dist: waitress
|
|
40
41
|
Requires-Dist: webhelpers2
|
|
41
|
-
Requires-Dist: wuttjamaican[db]>=0.18.
|
|
42
|
+
Requires-Dist: wuttjamaican[db]>=0.18.1
|
|
42
43
|
Requires-Dist: zope-sqlalchemy>=1.5
|
|
43
44
|
Provides-Extra: continuum
|
|
44
45
|
Requires-Dist: wutta-continuum; extra == 'continuum'
|
|
45
46
|
Provides-Extra: docs
|
|
46
47
|
Requires-Dist: furo; extra == 'docs'
|
|
47
48
|
Requires-Dist: sphinx; extra == 'docs'
|
|
49
|
+
Requires-Dist: sphinxcontrib-programoutput; extra == 'docs'
|
|
48
50
|
Provides-Extra: tests
|
|
49
51
|
Requires-Dist: pytest-cov; extra == 'tests'
|
|
50
52
|
Requires-Dist: tox; extra == 'tests'
|
|
@@ -19,6 +19,7 @@ the narrative docs are pretty scant. That will eventually change.
|
|
|
19
19
|
:caption: Documentation:
|
|
20
20
|
|
|
21
21
|
glossary
|
|
22
|
+
narr/cli/index
|
|
22
23
|
narr/templates/index
|
|
23
24
|
|
|
24
25
|
.. toctree::
|
|
@@ -28,6 +29,8 @@ the narrative docs are pretty scant. That will eventually change.
|
|
|
28
29
|
api/wuttaweb
|
|
29
30
|
api/wuttaweb.app
|
|
30
31
|
api/wuttaweb.auth
|
|
32
|
+
api/wuttaweb.cli
|
|
33
|
+
api/wuttaweb.cli.webapp
|
|
31
34
|
api/wuttaweb.conf
|
|
32
35
|
api/wuttaweb.db
|
|
33
36
|
api/wuttaweb.db.continuum
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
===================
|
|
3
|
+
Built-in Commands
|
|
4
|
+
===================
|
|
5
|
+
|
|
6
|
+
Below are the :term:`subcommands <subcommand>` which come with
|
|
7
|
+
WuttaWeb.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
.. _wutta-webapp:
|
|
11
|
+
|
|
12
|
+
``wutta webapp``
|
|
13
|
+
----------------
|
|
14
|
+
|
|
15
|
+
Run the web app, according to config file(s).
|
|
16
|
+
|
|
17
|
+
This command is a convenience only; under the hood it can run `uvicorn
|
|
18
|
+
<https://www.uvicorn.org/#uvicornrun>`_ but by default will run
|
|
19
|
+
whatever :ref:`pserve <pyramid:pserve_script>` is setup to do (which
|
|
20
|
+
usually is `waitress
|
|
21
|
+
<https://docs.pylonsproject.org/projects/waitress/en/latest/index.html>`_).
|
|
22
|
+
|
|
23
|
+
Ultimately it's all up to config, so run different web apps with
|
|
24
|
+
different config files.
|
|
25
|
+
|
|
26
|
+
Defined in: :mod:`wuttaweb.cli.webapp`
|
|
27
|
+
|
|
28
|
+
.. program-output:: wutta webapp --help
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "WuttaWeb"
|
|
9
|
-
version = "0.
|
|
9
|
+
version = "0.18.0"
|
|
10
10
|
description = "Web App for Wutta Framework"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
|
|
@@ -30,6 +30,7 @@ classifiers = [
|
|
|
30
30
|
]
|
|
31
31
|
requires-python = ">= 3.8"
|
|
32
32
|
dependencies = [
|
|
33
|
+
"asgiref",
|
|
33
34
|
"ColanderAlchemy",
|
|
34
35
|
"humanize",
|
|
35
36
|
"markdown",
|
|
@@ -43,32 +44,32 @@ dependencies = [
|
|
|
43
44
|
"pyramid_tm",
|
|
44
45
|
"waitress",
|
|
45
46
|
"WebHelpers2",
|
|
46
|
-
"WuttJamaican[db]>=0.18.
|
|
47
|
+
"WuttJamaican[db]>=0.18.1",
|
|
47
48
|
"zope.sqlalchemy>=1.5",
|
|
48
49
|
]
|
|
49
50
|
|
|
50
51
|
|
|
51
52
|
[project.optional-dependencies]
|
|
52
53
|
continuum = ["Wutta-Continuum"]
|
|
53
|
-
docs = ["Sphinx", "furo"]
|
|
54
|
+
docs = ["Sphinx", "furo", "sphinxcontrib-programoutput"]
|
|
54
55
|
tests = ["pytest-cov", "tox"]
|
|
55
56
|
|
|
56
57
|
|
|
57
58
|
[project.entry-points."fanstatic.libraries"]
|
|
58
59
|
wuttaweb_img = "wuttaweb.static:img"
|
|
59
60
|
|
|
60
|
-
|
|
61
61
|
[project.entry-points."paste.app_factory"]
|
|
62
62
|
main = "wuttaweb.app:main"
|
|
63
63
|
|
|
64
|
-
|
|
65
64
|
[project.entry-points."wutta.app.providers"]
|
|
66
65
|
wuttaweb = "wuttaweb.app:WebAppProvider"
|
|
67
66
|
|
|
68
|
-
|
|
69
67
|
[project.entry-points."wutta.config.extensions"]
|
|
70
68
|
wuttaweb = "wuttaweb.conf:WuttaWebConfigExtension"
|
|
71
69
|
|
|
70
|
+
[project.entry-points."wutta.typer_imports"]
|
|
71
|
+
wuttaweb = "wuttaweb.cli"
|
|
72
|
+
|
|
72
73
|
|
|
73
74
|
[project.urls]
|
|
74
75
|
Homepage = "https://wuttaproject.org/"
|
|
@@ -24,17 +24,22 @@
|
|
|
24
24
|
Application
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
|
+
import logging
|
|
27
28
|
import os
|
|
28
29
|
|
|
29
30
|
from wuttjamaican.app import AppProvider
|
|
30
31
|
from wuttjamaican.conf import make_config
|
|
31
32
|
|
|
33
|
+
from asgiref.wsgi import WsgiToAsgi
|
|
32
34
|
from pyramid.config import Configurator
|
|
33
35
|
|
|
34
36
|
import wuttaweb.db
|
|
35
37
|
from wuttaweb.auth import WuttaSecurityPolicy
|
|
36
38
|
|
|
37
39
|
|
|
40
|
+
log = logging.getLogger(__name__)
|
|
41
|
+
|
|
42
|
+
|
|
38
43
|
class WebAppProvider(AppProvider):
|
|
39
44
|
"""
|
|
40
45
|
The :term:`app provider` for WuttaWeb. This adds some methods to
|
|
@@ -87,17 +92,20 @@ def make_wutta_config(settings, config_maker=None, **kwargs):
|
|
|
87
92
|
|
|
88
93
|
If this config file path cannot be discovered, an error is raised.
|
|
89
94
|
"""
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
wutta_config = settings.get('wutta_config')
|
|
96
|
+
if not wutta_config:
|
|
97
|
+
|
|
98
|
+
# validate config file path
|
|
99
|
+
path = settings.get('wutta.config')
|
|
100
|
+
if not path or not os.path.exists(path):
|
|
101
|
+
raise ValueError("Please set 'wutta.config' in [app:main] "
|
|
102
|
+
"section of config to the path of your "
|
|
103
|
+
"config file. Lame, but necessary.")
|
|
104
|
+
|
|
105
|
+
# make config, add to settings
|
|
106
|
+
config_maker = config_maker or make_config
|
|
107
|
+
wutta_config = config_maker(path, **kwargs)
|
|
108
|
+
settings['wutta_config'] = wutta_config
|
|
101
109
|
|
|
102
110
|
# configure database sessions
|
|
103
111
|
if hasattr(wutta_config, 'appdb_engine'):
|
|
@@ -148,19 +156,18 @@ def make_pyramid_config(settings):
|
|
|
148
156
|
|
|
149
157
|
def main(global_config, **settings):
|
|
150
158
|
"""
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
instead.
|
|
159
|
+
Make and return the WSGI application, per given settings.
|
|
160
|
+
|
|
161
|
+
This function is designed to be called via Paste, hence it does
|
|
162
|
+
require params and therefore can't be used directly as app factory
|
|
163
|
+
for general WSGI servers. For the latter see
|
|
164
|
+
:func:`make_wsgi_app()` instead.
|
|
165
|
+
|
|
166
|
+
And this *particular* function is not even that useful, it only
|
|
167
|
+
constructs an app with minimal views built-in to WuttaWeb. Most
|
|
168
|
+
apps will define their own ``main()`` function (e.g. as
|
|
169
|
+
``poser.web.app:main``), similar to this one but with additional
|
|
170
|
+
views and other config.
|
|
164
171
|
"""
|
|
165
172
|
wutta_config = make_wutta_config(settings)
|
|
166
173
|
pyramid_config = make_pyramid_config(settings)
|
|
@@ -170,3 +177,71 @@ def main(global_config, **settings):
|
|
|
170
177
|
pyramid_config.include('wuttaweb.views')
|
|
171
178
|
|
|
172
179
|
return pyramid_config.make_wsgi_app()
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def make_wsgi_app(main_app=None, config=None):
|
|
183
|
+
"""
|
|
184
|
+
Make and return a WSGI app, using the given Paste app factory.
|
|
185
|
+
|
|
186
|
+
See also :func:`make_asgi_app()` for the ASGI equivalent.
|
|
187
|
+
|
|
188
|
+
This function could be used directly for general WSGI servers
|
|
189
|
+
(e.g. uvicorn), ***if*** you just want the built-in :func:`main()`
|
|
190
|
+
app factory.
|
|
191
|
+
|
|
192
|
+
But most likely you do not, in which case you must define your own
|
|
193
|
+
function and call this one with your preferred app factory::
|
|
194
|
+
|
|
195
|
+
from wuttaweb.app import make_wsgi_app
|
|
196
|
+
|
|
197
|
+
def my_main(global_config, **settings):
|
|
198
|
+
# TODO: build your app
|
|
199
|
+
pass
|
|
200
|
+
|
|
201
|
+
def make_my_wsgi_app():
|
|
202
|
+
return make_wsgi_app(my_main)
|
|
203
|
+
|
|
204
|
+
So ``make_my_wsgi_app()`` could then be used as-is for general
|
|
205
|
+
WSGI servers. However, note that this approach will require
|
|
206
|
+
setting the ``WUTTA_CONFIG_FILES`` environment variable, unless
|
|
207
|
+
running via :ref:`wutta-webapp`.
|
|
208
|
+
|
|
209
|
+
:param main_app: Either a Paste-compatible app factory, or
|
|
210
|
+
:term:`spec` for one. If not specified, the built-in
|
|
211
|
+
:func:`main()` is assumed.
|
|
212
|
+
|
|
213
|
+
:param config: Optional :term:`config object`. If not specified,
|
|
214
|
+
one is created based on ``WUTTA_CONFIG_FILES`` environment
|
|
215
|
+
variable.
|
|
216
|
+
"""
|
|
217
|
+
if not config:
|
|
218
|
+
config = make_config()
|
|
219
|
+
app = config.get_app()
|
|
220
|
+
|
|
221
|
+
# extract pyramid settings
|
|
222
|
+
settings = config.get_dict('app:main')
|
|
223
|
+
|
|
224
|
+
# keep same config object
|
|
225
|
+
settings['wutta_config'] = config
|
|
226
|
+
|
|
227
|
+
# determine the app factory
|
|
228
|
+
if isinstance(main_app, str):
|
|
229
|
+
make_wsgi_app = app.load_object(main_app)
|
|
230
|
+
elif callable(main_app):
|
|
231
|
+
make_wsgi_app = main_app
|
|
232
|
+
else:
|
|
233
|
+
raise ValueError("main_app must be spec or callable")
|
|
234
|
+
|
|
235
|
+
# construct a pyramid app "per usual"
|
|
236
|
+
return make_wsgi_app({}, **settings)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def make_asgi_app(main_app=None, config=None):
|
|
240
|
+
"""
|
|
241
|
+
Make and return a ASGI app, using the given Paste app factory.
|
|
242
|
+
|
|
243
|
+
This works the same as :func:`make_wsgi_app()` and should be
|
|
244
|
+
called in the same way etc.
|
|
245
|
+
"""
|
|
246
|
+
wsgi_app = make_wsgi_app(main_app, config=config)
|
|
247
|
+
return WsgiToAsgi(wsgi_app)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# -*- coding: utf-8; -*-
|
|
2
|
+
################################################################################
|
|
3
|
+
#
|
|
4
|
+
# wuttaweb -- Web App for Wutta Framework
|
|
5
|
+
# Copyright © 2024 Lance Edgar
|
|
6
|
+
#
|
|
7
|
+
# This file is part of Wutta Framework.
|
|
8
|
+
#
|
|
9
|
+
# Wutta Framework is free software: you can redistribute it and/or modify it
|
|
10
|
+
# under the terms of the GNU General Public License as published by the Free
|
|
11
|
+
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
12
|
+
# later version.
|
|
13
|
+
#
|
|
14
|
+
# Wutta Framework is distributed in the hope that it will be useful, but
|
|
15
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
17
|
+
# more details.
|
|
18
|
+
#
|
|
19
|
+
# You should have received a copy of the GNU General Public License along with
|
|
20
|
+
# Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
|
|
21
|
+
#
|
|
22
|
+
################################################################################
|
|
23
|
+
"""
|
|
24
|
+
WuttaWeb - ``wutta`` subcommands
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
# nb. must bring in all modules for discovery to work
|
|
28
|
+
from . import webapp
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# -*- coding: utf-8; -*-
|
|
2
|
+
################################################################################
|
|
3
|
+
#
|
|
4
|
+
# wuttaweb -- Web App for Wutta Framework
|
|
5
|
+
# Copyright © 2024 Lance Edgar
|
|
6
|
+
#
|
|
7
|
+
# This file is part of Wutta Framework.
|
|
8
|
+
#
|
|
9
|
+
# Wutta Framework is free software: you can redistribute it and/or modify it
|
|
10
|
+
# under the terms of the GNU General Public License as published by the Free
|
|
11
|
+
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
12
|
+
# later version.
|
|
13
|
+
#
|
|
14
|
+
# Wutta Framework is distributed in the hope that it will be useful, but
|
|
15
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
17
|
+
# more details.
|
|
18
|
+
#
|
|
19
|
+
# You should have received a copy of the GNU General Public License along with
|
|
20
|
+
# Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
|
|
21
|
+
#
|
|
22
|
+
################################################################################
|
|
23
|
+
"""
|
|
24
|
+
See also: :ref:`wutta-webapp`
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import os
|
|
28
|
+
import sys
|
|
29
|
+
from typing_extensions import Annotated
|
|
30
|
+
|
|
31
|
+
import typer
|
|
32
|
+
from pyramid.scripts import pserve
|
|
33
|
+
|
|
34
|
+
from wuttjamaican.cli import wutta_typer
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@wutta_typer.command()
|
|
38
|
+
def webapp(
|
|
39
|
+
ctx: typer.Context,
|
|
40
|
+
auto_reload: Annotated[
|
|
41
|
+
bool,
|
|
42
|
+
typer.Option('--reload', '-r',
|
|
43
|
+
help="Auto-reload web app when files change.")] = False,
|
|
44
|
+
):
|
|
45
|
+
"""
|
|
46
|
+
Run the configured web app
|
|
47
|
+
"""
|
|
48
|
+
config = ctx.parent.wutta_config
|
|
49
|
+
|
|
50
|
+
# we'll need config file(s) to specify for web app
|
|
51
|
+
if not config.files_read:
|
|
52
|
+
sys.stderr.write("no config files found!\n")
|
|
53
|
+
sys.exit(1)
|
|
54
|
+
|
|
55
|
+
runner = config.get(f'{config.appname}.web.app.runner', default='pserve')
|
|
56
|
+
if runner == 'pserve':
|
|
57
|
+
|
|
58
|
+
# run pserve
|
|
59
|
+
argv = ['pserve', f'file+ini:{config.files_read[0]}']
|
|
60
|
+
if ctx.params['auto_reload']:
|
|
61
|
+
argv.append('--reload')
|
|
62
|
+
pserve.main(argv=argv)
|
|
63
|
+
|
|
64
|
+
elif runner == 'uvicorn':
|
|
65
|
+
|
|
66
|
+
import uvicorn
|
|
67
|
+
|
|
68
|
+
# need service details from config
|
|
69
|
+
spec = config.require(f'{config.appname}.web.app.spec')
|
|
70
|
+
kw = {
|
|
71
|
+
'host': config.get(f'{config.appname}.web.app.host', default='127.0.0.1'),
|
|
72
|
+
'port': config.get_int(f'{config.appname}.web.app.port', default=8000),
|
|
73
|
+
'reload': ctx.params['auto_reload'],
|
|
74
|
+
'reload_dirs': config.get_list(f'{config.appname}.web.app.reload_dirs'),
|
|
75
|
+
'factory': config.get_bool(f'{config.appname}.web.app.factory', default=False),
|
|
76
|
+
'interface': config.get(f'{config.appname}.web.app.interface', default='auto'),
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# also must inject our config files to env, since there is no
|
|
80
|
+
# other way to specify when running via uvicorn
|
|
81
|
+
os.environ['WUTTA_CONFIG_FILES'] = os.pathsep.join(config.files_read)
|
|
82
|
+
|
|
83
|
+
# run uvicorn
|
|
84
|
+
uvicorn.run(spec, **kw)
|
|
85
|
+
|
|
86
|
+
else:
|
|
87
|
+
sys.stderr.write(f"unknown web app runner: {runner}\n")
|
|
88
|
+
sys.exit(2)
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
Grid Filters
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
|
+
import datetime
|
|
27
28
|
import logging
|
|
28
29
|
|
|
29
30
|
import sqlalchemy as sa
|
|
@@ -69,6 +70,18 @@ class GridFilter:
|
|
|
69
70
|
|
|
70
71
|
Display label for the filter field.
|
|
71
72
|
|
|
73
|
+
.. attribute:: data_type
|
|
74
|
+
|
|
75
|
+
Simplistic "data type" which the filter supports. So far this
|
|
76
|
+
will be one of:
|
|
77
|
+
|
|
78
|
+
* ``'string'``
|
|
79
|
+
* ``'date'``
|
|
80
|
+
|
|
81
|
+
Note that this mainly applies to the "value input" used by the
|
|
82
|
+
filter. There is no data type for boolean since it does not
|
|
83
|
+
need a value input; the verb is enough.
|
|
84
|
+
|
|
72
85
|
.. attribute:: active
|
|
73
86
|
|
|
74
87
|
Boolean indicating whether the filter is currently active.
|
|
@@ -110,12 +123,18 @@ class GridFilter:
|
|
|
110
123
|
|
|
111
124
|
See also :attr:`default_verb`.
|
|
112
125
|
"""
|
|
126
|
+
data_type = 'string'
|
|
113
127
|
default_verbs = ['equal', 'not_equal']
|
|
114
128
|
|
|
115
129
|
default_verb_labels = {
|
|
116
130
|
'is_any': "is any",
|
|
117
131
|
'equal': "equal to",
|
|
118
132
|
'not_equal': "not equal to",
|
|
133
|
+
'greater_than': "greater than",
|
|
134
|
+
'greater_equal': "greater than or equal to",
|
|
135
|
+
'less_than': "less than",
|
|
136
|
+
'less_equal': "less than or equal to",
|
|
137
|
+
# 'between': "between",
|
|
119
138
|
'is_true': "is true",
|
|
120
139
|
'is_false': "is false",
|
|
121
140
|
'is_false_null': "is false or null",
|
|
@@ -343,6 +362,42 @@ class AlchemyFilter(GridFilter):
|
|
|
343
362
|
self.model_property != value,
|
|
344
363
|
))
|
|
345
364
|
|
|
365
|
+
def filter_greater_than(self, query, value):
|
|
366
|
+
"""
|
|
367
|
+
Filter data with a greater than (``>``) condition.
|
|
368
|
+
"""
|
|
369
|
+
value = self.coerce_value(value)
|
|
370
|
+
if value is None:
|
|
371
|
+
return query
|
|
372
|
+
return query.filter(self.model_property > value)
|
|
373
|
+
|
|
374
|
+
def filter_greater_equal(self, query, value):
|
|
375
|
+
"""
|
|
376
|
+
Filter data with a greater than or equal (``>=``) condition.
|
|
377
|
+
"""
|
|
378
|
+
value = self.coerce_value(value)
|
|
379
|
+
if value is None:
|
|
380
|
+
return query
|
|
381
|
+
return query.filter(self.model_property >= value)
|
|
382
|
+
|
|
383
|
+
def filter_less_than(self, query, value):
|
|
384
|
+
"""
|
|
385
|
+
Filter data with a less than (``<``) condition.
|
|
386
|
+
"""
|
|
387
|
+
value = self.coerce_value(value)
|
|
388
|
+
if value is None:
|
|
389
|
+
return query
|
|
390
|
+
return query.filter(self.model_property < value)
|
|
391
|
+
|
|
392
|
+
def filter_less_equal(self, query, value):
|
|
393
|
+
"""
|
|
394
|
+
Filter data with a less than or equal (``<=``) condition.
|
|
395
|
+
"""
|
|
396
|
+
value = self.coerce_value(value)
|
|
397
|
+
if value is None:
|
|
398
|
+
return query
|
|
399
|
+
return query.filter(self.model_property <= value)
|
|
400
|
+
|
|
346
401
|
def filter_is_null(self, query, value):
|
|
347
402
|
"""
|
|
348
403
|
Filter data with an ``IS NULL`` query. The value is ignored.
|
|
@@ -467,9 +522,52 @@ class BooleanAlchemyFilter(AlchemyFilter):
|
|
|
467
522
|
self.model_property == None))
|
|
468
523
|
|
|
469
524
|
|
|
525
|
+
class DateAlchemyFilter(AlchemyFilter):
|
|
526
|
+
"""
|
|
527
|
+
SQLAlchemy filter option for a
|
|
528
|
+
:class:`sqlalchemy:sqlalchemy.types.Date` column.
|
|
529
|
+
|
|
530
|
+
Subclass of :class:`AlchemyFilter`.
|
|
531
|
+
"""
|
|
532
|
+
data_type = 'date'
|
|
533
|
+
default_verbs = [
|
|
534
|
+
'equal',
|
|
535
|
+
'not_equal',
|
|
536
|
+
'greater_than',
|
|
537
|
+
'greater_equal',
|
|
538
|
+
'less_than',
|
|
539
|
+
'less_equal',
|
|
540
|
+
# 'between',
|
|
541
|
+
]
|
|
542
|
+
|
|
543
|
+
default_verb_labels = {
|
|
544
|
+
'equal': "on",
|
|
545
|
+
'not_equal': "not on",
|
|
546
|
+
'greater_than': "after",
|
|
547
|
+
'greater_equal': "on or after",
|
|
548
|
+
'less_than': "before",
|
|
549
|
+
'less_equal': "on or before",
|
|
550
|
+
# 'between': "between",
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
def coerce_value(self, value):
|
|
554
|
+
""" """
|
|
555
|
+
if value:
|
|
556
|
+
if isinstance(value, datetime.date):
|
|
557
|
+
return value
|
|
558
|
+
|
|
559
|
+
try:
|
|
560
|
+
dt = datetime.datetime.strptime(value, '%Y-%m-%d')
|
|
561
|
+
except ValueError:
|
|
562
|
+
log.warning("invalid date value: %s", value)
|
|
563
|
+
else:
|
|
564
|
+
return dt.date()
|
|
565
|
+
|
|
566
|
+
|
|
470
567
|
default_sqlalchemy_filters = {
|
|
471
568
|
None: AlchemyFilter,
|
|
472
569
|
sa.String: StringAlchemyFilter,
|
|
473
570
|
sa.Text: StringAlchemyFilter,
|
|
474
571
|
sa.Boolean: BooleanAlchemyFilter,
|
|
572
|
+
sa.Date: DateAlchemyFilter,
|
|
475
573
|
}
|