WuttaWeb 0.20.4__tar.gz → 0.20.6__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 (205) hide show
  1. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/CHANGELOG.md +22 -0
  2. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/PKG-INFO +2 -2
  3. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/pyproject.toml +2 -2
  4. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/cli/webapp.py +1 -0
  5. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/forms/base.py +3 -2
  6. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/grids/base.py +23 -0
  7. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/menus.py +3 -1
  8. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/base.mako +16 -7
  9. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/grids/vue_template.mako +12 -0
  10. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/page.mako +1 -0
  11. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/views/common.py +13 -0
  12. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/views/master.py +8 -0
  13. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/views/roles.py +35 -4
  14. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/cli/test_webapp.py +4 -2
  15. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/grids/test_base.py +20 -2
  16. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/views/test_roles.py +22 -0
  17. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/.gitignore +0 -0
  18. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/COPYING.txt +0 -0
  19. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/README.md +0 -0
  20. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/Makefile +0 -0
  21. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/_static/.keepme +0 -0
  22. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.app.rst +0 -0
  23. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.auth.rst +0 -0
  24. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.cli.rst +0 -0
  25. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.cli.webapp.rst +0 -0
  26. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.conf.rst +0 -0
  27. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.db.continuum.rst +0 -0
  28. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.db.rst +0 -0
  29. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.db.sess.rst +0 -0
  30. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.emails.rst +0 -0
  31. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.forms.base.rst +0 -0
  32. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.forms.rst +0 -0
  33. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.forms.schema.rst +0 -0
  34. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.forms.widgets.rst +0 -0
  35. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.grids.base.rst +0 -0
  36. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.grids.filters.rst +0 -0
  37. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.grids.rst +0 -0
  38. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.handler.rst +0 -0
  39. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.helpers.rst +0 -0
  40. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.menus.rst +0 -0
  41. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.progress.rst +0 -0
  42. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.rst +0 -0
  43. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.static.rst +0 -0
  44. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.subscribers.rst +0 -0
  45. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.util.rst +0 -0
  46. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.views.auth.rst +0 -0
  47. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.views.base.rst +0 -0
  48. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.views.batch.rst +0 -0
  49. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.views.common.rst +0 -0
  50. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.views.email.rst +0 -0
  51. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.views.essential.rst +0 -0
  52. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.views.master.rst +0 -0
  53. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.views.people.rst +0 -0
  54. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.views.progress.rst +0 -0
  55. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.views.reports.rst +0 -0
  56. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.views.roles.rst +0 -0
  57. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.views.rst +0 -0
  58. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.views.settings.rst +0 -0
  59. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.views.upgrades.rst +0 -0
  60. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/api/wuttaweb.views.users.rst +0 -0
  61. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/conf.py +0 -0
  62. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/glossary.rst +0 -0
  63. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/index.rst +0 -0
  64. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/make.bat +0 -0
  65. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/narr/cli/builtin.rst +0 -0
  66. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/narr/cli/index.rst +0 -0
  67. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/narr/templates/base.rst +0 -0
  68. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/narr/templates/index.rst +0 -0
  69. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/narr/templates/lookup.rst +0 -0
  70. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/docs/narr/templates/overview.rst +0 -0
  71. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/__init__.py +0 -0
  72. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/_version.py +0 -0
  73. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/app.py +0 -0
  74. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/auth.py +0 -0
  75. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/cli/__init__.py +0 -0
  76. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/conf.py +0 -0
  77. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/db/__init__.py +0 -0
  78. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/db/continuum.py +0 -0
  79. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/db/sess.py +0 -0
  80. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/email-templates/feedback.html.mako +0 -0
  81. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/email-templates/feedback.txt.mako +0 -0
  82. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/emails.py +0 -0
  83. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/forms/__init__.py +0 -0
  84. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/forms/schema.py +0 -0
  85. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/forms/widgets.py +0 -0
  86. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/grids/__init__.py +0 -0
  87. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/grids/filters.py +0 -0
  88. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/handler.py +0 -0
  89. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/helpers.py +0 -0
  90. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/progress.py +0 -0
  91. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/static/__init__.py +0 -0
  92. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/static/img/favicon.ico +0 -0
  93. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/static/img/logo.png +0 -0
  94. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/static/img/testing.png +0 -0
  95. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/subscribers.py +0 -0
  96. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
  97. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/appinfo/index.mako +0 -0
  98. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/auth/change_password.mako +0 -0
  99. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/auth/login.mako +0 -0
  100. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/base_meta.mako +0 -0
  101. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/batch/view.mako +0 -0
  102. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/configure.mako +0 -0
  103. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
  104. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
  105. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
  106. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/deform/dateinput.pt +0 -0
  107. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/deform/datetimeinput.pt +0 -0
  108. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/deform/moneyinput.pt +0 -0
  109. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/deform/password.pt +0 -0
  110. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/deform/permissions.pt +0 -0
  111. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/deform/readonly/checkbox.pt +0 -0
  112. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/deform/readonly/email_recips.pt +0 -0
  113. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/deform/readonly/filedownload.pt +0 -0
  114. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
  115. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
  116. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
  117. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
  118. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/deform/select.pt +0 -0
  119. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/deform/textarea.pt +0 -0
  120. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/deform/textinput.pt +0 -0
  121. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/email/settings/view.mako +0 -0
  122. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/forbidden.mako +0 -0
  123. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/form.mako +0 -0
  124. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
  125. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/grids/table_element.mako +0 -0
  126. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/home.mako +0 -0
  127. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/master/configure.mako +0 -0
  128. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/master/create.mako +0 -0
  129. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/master/delete.mako +0 -0
  130. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/master/edit.mako +0 -0
  131. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/master/form.mako +0 -0
  132. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/master/index.mako +0 -0
  133. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/master/view.mako +0 -0
  134. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/notfound.mako +0 -0
  135. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/people/view_profile.mako +0 -0
  136. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/progress.mako +0 -0
  137. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/reports/view.mako +0 -0
  138. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/setup.mako +0 -0
  139. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/upgrade.mako +0 -0
  140. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/upgrades/configure.mako +0 -0
  141. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/upgrades/view.mako +0 -0
  142. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/templates/wutta-components.mako +0 -0
  143. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/testing.py +0 -0
  144. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/util.py +0 -0
  145. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/views/__init__.py +0 -0
  146. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/views/auth.py +0 -0
  147. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/views/base.py +0 -0
  148. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/views/batch.py +0 -0
  149. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/views/email.py +0 -0
  150. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/views/essential.py +0 -0
  151. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/views/people.py +0 -0
  152. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/views/progress.py +0 -0
  153. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/views/reports.py +0 -0
  154. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/views/settings.py +0 -0
  155. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/views/upgrades.py +0 -0
  156. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/src/wuttaweb/views/users.py +0 -0
  157. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tasks.py +0 -0
  158. {wuttaweb-0.20.4/tests/views → wuttaweb-0.20.6/tests}/__init__.py +0 -0
  159. {wuttaweb-0.20.4/tests/grids → wuttaweb-0.20.6/tests/cli}/__init__.py +0 -0
  160. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/db/__init__.py +0 -0
  161. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/db/test_continuum.py +0 -0
  162. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/forms/test_base.py +0 -0
  163. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/forms/test_schema.py +0 -0
  164. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/forms/test_widgets.py +0 -0
  165. {wuttaweb-0.20.4/tests/cli → wuttaweb-0.20.6/tests/grids}/__init__.py +0 -0
  166. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/grids/test_filters.py +0 -0
  167. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
  168. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
  169. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/libcache/bb_oruga.js +0 -0
  170. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/libcache/bb_oruga_bulma.css +0 -0
  171. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/libcache/bb_oruga_bulma.js +0 -0
  172. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/libcache/bb_vue.js +0 -0
  173. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/libcache/bb_vue_fontawesome.js +0 -0
  174. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/libcache/buefy.css +0 -0
  175. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/libcache/buefy.js +0 -0
  176. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/libcache/fontawesome.js +0 -0
  177. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/libcache/vue.js +0 -0
  178. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/libcache/vue_resource.js +0 -0
  179. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/test_app.py +0 -0
  180. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/test_auth.py +0 -0
  181. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/test_emails.py +0 -0
  182. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/test_handler.py +0 -0
  183. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/test_helpers.py +0 -0
  184. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/test_menus.py +0 -0
  185. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/test_progress.py +0 -0
  186. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/test_static.py +0 -0
  187. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/test_subscribers.py +0 -0
  188. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/test_util.py +0 -0
  189. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/util.py +0 -0
  190. {wuttaweb-0.20.4/tests → wuttaweb-0.20.6/tests/views}/__init__.py +0 -0
  191. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/views/test___init__.py +0 -0
  192. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/views/test_auth.py +0 -0
  193. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/views/test_base.py +0 -0
  194. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/views/test_batch.py +0 -0
  195. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/views/test_common.py +0 -0
  196. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/views/test_email.py +0 -0
  197. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/views/test_essential.py +0 -0
  198. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/views/test_master.py +0 -0
  199. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/views/test_people.py +0 -0
  200. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/views/test_progress.py +0 -0
  201. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/views/test_reports.py +0 -0
  202. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/views/test_settings.py +0 -0
  203. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/views/test_upgrades.py +0 -0
  204. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tests/views/test_users.py +0 -0
  205. {wuttaweb-0.20.4 → wuttaweb-0.20.6}/tox.ini +0 -0
