WuttaWeb 0.20.0__tar.gz → 0.20.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 (206) hide show
  1. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/CHANGELOG.md +9 -0
  2. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/PKG-INFO +2 -2
  3. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/pyproject.toml +5 -2
  4. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/grids/filters.py +29 -0
  5. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/handler.py +75 -7
  6. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/appinfo/configure.mako +17 -0
  7. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/views/master.py +10 -3
  8. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/views/settings.py +12 -4
  9. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/grids/test_filters.py +24 -0
  10. wuttaweb-0.20.1/tests/test_handler.py +149 -0
  11. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/views/test_master.py +42 -34
  12. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/views/test_reports.py +7 -2
  13. wuttaweb-0.20.0/tests/test_handler.py +0 -76
  14. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/.gitignore +0 -0
  15. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/COPYING.txt +0 -0
  16. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/README.md +0 -0
  17. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/Makefile +0 -0
  18. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/_static/.keepme +0 -0
  19. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.app.rst +0 -0
  20. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.auth.rst +0 -0
  21. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.cli.rst +0 -0
  22. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.cli.webapp.rst +0 -0
  23. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.conf.rst +0 -0
  24. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.db.continuum.rst +0 -0
  25. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.db.rst +0 -0
  26. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.db.sess.rst +0 -0
  27. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.emails.rst +0 -0
  28. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.forms.base.rst +0 -0
  29. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.forms.rst +0 -0
  30. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.forms.schema.rst +0 -0
  31. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.forms.widgets.rst +0 -0
  32. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.grids.base.rst +0 -0
  33. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.grids.filters.rst +0 -0
  34. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.grids.rst +0 -0
  35. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.handler.rst +0 -0
  36. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.helpers.rst +0 -0
  37. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.menus.rst +0 -0
  38. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.progress.rst +0 -0
  39. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.rst +0 -0
  40. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.static.rst +0 -0
  41. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.subscribers.rst +0 -0
  42. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.util.rst +0 -0
  43. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.views.auth.rst +0 -0
  44. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.views.base.rst +0 -0
  45. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.views.batch.rst +0 -0
  46. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.views.common.rst +0 -0
  47. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.views.email.rst +0 -0
  48. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.views.essential.rst +0 -0
  49. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.views.master.rst +0 -0
  50. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.views.people.rst +0 -0
  51. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.views.progress.rst +0 -0
  52. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.views.reports.rst +0 -0
  53. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.views.roles.rst +0 -0
  54. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.views.rst +0 -0
  55. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.views.settings.rst +0 -0
  56. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.views.upgrades.rst +0 -0
  57. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/api/wuttaweb.views.users.rst +0 -0
  58. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/conf.py +0 -0
  59. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/glossary.rst +0 -0
  60. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/index.rst +0 -0
  61. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/make.bat +0 -0
  62. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/narr/cli/builtin.rst +0 -0
  63. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/narr/cli/index.rst +0 -0
  64. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/narr/templates/base.rst +0 -0
  65. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/narr/templates/index.rst +0 -0
  66. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/narr/templates/lookup.rst +0 -0
  67. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/docs/narr/templates/overview.rst +0 -0
  68. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/__init__.py +0 -0
  69. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/_version.py +0 -0
  70. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/app.py +0 -0
  71. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/auth.py +0 -0
  72. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/cli/__init__.py +0 -0
  73. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/cli/webapp.py +0 -0
  74. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/conf.py +0 -0
  75. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/db/__init__.py +0 -0
  76. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/db/continuum.py +0 -0
  77. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/db/sess.py +0 -0
  78. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/email-templates/feedback.html.mako +0 -0
  79. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/email-templates/feedback.txt.mako +0 -0
  80. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/emails.py +0 -0
  81. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/forms/__init__.py +0 -0
  82. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/forms/base.py +0 -0
  83. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/forms/schema.py +0 -0
  84. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/forms/widgets.py +0 -0
  85. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/grids/__init__.py +0 -0
  86. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/grids/base.py +0 -0
  87. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/helpers.py +0 -0
  88. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/menus.py +0 -0
  89. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/progress.py +0 -0
  90. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/static/__init__.py +0 -0
  91. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/static/img/favicon.ico +0 -0
  92. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/static/img/logo.png +0 -0
  93. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/static/img/testing.png +0 -0
  94. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/subscribers.py +0 -0
  95. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/appinfo/index.mako +0 -0
  96. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/auth/change_password.mako +0 -0
  97. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/auth/login.mako +0 -0
  98. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/base.mako +0 -0
  99. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/base_meta.mako +0 -0
  100. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/batch/view.mako +0 -0
  101. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/configure.mako +0 -0
  102. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
  103. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
  104. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
  105. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/deform/dateinput.pt +0 -0
  106. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/deform/datetimeinput.pt +0 -0
  107. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/deform/moneyinput.pt +0 -0
  108. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/deform/password.pt +0 -0
  109. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/deform/permissions.pt +0 -0
  110. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/deform/readonly/checkbox.pt +0 -0
  111. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/deform/readonly/email_recips.pt +0 -0
  112. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/deform/readonly/filedownload.pt +0 -0
  113. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
  114. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
  115. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
  116. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
  117. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/deform/select.pt +0 -0
  118. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/deform/textarea.pt +0 -0
  119. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/deform/textinput.pt +0 -0
  120. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/email/settings/view.mako +0 -0
  121. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/forbidden.mako +0 -0
  122. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/form.mako +0 -0
  123. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
  124. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/grids/table_element.mako +0 -0
  125. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/grids/vue_template.mako +0 -0
  126. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/home.mako +0 -0
  127. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/master/configure.mako +0 -0
  128. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/master/create.mako +0 -0
  129. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/master/delete.mako +0 -0
  130. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/master/edit.mako +0 -0
  131. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/master/form.mako +0 -0
  132. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/master/index.mako +0 -0
  133. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/master/view.mako +0 -0
  134. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/notfound.mako +0 -0
  135. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/page.mako +0 -0
  136. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/people/view_profile.mako +0 -0
  137. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/progress.mako +0 -0
  138. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/reports/view.mako +0 -0
  139. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/setup.mako +0 -0
  140. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/upgrade.mako +0 -0
  141. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/upgrades/configure.mako +0 -0
  142. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/upgrades/view.mako +0 -0
  143. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/templates/wutta-components.mako +0 -0
  144. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/testing.py +0 -0
  145. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/util.py +0 -0
  146. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/views/__init__.py +0 -0
  147. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/views/auth.py +0 -0
  148. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/views/base.py +0 -0
  149. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/views/batch.py +0 -0
  150. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/views/common.py +0 -0
  151. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/views/email.py +0 -0
  152. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/views/essential.py +0 -0
  153. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/views/people.py +0 -0
  154. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/views/progress.py +0 -0
  155. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/views/reports.py +0 -0
  156. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/views/roles.py +0 -0
  157. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/views/upgrades.py +0 -0
  158. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/src/wuttaweb/views/users.py +0 -0
  159. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tasks.py +0 -0
  160. {wuttaweb-0.20.0/tests/views → wuttaweb-0.20.1/tests}/__init__.py +0 -0
  161. {wuttaweb-0.20.0/tests/grids → wuttaweb-0.20.1/tests/cli}/__init__.py +0 -0
  162. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/cli/test_webapp.py +0 -0
  163. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/db/__init__.py +0 -0
  164. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/db/test_continuum.py +0 -0
  165. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/forms/test_base.py +0 -0
  166. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/forms/test_schema.py +0 -0
  167. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/forms/test_widgets.py +0 -0
  168. {wuttaweb-0.20.0/tests/cli → wuttaweb-0.20.1/tests/grids}/__init__.py +0 -0
  169. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/grids/test_base.py +0 -0
  170. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
  171. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
  172. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/libcache/bb_oruga.js +0 -0
  173. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/libcache/bb_oruga_bulma.css +0 -0
  174. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/libcache/bb_oruga_bulma.js +0 -0
  175. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/libcache/bb_vue.js +0 -0
  176. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/libcache/bb_vue_fontawesome.js +0 -0
  177. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/libcache/buefy.css +0 -0
  178. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/libcache/buefy.js +0 -0
  179. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/libcache/fontawesome.js +0 -0
  180. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/libcache/vue.js +0 -0
  181. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/libcache/vue_resource.js +0 -0
  182. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/test_app.py +0 -0
  183. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/test_auth.py +0 -0
  184. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/test_emails.py +0 -0
  185. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/test_helpers.py +0 -0
  186. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/test_menus.py +0 -0
  187. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/test_progress.py +0 -0
  188. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/test_static.py +0 -0
  189. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/test_subscribers.py +0 -0
  190. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/test_util.py +0 -0
  191. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/util.py +0 -0
  192. {wuttaweb-0.20.0/tests → wuttaweb-0.20.1/tests/views}/__init__.py +0 -0
  193. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/views/test___init__.py +0 -0
  194. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/views/test_auth.py +0 -0
  195. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/views/test_base.py +0 -0
  196. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/views/test_batch.py +0 -0
  197. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/views/test_common.py +0 -0
  198. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/views/test_email.py +0 -0
  199. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/views/test_essential.py +0 -0
  200. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/views/test_people.py +0 -0
  201. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/views/test_progress.py +0 -0
  202. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/views/test_roles.py +0 -0
  203. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/views/test_settings.py +0 -0
  204. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/views/test_upgrades.py +0 -0
  205. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tests/views/test_users.py +0 -0
  206. {wuttaweb-0.20.0 → wuttaweb-0.20.1}/tox.ini +0 -0
