WuttaWeb 0.17.0__tar.gz → 0.17.2__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.0 → wuttaweb-0.17.2}/CHANGELOG.md +14 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/PKG-INFO +3 -2
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/pyproject.toml +1 -1
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/grids/base.py +1 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/grids/filters.py +98 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/batch/view.mako +3 -3
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/grids/vue_template.mako +24 -23
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/wutta-components.mako +67 -2
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/views/batch.py +12 -6
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/grids/test_filters.py +156 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/.gitignore +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/COPYING.txt +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/README.md +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/Makefile +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/_static/.keepme +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.app.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.auth.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.conf.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.db.continuum.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.db.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.db.sess.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.forms.base.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.forms.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.forms.schema.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.forms.widgets.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.grids.base.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.grids.filters.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.grids.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.handler.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.helpers.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.menus.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.progress.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.static.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.subscribers.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.util.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.views.auth.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.views.base.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.views.batch.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.views.common.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.views.essential.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.views.master.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.views.people.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.views.progress.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.views.roles.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.views.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.views.settings.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.views.upgrades.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/api/wuttaweb.views.users.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/conf.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/glossary.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/index.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/make.bat +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/narr/templates/base.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/narr/templates/index.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/narr/templates/lookup.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/docs/narr/templates/overview.rst +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/__init__.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/_version.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/app.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/auth.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/conf.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/db/__init__.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/db/continuum.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/db/sess.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/email/templates/feedback.html.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/email/templates/feedback.txt.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/forms/__init__.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/forms/base.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/forms/schema.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/forms/widgets.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/grids/__init__.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/handler.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/helpers.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/menus.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/progress.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/static/__init__.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/static/img/favicon.ico +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/static/img/logo.png +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/static/img/testing.png +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/subscribers.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/appinfo/index.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/auth/change_password.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/auth/login.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/base.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/base_meta.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/configure.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/deform/dateinput.pt +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/deform/datetimeinput.pt +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/deform/moneyinput.pt +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/deform/password.pt +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/deform/permissions.pt +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/deform/readonly/checkbox.pt +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/deform/readonly/filedownload.pt +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/deform/select.pt +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/deform/textarea.pt +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/deform/textinput.pt +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/forbidden.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/form.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/grids/table_element.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/home.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/master/configure.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/master/create.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/master/delete.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/master/edit.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/master/form.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/master/index.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/master/view.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/notfound.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/page.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/people/view_profile.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/progress.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/setup.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/upgrade.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/upgrades/configure.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/templates/upgrades/view.mako +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/util.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/views/__init__.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/views/auth.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/views/base.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/views/common.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/views/essential.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/views/master.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/views/people.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/views/progress.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/views/roles.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/views/settings.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/views/upgrades.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/src/wuttaweb/views/users.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tasks.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/__init__.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/db/__init__.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/db/test_continuum.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/forms/__init__.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/forms/test_base.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/forms/test_schema.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/forms/test_widgets.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/grids/__init__.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/grids/test_base.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/libcache/bb_oruga.js +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/libcache/bb_oruga_bulma.css +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/libcache/bb_oruga_bulma.js +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/libcache/bb_vue.js +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/libcache/bb_vue_fontawesome.js +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/libcache/buefy.css +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/libcache/buefy.js +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/libcache/fontawesome.js +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/libcache/vue.js +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/libcache/vue_resource.js +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/test_app.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/test_auth.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/test_handler.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/test_helpers.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/test_menus.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/test_progress.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/test_static.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/test_subscribers.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/test_util.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/util.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/views/__init__.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/views/test___init__.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/views/test_auth.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/views/test_base.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/views/test_batch.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/views/test_common.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/views/test_master.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/views/test_people.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/views/test_progress.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/views/test_roles.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/views/test_settings.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/views/test_upgrades.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/tests/views/test_users.py +0 -0
- {wuttaweb-0.17.0 → wuttaweb-0.17.2}/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.17.2 (2024-12-17)
|
|
9
|
+
|
|
10
|
+
### Fix
|
|
11
|
+
|
|
12
|
+
- add basic support for grid filters for Date fields
|
|
13
|
+
- fix style bug for grid "add filter" autocomplete
|
|
14
|
+
|
|
15
|
+
## v0.17.1 (2024-12-16)
|
|
16
|
+
|
|
17
|
+
### Fix
|
|
18
|
+
|
|
19
|
+
- tweak wording for batch execution
|
|
20
|
+
- let view subclass more easily inject kwargs for `make_batch()`
|
|
21
|
+
|
|
8
22
|
## v0.17.0 (2024-12-15)
|
|
9
23
|
|
|
10
24
|
### Feat
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: WuttaWeb
|
|
3
|
-
Version: 0.17.
|
|
3
|
+
Version: 0.17.2
|
|
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
|
|
@@ -8,6 +8,7 @@ Project-URL: Issues, https://forgejo.wuttaproject.org/wutta/wuttaweb/issues
|
|
|
8
8
|
Project-URL: Changelog, https://forgejo.wuttaproject.org/wutta/wuttaweb/src/branch/master/CHANGELOG.md
|
|
9
9
|
Author-email: Lance Edgar <lance@wuttaproject.org>
|
|
10
10
|
License: GNU GPL v3+
|
|
11
|
+
License-File: COPYING.txt
|
|
11
12
|
Classifier: Development Status :: 4 - Beta
|
|
12
13
|
Classifier: Environment :: Web Environment
|
|
13
14
|
Classifier: Framework :: Pyramid
|
|
@@ -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
|
}
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
<b-notification :closable="false">
|
|
26
26
|
<p class="block">
|
|
27
27
|
Batch was executed<br />
|
|
28
|
-
${app.render_time_ago(batch.executed)}
|
|
29
|
-
by ${batch.executed_by}
|
|
28
|
+
${app.render_time_ago(batch.executed)}<br />
|
|
29
|
+
by ${batch.executed_by}
|
|
30
30
|
</p>
|
|
31
31
|
</b-notification>
|
|
32
32
|
% elif why_not_execute:
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
% if master.has_perm('execute'):
|
|
43
43
|
<b-notification type="is-success" :closable="false">
|
|
44
44
|
<p class="block">
|
|
45
|
-
Batch can be executed
|
|
45
|
+
Batch can be executed
|
|
46
46
|
</p>
|
|
47
47
|
<b-button type="is-primary"
|
|
48
48
|
@click="executeInit()"
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
:is-small="smallFilters"
|
|
15
15
|
ref="gridFilters" />
|
|
16
16
|
|
|
17
|
-
<div
|
|
17
|
+
<div style="display: flex; gap: 0.5rem;">
|
|
18
18
|
|
|
19
19
|
<b-button @click="copyDirectLink()"
|
|
20
20
|
title="Copy grid link to clipboard"
|
|
@@ -30,28 +30,29 @@
|
|
|
30
30
|
Apply Filters
|
|
31
31
|
</b-button>
|
|
32
32
|
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
33
|
+
<div>
|
|
34
|
+
<b-button v-if="!addFilterShow"
|
|
35
|
+
@click="addFilterInit()"
|
|
36
|
+
icon-pack="fas"
|
|
37
|
+
icon-left="plus"
|
|
38
|
+
:size="smallFilters ? 'is-small' : null">
|
|
39
|
+
Add Filter
|
|
40
|
+
</b-button>
|
|
41
|
+
<b-autocomplete v-if="addFilterShow"
|
|
42
|
+
ref="addFilterAutocomplete"
|
|
43
|
+
:data="addFilterChoices"
|
|
44
|
+
v-model="addFilterTerm"
|
|
45
|
+
placeholder="Add Filter"
|
|
46
|
+
field="key"
|
|
47
|
+
:custom-formatter="formatAddFilterItem"
|
|
48
|
+
open-on-focus
|
|
49
|
+
keep-first
|
|
50
|
+
clearable
|
|
51
|
+
clear-on-select
|
|
52
|
+
@select="addFilterSelect"
|
|
53
|
+
icon-pack="fas"
|
|
54
|
+
:size="smallFilters ? 'is-small' : null" />
|
|
55
|
+
</div>
|
|
55
56
|
|
|
56
57
|
<b-button @click="resetView()"
|
|
57
58
|
:disabled="viewResetting"
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
${self.make_wutta_timepicker_component()}
|
|
7
7
|
${self.make_wutta_filter_component()}
|
|
8
8
|
${self.make_wutta_filter_value_component()}
|
|
9
|
+
${self.make_wutta_filter_date_value_component()}
|
|
9
10
|
${self.make_wutta_tool_panel_component()}
|
|
10
11
|
</%def>
|
|
11
12
|
|
|
@@ -155,11 +156,14 @@
|
|
|
155
156
|
<%def name="make_wutta_datepicker_component()">
|
|
156
157
|
<script type="text/x-template" id="wutta-datepicker-template">
|
|
157
158
|
<b-datepicker :name="name"
|
|
159
|
+
ref="datepicker"
|
|
158
160
|
:editable="editable"
|
|
159
161
|
icon-pack="fas"
|
|
160
162
|
icon="calendar-alt"
|
|
161
163
|
:date-formatter="formatDate"
|
|
162
|
-
:value="buefyValue"
|
|
164
|
+
:value="buefyValue"
|
|
165
|
+
:size="size"
|
|
166
|
+
@input="val => $emit('input', val)" />
|
|
163
167
|
</script>
|
|
164
168
|
<script>
|
|
165
169
|
const WuttaDatepicker = {
|
|
@@ -167,6 +171,7 @@
|
|
|
167
171
|
props: {
|
|
168
172
|
name: String,
|
|
169
173
|
value: String,
|
|
174
|
+
size: String,
|
|
170
175
|
editable: {
|
|
171
176
|
type: Boolean,
|
|
172
177
|
default: true,
|
|
@@ -179,6 +184,10 @@
|
|
|
179
184
|
},
|
|
180
185
|
methods: {
|
|
181
186
|
|
|
187
|
+
focus() {
|
|
188
|
+
this.$refs.datepicker.focus()
|
|
189
|
+
},
|
|
190
|
+
|
|
182
191
|
formatDate(date) {
|
|
183
192
|
if (date === null) {
|
|
184
193
|
return null
|
|
@@ -324,7 +333,16 @@
|
|
|
324
333
|
icon-pack="fas"
|
|
325
334
|
:size="isSmall ? 'is-small' : null" />
|
|
326
335
|
|
|
327
|
-
|
|
336
|
+
## nb. only *ONE* of the following is used, per filter data type
|
|
337
|
+
|
|
338
|
+
<wutta-filter-date-value v-if="filter.data_type == 'date'"
|
|
339
|
+
v-model="filter.value"
|
|
340
|
+
ref="filterValue"
|
|
341
|
+
v-show="valuedVerb()"
|
|
342
|
+
:is-small="isSmall" />
|
|
343
|
+
|
|
344
|
+
<wutta-filter-value v-else
|
|
345
|
+
v-model="filter.value"
|
|
328
346
|
ref="filterValue"
|
|
329
347
|
v-show="valuedVerb()"
|
|
330
348
|
:is-small="isSmall" />
|
|
@@ -479,6 +497,53 @@
|
|
|
479
497
|
</script>
|
|
480
498
|
</%def>
|
|
481
499
|
|
|
500
|
+
<%def name="make_wutta_filter_date_value_component()">
|
|
501
|
+
<script type="text/x-template" id="wutta-filter-date-value-template">
|
|
502
|
+
<div class="wutta-filter-value">
|
|
503
|
+
|
|
504
|
+
<wutta-datepicker v-model="inputValue"
|
|
505
|
+
ref="valueInput"
|
|
506
|
+
:size="isSmall ? 'is-small' : null"
|
|
507
|
+
@input="valueChanged" >
|
|
508
|
+
</wutta-datepicker>
|
|
509
|
+
|
|
510
|
+
</div>
|
|
511
|
+
</script>
|
|
512
|
+
<script>
|
|
513
|
+
|
|
514
|
+
const WuttaFilterDateValue = {
|
|
515
|
+
template: '#wutta-filter-date-value-template',
|
|
516
|
+
props: {
|
|
517
|
+
value: String,
|
|
518
|
+
isSmall: Boolean,
|
|
519
|
+
},
|
|
520
|
+
|
|
521
|
+
data() {
|
|
522
|
+
return {
|
|
523
|
+
inputValue: this.value,
|
|
524
|
+
}
|
|
525
|
+
},
|
|
526
|
+
|
|
527
|
+
methods: {
|
|
528
|
+
|
|
529
|
+
focus() {
|
|
530
|
+
this.$refs.valueInput.focus()
|
|
531
|
+
},
|
|
532
|
+
|
|
533
|
+
valueChanged(value) {
|
|
534
|
+
if (value) {
|
|
535
|
+
value = this.$refs.valueInput.formatDate(value)
|
|
536
|
+
}
|
|
537
|
+
this.$emit('input', value)
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
Vue.component('wutta-filter-date-value', WuttaFilterDateValue)
|
|
543
|
+
|
|
544
|
+
</script>
|
|
545
|
+
</%def>
|
|
546
|
+
|
|
482
547
|
<%def name="make_wutta_tool_panel_component()">
|
|
483
548
|
<script type="text/x-template" id="wutta-tool-panel-template">
|
|
484
549
|
<nav class="panel tool-panel">
|
|
@@ -203,12 +203,15 @@ class BatchMasterView(MasterView):
|
|
|
203
203
|
f.set_node('executed_by', UserRef(self.request))
|
|
204
204
|
f.set_readonly('executed_by')
|
|
205
205
|
|
|
206
|
-
def objectify(self, form):
|
|
206
|
+
def objectify(self, form, **kwargs):
|
|
207
207
|
"""
|
|
208
208
|
We override the default logic here, to invoke
|
|
209
209
|
:meth:`~wuttjamaican:wuttjamaican.batch.BatchHandler.make_batch()`
|
|
210
210
|
on the batch handler - when creating. Parent/default logic is
|
|
211
211
|
used when updating.
|
|
212
|
+
|
|
213
|
+
:param \**kwargs: Additional kwargs will be passed as-is to
|
|
214
|
+
the ``make_batch()`` call.
|
|
212
215
|
"""
|
|
213
216
|
if self.creating:
|
|
214
217
|
|
|
@@ -219,15 +222,18 @@ class BatchMasterView(MasterView):
|
|
|
219
222
|
batch = schema.objectify(form.validated, context=form.model_instance)
|
|
220
223
|
|
|
221
224
|
# then we collect attributes from the new batch
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
+
kw = dict([(key, getattr(batch, key))
|
|
226
|
+
for key in form.validated
|
|
227
|
+
if hasattr(batch, key)])
|
|
225
228
|
|
|
226
229
|
# and set attribute for user creating the batch
|
|
227
|
-
|
|
230
|
+
kw['created_by'] = self.request.user
|
|
231
|
+
|
|
232
|
+
# plus caller can override anything
|
|
233
|
+
kw.update(kwargs)
|
|
228
234
|
|
|
229
235
|
# finally let batch handler make the "real" batch
|
|
230
|
-
return self.batch_handler.make_batch(self.Session(), **
|
|
236
|
+
return self.batch_handler.make_batch(self.Session(), **kw)
|
|
231
237
|
|
|
232
238
|
# when not creating, normal logic is fine
|
|
233
239
|
return super().objectify(form)
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
# -*- coding: utf-8; -*-
|
|
2
2
|
|
|
3
|
+
import datetime
|
|
3
4
|
from unittest import TestCase
|
|
4
5
|
from unittest.mock import patch
|
|
5
6
|
|
|
7
|
+
import sqlalchemy as sa
|
|
8
|
+
|
|
9
|
+
from wuttjamaican.db.model import Base
|
|
10
|
+
|
|
6
11
|
from wuttaweb.grids import filters as mod
|
|
7
12
|
from tests.util import WebTestCase
|
|
8
13
|
|
|
@@ -422,6 +427,157 @@ class TestBooleanAlchemyFilter(WebTestCase):
|
|
|
422
427
|
self.assertEqual(filtered_query.count(), 2)
|
|
423
428
|
|
|
424
429
|
|
|
430
|
+
class TheLocalThing(Base):
|
|
431
|
+
__tablename__ = 'the_local_thing'
|
|
432
|
+
id = sa.Column(sa.Integer(), primary_key=True, autoincrement=False)
|
|
433
|
+
date = sa.Column(sa.DateTime(timezone=True), nullable=True)
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
class TestDateAlchemyFilter(WebTestCase):
|
|
437
|
+
|
|
438
|
+
def setUp(self):
|
|
439
|
+
self.setup_web()
|
|
440
|
+
model = self.app.model
|
|
441
|
+
|
|
442
|
+
# nb. create table for TheLocalThing
|
|
443
|
+
model.Base.metadata.create_all(bind=self.session.bind)
|
|
444
|
+
|
|
445
|
+
self.sample_data = [
|
|
446
|
+
{'id': 1, 'date': datetime.date(2024, 1, 1)},
|
|
447
|
+
{'id': 2, 'date': datetime.date(2024, 1, 1)},
|
|
448
|
+
{'id': 3, 'date': datetime.date(2024, 3, 1)},
|
|
449
|
+
{'id': 4, 'date': datetime.date(2024, 3, 1)},
|
|
450
|
+
{'id': 5, 'date': None},
|
|
451
|
+
{'id': 6, 'date': None},
|
|
452
|
+
]
|
|
453
|
+
|
|
454
|
+
for thing in self.sample_data:
|
|
455
|
+
thing = TheLocalThing(**thing)
|
|
456
|
+
self.session.add(thing)
|
|
457
|
+
self.session.commit()
|
|
458
|
+
|
|
459
|
+
self.sample_query = self.session.query(TheLocalThing)
|
|
460
|
+
|
|
461
|
+
def make_filter(self, model_property, **kwargs):
|
|
462
|
+
factory = kwargs.pop('factory', mod.DateAlchemyFilter)
|
|
463
|
+
kwargs['model_property'] = model_property
|
|
464
|
+
return factory(self.request, model_property.key, **kwargs)
|
|
465
|
+
|
|
466
|
+
def test_coerce_value(self):
|
|
467
|
+
filtr = self.make_filter(TheLocalThing.date)
|
|
468
|
+
|
|
469
|
+
# null value
|
|
470
|
+
self.assertIsNone(filtr.coerce_value(None))
|
|
471
|
+
|
|
472
|
+
# value as datetime
|
|
473
|
+
value = datetime.date(2024, 1, 1)
|
|
474
|
+
result = filtr.coerce_value(value)
|
|
475
|
+
self.assertIs(value, result)
|
|
476
|
+
|
|
477
|
+
# value as string
|
|
478
|
+
result = filtr.coerce_value('2024-04-01')
|
|
479
|
+
self.assertIsInstance(result, datetime.date)
|
|
480
|
+
self.assertEqual(result, datetime.date(2024, 4, 1))
|
|
481
|
+
|
|
482
|
+
# invalid
|
|
483
|
+
result = filtr.coerce_value('thisinputisbad')
|
|
484
|
+
self.assertIsNone(result)
|
|
485
|
+
|
|
486
|
+
def test_greater_than(self):
|
|
487
|
+
model = self.app.model
|
|
488
|
+
|
|
489
|
+
filtr = self.make_filter(TheLocalThing.date)
|
|
490
|
+
self.assertEqual(self.sample_query.count(), 6)
|
|
491
|
+
|
|
492
|
+
# null value ignored
|
|
493
|
+
filtered_query = filtr.filter_greater_than(self.sample_query, None)
|
|
494
|
+
self.assertIs(filtered_query, self.sample_query)
|
|
495
|
+
self.assertEqual(filtered_query.count(), 6)
|
|
496
|
+
|
|
497
|
+
# value as date
|
|
498
|
+
filtered_query = filtr.filter_greater_than(self.sample_query, datetime.date(2024, 2, 1))
|
|
499
|
+
self.assertIsNot(filtered_query, self.sample_query)
|
|
500
|
+
self.assertEqual(filtered_query.count(), 2)
|
|
501
|
+
|
|
502
|
+
# value as string
|
|
503
|
+
filtered_query = filtr.filter_greater_than(self.sample_query, '2024-02-01')
|
|
504
|
+
self.assertIsNot(filtered_query, self.sample_query)
|
|
505
|
+
self.assertEqual(filtered_query.count(), 2)
|
|
506
|
+
|
|
507
|
+
def test_greater_equal(self):
|
|
508
|
+
model = self.app.model
|
|
509
|
+
|
|
510
|
+
filtr = self.make_filter(TheLocalThing.date)
|
|
511
|
+
self.assertEqual(self.sample_query.count(), 6)
|
|
512
|
+
|
|
513
|
+
# null value ignored
|
|
514
|
+
filtered_query = filtr.filter_greater_equal(self.sample_query, None)
|
|
515
|
+
self.assertIs(filtered_query, self.sample_query)
|
|
516
|
+
self.assertEqual(filtered_query.count(), 6)
|
|
517
|
+
|
|
518
|
+
# value as date (clear of boundary)
|
|
519
|
+
filtered_query = filtr.filter_greater_equal(self.sample_query, datetime.date(2024, 2, 1))
|
|
520
|
+
self.assertIsNot(filtered_query, self.sample_query)
|
|
521
|
+
self.assertEqual(filtered_query.count(), 2)
|
|
522
|
+
|
|
523
|
+
# value as date (at boundary)
|
|
524
|
+
filtered_query = filtr.filter_greater_equal(self.sample_query, datetime.date(2024, 3, 1))
|
|
525
|
+
self.assertIsNot(filtered_query, self.sample_query)
|
|
526
|
+
self.assertEqual(filtered_query.count(), 2)
|
|
527
|
+
|
|
528
|
+
# value as string
|
|
529
|
+
filtered_query = filtr.filter_greater_equal(self.sample_query, '2024-01-01')
|
|
530
|
+
self.assertIsNot(filtered_query, self.sample_query)
|
|
531
|
+
self.assertEqual(filtered_query.count(), 4)
|
|
532
|
+
|
|
533
|
+
def test_less_than(self):
|
|
534
|
+
model = self.app.model
|
|
535
|
+
|
|
536
|
+
filtr = self.make_filter(TheLocalThing.date)
|
|
537
|
+
self.assertEqual(self.sample_query.count(), 6)
|
|
538
|
+
|
|
539
|
+
# null value ignored
|
|
540
|
+
filtered_query = filtr.filter_less_than(self.sample_query, None)
|
|
541
|
+
self.assertIs(filtered_query, self.sample_query)
|
|
542
|
+
self.assertEqual(filtered_query.count(), 6)
|
|
543
|
+
|
|
544
|
+
# value as date
|
|
545
|
+
filtered_query = filtr.filter_less_than(self.sample_query, datetime.date(2024, 2, 1))
|
|
546
|
+
self.assertIsNot(filtered_query, self.sample_query)
|
|
547
|
+
self.assertEqual(filtered_query.count(), 2)
|
|
548
|
+
|
|
549
|
+
# value as string
|
|
550
|
+
filtered_query = filtr.filter_less_than(self.sample_query, '2024-04-01')
|
|
551
|
+
self.assertIsNot(filtered_query, self.sample_query)
|
|
552
|
+
self.assertEqual(filtered_query.count(), 4)
|
|
553
|
+
|
|
554
|
+
def test_less_equal(self):
|
|
555
|
+
model = self.app.model
|
|
556
|
+
|
|
557
|
+
filtr = self.make_filter(TheLocalThing.date)
|
|
558
|
+
self.assertEqual(self.sample_query.count(), 6)
|
|
559
|
+
|
|
560
|
+
# null value ignored
|
|
561
|
+
filtered_query = filtr.filter_less_equal(self.sample_query, None)
|
|
562
|
+
self.assertIs(filtered_query, self.sample_query)
|
|
563
|
+
self.assertEqual(filtered_query.count(), 6)
|
|
564
|
+
|
|
565
|
+
# value as date (clear of boundary)
|
|
566
|
+
filtered_query = filtr.filter_less_equal(self.sample_query, datetime.date(2024, 2, 1))
|
|
567
|
+
self.assertIsNot(filtered_query, self.sample_query)
|
|
568
|
+
self.assertEqual(filtered_query.count(), 2)
|
|
569
|
+
|
|
570
|
+
# value as date (at boundary)
|
|
571
|
+
filtered_query = filtr.filter_less_equal(self.sample_query, datetime.date(2024, 3, 1))
|
|
572
|
+
self.assertIsNot(filtered_query, self.sample_query)
|
|
573
|
+
self.assertEqual(filtered_query.count(), 2)
|
|
574
|
+
|
|
575
|
+
# value as string
|
|
576
|
+
filtered_query = filtr.filter_less_equal(self.sample_query, '2024-04-01')
|
|
577
|
+
self.assertIsNot(filtered_query, self.sample_query)
|
|
578
|
+
self.assertEqual(filtered_query.count(), 4)
|
|
579
|
+
|
|
580
|
+
|
|
425
581
|
class TestVerbNotSupported(TestCase):
|
|
426
582
|
|
|
427
583
|
def test_basic(self):
|
|
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
|