WuttaWeb 0.21.3__tar.gz → 0.21.5__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.21.3 → wuttaweb-0.21.5}/CHANGELOG.md +12 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/PKG-INFO +1 -1
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/pyproject.toml +1 -1
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/grids/base.py +69 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/grids/filters.py +85 -1
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/wutta-components.mako +11 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/grids/test_base.py +67 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/grids/test_filters.py +63 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/.gitignore +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/COPYING.txt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/README.md +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/Makefile +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/_static/.keepme +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.app.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.auth.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.cli.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.cli.webapp.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.conf.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.db.continuum.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.db.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.db.sess.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.emails.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.forms.base.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.forms.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.forms.schema.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.forms.widgets.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.grids.base.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.grids.filters.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.grids.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.handler.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.helpers.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.menus.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.progress.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.static.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.subscribers.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.util.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.views.auth.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.views.base.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.views.batch.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.views.common.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.views.email.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.views.essential.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.views.master.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.views.people.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.views.progress.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.views.reports.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.views.roles.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.views.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.views.settings.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.views.upgrades.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/api/wuttaweb.views.users.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/conf.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/glossary.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/index.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/make.bat +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/narr/cli/builtin.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/narr/cli/index.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/narr/templates/base.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/narr/templates/index.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/narr/templates/lookup.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/docs/narr/templates/overview.rst +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/__init__.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/_version.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/app.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/auth.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/cli/__init__.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/cli/webapp.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/conf.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/db/__init__.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/db/continuum.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/db/sess.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/email-templates/feedback.html.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/email-templates/feedback.txt.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/emails.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/forms/__init__.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/forms/base.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/forms/schema.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/forms/widgets.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/grids/__init__.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/handler.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/helpers.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/menus.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/progress.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/static/__init__.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/static/img/favicon.ico +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/static/img/logo.png +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/static/img/testing.png +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/subscribers.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/appinfo/index.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/auth/change_password.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/auth/login.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/base.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/base_meta.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/batch/view.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/configure.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/deform/dateinput.pt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/deform/datetimeinput.pt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/deform/moneyinput.pt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/deform/password.pt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/deform/permissions.pt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/deform/readonly/checkbox.pt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/deform/readonly/email_recips.pt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/deform/readonly/filedownload.pt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/deform/select.pt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/deform/textarea.pt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/deform/textinput.pt +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/email/settings/view.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/forbidden.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/form.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/grids/table_element.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/grids/vue_template.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/home.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/master/configure.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/master/create.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/master/delete.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/master/edit.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/master/form.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/master/index.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/master/view.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/notfound.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/page.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/people/view_profile.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/progress.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/reports/view.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/setup.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/upgrade.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/upgrades/configure.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/templates/upgrades/view.mako +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/testing.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/util.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/views/__init__.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/views/auth.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/views/base.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/views/batch.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/views/common.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/views/email.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/views/essential.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/views/master.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/views/people.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/views/progress.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/views/reports.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/views/roles.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/views/settings.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/views/upgrades.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/src/wuttaweb/views/users.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tasks.py +0 -0
- {wuttaweb-0.21.3/tests/views → wuttaweb-0.21.5/tests}/__init__.py +0 -0
- {wuttaweb-0.21.3/tests/grids → wuttaweb-0.21.5/tests/cli}/__init__.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/cli/test_webapp.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/db/__init__.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/db/test_continuum.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/forms/test_base.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/forms/test_schema.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/forms/test_widgets.py +0 -0
- {wuttaweb-0.21.3/tests/cli → wuttaweb-0.21.5/tests/grids}/__init__.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/libcache/bb_oruga.js +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/libcache/bb_oruga_bulma.css +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/libcache/bb_oruga_bulma.js +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/libcache/bb_vue.js +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/libcache/bb_vue_fontawesome.js +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/libcache/buefy.css +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/libcache/buefy.js +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/libcache/fontawesome.js +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/libcache/vue.js +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/libcache/vue_resource.js +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/test_app.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/test_auth.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/test_emails.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/test_handler.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/test_helpers.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/test_menus.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/test_progress.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/test_static.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/test_subscribers.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/test_util.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/util.py +0 -0
- {wuttaweb-0.21.3/tests → wuttaweb-0.21.5/tests/views}/__init__.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/views/test___init__.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/views/test_auth.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/views/test_base.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/views/test_batch.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/views/test_common.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/views/test_email.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/views/test_essential.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/views/test_master.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/views/test_people.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/views/test_progress.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/views/test_reports.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/views/test_roles.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/views/test_settings.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/views/test_upgrades.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tests/views/test_users.py +0 -0
- {wuttaweb-0.21.3 → wuttaweb-0.21.5}/tox.ini +0 -0
|
@@ -5,6 +5,18 @@ 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.21.5 (2025-02-21)
|
|
9
|
+
|
|
10
|
+
### Fix
|
|
11
|
+
|
|
12
|
+
- avoid newer `EnumType` for python <= 3.10
|
|
13
|
+
|
|
14
|
+
## v0.21.4 (2025-02-21)
|
|
15
|
+
|
|
16
|
+
### Fix
|
|
17
|
+
|
|
18
|
+
- add value choice/enum support for grid filters
|
|
19
|
+
|
|
8
20
|
## v0.21.3 (2025-02-19)
|
|
9
21
|
|
|
10
22
|
### Fix
|
|
@@ -119,6 +119,12 @@ class Grid:
|
|
|
119
119
|
See also :meth:`set_renderer()` and
|
|
120
120
|
:meth:`set_default_renderers()`.
|
|
121
121
|
|
|
122
|
+
.. attribute:: enums
|
|
123
|
+
|
|
124
|
+
Dict of "enum" collections, for supported columns.
|
|
125
|
+
|
|
126
|
+
See also :meth:`set_enum()`.
|
|
127
|
+
|
|
122
128
|
.. attribute:: checkable
|
|
123
129
|
|
|
124
130
|
Boolean indicating whether the grid should expose per-row
|
|
@@ -377,6 +383,7 @@ class Grid:
|
|
|
377
383
|
data=None,
|
|
378
384
|
labels={},
|
|
379
385
|
renderers={},
|
|
386
|
+
enums={},
|
|
380
387
|
checkable=False,
|
|
381
388
|
row_class=None,
|
|
382
389
|
actions=[],
|
|
@@ -458,6 +465,11 @@ class Grid:
|
|
|
458
465
|
self.filters = {}
|
|
459
466
|
self.set_filter_defaults(**(filter_defaults or {}))
|
|
460
467
|
|
|
468
|
+
# enums
|
|
469
|
+
self.enums = {}
|
|
470
|
+
for key in enums:
|
|
471
|
+
self.set_enum(key, enums[key])
|
|
472
|
+
|
|
461
473
|
def get_columns(self):
|
|
462
474
|
"""
|
|
463
475
|
Returns the official list of column names for the grid, or
|
|
@@ -720,6 +732,27 @@ class Grid:
|
|
|
720
732
|
elif isinstance(column.type, sa.Boolean):
|
|
721
733
|
self.set_renderer(key, self.render_boolean)
|
|
722
734
|
|
|
735
|
+
def set_enum(self, key, enum):
|
|
736
|
+
"""
|
|
737
|
+
Set the "enum" collection for a given column.
|
|
738
|
+
|
|
739
|
+
This will set the column renderer to show the appropriate enum
|
|
740
|
+
value for each row in the grid. See also
|
|
741
|
+
:meth:`render_enum()`.
|
|
742
|
+
|
|
743
|
+
If the grid has a corresponding filter for the column, it will
|
|
744
|
+
be modified to show "choices" for values contained in the
|
|
745
|
+
enum.
|
|
746
|
+
|
|
747
|
+
:param key: Name of column.
|
|
748
|
+
|
|
749
|
+
:param enum: Instance of :class:`python:enum.Enum`.
|
|
750
|
+
"""
|
|
751
|
+
self.enums[key] = enum
|
|
752
|
+
self.set_renderer(key, self.render_enum, enum=enum)
|
|
753
|
+
if key in self.filters:
|
|
754
|
+
self.filters[key].set_choices(enum)
|
|
755
|
+
|
|
723
756
|
def set_link(self, key, link=True):
|
|
724
757
|
"""
|
|
725
758
|
Explicitly enable or disable auto-link behavior for a given
|
|
@@ -1945,6 +1978,33 @@ class Grid:
|
|
|
1945
1978
|
dt = getattr(obj, key)
|
|
1946
1979
|
return self.app.render_datetime(dt)
|
|
1947
1980
|
|
|
1981
|
+
def render_enum(self, obj, key, value, enum=None):
|
|
1982
|
+
"""
|
|
1983
|
+
Custom grid value renderer for "enum" fields.
|
|
1984
|
+
|
|
1985
|
+
See also :meth:`set_enum()`.
|
|
1986
|
+
|
|
1987
|
+
:param enum: Enum class for the field. This should be an
|
|
1988
|
+
instance of :class:`~python:enum.Enum`.
|
|
1989
|
+
|
|
1990
|
+
To use this feature for your grid::
|
|
1991
|
+
|
|
1992
|
+
from enum import Enum
|
|
1993
|
+
|
|
1994
|
+
class MyEnum(Enum):
|
|
1995
|
+
ONE = 1
|
|
1996
|
+
TWO = 2
|
|
1997
|
+
THREE = 3
|
|
1998
|
+
|
|
1999
|
+
grid.set_enum('my_enum_field', MyEnum)
|
|
2000
|
+
"""
|
|
2001
|
+
if enum:
|
|
2002
|
+
raw_value = obj[key]
|
|
2003
|
+
if raw_value:
|
|
2004
|
+
return raw_value.value
|
|
2005
|
+
|
|
2006
|
+
return value
|
|
2007
|
+
|
|
1948
2008
|
def render_percent(self, obj, key, value, **kwargs):
|
|
1949
2009
|
"""
|
|
1950
2010
|
Column renderer for percentage values.
|
|
@@ -2176,6 +2236,13 @@ class Grid:
|
|
|
2176
2236
|
"""
|
|
2177
2237
|
filters = []
|
|
2178
2238
|
for filtr in self.filters.values():
|
|
2239
|
+
|
|
2240
|
+
choices = []
|
|
2241
|
+
choice_labels = {}
|
|
2242
|
+
if filtr.choices:
|
|
2243
|
+
choices = list(filtr.choices)
|
|
2244
|
+
choice_labels = dict(filtr.choices)
|
|
2245
|
+
|
|
2179
2246
|
filters.append({
|
|
2180
2247
|
'key': filtr.key,
|
|
2181
2248
|
'data_type': filtr.data_type,
|
|
@@ -2185,6 +2252,8 @@ class Grid:
|
|
|
2185
2252
|
'verb_labels': filtr.get_verb_labels(),
|
|
2186
2253
|
'valueless_verbs': filtr.get_valueless_verbs(),
|
|
2187
2254
|
'verb': filtr.verb,
|
|
2255
|
+
'choices': choices,
|
|
2256
|
+
'choice_labels': choice_labels,
|
|
2188
2257
|
'value': filtr.value,
|
|
2189
2258
|
'label': filtr.label,
|
|
2190
2259
|
})
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
################################################################################
|
|
3
3
|
#
|
|
4
4
|
# wuttaweb -- Web App for Wutta Framework
|
|
5
|
-
# Copyright © 2024 Lance Edgar
|
|
5
|
+
# Copyright © 2024-2025 Lance Edgar
|
|
6
6
|
#
|
|
7
7
|
# This file is part of Wutta Framework.
|
|
8
8
|
#
|
|
@@ -26,6 +26,12 @@ Grid Filters
|
|
|
26
26
|
|
|
27
27
|
import datetime
|
|
28
28
|
import logging
|
|
29
|
+
from collections import OrderedDict
|
|
30
|
+
try:
|
|
31
|
+
from enum import EnumType
|
|
32
|
+
except ImportError: # pragma: no cover
|
|
33
|
+
# nb. python <= 3.10
|
|
34
|
+
from enum import EnumMeta as EnumType
|
|
29
35
|
|
|
30
36
|
import sqlalchemy as sa
|
|
31
37
|
|
|
@@ -77,6 +83,7 @@ class GridFilter:
|
|
|
77
83
|
|
|
78
84
|
* ``'string'``
|
|
79
85
|
* ``'date'``
|
|
86
|
+
* ``'choice'``
|
|
80
87
|
|
|
81
88
|
Note that this mainly applies to the "value input" used by the
|
|
82
89
|
filter. There is no data type for boolean since it does not
|
|
@@ -94,6 +101,13 @@ class GridFilter:
|
|
|
94
101
|
|
|
95
102
|
See also :attr:`value`.
|
|
96
103
|
|
|
104
|
+
.. attribute:: choices
|
|
105
|
+
|
|
106
|
+
OrderedDict of possible values for the filter.
|
|
107
|
+
|
|
108
|
+
This is safe to read from, but use :meth:`set_choices()` to
|
|
109
|
+
update it.
|
|
110
|
+
|
|
97
111
|
.. attribute:: value
|
|
98
112
|
|
|
99
113
|
Value for current filter, if :attr:`active` is true.
|
|
@@ -159,6 +173,7 @@ class GridFilter:
|
|
|
159
173
|
key,
|
|
160
174
|
label=None,
|
|
161
175
|
verbs=None,
|
|
176
|
+
choices={},
|
|
162
177
|
default_active=False,
|
|
163
178
|
default_verb=None,
|
|
164
179
|
default_value=None,
|
|
@@ -180,6 +195,9 @@ class GridFilter:
|
|
|
180
195
|
if default_verb:
|
|
181
196
|
self.default_verb = default_verb
|
|
182
197
|
|
|
198
|
+
# choices
|
|
199
|
+
self.set_choices(choices)
|
|
200
|
+
|
|
183
201
|
# value
|
|
184
202
|
self.default_value = default_value
|
|
185
203
|
self.value = self.default_value
|
|
@@ -255,6 +273,72 @@ class GridFilter:
|
|
|
255
273
|
|
|
256
274
|
return verb
|
|
257
275
|
|
|
276
|
+
def set_choices(self, choices):
|
|
277
|
+
"""
|
|
278
|
+
Set the value choices for the filter.
|
|
279
|
+
|
|
280
|
+
If ``choices`` is non-empty, it is passed to
|
|
281
|
+
:meth:`normalize_choices()` and the result is assigned to
|
|
282
|
+
:attr:`choices`. Also, the :attr:`data_type` is set to
|
|
283
|
+
``'choice'`` so the UI will present the value input as a
|
|
284
|
+
dropdown.
|
|
285
|
+
|
|
286
|
+
But if ``choices`` is empty, :attr:`choices` is set to an
|
|
287
|
+
empty dict, and :attr:`data_type` is set (back) to
|
|
288
|
+
``'string'``.
|
|
289
|
+
|
|
290
|
+
:param choices: Collection of "choices" or ``None``.
|
|
291
|
+
"""
|
|
292
|
+
if choices:
|
|
293
|
+
self.choices = self.normalize_choices(choices)
|
|
294
|
+
self.data_type = 'choice'
|
|
295
|
+
else:
|
|
296
|
+
self.choices = {}
|
|
297
|
+
self.data_type = 'string'
|
|
298
|
+
|
|
299
|
+
def normalize_choices(self, choices):
|
|
300
|
+
"""
|
|
301
|
+
Normalize a collection of "choices" to standard ``OrderedDict``.
|
|
302
|
+
|
|
303
|
+
This is called automatically by :meth:`set_choices()`.
|
|
304
|
+
|
|
305
|
+
:param choices: A collection of "choices" in one of the following
|
|
306
|
+
formats:
|
|
307
|
+
|
|
308
|
+
* :class:`python:enum.Enum` class
|
|
309
|
+
* simple list, each value of which should be a string,
|
|
310
|
+
which is assumed to be able to serve as both key and
|
|
311
|
+
value (ordering of choices will be preserved)
|
|
312
|
+
* simple dict, keys and values of which will define the
|
|
313
|
+
choices (note that the final choices will be sorted by
|
|
314
|
+
key!)
|
|
315
|
+
* OrderedDict, keys and values of which will define the
|
|
316
|
+
choices (ordering of choices will be preserved)
|
|
317
|
+
|
|
318
|
+
:rtype: :class:`python:collections.OrderedDict`
|
|
319
|
+
"""
|
|
320
|
+
normalized = choices
|
|
321
|
+
|
|
322
|
+
if isinstance(choices, EnumType):
|
|
323
|
+
normalized = OrderedDict([
|
|
324
|
+
(member.name, member.value)
|
|
325
|
+
for member in choices])
|
|
326
|
+
|
|
327
|
+
elif isinstance(choices, OrderedDict):
|
|
328
|
+
normalized = choices
|
|
329
|
+
|
|
330
|
+
elif isinstance(choices, dict):
|
|
331
|
+
normalized = OrderedDict([
|
|
332
|
+
(key, choices[key])
|
|
333
|
+
for key in sorted(choices)])
|
|
334
|
+
|
|
335
|
+
elif isinstance(choices, list):
|
|
336
|
+
normalized = OrderedDict([
|
|
337
|
+
(key, key)
|
|
338
|
+
for key in choices])
|
|
339
|
+
|
|
340
|
+
return normalized
|
|
341
|
+
|
|
258
342
|
def apply_filter(self, data, verb=None, value=UNSPECIFIED):
|
|
259
343
|
"""
|
|
260
344
|
Filter the given data set according to a verb/value pair.
|
|
@@ -486,6 +486,17 @@
|
|
|
486
486
|
v-show="valuedVerb()"
|
|
487
487
|
:is-small="isSmall" />
|
|
488
488
|
|
|
489
|
+
<b-select v-if="filter.data_type == 'choice'"
|
|
490
|
+
v-model="filter.value"
|
|
491
|
+
ref="filterValue"
|
|
492
|
+
v-show="valuedVerb()">
|
|
493
|
+
<option v-for="choice in filter.choices"
|
|
494
|
+
:key="choice"
|
|
495
|
+
:value="choice">
|
|
496
|
+
{{ filter.choice_labels[choice] || choice }}
|
|
497
|
+
</option>
|
|
498
|
+
</b-select>
|
|
499
|
+
|
|
489
500
|
<wutta-filter-value v-else
|
|
490
501
|
v-model="filter.value"
|
|
491
502
|
ref="filterValue"
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import datetime
|
|
4
4
|
import decimal
|
|
5
|
+
from collections import OrderedDict
|
|
6
|
+
from enum import Enum
|
|
5
7
|
from unittest import TestCase
|
|
6
8
|
from unittest.mock import patch, MagicMock
|
|
7
9
|
|
|
@@ -261,6 +263,36 @@ class TestGrid(WebTestCase):
|
|
|
261
263
|
self.assertIn('created', grid.renderers)
|
|
262
264
|
self.assertIsNot(grid.renderers['created'], myrender)
|
|
263
265
|
|
|
266
|
+
def test_set_enum(self):
|
|
267
|
+
model = self.app.model
|
|
268
|
+
|
|
269
|
+
class MockEnum(Enum):
|
|
270
|
+
FOO = 'foo'
|
|
271
|
+
BAR = 'bar'
|
|
272
|
+
|
|
273
|
+
# no enums by default
|
|
274
|
+
grid = self.make_grid(columns=['foo', 'bar'])
|
|
275
|
+
self.assertEqual(grid.enums, {})
|
|
276
|
+
|
|
277
|
+
# enum is set, but not filter choices
|
|
278
|
+
grid = self.make_grid(columns=['foo', 'bar'],
|
|
279
|
+
filterable=False,
|
|
280
|
+
enums={'foo': MockEnum})
|
|
281
|
+
self.assertIs(grid.enums['foo'], MockEnum)
|
|
282
|
+
self.assertEqual(grid.filters, {})
|
|
283
|
+
|
|
284
|
+
# both enum and filter choices are set
|
|
285
|
+
grid = self.make_grid(model_class=model.Setting,
|
|
286
|
+
filterable=True,
|
|
287
|
+
enums={'name': MockEnum})
|
|
288
|
+
self.assertIs(grid.enums['name'], MockEnum)
|
|
289
|
+
self.assertIn('name', grid.filters)
|
|
290
|
+
self.assertIn('value', grid.filters)
|
|
291
|
+
self.assertEqual(grid.filters['name'].choices, OrderedDict([
|
|
292
|
+
('FOO', 'foo'),
|
|
293
|
+
('BAR', 'bar'),
|
|
294
|
+
]))
|
|
295
|
+
|
|
264
296
|
def test_linked_columns(self):
|
|
265
297
|
grid = self.make_grid(columns=['foo', 'bar'])
|
|
266
298
|
self.assertEqual(grid.linked_columns, [])
|
|
@@ -1432,6 +1464,20 @@ class TestGrid(WebTestCase):
|
|
|
1432
1464
|
value = decimal.Decimal('-42.42')
|
|
1433
1465
|
self.assertEqual(grid.render_currency(obj, 'foo', value), '($42.42)')
|
|
1434
1466
|
|
|
1467
|
+
def test_render_enum(self):
|
|
1468
|
+
enum = self.app.enum
|
|
1469
|
+
grid = self.make_grid(columns=['foo', 'bar'])
|
|
1470
|
+
obj = {'status': None}
|
|
1471
|
+
|
|
1472
|
+
# null
|
|
1473
|
+
value = grid.render_enum(obj, 'status', None, enum=enum.UpgradeStatus)
|
|
1474
|
+
self.assertIsNone(value)
|
|
1475
|
+
|
|
1476
|
+
# normal
|
|
1477
|
+
obj['status'] = enum.UpgradeStatus.SUCCESS
|
|
1478
|
+
value = grid.render_enum(obj, 'status', 'SUCCESS', enum=enum.UpgradeStatus)
|
|
1479
|
+
self.assertEqual(value, 'success')
|
|
1480
|
+
|
|
1435
1481
|
def test_render_percent(self):
|
|
1436
1482
|
grid = self.make_grid(columns=['foo', 'bar'])
|
|
1437
1483
|
obj = MagicMock()
|
|
@@ -1572,6 +1618,27 @@ class TestGrid(WebTestCase):
|
|
|
1572
1618
|
grid.load_settings()
|
|
1573
1619
|
filters = grid.get_vue_filters()
|
|
1574
1620
|
self.assertEqual(len(filters), 2)
|
|
1621
|
+
name, value = filters
|
|
1622
|
+
self.assertEqual(name['choices'], [])
|
|
1623
|
+
self.assertEqual(name['choice_labels'], {})
|
|
1624
|
+
self.assertEqual(value['choices'], [])
|
|
1625
|
+
self.assertEqual(value['choice_labels'], {})
|
|
1626
|
+
|
|
1627
|
+
class MockEnum(Enum):
|
|
1628
|
+
FOO = 'foo'
|
|
1629
|
+
BAR = 'bar'
|
|
1630
|
+
|
|
1631
|
+
# with filter choices
|
|
1632
|
+
grid = self.make_grid(key='settings', model_class=model.Setting,
|
|
1633
|
+
filterable=True, enums={'name': MockEnum})
|
|
1634
|
+
grid.load_settings()
|
|
1635
|
+
filters = grid.get_vue_filters()
|
|
1636
|
+
self.assertEqual(len(filters), 2)
|
|
1637
|
+
name, value = filters
|
|
1638
|
+
self.assertEqual(name['choices'], ['FOO', 'BAR'])
|
|
1639
|
+
self.assertEqual(name['choice_labels'], {'FOO': 'foo', 'BAR': 'bar'})
|
|
1640
|
+
self.assertEqual(value['choices'], [])
|
|
1641
|
+
self.assertEqual(value['choice_labels'], {})
|
|
1575
1642
|
|
|
1576
1643
|
def test_object_to_dict(self):
|
|
1577
1644
|
grid = self.make_grid()
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# -*- coding: utf-8; -*-
|
|
2
2
|
|
|
3
3
|
import datetime
|
|
4
|
+
from collections import OrderedDict
|
|
5
|
+
from enum import Enum
|
|
4
6
|
from unittest import TestCase
|
|
5
7
|
from unittest.mock import patch
|
|
6
8
|
|
|
@@ -136,6 +138,67 @@ class TestGridFilter(WebTestCase):
|
|
|
136
138
|
self.assertIsInstance(verbs, list)
|
|
137
139
|
self.assertIn('is_any', verbs)
|
|
138
140
|
|
|
141
|
+
def test_set_choices(self):
|
|
142
|
+
model = self.app.model
|
|
143
|
+
|
|
144
|
+
filtr = self.make_filter(model.Setting.name, factory=mod.AlchemyFilter)
|
|
145
|
+
self.assertEqual(filtr.choices, {})
|
|
146
|
+
self.assertEqual(filtr.data_type, 'string')
|
|
147
|
+
|
|
148
|
+
class MockEnum(Enum):
|
|
149
|
+
FOO = 'foo'
|
|
150
|
+
BAR = 'bar'
|
|
151
|
+
|
|
152
|
+
filtr.set_choices(MockEnum)
|
|
153
|
+
self.assertEqual(filtr.choices, OrderedDict([
|
|
154
|
+
('FOO', 'foo'),
|
|
155
|
+
('BAR', 'bar'),
|
|
156
|
+
]))
|
|
157
|
+
self.assertEqual(filtr.data_type, 'choice')
|
|
158
|
+
|
|
159
|
+
filtr.set_choices(None)
|
|
160
|
+
self.assertEqual(filtr.choices, {})
|
|
161
|
+
self.assertEqual(filtr.data_type, 'string')
|
|
162
|
+
|
|
163
|
+
def test_normalize_choices(self):
|
|
164
|
+
model = self.app.model
|
|
165
|
+
filtr = self.make_filter(model.Setting.name, factory=mod.AlchemyFilter)
|
|
166
|
+
|
|
167
|
+
class MockEnum(Enum):
|
|
168
|
+
FOO = 'foo'
|
|
169
|
+
BAR = 'bar'
|
|
170
|
+
|
|
171
|
+
choices = filtr.normalize_choices(MockEnum)
|
|
172
|
+
self.assertEqual(choices, OrderedDict([
|
|
173
|
+
('FOO', 'foo'),
|
|
174
|
+
('BAR', 'bar'),
|
|
175
|
+
]))
|
|
176
|
+
|
|
177
|
+
choices = filtr.normalize_choices(OrderedDict([
|
|
178
|
+
('first', '1'),
|
|
179
|
+
('second', '2'),
|
|
180
|
+
]))
|
|
181
|
+
self.assertEqual(choices, OrderedDict([
|
|
182
|
+
('first', '1'),
|
|
183
|
+
('second', '2'),
|
|
184
|
+
]))
|
|
185
|
+
|
|
186
|
+
choices = filtr.normalize_choices({
|
|
187
|
+
'bbb': 'b',
|
|
188
|
+
'aaa': 'a',
|
|
189
|
+
})
|
|
190
|
+
self.assertEqual(choices, OrderedDict([
|
|
191
|
+
('aaa', 'a'),
|
|
192
|
+
('bbb', 'b'),
|
|
193
|
+
]))
|
|
194
|
+
|
|
195
|
+
choices = filtr.normalize_choices(['one', 'two', 'three'])
|
|
196
|
+
self.assertEqual(choices, OrderedDict([
|
|
197
|
+
('one', 'one'),
|
|
198
|
+
('two', 'two'),
|
|
199
|
+
('three', 'three'),
|
|
200
|
+
]))
|
|
201
|
+
|
|
139
202
|
def test_apply_filter(self):
|
|
140
203
|
model = self.app.model
|
|
141
204
|
filtr = self.make_filter(model.Setting.value, factory=mod.StringAlchemyFilter)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|