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,192 @@
1
+ """
2
+ Auto-fix enum naming collisions in OpenAPI schema.
3
+
4
+ This postprocessing hook automatically generates unique, descriptive enum names
5
+ based on model names to avoid collisions like "Status50eEnum", "StatusA98Enum".
6
+
7
+ Instead generates: "ProductStatusEnum", "OrderStatusEnum", "PostStatusEnum", etc.
8
+ """
9
+
10
+ import logging
11
+ from typing import Dict, Any, Optional
12
+ import re
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ def auto_fix_enum_names(result: Dict[str, Any], generator, request, public) -> Dict[str, Any]:
18
+ """
19
+ DRF Spectacular postprocessing hook to auto-fix enum naming collisions.
20
+
21
+ Automatically detects and fixes enum naming collisions by using model names.
22
+
23
+ Args:
24
+ result: OpenAPI schema dict
25
+ generator: Schema generator instance
26
+ request: HTTP request
27
+ public: Whether schema is public
28
+
29
+ Returns:
30
+ Modified OpenAPI schema with fixed enum names
31
+
32
+ Example:
33
+ Before: Status50eEnum, StatusA98Enum (collision hashes)
34
+ After: ProductStatusEnum, OrderStatusEnum (descriptive names)
35
+ """
36
+
37
+ if 'components' not in result or 'schemas' not in result['components']:
38
+ return result
39
+
40
+ schemas = result['components']['schemas']
41
+
42
+ # Track enum references and their sources (model + field)
43
+ enum_sources: Dict[str, list] = {} # enum_name -> [(model_name, field_name, choices)]
44
+ enum_renames: Dict[str, str] = {} # old_name -> new_name
45
+
46
+ # Step 1: Find all enums and their sources
47
+ for schema_name, schema in schemas.items():
48
+ if schema.get('type') == 'object' and 'properties' in schema:
49
+ # This is a model schema
50
+ model_name = _extract_model_name(schema_name)
51
+
52
+ for field_name, field_schema in schema['properties'].items():
53
+ # Check if field references an enum
54
+ if '$ref' in field_schema:
55
+ enum_ref = field_schema['$ref']
56
+ if '#/components/schemas/' in enum_ref:
57
+ enum_name = enum_ref.split('/')[-1]
58
+
59
+ # Track enum source
60
+ if enum_name not in enum_sources:
61
+ enum_sources[enum_name] = []
62
+
63
+ enum_sources[enum_name].append((model_name, field_name))
64
+
65
+ # Step 2: Detect collisions and generate better names
66
+ for enum_name, sources in enum_sources.items():
67
+ # Check if enum looks like a collision (contains hash or generic name)
68
+ if _is_collision_enum(enum_name):
69
+ # Multiple models use this enum - need unique names
70
+ if len(sources) == 1:
71
+ # Single source - generate descriptive name
72
+ model_name, field_name = sources[0]
73
+ new_name = _generate_enum_name(model_name, field_name)
74
+ enum_renames[enum_name] = new_name
75
+
76
+ logger.debug(f" Renaming {enum_name} -> {new_name} (from {model_name}.{field_name})")
77
+
78
+ # Step 3: Apply renames to schema
79
+ if enum_renames:
80
+ logger.info(f"🔧 Auto-fixed {len(enum_renames)} enum naming collision(s)")
81
+ _apply_enum_renames(result, enum_renames)
82
+
83
+ return result
84
+
85
+
86
+ def _extract_model_name(schema_name: str) -> str:
87
+ """
88
+ Extract model name from schema name.
89
+
90
+ Examples:
91
+ "Product" -> "Product"
92
+ "ProductDetail" -> "Product"
93
+ "PaginatedProductList" -> "Product"
94
+ """
95
+ # Remove common prefixes/suffixes
96
+ name = schema_name
97
+
98
+ # Remove pagination wrapper
99
+ if name.startswith('Paginated') and name.endswith('List'):
100
+ name = name[9:-4] # Remove "Paginated" and "List"
101
+
102
+ # Remove common suffixes
103
+ for suffix in ['Serializer', 'Detail', 'List', 'Create', 'Update']:
104
+ if name.endswith(suffix):
105
+ name = name[:-len(suffix)]
106
+ break
107
+
108
+ return name
109
+
110
+
111
+ def _is_collision_enum(enum_name: str) -> bool:
112
+ """
113
+ Check if enum name looks like a collision (contains hash).
114
+
115
+ Examples:
116
+ "Status50eEnum" -> True (has hash)
117
+ "StatusA98Enum" -> True (has hash)
118
+ "ProductStatusEnum" -> False (descriptive)
119
+ """
120
+ # Check if enum contains hash-like patterns (3+ hex chars)
121
+ if re.search(r'[0-9A-Fa-f]{3,}Enum$', enum_name):
122
+ return True
123
+
124
+ # Check for generic single-word enums that are likely collisions
125
+ # (e.g., "StatusEnum" without model prefix)
126
+ if re.match(r'^[A-Z][a-z]+Enum$', enum_name):
127
+ # Single word + Enum - likely collision
128
+ return True
129
+
130
+ return False
131
+
132
+
133
+ def _generate_enum_name(model_name: str, field_name: str) -> str:
134
+ """
135
+ Generate descriptive enum name from model and field.
136
+
137
+ Examples:
138
+ ("Product", "status") -> "ProductStatusEnum"
139
+ ("Order", "status") -> "OrderStatusEnum"
140
+ ("Post", "status") -> "PostStatusEnum"
141
+ """
142
+ # Capitalize field name
143
+ field_capitalized = field_name.capitalize()
144
+
145
+ # Combine: ModelName + FieldName + Enum
146
+ return f"{model_name}{field_capitalized}Enum"
147
+
148
+
149
+ def _apply_enum_renames(schema: Dict[str, Any], renames: Dict[str, str]) -> None:
150
+ """
151
+ Apply enum renames throughout the schema.
152
+
153
+ Renames both:
154
+ 1. Schema component definitions (components/schemas/OldName -> NewName)
155
+ 2. All references to renamed enums ($ref: #/components/schemas/OldName)
156
+ """
157
+ if 'components' not in schema or 'schemas' not in schema['components']:
158
+ return
159
+
160
+ schemas = schema['components']['schemas']
161
+
162
+ # Step 1: Rename schema definitions
163
+ for old_name, new_name in renames.items():
164
+ if old_name in schemas:
165
+ schemas[new_name] = schemas.pop(old_name)
166
+ logger.debug(f" Renamed schema: {old_name} -> {new_name}")
167
+
168
+ # Step 2: Update all $ref references
169
+ _update_refs_recursive(schema, renames)
170
+
171
+
172
+ def _update_refs_recursive(obj: Any, renames: Dict[str, str]) -> None:
173
+ """
174
+ Recursively update all $ref references in schema.
175
+ """
176
+ if isinstance(obj, dict):
177
+ # Check if this dict contains a $ref
178
+ if '$ref' in obj:
179
+ ref = obj['$ref']
180
+ if '#/components/schemas/' in ref:
181
+ enum_name = ref.split('/')[-1]
182
+ if enum_name in renames:
183
+ obj['$ref'] = f"#/components/schemas/{renames[enum_name]}"
184
+
185
+ # Recurse into dict values
186
+ for value in obj.values():
187
+ _update_refs_recursive(value, renames)
188
+
189
+ elif isinstance(obj, list):
190
+ # Recurse into list items
191
+ for item in obj:
192
+ _update_refs_recursive(item, renames)
@@ -0,0 +1,72 @@
1
+ """
2
+ Django URL integration.
3
+
4
+ Provides URL patterns for OpenAPI schema generation.
5
+ Each configured group gets its own schema endpoint.
6
+ """
7
+
8
+ from typing import List, Any
9
+
10
+
11
+ def _is_django_configured() -> bool:
12
+ """Check if Django settings are configured."""
13
+ try:
14
+ from django.conf import settings
15
+ return settings.configured
16
+ except ImportError:
17
+ return False
18
+
19
+
20
+ def get_openapi_urls() -> List[Any]:
21
+ """
22
+ Get URL patterns for OpenAPI schema generation.
23
+
24
+ Creates URLs for each configured group:
25
+ - /openapi/{group_name}/schema/ - JSON schema
26
+
27
+ Returns:
28
+ List of Django URL patterns
29
+ """
30
+ try:
31
+ from django.urls import path
32
+ from drf_spectacular.views import SpectacularAPIView
33
+ from django_cfg.modules.django_client.core import get_openapi_service
34
+ except ImportError:
35
+ return []
36
+
37
+ service = get_openapi_service()
38
+
39
+ if not service.config or not service.is_enabled():
40
+ return []
41
+
42
+ patterns = []
43
+
44
+ for group_name in service.get_group_names():
45
+ group_config = service.get_group(group_name)
46
+ if not group_config:
47
+ continue
48
+
49
+ # Schema endpoint for each group
50
+ patterns.append(
51
+ path(
52
+ f'{group_name}/schema/',
53
+ SpectacularAPIView.as_view(
54
+ urlconf=f'openapi_group_{group_name}',
55
+ api_version=group_config.version,
56
+ ),
57
+ name=f'openapi-schema-{group_name}',
58
+ )
59
+ )
60
+
61
+ return patterns
62
+
63
+
64
+ # Export urlpatterns for django.urls.include()
65
+ # Only create urlpatterns if Django is configured
66
+ if _is_django_configured():
67
+ urlpatterns = get_openapi_urls()
68
+ else:
69
+ urlpatterns = []
70
+
71
+
72
+ __all__ = ["get_openapi_urls", "urlpatterns"]
@@ -37,7 +37,7 @@ libs/django_cfg/src/django_cfg/debug/dashboard/
37
37
  ### 3. Программное использование
