WuttaWeb 0.27.0__tar.gz → 0.27.1__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 (248) hide show
  1. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/CHANGELOG.md +17 -0
  2. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/PKG-INFO +1 -1
  3. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/pyproject.toml +1 -1
  4. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/code-templates/new-table.mako +11 -15
  5. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/grids/base.py +27 -7
  6. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/appinfo/index.mako +27 -28
  7. wuttaweb-0.27.1/src/wuttaweb/templates/batch/view.mako +113 -0
  8. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/master/view.mako +2 -2
  9. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/tables/app/create.mako +2 -2
  10. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/views/batch.py +7 -3
  11. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/views/common.py +17 -0
  12. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/views/master.py +13 -1
  13. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/views/people.py +3 -1
  14. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/grids/test_base.py +17 -2
  15. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/test_batch.py +1 -0
  16. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/test_tables.py +1 -1
  17. wuttaweb-0.27.0/src/wuttaweb/templates/batch/view.mako +0 -111
  18. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/.gitignore +0 -0
  19. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/.hgignore +0 -0
  20. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/.pylintrc +0 -0
  21. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/COPYING.txt +0 -0
  22. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/README.md +0 -0
  23. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/Makefile +0 -0
  24. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/_static/.keepme +0 -0
  25. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.app.rst +0 -0
  26. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.auth.rst +0 -0
  27. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.cli.rst +0 -0
  28. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.cli.webapp.rst +0 -0
  29. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.conf.rst +0 -0
  30. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.db.continuum.rst +0 -0
  31. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.db.rst +0 -0
  32. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.db.sess.rst +0 -0
  33. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.diffs.rst +0 -0
  34. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.emails.rst +0 -0
  35. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.forms.base.rst +0 -0
  36. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.forms.rst +0 -0
  37. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.forms.schema.rst +0 -0
  38. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.forms.widgets.rst +0 -0
  39. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.grids.base.rst +0 -0
  40. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.grids.filters.rst +0 -0
  41. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.grids.rst +0 -0
  42. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.handler.rst +0 -0
  43. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.helpers.rst +0 -0
  44. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.menus.rst +0 -0
  45. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.progress.rst +0 -0
  46. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.rst +0 -0
  47. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.static.rst +0 -0
  48. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.subscribers.rst +0 -0
  49. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.util.rst +0 -0
  50. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.views.alembic.rst +0 -0
  51. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.views.auth.rst +0 -0
  52. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.views.base.rst +0 -0
  53. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.views.batch.rst +0 -0
  54. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.views.common.rst +0 -0
  55. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.views.email.rst +0 -0
  56. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.views.essential.rst +0 -0
  57. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.views.master.rst +0 -0
  58. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.views.people.rst +0 -0
  59. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.views.progress.rst +0 -0
  60. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.views.reports.rst +0 -0
  61. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.views.roles.rst +0 -0
  62. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.views.rst +0 -0
  63. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.views.settings.rst +0 -0
  64. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.views.tables.rst +0 -0
  65. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.views.upgrades.rst +0 -0
  66. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.views.users.rst +0 -0
  67. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/api/wuttaweb.views.views.rst +0 -0
  68. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/conf.py +0 -0
  69. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/glossary.rst +0 -0
  70. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/index.rst +0 -0
  71. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/make.bat +0 -0
  72. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/narr/cli/builtin.rst +0 -0
  73. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/narr/cli/index.rst +0 -0
  74. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/narr/templates/base.rst +0 -0
  75. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/narr/templates/index.rst +0 -0
  76. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/narr/templates/lookup.rst +0 -0
  77. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/docs/narr/templates/overview.rst +0 -0
  78. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/__init__.py +0 -0
  79. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/_version.py +0 -0
  80. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/app.py +0 -0
  81. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/auth.py +0 -0
  82. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/cli/__init__.py +0 -0
  83. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/cli/webapp.py +0 -0
  84. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/code-templates/new-master-view.mako +0 -0
  85. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/conf.py +0 -0
  86. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/db/__init__.py +0 -0
  87. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/db/continuum.py +0 -0
  88. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/db/sess.py +0 -0
  89. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/diffs.py +0 -0
  90. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/email-templates/feedback.html.mako +0 -0
  91. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/email-templates/feedback.txt.mako +0 -0
  92. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/emails.py +0 -0
  93. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/forms/__init__.py +0 -0
  94. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/forms/base.py +0 -0
  95. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/forms/schema.py +0 -0
  96. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/forms/widgets.py +0 -0
  97. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/grids/__init__.py +0 -0
  98. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/grids/filters.py +0 -0
  99. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/handler.py +0 -0
  100. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/helpers.py +0 -0
  101. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/menus.py +0 -0
  102. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/progress.py +0 -0
  103. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/static/__init__.py +0 -0
  104. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/static/img/favicon.ico +0 -0
  105. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/static/img/logo.png +0 -0
  106. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/static/img/testing.png +0 -0
  107. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/subscribers.py +0 -0
  108. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/alembic/dashboard.mako +0 -0
  109. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/alembic/migrations/configure.mako +0 -0
  110. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/alembic/migrations/create.mako +0 -0
  111. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/alembic/migrations/index.mako +0 -0
  112. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/alembic/migrations/view.mako +0 -0
  113. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
  114. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/auth/change_password.mako +0 -0
  115. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/auth/login.mako +0 -0
  116. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/base.mako +0 -0
  117. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/base_meta.mako +0 -0
  118. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/configure.mako +0 -0
  119. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
  120. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
  121. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
  122. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/dateinput.pt +0 -0
  123. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/datetimeinput.pt +0 -0
  124. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/moneyinput.pt +0 -0
  125. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/password.pt +0 -0
  126. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/permissions.pt +0 -0
  127. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/readonly/checkbox.pt +0 -0
  128. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/readonly/email_recips.pt +0 -0
  129. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/readonly/filedownload.pt +0 -0
  130. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
  131. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
  132. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
  133. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
  134. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/select.pt +0 -0
  135. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/textarea.pt +0 -0
  136. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/textinput.pt +0 -0
  137. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/deform/wutta_checked_password.pt +0 -0
  138. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/diff.mako +0 -0
  139. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/email/settings/view.mako +0 -0
  140. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/forbidden.mako +0 -0
  141. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/form.mako +0 -0
  142. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/forms/vue_buttons.mako +0 -0
  143. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/forms/vue_fields.mako +0 -0
  144. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
  145. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/grids/table_element.mako +0 -0
  146. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/grids/vue_template.mako +0 -0
  147. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/home.mako +0 -0
  148. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/master/configure.mako +0 -0
  149. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/master/create.mako +0 -0
  150. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/master/create_row.mako +0 -0
  151. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/master/delete.mako +0 -0
  152. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/master/edit.mako +0 -0
  153. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/master/form.mako +0 -0
  154. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/master/index.mako +0 -0
  155. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/master/view_version.mako +0 -0
  156. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/master/view_versions.mako +0 -0
  157. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/notfound.mako +0 -0
  158. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/page.mako +0 -0
  159. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/people/view_profile.mako +0 -0
  160. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/progress.mako +0 -0
  161. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/reports/view.mako +0 -0
  162. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/setup.mako +0 -0
  163. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/tables/app/index.mako +0 -0
  164. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/themes/butterfly/base.mako +0 -0
  165. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/themes/butterfly/buefy-components.mako +0 -0
  166. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/themes/butterfly/buefy-plugin.mako +0 -0
  167. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/themes/butterfly/http-plugin.mako +0 -0
  168. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/upgrade.mako +0 -0
  169. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/upgrades/configure.mako +0 -0
  170. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/upgrades/index.mako +0 -0
  171. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/upgrades/view.mako +0 -0
  172. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/users/view.mako +0 -0
  173. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/views/master/configure.mako +0 -0
  174. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/views/master/create.mako +0 -0
  175. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/views/master/index.mako +0 -0
  176. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/templates/wutta-components.mako +0 -0
  177. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/testing.py +0 -0
  178. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/util.py +0 -0
  179. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/views/__init__.py +0 -0
  180. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/views/alembic.py +0 -0
  181. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/views/auth.py +0 -0
  182. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/views/base.py +0 -0
  183. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/views/email.py +0 -0
  184. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/views/essential.py +0 -0
  185. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/views/progress.py +0 -0
  186. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/views/reports.py +0 -0
  187. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/views/roles.py +0 -0
  188. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/views/settings.py +0 -0
  189. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/views/tables.py +0 -0
  190. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/views/upgrades.py +0 -0
  191. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/views/users.py +0 -0
  192. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/src/wuttaweb/views/views.py +0 -0
  193. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tasks.py +0 -0
  194. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/__init__.py +0 -0
  195. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/cli/__init__.py +0 -0
  196. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/cli/test_webapp.py +0 -0
  197. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/db/__init__.py +0 -0
  198. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/db/test_continuum.py +0 -0
  199. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/forms/main_template.mako +0 -0
  200. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/forms/main_template_with_fields.mako +0 -0
  201. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/forms/test_base.py +0 -0
  202. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/forms/test_schema.py +0 -0
  203. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/forms/test_widgets.py +0 -0
  204. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/grids/__init__.py +0 -0
  205. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/grids/test_filters.py +0 -0
  206. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
  207. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
  208. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/libcache/bb_oruga.js +0 -0
  209. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/libcache/bb_oruga_bulma.css +0 -0
  210. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/libcache/bb_oruga_bulma.js +0 -0
  211. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/libcache/bb_vue.js +0 -0
  212. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/libcache/bb_vue_fontawesome.js +0 -0
  213. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/libcache/buefy.css +0 -0
  214. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/libcache/buefy.js +0 -0
  215. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/libcache/fontawesome.js +0 -0
  216. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/libcache/vue.js +0 -0
  217. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/libcache/vue_resource.js +0 -0
  218. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/test_app.py +0 -0
  219. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/test_auth.py +0 -0
  220. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/test_conf.py +0 -0
  221. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/test_diffs.py +0 -0
  222. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/test_emails.py +0 -0
  223. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/test_handler.py +0 -0
  224. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/test_helpers.py +0 -0
  225. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/test_menus.py +0 -0
  226. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/test_progress.py +0 -0
  227. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/test_static.py +0 -0
  228. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/test_subscribers.py +0 -0
  229. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/test_util.py +0 -0
  230. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/util.py +0 -0
  231. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/__init__.py +0 -0
  232. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/test___init__.py +0 -0
  233. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/test_alembic.py +0 -0
  234. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/test_auth.py +0 -0
  235. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/test_base.py +0 -0
  236. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/test_common.py +0 -0
  237. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/test_email.py +0 -0
  238. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/test_essential.py +0 -0
  239. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/test_master.py +0 -0
  240. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/test_people.py +0 -0
  241. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/test_progress.py +0 -0
  242. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/test_reports.py +0 -0
  243. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/test_roles.py +0 -0
  244. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/test_settings.py +0 -0
  245. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/test_upgrades.py +0 -0
  246. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/test_users.py +0 -0
  247. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tests/views/test_views.py +0 -0
  248. {wuttaweb-0.27.0 → wuttaweb-0.27.1}/tox.ini +0 -0