@@ -5,6 +5,28 @@ All notable changes to wuttaweb will be documented in this file.
5
5
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
6
6
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## v0.20.6 (2025-01-26)
9
+
10
+ ### Fix
11
+
12
+ - add `setup_enhance_admin_user()` method for initial setup
13
+ - add `render_percent()` method for Grid
14
+ - allow override for Admin menu title
15
+ - add `index_title_controls()` def block for base template
16
+ - add `make_users_grid()` method for RoleView
17
+ - fallback to empty string for uvicorn `root_path`
18
+ - add `root_path` config setting for running webapp via uvicorn
19
+
20
+ ## v0.20.5 (2025-01-23)
21
+
22
+ ### Fix
23
+
24
+ - improve styling for grid tools section
25
+ - add basic checkbox support for grids
26
+ - add WuttaRequestMixin for ThisPage component
27
+ - avoid literal `None` when rendering form field value
28
+ - let header title be even wider
29
+
8
30
  ## v0.20.4 (2025-01-15)
9
31
 
10
32
  ### Fix
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: WuttaWeb
3
- Version: 0.20.4
3
+ Version: 0.20.6
4
4
  Summary: Web App for Wutta Framework
5
5
  Project-URL: Homepage, https://wuttaproject.org/
6
6
  Project-URL: Repository, https://forgejo.wuttaproject.org/wutta/wuttaweb
