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.
Files changed (200) hide show
  1. {accrete-0.0.36 → accrete-0.0.37}/PKG-INFO +1 -1
  2. accrete-0.0.37/accrete/annotation.py +46 -0
  3. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/__init__.py +5 -12
  4. accrete-0.0.37/accrete/contrib/ui/context.py +218 -0
  5. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/filter.py +105 -43
  6. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/css/accrete.css +128 -128
  7. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/css/accrete.css.map +1 -1
  8. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/css/accrete.scss +9 -9
  9. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/js/filter.js +23 -0
  10. accrete-0.0.37/accrete/contrib/ui/static/js/htmx.min.js +1 -0
  11. accrete-0.0.37/accrete/contrib/ui/templates/ui/layout.html +178 -0
  12. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/list.html +2 -2
  13. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/filter.html +28 -4
  14. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/header.html +5 -5
  15. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/table.html +1 -1
  16. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templatetags/accrete_ui.py +12 -1
  17. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/templates/user/login.html +6 -12
  18. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/views.py +10 -9
  19. {accrete-0.0.36 → accrete-0.0.37}/accrete/middleware.py +15 -0
  20. {accrete-0.0.36 → accrete-0.0.37}/accrete/models.py +9 -7
  21. {accrete-0.0.36 → accrete-0.0.37}/accrete/querystring.py +3 -9
  22. accrete-0.0.37/accrete/utils/models.py +14 -0
  23. {accrete-0.0.36 → accrete-0.0.37}/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.37}/.gitignore +0 -0
  28. {accrete-0.0.36 → accrete-0.0.37}/LICENSE +0 -0
  29. {accrete-0.0.36 → accrete-0.0.37}/README.md +0 -0
  30. {accrete-0.0.36 → accrete-0.0.37}/accrete/__init__.py +0 -0
  31. {accrete-0.0.36 → accrete-0.0.37}/accrete/admin.py +0 -0
  32. {accrete-0.0.36 → accrete-0.0.37}/accrete/apps.py +0 -0
  33. {accrete-0.0.36 → accrete-0.0.37}/accrete/config.py +0 -0
  34. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/__init__.py +0 -0
  35. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/__init__.py +0 -0
  36. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/admin.py +0 -0
  37. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/apps.py +0 -0
  38. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/forms.py +0 -0
  39. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/migrations/0001_initial.py +0 -0
  40. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/migrations/0002_alter_sequence_name.py +0 -0
  41. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/migrations/__init__.py +0 -0
  42. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/models.py +0 -0
  43. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/queries.py +0 -0
  44. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/tests.py +0 -0
  45. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/sequence/views.py +0 -0
  46. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/__init__.py +0 -0
  47. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/admin.py +0 -0
  48. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/apps.py +0 -0
  49. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/forms.py +0 -0
  50. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/migrations/0001_initial.py +0 -0
  51. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/migrations/__init__.py +0 -0
  52. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/models.py +0 -0
  53. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/tasks.py +0 -0
  54. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/tests.py +0 -0
  55. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/system_mail/views.py +0 -0
  56. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/admin.py +0 -0
  57. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/apps.py +0 -0
  58. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/elements.py +0 -0
  59. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/migrations/__init__.py +0 -0
  60. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/LICENSE +0 -0
  61. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/README.md +0 -0
  62. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/bulma.sass +0 -0
  63. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/css/bulma-rtl.css +0 -0
  64. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/css/bulma-rtl.css.map +0 -0
  65. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/css/bulma-rtl.min.css +0 -0
  66. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/css/bulma.css +0 -0
  67. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/css/bulma.css.map +0 -0
  68. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/css/bulma.min.css +0 -0
  69. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/package.json +0 -0
  70. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/base/_all.sass +0 -0
  71. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/base/animations.sass +0 -0
  72. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/base/generic.sass +0 -0
  73. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/base/helpers.sass +0 -0
  74. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/base/minireset.sass +0 -0
  75. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/_all.sass +0 -0
  76. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/breadcrumb.sass +0 -0
  77. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/card.sass +0 -0
  78. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/dropdown.sass +0 -0
  79. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/level.sass +0 -0
  80. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/media.sass +0 -0
  81. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/menu.sass +0 -0
  82. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/message.sass +0 -0
  83. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/modal.sass +0 -0
  84. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/navbar.sass +0 -0
  85. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/pagination.sass +0 -0
  86. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/panel.sass +0 -0
  87. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/components/tabs.sass +0 -0
  88. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/_all.sass +0 -0
  89. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/box.sass +0 -0
  90. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/button.sass +0 -0
  91. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/container.sass +0 -0
  92. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/content.sass +0 -0
  93. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/form.sass +0 -0
  94. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/icon.sass +0 -0
  95. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/image.sass +0 -0
  96. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/notification.sass +0 -0
  97. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/other.sass +0 -0
  98. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/progress.sass +0 -0
  99. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/table.sass +0 -0
  100. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/tag.sass +0 -0
  101. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/elements/title.sass +0 -0
  102. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/_all.sass +0 -0
  103. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/checkbox-radio.sass +0 -0
  104. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/file.sass +0 -0
  105. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/input-textarea.sass +0 -0
  106. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/select.sass +0 -0
  107. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/shared.sass +0 -0
  108. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/form/tools.sass +0 -0
  109. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/grid/_all.sass +0 -0
  110. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/grid/columns.sass +0 -0
  111. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/grid/tiles.sass +0 -0
  112. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/_all.sass +0 -0
  113. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/color.sass +0 -0
  114. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/flexbox.sass +0 -0
  115. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/float.sass +0 -0
  116. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/other.sass +0 -0
  117. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/overflow.sass +0 -0
  118. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/position.sass +0 -0
  119. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/spacing.sass +0 -0
  120. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/typography.sass +0 -0
  121. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/helpers/visibility.sass +0 -0
  122. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/layout/_all.sass +0 -0
  123. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/layout/footer.sass +0 -0
  124. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/layout/hero.sass +0 -0
  125. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/layout/section.sass +0 -0
  126. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/_all.sass +0 -0
  127. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/animations.sass +0 -0
  128. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/controls.sass +0 -0
  129. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/derived-variables.sass +0 -0
  130. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/extends.sass +0 -0
  131. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/functions.sass +0 -0
  132. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/initial-variables.sass +0 -0
  133. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/bulma/sass/utilities/mixins.sass +0 -0
  134. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/css/accrete.css.bak +0 -0
  135. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/css/icons.css +0 -0
  136. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/icons/Logo.svg +0 -0
  137. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/icons/accrete.svg +0 -0
  138. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/static/js/list.js +0 -0
  139. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/attrs.html +0 -0
  140. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/email.html +0 -0
  141. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/file.html +0 -0
  142. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/input.html +0 -0
  143. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/select.html +0 -0
  144. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/text.html +0 -0
  145. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/django/forms/widgets/textarea.html +0 -0
  146. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/detail.html +0 -0
  147. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/form.html +0 -0
  148. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/form_errors.html +0 -0
  149. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/form_modal.html +0 -0
  150. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/onchange_form.html +0 -0
  151. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/pagination_detail.html +0 -0
  152. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/pagination_list.html +0 -0
  153. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/table_field.html +0 -0
  154. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/table_field_float.html +0 -0
  155. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/table_field_monetary.html +0 -0
  156. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templates/ui/partials/table_field_string.html +0 -0
  157. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/templatetags/__init__.py +0 -0
  158. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/tests.py +0 -0
  159. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/urls.py +0 -0
  160. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/ui/views.py +0 -0
  161. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/__init__.py +0 -0
  162. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/admin.py +0 -0
  163. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/apps.py +0 -0
  164. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/forms.py +0 -0
  165. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/locale/de/LC_MESSAGES/django.mo +0 -0
  166. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/locale/de/LC_MESSAGES/django.po +0 -0
  167. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/middleware.py +0 -0
  168. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/migrations/0001_initial.py +0 -0
  169. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/migrations/__init__.py +0 -0
  170. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/models.py +0 -0
  171. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/templates/user/accrete_navbar_end_dropdown.html +0 -0
  172. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/templates/user/change_email.html +0 -0
  173. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/templates/user/change_password.html +0 -0
  174. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/templates/user/user_detail.html +0 -0
  175. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/templates/user/user_form.html +0 -0
  176. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/tests.py +0 -0
  177. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user/urls.py +0 -0
  178. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/__init__.py +0 -0
  179. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/admin.py +0 -0
  180. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/apps.py +0 -0
  181. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/config.py +0 -0
  182. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/forms.py +0 -0
  183. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/migrations/0001_initial.py +0 -0
  184. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/migrations/__init__.py +0 -0
  185. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/models.py +0 -0
  186. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/templates/user_registration/mail_templates/confirmation_mail.html +0 -0
  187. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/templates/user_registration/registration.html +0 -0
  188. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/tests.py +0 -0
  189. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/urls.py +0 -0
  190. {accrete-0.0.36 → accrete-0.0.37}/accrete/contrib/user_registration/views.py +0 -0
  191. {accrete-0.0.36 → accrete-0.0.37}/accrete/forms.py +0 -0
  192. {accrete-0.0.36 → accrete-0.0.37}/accrete/migrations/0001_initial.py +0 -0
  193. {accrete-0.0.36 → accrete-0.0.37}/accrete/migrations/0002_initial.py +0 -0
  194. {accrete-0.0.36 → accrete-0.0.37}/accrete/migrations/__init__.py +0 -0
  195. {accrete-0.0.36 → accrete-0.0.37}/accrete/queries.py +0 -0
  196. {accrete-0.0.36 → accrete-0.0.37}/accrete/tenant.py +0 -0
  197. {accrete-0.0.36 → accrete-0.0.37}/accrete/tests.py +0 -0
  198. {accrete-0.0.36 → accrete-0.0.37}/accrete/utils/__init__.py +0 -0
  199. {accrete-0.0.36 → accrete-0.0.37}/accrete/utils/dates.py +0 -0
  200. {accrete-0.0.36 → 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.36
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
+
@@ -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,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.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': 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
- 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)