django-cfg 1.4.13__py3-none-any.whl → 1.4.14__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.
django_cfg/apps/urls.py CHANGED
@@ -5,130 +5,142 @@ Built-in API endpoints for django_cfg functionality.
5
5
  """
6
6
 
7
7
  from django.urls import path, include
8
- from typing import List, Dict
9
- from django.urls import URLPattern
8
+ from typing import List
10
9
 
11
10
 
12
- def _register_group_urls(patterns: List[URLPattern], groups: Dict) -> None:
11
+ def get_enabled_cfg_apps() -> List[str]:
13
12
  """
14
- Auto-register URLs from OpenAPI groups using convention.
15
-
16
- Convention: cfg_{app} → /cfg/{app}/
17
-
18
- Args:
19
- patterns: URL patterns list to append to
20
- groups: OpenAPI groups dict
21
- """
22
- for group_name in groups.keys():
23
- # Only django-cfg apps (convention: cfg_*)
24
- if not group_name.startswith('cfg_'):
25
- continue
26
-
27
- # Extract app name: cfg_payments → payments
28
- app_name = group_name[4:]
29
-
30
- # Register main URLs: /cfg/{app}/
31
- try:
32
- patterns.append(
33
- path(f'cfg/{app_name}/', include(f'django_cfg.apps.{app_name}.urls'))
34
- )
35
- except ImportError:
36
- pass # URL module doesn't exist
37
-
38
- # Register admin URLs: /cfg/{app}/admin/ (if exists)
39
- try:
40
- patterns.append(
41
- path(f'cfg/{app_name}/admin/', include(f'django_cfg.apps.{app_name}.urls_admin'))
42
- )
43
- except ImportError:
44
- pass # Admin URL module doesn't exist
45
-
46
-
47
- def _register_apps_fallback(patterns: List[URLPattern]) -> None:
48
- """
49
- Fallback: Register apps when OpenAPI is disabled.
50
-
51
- Uses BaseCfgModule checks to determine which apps are enabled.
52
-
53
- Args:
54
- patterns: URL patterns list to append to
13
+ Get list of enabled django-cfg apps based on configuration.
14
+
15
+ Returns:
16
+ List of enabled app paths (e.g., ['django_cfg.apps.accounts', ...])
55
17
  """
56
18
  from django_cfg.modules.base import BaseCfgModule
19
+
57
20
  base_module = BaseCfgModule()
58
-
59
- # Business logic apps
60
- if base_module.is_support_enabled():
61
- patterns.append(path('cfg/support/', include('django_cfg.apps.support.urls')))
62
-
21
+ enabled_apps = []
22
+
63
23
  if base_module.is_accounts_enabled():
64
- patterns.append(path('cfg/accounts/', include('django_cfg.apps.accounts.urls')))
65
-
24
+ enabled_apps.append("django_cfg.apps.accounts")
25
+
26
+ if base_module.is_support_enabled():
27
+ enabled_apps.append("django_cfg.apps.support")
28
+
66
29
  if base_module.is_newsletter_enabled():
67
- patterns.append(path('cfg/newsletter/', include('django_cfg.apps.newsletter.urls')))
68
-
30
+ enabled_apps.append("django_cfg.apps.newsletter")
31
+
69
32
  if base_module.is_leads_enabled():
70
- patterns.append(path('cfg/leads/', include('django_cfg.apps.leads.urls')))
71
-
33
+ enabled_apps.append("django_cfg.apps.leads")
34
+
72
35
  if base_module.is_knowbase_enabled():
73
- patterns.append(path('cfg/knowbase/', include('django_cfg.apps.knowbase.urls')))
74
-
36
+ enabled_apps.append("django_cfg.apps.knowbase")
37
+
75
38
  if base_module.is_agents_enabled():
76
- patterns.append(path('cfg/agents/', include('django_cfg.apps.agents.urls')))
77
-
39
+ enabled_apps.append("django_cfg.apps.agents")
40
+
78
41
  if base_module.should_enable_tasks():
79
- patterns.append(path('cfg/tasks/', include('django_cfg.apps.tasks.urls')))
80
- patterns.append(path('cfg/tasks/admin/', include('django_cfg.apps.tasks.urls_admin')))
81
-
42
+ enabled_apps.append("django_cfg.apps.tasks")
43
+
82
44
  if base_module.is_payments_enabled():
83
- patterns.append(path('cfg/payments/', include('django_cfg.apps.payments.urls')))
84
- patterns.append(path('cfg/payments/admin/', include('django_cfg.apps.payments.urls_admin')))
85
-
86
- # Standalone apps
87
- if base_module.is_maintenance_enabled():
88
- patterns.append(
89
- path('admin/django_cfg_maintenance/', include('django_cfg.apps.maintenance.urls_admin'))
90
- )
91
-
92
- if base_module.is_rpc_enabled():
93
- patterns.append(path('rpc/', include('django_cfg.modules.django_ipc_client.dashboard.urls')))
94
- patterns.append(path('admin/rpc/', include('django_cfg.modules.django_ipc_client.dashboard.urls_admin')))
45
+ enabled_apps.append("django_cfg.apps.payments")
46
+
47
+ return enabled_apps
95
48
 
96
49
 
97
- def get_django_cfg_urlpatterns() -> List[URLPattern]:
50
+ def get_default_cfg_group():
98
51
  """