@@ -39,7 +39,7 @@ Requires-Dist: pyramid-tm
39
39
  Requires-Dist: pyramid>=2
40
40
  Requires-Dist: waitress
41
41
  Requires-Dist: webhelpers2
42
- Requires-Dist: wuttjamaican[db]>=0.20.1
42
+ Requires-Dist: wuttjamaican[db]>=0.20.2
43
43
  Requires-Dist: zope-sqlalchemy>=1.5
44
44
  Provides-Extra: continuum
45
45
  Requires-Dist: wutta-continuum; extra == 'continuum'
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
 
7
7
  [project]
8
8
  name = "WuttaWeb"
9
- version = "0.20.4"
9
+ version = "0.20.6"
10
10
  description = "Web App for Wutta Framework"
11
11
  readme = "README.md"
12
12
  authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
@@ -44,7 +44,7 @@ dependencies = [
44
44
  "pyramid_tm",
45
45
  "waitress",
46
46
  "WebHelpers2",
47
- "WuttJamaican[db]>=0.20.1",
47
+ "WuttJamaican[db]>=0.20.2",
48
48
  "zope.sqlalchemy>=1.5",
49
49
  ]
50
50
 
@@ -74,6 +74,7 @@ def webapp(
74
74
  'reload_dirs': config.get_list(f'{config.appname}.web.app.reload_dirs'),
75
75
  'factory': config.get_bool(f'{config.appname}.web.app.factory', default=False),
76
76
  'interface': config.get(f'{config.appname}.web.app.interface', default='auto'),
77
+ 'root_path': config.get(f'{config.appname}.web.app.root_path', default=''),
77
78
  }