@@ -5,6 +5,23 @@ 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.27.1 (2026-01-03)
9
+
10
+ ### Fix
11
+
12
+ - add separate def for middle buttons on App Info page
13
+ - expose version history for site admin in default setup
14
+ - only show execution panel if batch view supports it
15
+ - remove the Users field for create/edit Person
16
+ - only show "create row" button if batch view supports it
17
+ - show proper batch status text, from enum
18
+ - use wutta hint from model, for master view title
19
+ - accept dict instead of true enum, for `Grid.set_enum()`
20
+ - improve FK handling in generated model class code
21
+ - use custom UUID type when generating model class code for FK
22
+ - fix spacing style when viewing object with row grid
23
+ - grant access to alembic, tables, master view admin for new apps
24
+
8
25
  ## v0.27.0 (2025-12-31)
9
26
 
10
27
  ### Feat
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: WuttaWeb
3
- Version: 0.27.0
3
+ Version: 0.27.1
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
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
 
7
7
  [project]
8
8
  name = "WuttaWeb"
9
- version = "0.27.0"
9
+ version = "0.27.1"
10
10
  description = "Web App for Wutta Framework"
11
11
  readme = "README.md"
12
12
  authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
@@ -15,16 +15,6 @@ class ${model_name}(model.Base):
15
15
  ${description}
