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,225 @@
1
+ """
2
+ Django management command to show current configuration.
3
+
4
+ Usage:
5
+ python manage.py show_config
6
+ python manage.py show_config --format json
7
+ """
8
+
9
+ import json
10
+ import os
11
+ from django.core.management.base import BaseCommand
12
+ from django.conf import settings
13
+ from django_cfg.modules.django_logging import get_logger
14
+
15
+ logger = get_logger('show_config')
16
+
17
+
18
+ class Command(BaseCommand):
19
+ # Web execution metadata
20
+ web_executable = True
21
+ requires_input = False
22
+ is_destructive = False
23
+
24
+ help = 'Show Django Config configuration'
25
+
26
+ def add_arguments(self, parser):
27
+ parser.add_argument(
28
+ '--format',
29
+ choices=['table', 'json'],
30
+ default='table',
31
+ help='Output format (default: table)',
32
+ )
33
+ parser.add_argument(
34
+ '--include-secrets',
35
+ action='store_true',
36
+ help='Include sensitive information (use carefully)',
37
+ )
38
+
39
+ def handle(self, *args, **options):
40
+ """Show configuration in requested format."""
41
+ logger.info("Starting show_config command")
42
+ try:
43
+ # Get the config instance from Django settings
44
+ config = self._get_config_instance()
45
+ logger.info("Successfully retrieved configuration instance")
46
+
47
+ if options['format'] == 'json':
48
+ logger.info("Displaying configuration in JSON format")
49
+ self._show_json_format(config, options['include_secrets'])
50
+ else:
51
+ logger.info("Displaying configuration in table format")
52
+ self._show_table_format(config, options['include_secrets'])
53
+
54
+ logger.info("show_config command completed successfully")
55
+ except Exception as e:
56
+ error_msg = f'Failed to show configuration: {e}'
57
+ logger.error(error_msg, exc_info=True)
58
+ self.stdout.write(
59
+ self.style.ERROR(f'āŒ {error_msg}')
60
+ )
61
+
62
+ def _get_config_instance(self):
63
+ """Get the DjangoConfig instance from Django settings."""
64
+ # Try to get config from settings module
65
+ settings_module = os.environ.get('DJANGO_SETTINGS_MODULE')
66
+ if not settings_module:
67
+ raise ValueError("DJANGO_SETTINGS_MODULE environment variable not set")
68
+
69
+ # Import the settings module and get the config
70
+ import importlib
71
+ settings_mod = importlib.import_module(settings_module)
72
+
73
+ if hasattr(settings_mod, 'config'):
74
+ return settings_mod.config
75
+ else:
76
+ # Fallback: create a minimal config from Django settings
77
+ from django_cfg import DjangoConfig
78
+ return DjangoConfig(
79
+ project_name=getattr(settings, 'PROJECT_NAME', 'Django Project'),
80
+ secret_key=settings.SECRET_KEY,
81
+ debug=settings.DEBUG,
82
+ allowed_hosts=settings.ALLOWED_HOSTS,
83
+ )
84
+
85
+ def _show_table_format(self, config, include_secrets=False):
86
+ """Show configuration in table format."""
87
+ self.stdout.write(
88
+ self.style.HTTP_INFO('šŸš€ Django Config - Current Configuration')
89
+ )
90
+ self.stdout.write('=' * 80)
91
+
92
+ # Project section
93
+ self.stdout.write(self.style.SUCCESS('\nšŸ“‹ Project'))
94
+ self.stdout.write('-' * 40)
95
+ project_data = [
96
+ ('Name', config.project_name),
97
+ ('Version', getattr(config, 'project_version', 'N/A')),
98
+ ('Description', getattr(config, 'project_description', 'N/A')),
99
+ ]
100
+
101
+ for key, value in project_data:
102
+ self.stdout.write(f' {key:<20}: {value}')
103
+
104
+ # Environment section
105
+ self.stdout.write(self.style.SUCCESS('\nšŸŒ Environment'))
106
+ self.stdout.write('-' * 40)
107
+ env_data = [
108
+ ('Environment', getattr(config, 'env_mode', 'auto-detected')),
109
+ ('Debug Mode', config.debug),
110
+ ('Security Domains', ', '.join(config.security_domains) if config.security_domains else 'None'),
111
+ ]
112
+
113
+ if include_secrets:
114
+ env_data.append(('Secret Key', config.secret_key[:20] + '...'))
115
+ else:
116
+ env_data.append(('Secret Key', '[HIDDEN]'))
117
+
118
+ for key, value in env_data:
119
+ self.stdout.write(f' {key:<20}: {value}')
120
+
121
+ # Database section
122
+ self.stdout.write(self.style.SUCCESS('\nšŸ—„ļø Databases'))
123
+ self.stdout.write('-' * 40)
124
+
125
+ for db_name, db_config in config.databases.items():
126
+ self.stdout.write(f' {db_name}:')
127
+ self.stdout.write(f' Engine: {db_config.engine}')
128
+ if include_secrets:
129
+ self.stdout.write(f' Name: {db_config.name}')
130
+ self.stdout.write(f' Host: {db_config.host}')
131
+ self.stdout.write(f' Port: {db_config.port}')
132
+ else:
133
+ self.stdout.write(f' Name: [HIDDEN]')
134
+ self.stdout.write(f' Host: [HIDDEN]')
135
+ self.stdout.write(f' Port: [HIDDEN]')
136
+
137
+ # Cache section
138
+ cache_configured = False
139
+ if hasattr(config, 'cache_default') and config.cache_default:
140
+ cache_configured = True
141
+
142
+ if cache_configured:
143
+ self.stdout.write(self.style.SUCCESS('\n⚔ Cache'))
144
+ self.stdout.write('-' * 40)
145
+ cache_data = [
146
+ ('Default Cache', 'Configured'),
147
+ ('Sessions Cache', 'Configured' if hasattr(config, 'cache_sessions') and config.cache_sessions else 'Not configured'),
148
+ ]
149
+
150
+ for key, value in cache_data:
151
+ self.stdout.write(f' {key:<20}: {value}')
152
+
153
+ # Services section
154
+ services_configured = []
155
+ if hasattr(config, 'email') and config.email:
156
+ services_configured.append(('Email', 'Configured'))
157
+ if hasattr(config, 'telegram') and config.telegram:
158
+ services_configured.append(('Telegram', 'Configured'))
159
+
160
+ if services_configured:
161
+ self.stdout.write(self.style.SUCCESS('\nšŸ”§ Services'))
162
+ self.stdout.write('-' * 40)
163
+
164
+ for key, value in services_configured:
165
+ self.stdout.write(f' {key:<20}: {value}')
166
+
167
+ # Apps section
168
+ if hasattr(config, 'project_apps') and config.project_apps:
169
+ self.stdout.write(self.style.SUCCESS('\nšŸ“¦ Project Apps'))
170
+ self.stdout.write('-' * 40)
171
+
172
+ for app in config.project_apps:
173
+ self.stdout.write(f' • {app}')
174
+
175
+ # Custom Middleware section
176
+ if hasattr(config, 'custom_middleware') and config.custom_middleware:
177
+ self.stdout.write(self.style.SUCCESS('\nšŸ›”ļø Custom Middleware'))
178
+ self.stdout.write('-' * 40)
179
+
180
+ for middleware in config.custom_middleware:
181
+ self.stdout.write(f' • {middleware}')
182
+
183
+ self.stdout.write('\n' + '=' * 80)
184
+
185
+ def _show_json_format(self, config, include_secrets=False):
186
+ """Show configuration in JSON format."""
187
+ config_data = {
188
+ 'project': {
189
+ 'name': config.project_name,
190
+ 'version': getattr(config, 'project_version', None),
191
+ 'description': getattr(config, 'project_description', None),
192
+ },
193
+ 'environment': {
194
+ 'environment': getattr(config, 'env_mode', 'auto-detected'),
195
+ 'debug': config.debug,
196
+ 'allowed_hosts': config.allowed_hosts,
197
+ },
198
+ 'databases': {},
199
+ 'cache': {
200
+ 'default_configured': hasattr(config, 'cache_default') and config.cache_default is not None,
201
+ 'sessions_configured': hasattr(config, 'cache_sessions') and config.cache_sessions is not None,
202
+ },
203
+ 'services': {
204
+ 'email_configured': hasattr(config, 'email') and config.email is not None,
205
+ 'telegram_configured': hasattr(config, 'telegram') and config.telegram is not None,
206
+ },
207
+ 'apps': {
208
+ 'project_apps': getattr(config, 'project_apps', []),
209
+ 'custom_middleware': getattr(config, 'custom_middleware', []),
210
+ }
211
+ }
212
+
213
+ # Add database info
214
+ for db_name, db_config in config.databases.items():
215
+ config_data['databases'][db_name] = {
216
+ 'engine': db_config.engine,
217
+ 'name': db_config.name if include_secrets else '[HIDDEN]',
218
+ 'host': db_config.host if include_secrets else '[HIDDEN]',
219
+ 'port': db_config.port if include_secrets else '[HIDDEN]',
220
+ }
221
+
222
+ if include_secrets:
223
+ config_data['environment']['secret_key'] = config.secret_key
224
+
225
+ self.stdout.write(json.dumps(config_data, indent=2, default=str))
@@ -0,0 +1,361 @@
1
+ """
2
+ Django management command to display all URL patterns in the project.
3
+ """
4
+
5
+ import re
6
+ from typing import List, Tuple, Optional
7
+ from django.core.management.base import BaseCommand, CommandParser
8
+ from django.urls import get_resolver
9
+ from django.conf import settings
10
+ from django.utils.termcolors import make_style
11
+ from django_cfg.modules.django_logging import get_logger
12
+
13
+ logger = get_logger('show_urls')
14
+
15
+
16
+ class Command(BaseCommand):
17
+ """
18
+ Display all URL patterns in the Django project.
19
+
20
+ This command recursively walks through all URL patterns and displays them
21
+ in a hierarchical format with colors and filtering options.
22
+ """
23
+
24
+ # Web execution metadata
25
+ web_executable = True
26
+ requires_input = False
27
+ is_destructive = False
28
+
29
+ help = 'Display all URL patterns in the project'
30
+
31
+ def __init__(self, *args, **kwargs):
32
+ super().__init__(*args, **kwargs)
33
+
34
+ # Define color styles
35
+ self.styles = {
36
+ 'SUCCESS': make_style(opts=('bold',), fg='green'),
37
+ 'WARNING': make_style(opts=('bold',), fg='yellow'),
38
+ 'ERROR': make_style(opts=('bold',), fg='red'),
39
+ 'HTTP_200': make_style(fg='green'),
40
+ 'HTTP_400': make_style(fg='yellow'),
41
+ 'HTTP_500': make_style(fg='red'),
42
+ 'URL': make_style(fg='cyan'),
43
+ 'NAME': make_style(fg='blue'),
44
+ 'INCLUDE': make_style(opts=('bold',), fg='magenta'),
45
+ 'NAMESPACE': make_style(opts=('bold',), fg='yellow'),
46
+ }
47
+
48
+ def add_arguments(self, parser: CommandParser) -> None:
49
+ """Add command arguments."""
50
+ parser.add_argument(
51
+ '--format',
52
+ choices=['list', 'tree', 'table'],
53
+ default='tree',
54
+ help='Output format (default: tree)'
55
+ )
56
+ parser.add_argument(
57
+ '--filter',
58
+ type=str,
59
+ help='Filter URLs by pattern (regex supported)'
60
+ )
61
+ parser.add_argument(
62
+ '--namespace',
63
+ type=str,
64
+ help='Show URLs only from specific namespace'
65
+ )
66
+ parser.add_argument(
67
+ '--plain',
68
+ action='store_true',
69
+ help='Plain output without colors'
70
+ )
71
+ parser.add_argument(
72
+ '--include-unnamed',
73
+ action='store_true',
74
+ help='Include URLs without names'
75
+ )
76
+ parser.add_argument(
77
+ '--show-methods',
78
+ action='store_true',
79
+ help='Show HTTP methods for each URL'
80
+ )
81
+ parser.add_argument(
82
+ '--show-views',
83
+ action='store_true',
84
+ help='Show view functions/classes'
85
+ )
86
+
87
+ def handle(self, *args, **options) -> None:
88
+ """Main command handler."""
89
+ logger.info("Starting show_urls command")
90
+ self.options = options
91
+
92
+ # Disable colors if requested
93
+ if options['plain'] or options.get('no_color', False):
94
+ for key in self.styles:
95
+ self.styles[key] = lambda x: x
96
+
97
+ self.stdout.write(
98
+ self.styles['SUCCESS']('🌐 Django URL Patterns')
99
+ )
100
+ self.stdout.write('=' * 50)
101
+
102
+ # Show OpenAPI Client info if available
103
+ if hasattr(settings, 'OPENAPI_CLIENT'):
104
+ self._show_openapi_client_info()
105
+
106
+ # Get and display URL patterns
107
+ resolver = get_resolver()
108
+ patterns = self._collect_patterns(resolver.url_patterns)
109
+
110
+ # Filter patterns if requested
111
+ if options['filter']:
112
+ patterns = self._filter_patterns(patterns, options['filter'])
113
+
114
+ if options['namespace']:
115
+ patterns = self._filter_by_namespace(patterns, options['namespace'])
116
+
117
+ # Display patterns in requested format
118
+ if options['format'] == 'list':
119
+ self._display_list(patterns)
120
+ elif options['format'] == 'table':
121
+ self._display_table(patterns)
122
+ else:
123
+ self._display_tree(patterns)
124
+
125
+ self.stdout.write(f"\nšŸ“Š Total URLs: {len(patterns)}")
126
+
127
+ def _show_openapi_client_info(self) -> None:
128
+ """Display OpenAPI Client configuration info."""
129
+ openapi_client = settings.OPENAPI_CLIENT
130
+
131
+ self.stdout.write(
132
+ self.styles['NAMESPACE']('\nšŸ“‹ OpenAPI Client Configuration:')
133
+ )
134
+
135
+ # Handle both dict and Pydantic model
136
+ if isinstance(openapi_client, dict):
137
+ api_prefix = openapi_client.get('api_prefix', 'N/A')
138
+ enabled = openapi_client.get('enabled', False)
139
+ groups = openapi_client.get('groups', [])
140
+ else:
141
+ api_prefix = getattr(openapi_client, 'api_prefix', 'N/A')
142
+ enabled = getattr(openapi_client, 'enabled', False)
143
+ groups = getattr(openapi_client, 'groups', [])
144
+
145
+ self.stdout.write(f" API Prefix: {api_prefix}")
146
+ self.stdout.write(f" Enabled: {enabled}")
147
+
148
+ if groups:
149
+ group_names = [g.get('name') if isinstance(g, dict) else getattr(g, 'name', 'Unknown') for g in groups]
150
+ self.stdout.write(f" Groups: {', '.join(group_names)}")
151
+ self.stdout.write('')
152
+
153
+ def _collect_patterns(
154
+ self,
155
+ urlpatterns,
156
+ prefix: str = '',
157
+ namespace: str = ''
158
+ ) -> List[Tuple[str, str, str, str, Optional[str]]]:
159
+ """
160
+ Recursively collect all URL patterns.
161
+
162
+ Returns list of tuples: (pattern, name, namespace, view, methods)
163
+ """
164
+ patterns = []
165
+
166
+ for pattern in urlpatterns:
167
+ if hasattr(pattern, 'url_patterns'):
168
+ # This is an include() pattern
169
+ new_prefix = prefix + str(pattern.pattern)
170
+ new_namespace = namespace
171
+
172
+ if hasattr(pattern, 'namespace') and pattern.namespace:
173
+ new_namespace = (
174
+ f"{namespace}:{pattern.namespace}"
175
+ if namespace
176
+ else pattern.namespace
177
+ )
178
+
179
+ # Add the include pattern itself
180
+ patterns.append((
181
+ new_prefix,
182
+ f"[INCLUDE: {getattr(pattern, 'app_name', 'unknown')}]",
183
+ new_namespace,
184
+ 'include',
185
+ None
186
+ ))
187
+
188
+ # Recursively collect nested patterns
189
+ patterns.extend(
190
+ self._collect_patterns(
191
+ pattern.url_patterns,
192
+ new_prefix,
193
+ new_namespace
194
+ )
195
+ )
196
+ else:
197
+ # Regular URL pattern
198
+ full_pattern = prefix + str(pattern.pattern)
199
+ name = getattr(pattern, 'name', None)
200
+ view_name = self._get_view_name(pattern)
201
+ methods = self._get_http_methods(pattern)
202
+
203
+ patterns.append((
204
+ full_pattern,
205
+ name or '[unnamed]',
206
+ namespace,
207
+ view_name,
208
+ methods
209
+ ))
210
+
211
+ return patterns
212
+
213
+ def _get_view_name(self, pattern) -> str:
214
+ """Get the view function/class name from a URL pattern."""
215
+ try:
216
+ if hasattr(pattern, 'callback'):
217
+ callback = pattern.callback
218
+ if hasattr(callback, '__name__'):
219
+ return callback.__name__
220
+ elif hasattr(callback, '__class__'):
221
+ return callback.__class__.__name__
222
+ else:
223
+ return str(callback)
224
+ return 'unknown'
225
+ except Exception:
226
+ return 'unknown'
227
+
228
+ def _get_http_methods(self, pattern) -> Optional[str]:
229
+ """Get HTTP methods supported by a URL pattern."""
230
+ if not self.options['show_methods']:
231
+ return None
232
+
233
+ try:
234
+ # Try to get methods from the view
235
+ if hasattr(pattern, 'callback'):
236
+ callback = pattern.callback
237
+ if hasattr(callback, 'cls'):
238
+ # DRF ViewSet or APIView
239
+ view_class = callback.cls
240
+ if hasattr(view_class, 'http_method_names'):
241
+ return ', '.join(view_class.http_method_names).upper()
242
+ elif hasattr(callback, 'view_class'):
243
+ # Class-based view
244
+ view_class = callback.view_class
245
+ if hasattr(view_class, 'http_method_names'):
246
+ return ', '.join(view_class.http_method_names).upper()
247
+ return 'GET, POST, PUT, PATCH, DELETE' # Default assumption
248
+ except Exception:
249
+ return None
250
+
251
+ def _filter_patterns(
252
+ self,
253
+ patterns: List[Tuple],
254
+ filter_pattern: str
255
+ ) -> List[Tuple]:
256
+ """Filter patterns by regex."""
257
+ try:
258
+ regex = re.compile(filter_pattern, re.IGNORECASE)
259
+ return [
260
+ pattern for pattern in patterns
261
+ if regex.search(pattern[0]) or regex.search(pattern[1])
262
+ ]
263
+ except re.error:
264
+ self.stdout.write(
265
+ self.styles['ERROR'](f"Invalid regex pattern: {filter_pattern}")
266
+ )
267
+ return patterns
268
+
269
+ def _filter_by_namespace(
270
+ self,
271
+ patterns: List[Tuple],
272
+ namespace: str
273
+ ) -> List[Tuple]:
274
+ """Filter patterns by namespace."""
275
+ return [
276
+ pattern for pattern in patterns
277
+ if pattern[2] and namespace in pattern[2]
278
+ ]
279
+
280
+ def _display_tree(self, patterns: List[Tuple]) -> None:
281
+ """Display patterns in tree format."""
282
+ self.stdout.write(self.styles['SUCCESS']('\n🌳 URL Tree:'))
283
+ self.stdout.write('-' * 30)
284
+
285
+ for pattern, name, namespace, view, methods in patterns:
286
+ # Format the output
287
+ if '[INCLUDE:' in name:
288
+ # Include pattern
289
+ icon = 'šŸ“'
290
+ pattern_display = self.styles['INCLUDE'](pattern)
291
+ name_display = self.styles['NAMESPACE'](name)
292
+ elif name == '[unnamed]' and not self.options['include_unnamed']:
293
+ continue
294
+ else:
295
+ # Regular pattern
296
+ icon = 'šŸ”—'
297
+ pattern_display = self.styles['URL'](pattern)
298
+ name_display = self.styles['NAME'](name)
299
+
300
+ # Build the line
301
+ line = f"{icon} {pattern_display}"
302
+ if name and name != '[unnamed]':
303
+ line += f" -> {name_display}"
304
+
305
+ if namespace:
306
+ line += f" [{self.styles['NAMESPACE'](namespace)}]"
307
+
308
+ if self.options['show_views'] and view and view != 'include':
309
+ line += f" ({view})"
310
+
311
+ if methods:
312
+ line += f" [{methods}]"
313
+
314
+ self.stdout.write(line)
315
+
316
+ def _display_list(self, patterns: List[Tuple]) -> None:
317
+ """Display patterns in simple list format."""
318
+ self.stdout.write(self.styles['SUCCESS']('\nšŸ“‹ URL List:'))
319
+ self.stdout.write('-' * 20)
320
+
321
+ for i, (pattern, name, namespace, view, methods) in enumerate(patterns, 1):
322
+ if name == '[unnamed]' and not self.options['include_unnamed']:
323
+ continue
324
+
325
+ line = f"{i:3d}. {self.styles['URL'](pattern)}"
326
+ if name and name != '[unnamed]':
327
+ line += f" ({self.styles['NAME'](name)})"
328
+
329
+ self.stdout.write(line)
330
+
331
+ def _display_table(self, patterns: List[Tuple]) -> None:
332
+ """Display patterns in table format."""
333
+ self.stdout.write(self.styles['SUCCESS']('\nšŸ“Š URL Table:'))
334
+ self.stdout.write('-' * 80)
335
+
336
+ # Header
337
+ headers = ['Pattern', 'Name', 'Namespace', 'View']
338
+ if self.options['show_methods']:
339
+ headers.append('Methods')
340
+
341
+ header_line = ' | '.join(f"{h:<20}" for h in headers)
342
+ self.stdout.write(self.styles['SUCCESS'](header_line))
343
+ self.stdout.write('-' * len(header_line))
344
+
345
+ # Rows
346
+ for pattern, name, namespace, view, methods in patterns:
347
+ if name == '[unnamed]' and not self.options['include_unnamed']:
348
+ continue
349
+
350
+ row = [
351
+ pattern[:20],
352
+ (name or '')[:20],
353
+ (namespace or '')[:20],
354
+ (view or '')[:20]
355
+ ]
356
+
357
+ if self.options['show_methods']:
358
+ row.append((methods or '')[:20])
359
+
360
+ row_line = ' | '.join(f"{cell:<20}" for cell in row)
361
+ self.stdout.write(row_line)