78
79
 
79
80
  # also must inject our config files to env, since there is no
@@ -1033,12 +1033,13 @@ class Form:
1033
1033
  # render static text if field not in deform/schema
1034
1034
  # TODO: need to abstract this somehow
1035
1035
  if self.model_instance:
1036
- html = str(self.model_instance[fieldname])
1036
+ value = self.model_instance[fieldname]
1037
+ html = str(value) if value is not None else ''
1037
1038
  else:
1038
1039
  html = ''
1039
1040
 
1040
1041
  # mark all that as safe
1041
- html = HTML.literal(html)
1042
+ html = HTML.literal(html or ' ')
1042
1043
 
1043
1044
  # render field label
1044
1045
  label = self.get_label(fieldname)
@@ -118,6 +118,11 @@ class Grid:
118
118
  See also :meth:`set_renderer()` and
119
119
  :meth:`set_default_renderers()`.
120
120
 
121
+ .. attribute:: checkable
122
+
123
+ Boolean indicating whether the grid should expose per-row
124
+ checkboxes.
125
+
121
126
  .. attribute:: row_class
122
127
 
123
128
  This represents the CSS ``class`` attribute for a row within
@@ -362,6 +367,7 @@ class Grid:
362
367
  data=None,
363
368
  labels={},
364
369
  renderers={},
370
+ checkable=False,
365
371
  row_class=None,
366
372
  actions=[],
367
373
  linked_columns=[],
@@ -388,6 +394,7 @@ class Grid:
388
394
  self.key = key
389
395
  self.data = data
390
396
  self.labels = labels or {}
397
+ self.checkable = checkable
391
398
  self.row_class = row_class
392
399
  self.actions = actions or []
393
400
  self.linked_columns = linked_columns or []
@@ -605,6 +612,7 @@ class Grid:
605
612
  * ``'date'`` -> :meth:`render_date()`
606
613
  * ``'datetime'`` -> :meth:`render_datetime()`
607
614
  * ``'quantity'`` -> :meth:`render_quantity()`
615
+ * ``'percent'`` -> :meth:`render_percent()`
608
616
 
609
617
  Renderer overrides are tracked via :attr:`renderers`.
610
618
  """
@@ -615,6 +623,7 @@ class Grid:
615
623
  'date': self.render_date,
616
624
  'datetime': self.render_datetime,
617
625
  'quantity': self.render_quantity,
626
+ 'percent': self.render_percent,
618
627
  }
619
628
 
620
629
  if renderer in builtins:
@@ -1877,6 +1886,20 @@ class Grid:
1877
1886
  dt = getattr(obj, key)
1878
1887
  return self.app.render_datetime(dt)
1879
1888
 
1889
+ def render_percent(self, obj, key, value, **kwargs):
1890
+ """
1891
+ Column renderer for percentage values.
1892
+
1893
+ This calls
1894
+ :meth:`~wuttjamaican:wuttjamaican.app.AppHandler.render_percent()`
1895
+ for the return value.
1896
+
1897
+ This is not used automatically but you can use it explicitly::
1898
+
1899
+ grid.set_renderer('foo', 'percent')
1900
+ """
1901
+ return self.app.render_percent(value, **kwargs)
1902
+
1880
1903
  def render_quantity(self, obj, key, value):
1881
1904
  """
1882
1905
  Column renderer for quantity values.
@@ -143,6 +143,8 @@ class MenuHandler(GenericHandler):
143
143
  which will ultimately be one element of the final list of
144
144
  dicts as described in :class:`MenuHandler`.
145
145
 
146
+ :param title: Override the menu title; default is "Admin".
147
+
146
148
  :param include_people: You can pass this flag to indicate the
147
149
  admin menu should contain an entry for the "People" view.
