accrete 0.0.36__tar.gz → 0.0.37__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.
- {accrete-0.0.36 → accrete-0.0.37}/PKG-INFO +1 -1
- accrete-0.0.37/accrete/annotation.py +46 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/__init__.py +5 -12
- accrete-0.0.37/accrete/contrib/ui/context.py +218 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/filter.py +105 -43
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/css/accrete.css +128 -128
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/css/accrete.css.map +1 -1
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/css/accrete.scss +9 -9
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/js/filter.js +23 -0
- accrete-0.0.37/accrete/contrib/ui/static/js/htmx.min.js +1 -0
- accrete-0.0.37/accrete/contrib/ui/templates/ui/layout.html +178 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/list.html +2 -2
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/filter.html +28 -4
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/header.html +5 -5
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/table.html +1 -1
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templatetags/accrete_ui.py +12 -1
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/templates/user/login.html +6 -12
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/views.py +10 -9
- {accrete-0.0.36 → accrete-0.0.37}/accrete/middleware.py +15 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/models.py +9 -7
- {accrete-0.0.36 → accrete-0.0.37}/accrete/querystring.py +3 -9
- accrete-0.0.37/accrete/utils/models.py +14 -0
- {accrete-0.0.36 → accrete-0.0.37}/pyproject.toml +1 -1
- accrete-0.0.36/accrete/contrib/ui/context.py +0 -345
- accrete-0.0.36/accrete/contrib/ui/querystring.py +0 -19
- accrete-0.0.36/accrete/contrib/ui/templates/ui/layout.html +0 -176
- {accrete-0.0.36 → accrete-0.0.37}/.gitignore +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/LICENSE +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/README.md +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/__init__.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/admin.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/apps.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/config.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/__init__.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/__init__.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/admin.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/apps.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/forms.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/migrations/0001_initial.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/migrations/0002_alter_sequence_name.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/migrations/__init__.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/models.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/queries.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/tests.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/views.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/__init__.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/admin.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/apps.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/forms.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/migrations/0001_initial.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/migrations/__init__.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/models.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/tasks.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/tests.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/views.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/admin.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/apps.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/elements.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/migrations/__init__.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/LICENSE +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/README.md +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/bulma.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/css/bulma-rtl.css +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/css/bulma-rtl.css.map +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/css/bulma-rtl.min.css +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/css/bulma.css +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/css/bulma.css.map +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/css/bulma.min.css +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/package.json +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/base/_all.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/base/animations.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/base/generic.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/base/helpers.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/base/minireset.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/_all.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/breadcrumb.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/card.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/dropdown.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/level.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/media.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/menu.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/message.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/modal.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/navbar.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/pagination.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/panel.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/tabs.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/_all.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/box.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/button.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/container.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/content.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/form.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/icon.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/image.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/notification.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/other.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/progress.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/table.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/tag.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/title.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/_all.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/checkbox-radio.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/file.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/input-textarea.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/select.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/shared.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/tools.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/grid/_all.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/grid/columns.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/grid/tiles.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/_all.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/color.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/flexbox.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/float.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/other.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/overflow.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/position.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/spacing.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/typography.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/visibility.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/layout/_all.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/layout/footer.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/layout/hero.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/layout/section.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/_all.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/animations.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/controls.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/derived-variables.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/extends.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/functions.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/initial-variables.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/mixins.sass +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/css/accrete.css.bak +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/css/icons.css +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/icons/Logo.svg +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/icons/accrete.svg +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/js/list.js +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/attrs.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/email.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/file.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/input.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/select.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/text.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/textarea.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/detail.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/form.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/form_errors.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/form_modal.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/onchange_form.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/pagination_detail.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/pagination_list.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/table_field.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/table_field_float.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/table_field_monetary.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/table_field_string.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templatetags/__init__.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/tests.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/urls.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/views.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/__init__.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/admin.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/apps.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/forms.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/locale/de/LC_MESSAGES/django.mo +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/locale/de/LC_MESSAGES/django.po +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/middleware.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/migrations/0001_initial.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/migrations/__init__.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/models.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/templates/user/accrete_navbar_end_dropdown.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/templates/user/change_email.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/templates/user/change_password.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/templates/user/user_detail.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/templates/user/user_form.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/tests.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/urls.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/__init__.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/admin.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/apps.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/config.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/forms.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/migrations/0001_initial.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/migrations/__init__.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/models.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/templates/user_registration/mail_templates/confirmation_mail.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/templates/user_registration/registration.html +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/tests.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/urls.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/views.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/forms.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/migrations/0001_initial.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/migrations/0002_initial.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/migrations/__init__.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/queries.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/tenant.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/tests.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/utils/__init__.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/utils/dates.py +0 -0
- {accrete-0.0.36 → accrete-0.0.37}/accrete/views.py +0 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
from django.db.models import Field, QuerySet
|
2
|
+
from django.db.models.expressions import Func
|
3
|
+
from django.db.models.aggregates import Aggregate
|
4
|
+
|
5
|
+
|
6
|
+
class Annotation:
|
7
|
+
|
8
|
+
def __init__(
|
9
|
+
self,
|
10
|
+
verbose_name: str,
|
11
|
+
field: type[Field],
|
12
|
+
function: type[Func] | type[Aggregate],
|
13
|
+
help_text: str = '',
|
14
|
+
**kwargs
|
15
|
+
):
|
16
|
+
self.verbose_name = verbose_name or self
|
17
|
+
self.field = field
|
18
|
+
self.function = function
|
19
|
+
self.help_text = help_text
|
20
|
+
self.__dict__.update(kwargs)
|
21
|
+
|
22
|
+
|
23
|
+
class AnnotationModelMixin:
|
24
|
+
|
25
|
+
@classmethod
|
26
|
+
def get_annotations(cls) -> list[dict]:
|
27
|
+
return list({'name': a, 'annotation': getattr(cls, a)} for a in filter(
|
28
|
+
lambda a:
|
29
|
+
not a.startswith('__')
|
30
|
+
and isinstance(getattr(cls, a), Annotation),
|
31
|
+
cls.__dict__
|
32
|
+
))
|
33
|
+
|
34
|
+
|
35
|
+
class AnnotationManagerMixin:
|
36
|
+
|
37
|
+
@staticmethod
|
38
|
+
def add_annotations(queryset: QuerySet):
|
39
|
+
model = queryset.model
|
40
|
+
if not hasattr(model, 'get_annotations'):
|
41
|
+
return queryset
|
42
|
+
return queryset.annotate(**{
|
43
|
+
annotation['name']: annotation['annotation'].function
|
44
|
+
for annotation in model.get_annotations()
|
45
|
+
})
|
46
|
+
|
@@ -4,18 +4,15 @@ from .context import (
|
|
4
4
|
TableContext,
|
5
5
|
ListContext,
|
6
6
|
FormContext,
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
DetailContent,
|
11
|
-
FormContent,
|
12
|
-
get_list_page,
|
13
|
-
get_detail_page,
|
7
|
+
form_actions,
|
8
|
+
list_page,
|
9
|
+
detail_page,
|
14
10
|
cast_param,
|
15
11
|
prepare_url_params,
|
16
12
|
extract_url_params,
|
17
13
|
exclude_params,
|
18
|
-
url_param_str
|
14
|
+
url_param_str,
|
15
|
+
get_table_fields
|
19
16
|
)
|
20
17
|
from .elements import (
|
21
18
|
ClientAction,
|
@@ -26,7 +23,3 @@ from .elements import (
|
|
26
23
|
TableFieldType,
|
27
24
|
Icon
|
28
25
|
)
|
29
|
-
from .querystring import (
|
30
|
-
load_querystring,
|
31
|
-
build_querystring,
|
32
|
-
)
|
@@ -0,0 +1,218 @@
|
|
1
|
+
import logging
|
2
|
+
from dataclasses import dataclass, field
|
3
|
+
from typing import TypedDict
|
4
|
+
from django.utils.translation import gettext_lazy as _t
|
5
|
+
from django.db.models import Model, QuerySet
|
6
|
+
from django.core.paginator import Paginator
|
7
|
+
from django.core import paginator
|
8
|
+
from django.forms import Form, ModelForm
|
9
|
+
from accrete.utils.models import get_related_model
|
10
|
+
from .elements import ClientAction, BreadCrumb, TableField, TableFieldType
|
11
|
+
from .filter import Filter
|
12
|
+
from ...annotation import Annotation
|
13
|
+
|
14
|
+
_logger = logging.getLogger(__name__)
|
15
|
+
|
16
|
+
DEFAULT_PAGINATE_BY = 40
|
17
|
+
|
18
|
+
|
19
|
+
class DetailPagination(TypedDict):
|
20
|
+
previous_object_url: str
|
21
|
+
next_object_url: str
|
22
|
+
current_object_idx: int
|
23
|
+
total_objects: int
|
24
|
+
|
25
|
+
|
26
|
+
@dataclass(kw_only=True)
|
27
|
+
class Context:
|
28
|
+
|
29
|
+
title: str = ''
|
30
|
+
breadcrumbs: list[BreadCrumb] = field(default_factory=list)
|
31
|
+
actions: list[ClientAction] = field(default_factory=list)
|
32
|
+
kwargs: dict = field(default_factory=dict)
|
33
|
+
|
34
|
+
def __post_init__(self):
|
35
|
+
for key, value in self.kwargs.items():
|
36
|
+
setattr(self, key, value)
|
37
|
+
|
38
|
+
def dict(self):
|
39
|
+
return {
|
40
|
+
attr: getattr(self, attr, '') for attr
|
41
|
+
in filter(lambda x: not x.startswith('_'), self.__dict__)
|
42
|
+
}
|
43
|
+
|
44
|
+
|
45
|
+
@dataclass
|
46
|
+
class TableContext(Context):
|
47
|
+
|
48
|
+
object_label: str
|
49
|
+
fields: list[TableField]
|
50
|
+
list_page: paginator.Page
|
51
|
+
pagination_param_str: str
|
52
|
+
endless_scroll: bool
|
53
|
+
filter: Filter
|
54
|
+
object_param_str: str = field(default='', kw_only=True)
|
55
|
+
|
56
|
+
|
57
|
+
@dataclass
|
58
|
+
class ListContext(Context):
|
59
|
+
|
60
|
+
object_label: str
|
61
|
+
object_param_str: str
|
62
|
+
list_page: paginator.Page
|
63
|
+
pagination_param_str: str
|
64
|
+
filter: Filter
|
65
|
+
endless_scroll: bool = True
|
66
|
+
column_height: int = 4
|
67
|
+
column_width: int = 12
|
68
|
+
|
69
|
+
def __post_init__(self):
|
70
|
+
super().__post_init__()
|
71
|
+
if self.column_width not in range(1, 13):
|
72
|
+
_logger.warning(
|
73
|
+
'ListContext parameter column_width should be in range 1 - 12'
|
74
|
+
)
|
75
|
+
|
76
|
+
|
77
|
+
class DetailContext(TypedDict, total=False):
|
78
|
+
|
79
|
+
title: str
|
80
|
+
object: Model
|
81
|
+
breadcrumbs: list[BreadCrumb]
|
82
|
+
detail_page: DetailPagination
|
83
|
+
pagination_param_str: str
|
84
|
+
actions: list[ClientAction]
|
85
|
+
|
86
|
+
|
87
|
+
class FormContext(TypedDict, total=False):
|
88
|
+
|
89
|
+
title: str
|
90
|
+
breadcrumbs: list[BreadCrumb]
|
91
|
+
form: Form | ModelForm
|
92
|
+
form_id: str
|
93
|
+
actions: list[ClientAction]
|
94
|
+
|
95
|
+
|
96
|
+
def cast_param(params: dict, param: str, cast_to: callable, default):
|
97
|
+
if param not in params:
|
98
|
+
return default
|
99
|
+
try:
|
100
|
+
return cast_to(params.get(param, default))
|
101
|
+
except Exception as e:
|
102
|
+
_logger.exception(e)
|
103
|
+
return default
|
104
|
+
|
105
|
+
|
106
|
+
def prepare_url_params(get_params: dict) -> dict:
|
107
|
+
return {key: f'&{key}={value}' for key, value in get_params.items()}
|
108
|
+
|
109
|
+
|
110
|
+
def url_param_str(params: dict, extract: list[str] = None) -> str:
|
111
|
+
"""
|
112
|
+
Return a URL Querystring from the given parameters
|
113
|
+
If extract is supplied, extract the value from the dictionary and prepare
|
114
|
+
them, so that each value is formatted e.g. {'page': '&page=1'}
|
115
|
+
"""
|
116
|
+
if extract:
|
117
|
+
params = prepare_url_params(extract_url_params(params, extract))
|
118
|
+
param_str = (
|
119
|
+
"".join(str(value) for value in params.values())
|
120
|
+
.replace('&&', '&')
|
121
|
+
.replace('?&', '?')
|
122
|
+
.strip('?&')
|
123
|
+
)
|
124
|
+
return f'?{param_str}'
|
125
|
+
|
126
|
+
|
127
|
+
def extract_url_params(params: dict, keys: list[str]) -> dict:
|
128
|
+
return {key: params[key] for key in keys if key in params}
|
129
|
+
|
130
|
+
|
131
|
+
def exclude_params(params: dict, keys: list[str]) -> dict:
|
132
|
+
return {key: val for key, val in params.items() if key not in keys}
|
133
|
+
|
134
|
+
|
135
|
+
def list_page(queryset: QuerySet, paginate_by: int, page_number: int) -> paginator.Page:
|
136
|
+
pages = Paginator(queryset, per_page=paginate_by)
|
137
|
+
return pages.page(page_number <= pages.num_pages and page_number or pages.num_pages)
|
138
|
+
|
139
|
+
|
140
|
+
def detail_page(queryset: QuerySet, obj: Model) -> dict:
|
141
|
+
if not hasattr(obj, 'get_absolute_url'):
|
142
|
+
_logger.warning(
|
143
|
+
'Detail pagination disabled for models without the '
|
144
|
+
'get_absolute_url attribute. Set paginate_by to 0 to '
|
145
|
+
'deactivate pagination.'
|
146
|
+
)
|
147
|
+
return {}
|
148
|
+
idx = (*queryset,).index(obj)
|
149
|
+
previous_object_url = (
|
150
|
+
queryset[idx - 1] if idx - 1 >= 0 else queryset.last()
|
151
|
+
).get_absolute_url()
|
152
|
+
next_object_url = (
|
153
|
+
queryset[idx + 1] if idx + 1 <= queryset.count() - 1 else queryset.first()
|
154
|
+
).get_absolute_url()
|
155
|
+
return {
|
156
|
+
'previous_object_url': previous_object_url,
|
157
|
+
'next_object_url': next_object_url,
|
158
|
+
'current_object_idx': idx + 1,
|
159
|
+
'total_objects': queryset.count()
|
160
|
+
}
|
161
|
+
|
162
|
+
|
163
|
+
def form_actions(discard_url: str, form_id: str = 'form') -> list[ClientAction]:
|
164
|
+
return [
|
165
|
+
ClientAction(
|
166
|
+
name=_t('Save'),
|
167
|
+
submit=True,
|
168
|
+
class_list=['is-success'],
|
169
|
+
form_id=form_id,
|
170
|
+
),
|
171
|
+
ClientAction(
|
172
|
+
name=_t('Discard'),
|
173
|
+
url=discard_url
|
174
|
+
)
|
175
|
+
]
|
176
|
+
|
177
|
+
|
178
|
+
def get_table_fields(
|
179
|
+
fields: list[str],
|
180
|
+
model: type[Model],
|
181
|
+
field_definition: dict[str | TableField] = None
|
182
|
+
) -> list[TableField]:
|
183
|
+
print(fields, model, field_definition)
|
184
|
+
|
185
|
+
if field_definition is None:
|
186
|
+
field_definition = {}
|
187
|
+
|
188
|
+
def get_field_definition(f_name: str) -> TableField:
|
189
|
+
if definition := field_definition.get(f_name):
|
190
|
+
return definition
|
191
|
+
if len(f_name.split('__')) == 1:
|
192
|
+
rel_model = model
|
193
|
+
else:
|
194
|
+
rel_model = get_related_model(model, f_name)
|
195
|
+
print(rel_model)
|
196
|
+
try:
|
197
|
+
return rel_model.table_field_definition[f_name]
|
198
|
+
except (AttributeError, KeyError):
|
199
|
+
attr = getattr(rel_model, f_name)
|
200
|
+
if isinstance(attr, Annotation):
|
201
|
+
attr = attr
|
202
|
+
elif hasattr(attr, 'field'):
|
203
|
+
attr = attr.field
|
204
|
+
return TableField(
|
205
|
+
label=str(attr.verbose_name),
|
206
|
+
name=f_name,
|
207
|
+
header_info=str(attr.help_text),
|
208
|
+
truncate_after=50
|
209
|
+
)
|
210
|
+
|
211
|
+
table_fields = []
|
212
|
+
for field_name in fields:
|
213
|
+
try:
|
214
|
+
table_fields.append(get_field_definition(field_name))
|
215
|
+
except AttributeError as e:
|
216
|
+
print(e)
|
217
|
+
pass
|
218
|
+
return table_fields
|
@@ -1,8 +1,12 @@
|
|
1
1
|
import time
|
2
2
|
import logging
|
3
|
+
from django.db import models
|
3
4
|
from django.core.cache import cache
|
4
5
|
from django.utils.translation import gettext_lazy as _
|
5
6
|
from django.utils.safestring import mark_safe
|
7
|
+
from django.utils.translation import get_language
|
8
|
+
|
9
|
+
from accrete.annotation import Annotation
|
6
10
|
|
7
11
|
_logger = logging.getLogger(__name__)
|
8
12
|
|
@@ -30,7 +34,7 @@ class Filter:
|
|
30
34
|
self.query_relation_depth = query_relation_depth
|
31
35
|
self.default_exclude = default_exclude or ['tenant', 'user']
|
32
36
|
self.default_filter_term = default_filter_term or ''
|
33
|
-
self.fields =
|
37
|
+
self.fields = []
|
34
38
|
self.field_paths = []
|
35
39
|
|
36
40
|
@staticmethod
|
@@ -43,8 +47,8 @@ class Filter:
|
|
43
47
|
def get_fields(self, model_path: list, field_path: str):
|
44
48
|
fields = self.get_local_fields(model_path[-1], field_path)
|
45
49
|
if len(model_path) <= self.query_relation_depth:
|
46
|
-
fields.
|
47
|
-
return fields
|
50
|
+
fields.extend(self.get_relation_fields(model_path, field_path))
|
51
|
+
return sorted(fields, key=lambda x: x['label'].lower())
|
48
52
|
|
49
53
|
def get_relation_fields(self, model_path, field_path):
|
50
54
|
filter_exclude = getattr(model_path[-1], 'filter_exclude', [])
|
@@ -53,7 +57,7 @@ class Filter:
|
|
53
57
|
lambda x: x.is_relation and x.name not in filter_exclude,
|
54
58
|
model_path[-1]._meta.get_fields()
|
55
59
|
)
|
56
|
-
res =
|
60
|
+
res = []
|
57
61
|
for field in fields:
|
58
62
|
if field.related_model in model_path:
|
59
63
|
continue
|
@@ -64,13 +68,14 @@ class Filter:
|
|
64
68
|
label = field.verbose_name
|
65
69
|
except AttributeError:
|
66
70
|
label = field.related_model._meta.verbose_name
|
67
|
-
res
|
68
|
-
'
|
71
|
+
res.append({
|
72
|
+
'name': field.name,
|
73
|
+
'label': str(label),
|
69
74
|
'type': field.get_internal_type(),
|
70
75
|
'null': field.null,
|
71
76
|
'choices': [],
|
72
77
|
'fields': self.get_fields(model_path_copy, rel_path)
|
73
|
-
}
|
78
|
+
})
|
74
79
|
return res
|
75
80
|
|
76
81
|
def get_local_fields(self, model, path):
|
@@ -80,34 +85,39 @@ class Filter:
|
|
80
85
|
lambda x: not x.is_relation and x.name not in filter_exclude,
|
81
86
|
model._meta.get_fields()
|
82
87
|
)
|
83
|
-
res =
|
88
|
+
res = []
|
84
89
|
for field in fields:
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
90
|
+
field_path = f'{path}{"__" if path else ""}{field.name}'
|
91
|
+
self.field_paths.append(field_path)
|
92
|
+
step = (hasattr(field, 'decimal_places')
|
93
|
+
and self.cast_decimal_places_to_step(field.decimal_places)
|
94
|
+
or 1)
|
95
|
+
res.append({
|
96
|
+
'name': field_path,
|
97
|
+
'label': str(field.verbose_name),
|
98
|
+
'type': field.get_internal_type(),
|
99
|
+
'choices': field.choices or [],
|
100
|
+
'null': field.null,
|
101
|
+
'step': step
|
95
102
|
})
|
96
|
-
|
97
|
-
res
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
103
|
+
if not hasattr(model, 'get_annotations'):
|
104
|
+
return res
|
105
|
+
for annotation in model.get_annotations():
|
106
|
+
field_path = f'{path}{"__" if path else ""}{annotation["name"]}'
|
107
|
+
self.field_paths.append(field_path)
|
108
|
+
res.append({
|
109
|
+
'name': field_path,
|
110
|
+
'label': str(annotation['annotation'].verbose_name),
|
111
|
+
'type': annotation['annotation'].field.__name__,
|
112
|
+
'choices': [],
|
113
|
+
'null': False,
|
114
|
+
'step': getattr(annotation['annotation'], 'step', '1')
|
105
115
|
})
|
106
116
|
return res
|
107
117
|
|
108
118
|
def to_html(self):
|
109
119
|
start = time.time()
|
110
|
-
key = f'filter-
|
120
|
+
key = f'filter-{self.model.__module__}.{self.model.__name__}-{get_language()}'
|
111
121
|
html = cache.get(key)
|
112
122
|
if html:
|
113
123
|
_logger.info(f'Fetching Filter-Terms from cache in {time.time() - start} seconds')
|
@@ -115,22 +125,27 @@ class Filter:
|
|
115
125
|
if not self.fields:
|
116
126
|
self.fields = self.get_fields([self.model], '')
|
117
127
|
html = ''
|
118
|
-
for
|
119
|
-
html += self.field_params(
|
120
|
-
html =
|
128
|
+
for f in self.fields:
|
129
|
+
html += self.field_params(f)
|
130
|
+
html = {
|
131
|
+
'params': mark_safe(html.strip().replace('\n', '')),
|
132
|
+
'field_paths': mark_safe(
|
133
|
+
self.field_path_selection().strip().replace('\n', '')
|
134
|
+
)
|
135
|
+
}
|
121
136
|
cache.set(key, html, 60 * 15)
|
122
137
|
_logger.info(f'Generated Filter-Terms in {time.time() - start} seconds')
|
123
|
-
return html
|
138
|
+
return {'params': html['params'], 'field_paths': html['field_paths']}
|
124
139
|
|
125
|
-
def field_params(self,
|
140
|
+
def field_params(self, field):
|
126
141
|
params = ''
|
127
|
-
params += self.params(
|
128
|
-
for
|
129
|
-
params += self.field_params(
|
142
|
+
params += self.params(field)
|
143
|
+
for f in field.get('fields', []):
|
144
|
+
params += self.field_params(f)
|
130
145
|
return f"""
|
131
|
-
<div class="query-param" tabindex="-1" data-param="{
|
132
|
-
<p class="px-1 arrow">{
|
133
|
-
<div class="query-params is-hidden" data-param="{
|
146
|
+
<div class="query-param" tabindex="-1" data-param="{field['name']}" data-param-label="{field['label']}">
|
147
|
+
<p class="px-1 arrow">{field['label']}</p>
|
148
|
+
<div class="query-params is-hidden" data-param="{field['name']}">
|
134
149
|
{params}
|
135
150
|
</div>
|
136
151
|
</div>
|
@@ -158,8 +173,8 @@ class Filter:
|
|
158
173
|
for choice in choices
|
159
174
|
])
|
160
175
|
|
161
|
-
def params(self,
|
162
|
-
return self.field_map().get(
|
176
|
+
def params(self, field):
|
177
|
+
return self.field_map().get(field['type'], self.no_param)(field['name'], field)
|
163
178
|
|
164
179
|
def param(
|
165
180
|
self, key: str, value: dict, param: str, data_type: str,
|
@@ -195,7 +210,6 @@ class Filter:
|
|
195
210
|
html += param_div(inverted=True)
|
196
211
|
return html
|
197
212
|
|
198
|
-
|
199
213
|
def char_param(self, key, value):
|
200
214
|
if value.get('choices'):
|
201
215
|
return self.char_choice_param(key, value)
|
@@ -262,7 +276,10 @@ class Filter:
|
|
262
276
|
return ''
|
263
277
|
|
264
278
|
def null_param(self, key, value):
|
265
|
-
options = self.parse_choices([
|
279
|
+
options = self.parse_choices([
|
280
|
+
('true', _('True')),
|
281
|
+
('false', _('False'))
|
282
|
+
])
|
266
283
|
return f"""
|
267
284
|
<div id="filter-id-~{key}__isnull"
|
268
285
|
class="query-param" tabindex="-1" data-type="selection"
|
@@ -278,3 +295,48 @@ class Filter:
|
|
278
295
|
|
279
296
|
def no_param(self, key, value):
|
280
297
|
return ''
|
298
|
+
|
299
|
+
def field_path_selection(self):
|
300
|
+
html = ''
|
301
|
+
for field in sorted(self.field_paths, key=lambda f: (len(f.split('__')), f.lower())):
|
302
|
+
label = self.get_field_path_label(field)
|
303
|
+
if not label:
|
304
|
+
continue
|
305
|
+
html += f"""
|
306
|
+
<label class="checkbox my-1" style="width: 100%">
|
307
|
+
<input type="checkbox" name="{field}">
|
308
|
+
{label}
|
309
|
+
</label>
|
310
|
+
"""
|
311
|
+
return html
|
312
|
+
|
313
|
+
def get_field_path_label(self, field_path):
|
314
|
+
label_parts = []
|
315
|
+
model = self.model
|
316
|
+
for field_part in field_path.split('__'):
|
317
|
+
if field_part.startswith('_a_'):
|
318
|
+
field_part = field_part.removeprefix('_a_')
|
319
|
+
annotations = {
|
320
|
+
annotation['name']: annotation
|
321
|
+
for annotation in getattr(model, 'annotations', [])
|
322
|
+
}
|
323
|
+
if annotations.get(field_part):
|
324
|
+
label_parts.append(str(annotations[field_part]['label']))
|
325
|
+
return ' / '.join(label_parts)
|
326
|
+
return False
|
327
|
+
|
328
|
+
field = getattr(model, field_part, False)
|
329
|
+
|
330
|
+
if not field:
|
331
|
+
return False
|
332
|
+
|
333
|
+
if isinstance(field.field, models.ForeignKey):
|
334
|
+
model = field.field.related_model
|
335
|
+
label_parts.append(str(model._meta.verbose_name))
|
336
|
+
continue
|
337
|
+
|
338
|
+
if not isinstance(field, Annotation):
|
339
|
+
field = field.field
|
340
|
+
|
341
|
+
label_parts.append(str(field.verbose_name))
|
342
|
+
return ' / '.join(label_parts)
|