@@ -5,6 +5,15 @@ 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.1 (2025-01-13)
9
+
10
+ ### Fix
11
+
12
+ - expose setting to choose menu handler, in appinfo/configure
13
+ - use prop key instead of column name, for master view model key
14
+ - add grid filters specific to numeric, integer types
15
+ - use default value for config settings
16
+
8
17
  ## v0.20.0 (2025-01-11)
9
18
 
10
19
  ### Feat
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: WuttaWeb
3
- Version: 0.20.0
3
+ Version: 0.20.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
@@ -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.0
42
+ Requires-Dist: wuttjamaican[db]>=0.20.1
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.0"
9
+ version = "0.20.1"
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.0",
47
+ "WuttJamaican[db]>=0.20.1",
48
48
  "zope.sqlalchemy>=1.5",
49
49
  ]
50
50
 
@@ -70,6 +70,9 @@ wuttaweb = "wuttaweb.conf:WuttaWebConfigExtension"
70
70
  [project.entry-points."wutta.typer_imports"]
71
71
  wuttaweb = "wuttaweb.cli"
72
72
 
73
+ [project.entry-points."wutta.web.menus"]
74
+ wuttaweb = "wuttaweb.menus:MenuHandler"
75
+
73
76
 
74
77
  [project.urls]
75
78
  Homepage = "https://wuttaproject.org/"