99
- Get Django CFG URL patterns based on OpenAPI groups.
100
-
52
+ Returns default OpenAPIGroupConfig for enabled django-cfg apps.
53
+
54
+ Only includes apps that are enabled in the current configuration.
55
+
56
+ This can be imported and added to your project's OpenAPIClientConfig groups:
57
+
58
+ ```python
59
+ from django_cfg.apps.urls import get_default_cfg_group
60
+
61
+ openapi_client = OpenAPIClientConfig(
62
+ groups=[
63
+ get_default_cfg_group(),
64
+ # ... your custom groups
65
+ ]
66
+ )
67
+ ```
68
+
101
69
  Returns:
102
- List of URL patterns for django_cfg
70
+ OpenAPIGroupConfig with enabled django-cfg apps
103
71
  """
104
- patterns = [
105
- # Core APIs (always enabled)
106
- path('health/', include('django_cfg.apps.api.health.urls')),
107
- path('endpoints/', include('django_cfg.apps.api.endpoints.urls')),
108
- path('commands/', include('django_cfg.apps.api.commands.urls')),
109
-
110
- # OpenAPI schemas (if enabled)
111
- # Provides /openapi/{group}/schema/
112
- path('openapi/', include('django_cfg.modules.django_client.urls')),
113
- ]
114
-
72
+ from django_cfg.modules.django_client.core.config import OpenAPIGroupConfig
73
+
74
+ return OpenAPIGroupConfig(
75
+ name="cfg",
76
+ apps=get_enabled_cfg_apps(),
77
+ title="Django-CFG API",
78
+ description="Authentication (OTP), Support, Newsletter, Leads, Knowledge Base, AI Agents, Tasks, Payments",
79
+ version="1.0.0",
80
+ )
81
+
82
+
83
+ def _safe_include(pattern_path: str, module_path: str):
84
+ """Helper to safely include URL module if it exists."""
115
85
  try:
116
- # Auto-register from OpenAPI groups (preferred)
117
- from django_cfg.modules.django_client.core import get_openapi_service
118
- service = get_openapi_service()
119
-
120
- if service and service.is_enabled():
121
- _register_group_urls(patterns, service.get_groups())
122
- else:
123
- # Fallback: Use BaseCfgModule when OpenAPI disabled
124
- _register_apps_fallback(patterns)
125
-
126
- except Exception:
127
- # Last resort fallback
128
- _register_apps_fallback(patterns)
129
-
130
- return patterns
131
-
132
-
133
- # Generate URL patterns dynamically
134
- urlpatterns = get_django_cfg_urlpatterns()
86
+ return path(pattern_path, include(module_path))
87
+ except ImportError:
88
+ return None
89
+
90
+
91
+ # Core API endpoints (always enabled)
92
+ # Note: All prefixes are explicit here (cfg/, health/, etc.)
93
+ urlpatterns = [
94
+ path('cfg/health/', include('django_cfg.apps.api.health.urls')),
95
+ path('cfg/endpoints/', include('django_cfg.apps.api.endpoints.urls')),
96
+ path('cfg/commands/', include('django_cfg.apps.api.commands.urls')),
97
+ path('cfg/openapi/', include('django_cfg.modules.django_client.urls')),
98
+ ]
99
+
100
+ # Django-CFG apps - conditionally registered based on config
101
+ # Map app paths to URL patterns (with cfg/ prefix from add_django_cfg_urls)
102
+ APP_URL_MAP = {
103
+ "django_cfg.apps.accounts": [
104
+ ("cfg/accounts/", "django_cfg.apps.accounts.urls"),
105
+ ],
106
+ "django_cfg.apps.support": [
107
+ ("cfg/support/", "django_cfg.apps.support.urls"),
108
+ ],
109
+ "django_cfg.apps.newsletter": [
110
+ ("cfg/newsletter/", "django_cfg.apps.newsletter.urls"),
111
+ ],
112
+ "django_cfg.apps.leads": [
113
+ ("cfg/leads/", "django_cfg.apps.leads.urls"),
114
+ ],
115
+ "django_cfg.apps.knowbase": [
116
+ ("cfg/knowbase/", "django_cfg.apps.knowbase.urls"),
117
+ ],
118
+ "django_cfg.apps.agents": [
119
+ ("cfg/agents/", "django_cfg.apps.agents.urls"),
120
+ ],
121
+ "django_cfg.apps.tasks": [
122
+ ("cfg/tasks/", "django_cfg.apps.tasks.urls"),
123
+ ("cfg/tasks/admin/", "django_cfg.apps.tasks.urls_admin"),
124
+ ],
125
+ "django_cfg.apps.payments": [
126
+ ("cfg/payments/", "django_cfg.apps.payments.urls"),
127
+ ("cfg/payments/admin/", "django_cfg.apps.payments.urls_admin"),
128
+ ],
129
+ }
130
+
131
+ # Register URLs for enabled apps only
132
+ enabled_apps = get_enabled_cfg_apps()
133
+ cfg_app_urls = []
134
+
135
+ for app_path in enabled_apps:
136
+ if app_path in APP_URL_MAP:
137
+ for url_pattern, url_module in APP_URL_MAP[app_path]:
138
+ cfg_app_urls.append(_safe_include(url_pattern, url_module))
139
+
140
+ # Maintenance (special case - admin only)
141
+ from django_cfg.modules.base import BaseCfgModule
142
+ if BaseCfgModule().is_maintenance_enabled():
143
+ cfg_app_urls.append(_safe_include('admin/django_cfg_maintenance/', 'django_cfg.apps.maintenance.urls_admin'))
144
+
145
+ # Add only successfully imported URLs
146
+ urlpatterns.extend([url for url in cfg_app_urls if url is not None])
@@ -10,18 +10,17 @@ from django_cfg.core.environment import EnvironmentDetector
10
10
  from django.conf import settings
11
11
 
12
12
 
13
- def add_django_cfg_urls(urlpatterns: List[URLPattern], cfg_prefix: str = "cfg/") -> List[URLPattern]:
13
+ def add_django_cfg_urls(urlpatterns: List[URLPattern]) -> List[URLPattern]:
14
14
  """
