django-spire 0.0.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 (405) hide show
  1. django_spire-0.0.1/LICENSE.md +22 -0
  2. django_spire-0.0.1/PKG-INFO +39 -0
  3. django_spire-0.0.1/README.rst +62 -0
  4. django_spire-0.0.1/django_spire/__init__.py +0 -0
  5. django_spire-0.0.1/django_spire/ai/__init__.py +0 -0
  6. django_spire-0.0.1/django_spire/ai/apps.py +7 -0
  7. django_spire-0.0.1/django_spire/ai/migrations/__init__.py +0 -0
  8. django_spire-0.0.1/django_spire/ai/tests/__init__.py +0 -0
  9. django_spire-0.0.1/django_spire/ai/urls.py +14 -0
  10. django_spire-0.0.1/django_spire/ai/views.py +48 -0
  11. django_spire-0.0.1/django_spire/authentication/__init__.py +0 -0
  12. django_spire-0.0.1/django_spire/authentication/apps.py +7 -0
  13. django_spire-0.0.1/django_spire/authentication/mfa/__init__.py +0 -0
  14. django_spire-0.0.1/django_spire/authentication/mfa/admin.py +21 -0
  15. django_spire-0.0.1/django_spire/authentication/mfa/apps.py +7 -0
  16. django_spire-0.0.1/django_spire/authentication/mfa/forms.py +31 -0
  17. django_spire-0.0.1/django_spire/authentication/mfa/migrations/0001_initial.py +29 -0
  18. django_spire-0.0.1/django_spire/authentication/mfa/migrations/__init__.py +0 -0
  19. django_spire-0.0.1/django_spire/authentication/mfa/models.py +61 -0
  20. django_spire-0.0.1/django_spire/authentication/mfa/querysets.py +9 -0
  21. django_spire-0.0.1/django_spire/authentication/mfa/tests/__init__.py +0 -0
  22. django_spire-0.0.1/django_spire/authentication/mfa/urls/__init__.py +9 -0
  23. django_spire-0.0.1/django_spire/authentication/mfa/urls/page_urls.py +12 -0
  24. django_spire-0.0.1/django_spire/authentication/mfa/urls/redirect_urls.py +12 -0
  25. django_spire-0.0.1/django_spire/authentication/mfa/utils.py +17 -0
  26. django_spire-0.0.1/django_spire/authentication/mfa/views/__init__.py +0 -0
  27. django_spire-0.0.1/django_spire/authentication/mfa/views/page_views.py +36 -0
  28. django_spire-0.0.1/django_spire/authentication/mfa/views/redirect_views.py +20 -0
  29. django_spire-0.0.1/django_spire/authentication/migrations/__init__.py +0 -0
  30. django_spire-0.0.1/django_spire/authentication/templates/spire/authentication/mfa/mfa_form.html +25 -0
  31. django_spire-0.0.1/django_spire/authentication/templates/spire/authentication/page/authentication_page.html +26 -0
  32. django_spire-0.0.1/django_spire/authentication/templates/spire/authentication/page/login_page.html +23 -0
  33. django_spire-0.0.1/django_spire/authentication/templates/spire/authentication/page/logout_page.html +23 -0
  34. django_spire-0.0.1/django_spire/authentication/templates/spire/authentication/page/password_change_done_page.html +12 -0
  35. django_spire-0.0.1/django_spire/authentication/templates/spire/authentication/page/password_change_page.html +14 -0
  36. django_spire-0.0.1/django_spire/authentication/templates/spire/authentication/page/password_reset_complete_page.html +10 -0
  37. django_spire-0.0.1/django_spire/authentication/templates/spire/authentication/page/password_reset_confirmation_page.html +16 -0
  38. django_spire-0.0.1/django_spire/authentication/templates/spire/authentication/page/password_reset_done_page.html +10 -0
  39. django_spire-0.0.1/django_spire/authentication/templates/spire/authentication/page/password_reset_key_done_page.html +13 -0
  40. django_spire-0.0.1/django_spire/authentication/templates/spire/authentication/page/password_reset_key_form_page.html +24 -0
  41. django_spire-0.0.1/django_spire/authentication/templates/spire/authentication/page/password_reset_page.html +16 -0
  42. django_spire-0.0.1/django_spire/authentication/templates/spire/authentication/page/password_set_form_page.html +12 -0
  43. django_spire-0.0.1/django_spire/authentication/templates/spire/authentication/password_reset_email.html +14 -0
  44. django_spire-0.0.1/django_spire/authentication/tests/__init__.py +0 -0
  45. django_spire-0.0.1/django_spire/authentication/tests/test_url_endpoints.py +29 -0
  46. django_spire-0.0.1/django_spire/authentication/urls/__init__.py +10 -0
  47. django_spire-0.0.1/django_spire/authentication/urls/admin_urls.py +52 -0
  48. django_spire-0.0.1/django_spire/authentication/urls/redirect_urls.py +16 -0
  49. django_spire-0.0.1/django_spire/authentication/views/__init__.py +0 -0
  50. django_spire-0.0.1/django_spire/authentication/views/admin_views.py +48 -0
  51. django_spire-0.0.1/django_spire/authentication/views/redirect_views.py +26 -0
  52. django_spire-0.0.1/django_spire/breadcrumb/__init__.py +0 -0
  53. django_spire-0.0.1/django_spire/breadcrumb/apps.py +7 -0
  54. django_spire-0.0.1/django_spire/breadcrumb/models.py +76 -0
  55. django_spire-0.0.1/django_spire/breadcrumb/tests/__init__.py +0 -0
  56. django_spire-0.0.1/django_spire/comment/__init__.py +0 -0
  57. django_spire-0.0.1/django_spire/comment/admin.py +43 -0
  58. django_spire-0.0.1/django_spire/comment/apps.py +7 -0
  59. django_spire-0.0.1/django_spire/comment/factories.py +11 -0
  60. django_spire-0.0.1/django_spire/comment/forms.py +20 -0
  61. django_spire-0.0.1/django_spire/comment/migrations/0001_initial.py +40 -0
  62. django_spire-0.0.1/django_spire/comment/migrations/__init__.py +0 -0
  63. django_spire-0.0.1/django_spire/comment/mixins.py +37 -0
  64. django_spire-0.0.1/django_spire/comment/models.py +78 -0
  65. django_spire-0.0.1/django_spire/comment/querysets.py +20 -0
  66. django_spire-0.0.1/django_spire/comment/templates/spire/comment/card/comment_list_card.html +30 -0
  67. django_spire-0.0.1/django_spire/comment/templates/spire/comment/card/comment_textarea_card.html +7 -0
  68. django_spire-0.0.1/django_spire/comment/templates/spire/comment/container/comment_container.html +21 -0
  69. django_spire-0.0.1/django_spire/comment/templates/spire/comment/container/comment_reply_list_container.html +7 -0
  70. django_spire-0.0.1/django_spire/comment/templates/spire/comment/element/comment_button_element.html +165 -0
  71. django_spire-0.0.1/django_spire/comment/templates/spire/comment/element/comment_edit_link.html +5 -0
  72. django_spire-0.0.1/django_spire/comment/templates/spire/comment/element/comment_reply_element.html +8 -0
  73. django_spire-0.0.1/django_spire/comment/templates/spire/comment/element/comment_reply_link.html +5 -0
  74. django_spire-0.0.1/django_spire/comment/templates/spire/comment/element/comment_textarea_element.html +120 -0
  75. django_spire-0.0.1/django_spire/comment/templates/spire/comment/form/comment_form.html +18 -0
  76. django_spire-0.0.1/django_spire/comment/templates/spire/comment/form/content/comment_form_content.html +13 -0
  77. django_spire-0.0.1/django_spire/comment/templates/spire/comment/item/comment_item.html +42 -0
  78. django_spire-0.0.1/django_spire/comment/templates/spire/comment/item/comment_item_ellipsis.html +22 -0
  79. django_spire-0.0.1/django_spire/comment/templates/spire/comment/item/comment_reply_item.html +30 -0
  80. django_spire-0.0.1/django_spire/comment/templates/spire/comment/modal/comment_edit_modal.html +17 -0
  81. django_spire-0.0.1/django_spire/comment/templates/spire/comment/modal/comment_modal.html +17 -0
  82. django_spire-0.0.1/django_spire/comment/templates/spire/comment/modal/comment_reply_modal.html +23 -0
  83. django_spire-0.0.1/django_spire/comment/templates/spire/comment/widget/tagging_widget.html +130 -0
  84. django_spire-0.0.1/django_spire/comment/templatetags/__init__.py +0 -0
  85. django_spire-0.0.1/django_spire/comment/templatetags/comment_tags.py +29 -0
  86. django_spire-0.0.1/django_spire/comment/tests/__init__.py +0 -0
  87. django_spire-0.0.1/django_spire/comment/urls.py +20 -0
  88. django_spire-0.0.1/django_spire/comment/utils.py +45 -0
  89. django_spire-0.0.1/django_spire/comment/views.py +136 -0
  90. django_spire-0.0.1/django_spire/comment/widgets.py +28 -0
  91. django_spire-0.0.1/django_spire/consts.py +1 -0
  92. django_spire-0.0.1/django_spire/core/__init__.py +0 -0
  93. django_spire-0.0.1/django_spire/core/apps.py +7 -0
  94. django_spire-0.0.1/django_spire/core/context_processors.py +12 -0
  95. django_spire-0.0.1/django_spire/core/management/__init__.py +0 -0
  96. django_spire-0.0.1/django_spire/core/management/commands/__init__.py +0 -0
  97. django_spire-0.0.1/django_spire/core/management/commands/spire_bootstrap.py +93 -0
  98. django_spire-0.0.1/django_spire/core/management/commands/spire_remove_migration.py +62 -0
  99. django_spire-0.0.1/django_spire/core/redirect/__init__.py +1 -0
  100. django_spire-0.0.1/django_spire/core/redirect/generic_redirect.py +37 -0
  101. django_spire-0.0.1/django_spire/core/redirect/safe_redirect.py +70 -0
  102. django_spire-0.0.1/django_spire/core/shortcuts.py +47 -0
  103. django_spire-0.0.1/django_spire/core/static/spire/css/app-background.css +108 -0
  104. django_spire-0.0.1/django_spire/core/static/spire/css/app-border.css +105 -0
  105. django_spire-0.0.1/django_spire/core/static/spire/css/app-button.css +143 -0
  106. django_spire-0.0.1/django_spire/core/static/spire/css/app-text.css +101 -0
  107. django_spire-0.0.1/django_spire/core/static/spire/css/app.css +44 -0
  108. django_spire-0.0.1/django_spire/core/static/spire/css/base.css +53 -0
  109. django_spire-0.0.1/django_spire/core/static/spire/css/bootstrap-extension.css +640 -0
  110. django_spire-0.0.1/django_spire/core/static/spire/css/bootstrap-override.css +0 -0
  111. django_spire-0.0.1/django_spire/core/static/spire/css/bootstrap.css +7 -0
  112. django_spire-0.0.1/django_spire/core/static/spire/css/flatpickr.min.css +13 -0
  113. django_spire-0.0.1/django_spire/core/static/spire/favicons/android-chrome-192x192.png +0 -0
  114. django_spire-0.0.1/django_spire/core/static/spire/favicons/android-chrome-256x256.png +0 -0
  115. django_spire-0.0.1/django_spire/core/static/spire/favicons/apple-touch-icon.png +0 -0
  116. django_spire-0.0.1/django_spire/core/static/spire/favicons/favicon-16x16.png +0 -0
  117. django_spire-0.0.1/django_spire/core/static/spire/favicons/favicon-32x32.png +0 -0
  118. django_spire-0.0.1/django_spire/core/static/spire/favicons/mstile-150x150.png +0 -0
  119. django_spire-0.0.1/django_spire/core/static/spire/js/Chart.min.js +13 -0
  120. django_spire-0.0.1/django_spire/core/static/spire/js/ajax.js +25 -0
  121. django_spire-0.0.1/django_spire/core/static/spire/js/alpine-plugins.min.js +9 -0
  122. django_spire-0.0.1/django_spire/core/static/spire/js/alpine.min.js +5 -0
  123. django_spire-0.0.1/django_spire/core/static/spire/js/axios.min.js +3 -0
  124. django_spire-0.0.1/django_spire/core/static/spire/js/base.js +86 -0
  125. django_spire-0.0.1/django_spire/core/static/spire/js/bootstrap.bundle.min.js +7 -0
  126. django_spire-0.0.1/django_spire/core/static/spire/js/csrf.js +18 -0
  127. django_spire-0.0.1/django_spire/core/static/spire/js/dispatch_modal.js +5 -0
  128. django_spire-0.0.1/django_spire/core/static/spire/js/flatpickr.js +2 -0
  129. django_spire-0.0.1/django_spire/core/static/spire/js/spire_alpine.js +33 -0
  130. django_spire-0.0.1/django_spire/core/templates/spire/403.html +36 -0
  131. django_spire-0.0.1/django_spire/core/templates/spire/404.html +34 -0
  132. django_spire-0.0.1/django_spire/core/templates/spire/500.html +7 -0
  133. django_spire-0.0.1/django_spire/core/templates/spire/accordion/accordion.html +23 -0
  134. django_spire-0.0.1/django_spire/core/templates/spire/badge/accent_badge.html +3 -0
  135. django_spire-0.0.1/django_spire/core/templates/spire/badge/base_badge.html +8 -0
  136. django_spire-0.0.1/django_spire/core/templates/spire/badge/danger_badge.html +3 -0
  137. django_spire-0.0.1/django_spire/core/templates/spire/badge/primary_badge.html +3 -0
  138. django_spire-0.0.1/django_spire/core/templates/spire/badge/primary_outlined_badge.html +3 -0
  139. django_spire-0.0.1/django_spire/core/templates/spire/badge/secondary_badge.html +3 -0
  140. django_spire-0.0.1/django_spire/core/templates/spire/badge/secondary_outlined_badge.html +3 -0
  141. django_spire-0.0.1/django_spire/core/templates/spire/badge/success_badge.html +3 -0
  142. django_spire-0.0.1/django_spire/core/templates/spire/badge/warning_badge.html +3 -0
  143. django_spire-0.0.1/django_spire/core/templates/spire/base/base.html +91 -0
  144. django_spire-0.0.1/django_spire/core/templates/spire/button/accent_button.html +3 -0
  145. django_spire-0.0.1/django_spire/core/templates/spire/button/accent_dark_button.html +3 -0
  146. django_spire-0.0.1/django_spire/core/templates/spire/button/accent_outlined_button.html +3 -0
  147. django_spire-0.0.1/django_spire/core/templates/spire/button/base_button.html +16 -0
  148. django_spire-0.0.1/django_spire/core/templates/spire/button/danger_button.html +3 -0
  149. django_spire-0.0.1/django_spire/core/templates/spire/button/danger_dark_button.html +3 -0
  150. django_spire-0.0.1/django_spire/core/templates/spire/button/danger_outlined_button.html +3 -0
  151. django_spire-0.0.1/django_spire/core/templates/spire/button/primary_button.html +3 -0
  152. django_spire-0.0.1/django_spire/core/templates/spire/button/primary_dark_button.html +3 -0
  153. django_spire-0.0.1/django_spire/core/templates/spire/button/primary_outlined_button.html +3 -0
  154. django_spire-0.0.1/django_spire/core/templates/spire/button/secondary_button.html +3 -0
  155. django_spire-0.0.1/django_spire/core/templates/spire/button/secondary_dark_button.html +3 -0
  156. django_spire-0.0.1/django_spire/core/templates/spire/button/secondary_outlined_button.html +3 -0
  157. django_spire-0.0.1/django_spire/core/templates/spire/button/success_button.html +3 -0
  158. django_spire-0.0.1/django_spire/core/templates/spire/button/success_dark_button.html +3 -0
  159. django_spire-0.0.1/django_spire/core/templates/spire/button/success_outlined_button.html +3 -0
  160. django_spire-0.0.1/django_spire/core/templates/spire/button/warning_button.html +3 -0
  161. django_spire-0.0.1/django_spire/core/templates/spire/button/warning_dark_button.html +3 -0
  162. django_spire-0.0.1/django_spire/core/templates/spire/button/warning_outlined_button.html +3 -0
  163. django_spire-0.0.1/django_spire/core/templates/spire/card/card.html +7 -0
  164. django_spire-0.0.1/django_spire/core/templates/spire/card/form_card.html +21 -0
  165. django_spire-0.0.1/django_spire/core/templates/spire/card/title_card.html +31 -0
  166. django_spire-0.0.1/django_spire/core/templates/spire/container/container.html +27 -0
  167. django_spire-0.0.1/django_spire/core/templates/spire/container/form_container.html +15 -0
  168. django_spire-0.0.1/django_spire/core/templates/spire/dropdown/dropdown.html +14 -0
  169. django_spire-0.0.1/django_spire/core/templates/spire/dropdown/element/dropdown_link_element.html +15 -0
  170. django_spire-0.0.1/django_spire/core/templates/spire/dropdown/element/ellipsis_dropdown_modal_link_element.html +8 -0
  171. django_spire-0.0.1/django_spire/core/templates/spire/dropdown/ellipsis_dropdown.html +25 -0
  172. django_spire-0.0.1/django_spire/core/templates/spire/dropdown/ellipsis_modal_dropdown.html +25 -0
  173. django_spire-0.0.1/django_spire/core/templates/spire/element/attribute_element.html +34 -0
  174. django_spire-0.0.1/django_spire/core/templates/spire/element/breadcrumb_element.html +19 -0
  175. django_spire-0.0.1/django_spire/core/templates/spire/element/divider_element.html +11 -0
  176. django_spire-0.0.1/django_spire/core/templates/spire/element/no_data_element.html +3 -0
  177. django_spire-0.0.1/django_spire/core/templates/spire/element/page_loading_element.html +10 -0
  178. django_spire-0.0.1/django_spire/core/templates/spire/element/pagination_element.html +45 -0
  179. django_spire-0.0.1/django_spire/core/templates/spire/item/element/item_ellipsis_spacer_element.html +1 -0
  180. django_spire-0.0.1/django_spire/core/templates/spire/item/item.html +29 -0
  181. django_spire-0.0.1/django_spire/core/templates/spire/item/no_data_item.html +11 -0
  182. django_spire-0.0.1/django_spire/core/templates/spire/modal/center_modal.html +17 -0
  183. django_spire-0.0.1/django_spire/core/templates/spire/modal/content/modal_title_content.html +26 -0
  184. django_spire-0.0.1/django_spire/core/templates/spire/modal/dispatch_modal.html +8 -0
  185. django_spire-0.0.1/django_spire/core/templates/spire/modal/element/model_close_element.html +1 -0
  186. django_spire-0.0.1/django_spire/core/templates/spire/modal/modal.html +67 -0
  187. django_spire-0.0.1/django_spire/core/templates/spire/modal/title_modal.html +22 -0
  188. django_spire-0.0.1/django_spire/core/templates/spire/navigation/accordion/cookbook_accordion.html +20 -0
  189. django_spire-0.0.1/django_spire/core/templates/spire/navigation/accordion/dashboard_nav_accordion.html +12 -0
  190. django_spire-0.0.1/django_spire/core/templates/spire/navigation/accordion/nav_accordion.html +43 -0
  191. django_spire-0.0.1/django_spire/core/templates/spire/navigation/accordion/settings_nav_accordion.html +19 -0
  192. django_spire-0.0.1/django_spire/core/templates/spire/navigation/elements/nav_link.html +9 -0
  193. django_spire-0.0.1/django_spire/core/templates/spire/navigation/elements/nav_title_divider.html +3 -0
  194. django_spire-0.0.1/django_spire/core/templates/spire/navigation/elements/search_bar_element.html +15 -0
  195. django_spire-0.0.1/django_spire/core/templates/spire/navigation/mobile_navigation.html +34 -0
  196. django_spire-0.0.1/django_spire/core/templates/spire/navigation/navigation_links.html +19 -0
  197. django_spire-0.0.1/django_spire/core/templates/spire/navigation/side_navigation.html +21 -0
  198. django_spire-0.0.1/django_spire/core/templates/spire/navigation/top_navigation.html +73 -0
  199. django_spire-0.0.1/django_spire/core/templates/spire/note/info_note.html +3 -0
  200. django_spire-0.0.1/django_spire/core/templates/spire/note/note.html +14 -0
  201. django_spire-0.0.1/django_spire/core/templates/spire/page/center_card_page.html +19 -0
  202. django_spire-0.0.1/django_spire/core/templates/spire/page/form_full_page.html +15 -0
  203. django_spire-0.0.1/django_spire/core/templates/spire/page/full_page.html +38 -0
  204. django_spire-0.0.1/django_spire/core/templates/spire/page/page.html +18 -0
  205. django_spire-0.0.1/django_spire/core/templates/spire/tab/element/tab_section_element.html +8 -0
  206. django_spire-0.0.1/django_spire/core/templates/spire/tab/element/tab_trigger_element.html +12 -0
  207. django_spire-0.0.1/django_spire/core/templates/spire/tab/tab.html +67 -0
  208. django_spire-0.0.1/django_spire/core/templates/spire/wizard/wizard.html +66 -0
  209. django_spire-0.0.1/django_spire/core/templates/spire/wizard/wizard_page.html +3 -0
  210. django_spire-0.0.1/django_spire/core/templatetags/__init__.py +0 -0
  211. django_spire-0.0.1/django_spire/core/templatetags/core_tags.py +67 -0
  212. django_spire-0.0.1/django_spire/core/tests/__init__.py +0 -0
  213. django_spire-0.0.1/django_spire/core/tests/test_cases.py +16 -0
  214. django_spire-0.0.1/django_spire/file/__init__.py +0 -0
  215. django_spire-0.0.1/django_spire/file/admin.py +33 -0
  216. django_spire-0.0.1/django_spire/file/apps.py +7 -0
  217. django_spire-0.0.1/django_spire/file/fields.py +46 -0
  218. django_spire-0.0.1/django_spire/file/forms.py +25 -0
  219. django_spire-0.0.1/django_spire/file/interfaces.py +175 -0
  220. django_spire-0.0.1/django_spire/file/migrations/0001_initial.py +39 -0
  221. django_spire-0.0.1/django_spire/file/migrations/__init__.py +0 -0
  222. django_spire-0.0.1/django_spire/file/mixins.py +12 -0
  223. django_spire-0.0.1/django_spire/file/models.py +48 -0
  224. django_spire-0.0.1/django_spire/file/queryset.py +11 -0
  225. django_spire-0.0.1/django_spire/file/templates/spire/file/card/demo_detail_card.html +9 -0
  226. django_spire-0.0.1/django_spire/file/templates/spire/file/card/demo_file_list_card.html +20 -0
  227. django_spire-0.0.1/django_spire/file/templates/spire/file/card/demo_filler_card.html +9 -0
  228. django_spire-0.0.1/django_spire/file/templates/spire/file/card/demo_list_card.html +20 -0
  229. django_spire-0.0.1/django_spire/file/templates/spire/file/item/demo_item.html +16 -0
  230. django_spire-0.0.1/django_spire/file/templates/spire/file/item/file_item.html +22 -0
  231. django_spire-0.0.1/django_spire/file/templates/spire/file/navigation/file_navigation.html +2 -0
  232. django_spire-0.0.1/django_spire/file/templates/spire/file/page/demo_detail_page.html +19 -0
  233. django_spire-0.0.1/django_spire/file/templates/spire/file/page/demo_list_page.html +9 -0
  234. django_spire-0.0.1/django_spire/file/templates/spire/file/page/version_list_page.html +9 -0
  235. django_spire-0.0.1/django_spire/file/templates/spire/file/widget/multiple_file_widget.html +1 -0
  236. django_spire-0.0.1/django_spire/file/templates/spire/file/widget/single_file_widget.html +1 -0
  237. django_spire-0.0.1/django_spire/file/tests/__init__.py +0 -0
  238. django_spire-0.0.1/django_spire/file/urls.py +18 -0
  239. django_spire-0.0.1/django_spire/file/utils.py +8 -0
  240. django_spire-0.0.1/django_spire/file/views.py +33 -0
  241. django_spire-0.0.1/django_spire/file/widgets.py +39 -0
  242. django_spire-0.0.1/django_spire/form/__init__.py +0 -0
  243. django_spire-0.0.1/django_spire/form/confirmation_forms.py +62 -0
  244. django_spire-0.0.1/django_spire/form/templates/spire/form/button/form_submit_button.html +46 -0
  245. django_spire-0.0.1/django_spire/form/utils.py +16 -0
  246. django_spire-0.0.1/django_spire/gamification/__init__.py +0 -0
  247. django_spire-0.0.1/django_spire/gamification/static/spire/css/gamification.css +0 -0
  248. django_spire-0.0.1/django_spire/gamification/templates/spire/gamification/effect/kapow.html +0 -0
  249. django_spire-0.0.1/django_spire/help/__init__.py +0 -0
  250. django_spire-0.0.1/django_spire/help/apps.py +7 -0
  251. django_spire-0.0.1/django_spire/help/templates/spire/help/help_button.html +8 -0
  252. django_spire-0.0.1/django_spire/help/templates/spire/help/help_modal.html +41 -0
  253. django_spire-0.0.1/django_spire/help/templatetags/__init__.py +0 -0
  254. django_spire-0.0.1/django_spire/help/templatetags/help.py +32 -0
  255. django_spire-0.0.1/django_spire/history/__init__.py +0 -0
  256. django_spire-0.0.1/django_spire/history/admin.py +96 -0
  257. django_spire-0.0.1/django_spire/history/apps.py +9 -0
  258. django_spire-0.0.1/django_spire/history/migrations/0001_initial.py +74 -0
  259. django_spire-0.0.1/django_spire/history/migrations/__init__.py +0 -0
  260. django_spire-0.0.1/django_spire/history/mixins.py +121 -0
  261. django_spire-0.0.1/django_spire/history/models.py +129 -0
  262. django_spire-0.0.1/django_spire/history/querysets.py +16 -0
  263. django_spire-0.0.1/django_spire/history/static/spire/js/history.js +0 -0
  264. django_spire-0.0.1/django_spire/history/templates/spire/history/card/activity_list_card.html +17 -0
  265. django_spire-0.0.1/django_spire/history/templates/spire/history/item/activity_item.html +15 -0
  266. django_spire-0.0.1/django_spire/history/tests/__init__.py +0 -0
  267. django_spire-0.0.1/django_spire/history/utils.py +25 -0
  268. django_spire-0.0.1/django_spire/home/__init__.py +0 -0
  269. django_spire-0.0.1/django_spire/home/apps.py +9 -0
  270. django_spire-0.0.1/django_spire/home/migrations/__init__.py +0 -0
  271. django_spire-0.0.1/django_spire/home/templates/spire/home/card/home_detail_card.html +49 -0
  272. django_spire-0.0.1/django_spire/home/templates/spire/home/page/home_page.html +9 -0
  273. django_spire-0.0.1/django_spire/home/templates/spire/home/page/maintenance_mode_page.html +7 -0
  274. django_spire-0.0.1/django_spire/home/urls.py +16 -0
  275. django_spire-0.0.1/django_spire/home/views.py +42 -0
  276. django_spire-0.0.1/django_spire/maintenance/__init__.py +0 -0
  277. django_spire-0.0.1/django_spire/maintenance/middleware.py +22 -0
  278. django_spire-0.0.1/django_spire/modal/__init__.py +0 -0
  279. django_spire-0.0.1/django_spire/modal/static/spire/js/modal.js +0 -0
  280. django_spire-0.0.1/django_spire/modal/templates/spire/base/modal.html +43 -0
  281. django_spire-0.0.1/django_spire/notification/__init__.py +0 -0
  282. django_spire-0.0.1/django_spire/notification/admin.py +32 -0
  283. django_spire-0.0.1/django_spire/notification/apps.py +7 -0
  284. django_spire-0.0.1/django_spire/notification/automations.py +10 -0
  285. django_spire-0.0.1/django_spire/notification/email.py +50 -0
  286. django_spire-0.0.1/django_spire/notification/enums.py +24 -0
  287. django_spire-0.0.1/django_spire/notification/factories.py +19 -0
  288. django_spire-0.0.1/django_spire/notification/migrations/0001_initial.py +36 -0
  289. django_spire-0.0.1/django_spire/notification/migrations/__init__.py +0 -0
  290. django_spire-0.0.1/django_spire/notification/models.py +40 -0
  291. django_spire-0.0.1/django_spire/notification/sender.py +33 -0
  292. django_spire-0.0.1/django_spire/notification/templates/spire/notification/element/notification_element.html +48 -0
  293. django_spire-0.0.1/django_spire/notification/templates/spire/notification/notification.html +28 -0
  294. django_spire-0.0.1/django_spire/notification/templatetags/__init__.py +0 -0
  295. django_spire-0.0.1/django_spire/notification/templatetags/notification_tags.py +24 -0
  296. django_spire-0.0.1/django_spire/options/__init__.py +0 -0
  297. django_spire-0.0.1/django_spire/options/mixins.py +44 -0
  298. django_spire-0.0.1/django_spire/options/options.py +116 -0
  299. django_spire-0.0.1/django_spire/options/tests/__init__.py +0 -0
  300. django_spire-0.0.1/django_spire/options/tests/factories.py +60 -0
  301. django_spire-0.0.1/django_spire/options/tests/test_unit.py +152 -0
  302. django_spire-0.0.1/django_spire/pagination/__init__.py +0 -0
  303. django_spire-0.0.1/django_spire/pagination/pagination.py +12 -0
  304. django_spire-0.0.1/django_spire/pagination/templatetags/__init__.py +0 -0
  305. django_spire-0.0.1/django_spire/pagination/templatetags/pagination_tags.py +44 -0
  306. django_spire-0.0.1/django_spire/permission/__init__.py +0 -0
  307. django_spire-0.0.1/django_spire/permission/admin.py +44 -0
  308. django_spire-0.0.1/django_spire/permission/apps.py +7 -0
  309. django_spire-0.0.1/django_spire/permission/constants.py +30 -0
  310. django_spire-0.0.1/django_spire/permission/decorators.py +34 -0
  311. django_spire-0.0.1/django_spire/permission/factories.py +15 -0
  312. django_spire-0.0.1/django_spire/permission/forms.py +64 -0
  313. django_spire-0.0.1/django_spire/permission/migrations/0001_initial.py +48 -0
  314. django_spire-0.0.1/django_spire/permission/migrations/__init__.py +0 -0
  315. django_spire-0.0.1/django_spire/permission/models.py +60 -0
  316. django_spire-0.0.1/django_spire/permission/permissions.py +235 -0
  317. django_spire-0.0.1/django_spire/permission/querysets.py +8 -0
  318. django_spire-0.0.1/django_spire/permission/templates/spire/permission/card/group_detail_card.html +25 -0
  319. django_spire-0.0.1/django_spire/permission/templates/spire/permission/card/group_list_card.html +26 -0
  320. django_spire-0.0.1/django_spire/permission/templates/spire/permission/card/group_permission_list_card.html +13 -0
  321. django_spire-0.0.1/django_spire/permission/templates/spire/permission/card/group_user_list_card.html +24 -0
  322. django_spire-0.0.1/django_spire/permission/templates/spire/permission/card/permission_user_groups_form_card.html +77 -0
  323. django_spire-0.0.1/django_spire/permission/templates/spire/permission/container/user_detail_card.html +51 -0
  324. django_spire-0.0.1/django_spire/permission/templates/spire/permission/element/group_perm_element.html +45 -0
  325. django_spire-0.0.1/django_spire/permission/templates/spire/permission/element/group_perm_level_badge.html +11 -0
  326. django_spire-0.0.1/django_spire/permission/templates/spire/permission/element/group_special_role_element.html +48 -0
  327. django_spire-0.0.1/django_spire/permission/templates/spire/permission/element/is_active_badge.html +5 -0
  328. django_spire-0.0.1/django_spire/permission/templates/spire/permission/form/group_form.html +12 -0
  329. django_spire-0.0.1/django_spire/permission/templates/spire/permission/item/group_item.html +33 -0
  330. django_spire-0.0.1/django_spire/permission/templates/spire/permission/item/group_perm_item.html +28 -0
  331. django_spire-0.0.1/django_spire/permission/templates/spire/permission/item/group_user_item.html +24 -0
  332. django_spire-0.0.1/django_spire/permission/templates/spire/permission/item/permission_user_group_form_item.html +15 -0
  333. django_spire-0.0.1/django_spire/permission/templates/spire/permission/page/group_detail_page.html +21 -0
  334. django_spire-0.0.1/django_spire/permission/templates/spire/permission/page/group_list_page.html +9 -0
  335. django_spire-0.0.1/django_spire/permission/templatetags/__init__.py +0 -0
  336. django_spire-0.0.1/django_spire/permission/templatetags/permission_tags.py +11 -0
  337. django_spire-0.0.1/django_spire/permission/urls.py +44 -0
  338. django_spire-0.0.1/django_spire/permission/utils.py +83 -0
  339. django_spire-0.0.1/django_spire/permission/views.py +244 -0
  340. django_spire-0.0.1/django_spire/search/__init__.py +0 -0
  341. django_spire-0.0.1/django_spire/search/urls.py +12 -0
  342. django_spire-0.0.1/django_spire/search/views.py +45 -0
  343. django_spire-0.0.1/django_spire/template/__init__.py +0 -0
  344. django_spire-0.0.1/django_spire/template/app/__init__.py +0 -0
  345. django_spire-0.0.1/django_spire/template/app/apps.py +7 -0
  346. django_spire-0.0.1/django_spire/template/app/factories.py +1 -0
  347. django_spire-0.0.1/django_spire/template/app/forms.py +15 -0
  348. django_spire-0.0.1/django_spire/template/app/migrations/__init__.py +0 -0
  349. django_spire-0.0.1/django_spire/template/app/models.py +50 -0
  350. django_spire-0.0.1/django_spire/template/app/querysets.py +13 -0
  351. django_spire-0.0.1/django_spire/template/app/tests/__init__.py +0 -0
  352. django_spire-0.0.1/django_spire/template/app/urls.py +13 -0
  353. django_spire-0.0.1/django_spire/template/app/views.py +91 -0
  354. django_spire-0.0.1/django_spire/template/templates/card/placeholder_detail_card.html +24 -0
  355. django_spire-0.0.1/django_spire/template/templates/card/placeholder_form_card.html +9 -0
  356. django_spire-0.0.1/django_spire/template/templates/card/placeholder_list_card.html +18 -0
  357. django_spire-0.0.1/django_spire/template/templates/form/placeholder_form.html +21 -0
  358. django_spire-0.0.1/django_spire/template/templates/item/placeholder_item.html +22 -0
  359. django_spire-0.0.1/django_spire/template/templates/page/placeholder_detail_page.html +13 -0
  360. django_spire-0.0.1/django_spire/template/templates/page/placeholder_form_page.html +13 -0
  361. django_spire-0.0.1/django_spire/template/templates/page/placeholder_list_page.html +9 -0
  362. django_spire-0.0.1/django_spire/user_account/__init__.py +0 -0
  363. django_spire-0.0.1/django_spire/user_account/apps.py +7 -0
  364. django_spire-0.0.1/django_spire/user_account/factories.py +20 -0
  365. django_spire-0.0.1/django_spire/user_account/forms.py +108 -0
  366. django_spire-0.0.1/django_spire/user_account/migrations/__init__.py +0 -0
  367. django_spire-0.0.1/django_spire/user_account/seed.py +4 -0
  368. django_spire-0.0.1/django_spire/user_account/templates/spire/user_account/card/register_user_detail_form_card.html +55 -0
  369. django_spire-0.0.1/django_spire/user_account/templates/spire/user_account/card/user_detail_card.html +9 -0
  370. django_spire-0.0.1/django_spire/user_account/templates/spire/user_account/card/user_detail_title_card.html +38 -0
  371. django_spire-0.0.1/django_spire/user_account/templates/spire/user_account/card/user_form_card.html +47 -0
  372. django_spire-0.0.1/django_spire/user_account/templates/spire/user_account/card/user_group_list_card.html +25 -0
  373. django_spire-0.0.1/django_spire/user_account/templates/spire/user_account/card/user_list_card.html +28 -0
  374. django_spire-0.0.1/django_spire/user_account/templates/spire/user_account/card/user_profile_list_card.html +20 -0
  375. django_spire-0.0.1/django_spire/user_account/templates/spire/user_account/container/user_detail_container.html +16 -0
  376. django_spire-0.0.1/django_spire/user_account/templates/spire/user_account/container/user_list_container.html +12 -0
  377. django_spire-0.0.1/django_spire/user_account/templates/spire/user_account/element/is_active_badge.html +5 -0
  378. django_spire-0.0.1/django_spire/user_account/templates/spire/user_account/item/user_item.html +25 -0
  379. django_spire-0.0.1/django_spire/user_account/templates/spire/user_account/item/user_master_perm_item.html +18 -0
  380. django_spire-0.0.1/django_spire/user_account/templates/spire/user_account/item/user_profile_item.html +17 -0
  381. django_spire-0.0.1/django_spire/user_account/templates/spire/user_account/page/register_user_form_page.html +15 -0
  382. django_spire-0.0.1/django_spire/user_account/templates/spire/user_account/page/user_detail_page.html +17 -0
  383. django_spire-0.0.1/django_spire/user_account/templates/spire/user_account/page/user_form_page.html +9 -0
  384. django_spire-0.0.1/django_spire/user_account/templates/spire/user_account/page/user_list_page.html +9 -0
  385. django_spire-0.0.1/django_spire/user_account/tests/__init__.py +0 -0
  386. django_spire-0.0.1/django_spire/user_account/tests/factories.py +20 -0
  387. django_spire-0.0.1/django_spire/user_account/tests/test_factories.py +7 -0
  388. django_spire-0.0.1/django_spire/user_account/tests/test_unit.py +9 -0
  389. django_spire-0.0.1/django_spire/user_account/urls.py +27 -0
  390. django_spire-0.0.1/django_spire/user_account/views.py +174 -0
  391. django_spire-0.0.1/django_spire/views/__init__.py +0 -0
  392. django_spire-0.0.1/django_spire/views/modal_views.py +66 -0
  393. django_spire-0.0.1/django_spire/views/portal_views.py +246 -0
  394. django_spire-0.0.1/django_spire.egg-info/PKG-INFO +39 -0
  395. django_spire-0.0.1/django_spire.egg-info/SOURCES.txt +403 -0
  396. django_spire-0.0.1/django_spire.egg-info/dependency_links.txt +1 -0
  397. django_spire-0.0.1/django_spire.egg-info/not-zip-safe +1 -0
  398. django_spire-0.0.1/django_spire.egg-info/requires.txt +3 -0
  399. django_spire-0.0.1/django_spire.egg-info/top_level.txt +2 -0
  400. django_spire-0.0.1/pyproject.toml +55 -0
  401. django_spire-0.0.1/setup.cfg +4 -0
  402. django_spire-0.0.1/setup.py +75 -0
  403. django_spire-0.0.1/testing/__init__.py +0 -0
  404. django_spire-0.0.1/testing/manage.py +30 -0
  405. django_spire-0.0.1/testing/settings.py +81 -0
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2024 Stratus Advanced Technologies and Contributors.
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,39 @@
1
+ Metadata-Version: 2.1
2
+ Name: django-spire
3
+ Version: 0.0.1
4
+ Summary: A project for Django Spire
5
+ Home-page: https://github.com/stratusadv/django-spire
6
+ Author: Austin Sauer, Brayden Carlson, Nathan Johnson & Wesley Howery
7
+ Author-email: Brayden Carlson <braydenc@stratusadv.com>, Nathan Johnson <nathanj@stratusadv.com>
8
+ License: Copyright (c) 2024 Stratus Advanced Technologies and Contributors.
9
+
10
+ Permission is hereby granted, free of charge, to any person
11
+ obtaining a copy of this software and associated documentation
12
+ files (the "Software"), to deal in the Software without
13
+ restriction, including without limitation the rights to use,
14
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the
16
+ Software is furnished to do so, subject to the following
17
+ conditions:
18
+
19
+ The above copyright notice and this permission notice shall be
20
+ included in all copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
24
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
26
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
27
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
29
+ OTHER DEALINGS IN THE SOFTWARE.
30
+
31
+ Keywords: portal,cmms
32
+ Classifier: Programming Language :: Python :: 3
33
+ Classifier: Operating System :: OS Independent
34
+ Requires-Python: >=3.10
35
+ Description-Content-Type: text/markdown
36
+ License-File: LICENSE.md
37
+ Requires-Dist: django>=5.1.4
38
+ Requires-Dist: django-glue>=0.7.8
39
+ Requires-Dist: dandy>=0.5.2
@@ -0,0 +1,62 @@
1
+ django-spire
2
+ ============
3
+
4
+ .. image:: https://camo.githubusercontent.com/dbdd84d7f7838316851e62ce34b0066e7cc57abc8f348aaadedc3888819992a9/68747470733a2f2f692e696d6775722e636f6d2f4e736958794f582e706e67
5
+ :alt: Django Spire Logo
6
+ :align: center
7
+
8
+ ``django_spire`` is a modular framework designed to enhance observability and improve the development of Django web applications.
9
+
10
+ Features
11
+ --------
12
+
13
+ - **Modular Architecture**: Select and install only the modules you need.
14
+ - **Seamless Integration**: Works alongside your existing Django application without intrusive changes.
15
+ - **Customizable & Extensible**: Configure and extend modules to suit specific project requirements.
16
+
17
+ Getting Started
18
+ ---------------
19
+
20
+ To install ``django_spire``, run:
21
+
22
+ .. code-block:: bash
23
+
24
+ pip install django_spire
25
+
26
+ Add ``django_spire`` to your Django project’s ``INSTALLED_APPS``:
27
+
28
+ .. code-block:: python
29
+
30
+ INSTALLED_APPS = [
31
+ 'django_spire.ai',
32
+ 'django_spire.core',
33
+ 'django_spire.authentication',
34
+ 'django_spire.authentication.mfa',
35
+ 'django_spire.breadcrumb',
36
+ 'django_spire.comment',
37
+ 'django_spire.core',
38
+ 'django_spire.file',
39
+ 'django_spire.form',
40
+ 'django_spire.gamification',
41
+ 'django_spire.help',
42
+ 'django_spire.history',
43
+ 'django_spire.maintenance',
44
+ 'django_spire.modal',
45
+ 'django_spire.notification',
46
+ 'django_spire.options',
47
+ 'django_spire.pagination',
48
+ 'django_spire.permission',
49
+ 'django_spire.search',
50
+ 'django_spire.user_account',
51
+ 'django_spire.user_account.profile'
52
+ ]
53
+
54
+ Documentation
55
+ -------------
56
+
57
+ Please refer to our documentation for detailed setup, module configuration, and usage examples.
58
+
59
+ Contributing
60
+ ------------
61
+
62
+ We welcome contributions! See our contributing guide for more details on how to get involved.
File without changes
File without changes
@@ -0,0 +1,7 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class AIConfig(AppConfig):
5
+ default_auto_field = 'django.db.models.BigAutoField'
6
+ label = 'spire_ai'
7
+ name = 'django_spire.ai'
File without changes
@@ -0,0 +1,14 @@
1
+ from django.urls import path
2
+
3
+ from django_spire.ai import views
4
+
5
+
6
+ app_name = 'ai'
7
+
8
+ urlpatterns = [
9
+ path(
10
+ 'transcribe/',
11
+ views.transcribe_audio,
12
+ name='transcribe'
13
+ )
14
+ ]
@@ -0,0 +1,48 @@
1
+ from __future__ import annotations
2
+
3
+ import whisper
4
+
5
+ from enum import Enum
6
+ from pathlib import Path
7
+
8
+ from django.core.files.storage import FileSystemStorage
9
+ from django.http import JsonResponse
10
+ from django.views.decorators.csrf import csrf_protect
11
+
12
+
13
+ class WhisperModel(Enum):
14
+ TINY = 'tiny'
15
+ BASE = 'base'
16
+ SMALL = 'small'
17
+ MEDIUM = 'medium'
18
+ LARGE = 'large'
19
+ TURBO = 'turbo'
20
+
21
+
22
+ model = whisper.load_model(WhisperModel.TINY.value)
23
+
24
+
25
+ @csrf_protect
26
+ def transcribe_audio(request):
27
+ if request.method == 'POST':
28
+ file = request.FILES.get('audio')
29
+
30
+ if not file:
31
+ return JsonResponse({'error': 'No audio file provided'}, status=400)
32
+
33
+ storage = FileSystemStorage()
34
+ location = storage.save(file.name, file)
35
+ path = Path(storage.path(location))
36
+
37
+ try:
38
+ result = model.transcribe(str(path))
39
+ transcription = result['text']
40
+ except Exception as e:
41
+ return JsonResponse({'error': str(e)}, status=500)
42
+ finally:
43
+ if path.exists():
44
+ path.unlink()
45
+
46
+ return JsonResponse({'transcription': transcription}, status=200)
47
+
48
+ return JsonResponse({'error': 'Invalid request method'}, status=405)
@@ -0,0 +1,7 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class AuthenticationConfig(AppConfig):
5
+ default_auto_field = 'django.db.models.BigAutoField'
6
+ label = 'spire_authentication'
7
+ name = 'django_spire.authentication'
@@ -0,0 +1,21 @@
1
+ from django.contrib import admin
2
+ from django.urls import reverse
3
+ from django.utils.html import format_html
4
+
5
+ from django_spire.authentication.mfa import models
6
+
7
+
8
+ @admin.register(models.MfaCode)
9
+ class MfaCodeAdmin(admin.ModelAdmin):
10
+ list_display = (
11
+ 'id', 'user_link', 'code', 'expiration_datetime', 'is_valid'
12
+ )
13
+ list_filter = ('expiration_datetime',)
14
+ search_fields = ('id', 'user__username', 'code')
15
+ ordering = ('-expiration_datetime',)
16
+
17
+ def user_link(self, mfa_code: models.MfaCode) -> str:
18
+ url = reverse('admin:auth_user_change', args=[mfa_code.user.id])
19
+ return format_html(f'<a href="{url}">{mfa_code.user.username}</a>')
20
+
21
+ user_link.short_description = 'User'
@@ -0,0 +1,7 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class MfaConfig(AppConfig):
5
+ default_auto_field = 'django.db.models.BigAutoField'
6
+ label = 'spire_authentication_mfa'
7
+ name = 'django_spire.authentication.mfa'
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ from django import forms
4
+
5
+ from crispy_forms.helper import FormHelper
6
+ from crispy_forms.layout import Layout, Row, Column, HTML
7
+
8
+
9
+ class MFAForm(forms.Form):
10
+ def __init__(self, generated_mfa_code, *args, **kwargs):
11
+ super().__init__(*args, **kwargs)
12
+
13
+ self.helper = FormHelper(self)
14
+ self.helper.include_media = False
15
+ self.generated_mfa_code = generated_mfa_code
16
+ self.fields['mfa_code'] = forms.CharField(max_length=6, label='Authentication Code')
17
+
18
+ self.helper.layout = Layout(
19
+ Row(
20
+ Column('mfa_code', css_class='form-group col-12'),
21
+ ),
22
+ HTML("{% include 'spire/form/form_submit_button.html' %}")
23
+ )
24
+
25
+ def clean(self) -> None:
26
+ cleaned_data = super().clean()
27
+
28
+ mfa_code = cleaned_data.get('mfa_code')
29
+
30
+ if mfa_code != self.generated_mfa_code.code:
31
+ self.add_error('mfa_code', 'Invalid MFA Code.')
@@ -0,0 +1,29 @@
1
+ # Generated by Django 5.1.3 on 2024-11-08 22:57
2
+
3
+ import django.db.models.deletion
4
+ from django.conf import settings
5
+ from django.db import migrations, models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ initial = True
11
+
12
+ dependencies = [
13
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
14
+ ]
15
+
16
+ operations = [
17
+ migrations.CreateModel(
18
+ name='MfaCode',
19
+ fields=[
20
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21
+ ('code', models.CharField(editable=False, max_length=6, unique=True)),
22
+ ('expiration_datetime', models.DateTimeField(editable=False)),
23
+ ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='mfa_codes', related_query_name='mfa_code', to=settings.AUTH_USER_MODEL)),
24
+ ],
25
+ options={
26
+ 'db_table': 'spire_authentication_mfa_code',
27
+ },
28
+ ),
29
+ ]
@@ -0,0 +1,61 @@
1
+ from __future__ import annotations
2
+
3
+ import random
4
+
5
+ from dateutil import relativedelta
6
+
7
+ from django.contrib.auth.models import User
8
+ from django.contrib.sites.models import Site
9
+ from django.db import models
10
+ from django.urls import reverse
11
+ from django.utils.timezone import localtime
12
+
13
+ from django_spire.notification.sender import SendGridEmailHelper
14
+ from django_spire.authentication.mfa.querysets import MfaCodeQuerySet
15
+
16
+
17
+ class MfaCode(models.Model):
18
+ user = models.ForeignKey(User, on_delete=models.CASCADE, related_query_name='mfa_code', related_name='mfa_codes')
19
+ code = models.CharField(max_length=6, editable=False, unique=True)
20
+ expiration_datetime = models.DateTimeField(editable=False)
21
+
22
+ objects = MfaCodeQuerySet.as_manager()
23
+
24
+ def __str__(self):
25
+ return f'{self.expiration_datetime} - {self.code}'
26
+
27
+ def is_valid(self) -> bool:
28
+ return self.expiration_datetime > localtime()
29
+
30
+ def set_expired(self) -> None:
31
+ self.expiration_datetime = localtime()
32
+ self.save()
33
+
34
+ def send_notification(self):
35
+ user = self.user
36
+
37
+ context_data = {
38
+ 'title': 'Authentication Code',
39
+ 'body': f"Here is your multifactor authentication code: </br> <h3>{self.code}</h3>",
40
+ 'name': user.first_name,
41
+ 'button_url': f'{Site.objects.get_current()}/{reverse("authentication:redirect:login_redirect")[1:]}'
42
+ }
43
+
44
+ # Directly connecting to SendGrid to send MFA email faster
45
+ SendGridEmailHelper(
46
+ to=[user.email],
47
+ template_id='d-9ae6be6be95d4f79b0daad9055f03cc9',
48
+ template_data=context_data,
49
+ fail_silently=False
50
+ ).send()
51
+
52
+ @classmethod
53
+ def generate_code(cls, user):
54
+ return MfaCode.objects.create(
55
+ user=user,
56
+ code=int(''.join([str(random.randint(0, 9)) for _ in range(6)])),
57
+ expiration_datetime=localtime() + relativedelta.relativedelta(minutes=5)
58
+ )
59
+
60
+ class Meta:
61
+ db_table = 'spire_authentication_mfa_code'
@@ -0,0 +1,9 @@
1
+ from __future__ import annotations
2
+
3
+ from django.db.models import QuerySet
4
+ from django.utils.timezone import localtime
5
+
6
+
7
+ class MfaCodeQuerySet(QuerySet):
8
+ def valid_code(self, user):
9
+ return self.filter(user=user, expiration_datetime__gte=localtime()).first()
@@ -0,0 +1,9 @@
1
+ from django.urls import include, path
2
+
3
+
4
+ app_name = '__init__'
5
+
6
+ urlpatterns = [
7
+ path('', include('django_spire.authentication.mfa.urls.page_urls', namespace='page')),
8
+ path('', include('django_spire.authentication.mfa.urls.redirect_urls', namespace='redirect')),
9
+ ]
@@ -0,0 +1,12 @@
1
+ from django.urls import path
2
+
3
+ from django_spire.authentication.mfa.views import page_views
4
+
5
+
6
+ app_name = 'page'
7
+
8
+ urlpatterns = [
9
+ path('form/',
10
+ page_views.mfa_form_view,
11
+ name='form'),
12
+ ]
@@ -0,0 +1,12 @@
1
+ from django.urls import path
2
+
3
+ from django_spire.authentication.mfa.views import redirect_views
4
+
5
+
6
+ app_name = 'redirect'
7
+
8
+ urlpatterns = [
9
+ path('notification/',
10
+ redirect_views.mfa_notification_redirect_view,
11
+ name='notification'),
12
+ ]
@@ -0,0 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+ from typing_extensions import TYPE_CHECKING
4
+
5
+ from django_spire.authentication.mfa.models import MfaCode
6
+
7
+ if TYPE_CHECKING:
8
+ from django.contrib.auth.models import User
9
+
10
+
11
+ def get_or_generate_user_mfa_code(user: User) -> MfaCode:
12
+ mfa_code = MfaCode.objects.valid_code(user)
13
+
14
+ if mfa_code:
15
+ return mfa_code
16
+
17
+ return MfaCode.generate_code(user)
@@ -0,0 +1,36 @@
1
+ from __future__ import annotations
2
+
3
+ from django.contrib import messages
4
+ from django.http import HttpResponseRedirect
5
+ from django.template.response import TemplateResponse
6
+ from django.urls import reverse
7
+
8
+ from django_spire.authentication.mfa.utils import get_or_generate_user_mfa_code
9
+ from django_spire.authentication.mfa import forms
10
+
11
+
12
+ def mfa_form_view(request):
13
+ mfa_code = get_or_generate_user_mfa_code(request.user)
14
+ profile = request.user.profile
15
+
16
+ if request.method == 'POST':
17
+ form = forms.MFAForm(mfa_code, request.POST)
18
+ if form.is_valid():
19
+ mfa_code.set_expired()
20
+ profile.set_mfa_grace_period()
21
+ return HttpResponseRedirect(reverse('home:home'))
22
+
23
+ if 'mfa_code' in form.errors:
24
+ messages.error(request, form.errors['mfa_code'][0])
25
+
26
+ context_data = {
27
+ 'form': forms.MFAForm(mfa_code, initial=request.POST),
28
+ 'form_description': 'An authentication code has been sent to your email. Please enter it below to continue.',
29
+ }
30
+
31
+ return TemplateResponse(
32
+ request,
33
+ context=context_data,
34
+ template='spire/authentication/mfa/mfa_form.html'
35
+ )
36
+
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+ from django.contrib import messages
4
+ from django.http import HttpResponseRedirect
5
+ from django.urls import reverse
6
+
7
+ from django_spire.authentication.mfa.utils import get_or_generate_user_mfa_code
8
+
9
+
10
+ def mfa_notification_redirect_view(request):
11
+ mfa_code = get_or_generate_user_mfa_code(request.user)
12
+
13
+ if request.GET.get('resend', False):
14
+ mfa_code.set_expired()
15
+ mfa_code = get_or_generate_user_mfa_code(request.user)
16
+
17
+ mfa_code.send_notification()
18
+ messages.info(request, 'A authentication code has been sent to your email.')
19
+
20
+ return HttpResponseRedirect(reverse('authentication:mfa:page:form'))
@@ -0,0 +1,25 @@
1
+ {% extends "spire/page/center_card_page.html" %}
2
+
3
+ {% load crispy_forms_filters %}
4
+
5
+ {% load static %}
6
+
7
+ {% block base_title %}
8
+ Login
9
+ {% endblock %}
10
+
11
+ {% block page_center_card_content %}
12
+ {% include 'spire/notification/notification.html' %}
13
+
14
+ {% if form_description %}
15
+ {% include 'spire/note/info_note.html' with note_type='Note' note_text=form_description %}
16
+ {% endif %}
17
+
18
+ <form method="post">
19
+ {% csrf_token %}
20
+ {{ form|crispy }}
21
+ <button type="submit" class="w-100 btn btn-app-primary mt-2">Submit</button>
22
+ </form>
23
+
24
+ <a class="text-center text-decoration-none" href={% url 'user_account:authentication:mfa:redirect:notification' %}?resend=True>Resend Code?</a>
25
+ {% endblock %}
@@ -0,0 +1,26 @@
1
+ {% extends 'spire/page/page.html' %}
2
+
3
+ {% load static %}
4
+
5
+ {% block page_content %}
6
+ <div class="row justify-content-center ps-3 mt-5">
7
+ <div class="col-11 col-md-6 col-lg-4 bg-app-layer-one">
8
+ <div class="px-4 py-5">
9
+ {% block authentication_page_top_content %}
10
+ <div class="text-center pb-5">
11
+ <a href="/">
12
+ {% block authentication_page_img %}
13
+ <img class="ms-1 img-fluid" src="{% static 'spire/img/stratus_secondary_medium.png' %}"
14
+ alt="">
15
+ {% endblock %}
16
+ </a>
17
+ </div>
18
+ {% endblock %}
19
+ {% block authentication_page_content %}
20
+
21
+ {% endblock %}
22
+ </div>
23
+ </div>
24
+ </div>
25
+ {% endblock %}
26
+
@@ -0,0 +1,23 @@
1
+ {% extends 'spire/authentication/page/authentication_page.html' %}
2
+
3
+ {% load crispy_forms_filters %}
4
+
5
+ {% block authentication_page_content %}
6
+
7
+ <form method="post" class="text-app-secondary">
8
+ {% csrf_token %}
9
+ {{ form|crispy }}
10
+ <button type="submit" class="d-block mx-auto btn btn-app-primary-outlined my-4 w-100">Login</button>
11
+ </form>
12
+
13
+ <div class="text-center">
14
+ <a href="{% url 'authentication:admin:password_reset' %}"
15
+ class="fs--1 text-app-secondary cursor-pointer">
16
+ Forgot my password
17
+ </a>
18
+ </div>
19
+
20
+ {% endblock %}
21
+
22
+
23
+
@@ -0,0 +1,23 @@
1
+ {% extends 'spire/page/centered_card_page.html' %}
2
+
3
+ {% load crispy_forms_filters static %}
4
+
5
+ {% block page_card_content %}
6
+ <div class="row">
7
+ <div class="col-12">
8
+ <div class="d-flex justify-content-center align-items-end my-2">
9
+ <img height="70px" src="" alt="Logo Here">
10
+ <div class="h4">Application Logout</div>
11
+
12
+ </div>
13
+ <hr class="mt-0">
14
+ </div>
15
+ <div class="col px-4">
16
+ <form method="post" action="{% url 'authentication:redirect:logout' %}">
17
+ {% csrf_token %}
18
+ {{ form|crispy }}
19
+ <button type="submit" class="d-block mx-auto bg-light-primary shadow-sm text-white btn my-4 w-100">Logout</button>
20
+ </form>
21
+ </div>
22
+ </div>
23
+ {% endblock %}
@@ -0,0 +1,12 @@
1
+ {% extends 'spire/authentication/page/authentication_page.html' %}
2
+
3
+ {% load static %}
4
+ {% load crispy_forms_filters %}
5
+
6
+ {% block base_title %}Password Changed Successfully{% endblock %}
7
+
8
+ {% block authentication_page_content %}
9
+ <h3>Password Reset</h3>
10
+ <div class="py-2">You have successfully changed your password!</div>
11
+ <a class="w-100 btn btn-app-primary bg-app-primary mt-2" href="{% url 'home:home' %}">Home</a>
12
+ {% endblock %}
@@ -0,0 +1,14 @@
1
+ {% extends 'spire/authentication/page/authentication_page.html' %}
2
+
3
+ {% load crispy_forms_filters %}
4
+
5
+ {% block authentication_page_content %}
6
+ <h5 class="mb-2">Change Password</h5>
7
+ <div>
8
+ <form method="post">
9
+ {% csrf_token %}
10
+ {{ form|crispy }}
11
+ <button type="submit" class="w-100 btn btn-app-primary bg-app-primary mt-2">Submit</button>
12
+ </form>
13
+ </div>
14
+ {% endblock %}
@@ -0,0 +1,10 @@
1
+ {% extends 'spire/authentication/page/authentication_page.html' %}
2
+
3
+ {% load static %}
4
+
5
+ {% block base_title %}Password Reset Complete{% endblock %}
6
+
7
+ {% block authentication_page_content %}
8
+ <h3>Password Reset Complete</h3>
9
+ <div>Your new password has been set. You can log in now on the <a href="{% url 'user_account:authentication:admin:login' %}">log in page</a>.</div>
10
+ {% endblock %}