accrete 0.0.36__tar.gz → 0.0.38__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.
Files changed (200) hide show
  1. {accrete-0.0.36 → accrete-0.0.38}/PKG-INFO +1 -1
  2. accrete-0.0.38/accrete/annotation.py +46 -0
  3. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/__init__.py +5 -12
  4. accrete-0.0.38/accrete/contrib/ui/context.py +238 -0
  5. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/filter.py +105 -43
  6. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/css/accrete.css +128 -128
  7. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/css/accrete.css.map +1 -1
  8. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/css/accrete.scss +9 -9
  9. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/js/filter.js +23 -0
  10. accrete-0.0.38/accrete/contrib/ui/static/js/htmx.min.js +1 -0
  11. accrete-0.0.38/accrete/contrib/ui/templates/ui/layout.html +178 -0
  12. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/ui/list.html +2 -2
  13. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/ui/partials/filter.html +28 -4
  14. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/ui/partials/header.html +5 -5
  15. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/ui/table.html +1 -1
  16. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templatetags/accrete_ui.py +10 -6
  17. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/templates/user/login.html +6 -12
  18. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/views.py +10 -9
  19. {accrete-0.0.36 → accrete-0.0.38}/accrete/middleware.py +15 -0
  20. {accrete-0.0.36 → accrete-0.0.38}/accrete/models.py +9 -7
  21. {accrete-0.0.36 → accrete-0.0.38}/accrete/querystring.py +16 -25
  22. accrete-0.0.38/accrete/utils/models.py +19 -0
  23. {accrete-0.0.36 → accrete-0.0.38}/pyproject.toml +1 -1
  24. accrete-0.0.36/accrete/contrib/ui/context.py +0 -345
  25. accrete-0.0.36/accrete/contrib/ui/querystring.py +0 -19
  26. accrete-0.0.36/accrete/contrib/ui/templates/ui/layout.html +0 -176
  27. {accrete-0.0.36 → accrete-0.0.38}/.gitignore +0 -0
  28. {accrete-0.0.36 → accrete-0.0.38}/LICENSE +0 -0
  29. {accrete-0.0.36 → accrete-0.0.38}/README.md +0 -0
  30. {accrete-0.0.36 → accrete-0.0.38}/accrete/__init__.py +0 -0
  31. {accrete-0.0.36 → accrete-0.0.38}/accrete/admin.py +0 -0
  32. {accrete-0.0.36 → accrete-0.0.38}/accrete/apps.py +0 -0
  33. {accrete-0.0.36 → accrete-0.0.38}/accrete/config.py +0 -0
  34. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/__init__.py +0 -0
  35. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/sequence/__init__.py +0 -0
  36. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/sequence/admin.py +0 -0
  37. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/sequence/apps.py +0 -0
  38. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/sequence/forms.py +0 -0
  39. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/sequence/migrations/0001_initial.py +0 -0
  40. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/sequence/migrations/0002_alter_sequence_name.py +0 -0
  41. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/sequence/migrations/__init__.py +0 -0
  42. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/sequence/models.py +0 -0
  43. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/sequence/queries.py +0 -0
  44. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/sequence/tests.py +0 -0
  45. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/sequence/views.py +0 -0
  46. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/system_mail/__init__.py +0 -0
  47. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/system_mail/admin.py +0 -0
  48. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/system_mail/apps.py +0 -0
  49. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/system_mail/forms.py +0 -0
  50. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/system_mail/migrations/0001_initial.py +0 -0
  51. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/system_mail/migrations/__init__.py +0 -0
  52. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/system_mail/models.py +0 -0
  53. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/system_mail/tasks.py +0 -0
  54. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/system_mail/tests.py +0 -0
  55. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/system_mail/views.py +0 -0
  56. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/admin.py +0 -0
  57. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/apps.py +0 -0
  58. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/elements.py +0 -0
  59. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/migrations/__init__.py +0 -0
  60. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/LICENSE +0 -0
  61. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/README.md +0 -0
  62. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/bulma.sass +0 -0
  63. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/css/bulma-rtl.css +0 -0
  64. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/css/bulma-rtl.css.map +0 -0
  65. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/css/bulma-rtl.min.css +0 -0
  66. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/css/bulma.css +0 -0
  67. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/css/bulma.css.map +0 -0
  68. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/css/bulma.min.css +0 -0
  69. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/package.json +0 -0
  70. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/base/_all.sass +0 -0
  71. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/base/animations.sass +0 -0
  72. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/base/generic.sass +0 -0
  73. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/base/helpers.sass +0 -0
  74. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/base/minireset.sass +0 -0
  75. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/components/_all.sass +0 -0
  76. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/components/breadcrumb.sass +0 -0
  77. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/components/card.sass +0 -0
  78. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/components/dropdown.sass +0 -0
  79. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/components/level.sass +0 -0
  80. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/components/media.sass +0 -0
  81. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/components/menu.sass +0 -0
  82. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/components/message.sass +0 -0
  83. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/components/modal.sass +0 -0
  84. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/components/navbar.sass +0 -0
  85. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/components/pagination.sass +0 -0
  86. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/components/panel.sass +0 -0
  87. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/components/tabs.sass +0 -0
  88. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/elements/_all.sass +0 -0
  89. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/elements/box.sass +0 -0
  90. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/elements/button.sass +0 -0
  91. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/elements/container.sass +0 -0
  92. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/elements/content.sass +0 -0
  93. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/elements/form.sass +0 -0
  94. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/elements/icon.sass +0 -0
  95. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/elements/image.sass +0 -0
  96. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/elements/notification.sass +0 -0
  97. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/elements/other.sass +0 -0
  98. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/elements/progress.sass +0 -0
  99. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/elements/table.sass +0 -0
  100. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/elements/tag.sass +0 -0
  101. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/elements/title.sass +0 -0
  102. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/form/_all.sass +0 -0
  103. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/form/checkbox-radio.sass +0 -0
  104. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/form/file.sass +0 -0
  105. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/form/input-textarea.sass +0 -0
  106. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/form/select.sass +0 -0
  107. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/form/shared.sass +0 -0
  108. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/form/tools.sass +0 -0
  109. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/grid/_all.sass +0 -0
  110. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/grid/columns.sass +0 -0
  111. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/grid/tiles.sass +0 -0
  112. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/helpers/_all.sass +0 -0
  113. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/helpers/color.sass +0 -0
  114. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/helpers/flexbox.sass +0 -0
  115. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/helpers/float.sass +0 -0
  116. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/helpers/other.sass +0 -0
  117. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/helpers/overflow.sass +0 -0
  118. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/helpers/position.sass +0 -0
  119. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/helpers/spacing.sass +0 -0
  120. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/helpers/typography.sass +0 -0
  121. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/helpers/visibility.sass +0 -0
  122. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/layout/_all.sass +0 -0
  123. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/layout/footer.sass +0 -0
  124. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/layout/hero.sass +0 -0
  125. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/layout/section.sass +0 -0
  126. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/utilities/_all.sass +0 -0
  127. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/utilities/animations.sass +0 -0
  128. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/utilities/controls.sass +0 -0
  129. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/utilities/derived-variables.sass +0 -0
  130. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/utilities/extends.sass +0 -0
  131. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/utilities/functions.sass +0 -0
  132. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/utilities/initial-variables.sass +0 -0
  133. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/bulma/sass/utilities/mixins.sass +0 -0
  134. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/css/accrete.css.bak +0 -0
  135. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/css/icons.css +0 -0
  136. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/icons/Logo.svg +0 -0
  137. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/icons/accrete.svg +0 -0
  138. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/static/js/list.js +0 -0
  139. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/django/forms/widgets/attrs.html +0 -0
  140. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/django/forms/widgets/email.html +0 -0
  141. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/django/forms/widgets/file.html +0 -0
  142. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/django/forms/widgets/input.html +0 -0
  143. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/django/forms/widgets/select.html +0 -0
  144. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/django/forms/widgets/text.html +0 -0
  145. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/django/forms/widgets/textarea.html +0 -0
  146. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/ui/detail.html +0 -0
  147. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/ui/form.html +0 -0
  148. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/ui/partials/form_errors.html +0 -0
  149. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/ui/partials/form_modal.html +0 -0
  150. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/ui/partials/onchange_form.html +0 -0
  151. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/ui/partials/pagination_detail.html +0 -0
  152. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/ui/partials/pagination_list.html +0 -0
  153. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/ui/partials/table_field.html +0 -0
  154. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/ui/partials/table_field_float.html +0 -0
  155. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/ui/partials/table_field_monetary.html +0 -0
  156. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templates/ui/partials/table_field_string.html +0 -0
  157. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/templatetags/__init__.py +0 -0
  158. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/tests.py +0 -0
  159. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/urls.py +0 -0
  160. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/ui/views.py +0 -0
  161. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/__init__.py +0 -0
  162. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/admin.py +0 -0
  163. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/apps.py +0 -0
  164. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/forms.py +0 -0
  165. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/locale/de/LC_MESSAGES/django.mo +0 -0
  166. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/locale/de/LC_MESSAGES/django.po +0 -0
  167. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/middleware.py +0 -0
  168. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/migrations/0001_initial.py +0 -0
  169. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/migrations/__init__.py +0 -0
  170. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/models.py +0 -0
  171. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/templates/user/accrete_navbar_end_dropdown.html +0 -0
  172. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/templates/user/change_email.html +0 -0
  173. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/templates/user/change_password.html +0 -0
  174. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/templates/user/user_detail.html +0 -0
  175. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/templates/user/user_form.html +0 -0
  176. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/tests.py +0 -0
  177. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user/urls.py +0 -0
  178. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user_registration/__init__.py +0 -0
  179. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user_registration/admin.py +0 -0
  180. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user_registration/apps.py +0 -0
  181. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user_registration/config.py +0 -0
  182. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user_registration/forms.py +0 -0
  183. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user_registration/migrations/0001_initial.py +0 -0
  184. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user_registration/migrations/__init__.py +0 -0
  185. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user_registration/models.py +0 -0
  186. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user_registration/templates/user_registration/mail_templates/confirmation_mail.html +0 -0
  187. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user_registration/templates/user_registration/registration.html +0 -0
  188. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user_registration/tests.py +0 -0
  189. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user_registration/urls.py +0 -0
  190. {accrete-0.0.36 → accrete-0.0.38}/accrete/contrib/user_registration/views.py +0 -0
  191. {accrete-0.0.36 → accrete-0.0.38}/accrete/forms.py +0 -0
  192. {accrete-0.0.36 → accrete-0.0.38}/accrete/migrations/0001_initial.py +0 -0
  193. {accrete-0.0.36 → accrete-0.0.38}/accrete/migrations/0002_initial.py +0 -0
  194. {accrete-0.0.36 → accrete-0.0.38}/accrete/migrations/__init__.py +0 -0
  195. {accrete-0.0.36 → accrete-0.0.38}/accrete/queries.py +0 -0
  196. {accrete-0.0.36 → accrete-0.0.38}/accrete/tenant.py +0 -0
  197. {accrete-0.0.36 → accrete-0.0.38}/accrete/tests.py +0 -0
  198. {accrete-0.0.36 → accrete-0.0.38}/accrete/utils/__init__.py +0 -0
  199. {accrete-0.0.36 → accrete-0.0.38}/accrete/utils/dates.py +0 -0
  200. {accrete-0.0.36 → accrete-0.0.38}/accrete/views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: accrete
