WuttaWeb 0.20.3__tar.gz → 0.20.5__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.3 → wuttaweb-0.20.5}/CHANGELOG.md +17 -0
  2. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/PKG-INFO +2 -2
  3. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/pyproject.toml +2 -2
  4. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/forms/base.py +13 -8
  5. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/forms/schema.py +16 -3
  6. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/forms/widgets.py +39 -1
  7. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/grids/base.py +7 -0
  8. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/base.mako +6 -1
  9. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/grids/vue_template.mako +12 -0
  10. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/page.mako +1 -0
  11. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/views/master.py +8 -0
  12. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/forms/test_base.py +14 -0
  13. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/forms/test_widgets.py +40 -0
  14. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/grids/test_base.py +2 -2
  15. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/.gitignore +0 -0
  16. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/COPYING.txt +0 -0
  17. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/README.md +0 -0
  18. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/Makefile +0 -0
  19. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/_static/.keepme +0 -0
  20. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.app.rst +0 -0
  21. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.auth.rst +0 -0
  22. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.cli.rst +0 -0
  23. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.cli.webapp.rst +0 -0
  24. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.conf.rst +0 -0
  25. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.db.continuum.rst +0 -0
  26. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.db.rst +0 -0
  27. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.db.sess.rst +0 -0
  28. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.emails.rst +0 -0
  29. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.forms.base.rst +0 -0
  30. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.forms.rst +0 -0
  31. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.forms.schema.rst +0 -0
  32. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.forms.widgets.rst +0 -0
  33. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.grids.base.rst +0 -0
  34. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.grids.filters.rst +0 -0
  35. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.grids.rst +0 -0
  36. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.handler.rst +0 -0
  37. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.helpers.rst +0 -0
  38. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.menus.rst +0 -0
  39. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.progress.rst +0 -0
  40. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.rst +0 -0
  41. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.static.rst +0 -0
  42. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.subscribers.rst +0 -0
  43. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.util.rst +0 -0
  44. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.views.auth.rst +0 -0
  45. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.views.base.rst +0 -0
  46. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.views.batch.rst +0 -0
  47. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.views.common.rst +0 -0
  48. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.views.email.rst +0 -0
  49. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.views.essential.rst +0 -0
  50. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.views.master.rst +0 -0
  51. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.views.people.rst +0 -0
  52. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.views.progress.rst +0 -0
  53. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.views.reports.rst +0 -0
  54. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.views.roles.rst +0 -0
  55. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.views.rst +0 -0
  56. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.views.settings.rst +0 -0
  57. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.views.upgrades.rst +0 -0
  58. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/api/wuttaweb.views.users.rst +0 -0
  59. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/conf.py +0 -0
  60. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/glossary.rst +0 -0
  61. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/index.rst +0 -0
  62. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/make.bat +0 -0
  63. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/narr/cli/builtin.rst +0 -0
  64. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/narr/cli/index.rst +0 -0
  65. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/narr/templates/base.rst +0 -0
  66. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/narr/templates/index.rst +0 -0
  67. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/narr/templates/lookup.rst +0 -0
  68. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/docs/narr/templates/overview.rst +0 -0
  69. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/__init__.py +0 -0
  70. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/_version.py +0 -0
  71. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/app.py +0 -0
  72. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/auth.py +0 -0
  73. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/cli/__init__.py +0 -0
  74. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/cli/webapp.py +0 -0
  75. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/conf.py +0 -0
  76. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/db/__init__.py +0 -0
  77. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/db/continuum.py +0 -0
  78. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/db/sess.py +0 -0
  79. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/email-templates/feedback.html.mako +0 -0
  80. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/email-templates/feedback.txt.mako +0 -0
  81. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/emails.py +0 -0
  82. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/forms/__init__.py +0 -0
  83. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/grids/__init__.py +0 -0
  84. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/grids/filters.py +0 -0
  85. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/handler.py +0 -0
  86. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/helpers.py +0 -0
  87. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/menus.py +0 -0
  88. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/progress.py +0 -0
  89. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/static/__init__.py +0 -0
  90. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/static/img/favicon.ico +0 -0
  91. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/static/img/logo.png +0 -0
  92. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/static/img/testing.png +0 -0
  93. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/subscribers.py +0 -0
  94. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
  95. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/appinfo/index.mako +0 -0
  96. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/auth/change_password.mako +0 -0
  97. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/auth/login.mako +0 -0
  98. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/base_meta.mako +0 -0
  99. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/batch/view.mako +0 -0
  100. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/configure.mako +0 -0
  101. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
  102. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
  103. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
  104. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/deform/dateinput.pt +0 -0
  105. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/deform/datetimeinput.pt +0 -0
  106. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/deform/moneyinput.pt +0 -0
  107. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/deform/password.pt +0 -0
  108. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/deform/permissions.pt +0 -0
  109. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/deform/readonly/checkbox.pt +0 -0
  110. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/deform/readonly/email_recips.pt +0 -0
  111. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/deform/readonly/filedownload.pt +0 -0
  112. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
  113. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
  114. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
  115. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
  116. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/deform/select.pt +0 -0
  117. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/deform/textarea.pt +0 -0
  118. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/deform/textinput.pt +0 -0
  119. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/email/settings/view.mako +0 -0
  120. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/forbidden.mako +0 -0
  121. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/form.mako +0 -0
  122. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
  123. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/grids/table_element.mako +0 -0
  124. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/home.mako +0 -0
  125. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/master/configure.mako +0 -0
  126. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/master/create.mako +0 -0
  127. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/master/delete.mako +0 -0
  128. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/master/edit.mako +0 -0
  129. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/master/form.mako +0 -0
  130. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/master/index.mako +0 -0
  131. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/master/view.mako +0 -0
  132. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/notfound.mako +0 -0
  133. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/people/view_profile.mako +0 -0
  134. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/progress.mako +0 -0
  135. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/reports/view.mako +0 -0
  136. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/setup.mako +0 -0
  137. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/upgrade.mako +0 -0
  138. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/upgrades/configure.mako +0 -0
  139. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/upgrades/view.mako +0 -0
  140. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/templates/wutta-components.mako +0 -0
  141. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/testing.py +0 -0
  142. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/util.py +0 -0
  143. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/views/__init__.py +0 -0
  144. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/views/auth.py +0 -0
  145. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/views/base.py +0 -0
  146. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/views/batch.py +0 -0
  147. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/views/common.py +0 -0
  148. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/views/email.py +0 -0
  149. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/views/essential.py +0 -0
  150. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/views/people.py +0 -0
  151. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/views/progress.py +0 -0
  152. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/views/reports.py +0 -0
  153. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/views/roles.py +0 -0
  154. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/views/settings.py +0 -0
  155. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/views/upgrades.py +0 -0
  156. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/src/wuttaweb/views/users.py +0 -0
  157. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tasks.py +0 -0
  158. {wuttaweb-0.20.3/tests/views → wuttaweb-0.20.5/tests}/__init__.py +0 -0
  159. {wuttaweb-0.20.3/tests/grids → wuttaweb-0.20.5/tests/cli}/__init__.py +0 -0
  160. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/cli/test_webapp.py +0 -0
  161. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/db/__init__.py +0 -0
  162. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/db/test_continuum.py +0 -0
  163. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/forms/test_schema.py +0 -0
  164. {wuttaweb-0.20.3/tests/cli → wuttaweb-0.20.5/tests/grids}/__init__.py +0 -0
  165. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/grids/test_filters.py +0 -0
  166. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
  167. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
  168. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/libcache/bb_oruga.js +0 -0
  169. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/libcache/bb_oruga_bulma.css +0 -0
  170. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/libcache/bb_oruga_bulma.js +0 -0
  171. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/libcache/bb_vue.js +0 -0
  172. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/libcache/bb_vue_fontawesome.js +0 -0
  173. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/libcache/buefy.css +0 -0
  174. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/libcache/buefy.js +0 -0
  175. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/libcache/fontawesome.js +0 -0
  176. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/libcache/vue.js +0 -0
  177. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/libcache/vue_resource.js +0 -0
  178. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/test_app.py +0 -0
  179. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/test_auth.py +0 -0
  180. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/test_emails.py +0 -0
  181. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/test_handler.py +0 -0
  182. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/test_helpers.py +0 -0
  183. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/test_menus.py +0 -0
  184. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/test_progress.py +0 -0
  185. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/test_static.py +0 -0
  186. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/test_subscribers.py +0 -0
  187. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/test_util.py +0 -0
  188. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/util.py +0 -0
  189. {wuttaweb-0.20.3/tests → wuttaweb-0.20.5/tests/views}/__init__.py +0 -0
  190. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/views/test___init__.py +0 -0
  191. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/views/test_auth.py +0 -0
  192. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/views/test_base.py +0 -0
  193. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/views/test_batch.py +0 -0
  194. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/views/test_common.py +0 -0
  195. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/views/test_email.py +0 -0
  196. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/views/test_essential.py +0 -0
  197. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/views/test_master.py +0 -0
  198. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/views/test_people.py +0 -0
  199. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/views/test_progress.py +0 -0
  200. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/views/test_reports.py +0 -0
  201. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/views/test_roles.py +0 -0
  202. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/views/test_settings.py +0 -0
  203. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/views/test_upgrades.py +0 -0
  204. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/tests/views/test_users.py +0 -0
  205. {wuttaweb-0.20.3 → wuttaweb-0.20.5}/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.20.5 (2025-01-23)
