accrete 0.0.142__tar.gz → 0.0.144__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 (293) hide show
  1. {accrete-0.0.142 → accrete-0.0.144}/.gitignore +2 -1
  2. {accrete-0.0.142 → accrete-0.0.144}/PKG-INFO +2 -2
  3. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/log/admin.py +1 -1
  4. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/system_mail/tasks.py +4 -5
  5. accrete-0.0.144/accrete/contrib/ui/__init__.py +19 -0
  6. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/filter.py +22 -13
  7. accrete-0.0.144/accrete/contrib/ui/response.py +540 -0
  8. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/css/accrete.css +69 -12
  9. accrete-0.0.144/accrete/contrib/ui/static/css/accrete.css.map +7 -0
  10. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/css/accrete.scss +370 -300
  11. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/ui/content_right.html +2 -2
  12. accrete-0.0.144/accrete/contrib/ui/templates/ui/detail.html +11 -0
  13. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/ui/filter/filter.html +5 -5
  14. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/ui/filter/query_tags.html +1 -1
  15. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/ui/form_error.html +1 -1
  16. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/ui/layout.html +69 -64
  17. accrete-0.0.144/accrete/contrib/ui/templates/ui/list.html +45 -0
  18. accrete-0.0.144/accrete/contrib/ui/templates/ui/list_update.html +9 -0
  19. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/ui/message.html +1 -1
  20. accrete-0.0.144/accrete/contrib/ui/templates/ui/modal.html +48 -0
  21. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/ui/table_row_update.html +2 -2
  22. accrete-0.0.144/accrete/contrib/ui/templates/ui/templatetags/field.html +19 -0
  23. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/ui/widgets/model_search_select.html +7 -7
  24. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/ui/widgets/model_search_select_multi.html +3 -3
  25. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templatetags/ui.py +24 -2
  26. accrete-0.0.144/accrete/contrib/user/templates/user/password_forgotten.html +1 -0
  27. accrete-0.0.144/accrete/contrib/user/templates/user/user_preferences.html +43 -0
  28. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/views.py +36 -37
  29. accrete-0.0.144/accrete/migrations/0008_alter_member_access_groups_and_more.py +23 -0
  30. accrete-0.0.144/accrete/migrations/__init__.py +0 -0
  31. {accrete-0.0.142 → accrete-0.0.144}/accrete/utils/__init__.py +1 -0
  32. {accrete-0.0.142 → accrete-0.0.144}/accrete/utils/forms.py +48 -28
  33. {accrete-0.0.142 → accrete-0.0.144}/accrete/utils/views.py +1 -2
  34. {accrete-0.0.142 → accrete-0.0.144}/accrete/views.py +2 -3
  35. {accrete-0.0.142 → accrete-0.0.144}/pyproject.toml +2 -2
  36. accrete-0.0.142/accrete/contrib/ui/__init__.py +0 -17
  37. accrete-0.0.142/accrete/contrib/ui/context.py +0 -123
  38. accrete-0.0.142/accrete/contrib/ui/static/css/.sass-cache/15adf1eed05371361b08787c918a7f18fc15be79/accrete.scssc +0 -0
  39. accrete-0.0.142/accrete/contrib/ui/static/css/accrete.css.map +0 -7
  40. accrete-0.0.142/accrete/contrib/ui/templates/ui/list.html +0 -43
  41. accrete-0.0.142/accrete/contrib/ui/templates/ui/list_update.html +0 -3
  42. accrete-0.0.142/accrete/contrib/ui/templates/ui/modal.html +0 -39
  43. accrete-0.0.142/accrete/contrib/ui/templates/ui/templatetags/field.html +0 -6
  44. accrete-0.0.142/accrete/contrib/ui/utils.py +0 -77
  45. accrete-0.0.142/accrete/contrib/user/templates/user/user_form.html +0 -58
  46. {accrete-0.0.142 → accrete-0.0.144}/LICENSE +0 -0
  47. {accrete-0.0.142 → accrete-0.0.144}/README.md +0 -0
  48. {accrete-0.0.142 → accrete-0.0.144}/accrete/__init__.py +0 -0
  49. {accrete-0.0.142 → accrete-0.0.144}/accrete/admin.py +0 -0
  50. {accrete-0.0.142 → accrete-0.0.144}/accrete/apps.py +0 -0
  51. {accrete-0.0.142 → accrete-0.0.144}/accrete/config.py +0 -0
  52. {accrete-0.0.142 → accrete-0.0.144}/accrete/context_processors.py +0 -0
  53. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/__init__.py +0 -0
  54. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/country/__init__.py +0 -0
  55. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/country/admin.py +0 -0
  56. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/country/apps.py +0 -0
  57. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/country/migrations/0001_initial.py +0 -0
  58. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/country/migrations/0002_auto_20250219_1759.py +0 -0
  59. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/country/migrations/0003_alter_country_options.py +0 -0
  60. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/country/migrations/__init__.py +0 -0
  61. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/country/models.py +0 -0
  62. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/log/__init__.py +0 -0
  63. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/log/apps.py +0 -0
  64. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/log/config.py +0 -0
  65. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/log/helper.py +0 -0
  66. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/log/migrations/0001_initial.py +0 -0
  67. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/log/migrations/0002_log_user_alter_log_new_value_type_and_more.py +0 -0
  68. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/log/migrations/0003_alter_log_tenant.py +0 -0
  69. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/log/migrations/0004_logconfig_logconfigfield.py +0 -0
  70. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/log/migrations/0005_logconfig_exclude_fields_and_more.py +0 -0
  71. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/log/migrations/__init__.py +0 -0
  72. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/log/models.py +0 -0
  73. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/log/queries.py +0 -0
  74. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/log/signals.py +0 -0
  75. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/log/tests.py +0 -0
  76. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/log/views.py +0 -0
  77. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/sequence/__init__.py +0 -0
  78. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/sequence/admin.py +0 -0
  79. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/sequence/apps.py +0 -0
  80. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/sequence/forms.py +0 -0
  81. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/sequence/migrations/0001_initial.py +0 -0
  82. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/sequence/migrations/0002_alter_sequence_name.py +0 -0
  83. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/sequence/migrations/0003_alter_sequence_tenant.py +0 -0
  84. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/sequence/migrations/__init__.py +0 -0
  85. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/sequence/models.py +0 -0
  86. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/sequence/queries.py +0 -0
  87. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/sequence/tests.py +0 -0
  88. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/sequence/views.py +0 -0
  89. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/system_mail/__init__.py +0 -0
  90. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/system_mail/admin.py +0 -0
  91. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/system_mail/apps.py +0 -0
  92. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/system_mail/forms.py +0 -0
  93. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/system_mail/migrations/0001_initial.py +0 -0
  94. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/system_mail/migrations/__init__.py +0 -0
  95. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/system_mail/models.py +0 -0
  96. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/system_mail/tests.py +0 -0
  97. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/system_mail/views.py +0 -0
  98. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/admin.py +0 -0
  99. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/apps.py +0 -0
  100. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/middleware.py +0 -0
  101. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/migrations/__init__.py +0 -0
  102. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/models.py +0 -0
  103. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/LICENSE +0 -0
  104. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/README.md +0 -0
  105. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/bulma.scss +0 -0
  106. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/css/bulma.css +0 -0
  107. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/css/bulma.css.map +0 -0
  108. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/css/bulma.min.css +0 -0
  109. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/css/versions/bulma-no-dark-mode.css +0 -0
  110. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/css/versions/bulma-no-dark-mode.css.map +0 -0
  111. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/css/versions/bulma-no-dark-mode.min.css +0 -0
  112. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers-prefixed.css +0 -0
  113. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers-prefixed.css.map +0 -0
  114. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers-prefixed.min.css +0 -0
  115. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers.css +0 -0
  116. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers.css.map +0 -0
  117. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers.min.css +0 -0
  118. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/css/versions/bulma-prefixed.css +0 -0
  119. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/css/versions/bulma-prefixed.css.map +0 -0
  120. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/css/versions/bulma-prefixed.min.css +0 -0
  121. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/package.json +0 -0
  122. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/_index.scss +0 -0
  123. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/base/_index.scss +0 -0
  124. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/base/animations.scss +0 -0
  125. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/base/generic.scss +0 -0
  126. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/base/minireset.scss +0 -0
  127. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/base/skeleton.scss +0 -0
  128. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/components/_index.scss +0 -0
  129. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/components/breadcrumb.scss +0 -0
  130. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/components/card.scss +0 -0
  131. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/components/dropdown.scss +0 -0
  132. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/components/menu.scss +0 -0
  133. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/components/message.scss +0 -0
  134. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/components/modal.scss +0 -0
  135. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/components/navbar.scss +0 -0
  136. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/components/pagination.scss +0 -0
  137. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/components/panel.scss +0 -0
  138. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/components/tabs.scss +0 -0
  139. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/elements/_index.scss +0 -0
  140. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/elements/block.scss +0 -0
  141. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/elements/box.scss +0 -0
  142. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/elements/button.scss +0 -0
  143. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/elements/content.scss +0 -0
  144. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/elements/delete.scss +0 -0
  145. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/elements/icon.scss +0 -0
  146. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/elements/image.scss +0 -0
  147. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/elements/loader.scss +0 -0
  148. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/elements/notification.scss +0 -0
  149. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/elements/progress.scss +0 -0
  150. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/elements/table.scss +0 -0
  151. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/elements/tag.scss +0 -0
  152. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/elements/title.scss +0 -0
  153. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/form/_index.scss +0 -0
  154. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/form/checkbox-radio.scss +0 -0
  155. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/form/file.scss +0 -0
  156. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/form/input-textarea.scss +0 -0
  157. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/form/select.scss +0 -0
  158. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/form/shared.scss +0 -0
  159. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/form/tools.scss +0 -0
  160. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/grid/_index.scss +0 -0
  161. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/grid/columns.scss +0 -0
  162. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/grid/grid.scss +0 -0
  163. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/helpers/_index.scss +0 -0
  164. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/helpers/aspect-ratio.scss +0 -0
  165. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/helpers/border.scss +0 -0
  166. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/helpers/color.scss +0 -0
  167. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/helpers/flexbox.scss +0 -0
  168. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/helpers/float.scss +0 -0
  169. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/helpers/gap.scss +0 -0
  170. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/helpers/other.scss +0 -0
  171. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/helpers/overflow.scss +0 -0
  172. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/helpers/position.scss +0 -0
  173. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/helpers/spacing.scss +0 -0
  174. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/helpers/typography.scss +0 -0
  175. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/helpers/visibility.scss +0 -0
  176. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/layout/_index.scss +0 -0
  177. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/layout/container.scss +0 -0
  178. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/layout/footer.scss +0 -0
  179. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/layout/hero.scss +0 -0
  180. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/layout/level.scss +0 -0
  181. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/layout/media.scss +0 -0
  182. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/layout/section.scss +0 -0
  183. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/themes/_index.scss +0 -0
  184. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/themes/dark.scss +0 -0
  185. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/themes/light.scss +0 -0
  186. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/themes/setup.scss +0 -0
  187. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/utilities/_index.scss +0 -0
  188. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/utilities/controls.scss +0 -0
  189. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/utilities/css-variables.scss +0 -0
  190. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/utilities/derived-variables.scss +0 -0
  191. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/utilities/extends.scss +0 -0
  192. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/utilities/functions.scss +0 -0
  193. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/utilities/initial-variables.scss +0 -0
  194. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/sass/utilities/mixins.scss +0 -0
  195. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/versions/bulma-no-dark-mode.scss +0 -0
  196. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/versions/bulma-no-helpers-prefixed.scss +0 -0
  197. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/versions/bulma-no-helpers.scss +0 -0
  198. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/bulma/versions/bulma-prefixed.scss +0 -0
  199. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/css/fa.css +0 -0
  200. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/css/icons.css +0 -0
  201. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/icons/Logo.svg +0 -0
  202. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/icons/accrete.svg +0 -0
  203. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/js/alpine-3.14.9.js +0 -0
  204. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/js/alpine-focus-3.14.9.js +0 -0
  205. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/js/alpine-sort-3.14.9.js +0 -0
  206. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/js/filter.js +0 -0
  207. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/js/htmx.min.js +0 -0
  208. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/webfonts/fa-brands-400.ttf +0 -0
  209. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/webfonts/fa-brands-400.woff2 +0 -0
  210. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/webfonts/fa-regular-400.ttf +0 -0
  211. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/webfonts/fa-regular-400.woff2 +0 -0
  212. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/webfonts/fa-solid-900.ttf +0 -0
  213. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/webfonts/fa-solid-900.woff2 +0 -0
  214. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/webfonts/fa-v4compatibility.ttf +0 -0
  215. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/static/webfonts/fa-v4compatibility.woff2 +0 -0
  216. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/django/forms/widgets/attrs.html +0 -0
  217. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/django/forms/widgets/date.html +0 -0
  218. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/django/forms/widgets/email.html +0 -0
  219. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/django/forms/widgets/file.html +0 -0
  220. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/django/forms/widgets/input.html +0 -0
  221. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/django/forms/widgets/select.html +0 -0
  222. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/django/forms/widgets/text.html +0 -0
  223. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/django/forms/widgets/textarea.html +0 -0
  224. /accrete-0.0.142/accrete/contrib/ui/templatetags/__init__.py → /accrete-0.0.144/accrete/contrib/ui/templates/ui/content.html +0 -0
  225. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/ui/favicon.html +0 -0
  226. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/ui/filter/query_input.html +0 -0
  227. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/ui/filter/query_operator.html +0 -0
  228. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/ui/filter/query_params.html +0 -0
  229. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/ui/oob.html +0 -0
  230. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/ui/table.html +0 -0
  231. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/templates/ui/widgets/model_search_select_options.html +0 -0
  232. {accrete-0.0.142/accrete/contrib/user → accrete-0.0.144/accrete/contrib/ui/templatetags}/__init__.py +0 -0
  233. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/tests.py +0 -0
  234. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/urls.py +0 -0
  235. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/views.py +0 -0
  236. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/widgets/__init__.py +0 -0
  237. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/ui/widgets/search_select.py +0 -0
  238. {accrete-0.0.142/accrete/contrib/user/migrations → accrete-0.0.144/accrete/contrib/user}/__init__.py +0 -0
  239. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/admin.py +0 -0
  240. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/apps.py +0 -0
  241. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/auth_backends.py +0 -0
  242. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/forms.py +0 -0
  243. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/locale/de/LC_MESSAGES/django.mo +0 -0
  244. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/locale/de/LC_MESSAGES/django.po +0 -0
  245. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/middleware.py +0 -0
  246. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/migrations/0001_initial.py +0 -0
  247. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/migrations/0002_user_theme.py +0 -0
  248. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/migrations/0003_alter_user_email.py +0 -0
  249. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/migrations/0004_user_login_alter_user_email_user_email_or_login_set.py +0 -0
  250. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/migrations/0005_remove_user_email_or_login_set_and_more.py +0 -0
  251. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/migrations/0006_remove_user_email_or_login_set_user_is_managed_and_more.py +0 -0
  252. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/migrations/0007_user_managed_login.py +0 -0
  253. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/migrations/0008_remove_user_no_email_for_managed_user_and_more.py +0 -0
  254. {accrete-0.0.142/accrete/contrib/user_registration → accrete-0.0.144/accrete/contrib/user/migrations}/__init__.py +0 -0
  255. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/models.py +0 -0
  256. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/templates/user/accrete_navbar_end_dropdown.html +0 -0
  257. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/templates/user/change_email.html +0 -0
  258. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/templates/user/change_password.html +0 -0
  259. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/templates/user/login.html +0 -0
  260. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/tests.py +0 -0
  261. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user/urls.py +0 -0
  262. {accrete-0.0.142/accrete/contrib/user_registration/migrations → accrete-0.0.144/accrete/contrib/user_registration}/__init__.py +0 -0
  263. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user_registration/admin.py +0 -0
  264. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user_registration/apps.py +0 -0
  265. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user_registration/config.py +0 -0
  266. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user_registration/forms.py +0 -0
  267. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user_registration/migrations/0001_initial.py +0 -0
  268. {accrete-0.0.142/accrete → accrete-0.0.144/accrete/contrib/user_registration}/migrations/__init__.py +0 -0
  269. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user_registration/models.py +0 -0
  270. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user_registration/templates/user_registration/mail_templates/confirmation_mail.html +0 -0
  271. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user_registration/templates/user_registration/registration.html +0 -0
  272. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user_registration/tests.py +0 -0
  273. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user_registration/urls.py +0 -0
  274. {accrete-0.0.142 → accrete-0.0.144}/accrete/contrib/user_registration/views.py +0 -0
  275. {accrete-0.0.142 → accrete-0.0.144}/accrete/fields.py +0 -0
  276. {accrete-0.0.142 → accrete-0.0.144}/accrete/forms.py +0 -0
  277. {accrete-0.0.142 → accrete-0.0.144}/accrete/managers.py +0 -0
  278. {accrete-0.0.142 → accrete-0.0.144}/accrete/middleware.py +0 -0
  279. {accrete-0.0.142 → accrete-0.0.144}/accrete/migrations/0001_initial.py +0 -0
  280. {accrete-0.0.142 → accrete-0.0.144}/accrete/migrations/0002_initial.py +0 -0
  281. {accrete-0.0.142 → accrete-0.0.144}/accrete/migrations/0003_remove_member_name.py +0 -0
  282. {accrete-0.0.142 → accrete-0.0.144}/accrete/migrations/0004_rename_accessgroupmember_memberaccessgrouprel_and_more.py +0 -0
  283. {accrete-0.0.142 → accrete-0.0.144}/accrete/migrations/0005_accessgroup_apply_on_alter_member_access_groups_and_more.py +0 -0
  284. {accrete-0.0.142 → accrete-0.0.144}/accrete/migrations/0006_alter_member_user.py +0 -0
  285. {accrete-0.0.142 → accrete-0.0.144}/accrete/migrations/0007_accessgroup_description.py +0 -0
  286. {accrete-0.0.142 → accrete-0.0.144}/accrete/models.py +0 -0
  287. {accrete-0.0.142 → accrete-0.0.144}/accrete/storage.py +0 -0
  288. {accrete-0.0.142 → accrete-0.0.144}/accrete/tenant.py +0 -0
  289. {accrete-0.0.142 → accrete-0.0.144}/accrete/tests.py +0 -0
  290. {accrete-0.0.142 → accrete-0.0.144}/accrete/urls.py +0 -0
  291. {accrete-0.0.142 → accrete-0.0.144}/accrete/utils/dates.py +0 -0
  292. {accrete-0.0.142 → accrete-0.0.144}/accrete/utils/log.py +0 -0
  293. {accrete-0.0.142 → accrete-0.0.144}/accrete/utils/models.py +0 -0