15
15
  Automatically add django_cfg URLs and all integrations to the main URL configuration.
16
16
 
17
17
  This function adds:
18
- - Django CFG management URLs (cfg/)
18
+ - Django CFG management URLs
19
19
  - Django Client URLs (if available)
20
20
  - Startup information display (based on config)
21
21
 
22
22
  Args:
23
23
  urlpatterns: Existing URL patterns list
24
- cfg_prefix: URL prefix for django_cfg endpoints (default: "cfg/")
25
24
 
26
25
  Returns:
27
26
  Updated URL patterns list with all URLs added
@@ -35,17 +34,13 @@ def add_django_cfg_urls(urlpatterns: List[URLPattern], cfg_prefix: str = "cfg/")
35
34
  path("admin/", admin.site.urls),
36
35
  ]
37
36
 
38
- # Automatically adds:
39
- # - path("cfg/", include("django_cfg.apps.urls"))
40
- # - Django Client URLs (if available)
41
- # - Startup info display (based on config.startup_info_mode)
37
+ # Automatically adds django_cfg URLs with proper prefixes
42
38
  urlpatterns = add_django_cfg_urls(urlpatterns)
43
39
  """
44
40
  # Add django_cfg API URLs
45
- # Note: Django Client URLs are included in django_cfg.apps.urls
46
- # at /cfg/openapi/{group}/schema/ to avoid conflicts
41
+ # Note: URL prefixes (cfg/, health/, etc.) are defined in django_cfg.apps.urls
47
42
  new_patterns = urlpatterns + [
48
- path(cfg_prefix, include("django_cfg.apps.urls")),
43
+ path("", include("django_cfg.apps.urls")),
49
44
  ]
50
45
 
51
46
  # Add django-browser-reload URLs in development (if installed)
@@ -57,8 +57,8 @@ class OpenAPIClientConfig(OpenAPIConfig):
57
57
  description="Schema path prefix for DRF Spectacular"
58
58
  )
59
59
  drf_enable_browsable_api: bool = Field(
60
- default=False,
61
- description="Enable DRF browsable API"
60
+ default=True,
61
+ description="Enable DRF browsable API with Tailwind theme"
62
62
  )
63
63
  drf_enable_throttling: bool = Field(
64
64
  default=False,
@@ -104,6 +104,8 @@ class OpenAPIClientConfig(OpenAPIConfig):
104
104
  def get_groups_with_defaults(self) -> Dict[str, OpenAPIGroupConfig]:
105
105
  """
106
106
  Get groups with django-cfg default groups automatically added.
107
+
108
+ Automatically adds the 'cfg' group with all django-cfg apps if not explicitly defined.
107
109
 
108
110
  Returns:
109
111
  Dict of groups including default django-cfg groups
@@ -111,52 +113,13 @@ class OpenAPIClientConfig(OpenAPIConfig):
111
113
  # Convert list to dict for compatibility
112
114
  groups_dict = {group.name: group for group in self.groups}
113
115
 
114
- # Add default django-cfg groups if enabled
115
- try:
116
- from django_cfg.modules.base import BaseCfgModule
117
- base_module = BaseCfgModule()
118
-
119
- support_enabled = base_module.is_support_enabled()
120
- accounts_enabled = base_module.is_accounts_enabled()
121
- newsletter_enabled = base_module.is_newsletter_enabled()
122
- leads_enabled = base_module.is_leads_enabled()
123
- knowbase_enabled = base_module.is_knowbase_enabled()
124
- agents_enabled = base_module.is_agents_enabled()
125
- tasks_enabled = base_module.should_enable_tasks()
126
- payments_enabled = base_module.is_payments_enabled()
127
-
128
- # Collect all enabled django-cfg apps for unified group
129
- enabled_cfg_apps = []
130
- if support_enabled:
131
- enabled_cfg_apps.append("django_cfg.apps.support")
132
- if accounts_enabled:
133
- enabled_cfg_apps.append("django_cfg.apps.accounts")
134
- if newsletter_enabled:
135
- enabled_cfg_apps.append("django_cfg.apps.newsletter")
136
- if leads_enabled:
137
- enabled_cfg_apps.append("django_cfg.apps.leads")
138
- if knowbase_enabled:
139
- enabled_cfg_apps.append("django_cfg.apps.knowbase")
140
- if agents_enabled:
141
- enabled_cfg_apps.append("django_cfg.apps.agents")
142
- if tasks_enabled:
143
- enabled_cfg_apps.append("django_cfg.apps.tasks")
144
- if payments_enabled:
145
- enabled_cfg_apps.append("django_cfg.apps.payments")
146
-
147
- # Add unified 'cfg' group with all enabled apps
148
- if enabled_cfg_apps and 'cfg' not in groups_dict:
149
- groups_dict['cfg'] = OpenAPIGroupConfig(
150
- name="cfg",
151
- apps=enabled_cfg_apps,
152
- title="Django-CFG API",
153
- description="All django-cfg built-in applications",
154
- )
155
-
156
- return groups_dict
157
-
158
- except Exception:
159
- pass
116
+ # Add default 'cfg' group if not explicitly defined
117
+ if 'cfg' not in groups_dict:
118
+ try:
119
+ from django_cfg.apps.urls import get_default_cfg_group
120
+ groups_dict['cfg'] = get_default_cfg_group()
121
+ except Exception:
122
+ pass
160
123
 