38
38
 
39
39
  ```python
40
- from django_cfg.dashboard.debug import save_section_render
40
+ from django_cfg.modules.django_dashboard.debug import save_section_render
41
41
 
42
42
  # В коде секции
43
43
  html = section.render()
@@ -62,7 +62,7 @@ dashboard/
62
62
  ## Сравнение с архивом
63
63
 
64
64
  ```python
65
- from django_cfg.dashboard.debug import get_debugger
65
+ from django_cfg.modules.django_dashboard.debug import get_debugger
66
66
  from pathlib import Path
67
67
 
68
68
  debugger = get_debugger()
@@ -65,7 +65,7 @@ python manage.py debug_dashboard
65
65
  ### 2. Автоматическое сохранение рендеров ✅
66
66
  При каждом рендере секции автоматически сохраняются через:
67
67
  ```python
68
- from django_cfg.dashboard.debug import save_section_render
68
+ from django_cfg.modules.django_dashboard.debug import save_section_render
69
69
 
70
70
  save_section_render('overview', html)
71
71
  ```
@@ -8,11 +8,11 @@ from django.core.management.base import BaseCommand
8
8
  from django.test import RequestFactory
9
9
  from django.contrib.auth import get_user_model
10
10
 
11
- from django_cfg.dashboard.sections.overview import OverviewSection
12
- from django_cfg.dashboard.sections.stats import StatsSection
13
- from django_cfg.dashboard.sections.system import SystemSection
14
- from django_cfg.dashboard.sections.commands import CommandsSection
15
- from django_cfg.dashboard.debug import get_debugger
11
+ from django_cfg.modules.django_dashboard.sections.overview import OverviewSection
12
+ from django_cfg.modules.django_dashboard.sections.stats import StatsSection
13
+ from django_cfg.modules.django_dashboard.sections.system import SystemSection
14
+ from django_cfg.modules.django_dashboard.sections.commands import CommandsSection
15
+ from django_cfg.modules.django_dashboard.debug import get_debugger
16
16
 
17
17
 
18
18
  class Command(BaseCommand):