@@ -5,4 +5,5 @@ build/
5
5
  accrete.egg-info/
6
6
  *.py[cod]
7
7
  *.log
8
- *.sqlite3
8
+ *.sqlite3
9
+ *.scssc
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: accrete
3
- Version: 0.0.142
3
+ Version: 0.0.144
4
4
  Summary: Django Shared Schema Multi Tenant
5
5
  Author-email: Benedikt Jilek <benedikt.jilek@pm.me>
6
6
  License: Copyright (c) 2025 Benedikt Jilek
@@ -31,7 +31,7 @@ Classifier: Operating System :: OS Independent
31
31
  Classifier: Programming Language :: Python :: 3
32
32
  Classifier: Topic :: Internet :: WWW/HTTP
33
33
  Requires-Python: >=3.10
34
- Requires-Dist: django>=5.1
34
+ Requires-Dist: django>=5.2
35
35
  Provides-Extra: contrib
36
36
  Requires-Dist: celery>=5.3.4; extra == 'contrib'
37
37
  Requires-Dist: django-celery-beat; extra == 'contrib'
@@ -44,4 +44,4 @@ class LogAdmin(admin.ModelAdmin):
44
44
 
45
45
 
46
46
  admin.site.register(models.LogConfig, LogConfigAdmin)
47
- admin.site.register(models.Log, LogAdmin)
47
+ admin.site.register(models.Log, LogAdmin)
@@ -36,8 +36,7 @@ def run_mail_queue():
36
36
  _logger.error(f'Failed to send system mail\n{error_str}')