9
+
10
+ ### Fix
11
+
12
+ - improve styling for grid tools section
13
+ - add basic checkbox support for grids
14
+ - add WuttaRequestMixin for ThisPage component
15
+ - avoid literal `None` when rendering form field value
16
+ - let header title be even wider
17
+
18
+ ## v0.20.4 (2025-01-15)
19
+
20
+ ### Fix
21
+
22
+ - add `WuttaDateWidget` and associated logic
23
+ - add `serialize_object()` method for `ObjectRef` schema node
24
+
8
25
  ## v0.20.3 (2025-01-14)
9
26
 
10
27
  ### Fix
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: WuttaWeb
3
- Version: 0.20.3
3
+ Version: 0.20.5
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.3"
9
+ version = "0.20.5"
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
 
@@ -546,10 +546,13 @@ class Form:
546
546
  This is generally only possible if :attr:`model_class` is set
547
547
  to a valid SQLAlchemy mapped class.
548
548
 
549
- As of writing this only looks for
550
- :class:`sqlalchemy:sqlalchemy.types.DateTime` fields and if
551
- any are found, they are configured to use
552
- :class:`~wuttaweb.forms.widgets.WuttaDateTimeWidget()`.
549
+ This only checks for a couple of data types, with mapping as
550
+ follows:
551
+
552
+ * :class:`sqlalchemy:sqlalchemy.types.Date` ->
553
+ :class:`~wuttaweb.forms.widgets.WuttaDateWidget`
554
+ * :class:`sqlalchemy:sqlalchemy.types.DateTime` ->
555
+ :class:`~wuttaweb.forms.widgets.WuttaDateTimeWidget`
553
556
  """
554
557
  from wuttaweb.forms import widgets
555
558
 
@@ -565,8 +568,9 @@ class Form:
565
568
  prop = getattr(attr, 'prop', None)
566
569
  if prop and isinstance(prop, orm.ColumnProperty):
567
570
  column = prop.columns[0]
568
- if isinstance(column.type, sa.DateTime):
569
- # self.set_renderer(key, self.render_datetime)
571
+ if isinstance(column.type, sa.Date):
572
+ self.set_widget(key, widgets.WuttaDateWidget(self.request))
573
+ elif isinstance(column.type, sa.DateTime):
570
574
  self.set_widget(key, widgets.WuttaDateTimeWidget(self.request))
571
575
 
572
576
  def set_grid(self, key, grid):
@@ -1029,12 +1033,13 @@ class Form:
1029
1033
  # render static text if field not in deform/schema
1030
1034
  # TODO: need to abstract this somehow
1031
1035
  if self.model_instance:
1032
- html = str(self.model_instance[fieldname])
1036
+ value = self.model_instance[fieldname]
1037
+ html = str(value) if value is not None else ''
1033
1038
  else:
1034
1039
  html = ''
1035
1040
 
1036
1041
  # mark all that as safe
1037
- html = HTML.literal(html)
1042
+ html = HTML.literal(html or ' ')
1038
1043
 
1039
1044
  # render field label
1040
1045
  label = self.get_label(fieldname)
@@ -334,8 +334,21 @@ class ObjectRef(colander.SchemaType):
334
334
  # nb. keep a ref to this for later use
335
335
  node.model_instance = appstruct
336
336
 
337
- # serialize to uuid
338
- return appstruct.uuid.hex
337
+ # serialize to PK as string
338
+ return self.serialize_object(appstruct)
339
+
340
+ def serialize_object(self, obj):
341
+ """
342
+ Serialize the given object to its primary key as string.
343
+
344
+ Default logic assumes the object has a UUID; subclass can
345
+ override as needed.
346
+
347
+ :param obj: Object reference for the node.
348
+
349
+ :returns: Object primary key as string.
350
+ """
351
+ return obj.uuid.hex
339
352
 
340
353
  def deserialize(self, node, cstruct):
341
354
  """ """
@@ -417,7 +430,7 @@ class ObjectRef(colander.SchemaType):
417
430
  if 'values' not in kwargs:
418
431
  query = self.get_query()
419
432
  objects = query.all()
420
- values = [(obj.uuid.hex, str(obj))
433
+ values = [(self.serialize_object(obj), str(obj))
421
434
  for obj in objects]
422
435
  if self.empty_option:
423
436
  values.insert(0, self.empty_option)
@@ -36,6 +36,7 @@ in the namespace:
36
36
  * :class:`deform:deform.widget.CheckboxWidget`
37
37
  * :class:`deform:deform.widget.SelectWidget`
38
38
  * :class:`deform:deform.widget.CheckboxChoiceWidget`
39
+ * :class:`deform:deform.widget.DateInputWidget`
39
40
  * :class:`deform:deform.widget.DateTimeInputWidget`
40
41
  * :class:`deform:deform.widget.MoneyInputWidget`
41
42
  """
