accrete 0.0.35__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.
Files changed (201) hide show
  1. {accrete-0.0.35 → accrete-0.0.37}/PKG-INFO +1 -1
  2. accrete-0.0.37/accrete/annotation.py +46 -0
  3. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/__init__.py +12 -3
  4. accrete-0.0.37/accrete/contrib/ui/context.py +218 -0
  5. accrete-0.0.35/accrete/contrib/ui/components.py → accrete-0.0.37/accrete/contrib/ui/elements.py +30 -48
  6. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/filter.py +110 -44
  7. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/css/accrete.css +128 -128
  8. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/css/accrete.css.map +1 -1
  9. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/css/accrete.scss +9 -9
  10. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/js/filter.js +24 -0
  11. accrete-0.0.37/accrete/contrib/ui/static/js/htmx.min.js +1 -0
  12. accrete-0.0.37/accrete/contrib/ui/templates/ui/layout.html +178 -0
  13. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/list.html +3 -3
  14. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/filter.html +29 -5
  15. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/header.html +7 -7
  16. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/pagination_detail.html +3 -3
  17. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/pagination_list.html +4 -4
  18. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/table.html +18 -13
  19. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templatetags/accrete_ui.py +12 -1
  20. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/forms.py +0 -4
  21. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/templates/user/login.html +6 -12
  22. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/views.py +31 -21
  23. {accrete-0.0.35 → accrete-0.0.37}/accrete/middleware.py +15 -0
  24. {accrete-0.0.35 → accrete-0.0.37}/accrete/models.py +9 -7
  25. {accrete-0.0.35 → accrete-0.0.37}/accrete/querystring.py +11 -8
  26. accrete-0.0.37/accrete/utils/models.py +14 -0
  27. {accrete-0.0.35 → accrete-0.0.37}/pyproject.toml +1 -1
  28. accrete-0.0.35/accrete/contrib/ui/context.py +0 -291
  29. accrete-0.0.35/accrete/contrib/ui/elements.py +0 -2
  30. accrete-0.0.35/accrete/contrib/ui/querystring.py +0 -19
  31. accrete-0.0.35/accrete/contrib/ui/templates/ui/layout.html +0 -173
  32. {accrete-0.0.35 → accrete-0.0.37}/.gitignore +0 -0
  33. {accrete-0.0.35 → accrete-0.0.37}/LICENSE +0 -0
  34. {accrete-0.0.35 → accrete-0.0.37}/README.md +0 -0
  35. {accrete-0.0.35 → accrete-0.0.37}/accrete/__init__.py +0 -0
  36. {accrete-0.0.35 → accrete-0.0.37}/accrete/admin.py +0 -0
  37. {accrete-0.0.35 → accrete-0.0.37}/accrete/apps.py +0 -0
  38. {accrete-0.0.35 → accrete-0.0.37}/accrete/config.py +0 -0
  39. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/__init__.py +0 -0
  40. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/sequence/__init__.py +0 -0
  41. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/sequence/admin.py +0 -0
  42. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/sequence/apps.py +0 -0
  43. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/sequence/forms.py +0 -0
  44. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/sequence/migrations/0001_initial.py +0 -0
  45. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/sequence/migrations/0002_alter_sequence_name.py +0 -0
  46. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/sequence/migrations/__init__.py +0 -0
  47. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/sequence/models.py +0 -0
  48. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/sequence/queries.py +0 -0
  49. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/sequence/tests.py +0 -0
  50. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/sequence/views.py +0 -0
  51. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/system_mail/__init__.py +0 -0
  52. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/system_mail/admin.py +0 -0
  53. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/system_mail/apps.py +0 -0
  54. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/system_mail/forms.py +0 -0
  55. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/system_mail/migrations/0001_initial.py +0 -0
  56. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/system_mail/migrations/__init__.py +0 -0
  57. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/system_mail/models.py +0 -0
  58. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/system_mail/tasks.py +0 -0
  59. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/system_mail/tests.py +0 -0
  60. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/system_mail/views.py +0 -0
  61. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/admin.py +0 -0
  62. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/apps.py +0 -0
  63. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/migrations/__init__.py +0 -0
  64. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/LICENSE +0 -0
  65. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/README.md +0 -0
  66. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/bulma.sass +0 -0
  67. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/css/bulma-rtl.css +0 -0
  68. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/css/bulma-rtl.css.map +0 -0
  69. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/css/bulma-rtl.min.css +0 -0
  70. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/css/bulma.css +0 -0
  71. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/css/bulma.css.map +0 -0
  72. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/css/bulma.min.css +0 -0
  73. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/package.json +0 -0
  74. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/base/_all.sass +0 -0
  75. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/base/animations.sass +0 -0
  76. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/base/generic.sass +0 -0
  77. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/base/helpers.sass +0 -0
  78. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/base/minireset.sass +0 -0
  79. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/_all.sass +0 -0
  80. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/breadcrumb.sass +0 -0
  81. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/card.sass +0 -0
  82. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/dropdown.sass +0 -0
  83. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/level.sass +0 -0
  84. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/media.sass +0 -0
  85. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/menu.sass +0 -0
  86. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/message.sass +0 -0
  87. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/modal.sass +0 -0
  88. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/navbar.sass +0 -0
  89. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/pagination.sass +0 -0
  90. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/panel.sass +0 -0
  91. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/tabs.sass +0 -0
  92. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/_all.sass +0 -0
  93. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/box.sass +0 -0
  94. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/button.sass +0 -0
  95. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/container.sass +0 -0
  96. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/content.sass +0 -0
  97. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/form.sass +0 -0
  98. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/icon.sass +0 -0
  99. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/image.sass +0 -0
  100. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/notification.sass +0 -0
  101. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/other.sass +0 -0
  102. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/progress.sass +0 -0
  103. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/table.sass +0 -0
  104. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/tag.sass +0 -0
  105. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/title.sass +0 -0
  106. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/_all.sass +0 -0
  107. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/checkbox-radio.sass +0 -0
  108. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/file.sass +0 -0
  109. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/input-textarea.sass +0 -0
  110. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/select.sass +0 -0
  111. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/shared.sass +0 -0
  112. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/tools.sass +0 -0
  113. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/grid/_all.sass +0 -0
  114. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/grid/columns.sass +0 -0
  115. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/grid/tiles.sass +0 -0
  116. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/_all.sass +0 -0
  117. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/color.sass +0 -0
  118. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/flexbox.sass +0 -0
  119. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/float.sass +0 -0
  120. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/other.sass +0 -0
  121. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/overflow.sass +0 -0
  122. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/position.sass +0 -0
  123. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/spacing.sass +0 -0
  124. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/typography.sass +0 -0
  125. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/visibility.sass +0 -0
  126. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/layout/_all.sass +0 -0
  127. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/layout/footer.sass +0 -0
  128. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/layout/hero.sass +0 -0
  129. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/layout/section.sass +0 -0
  130. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/_all.sass +0 -0
  131. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/animations.sass +0 -0
  132. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/controls.sass +0 -0
  133. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/derived-variables.sass +0 -0
  134. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/extends.sass +0 -0
  135. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/functions.sass +0 -0
  136. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/initial-variables.sass +0 -0
  137. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/mixins.sass +0 -0
  138. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/css/accrete.css.bak +0 -0
  139. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/css/icons.css +0 -0
  140. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/icons/Logo.svg +0 -0
  141. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/icons/accrete.svg +0 -0
  142. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/static/js/list.js +0 -0
  143. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/attrs.html +0 -0
  144. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/email.html +0 -0
  145. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/file.html +0 -0
  146. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/input.html +0 -0
  147. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/select.html +0 -0
  148. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/text.html +0 -0
  149. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/textarea.html +0 -0
  150. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/detail.html +0 -0
  151. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/form.html +0 -0
  152. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/form_errors.html +0 -0
  153. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/form_modal.html +0 -0
  154. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/onchange_form.html +0 -0
  155. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/table_field.html +0 -0
  156. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/table_field_float.html +0 -0
  157. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/table_field_monetary.html +0 -0
  158. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/table_field_string.html +0 -0
  159. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/templatetags/__init__.py +0 -0
  160. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/tests.py +0 -0
  161. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/urls.py +0 -0
  162. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/ui/views.py +0 -0
  163. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/__init__.py +0 -0
  164. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/admin.py +0 -0
  165. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/apps.py +0 -0
  166. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/locale/de/LC_MESSAGES/django.mo +0 -0
  167. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/locale/de/LC_MESSAGES/django.po +0 -0
  168. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/middleware.py +0 -0
  169. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/migrations/0001_initial.py +0 -0
  170. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/migrations/__init__.py +0 -0
  171. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/models.py +0 -0
  172. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/templates/user/accrete_navbar_end_dropdown.html +0 -0
  173. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/templates/user/change_email.html +0 -0
  174. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/templates/user/change_password.html +0 -0
  175. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/templates/user/user_detail.html +0 -0
  176. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/templates/user/user_form.html +0 -0
  177. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/tests.py +0 -0
  178. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user/urls.py +0 -0
  179. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user_registration/__init__.py +0 -0
  180. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user_registration/admin.py +0 -0
  181. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user_registration/apps.py +0 -0
  182. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user_registration/config.py +0 -0
  183. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user_registration/forms.py +0 -0
  184. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user_registration/migrations/0001_initial.py +0 -0
  185. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user_registration/migrations/__init__.py +0 -0
  186. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user_registration/models.py +0 -0
  187. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user_registration/templates/user_registration/mail_templates/confirmation_mail.html +0 -0
  188. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user_registration/templates/user_registration/registration.html +0 -0
  189. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user_registration/tests.py +0 -0
  190. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user_registration/urls.py +0 -0
  191. {accrete-0.0.35 → accrete-0.0.37}/accrete/contrib/user_registration/views.py +0 -0
  192. {accrete-0.0.35 → accrete-0.0.37}/accrete/forms.py +0 -0
  193. {accrete-0.0.35 → accrete-0.0.37}/accrete/migrations/0001_initial.py +0 -0
  194. {accrete-0.0.35 → accrete-0.0.37}/accrete/migrations/0002_initial.py +0 -0
  195. {accrete-0.0.35 → accrete-0.0.37}/accrete/migrations/__init__.py +0 -0
  196. {accrete-0.0.35 → accrete-0.0.37}/accrete/queries.py +0 -0
  197. {accrete-0.0.35 → accrete-0.0.37}/accrete/tenant.py +0 -0
  198. {accrete-0.0.35 → accrete-0.0.37}/accrete/tests.py +0 -0
  199. {accrete-0.0.35 → accrete-0.0.37}/accrete/utils/__init__.py +0 -0
  200. {accrete-0.0.35 → accrete-0.0.37}/accrete/utils/dates.py +0 -0
  201. {accrete-0.0.35 → accrete-0.0.37}/accrete/views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: accrete