@@ -465,6 +465,33 @@ class StringAlchemyFilter(AlchemyFilter):
465
465
  sa.and_(*criteria)))
466
466
 
467
467
 
468
+ class NumericAlchemyFilter(AlchemyFilter):
469
+ """
470
+ SQLAlchemy filter option for a numeric data column.
471
+
472
+ Subclass of :class:`AlchemyFilter`.
473
+ """
474
+ default_verbs = ['equal', 'not_equal',
475
+ 'greater_than', 'greater_equal',
476
+ 'less_than', 'less_equal']
477
+
478
+
479
+ class IntegerAlchemyFilter(NumericAlchemyFilter):
480
+ """
481
+ SQLAlchemy filter option for an integer data column.
482
+
483
+ Subclass of :class:`NumericAlchemyFilter`.
484
+ """
485
+
486
+ def coerce_value(self, value):
487
+ """ """
488
+ if value:
489
+ try:
490
+ return int(value)
491
+ except:
492
+ pass
493
+
494
+
468
495
  class BooleanAlchemyFilter(AlchemyFilter):
469
496
  """
470
497
  SQLAlchemy filter option for a boolean data column.
@@ -568,6 +595,8 @@ default_sqlalchemy_filters = {
568
595
  None: AlchemyFilter,
569
596
  sa.String: StringAlchemyFilter,
570
597
  sa.Text: StringAlchemyFilter,
598
+ sa.Numeric: NumericAlchemyFilter,
599
+ sa.Integer: IntegerAlchemyFilter,
571
600
  sa.Boolean: BooleanAlchemyFilter,
572
601
  sa.Date: DateAlchemyFilter,
573
602
  }
@@ -24,7 +24,10 @@
24
24
  Web Handler
25
25
  """
26
26
 
27
+ import warnings
28
+
27
29
  from wuttjamaican.app import GenericHandler
30
+ from wuttjamaican.util import load_entry_points
28
31
 
29
32
  from wuttaweb import static, forms, grids
30
33
 
@@ -106,22 +109,87 @@ class WebHandler(GenericHandler):
106
109
 
107
110
  def get_menu_handler(self, **kwargs):
108
111
  """
109
- Get the configured "menu" handler for the web app.
112
+ Get the configured :term:`menu handler` for the web app.
110
113
 
111
114
  Specify a custom handler in your config file like this:
112
115
 
113
116
  .. code-block:: ini
114
117
 
115
118
  [wutta.web]
116
- menus.handler_spec = poser.web.menus:PoserMenuHandler
119
+ menus.handler.spec = poser.web.menus:PoserMenuHandler
117
120
 
118
121
  :returns: Instance of :class:`~wuttaweb.menus.MenuHandler`.
119
122
  """
120
- if not hasattr(self, 'menu_handler'):
121
- spec = self.config.get(f'{self.appname}.web.menus.handler_spec',
122
- default='wuttaweb.menus:MenuHandler')
123
- self.menu_handler = self.app.load_object(spec)(self.config)
124
- return self.menu_handler
123
+ spec = self.config.get(f'{self.appname}.web.menus.handler.spec')
124
+ if not spec:
125
+ spec = self.config.get(f'{self.appname}.web.menus.handler_spec')
126
+ if spec:
127
+ warnings.warn(f"setting '{self.appname}.web.menus.handler_spec' is deprecated; "
128
+ f"please use '{self.appname}.web.menus.handler_spec' instead",
129
+ DeprecationWarning)
130
+ else:
131
+ spec = self.config.get(f'{self.appname}.web.menus.handler.default_spec',
132
+ default='wuttaweb.menus:MenuHandler')
133
+ factory = self.app.load_object(spec)
134
+ return factory(self.config)
135
+
136
+ def get_menu_handler_specs(self, default=None):
137
+ """
138
+ Get the :term:`spec` strings for all available :term:`menu
139
+ handlers <menu handler>`. See also
140
+ :meth:`get_menu_handler()`.
141
+
142
+ :param default: Default spec string(s) to include, even if not
143
+ registered. Can be a string or list of strings.
144
+
145
+ :returns: List of menu handler spec strings.
146
+
147
+ This will gather available spec strings from the following:
148
+
149
+ First, the ``default`` as provided by caller.
150
+
151
+ Second, the default spec from config, if set; for example:
152
+
153
+ .. code-block:: ini
154
+
155
+ [wutta.web]
156
+ menus.handler.default_spec = poser.web.menus:PoserMenuHandler
157
+
158
+ Third, each spec registered via entry points. For instance in
159
+ ``pyproject.toml``:
160
+
161
+ .. code-block:: toml
162
+
163
+ [project.entry-points."wutta.web.menus"]
164
+ poser = "poser.web.menus:PoserMenuHandler"
165
+
166
+ The final list will be "sorted" according to the above, with
167
+ the latter registered handlers being sorted alphabetically.
168
+ """
169
+ handlers = []
170
+
171
+ # defaults from caller
172
+ if isinstance(default, str):
173
+ handlers.append(default)
174
+ elif default:
175
+ handlers.extend(default)
176
+
177
+ # configured default, if applicable
178
+ default = self.config.get(f'{self.config.appname}.web.menus.handler.default_spec')
179
+ if default and default not in handlers:
180
+ handlers.append(default)
181
+
182
+ # registered via entry points
183
+ registered = []
184
+ for Handler in load_entry_points(f'{self.appname}.web.menus').values():
185
+ spec = Handler.get_spec()
186
+ if spec not in handlers:
187
+ registered.append(spec)
188
+ if registered:
189
+ registered.sort()
190
+ handlers.extend(registered)
191
+
192
+ return handlers
125
193
 
126
194
  def make_form(self, request, **kwargs):