3
- Version: 0.0.36
3
+ Version: 0.0.38
4
4
  Summary: Django Shared Schema Multi Tenant
5
5
  Author-email: Benedikt Jilek <benedikt.jilek@pm.me>
6
6
  License: Copyright (c) 2023 Benedikt Jilek
@@ -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
- default_form_actions,
8
- Page,
9
- ListContent,
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,238 @@
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 import models
6
+ from django.db.models import Model, QuerySet
7
+ from django.core.paginator import Paginator
8
+ from django.core import paginator
9
+ from django.forms import Form, ModelForm
10
+ from accrete.utils.models import get_related_model
11
+ from accrete.annotation import Annotation
12
+ from .elements import (
13
+ ClientAction, BreadCrumb, TableField, TableFieldType, TableFieldAlignment
14
+ )
15
+ from .filter import Filter
16
+
17
+
18
+ _logger = logging.getLogger(__name__)
19
+
20
+ DEFAULT_PAGINATE_BY = 40
21
+
22
+
23
+ class DetailPagination(TypedDict):
24
+ previous_object_url: str
25
+ next_object_url: str
26
+ current_object_idx: int
27
+ total_objects: int
28
+
29
+
30
+ @dataclass(kw_only=True)
31
+ class Context:
32
+
33
+ title: str = ''
34
+ breadcrumbs: list[BreadCrumb] = field(default_factory=list)
35
+ actions: list[ClientAction] = field(default_factory=list)
36
+ kwargs: dict = field(default_factory=dict)
37
+
38
+ def __post_init__(self):
39
+ for key, value in self.kwargs.items():
40
+ setattr(self, key, value)
41
+
42
+ def dict(self):
43
+ return {
44
+ attr: getattr(self, attr, '') for attr
45
+ in filter(lambda x: not x.startswith('_'), self.__dict__)
46
+ }
47
+
48
+
49
+ @dataclass
50
+ class TableContext(Context):
51
+
52
+ object_label: str
53
+ fields: list[TableField]
54
+ list_page: paginator.Page
55
+ pagination_param_str: str
56
+ endless_scroll: bool
57
+ filter: Filter
58
+ object_param_str: str = field(default='', kw_only=True)
59
+
60
+
61
+ @dataclass
62
+ class ListContext(Context):
63
+
64
+ object_label: str
65
+ object_param_str: str
66
+ list_page: paginator.Page
67
+ pagination_param_str: str
68
+ filter: Filter
69
+ endless_scroll: bool = True
70
+ column_height: int = 4
71
+ column_width: int = 12
72
+
73
+ def __post_init__(self):
74
+ super().__post_init__()
75
+ if self.column_width not in range(1, 13):
76
+ _logger.warning(
77
+ 'ListContext parameter column_width should be in range 1 - 12'
78
+ )
79
+
80
+
81
+ class DetailContext(TypedDict, total=False):
82
+
83
+ title: str
84
+ object: Model
85
+ breadcrumbs: list[BreadCrumb]
86
+ detail_page: DetailPagination
87
+ pagination_param_str: str
88
+ actions: list[ClientAction]
89
+
90
+
91
+ class FormContext(TypedDict, total=False):
92
+
93
+ title: str
94
+ breadcrumbs: list[BreadCrumb]
95
+ form: Form | ModelForm
96
+ form_id: str
97
+ actions: list[ClientAction]
98
+
99
+
100
+ def cast_param(params: dict, param: str, cast_to: callable, default):
101
+ if param not in params:
102
+ return default
103
+ try:
104
+ return cast_to(params.get(param, default))
105
+ except Exception as e:
106
+ _logger.exception(e)
107
+ return default
108
+
109
+
110
+ def prepare_url_params(get_params: dict) -> dict:
111
+ return {key: f'&{key}={value}' for key, value in get_params.items()}
112
+
113
+
114
+ def url_param_str(params: dict, extract: list[str] = None) -> str:
115
+ """
116
+ Return a URL Querystring from the given parameters
117
+ If extract is supplied, extract the value from the dictionary and prepare
118
+ them, so that each value is formatted e.g. {'page': '&page=1'}
119
+ """
120
+ if extract:
121
+ params = prepare_url_params(extract_url_params(params, extract))
122
+ param_str = (
123
+ "".join(str(value) for value in params.values())
124
+ .replace('&&', '&')
125
+ .replace('?&', '?')
126
+ .strip('?&')
127
+ )
128
+ return f'?{param_str}'
129
+
130
+
131
+ def extract_url_params(params: dict, keys: list[str]) -> dict:
132
+ return {key: params[key] for key in keys if key in params}
133
+
134
+
135
+ def exclude_params(params: dict, keys: list[str]) -> dict:
136
+ return {key: val for key, val in params.items() if key not in keys}
137
+
138
+
139
+ def list_page(queryset: QuerySet, paginate_by: int, page_number: int) -> paginator.Page:
140
+ pages = Paginator(queryset, per_page=paginate_by)
141
+ return pages.page(page_number <= pages.num_pages and page_number or pages.num_pages)
142
+
143
+
144
+ def detail_page(queryset: QuerySet, obj: Model) -> dict:
145
+ if not hasattr(obj, 'get_absolute_url'):
146
+ _logger.warning(
147
+ 'Detail pagination disabled for models without the '
148
+ 'get_absolute_url attribute. Set paginate_by to 0 to '
149
+ 'deactivate pagination.'
150
+ )
151
+ return {}
152
+ idx = (*queryset,).index(obj)
153
+ previous_object_url = (
154
+ queryset[idx - 1] if idx - 1 >= 0 else queryset.last()
155
+ ).get_absolute_url()
156
+ next_object_url = (
157
+ queryset[idx + 1] if idx + 1 <= queryset.count() - 1 else queryset.first()
158
+ ).get_absolute_url()
159
+ return {
160
+ 'previous_object_url': previous_object_url,
161
+ 'next_object_url': next_object_url,
162
+ 'current_object_idx': idx + 1,
163
+ 'total_objects': queryset.count()
164
+ }
165
+
166
+
167
+ def form_actions(discard_url: str, form_id: str = 'form') -> list[ClientAction]:
168
+ return [
169
+ ClientAction(
170
+ name=_t('Save'),
171
+ submit=True,
172
+ class_list=['is-success'],
173
+ form_id=form_id,
174
+ ),
175
+ ClientAction(
176
+ name=_t('Discard'),
177
+ url=discard_url
178
+ )
179
+ ]
180
+
181
+
182
+ def get_table_fields(
183
+ fields: list[str],
184
+ model: type[Model],
185
+ field_definition: dict[str | TableField] = None
186
+ ) -> list[TableField]:
187
+
188
+ def get_alignment(field_attr):
189
+ number_field_types = (
190
+ models.DecimalField, models.IntegerField, models.FloatField
191
+ )
192
+ if (
193
+ isinstance(field_attr, number_field_types)
194
+ or (
195
+ isinstance(field_attr, Annotation)
196
+ and field_attr.field in number_field_types
197
+ )):
198
+ return TableFieldAlignment.RIGHT
199
+ return TableFieldAlignment.LEFT
200
+
201
+
202
+ def get_field_definition(f_name: str) -> TableField:
203
+ if definition := field_definition.get(f_name):
204
+ return definition
205
+ parts = f_name.split('__')
206
+ if len(parts) == 1:
207
+ rel_model = model
208
+ names = [rel_model._meta.verbose_name]
209
+ else:
210
+ rel_model, names = get_related_model(model, f_name)
211
+ try:
212
+ return rel_model.table_field_definition[parts[-1]]
213
+ except (AttributeError, KeyError):
214
+ attr = getattr(rel_model, parts[-1])
215
+ if isinstance(attr, Annotation):
216
+ pass
217
+ elif hasattr(attr, 'field'):
218
+ attr = attr.field
219
+ names.append(attr.verbose_name)
220
+ return TableField(
221
+ label=' / '.join([str(name) for name in names[1:]]),
222
+ name=f_name,
223
+ header_info=str(attr.help_text),
224
+ truncate_after=50,
225
+ alignment=get_alignment(attr)
226
+ )
227
+
228
+ if field_definition is None:
229
+ field_definition = {}
230
+
231
+ table_fields = []
232
+ for field_name in fields:
233
+ try:
234
+ table_fields.append(get_field_definition(field_name))
235
+ except AttributeError as e:
236
+ print(e)
237
+ pass
238
+ 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.update(self.get_relation_fields(model_path, field_path))
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[field.name] = {
68
- 'label': label,
71
+ res.append({
72
+ 'name': f'{rel_path}',
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
- res.update({
86
- f'{path}{"__" if path else ""}{field.name}': {
87
- 'label': field.verbose_name,
88
- 'type': field.get_internal_type(),
89
- 'choices': field.choices or [],
90
- 'null': field.null,
91
- 'step': hasattr(field, 'decimal_places')
92
- and self.cast_decimal_places_to_step(field.decimal_places)
93
- or 1
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
- for annotation in getattr(model, 'annotations', []):
97
- res.update({
98
- f'{path}{"__" if path else ""}_a_{annotation["name"]}': {
99
- 'label': annotation['label'],
100
- 'type': annotation['type'].__name__,
101
- 'choices': [],
102
- 'null': False,
103
- 'step': self.cast_decimal_places_to_step(annotation.get('decimal_places', 1))
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-terms-{self.model.__module__}.{self.model.__name__}'
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 k, v in self.fields.items():
119
- html += self.field_params(k, v)
120
- html = mark_safe(html.strip().replace('\n', ''))
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, key, value):
140
+ def field_params(self, field):
126
141
  params = ''
127
- params += self.params(key, value)
128
- for k, v in value.get('fields', {}).items():
129
- params += self.field_params(k, v)
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="{key}" data-param-label="{value['label']}">
132
- <p class="px-1 arrow">{value['label']}</p>
133
- <div class="query-params is-hidden" data-param="{key}">
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, key, value):
162
- return self.field_map().get(value['type'], self.no_param)(key, value)
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([('true', _('True')), ('false', _('False'))])
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)