16
16
  """
17
17
  __tablename__ = "${table_name}"
18
- % if any([c["data_type"]["type"] == "_fk_uuid_" for c in columns]):
19
- __table_args__ = (
20
- % for column in columns:
21
- % if column["data_type"]["type"] == "_fk_uuid_":
22
- sa.ForeignKeyConstraint(["${column['name']}"], ["${column['data_type']['reference']}.uuid"],
23
- name="${table_name}_fk_${column['data_type']['reference']}"),
24
- % endif
25
- % endfor
26
- )
27
- % endif
28
18
  % if versioned:
29
19
  % if all([c["versioned"] for c in columns]):
30
20
  __versioned__ = {}
@@ -48,17 +38,23 @@ class ${model_name}(model.Base):
48
38
 
49
39
  % if column["name"] == "uuid":
50
40
  uuid = model.uuid_column()
51
- % else:
52
- ${column["name"]} = sa.Column(${column["formatted_data_type"]}, nullable=${column["nullable"]}, doc="""
53
- ${column["description"] or ""}
54
- """)
55
- % if column["data_type"]["type"] == "_fk_uuid_" and column["relationship"]:
41
+ % elif column["data_type"]["type"] == "_fk_uuid_":
42
+ ${column["name"]} = model.uuid_fk_column("${column['data_type']['reference']}.uuid",
43
+ nullable=${column["nullable"]})
44
+ % if column["relationship"]:
56
45
  ${column["relationship"]["name"]} = orm.relationship(
57
46
  "${column['relationship']['reference_model']}",
58
47
  doc="""
59
48
  ${column["description"] or ""}
60
49
  """)
61
50
  % endif
51
+ % else:
52
+ ${column["name"]} = sa.Column(
53
+ ${column["formatted_data_type"]},
54
+ nullable=${column["nullable"]},
55
+ doc="""
56
+ ${column["description"] or ""}
57
+ """)
62
58
  % endif
63
59
  % endfor
64
60
 
@@ -2,7 +2,7 @@
2
2
  ################################################################################
3
3
  #
4
4
  # wuttaweb -- Web App for Wutta Framework
5
- # Copyright © 2024-2025 Lance Edgar
5
+ # Copyright © 2024-2026 Lance Edgar
6
6
  #
7
7
  # This file is part of Wutta Framework.
8
8
  #
@@ -30,6 +30,12 @@ import logging
30
30
  import warnings
31
31
  from collections import namedtuple, OrderedDict
32
32
 
33
+ try:
34
+ from enum import EnumType
35
+ except ImportError: # pragma: no cover
36
+ # nb. python < 3.11
37
+ from enum import EnumMeta as EnumType
38
+
33
39
  import sqlalchemy as sa
34
40
  from sqlalchemy import orm
35
41
 
@@ -763,7 +769,7 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth
763
769
 
764
770
  :param key: Name of column.
765
771
 
766
- :param enum: Instance of :class:`python:enum.Enum`.
772
+ :param enum: Instance of :class:`python:enum.Enum`, or a dict.
767
773
  """
768
774
  self.enums[key] = enum
769
775
  self.set_renderer(key, self.render_enum, enum=enum)
@@ -2050,7 +2056,7 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth
2050
2056
  See also :meth:`set_enum()`.
2051
2057
 
2052
2058
  :param enum: Enum class for the field. This should be an
2053
- instance of :class:`~python:enum.Enum`.
2059
+ instance of :class:`~python:enum.Enum` or else a dict.
2054
2060
 
2055
2061
  To use this feature for your grid::
2056
2062
 
@@ -2061,12 +2067,26 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth
2061
2067
  TWO = 2
2062
2068
  THREE = 3
2063
2069
 
2064
- grid.set_enum('my_enum_field', MyEnum)
2070
+ grid.set_enum("my_enum_field", MyEnum)
2071
+
2072
+ Or, perhaps more common::
2073
+
2074
+ myenum = {
2075
+ 1: "ONE",
2076
+ 2: "TWO",
2077
+ 3: "THREE",
2078
+ }
2079
+
2080
+ grid.set_enum("my_enum_field", myenum)
2065
2081
  """
2066
2082
  if enum:
2067
- raw_value = obj[key]
2068
- if raw_value:
2069
- return raw_value.value
2083
+
2084
+ if isinstance(enum, EnumType):
2085
+ if raw_value := obj[key]:
2086
+ return raw_value.value
2087
+
2088
+ if isinstance(enum, dict):
2089
+ return enum.get(value, value)
2070
2090
 
2071
2091
  return value
2072
2092
 
@@ -36,31 +36,7 @@
36
36
  </nav>
37
37
 
38
38
  <div class="buttons">
39
-
40
- % if request.has_perm("master_views.list"):
41
- <wutta-button type="is-primary"
42
- tag="a" href="${url('master_views')}"
43
- icon-left="eye"
44
- label="Master Views"
45
- once />
46
- % endif
47
-
48
- % if request.has_perm("app_tables.list"):
49
- <wutta-button type="is-primary"
50
- tag="a" href="${url('app_tables')}"
51
- icon-left="table"
52
- label="App Tables"
53
- once />
54
- % endif
55
-
56
- % if request.has_perm("alembic.dashboard"):
57
- <wutta-button type="is-primary"
58
- tag="a" href="${url('alembic.dashboard')}"
59
- icon-left="forward"
60
- label="Alembic Dashboard"
61
- once />
62
- % endif
63
-
39
+ ${self.middle_buttons()}
64
40
  </div>
65
41
 
66
42
  <nav class="panel">
@@ -123,6 +99,32 @@
123
99
 
124
100
  </%def>
125
101
 
102
+ <%def name="middle_buttons()">
103
+ % if request.has_perm("master_views.list"):
104
+ <wutta-button type="is-primary"
105
+ tag="a" href="${url('master_views')}"
106
+ icon-left="eye"
107
+ label="Master Views"
108
+ once />
109
+ % endif
110
+
111
+ % if request.has_perm("app_tables.list"):
112
+ <wutta-button type="is-primary"
113
+ tag="a" href="${url('app_tables')}"
114
+ icon-left="table"
115
+ label="App Tables"
116
+ once />
117
+ % endif
118
+
119
+ % if request.has_perm("alembic.dashboard"):
120
+ <wutta-button type="is-primary"
121
+ tag="a" href="${url('alembic.dashboard')}"
122
+ icon-left="forward"
123
+ label="Alembic Dashboard"
124
+ once />
125
+ % endif
126
+ </%def>
127
+
126
128
  <%def name="modify_vue_vars()">
127
129
  ${parent.modify_vue_vars()}
128
130
  <script>
@@ -135,6 +137,3 @@
135
137
 
136
138
  </script>
137
139
  </%def>
138
-
139
-
140
- ${parent.body()}
@@ -0,0 +1,113 @@
1
+ ## -*- coding: utf-8; -*-
2
+ <%inherit file="/master/view.mako" />
3
+
4
+ <%def name="tool_panels()">
5
+ ${parent.tool_panels()}
6
+ ${self.tool_panel_execution()}
7
+ </%def>
8
+
9
+ <%def name="tool_panel_execution()">
10
+ % if master.executable:
11
+ <wutta-tool-panel heading="Execution">
12
+ % if batch.executed:
13
+ <b-notification :closable="false">
14
+ <p class="block">
15
+ Batch was executed<br />
16
+ ${app.render_time_ago(batch.executed)}<br />
17
+ by ${batch.executed_by}
18
+ </p>
19
+ </b-notification>
20
+ % elif why_not_execute:
21
+ <b-notification type="is-warning" :closable="false">
22
+ <p class="block">
23
+ Batch cannot be executed:
24
+ </p>
25
+ <p class="block">
26
+ ${why_not_execute}
27
+ </p>
28
+ </b-notification>
29
+ % else:
30
+ % if master.has_perm('execute'):
31
+ <b-notification type="is-success" :closable="false">
32
+ <p class="block">
33
+ Batch can be executed
34
+ </p>
35
+ <b-button type="is-primary"
36
+ @click="executeInit()"
37
+ icon-pack="fas"
38
+ icon-left="arrow-circle-right">
39
+ Execute Batch
40
+ </b-button>
41
+
42
+ <b-modal has-modal-card
43
+ :active.sync="executeShowDialog">
44
+ <div class="modal-card">
45
+
46
+ <header class="modal-card-head">
47
+ <p class="modal-card-title">Execute ${model_title}</p>
48
+ </header>
49
+
50
+ ## TODO: forcing black text b/c of b-notification
51
+ ## wrapping button, which has white text
52
+ <section class="modal-card-body has-text-black">
53
+ <p class="block has-text-weight-bold">
54
+ What will happen when this batch is executed?
55
+ </p>
56
+ <div class="content">
57
+ ${execution_described|n}
58
+ </div>
59
+ ${h.form(master.get_action_url('execute', batch), ref='executeForm')}
60
+ ${h.csrf_token(request)}
61
+ ${h.end_form()}
62
+ </section>
63
+
64
+ <footer class="modal-card-foot">
65
+ <b-button @click="executeShowDialog = false">
66
+ Cancel
67
+ </b-button>
68
+ <b-button type="is-primary"
69
+ @click="executeSubmit()"
70
+ icon-pack="fas"
71
+ icon-left="arrow-circle-right"
72
+ :disabled="executeSubmitting">
73
+ {{ executeSubmitting ? "Working, please wait..." : "Execute Batch" }}
74
+ </b-button>
75
+ </footer>
76
+
77
+ </div>
78
+ </b-modal>
79
+ </b-notification>
80
+
81
+ % else:
82
+ <b-notification type="is-warning" :closable="false">
83
+ <p class="block">
84
+ Batch may be executed,<br />
85
+ but you do not have permission.
86
+ </p>
87
+ </b-notification>
88
+ % endif
89
+ % endif
90
+ </wutta-tool-panel>
91
+ % endif
92
+ </%def>
93
+
94
+ <%def name="modify_vue_vars()">
95
+ ${parent.modify_vue_vars()}
96
+ % if not batch.executed and not why_not_execute and master.has_perm('execute'):
97
+ <script>
98
+
99
+ ThisPageData.executeShowDialog = false
100
+ ThisPageData.executeSubmitting = false
101
+
102
+ ThisPage.methods.executeInit = function() {
103
+ this.executeShowDialog = true
104
+ }
105
+
106
+ ThisPage.methods.executeSubmit = function() {
107
+ this.executeSubmitting = true
108
+ this.$refs.executeForm.submit()
109
+ }
110
+
111
+ </script>
112
+ % endif
113
+ </%def>
@@ -21,7 +21,8 @@
21
21
 
22
22
  % if master.has_rows:
23
23
  <div style="display: flex; flex-direction: column;">
24
- <div style="display: flex; justify-content: space-between;">
24
+ <div class="block"
25
+ style="display: flex; justify-content: space-between;">
25
26
 
26
27
  ## main form
27
28
  <div style="flex-grow: 1;">
@@ -34,7 +35,6 @@
34
35
  </div>
35
36
 
36
37
  ## rows grid
37
- <br />
38
38
  <h4 class="block is-size-4">${master.get_rows_title() or ''}</h4>
39
39
  ${rows_grid.render_vue_tag()}
40
40
  </div>
@@ -818,7 +818,7 @@
818
818
  } else if (dataType.type == 'UUID') {
819
819
  return `UUID()`
820
820
  } else if (dataType.type == '_fk_uuid_') {
821
- return 'sa.String(length=32)'
821
+ return `UUID()`
822
822
  } else if (dataType.type == '_other_') {
823
823
  return dataType.literal
824
824
  } else {
@@ -947,7 +947,7 @@
947
947
  } else {
948
948
  this.modelImportProblem = false
949
949
  this.modelImported = this.modelImportName
950
- this.revisionMessage = `add table for ${"$"}{this.tableModelTitlePlural}`
950
+ this.revisionMessage = `add ${"$"}{this.tableModelTitlePlural}`
951
951
  }
952
952
  }, 200)
953
953
  })
@@ -2,7 +2,7 @@
2
2
  ################################################################################
3
3
  #
4
4
  # wuttaweb -- Web App for Wutta Framework
5
- # Copyright © 2024-2025 Lance Edgar
5
+ # Copyright © 2024-2026 Lance Edgar
6
6
  #
7
7
  # This file is part of Wutta Framework.
8
8
  #
@@ -32,7 +32,7 @@ import markdown
32
32
  from sqlalchemy import orm
33
33
 
34
34
  from wuttaweb.views import MasterView
35
- from wuttaweb.forms.schema import UserRef
35
+ from wuttaweb.forms.schema import UserRef, WuttaDictEnum
36
36
  from wuttaweb.forms.widgets import BatchIdWidget
37
37
 
38
38
 
@@ -147,6 +147,9 @@ class BatchMasterView(MasterView):
147
147
  # description
148
148
  g.set_link("description")
149
149
 
150
+ # status_code
151
+ g.set_enum("status_code", self.model_class.STATUS)
152
+
150
153
  def render_batch_id( # pylint: disable=empty-docstring,unused-argument
151
154
  self, batch, key, value
152
155
  ):
@@ -191,6 +194,7 @@ class BatchMasterView(MasterView):
191
194
  if self.creating:
192
195
  f.remove("status_code")
193
196
  else:
197
+ f.set_node("status_code", WuttaDictEnum(self.request, batch.STATUS))
194
198
  f.set_readonly("status_code")
195
199
 
196
200
  # created
@@ -441,7 +445,7 @@ class BatchMasterView(MasterView):
441
445
  g.set_renderer("status_code", self.render_row_status)
442
446
 
443
447
  # tool button - create row
444
- if not batch.executed and self.has_perm("create_row"):
448
+ if self.rows_creatable and not batch.executed and self.has_perm("create_row"):
445
449
  button = self.make_button(
446
450
  f"New {self.get_row_model_title()}",
447
451
  primary=True,
@@ -201,19 +201,35 @@ class CommonView(View):
201
201
 
202
202
  # also make "Site Admin" role
203
203
  site_admin_perms = [
204
+ "alembic.migrations.list",
205
+ "alembic.migrations.create",
206
+ "alembic.migrations.view",
207
+ "alembic.migrations.delete",
208
+ "alembic.migrations.configure",
209
+ "alembic.dashboard",
210
+ "alembic.migrate",
211
+ "app_tables.list",
212
+ "app_tables.create",
213
+ "app_tables.view",
204
214
  "appinfo.list",
205
215
  "appinfo.configure",
216
+ "master_views.list",
217
+ "master_views.create",
218
+ "master_views.configure",
219
+ "app_tables.view",
206
220
  "people.list",
207
221
  "people.create",
208
222
  "people.view",
209
223
  "people.edit",
210
224
  "people.delete",
225
+ "people.versions",
211
226
  "roles.list",
212
227
  "roles.create",
213
228
  "roles.view",
214
229
  "roles.edit",
215
230
  "roles.edit_builtin",
216
231
  "roles.delete",
232
+ "roles.versions",
217
233
  "settings.list",
218
234
  "settings.create",
219
235
  "settings.view",
@@ -233,6 +249,7 @@ class CommonView(View):
233
249
  "users.view",
234
250
  "users.edit",
235
251
  "users.delete",
252
+ "users.versions",
236
253
  ]
237
254
  admin2 = model.Role(name="Site Admin")
238
255
  admin2.notes = (
@@ -2,7 +2,7 @@
2
2
  ################################################################################
3
3
  #
4
4
  # wuttaweb -- Web App for Wutta Framework
5
- # Copyright © 2024-2025 Lance Edgar
5
+ # Copyright © 2024-2026 Lance Edgar
6
6
  #
7
7
  # This file is part of Wutta Framework.
8
8
  #
@@ -3514,6 +3514,11 @@ class MasterView(View): # pylint: disable=too-many-public-methods
3514
3514
  if hasattr(cls, "model_title"):
3515
3515
  return cls.model_title
3516
3516
 
3517
+ if model_class := cls.get_model_class():
3518
+ if hasattr(model_class, "__wutta_hint__"):
3519
+ if model_title := model_class.__wutta_hint__.get("model_title"):
3520
+ return model_title
3521
+
3517
3522
  return cls.get_model_name()
3518
3523
 
3519
3524
  @classmethod
@@ -3532,6 +3537,13 @@ class MasterView(View): # pylint: disable=too-many-public-methods
3532
3537
  if hasattr(cls, "model_title_plural"):
3533
3538
  return cls.model_title_plural
3534
3539
 
3540
+ if model_class := cls.get_model_class():
3541
+ if hasattr(model_class, "__wutta_hint__"):
3542
+ if model_title_plural := model_class.__wutta_hint__.get(
3543
+ "model_title_plural"
3544
+ ):
3545
+ return model_title_plural
3546
+
3535
3547
  model_title = cls.get_model_title()
3536
3548
  return f"{model_title}s"
3537
3549
 
@@ -96,7 +96,9 @@ class PersonView(MasterView): # pylint: disable=abstract-method
96
96
  f.remove("full_name")
97
97
 
98
98
  # users
99
- if self.viewing:
99
+ if self.creating or self.editing:
100
+ f.remove("users")
101
+ elif self.viewing:
100
102
  f.set_grid("users", self.make_users_grid(person))
101
103
 
102
104
  def make_users_grid(self, person):
@@ -1602,15 +1602,30 @@ class TestGrid(WebTestCase):
1602
1602
  grid = self.make_grid(columns=["foo", "bar"])
1603
1603
  obj = {"status": None}
1604
1604
 
1605
- # null
1605
+ # true enum, null
1606
1606
  value = grid.render_enum(obj, "status", None, enum=enum.UpgradeStatus)
1607
1607
  self.assertIsNone(value)
1608
1608
 
1609
- # normal
1609
+ # true enum, normal value
1610
1610
  obj["status"] = enum.UpgradeStatus.SUCCESS
1611
1611
  value = grid.render_enum(obj, "status", "SUCCESS", enum=enum.UpgradeStatus)
1612
1612
  self.assertEqual(value, "success")
1613
1613
 
1614
+ # dict enum
1615
+ statuses = {
1616
+ enum.UpgradeStatus.SUCCESS.name: "success",
1617
+ enum.UpgradeStatus.FAILURE.name: "failure",
1618
+ }
1619
+
1620
+ # dict enum, null
1621
+ value = grid.render_enum(obj, "status", None, enum=statuses)
1622
+ self.assertIsNone(value)
1623
+
1624
+ # true enum, normal value
1625
+ obj["status"] = enum.UpgradeStatus.SUCCESS.value
1626
+ value = grid.render_enum(obj, "status", "SUCCESS", enum=statuses)
1627
+ self.assertEqual(value, "success")
1628
+
1614
1629
  def test_render_percent(self):
1615
1630
  grid = self.make_grid(columns=["foo", "bar"])
1616
1631
  obj = MagicMock()
@@ -442,6 +442,7 @@ class TestBatchMasterView(WebTestCase):
442
442
  mod.BatchMasterView,
443
443
  model_class=MockBatch,
444
444
  route_prefix="mock_batches",
445
+ rows_creatable=True,
445
446
  create=True,
446
447
  ):
447
448
  with patch.object(
@@ -251,7 +251,7 @@ version_locations = wuttjamaican.db:alembic/versions
251
251
  with patch.dict(sample, {"overwrite": True}):
252
252
  result = view.wizard_action()
253
253
  self.assertNotIn("error", result)
254
- self.assertGreater(os.path.getsize(module_path), 1000)
254
+ self.assertGreater(os.path.getsize(module_path), 500)
255
255
 
256
256
  def test_check_model(self):
257
257
  view = self.make_view()