148
150
  """
@@ -198,7 +200,7 @@ class MenuHandler(GenericHandler):
198
200
  ])
199
201
 
200
202
  return {
201
- 'title': "Admin",
203
+ 'title': kwargs.get('title', "Admin"),
202
204
  'type': 'menu',
203
205
  'items': items,
204
206
  }
@@ -155,7 +155,7 @@
155
155
  }
156
156
 
157
157
  #content-title h1 {
158
- max-width: 85%;
158
+ max-width: 95%;
159
159
  overflow: hidden;
160
160
  padding-left: 0.5rem;
161
161
  text-overflow: ellipsis;
@@ -186,6 +186,11 @@
186
186
  width: 100%;
187
187
  }
188
188
 
189
+ .wutta-grid-tools-wrapper {
190
+ display: flex;
191
+ gap: 0.5rem;
192
+ }
193
+
189
194
  ##############################
190
195
  ## forms
191
196
  ##############################
@@ -243,12 +248,7 @@
243
248
  % else:
244
249
  <h1 class="title">${index_title}</h1>
245
250
  % endif
246
- % if master and master.creatable and not master.creating and master.has_perm('create'):
247
- <wutta-button once type="is-primary"
248
- tag="a" href="${url(f'{route_prefix}.create')}"
249
- icon-left="plus"
250
- label="Create New" />
251
- % endif
251
+ ${self.index_title_controls()}
252
252
  % endif
253
253
  </div>
254
254
  </div>
@@ -344,6 +344,15 @@
344
344
  </footer>
345
345
  </%def>
346
346
 
347
+ <%def name="index_title_controls()">
348
+ % if master and master.creatable and not master.creating and master.has_perm('create'):
349
+ <wutta-button once type="is-primary"
350
+ tag="a" href="${url(f'{route_prefix}.create')}"
351
+ icon-left="plus"
352
+ label="Create New" />
353
+ % endif
354
+ </%def>
355
+
347
356
  <%def name="render_vue_template_whole_page()">
348
357
  <script type="text/x-template" id="whole-page-template">
349
358
 
@@ -116,6 +116,13 @@
116
116
  hoverable
117
117
  icon-pack="fas"
118
118
 
119
+ ## checkboxes
120
+ % if grid.checkable:
121
+ checkable
122
+ checkbox-position="right"
123
+ :checked-rows.sync="checkedRows"
124
+ % endif
125
+
119
126
  ## sorting
120
127
  % if grid.sortable:
121
128
  ## nb. buefy/oruga only support *one* default sorter
@@ -267,6 +274,11 @@
267
274
  shareLink: null,
268
275
  % endif
269
276
 
277
+ ## checkboxes
278
+ % if grid.checkable:
279
+ checkedRows: [],
280
+ % endif
281
+
270
282
  ## filtering
271
283
  % if grid.filterable:
272
284
  filters: ${json.dumps(grid.get_vue_filters())|n},
@@ -26,6 +26,7 @@
26
26
 
27
27
  const ThisPage = {
28
28
  template: '#this-page-template',
29
+ mixins: [WuttaRequestMixin],
29
30
  props: {
30
31
  ## configureFieldsHelp: Boolean,
31
32
  },
@@ -245,6 +245,8 @@ class CommonView(View):
245
245
  session.add(person)
246
246
  user.person = person
247
247
 
248
+ self.setup_enhance_admin_user(user)
249
+
248
250
  # send user to /login
249
251
  self.request.session.flash("Account created! Please login below.")
250
252
  return self.redirect(self.request.route_url('login'))
@@ -254,6 +256,17 @@ class CommonView(View):
254
256
  'form': form,
255
257
  }
256
258
 
259
+ def setup_enhance_admin_user(self, user):
260
+ """
261
+ Further "enhance" the initial admin user when it is first created.
262
+
263
+ This does nothing by default; subclass can override if needed.
264
+
265
+ :param user: New admin
266
+ :class:`~wuttjamaican:wuttjamaican.db.model.auth.User`
267
+ which was just created as part of initial setup.
268
+ """
269
+
257
270
  @classmethod
258
271
  def defaults(cls, config):
259
272
  cls._defaults(config)
@@ -189,6 +189,12 @@ class MasterView(View):
189
189
 
190
190
  This is optional; see also :meth:`get_grid_columns()`.
191
191
 
192
+ .. attribute:: checkable
193
+
194
+ Boolean indicating whether the grid should expose per-row
195
+ checkboxes. This is passed along to set
196
+ :attr:`~wuttaweb.grids.base.Grid.checkable` on the grid.
197
+
192
198
  .. method:: grid_row_class(obj, data, i)
193
199
 
194
200
  This method is *not* defined on the ``MasterView`` base class;
@@ -395,6 +401,7 @@ class MasterView(View):
395
401
  # features
396
402
  listable = True
397
403
  has_grid = True
404
+ checkable = False
398
405
  filterable = True
399
406
  filter_defaults = None
400
407
  sortable = True
@@ -1992,6 +1999,7 @@ class MasterView(View):
1992
1999
 
1993
2000
  kwargs['tools'] = tools
1994
2001
 
2002
+ kwargs.setdefault('checkable', self.checkable)
1995
2003
  if hasattr(self, 'grid_row_class'):
1996
2004
  kwargs.setdefault('row_class', self.grid_row_class)
1997
2005
  kwargs.setdefault('filterable', self.filterable)
@@ -28,7 +28,7 @@ from wuttjamaican.db.model import Role, Permission
28
28
  from wuttaweb.views import MasterView
29
29
  from wuttaweb.db import Session
30
30
  from wuttaweb.forms import widgets
31
- from wuttaweb.forms.schema import UserRefs, Permissions, RoleRef
31
+ from wuttaweb.forms.schema import Permissions, RoleRef
32
32
 
33
33
 
34
34
  class RoleView(MasterView):
@@ -123,9 +123,7 @@ class RoleView(MasterView):
123
123
  # users
124
124
  if not (self.creating or self.editing):
125
125
  f.append('users')
126
- f.set_readonly('users')
127
- f.set_node('users', UserRefs(self.request))
128
- f.set_default('users', [u.uuid for u in role.users])
126
+ f.set_grid('users', self.make_users_grid(role))
129
127
 
130
128
  # permissions
131
129
  f.append('permissions')
@@ -134,6 +132,39 @@ class RoleView(MasterView):
134
132
  if not self.creating:
135
133
  f.set_default('permissions', list(role.permissions))
136
134
 
135
+ def make_users_grid(self, role):
136
+ """
137
+ Make and return the grid for the Users field.
138
+
139
+ This grid is shown for the Users field when viewing a Role.
140
+
141
+ :returns: Fully configured :class:`~wuttaweb.grids.base.Grid`
142
+ instance.
143
+ """
144
+ model = self.app.model
145
+ route_prefix = self.get_route_prefix()
146
+
147
+ grid = self.make_grid(key=f'{route_prefix}.view.users',
148
+ model_class=model.User,
149
+ data=role.users,
150
+ columns=[
151
+ 'username',
152
+ 'person',
153
+ 'active',
154
+ ])
155
+
156
+ if self.request.has_perm('users.view'):
157
+ url = lambda user, i: self.request.route_url('users.view', uuid=user.uuid)
158
+ grid.add_action('view', icon='eye', url=url)
159
+ grid.set_link('person')
160
+ grid.set_link('username')
161
+
162
+ if self.request.has_perm('users.edit'):
163
+ url = lambda user, i: self.request.route_url('users.edit', uuid=user.uuid)
164
+ grid.add_action('edit', url=url)
165
+
166
+ return grid
167
+
137
168
  def unique_name(self, node, value):
138
169
  """ """
139
170
  model = self.app.model
@@ -88,7 +88,8 @@ app.spec = wuttaweb.app:make_wsgi_app
88
88
  reload=False,
89
89
  reload_dirs=None,
90
90
  factory=False,
91
- interface='auto')
91
+ interface='auto',
92
+ root_path='')
92
93
 
93
94
  # with reload
94
95
  uvicorn.run.reset_mock()
@@ -101,4 +102,5 @@ app.spec = wuttaweb.app:make_wsgi_app
101
102
  reload=True,
102
103
  reload_dirs=None,
103
104
  factory=False,
104
- interface='auto')
105
+ interface='auto',
106
+ root_path='')
@@ -1409,6 +1409,24 @@ class TestGrid(WebTestCase):
1409
1409
  value = decimal.Decimal('-42.42')
1410
1410
  self.assertEqual(grid.render_currency(obj, 'foo', value), '($42.42)')
1411
1411
 
1412
+ def test_render_percent(self):
1413
+ grid = self.make_grid(columns=['foo', 'bar'])
1414
+ obj = MagicMock()
1415
+
1416
+ # null
1417
+ self.assertEqual(grid.render_percent(obj, 'foo', None), "")
1418
+
1419
+ # typical
1420
+ self.assertEqual(grid.render_percent(obj, 'foo', 12.3419), '12.34 %')
1421
+
1422
+ # more decimal places
1423
+ self.assertEqual(grid.render_percent(obj, 'foo', 12.3419, decimals=3), '12.342 %')
1424
+ self.assertEqual(grid.render_percent(obj, 'foo', 12.3419, decimals=4), '12.3419 %')
1425
+
1426
+ # negative
1427
+ self.assertEqual(grid.render_percent(obj, 'foo', -12.3419), '(12.34 %)')
1428
+ self.assertEqual(grid.render_percent(obj, 'foo', -12.3419, decimals=3), '(12.342 %)')
1429
+
1412
1430
  def test_render_quantity(self):
1413
1431
  grid = self.make_grid(columns=['foo', 'bar'])
1414
1432
  obj = MagicMock()
@@ -1433,7 +1451,7 @@ class TestGrid(WebTestCase):
1433
1451
  # null
1434
1452
  obj = MagicMock(dt=None)
1435
1453
  result = grid.render_date(obj, 'dt', None)
1436
- self.assertIsNone(result)
1454
+ self.assertEqual(result, '')
1437
1455
 
1438
1456
  # typical
1439
1457
  dt = datetime.date(2025, 1, 13)
@@ -1446,7 +1464,7 @@ class TestGrid(WebTestCase):
1446
1464
 
1447
1465
  obj = MagicMock(dt=None)
1448
1466
  result = grid.render_datetime(obj, 'dt', None)
1449
- self.assertIsNone(result)
1467
+ self.assertEqual(result, '')
1450
1468
 
1451
1469
  dt = datetime.datetime(2024, 12, 12, 13, 44, tzinfo=datetime.timezone.utc)
1452
1470
  obj = MagicMock(dt=dt)
@@ -7,6 +7,7 @@ from sqlalchemy import orm
7
7
  import colander
8
8
 
9
9
  from wuttaweb.views import roles as mod
10
+ from wuttaweb.grids import Grid
10
11
  from wuttaweb.forms.schema import RoleRef
11
12
  from wuttaweb.testing import WebTestCase
12
13
 
@@ -93,6 +94,27 @@ class TestRoleView(WebTestCase):
93
94
  view.configure_form(form)
94
95
  self.assertIsNotNone(form.validators['name'])
95
96
 
97
+ def test_make_users_grid(self):
98
+ model = self.app.model
99
+ view = self.make_view()
100
+ role = model.Role(name="Manager")
101
+
102
+ # basic
103
+ grid = view.make_users_grid(role)
104
+ self.assertIsInstance(grid, Grid)
105
+ self.assertFalse(grid.linked_columns)
106
+ self.assertFalse(grid.actions)
107
+
108
+ # view + edit actions
109
+ with patch.object(self.request, 'is_root', new=True):
110
+ grid = view.make_users_grid(role)
111
+ self.assertIsInstance(grid, Grid)
112
+ self.assertIn('person', grid.linked_columns)
113
+ self.assertIn('username', grid.linked_columns)
114
+ self.assertEqual(len(grid.actions), 2)
115
+ self.assertEqual(grid.actions[0].key, 'view')
116
+ self.assertEqual(grid.actions[1].key, 'edit')
117
+
96
118
  def test_unique_name(self):
97
119
  model = self.app.model
98
120
  view = self.make_view()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes