django-cfg 1.4.10__py3-none-any.whl → 1.4.13__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 (225) hide show
  1. django_cfg/apps/agents/management/commands/create_agent.py +1 -1
  2. django_cfg/apps/agents/management/commands/orchestrator_status.py +3 -3
  3. django_cfg/apps/newsletter/serializers.py +40 -3
  4. django_cfg/apps/newsletter/views/campaigns.py +12 -3
  5. django_cfg/apps/newsletter/views/emails.py +14 -3
  6. django_cfg/apps/newsletter/views/subscriptions.py +12 -2
  7. django_cfg/apps/payments/views/api/currencies.py +49 -6
  8. django_cfg/apps/payments/views/api/webhooks.py +72 -7
  9. django_cfg/apps/payments/views/overview/serializers.py +34 -1
  10. django_cfg/apps/payments/views/overview/views.py +2 -1
  11. django_cfg/apps/payments/views/serializers/payments.py +6 -6
  12. django_cfg/apps/urls.py +106 -45
  13. django_cfg/core/base/config_model.py +2 -2
  14. django_cfg/core/constants.py +1 -1
  15. django_cfg/core/generation/integration_generators/__init__.py +1 -1
  16. django_cfg/core/generation/integration_generators/api.py +73 -49
  17. django_cfg/core/integration/display/startup.py +30 -22
  18. django_cfg/core/integration/url_integration.py +15 -16
  19. django_cfg/management/commands/check_endpoints.py +11 -160
  20. django_cfg/management/commands/check_settings.py +13 -348
  21. django_cfg/management/commands/clear_constance.py +13 -201
  22. django_cfg/management/commands/create_token.py +13 -321
  23. django_cfg/management/commands/generate_clients.py +23 -0
  24. django_cfg/management/commands/list_urls.py +13 -306
  25. django_cfg/management/commands/migrate_all.py +13 -126
  26. django_cfg/management/commands/migrator.py +13 -396
  27. django_cfg/management/commands/rundramatiq.py +15 -247
  28. django_cfg/management/commands/rundramatiq_simulator.py +12 -429
  29. django_cfg/management/commands/runserver_ngrok.py +15 -160
  30. django_cfg/management/commands/script.py +12 -488
  31. django_cfg/management/commands/show_config.py +12 -215
  32. django_cfg/management/commands/show_urls.py +12 -342
  33. django_cfg/management/commands/superuser.py +15 -295
  34. django_cfg/management/commands/task_clear.py +14 -217
  35. django_cfg/management/commands/task_status.py +13 -248
  36. django_cfg/management/commands/test_email.py +15 -86
  37. django_cfg/management/commands/test_telegram.py +14 -61
  38. django_cfg/management/commands/test_twilio.py +15 -105
  39. django_cfg/management/commands/tree.py +13 -383
  40. django_cfg/management/commands/validate_openapi.py +10 -0
  41. django_cfg/middleware/README.md +1 -1
  42. django_cfg/middleware/user_activity.py +3 -3
  43. django_cfg/models/__init__.py +2 -2
  44. django_cfg/models/api/drf/spectacular.py +6 -6
  45. django_cfg/models/django/__init__.py +2 -2
  46. django_cfg/models/django/openapi.py +162 -0
  47. django_cfg/modules/django_admin/management/commands/check_endpoints.py +169 -0
  48. django_cfg/modules/django_admin/management/commands/check_settings.py +355 -0
  49. django_cfg/modules/django_admin/management/commands/clear_constance.py +208 -0
  50. django_cfg/modules/django_admin/management/commands/create_token.py +328 -0
  51. django_cfg/modules/django_admin/management/commands/list_urls.py +313 -0
  52. django_cfg/modules/django_admin/management/commands/migrate_all.py +133 -0
  53. django_cfg/modules/django_admin/management/commands/migrator.py +403 -0
  54. django_cfg/modules/django_admin/management/commands/script.py +496 -0
  55. django_cfg/modules/django_admin/management/commands/show_config.py +225 -0
  56. django_cfg/modules/django_admin/management/commands/show_urls.py +361 -0
  57. django_cfg/modules/django_admin/management/commands/superuser.py +302 -0
  58. django_cfg/modules/django_admin/management/commands/tree.py +390 -0
  59. django_cfg/modules/django_client/__init__.py +20 -0
  60. django_cfg/modules/django_client/apps.py +35 -0
  61. django_cfg/modules/django_client/core/__init__.py +56 -0
  62. django_cfg/modules/django_client/core/archive/__init__.py +11 -0
  63. django_cfg/modules/django_client/core/archive/manager.py +134 -0
  64. django_cfg/modules/django_client/core/cli/__init__.py +12 -0
  65. django_cfg/modules/django_client/core/cli/main.py +235 -0
  66. django_cfg/modules/django_client/core/config/__init__.py +18 -0
  67. django_cfg/modules/django_client/core/config/config.py +208 -0
  68. django_cfg/modules/django_client/core/config/group.py +101 -0
  69. django_cfg/modules/django_client/core/config/service.py +209 -0
  70. django_cfg/modules/django_client/core/generator/__init__.py +115 -0
  71. django_cfg/modules/django_client/core/generator/base.py +838 -0
  72. django_cfg/modules/django_client/core/generator/python/__init__.py +16 -0
  73. django_cfg/modules/django_client/core/generator/python/async_client_gen.py +174 -0
  74. django_cfg/modules/django_client/core/generator/python/files_generator.py +180 -0
  75. django_cfg/modules/django_client/core/generator/python/generator.py +182 -0
  76. django_cfg/modules/django_client/core/generator/python/models_generator.py +318 -0
  77. django_cfg/modules/django_client/core/generator/python/operations_generator.py +278 -0
  78. django_cfg/modules/django_client/core/generator/python/sync_client_gen.py +102 -0
  79. django_cfg/modules/django_client/core/generator/python/templates/__init__.py.jinja +9 -0
  80. django_cfg/modules/django_client/core/generator/python/templates/api_wrapper.py.jinja +153 -0
  81. django_cfg/modules/django_client/core/generator/python/templates/app_init.py.jinja +6 -0
  82. django_cfg/modules/django_client/core/generator/python/templates/client/app_client.py.jinja +18 -0
  83. django_cfg/modules/django_client/core/generator/python/templates/client/flat_client.py.jinja +38 -0
  84. django_cfg/modules/django_client/core/generator/python/templates/client/main_client.py.jinja +68 -0
  85. django_cfg/modules/django_client/core/generator/python/templates/client/main_client_file.py.jinja +14 -0
  86. django_cfg/modules/django_client/core/generator/python/templates/client/operation_method.py.jinja +9 -0
  87. django_cfg/modules/django_client/core/generator/python/templates/client/sub_client.py.jinja +18 -0
  88. django_cfg/modules/django_client/core/generator/python/templates/client/sync_main_client.py.jinja +50 -0
  89. django_cfg/modules/django_client/core/generator/python/templates/client/sync_operation_method.py.jinja +9 -0
  90. django_cfg/modules/django_client/core/generator/python/templates/client/sync_sub_client.py.jinja +18 -0
  91. django_cfg/modules/django_client/core/generator/python/templates/client_file.py.jinja +13 -0
  92. django_cfg/modules/django_client/core/generator/python/templates/main_init.py.jinja +52 -0
  93. django_cfg/modules/django_client/core/generator/python/templates/models/app_models.py.jinja +17 -0
  94. django_cfg/modules/django_client/core/generator/python/templates/models/enum_class.py.jinja +17 -0
  95. django_cfg/modules/django_client/core/generator/python/templates/models/enums.py.jinja +8 -0
  96. django_cfg/modules/django_client/core/generator/python/templates/models/models.py.jinja +17 -0
  97. django_cfg/modules/django_client/core/generator/python/templates/models/schema_class.py.jinja +21 -0
  98. django_cfg/modules/django_client/core/generator/python/templates/pyproject.toml.jinja +55 -0
  99. django_cfg/modules/django_client/core/generator/python/templates/utils/logger.py.jinja +255 -0
  100. django_cfg/modules/django_client/core/generator/python/templates/utils/retry.py.jinja +271 -0
  101. django_cfg/modules/django_client/core/generator/python/templates/utils/schema.py.jinja +12 -0
  102. django_cfg/modules/django_client/core/generator/typescript/__init__.py +14 -0
  103. django_cfg/modules/django_client/core/generator/typescript/client_generator.py +165 -0
  104. django_cfg/modules/django_client/core/generator/typescript/fetchers_generator.py +428 -0
  105. django_cfg/modules/django_client/core/generator/typescript/files_generator.py +207 -0
  106. django_cfg/modules/django_client/core/generator/typescript/generator.py +432 -0
  107. django_cfg/modules/django_client/core/generator/typescript/hooks_generator.py +536 -0
  108. django_cfg/modules/django_client/core/generator/typescript/models_generator.py +245 -0
  109. django_cfg/modules/django_client/core/generator/typescript/operations_generator.py +298 -0
  110. django_cfg/modules/django_client/core/generator/typescript/schemas_generator.py +329 -0
  111. django_cfg/modules/django_client/core/generator/typescript/templates/api_instance.ts.jinja +131 -0
  112. django_cfg/modules/django_client/core/generator/typescript/templates/app_index.ts.jinja +2 -0
  113. django_cfg/modules/django_client/core/generator/typescript/templates/client/app_client.ts.jinja +18 -0
  114. django_cfg/modules/django_client/core/generator/typescript/templates/client/client.ts.jinja +403 -0
  115. django_cfg/modules/django_client/core/generator/typescript/templates/client/flat_client.ts.jinja +109 -0
  116. django_cfg/modules/django_client/core/generator/typescript/templates/client/main_client_file.ts.jinja +10 -0
  117. django_cfg/modules/django_client/core/generator/typescript/templates/client/operation.ts.jinja +61 -0
  118. django_cfg/modules/django_client/core/generator/typescript/templates/client/sub_client.ts.jinja +15 -0
  119. django_cfg/modules/django_client/core/generator/typescript/templates/client_file.ts.jinja +9 -0
  120. django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/fetchers.ts.jinja +45 -0
  121. django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/index.ts.jinja +30 -0
  122. django_cfg/modules/django_client/core/generator/typescript/templates/index.ts.jinja +5 -0
  123. django_cfg/modules/django_client/core/generator/typescript/templates/main_index.ts.jinja +268 -0
  124. django_cfg/modules/django_client/core/generator/typescript/templates/models/app_models.ts.jinja +8 -0
  125. django_cfg/modules/django_client/core/generator/typescript/templates/models/enums.ts.jinja +4 -0
  126. django_cfg/modules/django_client/core/generator/typescript/templates/models/models.ts.jinja +8 -0
  127. django_cfg/modules/django_client/core/generator/typescript/templates/package.json.jinja +52 -0
  128. django_cfg/modules/django_client/core/generator/typescript/templates/schemas/index.ts.jinja +21 -0
  129. django_cfg/modules/django_client/core/generator/typescript/templates/schemas/schema.ts.jinja +24 -0
  130. django_cfg/modules/django_client/core/generator/typescript/templates/tsconfig.json.jinja +20 -0
  131. django_cfg/modules/django_client/core/generator/typescript/templates/utils/errors.ts.jinja +116 -0
  132. django_cfg/modules/django_client/core/generator/typescript/templates/utils/http.ts.jinja +98 -0
  133. django_cfg/modules/django_client/core/generator/typescript/templates/utils/logger.ts.jinja +259 -0
  134. django_cfg/modules/django_client/core/generator/typescript/templates/utils/retry.ts.jinja +175 -0
  135. django_cfg/modules/django_client/core/generator/typescript/templates/utils/schema.ts.jinja +7 -0
  136. django_cfg/modules/django_client/core/generator/typescript/templates/utils/storage.ts.jinja +158 -0
  137. django_cfg/modules/django_client/core/groups/__init__.py +13 -0
  138. django_cfg/modules/django_client/core/groups/detector.py +178 -0
  139. django_cfg/modules/django_client/core/groups/manager.py +314 -0
  140. django_cfg/modules/django_client/core/ir/__init__.py +57 -0
  141. django_cfg/modules/django_client/core/ir/context.py +387 -0
  142. django_cfg/modules/django_client/core/ir/operation.py +518 -0
  143. django_cfg/modules/django_client/core/ir/schema.py +353 -0
  144. django_cfg/modules/django_client/core/parser/__init__.py +74 -0
  145. django_cfg/modules/django_client/core/parser/base.py +648 -0
  146. django_cfg/modules/django_client/core/parser/models/__init__.py +74 -0
  147. django_cfg/modules/django_client/core/parser/models/base.py +212 -0
  148. django_cfg/modules/django_client/core/parser/models/components.py +160 -0
  149. django_cfg/modules/django_client/core/parser/models/openapi.py +203 -0
  150. django_cfg/modules/django_client/core/parser/models/operation.py +207 -0
  151. django_cfg/modules/django_client/core/parser/models/schema.py +266 -0
  152. django_cfg/modules/django_client/core/parser/openapi30.py +56 -0
  153. django_cfg/modules/django_client/core/parser/openapi31.py +64 -0
  154. django_cfg/modules/django_client/core/validation/__init__.py +22 -0
  155. django_cfg/modules/django_client/core/validation/checker.py +134 -0
  156. django_cfg/modules/django_client/core/validation/fixer.py +216 -0
  157. django_cfg/modules/django_client/core/validation/reporter.py +480 -0
  158. django_cfg/modules/django_client/core/validation/rules/__init__.py +11 -0
  159. django_cfg/modules/django_client/core/validation/rules/base.py +96 -0
  160. django_cfg/modules/django_client/core/validation/rules/type_hints.py +288 -0
  161. django_cfg/modules/django_client/core/validation/safety.py +266 -0
  162. django_cfg/modules/django_client/management/__init__.py +3 -0
  163. django_cfg/modules/django_client/management/commands/__init__.py +3 -0
  164. django_cfg/modules/django_client/management/commands/generate_client.py +427 -0
  165. django_cfg/modules/django_client/management/commands/validate_openapi.py +343 -0
  166. django_cfg/modules/django_client/pytest.ini +30 -0
  167. django_cfg/modules/django_client/spectacular/__init__.py +10 -0
  168. django_cfg/modules/django_client/spectacular/async_detection.py +187 -0
  169. django_cfg/modules/django_client/spectacular/enum_naming.py +192 -0
  170. django_cfg/modules/django_client/urls.py +72 -0
  171. django_cfg/{dashboard → modules/django_dashboard}/DEBUG_README.md +2 -2
  172. django_cfg/{dashboard → modules/django_dashboard}/REFACTORING_SUMMARY.md +1 -1
  173. django_cfg/modules/django_dashboard/management/__init__.py +0 -0
  174. django_cfg/modules/django_dashboard/management/commands/__init__.py +0 -0
  175. django_cfg/{dashboard → modules/django_dashboard}/management/commands/debug_dashboard.py +5 -5
  176. django_cfg/modules/django_dashboard/sections/documentation.py +391 -0
  177. django_cfg/modules/django_email/management/__init__.py +0 -0
  178. django_cfg/modules/django_email/management/commands/__init__.py +0 -0
  179. django_cfg/modules/django_email/management/commands/test_email.py +93 -0
  180. django_cfg/modules/django_logging/LOGGING_GUIDE.md +1 -1
  181. django_cfg/modules/django_logging/django_logger.py +6 -6
  182. django_cfg/modules/django_ngrok/management/__init__.py +0 -0
  183. django_cfg/modules/django_ngrok/management/commands/__init__.py +0 -0
  184. django_cfg/modules/django_ngrok/management/commands/runserver_ngrok.py +167 -0
  185. django_cfg/modules/django_tasks/management/__init__.py +0 -0
  186. django_cfg/modules/django_tasks/management/commands/__init__.py +0 -0
  187. django_cfg/modules/django_tasks/management/commands/rundramatiq.py +254 -0
  188. django_cfg/modules/django_tasks/management/commands/rundramatiq_simulator.py +437 -0
  189. django_cfg/modules/django_tasks/management/commands/task_clear.py +226 -0
  190. django_cfg/modules/django_tasks/management/commands/task_status.py +257 -0
  191. django_cfg/modules/django_telegram/management/__init__.py +0 -0
  192. django_cfg/modules/django_telegram/management/commands/__init__.py +0 -0
  193. django_cfg/modules/django_telegram/management/commands/test_telegram.py +68 -0
  194. django_cfg/modules/django_twilio/management/__init__.py +0 -0
  195. django_cfg/modules/django_twilio/management/commands/__init__.py +0 -0
  196. django_cfg/modules/django_twilio/management/commands/test_twilio.py +112 -0
  197. django_cfg/modules/django_unfold/callbacks/main.py +21 -10
  198. django_cfg/modules/django_unfold/callbacks/revolution.py +41 -36
  199. django_cfg/pyproject.toml +2 -6
  200. django_cfg/registry/third_party.py +5 -7
  201. django_cfg/routing/callbacks.py +1 -1
  202. django_cfg/static/admin/css/prose-unfold.css +666 -0
  203. django_cfg/templates/admin/index.html +8 -0
  204. django_cfg/templates/admin/index_new.html +13 -0
  205. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +15 -3
  206. django_cfg/templates/admin/sections/documentation_section.html +172 -0
  207. django_cfg/templates/admin/snippets/tabs/documentation_tab.html +231 -0
  208. {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/METADATA +2 -2
  209. {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/RECORD +224 -74
  210. django_cfg/management/commands/generate.py +0 -107
  211. /django_cfg/models/django/{revolution.py → revolution_legacy.py} +0 -0
  212. /django_cfg/{dashboard → modules/django_admin}/management/__init__.py +0 -0
  213. /django_cfg/{dashboard → modules/django_admin}/management/commands/__init__.py +0 -0
  214. /django_cfg/{dashboard → modules/django_dashboard}/__init__.py +0 -0
  215. /django_cfg/{dashboard → modules/django_dashboard}/components.py +0 -0
  216. /django_cfg/{dashboard → modules/django_dashboard}/debug.py +0 -0
  217. /django_cfg/{dashboard → modules/django_dashboard}/sections/__init__.py +0 -0
  218. /django_cfg/{dashboard → modules/django_dashboard}/sections/base.py +0 -0
  219. /django_cfg/{dashboard → modules/django_dashboard}/sections/commands.py +0 -0
  220. /django_cfg/{dashboard → modules/django_dashboard}/sections/overview.py +0 -0
  221. /django_cfg/{dashboard → modules/django_dashboard}/sections/stats.py +0 -0
  222. /django_cfg/{dashboard → modules/django_dashboard}/sections/system.py +0 -0
  223. {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/WHEEL +0 -0
  224. {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/entry_points.txt +0 -0
  225. {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,391 @@
1
+ """Documentation section for dashboard."""
2
+
3
+ import os
4
+ import inspect
5
+ from pathlib import Path
6
+ from typing import Dict, Any, List
7
+ from django.core.management import get_commands, load_command_class
8
+ from .base import DataSection
9
+
10
+
11
+ class DocumentationSection(DataSection):
12
+ """
13
+ Management commands documentation section.
14
+
15
+ Displays the README.md file from django_cfg/management/ directory.
16
+ """
17
+
18
+ template_name = "admin/sections/documentation_section.html"
19
+ title = "Commands Documentation"
20
+ icon = "description"
21
+
22
+ def get_context_data(self, **kwargs) -> Dict[str, Any]:
23
+ """Add documentation data directly to context (not nested under 'data')."""
24
+ # Call parent but skip DataSection's get_data() nesting
25
+ context = super(DataSection, self).get_context_data(**kwargs)
26
+ # Add our data directly to context
27
+ context.update(self.get_data())
28
+ return context
29
+
30
+ def find_module_readme(self, module_name: str) -> str:
31
+ """Find and read README.md from module's management directory."""
32
+ import logging
33
+ logger = logging.getLogger(__name__)
34
+
35
+ try:
36
+ import django_cfg
37
+ django_cfg_path = Path(django_cfg.__file__).parent
38
+
39
+ # Try different paths
40
+ possible_paths = [
41
+ django_cfg_path / 'modules' / module_name / 'management' / 'README.md',
42
+ django_cfg_path / 'management' / 'commands' / module_name / 'README.md',
43
+ ]
44
+
45
+ for readme_path in possible_paths:
46
+ if readme_path.exists():
47
+ logger.info(f"Found module README at: {readme_path}")
48
+ with open(readme_path, 'r', encoding='utf-8') as f:
49
+ return f.read()
50
+ except Exception as e:
51
+ logger.debug(f"Could not find README for module {module_name}: {e}")
52
+
53
+ return ""
54
+
55
+ def get_commands_structure(self) -> Dict[str, List[Dict[str, Any]]]:
56
+ """Get structured command data grouped by app."""
57
+ import logging
58
+ logger = logging.getLogger(__name__)
59
+
60
+ # Get all management commands
61
+ commands = get_commands()
62
+
63
+ # Filter only django_cfg commands
64
+ django_cfg_commands = {
65
+ name: app for name, app in commands.items()
66
+ if 'django_cfg' in app
67
+ }
68
+
69
+ logger.info(f"Found {len(django_cfg_commands)} django_cfg commands")
70
+
71
+ # Group commands by app
72
+ commands_by_app = {}
73
+
74
+ for cmd_name, app_name in sorted(django_cfg_commands.items()):
75
+ try:
76
+ # Load the command class
77
+ try:
78
+ command = load_command_class(app_name, cmd_name)
79
+ except (ImportError, ModuleNotFoundError) as e:
80
+ logger.debug(f"Skipping command {cmd_name}: module not found - {e}")
81
+ continue
82
+
83
+ # Use app_name as group key
84
+ if app_name not in commands_by_app:
85
+ # Create display name from app_name
86
+ display_name = app_name.replace('django_cfg.modules.', '').replace('django_cfg', 'Django CFG').replace('_', ' ').title()
87
+
88
+ commands_by_app[app_name] = {
89
+ 'name': app_name,
90
+ 'display_name': display_name,
91
+ 'commands': []
92
+ }
93
+
94
+ # Extract command information
95
+ command_info = {
96
+ 'name': cmd_name,
97
+ 'help': getattr(command, 'help', 'No description available'),
98
+ 'docstring': inspect.cleandoc(command.__doc__) if command.__doc__ else '',
99
+ 'web_executable': getattr(command, 'web_executable', None),
100
+ 'requires_input': getattr(command, 'requires_input', None),
101
+ 'is_destructive': getattr(command, 'is_destructive', None),
102
+ 'arguments': []
103
+ }
104
+
105
+ # Get arguments
106
+ try:
107
+ parser = command.create_parser('', cmd_name)
108
+ actions = [action for action in parser._actions if action.dest != 'help']
109
+
110
+ for action in actions:
111
+ if action.option_strings:
112
+ command_info['arguments'].append({
113
+ 'options': action.option_strings,
114
+ 'help': action.help or 'No description',
115
+ 'default': action.default if action.default != '==SUPPRESS==' else None,
116
+ 'required': getattr(action, 'required', False),
117
+ })
118
+ except Exception as e:
119
+ logger.debug(f"Could not extract arguments for {cmd_name}: {e}")
120
+
121
+ commands_by_app[app_name]['commands'].append(command_info)
122
+
123
+ except Exception as e:
124
+ logger.error(f"Error loading command {cmd_name}: {e}")
125
+ continue
126
+
127
+ return commands_by_app
128
+
129
+ def generate_documentation_from_commands(self) -> str:
130
+ """Generate markdown documentation from command docstrings and module READMEs."""
131
+ import logging
132
+ logger = logging.getLogger(__name__)
133
+
134
+ markdown_lines = []
135
+ markdown_lines.append("# Django-CFG Management Commands\n\n")
136
+
137
+ # Check for main README.md
138
+ try:
139
+ import django_cfg
140
+ django_cfg_path = Path(django_cfg.__file__).parent
141
+ main_readme_path = django_cfg_path / 'management' / 'README.md'
142
+
143
+ if main_readme_path.exists():
144
+ logger.info("Found main management README.md, using it as primary documentation")
145
+ with open(main_readme_path, 'r', encoding='utf-8') as f:
146
+ return f.read() # Use existing README if it exists
147
+ except Exception as e:
148
+ logger.debug(f"No main README found, continuing with auto-generation: {e}")
149
+
150
+ markdown_lines.append("_Auto-generated documentation from command docstrings and module READMEs._\n\n")
151
+
152
+ # Get all management commands
153
+ commands = get_commands()
154
+
155
+ # Filter only django_cfg commands
156
+ django_cfg_commands = {
157
+ name: app for name, app in commands.items()
158
+ if 'django_cfg' in app
159
+ }
160
+
161
+ logger.info(f"Found {len(django_cfg_commands)} django_cfg commands")
162
+
163
+ # Group commands by module
164
+ commands_by_module = {}
165
+ module_paths = {} # Store module paths for README lookup
166
+
167
+ for cmd_name, app_name in sorted(django_cfg_commands.items()):
168
+ try:
169
+ # Load the command class
170
+ command = load_command_class(app_name, cmd_name)
171
+
172
+ # Get module path
173
+ module_path = command.__module__
174
+ if 'modules' in module_path:
175
+ # Extract module name like 'django_ngrok', 'django_tasks', etc.
176
+ parts = module_path.split('.')
177
+ if 'modules' in parts:
178
+ module_idx = parts.index('modules')
179
+ if len(parts) > module_idx + 1:
180
+ module_name = parts[module_idx + 1]
181
+ else:
182
+ module_name = 'core'
183
+ else:
184
+ module_name = 'core'
185
+ else:
186
+ module_name = 'core'
187
+
188
+ if module_name not in commands_by_module:
189
+ commands_by_module[module_name] = []
190
+ module_paths[module_name] = module_path
191
+
192
+ commands_by_module[module_name].append((cmd_name, command))
193
+
194
+ except Exception as e:
195
+ logger.error(f"Error loading command {cmd_name}: {e}")
196
+ continue
197
+
198
+ # Generate documentation for each module
199
+ for module_name in sorted(commands_by_module.keys()):
200
+ markdown_lines.append(f"\n## {module_name.replace('_', ' ').title()} Commands\n\n")
201
+
202
+ # Check for module-specific README
203
+ module_readme = self.find_module_readme(module_name)
204
+ if module_readme:
205
+ markdown_lines.append(f"_{module_name} module documentation:_\n\n")
206
+ markdown_lines.append(module_readme)
207
+ markdown_lines.append("\n\n### Available Commands\n\n")
208
+
209
+ for cmd_name, command_class in sorted(commands_by_module[module_name]):
210
+ markdown_lines.append(f"\n#### `{cmd_name}`\n\n")
211
+
212
+ # Get help text
213
+ if hasattr(command_class, 'help'):
214
+ markdown_lines.append(f"{command_class.help}\n\n")
215
+
216
+ # Get class docstring
217
+ if command_class.__doc__:
218
+ doc = inspect.cleandoc(command_class.__doc__)
219
+ markdown_lines.append(f"{doc}\n\n")
220
+
221
+ # Get metadata
222
+ metadata = []
223
+ if hasattr(command_class, 'web_executable'):
224
+ metadata.append(f"**Web Executable**: {'Yes' if command_class.web_executable else 'No'}")
225
+ if hasattr(command_class, 'requires_input'):
226
+ metadata.append(f"**Requires Input**: {'Yes' if command_class.requires_input else 'No'}")
227
+ if hasattr(command_class, 'is_destructive'):
228
+ metadata.append(f"**Destructive**: {'Yes' if command_class.is_destructive else 'No'}")
229
+
230
+ if metadata:
231
+ markdown_lines.append("**Metadata:**\n\n")
232
+ for meta in metadata:
233
+ markdown_lines.append(f"- {meta}\n")
234
+ markdown_lines.append("\n")
235
+
236
+ # Get arguments
237
+ try:
238
+ parser = command_class.create_parser('', cmd_name)
239
+ actions = [action for action in parser._actions if action.dest != 'help']
240
+
241
+ if actions:
242
+ markdown_lines.append("**Arguments:**\n\n")
243
+ for action in actions:
244
+ if action.option_strings:
245
+ opts = ', '.join(f"`{opt}`" for opt in action.option_strings)
246
+ help_text = action.help or 'No description'
247
+ markdown_lines.append(f"- {opts}: {help_text}\n")
248
+ markdown_lines.append("\n")
249
+ except Exception as e:
250
+ logger.debug(f"Could not extract arguments for {cmd_name}: {e}")
251
+
252
+ # Add usage example if available in docstring
253
+ if command_class.__doc__ and '```' in command_class.__doc__:
254
+ markdown_lines.append("**Example:**\n\n")
255
+
256
+ return ''.join(markdown_lines)
257
+
258
+ def get_data(self) -> Dict[str, Any]:
259
+ """Get structured command documentation data."""
260
+ import logging
261
+ logger = logging.getLogger(__name__)
262
+
263
+ try:
264
+ commands_structure = self.get_commands_structure()
265
+
266
+ return {
267
+ 'commands_by_module': commands_structure,
268
+ 'total_commands': sum(len(app['commands']) for app in commands_structure.values()),
269
+ 'total_modules': len(commands_structure),
270
+ }
271
+ except Exception as e:
272
+ logger.error(f"Error generating command structure: {e}", exc_info=True)
273
+ return {
274
+ 'commands_by_module': {},
275
+ 'total_commands': 0,
276
+ 'total_modules': 0,
277
+ 'error': str(e)
278
+ }
279
+
280
+ def get_data_old(self) -> Dict[str, Any]:
281
+ """DEPRECATED: Get documentation content from README.md or auto-generate from docstrings."""
282
+ import logging
283
+ logger = logging.getLogger(__name__)
284
+
285
+ # Check if we should use auto-generation
286
+ use_autogen = os.environ.get('DJANGO_CFG_AUTOGEN_DOCS', 'true').lower() == 'true'
287
+
288
+ readme_content = ""
289
+ documentation_html = ""
290
+ readme_path = None
291
+ readme_exists = False
292
+
293
+ if use_autogen:
294
+ # Auto-generate documentation from command docstrings
295
+ logger.info("Auto-generating documentation from command docstrings")
296
+ try:
297
+ readme_content = self.generate_documentation_from_commands()
298
+
299
+ # Convert to HTML
300
+ try:
301
+ import markdown
302
+ documentation_html = markdown.markdown(
303
+ readme_content,
304
+ extensions=[
305
+ 'markdown.extensions.fenced_code',
306
+ 'markdown.extensions.tables',
307
+ 'markdown.extensions.toc',
308
+ 'markdown.extensions.nl2br',
309
+ 'markdown.extensions.sane_lists',
310
+ ]
311
+ )
312
+ logger.info("Auto-generated documentation rendered successfully")
313
+ except ImportError:
314
+ logger.warning("Markdown library not available, will display as plain text")
315
+
316
+ readme_exists = True
317
+ readme_path = "Auto-generated from docstrings"
318
+
319
+ except Exception as e:
320
+ logger.error(f"Error auto-generating documentation: {e}", exc_info=True)
321
+ readme_content = f"Error auto-generating documentation: {str(e)}"
322
+ else:
323
+ # Use static README.md file
324
+ # Try multiple path resolution strategies
325
+ possible_paths = [
326
+ # Strategy 1: Relative to this file
327
+ Path(__file__).parent.parent.parent / 'management' / 'README.md',
328
+ # Strategy 2: Using django_cfg module location
329
+ Path(__file__).parent.parent.parent / 'management' / 'README.md',
330
+ # Strategy 3: Absolute path for django_cfg package
331
+ Path('/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_cfg/management/README.md'),
332
+ ]
333
+
334
+ # Find the first existing path
335
+ for path in possible_paths:
336
+ if path.exists():
337
+ readme_path = path
338
+ logger.info(f"Found README.md at: {readme_path}")
339
+ break
340
+
341
+ # If still not found, try using django_cfg module import
342
+ if not readme_path or not readme_path.exists():
343
+ try:
344
+ import django_cfg
345
+ django_cfg_path = Path(django_cfg.__file__).parent
346
+ readme_path = django_cfg_path / 'management' / 'README.md'
347
+ logger.info(f"Using django_cfg module path: {readme_path}")
348
+ except Exception as e:
349
+ logger.error(f"Error finding django_cfg module: {e}")
350
+ readme_path = possible_paths[0] # Fallback to first path
351
+
352
+ try:
353
+ if readme_path and readme_path.exists():
354
+ logger.info(f"Reading README from: {readme_path}")
355
+ with open(readme_path, 'r', encoding='utf-8') as f:
356
+ readme_content = f.read()
357
+ logger.info(f"README content loaded: {len(readme_content)} characters")
358
+
359
+ # Try to convert markdown to HTML
360
+ try:
361
+ import markdown
362
+ documentation_html = markdown.markdown(
363
+ readme_content,
364
+ extensions=[
365
+ 'markdown.extensions.fenced_code',
366
+ 'markdown.extensions.tables',
367
+ 'markdown.extensions.toc',
368
+ 'markdown.extensions.nl2br',
369
+ 'markdown.extensions.sane_lists',
370
+ ]
371
+ )
372
+ logger.info("Markdown rendered successfully")
373
+ except ImportError:
374
+ logger.warning("Markdown library not available, will display as plain text")
375
+ pass
376
+
377
+ readme_exists = True
378
+ else:
379
+ logger.error(f"README.md not found at: {readme_path}")
380
+ readme_content = f"README.md not found. Searched paths:\n" + "\n".join(str(p) for p in possible_paths)
381
+
382
+ except Exception as e:
383
+ logger.error(f"Error loading documentation: {e}", exc_info=True)
384
+ readme_content = f"Error loading documentation: {str(e)}"
385
+
386
+ return {
387
+ 'readme_content': readme_content,
388
+ 'documentation_content': documentation_html if documentation_html else None,
389
+ 'readme_path': str(readme_path) if readme_path else 'Not found',
390
+ 'readme_exists': readme_exists,
391
+ }
File without changes
@@ -0,0 +1,93 @@
1
+ """
2
+ Test Email Command
3
+
4
+ Tests email sending functionality using django_cfg configuration.
5
+ """
6
+
7
+ from django.core.management.base import BaseCommand
8
+ from django.contrib.auth import get_user_model
9
+ from django_cfg.modules.django_logging import get_logger
10
+
11
+ User = get_user_model()
12
+ logger = get_logger('test_email')
13
+
14
+
15
+ class Command(BaseCommand):
16
+ """Command to test email functionality."""
17
+
18
+ # Web execution metadata
19
+ web_executable = True
20
+ requires_input = False
21
+ is_destructive = False
22
+
23
+ help = "Test email sending functionality"
24
+
25
+ def add_arguments(self, parser):
26
+ parser.add_argument(
27
+ "--email",
28
+ type=str,
29
+ help="Email address to send test message to",
30
+ default="markolofsen@gmail.com",
31
+ )
32
+ parser.add_argument(
33
+ "--subject",
34
+ type=str,
35
+ help="Email subject",
36
+ default="Test Email from UnrealON",
37
+ )
38
+ parser.add_argument(
39
+ "--message",
40
+ type=str,
41
+ help="Email message",
42
+ default="This is a test email from UnrealON system.",
43
+ )
44
+
45
+ def handle(self, *args, **options):
46
+ email = options["email"]
47
+ subject = options["subject"]
48
+ message = options["message"]
49
+
50
+ logger.info(f"Starting email test for {email}")
51
+ self.stdout.write(f"🚀 Testing email service for {email}")
52
+
53
+ # Create test user if not exists
54
+ user, created = User.objects.get_or_create(
55
+ email=email, defaults={"username": email.split("@")[0], "is_active": True}
56
+ )
57
+ if created:
58
+ self.stdout.write(f"✨ Created test user: {user.username}")
59
+
60
+ # Get email service from django-cfg (автоматически настроен!)
61
+ try:
62
+ from django_cfg.modules.django_email import DjangoEmailService
63
+ email_service = DjangoEmailService()
64
+
65
+ # Показать информацию о backend
66
+ backend_info = email_service.get_backend_info()
67
+ self.stdout.write(f"\n📧 Backend: {backend_info['backend']}")
68
+ self.stdout.write(f"📧 Configured: {backend_info['configured']}")
69
+
70
+ self.stdout.write("\n📧 Sending test email with HTML template...")
71
+
72
+ # Отправить письмо с HTML шаблоном
73
+ result = email_service.send_template(
74
+ subject=subject,
75
+ template_name="emails/base_email",
76
+ context={
77
+ 'email_title': subject,
78
+ 'greeting': 'Hello',
79
+ 'main_text': message,
80
+ 'project_name': 'Django CFG Sample',
81
+ 'site_url': 'http://localhost:8000',
82
+ 'logo_url': 'https://djangocfg.com/favicon.png',
83
+ 'button_text': 'Visit Website',
84
+ 'button_url': 'http://localhost:8000',
85
+ 'secondary_text': 'This is a test email sent from django-cfg management command.',
86
+ },
87
+ recipient_list=[email]
88
+ )
89
+
90
+ self.stdout.write(self.style.SUCCESS(f"✅ Email sent successfully! Result: {result}"))
91
+
92
+ except Exception as e:
93
+ self.stdout.write(self.style.ERROR(f"❌ Failed to send email: {e}"))
@@ -247,7 +247,7 @@ config = get_current_config()
247
247
 
248
248
  if config and config.debug:
249
249
  # Expensive operation only in debug mode
250
- from django_cfg.dashboard.debug import save_section_render
250
+ from django_cfg.modules.django_dashboard.debug import save_section_render
251
251
  save_section_render('overview', html_content)
252
252
  else:
253
253
  # Skip debug rendering in production
@@ -40,9 +40,9 @@ class DjangoLogger(BaseCfgModule):
40
40
  logs_dir.mkdir(parents=True, exist_ok=True)
41
41
  djangocfg_logs_dir.mkdir(parents=True, exist_ok=True)
42
42
 
43
- print(f"[django-cfg] Setting up modular logging:")
44
- print(f" Django logs: {logs_dir / 'django.log'}")
45
- print(f" Django-CFG logs: {djangocfg_logs_dir}/")
43
+ # print(f"[django-cfg] Setting up modular logging:")
44
+ # print(f" Django logs: {logs_dir / 'django.log'}")
45
+ # print(f" Django-CFG logs: {djangocfg_logs_dir}/")
46
46
 
47
47
  # Get debug mode
48
48
  try:
@@ -79,7 +79,7 @@ class DjangoLogger(BaseCfgModule):
79
79
  root_logger.addHandler(console_handler)
80
80
  root_logger.addHandler(django_handler) # All logs go to django.log
81
81
 
82
- print(f"[django-cfg] Modular logging configured successfully! Debug: {debug}")
82
+ # print(f"[django-cfg] Modular logging configured successfully! Debug: {debug}")
83
83
  cls._configured = True
84
84
 
85
85
  except Exception as e:
@@ -139,7 +139,7 @@ class DjangoLogger(BaseCfgModule):
139
139
  logger.addHandler(file_handler)
140
140
  logger.propagate = True # Also send to parent (django.log)
141
141
 
142
- print(f"[django-cfg] Created modular logger: {name} -> {log_file_path}")
142
+ # print(f"[django-cfg] Created modular logger: {name} -> {log_file_path}")
143
143
 
144
144
  except Exception as e:
145
145
  print(f"[django-cfg] ERROR creating modular logger for {name}: {e}")
@@ -189,7 +189,7 @@ def get_logger(name: str = "django_cfg") -> logging.Logger:
189
189
 
190
190
  if clean_parts:
191
191
  auto_name = f"django_cfg.{'.'.join(clean_parts)}"
192
- print(f"[django-cfg] Auto-detected logger name: {name} -> {auto_name}")
192
+ # print(f"[django-cfg] Auto-detected logger name: {name} -> {auto_name}")
193
193
  name = auto_name
194
194
 
195
195
  elif module_path.startswith('modules/'):
File without changes