3
- Version: 0.0.35
3
+ Version: 0.0.37
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
+
@@ -1,10 +1,20 @@
1
1
  from .filter import Filter
2
2
  from .context import (
3
- ListContext,
4
3
  DetailContext,
4
+ TableContext,
5
+ ListContext,
5
6
  FormContext,
7
+ form_actions,
8
+ list_page,
9
+ detail_page,
10
+ cast_param,
11
+ prepare_url_params,
12
+ extract_url_params,
13
+ exclude_params,
14
+ url_param_str,
15
+ get_table_fields
6
16
  )
7
- from .components import (
17
+ from .elements import (
8
18
  ClientAction,
9
19
  ActionMethod,
10
20
  BreadCrumb,
@@ -13,4 +23,3 @@ from .components import (
13
23
  TableFieldType,
14
24
  Icon
15
25
  )
16
- from .querystring import load_querystring, build_querystring
@@ -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,19 @@
1
- from dataclasses import dataclass, field
2
1
  from enum import Enum
3
- from django.db.models import Model, QuerySet, Q
2
+ from dataclasses import dataclass, field
4
3
 
5
- DEFAULT_PAGINATE_BY = 40
4
+
5
+ class Icon(Enum):
6
+
7
+ ADD = 'icon-add'
8
+ EDIT = 'icon-edit'
9
+ LIST = 'icon-list'
10
+ OPEN_RELATED = 'icon-open-related'
11
+ ENVELOPE = 'icon-envelope'
12
+ CLEAR = 'icon-clear'
13
+ BACKSPACE = 'icon-backspace'
14
+ FILTER = 'icon-filter'
15
+ DELETE_FILTER = 'icon-delete-filter'
16
+ SELECT = 'icon-select'
6
17
 
7
18
 
8
19
  class ActionMethod(Enum):
@@ -14,6 +25,22 @@ class ActionMethod(Enum):
14
25
  DELETE = 'hx-delete'
15
26
 
16
27
 
28
+ @dataclass
29
+ class ClientAction:
30
+
31
+ name: str
32
+ url: str = ''
33
+ method: ActionMethod = ActionMethod.HREF
34
+ attrs: list[tuple[str, str]] = field(default_factory=list)
35
+ submit: bool = False
36
+ form_id: str = 'form'
37
+ class_list: list[str] = field(default_factory=list)
38
+ icon: Icon | type[Enum] = None
39
+
40
+ def attrs_str(self):
41
+ return ' '.join([f'{str(attr[0])}={str(attr[1])}' for attr in self.attrs])
42
+
43
+
17
44
  class TableFieldAlignment(Enum):
18
45
 
19
46
  LEFT = 'left'
@@ -49,48 +76,3 @@ class BreadCrumb:
49
76
 
50
77
  name: str
51
78
  url: str
52
- add_url_params: bool = True
53
-
54
-
55
- class Icon(Enum):
56
-
57
- ADD = 'icon-add'
58
- EDIT = 'icon-edit'
59
- LIST = 'icon-list'
60
- OPEN_RELATED = 'icon-open-related'
61
- ENVELOPE = 'icon-envelope'
62
- CLEAR = 'icon-clear'
63
- BACKSPACE = 'icon-backspace'
64
- FILTER = 'icon-filter'
65
- DELETE_FILTER = 'icon-delete-filter'
66
- SELECT = 'icon-select'
67
-
68
-
69
-
70
- @dataclass
71
- class ClientAction:
72
-
73
- name: str
74
- url: str = ''
75
- method: ActionMethod = ActionMethod.HREF
76
- query_params: str = ''
77
- attrs: list[tuple[str, str]] = field(default_factory=list)
78
- submit: bool = False
79
- form_id: str = 'form'
80
- class_list: list[str] = field(default_factory=list)
81
- add_url_params: bool = False
82
- icon: Icon | type[Enum] = None
83
-
84
- def attrs_str(self):
85
- return ' '.join([f'{str(attr[0])}={str(attr[1])}' for attr in self.attrs])
86
-
87
-
88
- @dataclass
89
- class List:
90
-
91
- queryset: QuerySet
92
- title: str = None
93
- paginate_by: int = DEFAULT_PAGINATE_BY
94
- breadcrumbs: list[BreadCrumb] = field(default_factory=list)
95
- actions: list[ClientAction] = field(default_factory=list)
96
- fields: list[TableField] = field(default_factory=list)
@@ -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
 
@@ -22,11 +26,15 @@ class Filter:
22
26
  LABEL_SET = _('Is Set')
23
27
  LABEL_NOT_SET = _('Is Not Set')
24
28
 
25
- def __init__(self, model, query_relation_depth: int = 4, default_exclude: list = None):
29
+ def __init__(
30
+ self, model, query_relation_depth: int = 4,
31
+ default_exclude: list[str] = None, default_filter_term: str = None
32
+ ):
26
33
  self.model = model
27
34
  self.query_relation_depth = query_relation_depth
28
35
  self.default_exclude = default_exclude or ['tenant', 'user']
29
- self.fields = {}
36
+ self.default_filter_term = default_filter_term or ''
37
+ self.fields = []
30
38
  self.field_paths = []
31
39
 
32
40
  @staticmethod
@@ -39,8 +47,8 @@ class Filter:
39
47
  def get_fields(self, model_path: list, field_path: str):
40
48
  fields = self.get_local_fields(model_path[-1], field_path)
41
49
  if len(model_path) <= self.query_relation_depth:
42
- fields.update(self.get_relation_fields(model_path, field_path))
43
- return fields
50
+ fields.extend(self.get_relation_fields(model_path, field_path))
51
+ return sorted(fields, key=lambda x: x['label'].lower())
44
52
 
45
53
  def get_relation_fields(self, model_path, field_path):
46
54
  filter_exclude = getattr(model_path[-1], 'filter_exclude', [])
@@ -49,7 +57,7 @@ class Filter:
49
57
  lambda x: x.is_relation and x.name not in filter_exclude,
50
58
  model_path[-1]._meta.get_fields()
51
59
  )
52
- res = {}
60
+ res = []
53
61
  for field in fields:
54
62
  if field.related_model in model_path:
55
63
  continue
@@ -60,13 +68,14 @@ class Filter:
60
68
  label = field.verbose_name
61
69
  except AttributeError:
62
70
  label = field.related_model._meta.verbose_name
63
- res[field.name] = {
64
- 'label': label,
71
+ res.append({
72
+ 'name': field.name,
73
+ 'label': str(label),
65
74
  'type': field.get_internal_type(),
66
75
  'null': field.null,
67
76
  'choices': [],
68
77
  'fields': self.get_fields(model_path_copy, rel_path)
69
- }
78
+ })
70
79
  return res
71
80
 
72
81
  def get_local_fields(self, model, path):
@@ -76,34 +85,39 @@ class Filter:
76
85
  lambda x: not x.is_relation and x.name not in filter_exclude,
77
86
  model._meta.get_fields()
78
87
  )
79
- res = {}
88
+ res = []
80
89
  for field in fields:
81
- res.update({
82
- f'{path}{"__" if path else ""}{field.name}': {
83
- 'label': field.verbose_name,
84
- 'type': field.get_internal_type(),
85
- 'choices': field.choices or [],
86
- 'null': field.null,
87
- 'step': hasattr(field, 'decimal_places')
88
- and self.cast_decimal_places_to_step(field.decimal_places)
89
- or 1
90
- }
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
91
102
  })
92
- for annotation in getattr(model, 'annotations', []):
93
- res.update({
94
- f'{path}{"__" if path else ""}_a_{annotation["name"]}': {
95
- 'label': annotation['label'],
96
- 'type': annotation['type'].__name__,
97
- 'choices': [],
98
- 'null': False,
99
- 'step': self.cast_decimal_places_to_step(annotation.get('step', 1))
100
- }
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')
101
115
  })
102
116
  return res
103
117
 
104
118
  def to_html(self):
105
119
  start = time.time()
106
- key = f'filter-terms-{self.model.__module__}.{self.model.__name__}'
120
+ key = f'filter-{self.model.__module__}.{self.model.__name__}-{get_language()}'
107
121
  html = cache.get(key)
108
122
  if html:
109
123
  _logger.info(f'Fetching Filter-Terms from cache in {time.time() - start} seconds')
@@ -111,22 +125,27 @@ class Filter:
111
125
  if not self.fields:
112
126
  self.fields = self.get_fields([self.model], '')
113
127
  html = ''
114
- for k, v in self.fields.items():
115
- html += self.field_params(k, v)
116
- 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
+ }
117
136
  cache.set(key, html, 60 * 15)
118
137
  _logger.info(f'Generated Filter-Terms in {time.time() - start} seconds')
119
- return html
138
+ return {'params': html['params'], 'field_paths': html['field_paths']}
120
139
 
121
- def field_params(self, key, value):
140
+ def field_params(self, field):
122
141
  params = ''
123
- params += self.params(key, value)
124
- for k, v in value.get('fields', {}).items():
125
- params += self.field_params(k, v)
142
+ params += self.params(field)
143
+ for f in field.get('fields', []):
144
+ params += self.field_params(f)
126
145
  return f"""
127
- <div class="query-param" tabindex="-1" data-param="{key}" data-param-label="{value['label']}">
128
- <p class="px-1 arrow">{value['label']}</p>
129
- <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']}">
130
149
  {params}
131
150
  </div>
132
151
  </div>
@@ -154,8 +173,8 @@ class Filter:
154
173
  for choice in choices
155
174
  ])
156
175
 
157
- def params(self, key, value):
158
- 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)
159
178
 
160
179
  def param(
161
180
  self, key: str, value: dict, param: str, data_type: str,
@@ -191,7 +210,6 @@ class Filter:
191
210
  html += param_div(inverted=True)
192
211
  return html
193
212
 
194
-
195
213
  def char_param(self, key, value):
196
214
  if value.get('choices'):
197
215
  return self.char_choice_param(key, value)
@@ -258,7 +276,10 @@ class Filter:
258
276
  return ''
259
277
 
260
278
  def null_param(self, key, value):
261
- options = self.parse_choices([('true', _('True')), ('false', _('False'))])
279
+ options = self.parse_choices([
280
+ ('true', _('True')),
281
+ ('false', _('False'))
282
+ ])
262
283
  return f"""
263
284
  <div id="filter-id-~{key}__isnull"
264
285
  class="query-param" tabindex="-1" data-type="selection"
@@ -274,3 +295,48 @@ class Filter:
274
295
 
275
296
  def no_param(self, key, value):
276
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)