django-cfg 1.1.69__tar.gz → 1.1.71__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 (273) hide show
  1. {django_cfg-1.1.69 → django_cfg-1.1.71}/PKG-INFO +1 -1
  2. {django_cfg-1.1.69 → django_cfg-1.1.71}/pyproject.toml +3 -3
  3. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/__init__.py +1 -1
  4. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/admin/__init__.py +2 -0
  5. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/admin/activity.py +9 -2
  6. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/admin/filters.py +42 -0
  7. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/admin/registration_source.py +8 -1
  8. django_cfg-1.1.71/src/django_cfg/apps/accounts/admin/resources.py +271 -0
  9. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/admin/twilio_response.py +7 -1
  10. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/admin/user.py +9 -1
  11. django_cfg-1.1.71/src/django_cfg/apps/accounts/migrations/0005_twilioresponse.py +43 -0
  12. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/models.py +83 -0
  13. django_cfg-1.1.71/src/django_cfg/apps/leads/admin/__init__.py +9 -0
  14. django_cfg-1.1.69/src/django_cfg/apps/leads/admin.py → django_cfg-1.1.71/src/django_cfg/apps/leads/admin/leads_admin.py +10 -2
  15. django_cfg-1.1.71/src/django_cfg/apps/leads/admin/resources.py +119 -0
  16. django_cfg-1.1.71/src/django_cfg/apps/newsletter/admin/__init__.py +12 -0
  17. django_cfg-1.1.69/src/django_cfg/apps/newsletter/admin.py → django_cfg-1.1.71/src/django_cfg/apps/newsletter/admin/newsletter_admin.py +20 -5
  18. django_cfg-1.1.71/src/django_cfg/apps/newsletter/admin/resources.py +241 -0
  19. django_cfg-1.1.71/src/django_cfg/apps/support/admin/__init__.py +10 -0
  20. django_cfg-1.1.71/src/django_cfg/apps/support/admin/resources.py +183 -0
  21. django_cfg-1.1.69/src/django_cfg/apps/support/admin.py → django_cfg-1.1.71/src/django_cfg/apps/support/admin/support_admin.py +16 -6
  22. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
  23. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/urls.py +1 -2
  24. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/archive/django_sample.zip +0 -0
  25. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/core/config.py +1 -0
  26. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/integration.py +14 -16
  27. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/migrator.py +49 -13
  28. {django_cfg-1.1.69 → django_cfg-1.1.71}/.gitignore +0 -0
  29. {django_cfg-1.1.69 → django_cfg-1.1.71}/LICENSE +0 -0
  30. {django_cfg-1.1.69 → django_cfg-1.1.71}/MANIFEST.in +0 -0
  31. {django_cfg-1.1.69 → django_cfg-1.1.71}/README.md +0 -0
  32. {django_cfg-1.1.69 → django_cfg-1.1.71}/requirements-dev.txt +0 -0
  33. {django_cfg-1.1.69 → django_cfg-1.1.71}/requirements-test.txt +0 -0
  34. {django_cfg-1.1.69 → django_cfg-1.1.71}/requirements.txt +0 -0
  35. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/README.md +0 -0
  36. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/__init__.py +0 -0
  37. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/README.md +0 -0
  38. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/__init__.py +0 -0
  39. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/admin/group.py +0 -0
  40. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/admin/inlines.py +0 -0
  41. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/admin/otp.py +0 -0
  42. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/apps.py +0 -0
  43. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/management/commands/test_otp.py +0 -0
  44. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/managers/__init__.py +0 -0
  45. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/managers/user_manager.py +0 -0
  46. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/migrations/0001_initial.py +0 -0
  47. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/migrations/0002_add_phone_otp_clean.py +0 -0
  48. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/migrations/0003_twilioresponse.py +0 -0
  49. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/migrations/0004_delete_twilioresponse.py +0 -0
  50. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/migrations/__init__.py +0 -0
  51. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/serializers/__init__.py +0 -0
  52. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/serializers/otp.py +0 -0
  53. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/serializers/profile.py +0 -0
  54. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/serializers/webhook.py +0 -0
  55. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/services/__init__.py +0 -0
  56. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/services/activity_service.py +0 -0
  57. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/services/otp_service.py +0 -0
  58. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/signals.py +0 -0
  59. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/templates/emails/base_email.html +0 -0
  60. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/templates/emails/base_email.txt +0 -0
  61. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/templates/emails/otp_email.html +0 -0
  62. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/templates/emails/otp_email.txt +0 -0
  63. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/templates/emails/welcome_email.html +0 -0
  64. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/templates/emails/welcome_email.txt +0 -0
  65. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/urls.py +0 -0
  66. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/utils/notifications.py +0 -0
  67. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/views/__init__.py +0 -0
  68. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/views/otp.py +0 -0
  69. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/views/profile.py +0 -0
  70. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/accounts/views/webhook.py +0 -0
  71. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/api/__init__.py +0 -0
  72. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/api/commands/__init__.py +0 -0
  73. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/api/commands/urls.py +0 -0
  74. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/api/commands/views.py +0 -0
  75. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/api/health/__init__.py +0 -0
  76. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/api/health/urls.py +0 -0
  77. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/api/health/views.py +0 -0
  78. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/leads/README.md +0 -0
  79. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/leads/__init__.py +0 -0
  80. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/leads/apps.py +0 -0
  81. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/leads/migrations/0001_initial.py +0 -0
  82. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/leads/migrations/__init__.py +0 -0
  83. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/leads/models.py +0 -0
  84. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/leads/serializers.py +0 -0
  85. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/leads/signals.py +0 -0
  86. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/leads/tests.py +0 -0
  87. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/leads/urls.py +0 -0
  88. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/leads/views.py +0 -0
  89. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/README.md +0 -0
  90. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/__init__.py +0 -0
  91. /django_cfg-1.1.69/src/django_cfg/apps/newsletter/admin_filters.py → /django_cfg-1.1.71/src/django_cfg/apps/newsletter/admin/filters.py +0 -0
  92. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/apps.py +0 -0
  93. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/management/__init__.py +0 -0
  94. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/management/commands/__init__.py +0 -0
  95. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/management/commands/test_newsletter.py +0 -0
  96. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/managers/README.md +0 -0
  97. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/managers/__init__.py +0 -0
  98. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/migrations/0001_initial.py +0 -0
  99. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/migrations/__init__.py +0 -0
  100. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/models.py +0 -0
  101. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/serializers.py +0 -0
  102. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/services/email_service.py +0 -0
  103. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/signals.py +0 -0
  104. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/urls.py +0 -0
  105. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/utils/__init__.py +0 -0
  106. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/views/__init__.py +0 -0
  107. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/views/campaigns.py +0 -0
  108. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/views/emails.py +0 -0
  109. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/views/newsletters.py +0 -0
  110. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/views/subscriptions.py +0 -0
  111. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/newsletter/views/tracking.py +0 -0
  112. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/__init__.py +0 -0
  113. /django_cfg-1.1.69/src/django_cfg/apps/support/admin_filters.py → /django_cfg-1.1.71/src/django_cfg/apps/support/admin/filters.py +0 -0
  114. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/apps.py +0 -0
  115. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/managers/message_manager.py +0 -0
  116. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/managers/ticket_manager.py +0 -0
  117. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/migrations/0001_initial.py +0 -0
  118. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/migrations/0002_alter_message_ticket.py +0 -0
  119. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/migrations/__init__.py +0 -0
  120. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/models.py +0 -0
  121. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/serializers.py +0 -0
  122. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/signals.py +0 -0
  123. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/templates/support/chat/access_denied.html +0 -0
  124. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/urls.py +0 -0
  125. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/utils/__init__.py +0 -0
  126. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/utils/support_email_service.py +0 -0
  127. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/views/__init__.py +0 -0
  128. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/views/admin.py +0 -0
  129. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/views/api.py +0 -0
  130. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/support/views/chat.py +0 -0
  131. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/__init__.py +0 -0
  132. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/admin.py +0 -0
  133. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/apps.py +0 -0
  134. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/serializers.py +0 -0
  135. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/static/tasks/css/dashboard.css +0 -0
  136. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/static/tasks/js/api.js +0 -0
  137. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -0
  138. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/static/tasks/js/modals.js +0 -0
  139. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -0
  140. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -0
  141. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/static/tasks/js/theme.js +0 -0
  142. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/templates/tasks/base.html +0 -0
  143. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -0
  144. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/templates/tasks/components/management_actions.html +0 -0
  145. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -0
  146. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -0
  147. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/templates/tasks/components/status_cards.html +0 -0
  148. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +0 -0
  149. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -0
  150. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -0
  151. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -0
  152. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -0
  153. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/urls.py +0 -0
  154. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps/tasks/views.py +0 -0
  155. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/apps.py +0 -0
  156. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/cli/README.md +0 -0
  157. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/cli/__init__.py +0 -0
  158. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/cli/commands/__init__.py +0 -0
  159. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/cli/commands/create_project.py +0 -0
  160. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/cli/commands/info.py +0 -0
  161. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/cli/main.py +0 -0
  162. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/cli/utils.py +0 -0
  163. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/core/__init__.py +0 -0
  164. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/core/environment.py +0 -0
  165. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/core/generation.py +0 -0
  166. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/core/validation.py +0 -0
  167. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/exceptions.py +0 -0
  168. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/__init__.py +0 -0
  169. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/__init__.py +0 -0
  170. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/check_settings.py +0 -0
  171. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/clear_constance.py +0 -0
  172. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/create_token.py +0 -0
  173. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/generate.py +0 -0
  174. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/list_urls.py +0 -0
  175. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/rundramatiq.py +0 -0
  176. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/runserver_ngrok.py +0 -0
  177. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/script.py +0 -0
  178. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/show_config.py +0 -0
  179. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/show_urls.py +0 -0
  180. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/superuser.py +0 -0
  181. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/task_clear.py +0 -0
  182. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/task_status.py +0 -0
  183. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/test_email.py +0 -0
  184. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/test_telegram.py +0 -0
  185. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/test_twilio.py +0 -0
  186. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/tree.py +0 -0
  187. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/management/commands/validate_config.py +0 -0
  188. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/middleware/README.md +0 -0
  189. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/middleware/__init__.py +0 -0
  190. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/middleware/user_activity.py +0 -0
  191. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/models/__init__.py +0 -0
  192. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/models/cache.py +0 -0
  193. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/models/constance.py +0 -0
  194. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/models/database.py +0 -0
  195. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/models/drf.py +0 -0
  196. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/models/jwt.py +0 -0
  197. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/models/limits.py +0 -0
  198. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/models/ngrok.py +0 -0
  199. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/models/revolution.py +0 -0
  200. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/models/services.py +0 -0
  201. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/models/tasks.py +0 -0
  202. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/models/unfold.py +0 -0
  203. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/__init__.py +0 -0
  204. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/base.py +0 -0
  205. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_currency/README.md +0 -0
  206. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_currency/__init__.py +0 -0
  207. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_currency/cache.py +0 -0
  208. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_currency/converter.py +0 -0
  209. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_currency/service.py +0 -0
  210. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_email.py +0 -0
  211. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_llm/README.md +0 -0
  212. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_llm/__init__.py +0 -0
  213. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_llm/example.py +0 -0
  214. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_llm/llm/__init__.py +0 -0
  215. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_llm/llm/cache.py +0 -0
  216. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_llm/llm/client.py +0 -0
  217. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_llm/llm/costs.py +0 -0
  218. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_llm/llm/extractor.py +0 -0
  219. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_llm/llm/models_cache.py +0 -0
  220. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_llm/llm/tokenizer.py +0 -0
  221. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_llm/translator/__init__.py +0 -0
  222. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_llm/translator/cache.py +0 -0
  223. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_llm/translator/translator.py +0 -0
  224. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_logger.py +0 -0
  225. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_ngrok.py +0 -0
  226. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_tasks.py +0 -0
  227. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_telegram.py +0 -0
  228. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_twilio/README.md +0 -0
  229. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_twilio/__init__.py +0 -0
  230. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_twilio/exceptions.py +0 -0
  231. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_twilio/models.py +0 -0
  232. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_twilio/sendgrid_service.py +0 -0
  233. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_twilio/service.py +0 -0
  234. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_twilio/simple_service.py +0 -0
  235. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_twilio/templates/guide.md +0 -0
  236. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_twilio/templates/sendgrid_otp_email.html +0 -0
  237. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_twilio/templates/sendgrid_test_data.json +0 -0
  238. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/django_twilio/twilio_service.py +0 -0
  239. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/dramatiq_setup.py +0 -0
  240. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/logger.py +0 -0
  241. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/unfold/__init__.py +0 -0
  242. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/unfold/callbacks.py +0 -0
  243. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/unfold/dashboard.py +0 -0
  244. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/unfold/models.py +0 -0
  245. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/unfold/system_monitor.py +0 -0
  246. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/modules/unfold/tailwind.py +0 -0
  247. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/pyproject.toml +0 -0
  248. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/routers.py +0 -0
  249. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/__init__.py +0 -0
  250. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/index.html +0 -0
  251. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/layouts/dashboard_with_tabs.html +0 -0
  252. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/snippets/components/activity_tracker.html +0 -0
  253. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/snippets/components/charts_section.html +0 -0
  254. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/snippets/components/django_commands.html +0 -0
  255. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/snippets/components/quick_actions.html +0 -0
  256. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/snippets/components/recent_activity.html +0 -0
  257. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/snippets/components/recent_users_table.html +0 -0
  258. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/snippets/components/stats_cards.html +0 -0
  259. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/snippets/components/stats_tiles.html +0 -0
  260. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/snippets/components/system_health.html +0 -0
  261. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/snippets/components/system_metrics.html +0 -0
  262. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/snippets/components/user_permissions.html +0 -0
  263. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/snippets/tabs/app_stats_tab.html +0 -0
  264. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/snippets/tabs/commands_tab.html +0 -0
  265. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/snippets/tabs/overview_tab.html +0 -0
  266. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/snippets/tabs/stats_tab.html +0 -0
  267. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/snippets/tabs/users_tab.html +0 -0
  268. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/admin/snippets/zones/zones_table.html +0 -0
  269. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/templates/emails/base_email.html +0 -0
  270. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/utils/__init__.py +0 -0
  271. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/utils/path_resolution.py +0 -0
  272. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/utils/smart_defaults.py +0 -0
  273. {django_cfg-1.1.69 → django_cfg-1.1.71}/src/django_cfg/version_check.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-cfg
3
- Version: 1.1.69
3
+ Version: 1.1.71
4
4
  Summary: 🚀 Production-ready Django configuration framework with type-safe settings, smart automation, and modern developer experience
5
5
  Project-URL: Homepage, https://github.com/markolofsen/django-cfg
6
6
  Project-URL: Documentation, https://django-cfg.readthedocs.io
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "django-cfg"
7
- version = "1.1.69"
7
+ version = "1.1.71"
8
8
  description = "🚀 Production-ready Django configuration framework with type-safe settings, smart automation, and modern developer experience"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -59,6 +59,7 @@ dependencies = [
59
59
  "loguru>=0.7.0",
60
60
  "colorlog>=6.9.0",
61
61
  "cachetools>=6.1.0",
62
+ "toml (>=0.10.2,<0.11.0)",
62
63
 
63
64
  # Development tools (Python 3.12+ only)
64
65
  "ngrok>=1.5.1; python_version>='3.12'",
@@ -79,6 +80,7 @@ dependencies = [
79
80
  "drf-spectacular>=0.28.0",
80
81
  "drf-spectacular-sidecar>=2025.8.1",
81
82
  "django-json-widget>=2.0.3",
83
+ "django-import-export (>=4.3.9,<5.0.0)",
82
84
  "django-extensions>=4.1",
83
85
  "django-constance>=4.3.2",
84
86
  "django-revolution>=1.0.34",
@@ -103,8 +105,6 @@ dependencies = [
103
105
  "beautifulsoup4 (>=4.13.5,<5.0.0)",
104
106
  "lxml (>=6.0.1,<7.0.0)",
105
107
  "pgvector (>=0.4.1,<0.5.0)",
106
- "toml (>=0.10.2,<0.11.0)",
107
- "django-import-export (>=4.3.9,<5.0.0)",
108
108
  ]
109
109
 
110
110
  [project.optional-dependencies]
@@ -38,7 +38,7 @@ default_app_config = "django_cfg.apps.DjangoCfgConfig"
38
38
  from typing import TYPE_CHECKING
39
39
 
40
40
  # Version information
41
- __version__ = "1.1.69"
41
+ __version__ = "1.1.71"
42
42
  __author__ = "Unrealos Team"
43
43
  __email__ = "info@unrealos.com"
44
44
  __license__ = "MIT"
@@ -7,6 +7,7 @@ from .otp import OTPSecretAdmin
7
7
  from .registration_source import RegistrationSourceAdmin, UserRegistrationSourceAdmin
8
8
  from .activity import UserActivityAdmin
9
9
  from .group import GroupAdmin
10
+ from .twilio_response import TwilioResponseAdmin
10
11
 
11
12
  __all__ = [
12
13
  'CustomUserAdmin',
@@ -15,4 +16,5 @@ __all__ = [
15
16
  'UserRegistrationSourceAdmin',
16
17
  'UserActivityAdmin',
17
18
  'GroupAdmin',
19
+ 'TwilioResponseAdmin',
18
20
  ]
@@ -6,14 +6,21 @@ from django.contrib import admin
6
6
  from django.utils.html import format_html
7
7
  from django.contrib.humanize.templatetags.humanize import naturaltime
8
8
  from unfold.admin import ModelAdmin
9
+ from import_export.admin import ExportMixin
10
+ from unfold.contrib.import_export.forms import ExportForm
9
11
 
10
12
  from ..models import UserActivity
11
13
  from .filters import ActivityTypeFilter
14
+ from .resources import UserActivityResource
12
15
 
13
16
 
14
17
  @admin.register(UserActivity)
15
- class UserActivityAdmin(ModelAdmin):
16
- """Enhanced admin for UserActivity model."""
18
+ class UserActivityAdmin(ModelAdmin, ExportMixin):
19
+ """Enhanced admin for UserActivity model with export functionality."""
20
+
21
+ # Export-only configuration
22
+ resource_class = UserActivityResource
23
+ export_form_class = ExportForm
17
24
 
18
25
  list_display = [
19
26
  'user_display',
@@ -4,6 +4,7 @@ Custom admin filters for Accounts app.
4
4
 
5
5
  from django.contrib import admin
6
6
  from django.utils import timezone
7
+ from django.db import models
7
8
  from datetime import timedelta
8
9
 
9
10
 
@@ -96,3 +97,44 @@ class ActivityTypeFilter(admin.SimpleListFilter):
96
97
  elif self.value():
97
98
  return queryset.filter(activity_type=self.value())
98
99
  return queryset
100
+
101
+
102
+ class TwilioResponseStatusFilter(admin.SimpleListFilter):
103
+ title = "Response Status"
104
+ parameter_name = "twilio_status"
105
+
106
+ def lookups(self, request, model_admin):
107
+ return (
108
+ ("successful", "Successful"),
109
+ ("error", "Has Error"),
110
+ ("recent", "Recent (24h)"),
111
+ )
112
+
113
+ def queryset(self, request, queryset):
114
+ now = timezone.now()
115
+ if self.value() == "successful":
116
+ return queryset.filter(error_code__isnull=True, error_message__isnull=True)
117
+ elif self.value() == "error":
118
+ return queryset.filter(
119
+ models.Q(error_code__isnull=False) | models.Q(error_message__isnull=False)
120
+ )
121
+ elif self.value() == "recent":
122
+ return queryset.filter(created_at__gte=now - timedelta(hours=24))
123
+ return queryset
124
+
125
+
126
+ class TwilioResponseTypeFilter(admin.SimpleListFilter):
127
+ title = "Response Type"
128
+ parameter_name = "twilio_response_type"
129
+
130
+ def lookups(self, request, model_admin):
131
+ return (
132
+ ("sms", "SMS"),
133
+ ("verification", "Verification"),
134
+ ("call", "Call"),
135
+ )
136
+
137
+ def queryset(self, request, queryset):
138
+ if self.value():
139
+ return queryset.filter(response_type=self.value())
140
+ return queryset
@@ -5,14 +5,21 @@ Registration Source admin configuration.
5
5
  from django.contrib import admin
6
6
  from django.contrib.humanize.templatetags.humanize import naturaltime
7
7
  from unfold.admin import ModelAdmin
8
+ from import_export.admin import ImportExportModelAdmin
9
+ from unfold.contrib.import_export.forms import ImportForm, ExportForm
8
10
 
9
11
  from ..models import RegistrationSource, UserRegistrationSource
10
12
  from .filters import RegistrationSourceStatusFilter
11
13
  from .inlines import RegistrationSourceInline
14
+ from .resources import RegistrationSourceResource
12
15
 
13
16
 
14
17
  @admin.register(RegistrationSource)
15
- class RegistrationSourceAdmin(ModelAdmin):
18
+ class RegistrationSourceAdmin(ModelAdmin, ImportExportModelAdmin):
19
+ # Import/Export configuration
20
+ resource_class = RegistrationSourceResource
21
+ import_form_class = ImportForm
22
+ export_form_class = ExportForm
16
23
  list_display = ["name", "url", "status", "users_count", "created"]
17
24
  list_display_links = ["name", "url"]
18
25
  list_filter = [RegistrationSourceStatusFilter, "is_active", "created_at"]
@@ -0,0 +1,271 @@
1
+ """
2
+ Import/Export resources for Accounts app.
3
+ """
4
+
5
+ from import_export import resources, fields
6
+ from import_export.widgets import ForeignKeyWidget, DateTimeWidget, BooleanWidget, ManyToManyWidget
7
+ from django.contrib.auth.models import Group
8
+
9
+ from ..models import CustomUser, UserActivity, RegistrationSource, TwilioResponse
10
+
11
+
12
+ class CustomUserResource(resources.ModelResource):
13
+ """Resource for importing/exporting users."""
14
+
15
+ # Custom fields for better export/import
16
+ full_name = fields.Field(
17
+ column_name='full_name',
18
+ attribute='get_full_name',
19
+ readonly=True
20
+ )
21
+
22
+ groups = fields.Field(
23
+ column_name='groups',
24
+ attribute='groups',
25
+ widget=ManyToManyWidget(Group, field='name', separator='|')
26
+ )
27
+
28
+ last_login = fields.Field(
29
+ column_name='last_login',
30
+ attribute='last_login',
31
+ widget=DateTimeWidget(format='%Y-%m-%d %H:%M:%S')
32
+ )
33
+
34
+ date_joined = fields.Field(
35
+ column_name='date_joined',
36
+ attribute='date_joined',
37
+ widget=DateTimeWidget(format='%Y-%m-%d %H:%M:%S')
38
+ )
39
+
40
+ is_active = fields.Field(
41
+ column_name='is_active',
42
+ attribute='is_active',
43
+ widget=BooleanWidget()
44
+ )
45
+
46
+ is_staff = fields.Field(
47
+ column_name='is_staff',
48
+ attribute='is_staff',
49
+ widget=BooleanWidget()
50
+ )
51
+
52
+ phone_verified = fields.Field(
53
+ column_name='phone_verified',
54
+ attribute='phone_verified',
55
+ widget=BooleanWidget()
56
+ )
57
+
58
+ class Meta:
59
+ model = CustomUser
60
+ fields = (
61
+ 'id',
62
+ 'email',
63
+ 'first_name',
64
+ 'last_name',
65
+ 'full_name',
66
+ 'company',
67
+ 'phone',
68
+ 'phone_verified',
69
+ 'position',
70
+ 'is_active',
71
+ 'is_staff',
72
+ 'is_superuser',
73
+ 'groups',
74
+ 'last_login',
75
+ 'date_joined',
76
+ )
77
+ export_order = fields
78
+ import_id_fields = ('email',) # Use email as unique identifier
79
+ skip_unchanged = True
80
+ report_skipped = True
81
+
82
+ def before_import_row(self, row, **kwargs):
83
+ """Process row before import."""
84
+ # Ensure email is lowercase
85
+ if 'email' in row:
86
+ row['email'] = row['email'].lower().strip()
87
+
88
+ def skip_row(self, instance, original, row, import_validation_errors=None):
89
+ """Skip rows with validation errors."""
90
+ if import_validation_errors:
91
+ return True
92
+ return super().skip_row(instance, original, row, import_validation_errors)
93
+
94
+
95
+ class UserActivityResource(resources.ModelResource):
96
+ """Resource for exporting user activity (export only)."""
97
+
98
+ user_email = fields.Field(
99
+ column_name='user_email',
100
+ attribute='user__email',
101
+ readonly=True
102
+ )
103
+
104
+ user_full_name = fields.Field(
105
+ column_name='user_full_name',
106
+ attribute='user__get_full_name',
107
+ readonly=True
108
+ )
109
+
110
+ activity_type_display = fields.Field(
111
+ column_name='activity_type_display',
112
+ attribute='get_activity_type_display',
113
+ readonly=True
114
+ )
115
+
116
+ created_at = fields.Field(
117
+ column_name='created_at',
118
+ attribute='created_at',
119
+ widget=DateTimeWidget(format='%Y-%m-%d %H:%M:%S')
120
+ )
121
+
122
+ class Meta:
123
+ model = UserActivity
124
+ fields = (
125
+ 'id',
126
+ 'user_email',
127
+ 'user_full_name',
128
+ 'activity_type',
129
+ 'activity_type_display',
130
+ 'description',
131
+ 'ip_address',
132
+ 'user_agent',
133
+ 'object_id',
134
+ 'object_type',
135
+ 'created_at',
136
+ )
137
+ export_order = fields
138
+ # No import - this is export only
139
+
140
+ def get_queryset(self):
141
+ """Optimize queryset for export."""
142
+ return super().get_queryset().select_related('user')
143
+
144
+
145
+ class RegistrationSourceResource(resources.ModelResource):
146
+ """Resource for importing/exporting registration sources."""
147
+
148
+ is_active = fields.Field(
149
+ column_name='is_active',
150
+ attribute='is_active',
151
+ widget=BooleanWidget()
152
+ )
153
+
154
+ created_at = fields.Field(
155
+ column_name='created_at',
156
+ attribute='created_at',
157
+ widget=DateTimeWidget(format='%Y-%m-%d %H:%M:%S')
158
+ )
159
+
160
+ updated_at = fields.Field(
161
+ column_name='updated_at',
162
+ attribute='updated_at',
163
+ widget=DateTimeWidget(format='%Y-%m-%d %H:%M:%S')
164
+ )
165
+
166
+ users_count = fields.Field(
167
+ column_name='users_count',
168
+ readonly=True
169
+ )
170
+
171
+ class Meta:
172
+ model = RegistrationSource
173
+ fields = (
174
+ 'id',
175
+ 'url',
176
+ 'name',
177
+ 'description',
178
+ 'is_active',
179
+ 'users_count',
180
+ 'created_at',
181
+ 'updated_at',
182
+ )
183
+ export_order = fields
184
+ import_id_fields = ('url',) # Use URL as unique identifier
185
+ skip_unchanged = True
186
+ report_skipped = True
187
+
188
+ def dehydrate_users_count(self, registration_source):
189
+ """Calculate users count for export."""
190
+ return registration_source.user_registration_sources.count()
191
+
192
+ def before_import_row(self, row, **kwargs):
193
+ """Process row before import."""
194
+ # Ensure URL is properly formatted
195
+ if 'url' in row and row['url']:
196
+ url = row['url'].strip()
197
+ if not url.startswith(('http://', 'https://')):
198
+ row['url'] = f'https://{url}'
199
+ else:
200
+ row['url'] = url
201
+
202
+
203
+ class TwilioResponseResource(resources.ModelResource):
204
+ """Resource for exporting Twilio responses (export only)."""
205
+
206
+ otp_recipient = fields.Field(
207
+ column_name='otp_recipient',
208
+ attribute='otp_secret__recipient',
209
+ readonly=True
210
+ )
211
+
212
+ created_at = fields.Field(
213
+ column_name='created_at',
214
+ attribute='created_at',
215
+ widget=DateTimeWidget(format='%Y-%m-%d %H:%M:%S')
216
+ )
217
+
218
+ updated_at = fields.Field(
219
+ column_name='updated_at',
220
+ attribute='updated_at',
221
+ widget=DateTimeWidget(format='%Y-%m-%d %H:%M:%S')
222
+ )
223
+
224
+ twilio_created_at = fields.Field(
225
+ column_name='twilio_created_at',
226
+ attribute='twilio_created_at',
227
+ widget=DateTimeWidget(format='%Y-%m-%d %H:%M:%S')
228
+ )
229
+
230
+ has_error = fields.Field(
231
+ column_name='has_error',
232
+ attribute='has_error',
233
+ widget=BooleanWidget(),
234
+ readonly=True
235
+ )
236
+
237
+ is_successful = fields.Field(
238
+ column_name='is_successful',
239
+ attribute='is_successful',
240
+ widget=BooleanWidget(),
241
+ readonly=True
242
+ )
243
+
244
+ class Meta:
245
+ model = TwilioResponse
246
+ fields = (
247
+ 'id',
248
+ 'response_type',
249
+ 'service_type',
250
+ 'status',
251
+ 'message_sid',
252
+ 'verification_sid',
253
+ 'to_number',
254
+ 'from_number',
255
+ 'otp_recipient',
256
+ 'error_code',
257
+ 'error_message',
258
+ 'price',
259
+ 'price_unit',
260
+ 'has_error',
261
+ 'is_successful',
262
+ 'created_at',
263
+ 'updated_at',
264
+ 'twilio_created_at',
265
+ )
266
+ export_order = fields
267
+ # No import - this is export only
268
+
269
+ def get_queryset(self):
270
+ """Optimize queryset for export."""
271
+ return super().get_queryset().select_related('otp_secret')
@@ -6,9 +6,12 @@ from django.contrib import admin
6
6
  from django.contrib.humanize.templatetags.humanize import naturaltime
7
7
  from django.utils.html import format_html
8
8
  from unfold.admin import ModelAdmin
9
+ from import_export.admin import ExportMixin
10
+ from unfold.contrib.import_export.forms import ExportForm
9
11
 
10
12
  from ..models import TwilioResponse
11
13
  from .filters import TwilioResponseStatusFilter, TwilioResponseTypeFilter
14
+ from .resources import TwilioResponseResource
12
15
 
13
16
 
14
17
  class TwilioResponseInline(admin.TabularInline):
@@ -23,7 +26,10 @@ class TwilioResponseInline(admin.TabularInline):
23
26
 
24
27
 
25
28
  @admin.register(TwilioResponse)
26
- class TwilioResponseAdmin(ModelAdmin):
29
+ class TwilioResponseAdmin(ModelAdmin, ExportMixin):
30
+ # Export-only configuration
31
+ resource_class = TwilioResponseResource
32
+ export_form_class = ExportForm
27
33
  list_display = [
28
34
  'identifier',
29
35
  'service_type',
@@ -12,18 +12,26 @@ from unfold.admin import ModelAdmin
12
12
  from unfold.forms import AdminPasswordChangeForm, UserChangeForm, UserCreationForm
13
13
  from unfold.decorators import action
14
14
  from unfold.enums import ActionVariant
15
+ from import_export.admin import ImportExportModelAdmin
16
+ from unfold.contrib.import_export.forms import ImportForm, ExportForm
15
17
 
16
18
  from ..models import CustomUser
17
19
  from .filters import UserStatusFilter
18
20
  from .inlines import UserRegistrationSourceInline, UserActivityInline, UserEmailLogInline, UserSupportTicketsInline
21
+ from .resources import CustomUserResource
19
22
 
20
23
 
21
24
  @admin.register(CustomUser)
22
- class CustomUserAdmin(BaseUserAdmin, ModelAdmin):
25
+ class CustomUserAdmin(BaseUserAdmin, ModelAdmin, ImportExportModelAdmin):
23
26
  # Forms loaded from `unfold.forms`
24
27
  form = UserChangeForm
25
28
  add_form = UserCreationForm
26
29
  change_password_form = AdminPasswordChangeForm
30
+
31
+ # Import/Export configuration
32
+ resource_class = CustomUserResource
33
+ import_form_class = ImportForm
34
+ export_form_class = ExportForm
27
35
 
28
36
  list_display = [
29
37
  "avatar",
@@ -0,0 +1,43 @@
1
+ # Generated by Django 5.2.6 on 2025-09-17 19:28
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('django_cfg_accounts', '0004_delete_twilioresponse'),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.CreateModel(
15
+ name='TwilioResponse',
16
+ fields=[
17
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18
+ ('response_type', models.CharField(choices=[('api_send', 'API Send Request'), ('api_verify', 'API Verify Request'), ('webhook_status', 'Webhook Status Update'), ('webhook_delivery', 'Webhook Delivery Report')], max_length=20)),
19
+ ('service_type', models.CharField(choices=[('whatsapp', 'WhatsApp'), ('sms', 'SMS'), ('voice', 'Voice'), ('email', 'Email'), ('verify', 'Verify API')], max_length=10)),
20
+ ('message_sid', models.CharField(blank=True, help_text='Twilio Message SID', max_length=34)),
21
+ ('verification_sid', models.CharField(blank=True, help_text='Twilio Verification SID', max_length=34)),
22
+ ('request_data', models.JSONField(default=dict, help_text='Original request parameters')),
23
+ ('response_data', models.JSONField(default=dict, help_text='Twilio API response')),
24
+ ('status', models.CharField(blank=True, help_text='Message/Verification status', max_length=20)),
25
+ ('error_code', models.CharField(blank=True, help_text='Twilio error code', max_length=10)),
26
+ ('error_message', models.TextField(blank=True, help_text='Error description')),
27
+ ('to_number', models.CharField(blank=True, help_text='Recipient phone/email', max_length=20)),
28
+ ('from_number', models.CharField(blank=True, help_text='Sender phone/email', max_length=20)),
29
+ ('price', models.DecimalField(blank=True, decimal_places=6, max_digits=10, null=True)),
30
+ ('price_unit', models.CharField(blank=True, help_text='Currency code', max_length=3)),
31
+ ('created_at', models.DateTimeField(auto_now_add=True)),
32
+ ('updated_at', models.DateTimeField(auto_now=True)),
33
+ ('twilio_created_at', models.DateTimeField(blank=True, help_text='Timestamp from Twilio', null=True)),
34
+ ('otp_secret', models.ForeignKey(blank=True, help_text='Related OTP if applicable', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='twilio_responses', to='django_cfg_accounts.otpsecret')),
35
+ ],
36
+ options={
37
+ 'verbose_name': 'Twilio Response',
38
+ 'verbose_name_plural': 'Twilio Responses',
39
+ 'ordering': ['-created_at'],
40
+ 'indexes': [models.Index(fields=['message_sid'], name='django_cfg__message_c37dcd_idx'), models.Index(fields=['verification_sid'], name='django_cfg__verific_7de689_idx'), models.Index(fields=['status', 'created_at'], name='django_cfg__status_95d8c8_idx'), models.Index(fields=['response_type', 'service_type'], name='django_cfg__respons_20ca26_idx')],
41
+ },
42
+ ),
43
+ ]
@@ -237,3 +237,86 @@ class UserActivity(models.Model):
237
237
 
238
238
  def __str__(self):
239
239
  return f"{self.user.username} - {self.get_activity_type_display()}"
240
+
241
+
242
+ class TwilioResponse(models.Model):
243
+ """Model for storing Twilio API responses and webhook data."""
244
+
245
+ class ResponseType(models.TextChoices):
246
+ API_SEND = 'api_send', 'API Send Request'
247
+ API_VERIFY = 'api_verify', 'API Verify Request'
248
+ WEBHOOK_STATUS = 'webhook_status', 'Webhook Status Update'
249
+ WEBHOOK_DELIVERY = 'webhook_delivery', 'Webhook Delivery Report'
250
+
251
+ class ServiceType(models.TextChoices):
252
+ WHATSAPP = 'whatsapp', 'WhatsApp'
253
+ SMS = 'sms', 'SMS'
254
+ VOICE = 'voice', 'Voice'
255
+ EMAIL = 'email', 'Email'
256
+ VERIFY = 'verify', 'Verify API'
257
+
258
+ # Response metadata
259
+ response_type = models.CharField(max_length=20, choices=ResponseType.choices)
260
+ service_type = models.CharField(max_length=10, choices=ServiceType.choices)
261
+
262
+ # Twilio identifiers
263
+ message_sid = models.CharField(max_length=34, blank=True, help_text="Twilio Message SID")
264
+ verification_sid = models.CharField(max_length=34, blank=True, help_text="Twilio Verification SID")
265
+
266
+ # Request/Response data
267
+ request_data = models.JSONField(default=dict, help_text="Original request parameters")
268
+ response_data = models.JSONField(default=dict, help_text="Twilio API response")
269
+
270
+ # Status and error information
271
+ status = models.CharField(max_length=20, blank=True, help_text="Message/Verification status")
272
+ error_code = models.CharField(max_length=10, blank=True, help_text="Twilio error code")
273
+ error_message = models.TextField(blank=True, help_text="Error description")
274
+
275
+ # Contact information
276
+ to_number = models.CharField(max_length=20, blank=True, help_text="Recipient phone/email")
277
+ from_number = models.CharField(max_length=20, blank=True, help_text="Sender phone/email")
278
+
279
+ # Pricing information
280
+ price = models.DecimalField(max_digits=10, decimal_places=6, null=True, blank=True)
281
+ price_unit = models.CharField(max_length=3, blank=True, help_text="Currency code")
282
+
283
+ # Timestamps
284
+ created_at = models.DateTimeField(auto_now_add=True)
285
+ updated_at = models.DateTimeField(auto_now=True)
286
+ twilio_created_at = models.DateTimeField(null=True, blank=True, help_text="Timestamp from Twilio")
287
+
288
+ # Relations
289
+ otp_secret = models.ForeignKey(
290
+ OTPSecret,
291
+ on_delete=models.SET_NULL,
292
+ null=True,
293
+ blank=True,
294
+ related_name='twilio_responses',
295
+ help_text="Related OTP if applicable"
296
+ )
297
+
298
+ class Meta:
299
+ app_label = 'django_cfg_accounts'
300
+ verbose_name = 'Twilio Response'
301
+ verbose_name_plural = 'Twilio Responses'
302
+ ordering = ['-created_at']
303
+ indexes = [
304
+ models.Index(fields=['message_sid']),
305
+ models.Index(fields=['verification_sid']),
306
+ models.Index(fields=['status', 'created_at']),
307
+ models.Index(fields=['response_type', 'service_type']),
308
+ ]
309
+
310
+ def __str__(self):
311
+ identifier = self.message_sid or self.verification_sid or f"#{self.id}"
312
+ return f"{self.get_service_type_display()} {self.get_response_type_display()} - {identifier}"
313
+
314
+ @property
315
+ def has_error(self):
316
+ """Check if response has error."""
317
+ return bool(self.error_code or self.error_message)
318
+
319
+ @property
320
+ def is_successful(self):
321
+ """Check if response is successful."""
322
+ return not self.has_error and self.status in ['sent', 'delivered', 'approved']
@@ -0,0 +1,9 @@
1
+ """
2
+ Admin configuration for Leads app.
3
+ """
4
+
5
+ from .leads_admin import LeadAdmin
6
+
7
+ __all__ = [
8
+ 'LeadAdmin',
9
+ ]
@@ -4,11 +4,19 @@ from django.utils.html import format_html
4
4
  from django.http import HttpResponseRedirect
5
5
  from unfold.admin import ModelAdmin
6
6
  from unfold.decorators import action
7
- from .models import Lead
7
+ from import_export.admin import ImportExportModelAdmin
8
+ from unfold.contrib.import_export.forms import ImportForm, ExportForm
9
+
10
+ from ..models import Lead
11
+ from .resources import LeadResource
8
12
 
9
13
 
10
14
  @admin.register(Lead)
11
- class LeadAdmin(ModelAdmin):
15
+ class LeadAdmin(ModelAdmin, ImportExportModelAdmin):
16
+ # Import/Export configuration
17
+ resource_class = LeadResource
18
+ import_form_class = ImportForm
19
+ export_form_class = ExportForm
12
20
  list_display = [
13
21
  'name', 'email', 'company', 'contact_type', 'contact_value',
14
22
  'subject', 'status_display', 'created_at'