161
124
  return groups_dict
162
125
 
@@ -110,24 +110,24 @@ class FetchersGenerator:
110
110
  Convert operation to function name.
111
111
 
112
112
  Examples:
113
- users_list (GET) -> getUsers
114
- users_retrieve (GET) -> getUser
115
- users_create (POST) -> createUser
116
- users_update (PUT) -> updateUser
117
- users_partial_update (PATCH) -> updateUser
118
- users_destroy (DELETE) -> deleteUser
113
+ users_list (GET) -> getUsersList
114
+ users_retrieve (GET) -> getUsersById
115
+ users_create (POST) -> createUsers
116
+ users_update (PUT) -> updateUsers
117
+ users_partial_update (PATCH) -> partialUpdateUsers
118
+ users_destroy (DELETE) -> deleteUsers
119
119
  """
120
120
  # Remove tag prefix from operation_id
121
121
  op_id = operation.operation_id
122
122
 
123
- # Handle common patterns (remove only suffix, not all occurrences)
123
+ # Handle common patterns - keep full resource name for uniqueness
124
124
  if op_id.endswith("_list"):
125
125
  resource = op_id.removesuffix("_list")
126
- return f"get{self._to_pascal_case(resource)}"
126
+ return f"get{self._to_pascal_case(resource)}List"
127
127
  elif op_id.endswith("_retrieve"):
128
128
  resource = op_id.removesuffix("_retrieve")
129
- # Singular
130
- return f"get{self._to_pascal_case(resource).rstrip('s')}"
129
+ # Add ById suffix to distinguish from list
130
+ return f"get{self._to_pascal_case(resource)}ById"
131
131
  elif op_id.endswith("_create"):
132
132
  resource = op_id.removesuffix("_create")
133
133
  return f"create{self._to_pascal_case(resource)}"
@@ -180,24 +180,23 @@ class HooksGenerator:
180
180
  Convert operation to hook name.
181
181
 
182
182
  Examples:
183
- users_list (GET) -> useUsers
184
- users_retrieve (GET) -> useUser
185
- users_create (POST) -> useCreateUser
186
- users_update (PUT) -> useUpdateUser
187
- users_partial_update (PATCH) -> useUpdateUser
188
- users_destroy (DELETE) -> useDeleteUser
183
+ users_list (GET) -> useUsersList
184
+ users_retrieve (GET) -> useUsersById
185
+ users_create (POST) -> useCreateUsers
186
+ users_update (PUT) -> useUpdateUsers
187
+ users_partial_update (PATCH) -> usePartialUpdateUsers
188
+ users_destroy (DELETE) -> useDeleteUsers
189
189
  """
190
190
  op_id = operation.operation_id
191
191
 
192
+ # Keep full resource name and add suffixes for uniqueness
192
193
  if op_id.endswith("_list"):
193
- resource = op_id.replace("_list", "")
194
- # Plural form
195
- return f"use{self._to_pascal_case(resource)}"
194
+ resource = op_id.removesuffix("_list")
195
+ return f"use{self._to_pascal_case(resource)}List"
196
196
  elif op_id.endswith("_retrieve"):
197
- resource = op_id.replace("_retrieve", "")
198
- # Singular form (remove trailing 's')
199
- resource_singular = resource.rstrip('s') if resource.endswith('s') and len(resource) > 1 else resource
200
- return f"use{self._to_pascal_case(resource_singular)}"
197
+ resource = op_id.removesuffix("_retrieve")
198
+ # Add ById suffix to distinguish from list
199
+ return f"use{self._to_pascal_case(resource)}ById"
201
200
  elif op_id.endswith("_create"):
202
201
  resource = op_id.removesuffix("_create")
203
202
  return f"useCreate{self._to_pascal_case(resource)}"
@@ -215,18 +214,17 @@ class HooksGenerator:
215
214
  return f"use{self._to_pascal_case(op_id)}"
216
215
 
217
216
  def _operation_to_fetcher_name(self, operation: IROperationObject) -> str:
218
- """Get corresponding fetcher function name."""
217
+ """Get corresponding fetcher function name (must match fetchers_generator logic)."""
219
218
  op_id = operation.operation_id
220
219
 
221
- # Remove only suffix, not all occurrences (same logic as fetchers_generator)
220
+ # Must match fetchers_generator._operation_to_function_name() exactly
222
221
  if op_id.endswith("_list"):
223
222
  resource = op_id.removesuffix("_list")
224
- return f"get{self._to_pascal_case(resource)}"
223
+ return f"get{self._to_pascal_case(resource)}List"
225
224
  elif op_id.endswith("_retrieve"):
226
225
  resource = op_id.removesuffix("_retrieve")
227
- # Singular
228
- resource_singular = resource.rstrip('s') if resource.endswith('s') else resource
229
- return f"get{self._to_pascal_case(resource_singular)}"
226
+ # Add ById suffix to match fetchers_generator
227
+ return f"get{self._to_pascal_case(resource)}ById"
230
228
  elif op_id.endswith("_create"):
231
229
  resource = op_id.removesuffix("_create")
232
230
  return f"create{self._to_pascal_case(resource)}"
@@ -240,7 +238,7 @@ class HooksGenerator:
240
238
  resource = op_id.removesuffix("_destroy")
241
239
  return f"delete{self._to_pascal_case(resource)}"
242
240
  else:
243
- return f"{operation.http_method.lower()}{self._to_pascal_case(op_id)}"
241
+ return self._to_camel_case(op_id)
244
242
 
245
243
  def _get_param_info(self, operation: IROperationObject) -> dict:
246
244
  """
@@ -391,6 +389,11 @@ class HooksGenerator:
391
389
  """Convert snake_case to PascalCase."""
392
390
  return ''.join(word.capitalize() for word in snake_str.split('_'))
393
391
 
392
+ def _to_camel_case(self, snake_str: str) -> str:
393
+ """Convert snake_case to camelCase."""
394
+ components = snake_str.split('_')
395
+ return components[0] + ''.join(x.capitalize() for x in components[1:])
396
+
394
397
  def generate_tag_hooks_file(
395
398
  self,
396
399
  tag: str,
@@ -79,7 +79,7 @@ class DashboardManager(BaseCfgModule):
79
79
  if self.should_enable_tasks():
80
80
  operations_items.extend([
81
81
  NavigationItem(title="Background Tasks", icon=Icons.TASK, link="/admin/django_dramatiq/task/"),
82
- NavigationItem(title="Task Dashboard", icon=Icons.SETTINGS_APPLICATIONS, link="/cfg/admin/django_cfg_tasks/admin/dashboard/"),
82
+ NavigationItem(title="Task Dashboard", icon=Icons.SETTINGS_APPLICATIONS, link="/cfg/tasks/admin/dashboard/"),
83
83
  ])
84
84
 
85
85
  # Maintenance Mode (if enabled)
@@ -194,7 +194,7 @@ class DashboardManager(BaseCfgModule):
194
194
 
195
195
  # Main dashboard (always show if payments app enabled)
196
196
  payments_items.append(
197
- NavigationItem(title="Payment Dashboard", icon=Icons.DASHBOARD, link="/cfg/admin/django_cfg_payments/admin/")
197
+ NavigationItem(title="Payment Dashboard", icon=Icons.DASHBOARD, link="/cfg/payments/admin/")
198
198
  )
199
199
 
200
200
  # Always show basic admin models (even if payments functionality is disabled)
@@ -208,13 +208,13 @@ class DashboardManager(BaseCfgModule):
208
208
  # Add advanced features only if payments functionality is enabled
209
209
  if config.enabled:
210
210
  # payments_items.append(
211
- # NavigationItem(title="Webhook Dashboard", icon=Icons.WEBHOOK, link="/cfg/admin/django_cfg_payments/admin/webhooks/")
211
+ # NavigationItem(title="Webhook Dashboard", icon=Icons.WEBHOOK, link="/cfg/payments/admin/webhooks/")
212
212
  # )
213
213
  # payments_items.append(
214
- # NavigationItem(title="Create Payment", icon=Icons.ADD, link="/cfg/admin/django_cfg_payments/admin/payments/create/")
214
+ # NavigationItem(title="Create Payment", icon=Icons.ADD, link="/cfg/payments/admin/payments/create/")
215
215
  # )
216
216
  # payments_items.append(
217
- # NavigationItem(title="Currency Converter", icon=Icons.CURRENCY_EXCHANGE, link="/cfg/admin/django_cfg_payments/admin/tools/converter/")
217
+ # NavigationItem(title="Currency Converter", icon=Icons.CURRENCY_EXCHANGE, link="/cfg/payments/admin/tools/converter/")
218
218
  # )
219
219
 
220
220
  # Show subscription features only if enabled
@@ -246,7 +246,7 @@ class DashboardManager(BaseCfgModule):
246
246
  except Exception:
247
247
  # Fallback
248
248
  payments_items = [
249
- NavigationItem(title="Payment Dashboard", icon=Icons.DASHBOARD, link="/cfg/admin/django_cfg_payments/admin/"),
249
+ NavigationItem(title="Payment Dashboard", icon=Icons.DASHBOARD, link="/cfg/payments/admin/"),
250
250
  NavigationItem(title="Universal Payments", icon=Icons.ACCOUNT_BALANCE, link="/admin/payments/universalpayment/"),
251
251
  ]
252
252
 
django_cfg/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "django-cfg"
7
- version = "1.4.13"
7
+ version = "1.4.14"
8
8
  description = "Django AI framework with built-in agents, type-safe Pydantic v2 configuration, and 8 enterprise apps. Replace settings.py, validate at startup, 90% less code. Production-ready AI workflows for Django."
9
9
  readme = "README.md"
10
10
  keywords = [ "django", "configuration", "pydantic", "settings", "type-safety", "pydantic-settings", "django-environ", "startup-validation", "ide-autocomplete", "ai-agents", "enterprise-django", "django-settings", "type-safe-config",]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-cfg
3
- Version: 1.4.13
3
+ Version: 1.4.14
4
4
  Summary: Django AI framework with built-in agents, type-safe Pydantic v2 configuration, and 8 enterprise apps. Replace settings.py, validate at startup, 90% less code. Production-ready AI workflows for Django.
5
5
  Project-URL: Homepage, https://djangocfg.com
6
6
  Project-URL: Documentation, https://djangocfg.com
@@ -3,7 +3,7 @@ django_cfg/__init__.py,sha256=4dZgnuTlq8YmLISTJAqnPrr080kxPfmPKUjhiWhkEDc,1630
3
3
  django_cfg/apps.py,sha256=k84brkeXJI7EgKZLEpTkM9YFZofKI4PzhFOn1cl9Msc,1656
4
4
  django_cfg/config.py,sha256=3hX5bOCbOWdUvtD9Z5qEHEOEyWzY1-4CsvFs_EO7VSw,1398
5
5
  django_cfg/apps/__init__.py,sha256=JtDmEYt1OcleWM2ZaeX0LKDnRQzPOavfaXBWG4ECB5Q,26
6
- django_cfg/apps/urls.py,sha256=5fy2TPnYGyiLyy3EpDQkDuR6OuNTSw9avASrD7fSg38,4589
6
+ django_cfg/apps/urls.py,sha256=DKlW8kSPv4MImjNlc9uaukOEkKrk-AvOQrOSjerVUf8,4633
7
7
  django_cfg/apps/accounts/README.md,sha256=YkUYJ3iKMYTmm9ALK2PDnX75SDqZxgnkzNLCD5efxRs,8227
8
8
  django_cfg/apps/accounts/__init__.py,sha256=osecEQhMJVP8ejhZzElNsAqA1fX-GPD3K5_yNwDk6IE,100
9
9
  django_cfg/apps/accounts/__models.py,sha256=65AomWYd78ptQ60drPbodxf0Ue310vmJQpQOPHL6V3E,10161
@@ -588,7 +588,7 @@ django_cfg/core/generation/utils/helpers.py,sha256=V6dTWIzGzAHYDLR7YgV50g_eeWIV8
588
588
  django_cfg/core/integration/README.md,sha256=3V27xNWoGQkauixJGW8TAE8p0FB8iCdt6zI4G4Nnssw,10699
589
589
  django_cfg/core/integration/__init__.py,sha256=2lIMD6W1-uAuo6S3eOWNLPgPgr-uZYSUWZd9F7QZR-c,1880
590
590
  django_cfg/core/integration/commands_collector.py,sha256=-9ppg2jYSrTC35Ijm5vAHUcNZM6AkayuPkfw715Ca2Q,8325
591
- django_cfg/core/integration/url_integration.py,sha256=mEOyhMR_L4iHclM85hl5Jlg8eFtinFH8_6I6lPdKrQQ,4049
591
+ django_cfg/core/integration/url_integration.py,sha256=lTQ_OTDJ5nZQyx4vvkGRj3Edg7sjel7O_NOVZ1NTZY4,3755
592
592
  django_cfg/core/integration/version_checker.py,sha256=NrrbyTAtIkZdNPQHO2PMQjrp8DHk1xVMpvG9xiIvevA,4834
593
593
  django_cfg/core/integration/display/__init__.py,sha256=YwU9nmXOcDJL3q0wW5Q_59gLh_BXPsOfDlT3sZqHWZU,315
594
594
  django_cfg/core/integration/display/base.py,sha256=67ABqa1WErBcFTROFy8fBzwvYYmHiloBk7-Hpexd2rE,5720
@@ -648,7 +648,7 @@ django_cfg/models/base/module.py,sha256=P6YowmE-VOqp_L25Ijxj2hjjNhB9xtlm8G35DHWq
648
648
  django_cfg/models/django/__init__.py,sha256=eAV-U4Kj7lW1ymGEQ8Ng6Y7MAq0Nro3wk4_yf80nPEI,350
649
649
  django_cfg/models/django/constance.py,sha256=IVklMTtusxWnWaU3PSatGLQfg5qY_Y89MZQjsJFwbCk,9175
650
650
  django_cfg/models/django/environment.py,sha256=jdg6DXQrnuLSdfZNV4KoFlkiPl1n2jOicPU8NFzyB5U,9439
651
- django_cfg/models/django/openapi.py,sha256=UyObu4IJqDUWQ_CcaZFXsd0rdCpAejlobYTVj5Y1pPA,5846
651
+ django_cfg/models/django/openapi.py,sha256=AElzz_cPWRgibBLWEnT5dg6CAbGymMMOtRLIsBEiye0,4226
652
652
  django_cfg/models/django/revolution_legacy.py,sha256=NONFa3U-w2HSMKumEh0BOD5HVI3ghiMvDbISpWC_wr4,9005
653
653
  django_cfg/models/infrastructure/__init__.py,sha256=If0XLyDNaR_F6rOhDJBCT5RmkOOoNcY61L70pQvaO1s,369
654
654
  django_cfg/models/infrastructure/cache.py,sha256=N6LWinyokWcmuJmInn0q48TQq0Je-xXMJdZ0DbelGPU,12175
@@ -760,10 +760,10 @@ django_cfg/modules/django_client/core/generator/python/templates/utils/retry.py.
760
760
  django_cfg/modules/django_client/core/generator/python/templates/utils/schema.py.jinja,sha256=xrnhAc-hNk6DigxHmTvhR3dyDFI9ocnkhL-_Hz9hCs8,270
761
761
  django_cfg/modules/django_client/core/generator/typescript/__init__.py,sha256=eHOZp7M65WZ9u3tA_xQlON5-oijZZiGXDhz22Bq73s0,371
762
762
  django_cfg/modules/django_client/core/generator/typescript/client_generator.py,sha256=7ql-m59YVt6zGKfVBCxy1OR3CNy6C9lkaMEUqexiRvo,5878
763
- django_cfg/modules/django_client/core/generator/typescript/fetchers_generator.py,sha256=m2bIpgGK11_LGXw9P0QFiSPSQL_05pVYReWBOZ6aUhI,15602
763
+ django_cfg/modules/django_client/core/generator/typescript/fetchers_generator.py,sha256=FxoAIVq8x1o2WxbU0zGzExP6geC4vH7ofF3w53OXC08,15649
764
764
  django_cfg/modules/django_client/core/generator/typescript/files_generator.py,sha256=faRdhVVf9GQ-0esVz94dsaQMB56zK3csyNkhEHL4al4,7044
765
765
  django_cfg/modules/django_client/core/generator/typescript/generator.py,sha256=_xuQC10SJ1ZKwA2_h3te4ZkSmH2jlnjUr__EJuBwNVE,16982
766
- django_cfg/modules/django_client/core/generator/typescript/hooks_generator.py,sha256=suTnbSLHtaezc2kjZwbfNHYSDCYpq8DSeDJ0qL_Bkoc,19886
766
+ django_cfg/modules/django_client/core/generator/typescript/hooks_generator.py,sha256=DVGb6z_HrNbtMC6QqsyKOjZmAUGBFSEqSo-AijPLw7A,19994
767
767
  django_cfg/modules/django_client/core/generator/typescript/models_generator.py,sha256=m8K0V1sg3wXHv4IbJggFB1nxkCwiSBGruVavo9yzlxs,8283
768
768
  django_cfg/modules/django_client/core/generator/typescript/operations_generator.py,sha256=CIcRLrCBqpxOrYcjFtk0mkZGJgUnQlaRDK6G0xADoUA,13171
769
769
  django_cfg/modules/django_client/core/generator/typescript/schemas_generator.py,sha256=gd6w8kvogGhy0CDmNYSLRpMG8GiEqvXrBq5TUwOfgn0,10831
@@ -840,8 +840,6 @@ django_cfg/modules/django_currency/examples/__init__.py,sha256=AaHTrP-T1wQot01qi
840
840
  django_cfg/modules/django_currency/examples/example_database_usage.py,sha256=VelbgtnvKH37jkz6e2PHTgjMEc9GiUGkcr2cPyWIwL4,4518
841
841
  django_cfg/modules/django_currency/utils/__init__.py,sha256=6nD3BGQrBn94ZqCCBG8X78KPuTmberoOauwNOUi3SUk,130
842
842
  django_cfg/modules/django_currency/utils/cache.py,sha256=lkbkjtBpok-6UljoL8u3yWgyIaaayujlaPXiCamxGHE,2557
843
- django_cfg/modules/django_dashboard/DEBUG_README.md,sha256=X1yKA_IVQHwnyam2dZ7hHroLWyLeAOQ_OMfog1ILggk,3387
844
- django_cfg/modules/django_dashboard/REFACTORING_SUMMARY.md,sha256=PAxz0u0ueJRpzDbdhHEiJQncYob5Tc_LfuE1E5OtQko,8042
845
843
  django_cfg/modules/django_dashboard/__init__.py,sha256=Uh42VBq8QRr-x1eCi2cPGdXGgQM1g4tapIv4zk-PRuo,626
846
844
  django_cfg/modules/django_dashboard/components.py,sha256=TDuD54gMzae4jirPZ-vycJN_B0Fdzvq-uvcsnMa4nnw,9451
847
845
  django_cfg/modules/django_dashboard/debug.py,sha256=XpbVYvkX7dcYqYi2ogV88hQfwAwXVep1C57WiDiIXE8,5406
@@ -855,11 +853,6 @@ django_cfg/modules/django_dashboard/sections/documentation.py,sha256=ShWCIaC2VWH
855
853
  django_cfg/modules/django_dashboard/sections/overview.py,sha256=ysycr-qTRC1ndCFmO7KaEkc1BWU-eoaNqkoRnvnaYgw,13465
856
854
  django_cfg/modules/django_dashboard/sections/stats.py,sha256=k4ogZtZtR1CEkFTvoWgU-HTysr-Y2S4Lie7CNRzl534,1348
857
855
  django_cfg/modules/django_dashboard/sections/system.py,sha256=IP4SJMPOL-gqDancE_g46ZbmlveYDvljpszRJmz1tSc,2025
858
- django_cfg/modules/django_drf_theme/CHANGELOG.md,sha256=1yT2B3opzmo0e1rgnm79rCAhfLuWAA8RxkdfSFqRGRk,5394
859
- django_cfg/modules/django_drf_theme/EXAMPLE.md,sha256=TohVazV3tafLRVUPRhrmUahOdQZhOYhLtXcXJIupxTs,10967
860
- django_cfg/modules/django_drf_theme/IMPLEMENTATION.md,sha256=clVkX9ABfwhvWMnsV6jtD-lSuV5rj_xBO2spp4oS8so,7373
861
- django_cfg/modules/django_drf_theme/README.md,sha256=yptVECIXg9qOaAJ9Fsl_oXWaEs7ThuUmbOZ3ISqWhXY,5663
862
- django_cfg/modules/django_drf_theme/TAILWIND_CDN_GUIDE.md,sha256=00uWtI7DSzJ1khKGBkMiqnkpRXAAfy_6iSEY3tcu-D8,7107
863
856
  django_cfg/modules/django_drf_theme/__init__.py,sha256=0D5ctzcjadbw-z7jpvVjvo8C1ufrGRk6b-mA4xSutTE,525
864
857
  django_cfg/modules/django_drf_theme/apps.py,sha256=UzcjnTQyMqExKGwhg5KhCK0eEqgGXtaYbCztHd_V2pw,428
865
858
  django_cfg/modules/django_drf_theme/renderers.py,sha256=AVlXpepI62iHcZhbGAsLwtXpnQ8NeN0zyWQbePUnN3w,1808
@@ -1003,7 +996,7 @@ django_cfg/modules/django_twilio/templates/guide.md,sha256=nZfwx-sgWyK5NApm93zOe
1003
996
  django_cfg/modules/django_twilio/templates/sendgrid_otp_email.html,sha256=sXR6_D9hmOFfk9CrfPizpLddVhkRirBWpZd_ioEsxVk,6671
1004
997
  django_cfg/modules/django_twilio/templates/sendgrid_test_data.json,sha256=fh1VyuSiDELHsS_CIz9gp7tlsMAEjaDOoqbAPSZ3yyo,339
1005
998
  django_cfg/modules/django_unfold/__init__.py,sha256=Z91x1iGmkzlRbEb2L9OCFmYDKNAV9C4G3i15j5S0esc,1898
1006
- django_cfg/modules/django_unfold/dashboard.py,sha256=qzbk4LblELnQYE_HLVrf25-XHrksoaZAMbfWC59qLE8,18112
999
+ django_cfg/modules/django_unfold/dashboard.py,sha256=EFQWcUG1kfxgaP_cQJjd0qh_xFbm6cehuAeSSPipP8g,18010
1007
1000
  django_cfg/modules/django_unfold/models.py,sha256=bY6QSSaH_-r9vOTkSQjxeIkl5RaED7XkxXkT8-W5stk,4014
1008
1001
  django_cfg/modules/django_unfold/system_monitor.py,sha256=cznZqldRJqiSLSJbs4U7R2rX8ClzoIpqdfXdXqI2iQw,6955
1009
1002
  django_cfg/modules/django_unfold/tailwind.py,sha256=X9o1K3QL0VwUISgJ26sLb6zkdK-00qiDuekqTw-fydc,10846
@@ -1100,9 +1093,9 @@ django_cfg/utils/version_check.py,sha256=jI4v3YMdQriUEeb_TvRl511sDghy6I75iKRDUaN
1100
1093
  django_cfg/CHANGELOG.md,sha256=jtT3EprqEJkqSUh7IraP73vQ8PmKUMdRtznQsEnqDZk,2052
1101
1094
  django_cfg/CONTRIBUTING.md,sha256=DU2kyQ6PU0Z24ob7O_OqKWEYHcZmJDgzw-lQCmu6uBg,3041
1102
1095
  django_cfg/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1103
- django_cfg/pyproject.toml,sha256=0WNgZnwXJtXHRf27_wAqyXZd3paevoZLLw-7p4wD-R4,8210
1104
- django_cfg-1.4.13.dist-info/METADATA,sha256=HCB5jEjGz2UBDub6qlL55AU8SDwqHHukwNiK2OCb-b4,22533
1105
- django_cfg-1.4.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1106
- django_cfg-1.4.13.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
1107
- django_cfg-1.4.13.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1108
- django_cfg-1.4.13.dist-info/RECORD,,
1096
+ django_cfg/pyproject.toml,sha256=FqIAwbsy4vIgNZhhoM8qb7w7Pmtx3V1fAxBSEIMyDHw,8210
1097
+ django_cfg-1.4.14.dist-info/METADATA,sha256=LtLAMrSnJXG5HSuhzLtMT7mz5MOfDyQkkPIUDUVPdtg,22533
1098
+ django_cfg-1.4.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1099
+ django_cfg-1.4.14.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
1100
+ django_cfg-1.4.14.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1101
+ django_cfg-1.4.14.dist-info/RECORD,,