127
195
  """
@@ -41,6 +41,21 @@
41
41
  </b-checkbox>
42
42
  </b-field>
43
43
 
44
+ <b-field label="Menu Handler">
45
+ <input type="hidden"
46
+ name="${app.appname}.web.menus.handler.spec"
47
+ :value="simpleSettings['${app.appname}.web.menus.handler.spec']" />
48
+ <b-select v-model="simpleSettings['${app.appname}.web.menus.handler.spec']"
49
+ @input="settingsNeedSaved = true">
50
+ <option :value="null">(use default)</option>
51
+ <option v-for="handler in menuHandlers"
52
+ :key="handler.spec"
53
+ :value="handler.spec">
54
+ {{ handler.spec }}
55
+ </option>
56
+ </b-select>
57
+ </b-field>
58
+
44
59
  </div>
45
60
 
46
61
  <h3 class="block is-size-3">User/Auth</h3>
@@ -241,6 +256,8 @@
241
256
  ${parent.modify_vue_vars()}
242
257
  <script>
243
258
 
259
+ ThisPageData.menuHandlers = ${json.dumps(menu_handlers)|n}
260
+
244
261
  ThisPageData.weblibs = ${json.dumps(weblibs or [])|n}
245
262
 
246
263
  ThisPageData.editWebLibraryShowDialog = false
@@ -1224,7 +1224,7 @@ class MasterView(View):
1224
1224
  elif simple.get('type') is bool:
1225
1225
  value = self.config.get_bool(name, default=simple.get('default', False))
1226
1226
  else:
1227
- value = self.config.get(name)
1227
+ value = self.config.get(name, default=simple.get('default'))
1228
1228
 
1229
1229
  normalized[name] = value
1230
1230
 
@@ -2707,6 +2707,9 @@ class MasterView(View):
2707
2707
  represents a Wutta-based SQLAlchemy model, the return value
2708
2708
  for this method is: ``('uuid',)``
2709
2709
 
2710
+ Any class mapped via SQLAlchemy should be supported
2711
+ automatically, the keys are determined from class inspection.
2712
+
2710
2713
  But there is no "sane" default for other scenarios, in which
2711
2714
  case subclass should define :attr:`model_key`. If the model
2712
2715
  key cannot be determined, raises ``AttributeError``.
@@ -2721,8 +2724,12 @@ class MasterView(View):
2721
2724
 
2722
2725
  model_class = cls.get_model_class()
2723
2726
  if model_class:
2724
- mapper = sa.inspect(model_class)
2725
- return tuple([column.key for column in mapper.primary_key])
2727
+ # nb. we want the primary key but must avoid column names
2728
+ # in case mapped class uses different prop keys
2729
+ inspector = sa.inspect(model_class)
2730
+ keys = [col.name for col in inspector.primary_key]
2731
+ return tuple([prop.key for prop in inspector.column_attrs
2732
+ if [col.name for col in prop.columns] == keys])
2726
2733
 
2727
2734
  raise AttributeError(f"you must define model_key for view class: {cls}")
2728
2735
 
@@ -129,6 +129,10 @@ class AppInfoView(MasterView):
129
129
  {'name': f'{self.config.appname}.node_title'},
130
130
  {'name': f'{self.config.appname}.production',
131
131
  'type': bool},
132
+ {'name': f'{self.config.appname}.web.menus.handler.spec'},
133
+ # nb. this is deprecated; we define so it is auto-deleted
134
+ # when we replace with newer setting
135
+ {'name': f'{self.config.appname}.web.menus.handler_spec'},
132
136
 
133
137
  # user/auth
134
138
  {'name': 'wuttaweb.home_redirect_to_login',
@@ -164,11 +168,15 @@ class AppInfoView(MasterView):
164
168
 
165
169
  def configure_get_context(self, **kwargs):
166
170
  """ """
167
-
168
- # normal context
169
171
  context = super().configure_get_context(**kwargs)
170
172
 
171
- # we will add `weblibs` to context, based on config values
173
+ # add registered menu handlers
174
+ web = self.app.get_web_handler()
175
+ handlers = web.get_menu_handler_specs()
176
+ handlers = [{'spec': spec} for spec in handlers]
177
+ context['menu_handlers'] = handlers
178
+
179
+ # add `weblibs` to context, based on config values
172
180
  weblibs = self.get_weblibs()
173
181
  for key in weblibs:
174
182
  title = weblibs[key]
@@ -192,8 +200,8 @@ class AppInfoView(MasterView):
192
200
  'live_url': get_liburl(self.request, key,
193
201
  prefix=self.weblib_config_prefix),
194
202
  }
195
-
196
203
  context['weblibs'] = list(weblibs.values())
204
+
197
205
  return context
198
206
 
199
207
 
@@ -326,6 +326,30 @@ class TestStringAlchemyFilter(WebTestCase):
326
326
  self.assertEqual(filtered_query.count(), 6)
327
327
 
328
328
 
329
+ class TestIntegerAlchemyFilter(WebTestCase):
330
+
331
+ def make_filter(self, model_property, **kwargs):
332
+ factory = kwargs.pop('factory', mod.IntegerAlchemyFilter)
333
+ kwargs['model_property'] = model_property
334
+ return factory(self.request, model_property.key, **kwargs)
335
+
336
+ def test_coerce_value(self):
337
+ model = self.app.model
338
+ filtr = self.make_filter(model.Upgrade.exit_code)
339
+
340
+ # null
341
+ self.assertIsNone(filtr.coerce_value(None))
342
+ self.assertIsNone(filtr.coerce_value(''))
343
+
344
+ # typical
345
+ self.assertEqual(filtr.coerce_value('42'), 42)
346
+ self.assertEqual(filtr.coerce_value('-42'), -42)
347
+
348
+ # invalid
349
+ self.assertIsNone(filtr.coerce_value('42.12'))
350
+ self.assertIsNone(filtr.coerce_value('bogus'))
351
+
352
+
329
353
  class TestBooleanAlchemyFilter(WebTestCase):
330
354
 
331
355
  def setUp(self):
@@ -0,0 +1,149 @@
1
+ # -*- coding: utf-8; -*-
2
+
3
+ from unittest.mock import patch
4
+
5
+ from wuttaweb import handler as mod, static
6
+ from wuttaweb.forms import Form
7
+ from wuttaweb.grids import Grid
8
+ from wuttaweb.menus import MenuHandler
9
+ from wuttaweb.testing import WebTestCase
10
+
11
+
12
+ class MockMenuHandler(MenuHandler):
13
+ pass
14
+
15
+ class LegacyMenuHandler(MenuHandler):
16
+ pass
17
+
18
+ class AnotherMenuHandler(MenuHandler):
19
+ pass
20
+
21
+
22
+ class TestWebHandler(WebTestCase):
23
+
24
+ def make_handler(self):
25
+ return mod.WebHandler(self.config)
26
+
27
+ def test_get_fanstatic_url(self):
28
+ handler = self.make_handler()
29
+
30
+ # default with / root path
31
+ url = handler.get_fanstatic_url(self.request, static.logo)
32
+ self.assertEqual(url, '/fanstatic/wuttaweb_img/logo.png')
33
+
34
+ # what about a subpath
35
+ self.request.script_name = '/testing'
36
+ url = handler.get_fanstatic_url(self.request, static.logo)
37
+ self.assertEqual(url, '/testing/fanstatic/wuttaweb_img/logo.png')
38
+
39
+ def test_get_favicon_url(self):
40
+ handler = self.make_handler()
41
+
42
+ # default
43
+ url = handler.get_favicon_url(self.request)
44
+ self.assertEqual(url, '/fanstatic/wuttaweb_img/favicon.ico')
45
+
46
+ # config override
47
+ self.config.setdefault('wuttaweb.favicon_url', '/testing/other.ico')
48
+ url = handler.get_favicon_url(self.request)
49
+ self.assertEqual(url, '/testing/other.ico')
50
+
51
+ def test_get_header_logo_url(self):
52
+ handler = self.make_handler()
53
+
54
+ # default
55
+ url = handler.get_header_logo_url(self.request)
56
+ self.assertEqual(url, '/fanstatic/wuttaweb_img/favicon.ico')
57
+
58
+ # config override
59
+ self.config.setdefault('wuttaweb.header_logo_url', '/testing/header.png')
60
+ url = handler.get_header_logo_url(self.request)
61
+ self.assertEqual(url, '/testing/header.png')
62
+
63
+ def test_get_main_logo_url(self):
64
+ handler = self.make_handler()
65
+
66
+ # default
67
+ url = handler.get_main_logo_url(self.request)
68
+ self.assertEqual(url, '/fanstatic/wuttaweb_img/logo.png')
69
+
70
+ # config override
71
+ self.config.setdefault('wuttaweb.logo_url', '/testing/other.png')
72
+ url = handler.get_main_logo_url(self.request)
73
+ self.assertEqual(url, '/testing/other.png')
74
+
75
+ def test_get_menu_handler(self):
76
+ handler = self.make_handler()
77
+
78
+ # built-in default
79
+ menus = handler.get_menu_handler()
80
+ self.assertIsInstance(menus, MenuHandler)
81
+ self.assertIs(type(menus), MenuHandler)
82
+
83
+ # configured default
84
+ self.config.setdefault('wutta.web.menus.handler.default_spec',
85
+ 'tests.test_handler:MockMenuHandler')
86
+ menus = handler.get_menu_handler()
87
+ self.assertIsInstance(menus, MockMenuHandler)
88
+
89
+ # configured handler (legacy)
90
+ self.config.setdefault('wutta.web.menus.handler_spec',
91
+ 'tests.test_handler:LegacyMenuHandler')
92
+ menus = handler.get_menu_handler()
93
+ self.assertIsInstance(menus, LegacyMenuHandler)
94
+
95
+ # configued handler (proper)
96
+ self.config.setdefault('wutta.web.menus.handler.spec',
97
+ 'tests.test_handler:AnotherMenuHandler')
98
+ menus = handler.get_menu_handler()
99
+ self.assertIsInstance(menus, AnotherMenuHandler)
100
+
101
+ def test_get_menu_handler_specs(self):
102
+ handler = self.make_handler()
103
+
104
+ # at least one spec by default
105
+ specs = handler.get_menu_handler_specs()
106
+ self.assertIn('wuttaweb.menus:MenuHandler', specs)
107
+
108
+ # caller can specify default as string
109
+ specs = handler.get_menu_handler_specs(default='tests.test_handler:MockMenuHandler')
110
+ self.assertIn('wuttaweb.menus:MenuHandler', specs)
111
+ self.assertIn('tests.test_handler:MockMenuHandler', specs)
112
+ self.assertNotIn('tests.test_handler:AnotherMenuHandler', specs)
113
+
114
+ # caller can specify default as list
115
+ specs = handler.get_menu_handler_specs(default=[
116
+ 'tests.test_handler:MockMenuHandler',
117
+ 'tests.test_handler:AnotherMenuHandler'])
118
+ self.assertIn('wuttaweb.menus:MenuHandler', specs)
119
+ self.assertIn('tests.test_handler:MockMenuHandler', specs)
120
+ self.assertIn('tests.test_handler:AnotherMenuHandler', specs)
121
+
122
+ # default can be configured
123
+ self.config.setdefault('wutta.web.menus.handler.default_spec',
124
+ 'tests.test_handler:AnotherMenuHandler')
125
+ specs = handler.get_menu_handler_specs()
126
+ self.assertIn('wuttaweb.menus:MenuHandler', specs)
127
+ self.assertNotIn('tests.test_handler:MockMenuHandler', specs)
128
+ self.assertIn('tests.test_handler:AnotherMenuHandler', specs)
129
+
130
+ # the rest come from entry points
131
+ with patch.object(mod, 'load_entry_points', return_value={
132
+ 'legacy': LegacyMenuHandler,
133
+ }):
134
+ specs = handler.get_menu_handler_specs()
135
+ self.assertNotIn('wuttaweb.menus:MenuHandler', specs)
136
+ self.assertNotIn('tests.test_handler:MockMenuHandler', specs)
137
+ self.assertIn('tests.test_handler:LegacyMenuHandler', specs)
138
+ # nb. this remains from previous config default
139
+ self.assertIn('tests.test_handler:AnotherMenuHandler', specs)
140
+
141
+ def test_make_form(self):
142
+ handler = self.make_handler()
143
+ form = handler.make_form(self.request)
144
+ self.assertIsInstance(form, Form)
145
+
146
+ def test_make_grid(self):
147
+ handler = self.make_handler()
148
+ grid = handler.make_grid(self.request)
149
+ self.assertIsInstance(grid, Grid)
@@ -1528,42 +1528,50 @@ class TestMasterView(WebTestCase):
1528
1528
  get_index_url=MagicMock(return_value='/appinfo/'),
1529
1529
  configure_get_simple_settings=MagicMock(return_value=settings)):
1530
1530
 
1531
- # get the form page
1532
- response = view.configure(session=self.session)
1533
- self.assertIsInstance(response, Response)
1534
-
1535
- # post request to save settings
1536
- self.request.method = 'POST'
1537
- self.request.POST = {
1538
- 'wutta.app_title': 'Wutta',
1539
- 'wutta.foo': 'bar',
1540
- 'wutta.flag': 'true',
1541
- }
1542
- response = view.configure(session=self.session)
1543
- # nb. should get redirect back to configure page
1544
- self.assertEqual(response.status_code, 302)
1531
+ # nb. appinfo/configure template requires menu_handlers
1532
+ original_context = view.configure_get_context
1533
+ def get_context(**kw):
1534
+ kw = original_context(**kw)
1535
+ kw['menu_handlers'] = []
1536
+ return kw
1537
+ with patch.object(view, 'configure_get_context', new=get_context):
1538
+
1539
+ # get the form page
1540
+ response = view.configure(session=self.session)
1541
+ self.assertIsInstance(response, Response)
1542
+
1543
+ # post request to save settings
1544
+ self.request.method = 'POST'
1545
+ self.request.POST = {
1546
+ 'wutta.app_title': 'Wutta',
1547
+ 'wutta.foo': 'bar',
1548
+ 'wutta.flag': 'true',
1549
+ }
1550
+ response = view.configure(session=self.session)
1551
+ # nb. should get redirect back to configure page
1552
+ self.assertEqual(response.status_code, 302)
1545
1553
 
1546
- # should now have 5 settings
1547
- count = self.session.query(model.Setting).count()
1548
- self.assertEqual(count, 5)
1549
- get_setting = functools.partial(self.app.get_setting, self.session)
1550
- self.assertEqual(get_setting('wutta.app_title'), 'Wutta')
1551
- self.assertEqual(get_setting('wutta.foo'), 'bar')
1552
- self.assertEqual(get_setting('wutta.flag'), 'true')
1553
- self.assertEqual(get_setting('wutta.number'), '42')
1554
- self.assertEqual(get_setting('wutta.value1'), '')
1555
- self.assertEqual(get_setting('wutta.value2'), None)
1556
-
1557
- # post request to remove settings
1558
- self.request.method = 'POST'
1559
- self.request.POST = {'remove_settings': '1'}
1560
- response = view.configure(session=self.session)
1561
- # nb. should get redirect back to configure page
1562
- self.assertEqual(response.status_code, 302)
1554
+ # should now have 5 settings
1555
+ count = self.session.query(model.Setting).count()
1556
+ self.assertEqual(count, 5)
1557
+ get_setting = functools.partial(self.app.get_setting, self.session)
1558
+ self.assertEqual(get_setting('wutta.app_title'), 'Wutta')
1559
+ self.assertEqual(get_setting('wutta.foo'), 'bar')
1560
+ self.assertEqual(get_setting('wutta.flag'), 'true')
1561
+ self.assertEqual(get_setting('wutta.number'), '42')
1562
+ self.assertEqual(get_setting('wutta.value1'), '')
1563
+ self.assertEqual(get_setting('wutta.value2'), None)
1564
+
1565
+ # post request to remove settings
1566
+ self.request.method = 'POST'
1567
+ self.request.POST = {'remove_settings': '1'}
1568
+ response = view.configure(session=self.session)
1569
+ # nb. should get redirect back to configure page
1570
+ self.assertEqual(response.status_code, 302)
1563
1571
 
1564
- # should now have 0 settings
1565
- count = self.session.query(model.Setting).count()
1566
- self.assertEqual(count, 0)
1572
+ # should now have 0 settings
1573
+ count = self.session.query(model.Setting).count()
1574
+ self.assertEqual(count, 0)
1567
1575
 
1568
1576
  def test_configure_gather_settings(self):
1569
1577
  view = self.make_view()
@@ -51,8 +51,13 @@ class TestReportViews(WebTestCase):
51
51
 
52
52
  def test_get_grid_data(self):
53
53
  view = self.make_view()
54
- data = view.get_grid_data()
55
- self.assertIsInstance(data, list)
54
+ providers = dict(self.app.providers)
55
+ providers['wuttatest'] = MagicMock(report_modules=['tests.views.test_reports'])
56
+ with patch.object(self.app, 'providers', new=providers):
57
+
58
+ data = view.get_grid_data()
59
+ self.assertIsInstance(data, list)
60
+ self.assertTrue(data) # 1+ reports
56
61
 
57
62
  def test_normalize_report(self):
58
63
  view = self.make_view()