@@ -49,7 +50,7 @@ import humanize
49
50
  from deform.widget import (Widget, TextInputWidget, TextAreaWidget,
50
51
  PasswordWidget, CheckedPasswordWidget,
51
52
  CheckboxWidget, SelectWidget, CheckboxChoiceWidget,
52
- DateTimeInputWidget, MoneyInputWidget)
53
+ DateInputWidget, DateTimeInputWidget, MoneyInputWidget)
53
54
  from webhelpers2.html import HTML
54
55
 
55
56
  from wuttjamaican.conf import parse_list
@@ -153,6 +154,43 @@ class WuttaCheckboxChoiceWidget(CheckboxChoiceWidget):
153
154
  self.app = self.config.get_app()
154
155
 
155
156
 
157
+ class WuttaDateWidget(DateInputWidget):
158
+ """
159
+ Custom widget for :class:`python:datetime.date` fields.
160
+
161
+ The main purpose of this widget is to leverage
162
+ :meth:`~wuttjamaican:wuttjamaican.app.AppHandler.render_date()`
163
+ for the readonly display.
164
+
165
+ It is automatically used for SQLAlchemy mapped classes where the
166
+ field maps to a :class:`sqlalchemy:sqlalchemy.types.Date` column.
167
+ For other (non-mapped) date fields, or mapped datetime fields for
168
+ which a date widget is preferred, use
169
+ :meth:`~wuttaweb.forms.base.Form.set_widget()`.
170
+
171
+ This is a subclass of
172
+ :class:`deform:deform.widget.DateInputWidget` and uses these
173
+ Deform templates:
174
+
175
+ * ``dateinput``
176
+ """
177
+
178
+ def __init__(self, request, *args, **kwargs):
179
+ super().__init__(*args, **kwargs)
180
+ self.request = request
181
+ self.config = self.request.wutta_config
182
+ self.app = self.config.get_app()
183
+
184
+ def serialize(self, field, cstruct, **kw):
185
+ """ """
186
+ readonly = kw.get('readonly', self.readonly)
187
+ if readonly and cstruct:
188
+ dt = datetime.datetime.fromisoformat(cstruct)
189
+ return self.app.render_date(dt)
190
+
191
+ return super().serialize(field, cstruct, **kw)
192
+
193
+
156
194
  class WuttaDateTimeWidget(DateTimeInputWidget):
157
195
  """
158
196
  Custom widget for :class:`python:datetime.datetime` fields.
@@ -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 []
@@ -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
  ##############################
@@ -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
  },
@@ -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)
@@ -3,6 +3,8 @@
3
3
  from unittest import TestCase
4
4
  from unittest.mock import MagicMock, patch
5
5
 
6
+ import sqlalchemy as sa
7
+
6
8
  import colander
7
9
  import deform
8
10
  from pyramid import testing
@@ -188,6 +190,18 @@ class TestForm(TestCase):
188
190
  self.assertIn('created', form.widgets)
189
191
  self.assertIsInstance(form.widgets['created'], MyWidget)
190
192
 
193
+ # mock up a table with all relevant column types
194
+ class Whatever(model.Base):
195
+ __tablename__ = 'whatever'
196
+ id = sa.Column(sa.Integer(), primary_key=True)
197
+ date = sa.Column(sa.Date())
198
+ date_time = sa.Column(sa.DateTime())
199
+
200
+ # widget set for all known types
201
+ form = self.make_form(model_class=Whatever)
202
+ self.assertIsInstance(form.widgets['date'], widgets.WuttaDateWidget)
203
+ self.assertIsInstance(form.widgets['date_time'], widgets.WuttaDateTimeWidget)
204
+
191
205
  def test_set_grid(self):
192
206
  form = self.make_form(fields=['foo', 'bar'])
193
207
  self.assertNotIn('foo', form.widgets)
@@ -87,6 +87,46 @@ class TestObjectRefWidget(WebTestCase):
87
87
  self.assertNotIn('url', values)
88
88
 
89
89
 
90
+ class TestWuttaDateWidget(WebTestCase):
91
+
92
+ def make_field(self, node, **kwargs):
93
+ # TODO: not sure why default renderer is in use even though
94
+ # pyramid_deform was included in setup? but this works..
95
+ kwargs.setdefault('renderer', deform.Form.default_renderer)
96
+ return deform.Field(node, **kwargs)
97
+
98
+ def make_widget(self, **kwargs):
99
+ return mod.WuttaDateWidget(self.request, **kwargs)
100
+
101
+ def test_serialize(self):
102
+ node = colander.SchemaNode(colander.Date())
103
+ field = self.make_field(node)
104
+
105
+ # first try normal date
106
+ widget = self.make_widget()
107
+ dt = datetime.date(2025, 1, 15)
108
+
109
+ # editable widget has normal picker html
110
+ result = widget.serialize(field, str(dt))
111
+ self.assertIn('<wutta-datepicker', result)
112
+
113
+ # readonly is rendered per app convention
114
+ result = widget.serialize(field, str(dt), readonly=True)
115
+ self.assertEqual(result, '2025-01-15')
116
+
117
+ # now try again with datetime
118
+ widget = self.make_widget()
119
+ dt = datetime.datetime(2025, 1, 15, 8, 35)
120
+
121
+ # editable widget has normal picker html
122
+ result = widget.serialize(field, str(dt))
123
+ self.assertIn('<wutta-datepicker', result)
124
+
125
+ # readonly is rendered per app convention
126
+ result = widget.serialize(field, str(dt), readonly=True)
127
+ self.assertEqual(result, '2025-01-15')
128
+
129
+
90
130
  class TestWuttaDateTimeWidget(WebTestCase):
91
131
 
92
132
  def make_field(self, node, **kwargs):
@@ -1433,7 +1433,7 @@ class TestGrid(WebTestCase):
1433
1433
  # null
1434
1434
  obj = MagicMock(dt=None)
1435
1435
  result = grid.render_date(obj, 'dt', None)
1436
- self.assertIsNone(result)
1436
+ self.assertEqual(result, '')
1437
1437
 
1438
1438
  # typical
1439
1439
  dt = datetime.date(2025, 1, 13)
@@ -1446,7 +1446,7 @@ class TestGrid(WebTestCase):
1446
1446
 
1447
1447
  obj = MagicMock(dt=None)
1448
1448
  result = grid.render_datetime(obj, 'dt', None)
1449
- self.assertIsNone(result)
1449
+ self.assertEqual(result, '')
1450
1450
 
1451
1451
  dt = datetime.datetime(2024, 12, 12, 13, 44, tzinfo=datetime.timezone.utc)
1452
1452
  obj = MagicMock(dt=dt)
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes