django-cfg 1.3.7__py3-none-any.whl → 1.3.9__py3-none-any.whl

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 (251) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/admin/__init__.py +24 -8
  3. django_cfg/apps/accounts/admin/activity_admin.py +146 -0
  4. django_cfg/apps/accounts/admin/filters.py +98 -22
  5. django_cfg/apps/accounts/admin/group_admin.py +86 -0
  6. django_cfg/apps/accounts/admin/inlines.py +42 -13
  7. django_cfg/apps/accounts/admin/otp_admin.py +115 -0
  8. django_cfg/apps/accounts/admin/registration_admin.py +173 -0
  9. django_cfg/apps/accounts/admin/resources.py +123 -19
  10. django_cfg/apps/accounts/admin/twilio_admin.py +327 -0
  11. django_cfg/apps/accounts/admin/user_admin.py +362 -0
  12. django_cfg/apps/agents/admin/__init__.py +17 -4
  13. django_cfg/apps/agents/admin/execution_admin.py +204 -183
  14. django_cfg/apps/agents/admin/registry_admin.py +230 -255
  15. django_cfg/apps/agents/admin/toolsets_admin.py +274 -321
  16. django_cfg/apps/agents/core/__init__.py +1 -1
  17. django_cfg/apps/agents/core/django_agent.py +221 -0
  18. django_cfg/apps/agents/core/exceptions.py +14 -0
  19. django_cfg/apps/agents/core/orchestrator.py +18 -3
  20. django_cfg/apps/knowbase/admin/__init__.py +1 -1
  21. django_cfg/apps/knowbase/admin/archive_admin.py +352 -640
  22. django_cfg/apps/knowbase/admin/chat_admin.py +258 -192
  23. django_cfg/apps/knowbase/admin/document_admin.py +269 -262
  24. django_cfg/apps/knowbase/admin/external_data_admin.py +271 -489
  25. django_cfg/apps/knowbase/config/settings.py +21 -4
  26. django_cfg/apps/knowbase/views/chat_views.py +3 -0
  27. django_cfg/apps/leads/admin/__init__.py +3 -1
  28. django_cfg/apps/leads/admin/leads_admin.py +235 -35
  29. django_cfg/apps/maintenance/admin/__init__.py +2 -2
  30. django_cfg/apps/maintenance/admin/api_key_admin.py +125 -63
  31. django_cfg/apps/maintenance/admin/log_admin.py +143 -61
  32. django_cfg/apps/maintenance/admin/scheduled_admin.py +212 -301
  33. django_cfg/apps/maintenance/admin/site_admin.py +213 -352
  34. django_cfg/apps/newsletter/admin/__init__.py +29 -2
  35. django_cfg/apps/newsletter/admin/newsletter_admin.py +531 -193
  36. django_cfg/apps/payments/admin/__init__.py +18 -27
  37. django_cfg/apps/payments/admin/api_keys_admin.py +179 -546
  38. django_cfg/apps/payments/admin/balance_admin.py +166 -632
  39. django_cfg/apps/payments/admin/currencies_admin.py +235 -607
  40. django_cfg/apps/payments/admin/endpoint_groups_admin.py +127 -0
  41. django_cfg/apps/payments/admin/filters.py +83 -3
  42. django_cfg/apps/payments/admin/networks_admin.py +258 -0
  43. django_cfg/apps/payments/admin/payments_admin.py +171 -461
  44. django_cfg/apps/payments/admin/subscriptions_admin.py +119 -636
  45. django_cfg/apps/payments/admin/tariffs_admin.py +248 -0
  46. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +105 -34
  47. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +12 -16
  48. django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
  49. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +13 -18
  50. django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
  51. django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
  52. django_cfg/apps/payments/middleware/api_access.py +32 -6
  53. django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +26 -0
  54. django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +28 -0
  55. django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +30 -0
  56. django_cfg/apps/payments/models/balance.py +12 -0
  57. django_cfg/apps/payments/models/currencies.py +106 -32
  58. django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
  59. django_cfg/apps/payments/services/core/currency_service.py +35 -28
  60. django_cfg/apps/payments/services/core/payment_service.py +1 -1
  61. django_cfg/apps/payments/services/providers/__init__.py +3 -0
  62. django_cfg/apps/payments/services/providers/base.py +95 -39
  63. django_cfg/apps/payments/services/providers/models/__init__.py +40 -0
  64. django_cfg/apps/payments/services/providers/models/base.py +122 -0
  65. django_cfg/apps/payments/services/providers/models/providers.py +87 -0
  66. django_cfg/apps/payments/services/providers/models/universal.py +48 -0
  67. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
  68. django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
  69. django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
  70. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
  71. django_cfg/apps/payments/services/providers/{nowpayments.py → nowpayments/provider.py} +240 -209
  72. django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
  73. django_cfg/apps/payments/services/providers/registry.py +4 -32
  74. django_cfg/apps/payments/services/providers/sync_service.py +277 -0
  75. django_cfg/apps/payments/static/payments/js/api-client.js +23 -5
  76. django_cfg/apps/payments/static/payments/js/payment-form.js +65 -8
  77. django_cfg/apps/payments/tasks/__init__.py +39 -0
  78. django_cfg/apps/payments/tasks/types.py +73 -0
  79. django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
  80. django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
  81. django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
  82. django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
  83. django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
  84. django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
  85. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
  86. django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
  87. django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
  88. django_cfg/apps/payments/urls_admin.py +1 -1
  89. django_cfg/apps/payments/views/api/currencies.py +5 -5
  90. django_cfg/apps/payments/views/overview/services.py +2 -2
  91. django_cfg/apps/payments/views/serializers/currencies.py +4 -3
  92. django_cfg/apps/support/admin/__init__.py +10 -1
  93. django_cfg/apps/support/admin/support_admin.py +338 -141
  94. django_cfg/apps/tasks/admin/__init__.py +11 -0
  95. django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
  96. django_cfg/config.py +1 -1
  97. django_cfg/core/config.py +10 -5
  98. django_cfg/core/generation.py +1 -1
  99. django_cfg/management/commands/__init__.py +13 -1
  100. django_cfg/management/commands/app_agent_diagnose.py +470 -0
  101. django_cfg/management/commands/app_agent_generate.py +342 -0
  102. django_cfg/management/commands/app_agent_info.py +308 -0
  103. django_cfg/management/commands/migrate_all.py +9 -3
  104. django_cfg/management/commands/migrator.py +11 -6
  105. django_cfg/management/commands/rundramatiq.py +3 -2
  106. django_cfg/middleware/__init__.py +0 -2
  107. django_cfg/models/api_keys.py +115 -0
  108. django_cfg/modules/django_admin/__init__.py +64 -0
  109. django_cfg/modules/django_admin/decorators/__init__.py +13 -0
  110. django_cfg/modules/django_admin/decorators/actions.py +106 -0
  111. django_cfg/modules/django_admin/decorators/display.py +106 -0
  112. django_cfg/modules/django_admin/mixins/__init__.py +14 -0
  113. django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
  114. django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
  115. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
  116. django_cfg/modules/django_admin/models/__init__.py +20 -0
  117. django_cfg/modules/django_admin/models/action_models.py +33 -0
  118. django_cfg/modules/django_admin/models/badge_models.py +20 -0
  119. django_cfg/modules/django_admin/models/base.py +26 -0
  120. django_cfg/modules/django_admin/models/display_models.py +31 -0
  121. django_cfg/modules/django_admin/utils/badges.py +159 -0
  122. django_cfg/modules/django_admin/utils/displays.py +247 -0
  123. django_cfg/modules/django_app_agent/__init__.py +87 -0
  124. django_cfg/modules/django_app_agent/agents/__init__.py +40 -0
  125. django_cfg/modules/django_app_agent/agents/base/__init__.py +24 -0
  126. django_cfg/modules/django_app_agent/agents/base/agent.py +354 -0
  127. django_cfg/modules/django_app_agent/agents/base/context.py +236 -0
  128. django_cfg/modules/django_app_agent/agents/base/executor.py +430 -0
  129. django_cfg/modules/django_app_agent/agents/generation/__init__.py +12 -0
  130. django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +15 -0
  131. django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +147 -0
  132. django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +99 -0
  133. django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +32 -0
  134. django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +290 -0
  135. django_cfg/modules/django_app_agent/agents/interfaces.py +376 -0
  136. django_cfg/modules/django_app_agent/core/__init__.py +33 -0
  137. django_cfg/modules/django_app_agent/core/config.py +300 -0
  138. django_cfg/modules/django_app_agent/core/exceptions.py +359 -0
  139. django_cfg/modules/django_app_agent/models/__init__.py +71 -0
  140. django_cfg/modules/django_app_agent/models/base.py +283 -0
  141. django_cfg/modules/django_app_agent/models/context.py +496 -0
  142. django_cfg/modules/django_app_agent/models/enums.py +481 -0
  143. django_cfg/modules/django_app_agent/models/requests.py +500 -0
  144. django_cfg/modules/django_app_agent/models/responses.py +585 -0
  145. django_cfg/modules/django_app_agent/pytest.ini +6 -0
  146. django_cfg/modules/django_app_agent/services/__init__.py +42 -0
  147. django_cfg/modules/django_app_agent/services/app_generator/__init__.py +30 -0
  148. django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +133 -0
  149. django_cfg/modules/django_app_agent/services/app_generator/context.py +40 -0
  150. django_cfg/modules/django_app_agent/services/app_generator/main.py +202 -0
  151. django_cfg/modules/django_app_agent/services/app_generator/structure.py +316 -0
  152. django_cfg/modules/django_app_agent/services/app_generator/validation.py +125 -0
  153. django_cfg/modules/django_app_agent/services/base.py +437 -0
  154. django_cfg/modules/django_app_agent/services/context_builder/__init__.py +34 -0
  155. django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +141 -0
  156. django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +276 -0
  157. django_cfg/modules/django_app_agent/services/context_builder/main.py +272 -0
  158. django_cfg/modules/django_app_agent/services/context_builder/models.py +40 -0
  159. django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +85 -0
  160. django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +31 -0
  161. django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +311 -0
  162. django_cfg/modules/django_app_agent/services/project_scanner/main.py +221 -0
  163. django_cfg/modules/django_app_agent/services/project_scanner/models.py +59 -0
  164. django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +94 -0
  165. django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +28 -0
  166. django_cfg/modules/django_app_agent/services/questioning_service/main.py +273 -0
  167. django_cfg/modules/django_app_agent/services/questioning_service/models.py +111 -0
  168. django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +251 -0
  169. django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +347 -0
  170. django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +356 -0
  171. django_cfg/modules/django_app_agent/services/report_service.py +332 -0
  172. django_cfg/modules/django_app_agent/services/template_manager/__init__.py +18 -0
  173. django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +236 -0
  174. django_cfg/modules/django_app_agent/services/template_manager/main.py +159 -0
  175. django_cfg/modules/django_app_agent/services/template_manager/models.py +36 -0
  176. django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +100 -0
  177. django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +105 -0
  178. django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +31 -0
  179. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +44 -0
  180. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +81 -0
  181. django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +107 -0
  182. django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +139 -0
  183. django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +91 -0
  184. django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +195 -0
  185. django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +35 -0
  186. django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +211 -0
  187. django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +200 -0
  188. django_cfg/modules/django_app_agent/services/validation_service/__init__.py +25 -0
  189. django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +333 -0
  190. django_cfg/modules/django_app_agent/services/validation_service/main.py +242 -0
  191. django_cfg/modules/django_app_agent/services/validation_service/models.py +66 -0
  192. django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +352 -0
  193. django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +272 -0
  194. django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +203 -0
  195. django_cfg/modules/django_app_agent/ui/__init__.py +25 -0
  196. django_cfg/modules/django_app_agent/ui/cli.py +419 -0
  197. django_cfg/modules/django_app_agent/ui/rich_components.py +622 -0
  198. django_cfg/modules/django_app_agent/utils/__init__.py +38 -0
  199. django_cfg/modules/django_app_agent/utils/logging.py +360 -0
  200. django_cfg/modules/django_app_agent/utils/validation.py +417 -0
  201. django_cfg/modules/django_currency/__init__.py +2 -2
  202. django_cfg/modules/django_currency/clients/__init__.py +2 -2
  203. django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
  204. django_cfg/modules/django_currency/core/converter.py +12 -12
  205. django_cfg/modules/django_currency/database/__init__.py +2 -2
  206. django_cfg/modules/django_currency/database/database_loader.py +93 -42
  207. django_cfg/modules/django_llm/llm/client.py +10 -2
  208. django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
  209. django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
  210. django_cfg/modules/django_unfold/dashboard.py +14 -13
  211. django_cfg/modules/django_unfold/models/config.py +1 -1
  212. django_cfg/registry/core.py +3 -0
  213. django_cfg/registry/third_party.py +2 -2
  214. django_cfg/template_archive/django_sample.zip +0 -0
  215. {django_cfg-1.3.7.dist-info → django_cfg-1.3.9.dist-info}/METADATA +2 -1
  216. {django_cfg-1.3.7.dist-info → django_cfg-1.3.9.dist-info}/RECORD +223 -117
  217. django_cfg/apps/accounts/admin/activity.py +0 -96
  218. django_cfg/apps/accounts/admin/group.py +0 -17
  219. django_cfg/apps/accounts/admin/otp.py +0 -59
  220. django_cfg/apps/accounts/admin/registration_source.py +0 -97
  221. django_cfg/apps/accounts/admin/twilio_response.py +0 -227
  222. django_cfg/apps/accounts/admin/user.py +0 -300
  223. django_cfg/apps/agents/core/agent.py +0 -281
  224. django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
  225. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
  226. django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
  227. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
  228. django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
  229. django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
  230. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
  231. django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
  232. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
  233. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
  234. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
  235. django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
  236. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
  237. django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
  238. django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
  239. django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
  240. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
  241. django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
  242. django_cfg/apps/tasks/admin.py +0 -320
  243. django_cfg/middleware/static_nocache.py +0 -55
  244. django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
  245. /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
  246. /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
  247. /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
  248. /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
  249. {django_cfg-1.3.7.dist-info → django_cfg-1.3.9.dist-info}/WHEEL +0 -0
  250. {django_cfg-1.3.7.dist-info → django_cfg-1.3.9.dist-info}/entry_points.txt +0 -0
  251. {django_cfg-1.3.7.dist-info → django_cfg-1.3.9.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,202 @@
1
+ """
2
+ Standalone Actions Mixin for Django Admin.
3
+
4
+ Provides convenient decorators and utilities for creating standalone action buttons
5
+ that work independently of selected items (not bulk actions).
6
+ """
7
+
8
+ from typing import Callable, Optional, Any
9
+ from functools import wraps
10
+ from django.contrib import messages
11
+ from django.shortcuts import redirect
12
+ from django.http import HttpResponse
13
+ import threading
14
+
15
+ from unfold.decorators import action as unfold_action
16
+ from unfold.enums import ActionVariant as UnfoldActionVariant
17
+ from django_cfg.modules.django_logger import get_logger
18
+ from ..models.action_models import ActionVariant
19
+
20
+
21
+ logger = get_logger("django_admin.mixins.standalone_actions")
22
+
23
+
24
+ def standalone_action(
25
+ description: str,
26
+ variant: Optional[ActionVariant] = None,
27
+ icon: Optional[str] = None,
28
+ url_path: Optional[str] = None,
29
+ background: bool = False,
30
+ success_message: Optional[str] = None,
31
+ error_message: Optional[str] = None
32
+ ):
33
+ """
34
+ Decorator for creating standalone action buttons.
35
+
36
+ Args:
37
+ description: Button text
38
+ variant: Button style (ActionVariant enum)
39
+ icon: Material icon name
40
+ url_path: URL path for the action (auto-generated if not provided)
41
+ background: Run action in background thread
42
+ success_message: Success message template (can use {result} placeholder)
43
+ error_message: Error message template (can use {error} placeholder)
44
+
45
+ Usage:
46
+ @standalone_action(
47
+ description="Update Rates",
48
+ variant=ActionVariant.SUCCESS,
49
+ icon="sync",
50
+ background=True,
51
+ success_message="💱 Rates update started! Refresh in 2-3 minutes.",
52
+ error_message="❌ Failed to start update: {error}"
53
+ )
54
+ def update_rates(self, request):
55
+ # Your logic here
56
+ call_command('manage_currencies', '--populate')
57
+ return "Update completed"
58
+ """
59
+ def decorator(func: Callable) -> Callable:
60
+ @wraps(func)
61
+ def wrapper(self, request: Any) -> HttpResponse:
62
+ try:
63
+ if background:
64
+ # Run in background thread
65
+ def background_task():
66
+ try:
67
+ result = func(self, request)
68
+ logger.info(f"Background action {func.__name__} completed: {result}")
69
+ except Exception as e:
70
+ logger.error(f"Background action {func.__name__} failed: {e}")
71
+
72
+ thread = threading.Thread(target=background_task)
73
+ thread.daemon = True
74
+ thread.start()
75
+
76
+ # Show immediate success message
77
+ if success_message:
78
+ messages.success(request, success_message)
79
+ else:
80
+ messages.success(request, f"{description} started in background.")
81
+
82
+ else:
83
+ # Run synchronously
84
+ result = func(self, request)
85
+
86
+ # Show success message
87
+ if success_message:
88
+ msg = success_message.format(result=result) if result else success_message
89
+ messages.success(request, msg)
90
+ else:
91
+ messages.success(request, f"{description} completed successfully.")
92
+
93
+ logger.info(f"Standalone action {func.__name__} executed by {request.user.username}")
94
+
95
+ except Exception as e:
96
+ # Show error message
97
+ if error_message:
98
+ msg = error_message.format(error=str(e))
99
+ messages.error(request, msg)
100
+ else:
101
+ messages.error(request, f"❌ {description} failed: {str(e)}")
102
+
103
+ logger.error(f"Standalone action {func.__name__} failed: {e}")
104
+
105
+ # Always redirect back
106
+ return redirect(request.META.get('HTTP_REFERER', '/admin/'))
107
+
108
+ # Convert ActionVariant to UnfoldActionVariant
109
+ unfold_variant = UnfoldActionVariant.DEFAULT
110
+ if variant:
111
+ variant_mapping = {
112
+ ActionVariant.DEFAULT: UnfoldActionVariant.DEFAULT,
113
+ ActionVariant.PRIMARY: UnfoldActionVariant.PRIMARY,
114
+ ActionVariant.SUCCESS: UnfoldActionVariant.SUCCESS,
115
+ ActionVariant.INFO: UnfoldActionVariant.INFO,
116
+ ActionVariant.WARNING: UnfoldActionVariant.WARNING,
117
+ ActionVariant.DANGER: UnfoldActionVariant.DANGER,
118
+ }
119
+ unfold_variant = variant_mapping.get(variant, UnfoldActionVariant.DEFAULT)
120
+
121
+ # Auto-generate url_path if not provided
122
+ final_url_path = url_path or func.__name__.replace('_', '-')
123
+
124
+ # Apply unfold decorator
125
+ decorator_kwargs = {
126
+ 'description': description,
127
+ 'variant': unfold_variant,
128
+ 'url_path': final_url_path
129
+ }
130
+ if icon:
131
+ decorator_kwargs['icon'] = icon
132
+
133
+ return unfold_action(**decorator_kwargs)(wrapper)
134
+
135
+ return decorator
136
+
137
+
138
+ class StandaloneActionsMixin:
139
+ """
140
+ Mixin for Django admin classes that provides utilities for standalone actions.
141
+
142
+ Usage:
143
+ class MyAdmin(OptimizedModelAdmin, DisplayMixin, StandaloneActionsMixin, ModelAdmin):
144
+ actions_list = ['update_data', 'sync_external']
145
+
146
+ @standalone_action(
147
+ description="Update Data",
148
+ variant=ActionVariant.SUCCESS,
149
+ icon="sync",
150
+ background=True
151
+ )
152
+ def update_data(self, request):
153
+ # Your update logic
154
+ return "Data updated"
155
+ """
156
+
157
+ def get_standalone_actions(self):
158
+ """Get list of standalone action method names."""
159
+ return getattr(self, 'actions_list', [])
160
+
161
+ def execute_background_task(self, task_func: Callable, *args, **kwargs):
162
+ """
163
+ Utility method to execute tasks in background.
164
+
165
+ Args:
166
+ task_func: Function to execute
167
+ *args, **kwargs: Arguments for the function
168
+ """
169
+ def background_task():
170
+ try:
171
+ result = task_func(*args, **kwargs)
172
+ logger.info(f"Background task {task_func.__name__} completed: {result}")
173
+ return result
174
+ except Exception as e:
175
+ logger.error(f"Background task {task_func.__name__} failed: {e}")
176
+ raise
177
+
178
+ thread = threading.Thread(target=background_task)
179
+ thread.daemon = True
180
+ thread.start()
181
+ return thread
182
+
183
+ def send_admin_notification(self, request, message: str, level: str = 'INFO'):
184
+ """
185
+ Send notification to admin user.
186
+
187
+ Args:
188
+ request: Django request object
189
+ message: Notification message
190
+ level: Message level (SUCCESS, INFO, WARNING, ERROR)
191
+ """
192
+ level_mapping = {
193
+ 'SUCCESS': messages.SUCCESS,
194
+ 'INFO': messages.INFO,
195
+ 'WARNING': messages.WARNING,
196
+ 'ERROR': messages.ERROR,
197
+ }
198
+
199
+ django_level = level_mapping.get(level.upper(), messages.INFO)
200
+ messages.add_message(request, django_level, message)
201
+
202
+ logger.info(f"Admin notification sent to {request.user.username}: {message}")
@@ -0,0 +1,20 @@
1
+ """
2
+ Pydantic 2 models for configuration.
3
+ """
4
+
5
+ from .base import BaseConfig
6
+ from .display_models import UserDisplayConfig, MoneyDisplayConfig, DateTimeDisplayConfig
7
+ from .badge_models import BadgeConfig, BadgeVariant, StatusBadgeConfig
8
+ from .action_models import ActionVariant, ActionConfig
9
+
10
+ __all__ = [
11
+ "BaseConfig",
12
+ "UserDisplayConfig",
13
+ "MoneyDisplayConfig",
14
+ "DateTimeDisplayConfig",
15
+ "BadgeConfig",
16
+ "BadgeVariant",
17
+ "StatusBadgeConfig",
18
+ "ActionVariant",
19
+ "ActionConfig",
20
+ ]
@@ -0,0 +1,33 @@
1
+ """
2
+ Action configuration models.
3
+ """
4
+
5
+ from enum import Enum
6
+ from pydantic import Field
7
+ from typing import Optional, List
8
+ from .base import BaseConfig
9
+
10
+
11
+ class ActionVariant(str, Enum):
12
+ """
13
+ Action variant enum for consistent styling.
14
+
15
+ Based on Unfold ActionVariant but with our own namespace.
16
+ Matches unfold.enums.ActionVariant exactly.
17
+ """
18
+ DEFAULT = "default"
19
+ PRIMARY = "primary"
20
+ SUCCESS = "success"
21
+ INFO = "info"
22
+ WARNING = "warning"
23
+ DANGER = "danger"
24
+
25
+
26
+ class ActionConfig(BaseConfig):
27
+ """Action configuration."""
28
+ variant: ActionVariant = Field(default=ActionVariant.PRIMARY)
29
+ icon: Optional[str] = Field(default=None)
30
+ permissions: List[str] = Field(default=[])
31
+ confirm_message: Optional[str] = Field(default=None)
32
+ success_message: Optional[str] = Field(default=None)
33
+ error_message: Optional[str] = Field(default=None)
@@ -0,0 +1,20 @@
1
+ """
2
+ Badge configuration models.
3
+ """
4
+
5
+ from pydantic import Field
6
+ from typing import Optional, Dict
7
+ from .base import BaseConfig, BadgeVariant
8
+
9
+
10
+ class BadgeConfig(BaseConfig):
11
+ """Base badge configuration."""
12
+ variant: BadgeVariant = Field(default=BadgeVariant.INFO)
13
+ icon: Optional[str] = Field(default=None)
14
+ css_classes: list = Field(default=[])
15
+
16
+
17
+ class StatusBadgeConfig(BadgeConfig):
18
+ """Status badge configuration."""
19
+ custom_mappings: Dict[str, str] = Field(default={})
20
+ show_icons: bool = Field(default=True)
@@ -0,0 +1,26 @@
1
+ """
2
+ Base Pydantic 2 models.
3
+ """
4
+
5
+ from pydantic import BaseModel, Field, ConfigDict
6
+ from typing import List
7
+ from enum import Enum
8
+
9
+
10
+ class BaseConfig(BaseModel):
11
+ """Base configuration for all utilities."""
12
+ model_config = ConfigDict(validate_assignment=True, extra="forbid")
13
+
14
+ cache_timeout: int = Field(default=300, ge=0, le=3600)
15
+ enable_icons: bool = Field(default=True)
16
+ debug_mode: bool = Field(default=False)
17
+
18
+
19
+ class BadgeVariant(str, Enum):
20
+ """Badge color variants."""
21
+ SUCCESS = "success"
22
+ WARNING = "warning"
23
+ DANGER = "danger"
24
+ INFO = "info"
25
+ PRIMARY = "primary"
26
+ SECONDARY = "secondary"
@@ -0,0 +1,31 @@
1
+ """
2
+ Display configuration models.
3
+ """
4
+
5
+ from pydantic import Field
6
+ from .base import BaseConfig
7
+
8
+
9
+ class UserDisplayConfig(BaseConfig):
10
+ """User display configuration."""
11
+ show_email: bool = Field(default=True)
12
+ show_avatar: bool = Field(default=True)
13
+ avatar_size: int = Field(default=32, ge=16, le=128)
14
+
15
+
16
+ class MoneyDisplayConfig(BaseConfig):
17
+ """Money display configuration."""
18
+ currency: str = Field(default="USD", min_length=3, max_length=3)
19
+ show_sign: bool = Field(default=True)
20
+ decimal_places: int = Field(default=2, ge=0, le=8)
21
+ thousand_separator: bool = Field(default=True)
22
+ show_currency_symbol: bool = Field(default=True)
23
+ smart_decimal_places: bool = Field(default=False) # Auto-adjust decimal places based on value
24
+ rate_mode: bool = Field(default=False) # Special formatting for exchange rates
25
+
26
+
27
+ class DateTimeDisplayConfig(BaseConfig):
28
+ """DateTime display configuration."""
29
+ show_relative: bool = Field(default=True)
30
+ show_seconds: bool = Field(default=False)
31
+ datetime_format: str = Field(default="%Y-%m-%d %H:%M:%S")
@@ -0,0 +1,159 @@
1
+ """
2
+ Badge utilities with Material Icons.
3
+ """
4
+
5
+ import logging
6
+ from typing import Optional, Union
7
+ from django.utils.html import format_html, escape
8
+ from django.utils.safestring import SafeString
9
+ from django.contrib.humanize.templatetags.humanize import intcomma
10
+
11
+ from ..models.badge_models import BadgeConfig, StatusBadgeConfig
12
+ from ..models.base import BadgeVariant
13
+ from ..icons import Icons
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class StatusBadge:
19
+ """Status badge utilities."""
20
+
21
+ # Status mappings
22
+ STATUS_MAPPINGS = {
23
+ 'active': BadgeVariant.SUCCESS,
24
+ 'success': BadgeVariant.SUCCESS,
25
+ 'completed': BadgeVariant.SUCCESS,
26
+ 'pending': BadgeVariant.WARNING,
27
+ 'processing': BadgeVariant.WARNING,
28
+ 'failed': BadgeVariant.DANGER,
29
+ 'error': BadgeVariant.DANGER,
30
+ 'cancelled': BadgeVariant.DANGER,
31
+ 'inactive': BadgeVariant.SECONDARY,
32
+ }
33
+
34
+ # Variant classes with semantic colors
35
+ VARIANT_CLASSES = {
36
+ BadgeVariant.SUCCESS: 'bg-success-100 text-success-800 dark:bg-success-900 dark:text-success-200',
37
+ BadgeVariant.WARNING: 'bg-warning-100 text-warning-800 dark:bg-warning-900 dark:text-warning-200',
38
+ BadgeVariant.DANGER: 'bg-danger-100 text-danger-800 dark:bg-danger-900 dark:text-danger-200',
39
+ BadgeVariant.INFO: 'bg-info-100 text-info-800 dark:bg-info-900 dark:text-info-200',
40
+ BadgeVariant.PRIMARY: 'bg-primary-100 text-primary-800 dark:bg-primary-900 dark:text-primary-200',
41
+ BadgeVariant.SECONDARY: 'bg-base-100 text-font-default-light dark:bg-base-800 dark:text-font-default-dark',
42
+ }
43
+
44
+ @classmethod
45
+ def auto(cls, status: str, config: Optional[StatusBadgeConfig] = None) -> SafeString:
46
+ """Auto status badge with color mapping."""
47
+ config = config or StatusBadgeConfig()
48
+
49
+ if not status:
50
+ return format_html('<span class="text-font-subtle-light dark:text-font-subtle-dark">—</span>')
51
+
52
+ # Determine variant
53
+ status_lower = status.lower().replace('_', '').replace('-', '')
54
+ variant = BadgeVariant.INFO
55
+
56
+ for keyword, mapped_variant in cls.STATUS_MAPPINGS.items():
57
+ if keyword in status_lower:
58
+ variant = mapped_variant
59
+ break
60
+
61
+ # Use custom mapping if provided
62
+ if config.custom_mappings and status in config.custom_mappings:
63
+ variant_str = config.custom_mappings[status]
64
+ try:
65
+ variant = BadgeVariant(variant_str)
66
+ except ValueError:
67
+ pass
68
+
69
+ return cls.create(status.replace('_', ' ').title(), variant, config)
70
+
71
+ @classmethod
72
+ def create(cls, text: str, variant: Union[BadgeVariant, str] = BadgeVariant.INFO,
73
+ config: Optional[StatusBadgeConfig] = None, icon: Optional[str] = None) -> SafeString:
74
+ """Create custom badge."""
75
+ config = config or StatusBadgeConfig()
76
+
77
+ if isinstance(variant, str):
78
+ try:
79
+ variant = BadgeVariant(variant)
80
+ except ValueError:
81
+ variant = BadgeVariant.INFO
82
+
83
+ css_classes = cls.VARIANT_CLASSES.get(variant, cls.VARIANT_CLASSES[BadgeVariant.INFO])
84
+
85
+ if config.css_classes:
86
+ css_classes += ' ' + ' '.join(config.css_classes)
87
+
88
+ # Icon with Material Icons integration
89
+ icon_html = ""
90
+ if icon or (config.show_icons and config.icon):
91
+ icon_to_use = icon or config.icon
92
+ if icon_to_use:
93
+ # Use custom icon
94
+ icon_html = format_html('<span class="material-symbols-outlined text-xs mr-1">{}</span>', icon_to_use)
95
+ else:
96
+ # Auto-detect icon based on variant
97
+ icon_map = {
98
+ BadgeVariant.SUCCESS: Icons.CHECK_CIRCLE,
99
+ BadgeVariant.WARNING: Icons.WARNING,
100
+ BadgeVariant.DANGER: Icons.ERROR,
101
+ BadgeVariant.INFO: Icons.INFO,
102
+ BadgeVariant.PRIMARY: Icons.STAR,
103
+ BadgeVariant.SECONDARY: Icons.INFO,
104
+ }
105
+ icon_name = icon_map.get(variant, Icons.INFO)
106
+ icon_html = format_html('<span class="material-symbols-outlined text-xs mr-1">{}</span>', icon_name)
107
+
108
+ return format_html(
109
+ '<span class="inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium {}">'
110
+ '{}{}'
111
+ '</span>',
112
+ css_classes, icon_html, escape(text)
113
+ )
114
+
115
+
116
+ class ProgressBadge:
117
+ """Progress badge utilities."""
118
+
119
+ @classmethod
120
+ def percentage(cls, percentage: Union[int, float]) -> SafeString:
121
+ """Progress badge with percentage."""
122
+ percentage = max(0, min(100, float(percentage)))
123
+
124
+ if percentage >= 100:
125
+ variant = BadgeVariant.SUCCESS
126
+ elif percentage >= 75:
127
+ variant = BadgeVariant.INFO
128
+ elif percentage >= 50:
129
+ variant = BadgeVariant.WARNING
130
+ else:
131
+ variant = BadgeVariant.SECONDARY
132
+
133
+ return StatusBadge.create(f"{percentage:.0f}%", variant)
134
+
135
+
136
+ class CounterBadge:
137
+ """Counter badge utilities."""
138
+
139
+ @classmethod
140
+ def simple(cls, count: int, label: str = None) -> SafeString:
141
+ """Simple counter badge."""
142
+ if count == 0:
143
+ variant = BadgeVariant.SECONDARY
144
+ elif count < 10:
145
+ variant = BadgeVariant.INFO
146
+ elif count < 100:
147
+ variant = BadgeVariant.WARNING
148
+ else:
149
+ variant = BadgeVariant.SUCCESS
150
+
151
+ # Format with humanize for large numbers
152
+ if count >= 1000:
153
+ count_text = intcomma(count)
154
+ else:
155
+ count_text = str(count)
156
+
157
+ display_text = f"{count_text} {label}" if label else count_text
158
+
159
+ return StatusBadge.create(display_text, variant)