37
37
  email.error = error_str
38
38
  email.save()
39
- continue
40
-
41
- email.sent = True
42
- email.error = None
43
- email.save()
39
+ else:
40
+ email.sent = True
41
+ email.error = None
42
+ email.save()
@@ -0,0 +1,19 @@
1
+ from . import widgets
2
+ from .filter import Filter
3
+ from .response import (
4
+ Response,
5
+ WindowResponse,
6
+ ListResponse,
7
+ ListEntryResponse,
8
+ TableResponse,
9
+ TableRowResponse,
10
+ DetailResponse,
11
+ ModalResponse,
12
+ OobResponse,
13
+ TriggerResponse,
14
+ ClientTrigger,
15
+ search_select_response,
16
+ message_response,
17
+ add_trigger,
18
+ update
19
+ )
@@ -46,12 +46,21 @@ class Filter:
46
46
  TYPES_TIME = ['TimeField']
47
47
 
48
48
  def __init__(
49
- self, model, query: QueryDict, default_lookup: str = None
49
+ self,
50
+ model,
51
+ query_dict: QueryDict,
52
+ default_lookup: str = None,
53
+ query: Q = None,
54
+ select_related: list[str] = None,
55
+ prefetch_related: list[str] = None
50
56
  ):
51
57
  self.model = model
52
- self.query = query
58
+ self.query_dict = query_dict
53
59
  self.model_name = f'{self.model._meta.app_label}.{self.model._meta.model_name}'
54
- seen_models = query.get('models', '').split(',')
60
+ self.query = query or Q()
61
+ self.select_related = select_related
62
+ self.prefetch_related = prefetch_related
63
+ seen_models = query_dict.get('models', '').split(',')
55
64
  self.seen_models = [
56
65
  model for model in seen_models
57
66
  if model and model != self.model_name
@@ -67,14 +76,14 @@ class Filter:
67
76
  ) -> paginator.Page:
68
77
  return page_from_querystring(
69
78
  self.model,
70
- self.query,
71
- select_related=select_related,
72
- prefetch_related=prefetch_related,
73
- query=query
79
+ self.query_dict,
80
+ select_related=select_related or self.select_related,
81
+ prefetch_related=prefetch_related or self.prefetch_related,
82
+ query=query or self.query
74
83
  )
75
84
 
76
85
  def get_queryset(self) -> QuerySet:
77
- return filter_from_querystring(self.model, self.query)
86
+ return filter_from_querystring(self.model, self.query_dict)
78
87
 
79
88
  def query_params(self):
80
89
  fields = filter(
@@ -82,7 +91,7 @@ class Filter:
82
91
  self.model._meta.get_fields()
83
92
  )
84
93
  params = []
85
- path = self.query.get('path', '')
94
+ path = self.query_dict.get('path', '')
86
95
  for field in fields:
87
96
  field_params = self._get_field_params(field)
88
97
  if not field.is_relation or field_params['model_name'] not in self.seen_models:
@@ -115,7 +124,7 @@ class Filter:
115
124
  if not lookup:
116
125
  ctx = {
117
126
  'verbose_lookup': str(_('Select an attribute')),
118
- 'query_dict': self.query,
127
+ 'query_dict': self.query_dict,
119
128
  'model_name': self.model_name
120
129
  }
121
130
  return render_to_string('ui/filter/query_input.html', ctx)
@@ -133,14 +142,14 @@ class Filter:
133
142
  'lookup': prefix + lookup,
134
143
  'input': input_params,
135
144
  'lookup_operator': lookup_operator,
136
- 'query_dict': self.query,
145
+ 'query_dict': self.query_dict,
137
146
  'model_name': self.model_name
138
147
  }
139
148
  return render_to_string('ui/filter/query_input.html', ctx)
140
149
 
141
150
  def query_tags(self, query: list | dict = None, operator: str = None):
142
151
  if not query:
143
- query = json.loads(self.query.get('q', '[]'))
152
+ query = json.loads(self.query_dict.get('q', '[]'))
144
153
  if isinstance(query, dict):
145
154
  query = [query]
146
155
  operator = operator or '&'
@@ -291,7 +300,7 @@ class Filter:
291
300
  return str(labels[lookup])
292
301
 
293
302
  def _get_field_params(self, field):
294
- path = self.query.get('path', '')
303
+ path = self.query_dict.get('path', '')
295
304
  label = ''
296
305
  name = field.name
297
306
  if path:
@@ -0,0 +1,540 @@
1
+ import re
2
+ import ast
3
+ import json
4
+ from dataclasses import dataclass
5
+
6
+ from django.core import paginator
7
+ from django.db.models import Model
8
+ from django.http import HttpResponse
9
+ from django.template.loader import render_to_string
10
+ from django.utils.translation import gettext_lazy as _
11
+ from accrete.contrib.ui import Filter
12
+
13
+
14
+ class Response:
15
+
16
+ def __init__(self, *, template: str, context: dict):
17
+ self.template = template
18
+ self.context = context
19
+
20
+ @staticmethod
21
+ def add_trigger(response):
22
+ pass
23
+
24
+ def render(self, request) -> str:
25
+ return render_to_string(
26
+ template_name=self.template, context=self.context, request=request
27
+ )
28
+
29
+ def response(self, request, extra_content: str = None, replace_body: bool = False) -> HttpResponse:
30
+ extra_content = extra_content or ''
31
+ res = HttpResponse(content=(
32
+ self.render(request)
33
+ + render_to_string('ui/message.html', request=request)
34
+ + extra_content
35
+ ))
36
+ self.add_trigger(res)
37
+ if replace_body:
38
+ res.headers['HX-Retarget'] = 'body'
39
+ res.headers['HX-Reswap'] = 'innerHTML'
40
+ res.headers['HX-Push-Url'] = request.path
41
+ return res
42
+
43
+
44
+ class OobResponse(Response):
45
+
46
+ oob_template = 'ui/oob.html'
47
+
48
+ def __init__(self, *, template: str, context: dict, swap: str, tag: str = 'div'):
49
+ super().__init__(template=self.oob_template, context=context)
50
+ self.context.update({'oob': {
51
+ 'template': template,
52
+ 'swap': swap,
53
+ 'tag': tag
54
+ }})
55
+
56
+
57
+ class WindowResponse(Response):
58
+
59
+ base_template = 'ui/layout.html'
60
+
61
+ def __init__(
62
+ self, *,
63
+ title: str,
64
+ context: dict,
65
+ overview_template: str = None,
66
+ header_template: str = None,
67
+ panel_template: str = None,
68
+ is_centered: bool = False
69
+ ):
70
+ super().__init__(template=self.base_template, context=context)
71
+ self.panel_template = panel_template
72
+ if 'has_panel' not in self.context.keys():
73
+ self.context.update(has_panel=self._has_panel())
74
+ self.context.update({
75
+ 'title': title,
76
+ 'overview_template': overview_template,
77
+ 'header_template': header_template,
78
+ 'panel_template': panel_template,
79
+ 'is_centered': is_centered
80
+ })
81
+
82
+ def _has_panel(self):
83
+ return bool(self.panel_template)
84
+
85
+ def response(self, request, extra_content: str = None, replace_body: bool = True) -> HttpResponse:
86
+ return super().response(request, extra_content, replace_body)
87
+
88
+
89
+ class ListResponse(WindowResponse):
90
+
91
+ def __init__(
92
+ self, *,
93
+ title: str,
94
+ context: dict,
95
+ list_entry_template: str = None,
96
+ page: paginator.Page = None,
97
+ ui_filter: Filter = None,
98
+ endless_scroll: bool = True,
99
+ header_template: str = None,
100
+ panel_template: str = None,
101
+ column_count: int = 1,
102
+ column_height: str = '150px',
103
+ overview_template: str = 'ui/list.html'
104
+ ):
105
+ assert page is not None or ui_filter is not None, _(
106
+ 'Argument page or ui_filter must be supplied'
107
+ )
108
+ self.ui_filter = ui_filter
109
+ self.overview_template = overview_template
110
+ super().__init__(
111
+ title=title,
112
+ context=context,
113
+ overview_template=self.overview_template,
114
+ header_template=header_template,
115
+ panel_template=panel_template,
116
+ )
117
+ if ui_filter and not page:
118
+ page = ui_filter.get_page()
119
+ self.context.update({
120
+ 'list_entry_template': list_entry_template,
121
+ 'page': page,
122
+ 'ui_filter': ui_filter,
123
+ 'endless_scroll': endless_scroll,
124
+ 'column_count': column_count,
125
+ 'column_height': column_height
126
+ })
127
+
128
+ def _has_panel(self):
129
+ return bool(self.panel_template or self.ui_filter)
130
+
131
+ def response(self, request, extra_content: str = None, replace_body: bool = False) -> HttpResponse:
132
+ return super().response(request, extra_content, replace_body)
133
+
134
+
135
+ class ListEntryResponse(Response):
136
+
137
+ base_template = 'ui/list_update.html'
138
+
139
+ def __init__(
140
+ self, *,
141
+ instance: Model,
142
+ list_entry_template: str,
143
+ context: dict = None,
144
+ page: paginator.Page,
145
+ is_new: bool,
146
+ column_count: int = 1,
147
+ column_height: str = '150px',
148
+ ):
149
+ self.page = page
150
+ super().__init__(template=self.base_template, context=context or {})
151
+ self.context.update({
152
+ 'instance': instance,
153
+ 'list_entry_template': list_entry_template,
154
+ 'is_new': is_new,
155
+ 'column_count': column_count,
156
+ 'column_height': column_height
157
+ })
158
+
159
+ def render(self, request) -> str:
160
+ res = super().render(request)
161
+ if self.page:
162
+ pagination_update = OobResponse(
163
+ template='ui/layout.html#pagination',
164
+ swap='innerHTML:#pagination',
165
+ context=dict(page=self.page)
166
+ ).render(request)
167
+ res += pagination_update
168
+ return res
169
+
170
+
171
+ class TableResponse(WindowResponse):
172
+
173
+ def __init__(
174
+ self, *,
175
+ title: str,
176
+ context: dict,
177
+ object_label: str,
178
+ fields: list[str],
179
+ footer: dict = None,
180
+ page: paginator.Page = None,
181
+ ui_filter: Filter = None,
182
+ endless_scroll: bool = True,
183
+ header_template: str = None,
184
+ panel_template: str = None,
185
+ overview_template: str = 'ui/table.html'
186
+ ):
187
+ assert page is not None or ui_filter is not None, _(
188
+ 'Argument page or ui_filter must be supplied'
189
+ )
190
+ self.ui_filter = ui_filter
191
+ self.overview_template = overview_template
192
+ super().__init__(
193
+ title=title,
194
+ context=context,
195
+ overview_template=self.overview_template,
196
+ header_template=header_template,
197
+ panel_template=panel_template
198
+ )
199
+ if ui_filter and not page:
200
+ page = ui_filter.get_page()
201
+ self.context.update({
202
+ 'page': page,
203
+ 'ui_filter': ui_filter,
204
+ 'endless_scroll': endless_scroll,
205
+ 'fields': fields,
206
+ 'object_label': object_label,
207
+ 'footer': footer
208
+ })
209
+
210
+ def _has_panel(self):
211
+ return bool(self.panel_template or self.ui_filter)
212
+
213
+ def response(self, request, extra_content: str = None, replace_body: bool = False) -> HttpResponse:
214
+ return super().response(request, extra_content, replace_body)
215
+
216
+
217
+ class TableRowResponse(Response):
218
+
219
+ base_template = 'ui/table_row_update.html'
220
+
221
+ def __init__(
222
+ self, *,
223
+ instance: Model,
224
+ fields: list[str],
225
+ footer: dict = None,
226
+ page: paginator.Page = None,
227
+ ):
228
+ self.page = page
229
+ context = {
230
+ 'instance': instance,
231
+ 'fields': fields,
232
+ 'footer': footer,
233
+ }
234
+ super().__init__(template=self.base_template, context=context)
235
+
236
+ def render(self, request) -> str:
237
+ res = super().render(request)
238
+ if self.page:
239
+ pagination_update = OobResponse(
240
+ template='ui/layout.html#pagination',
241
+ swap='innerHTML:#pagination',
242
+ context=dict(page=self.page)
243
+ ).render(request)
244
+ res += pagination_update
245
+ return res
246
+
247
+
248
+ class DetailResponse(Response):
249
+
250
+ base_template = 'ui/detail.html'
251
+
252
+ def __init__(
253
+ self, *,
254
+ context: dict,
255
+ detail_header_template: str = None,
256
+ detail_data_template: str = None
257
+ ):
258
+ super().__init__(template=self.base_template, context=context)
259
+ self.context.update({
260
+ 'detail_header_template': detail_header_template,
261
+ 'detail_data_template': detail_data_template
262
+ })
263
+
264
+ @staticmethod
265
+ def add_trigger(response):
266
+ add_trigger(response, 'activate-detail')
267
+
268
+
269
+ class ModalResponse(Response):
270
+
271
+ def __init__(
272
+ self, *,
273
+ modal_id: str,
274
+ template: str,
275
+ context: dict,
276
+ title: str = None,
277
+ is_update: bool = False,
278
+ is_blocking: bool = False,
279
+ modal_width: str = None
280
+
281
+ ):
282
+ super().__init__(template=template, context=context)
283
+ self.context.update({
284
+ 'title': title,
285
+ 'modal_id': re.sub(r'[^A-Za-z-]+', '', modal_id).strip('-'),
286
+ 'is_update': is_update,
287
+ 'is_blocking': is_blocking,
288
+ 'modal_width': modal_width
289
+ })
290
+
291
+
292
+ @dataclass
293
+ class ClientTrigger:
294
+
295
+ trigger: dict | str
296
+ header: str = 'HX-Trigger'
297
+
298
+
299
+ class TriggerResponse:
300
+
301
+ def __init__(self, trigger: list[ClientTrigger]):
302
+ self.trigger = trigger
303
+
304
+ def response(self):
305
+ res = HttpResponse()
306
+ res.headers['HX-Reswap'] = 'None'
307
+ for trigger in self.trigger:
308
+ add_trigger(res, trigger.trigger, trigger.header)
309
+ return res
310
+
311
+
312
+ # @dataclass(kw_only=True)
313
+ # class Response:
314
+ #
315
+ # template: str
316
+ # context: dict = field(default_factory=dict)
317
+ #
318
+ # base_template = None
319
+ #
320
+ # def __post_init__(self):
321
+ # for key, value in self.context.items():
322
+ # setattr(self, key, value)
323
+ # if self.base_template is None:
324
+ # self.base_template = self.template
325
+ #
326
+ # def dict(self):
327
+ # return {
328
+ # attr: getattr(self, attr, None) for attr
329
+ # in filter(lambda x: not x.startswith('_'), self.__dict__)
330
+ # }
331
+ #
332
+ # @staticmethod
333
+ # def add_trigger(response):
334
+ # pass
335
+ #
336
+ # def render(self, request) -> str:
337
+ # return render_to_string(
338
+ # template_name=self.base_template, context=self.dict(), request=request
339
+ # )
340
+ #
341
+ # def response(self, request, extra_content: str = None) -> HttpResponse:
342
+ # extra_content = extra_content or ''
343
+ # res = HttpResponse(content=(
344
+ # self.render(request)
345
+ # + render_to_string('ui/message.html', self.dict(), request)
346
+ # + extra_content
347
+ # ))
348
+ # self.add_trigger(res)
349
+ # return res
350
+ #
351
+ #
352
+ # @dataclass(kw_only=True)
353
+ # class OobResponse(Response):
354
+ #
355
+ # swap: str
356
+ # tag: str = 'div'
357
+ # id: str = None
358
+ #
359
+ # base_template = 'ui/oob.html'
360
+ #
361
+ # def dict(self) -> dict:
362
+ # attributes = filter(
363
+ # lambda x: not x.startswith('_') and x not in ['template', 'swap', 'tag'],
364
+ # self.__dict__
365
+ # )
366
+ # res = {attr: getattr(self, attr, None) for attr in attributes}
367
+ # res.update({'oob': {
368
+ # 'template': self.template,
369
+ # 'id': self.id,
370
+ # 'swap': self.swap,
371
+ # 'tag': self.tag
372
+ # }})
373
+ # return res
374
+ #
375
+ # # def render(self, request) -> str:
376
+ # # return render_to_string(
377
+ # # template_name='ui/oob.html', context=self.dict(), request=request
378
+ # # )
379
+ #
380
+ #
381
+ # @dataclass(kw_only=True)
382
+ # class WindowResponse(Response):
383
+ #
384
+ # title: str = ''
385
+ # panel_template: str = None
386
+ # overview_header_template: str = None
387
+ # overview_data_template: str = None
388
+ #
389
+ # base_template = 'ui/layout.html'
390
+ #
391
+ # def __post_init__(self):
392
+ # if 'has_panel' not in self.context.keys():
393
+ # self.context.update(has_panel=self._has_panel())
394
+ # super().__post_init__()
395
+ # if self.template:
396
+ # self.base_template = self.template
397
+ #
398
+ # def _has_panel(self) -> bool:
399
+ # return bool(self.panel_template)
400
+ #
401
+ #
402
+ # @dataclass(kw_only=True)
403
+ # class PageResponse(WindowResponse):
404
+ #
405
+ # page: paginator.Page = None
406
+ # ui_filter: Filter = None
407
+ # endless_scroll: bool = True
408
+ #
409
+ # def __post_init__(self):
410
+ # assert self.page is not None or self.ui_filter is not None, _(
411
+ # 'Argument page or ui_filter must be supplied'
412
+ # )
413
+ # if self.ui_filter and not self.page:
414
+ # self.page = self.ui_filter.get_page()
415
+ # super().__post_init__()
416
+ #
417
+ # def _has_panel(self) -> bool:
418
+ # return bool(self.panel_template or self.ui_filter)
419
+ #
420
+ #
421
+ # @dataclass(kw_only=True)
422
+ # class ListResponse(PageResponse):
423
+ #
424
+ # list_entry_template: str = None
425
+ # column_count: int = 1
426
+ # column_height: str = '150px'
427
+ # overview_data_template:str = 'ui/list.html'
428
+ #
429
+ #
430
+ # @dataclass(kw_only=True)
431
+ # class ListEntryResponse(Response):
432
+ #
433
+ # object: Model
434
+ # list_entry_template: str
435
+ # page: paginator.Page = None
436
+ # new_entry: bool = False
437
+ # template: str = 'ui/list_update.html'
438
+ #
439
+ # def render(self, request) -> str:
440
+ # res = super().render(request)
441
+ # if self.page:
442
+ # pagination_update = OobResponse(
443
+ # template='ui/layout.html#pagination',
444
+ # swap='innerHTML:#pagination',
445
+ # context=dict(page=self.page)
446
+ # ).render(request)
447
+ # res += pagination_update
448
+ # return res
449
+ #
450
+ #
451
+ # @dataclass(kw_only=True)
452
+ # class TableResponse(PageResponse):
453
+ #
454
+ # object_label: str
455
+ # fields: list[str]
456
+ # footer: dict = field(default_factory=dict)
457
+ # template = 'ui/table.html'
458
+ #
459
+ #
460
+ # @dataclass(kw_only=True)
461
+ # class TableRowResponse(Response):
462
+ #
463
+ # object: Model
464
+ # fields: list[str]
465
+ # footer: dict = field(default_factory=dict)
466
+ # page: paginator.Page = None
467
+ #
468
+ # base_template = 'ui/table_row_update.html'
469
+ #
470
+ #
471
+ # @dataclass(kw_only=True)
472
+ # class DetailResponse(Response):
473
+ #
474
+ # detail_data_template: str = None
475
+ # detail_header_template: str = None
476
+ #
477
+ # base_template = 'ui/detail.html'
478
+ #
479
+ # @staticmethod
480
+ # def add_trigger(response):
481
+ # add_trigger(response, 'activate-detail')
482
+ #
483
+ #
484
+ # @dataclass(kw_only=True)
485
+ # class ModalResponse(Response):
486
+ #
487
+ # title: str
488
+ # modal_id: str
489
+ # template: str
490
+ # modal_update: bool = False
491
+ # blocking: bool = False
492
+ # modal_width: str = None
493
+ #
494
+ # def __post_init__(self):
495
+ # super().__post_init__()
496
+ # self.modal_id = re.sub(r'[^A-Za-z-]+', '', self.modal_id).strip('-')
497
+
498
+
499
+ def search_select_response(queryset) -> HttpResponse:
500
+ return HttpResponse(render_to_string(
501
+ 'ui/widgets/model_search_select_options.html',
502
+ {'options': queryset}
503
+ ))
504
+
505
+
506
+ def message_response(request, persistent: bool = False):
507
+ return HttpResponse(content=(render_to_string(
508
+ 'ui/message.html', context={'persistent': persistent}, request=request
509
+ )))
510
+
511
+
512
+ def add_trigger(
513
+ response: HttpResponse,
514
+ trigger: dict | str,
515
+ header: str = 'HX-Trigger'
516
+ ) -> HttpResponse:
517
+ if isinstance(trigger, str):
518
+ trigger = {trigger: ''}
519
+ res_trigger = response.headers.get(header)
520
+ if not res_trigger:
521
+ response.headers[header] = json.dumps(trigger)
522
+ return response
523
+ try:
524
+ res_trigger = ast.literal_eval(response.headers.get(header, '{}'))
525
+ except SyntaxError:
526
+ res_trigger = {response.headers[header]: ''}
527
+ res_trigger.update(trigger)
528
+ response.headers[header] = json.dumps(res_trigger)
529
+ return response
530
+
531
+
532
+ def update(request, ui_responses: list[Response]) -> HttpResponse:
533
+ response = HttpResponse()
534
+ content = ''
535
+ for res in ui_responses:
536
+ content += res.render(request)
537
+ res.add_trigger(response)
538
+ content += render_to_string('ui/message.html', request=request)
539
+ response.content = content
540
+ return response