django-cfg 1.4.84__py3-none-any.whl → 1.4.86__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.

Potentially problematic release.


This version of django-cfg might be problematic. Click here for more details.

Files changed (88) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/dashboard/serializers/__init__.py +55 -0
  3. django_cfg/apps/dashboard/serializers/activity.py +38 -0
  4. django_cfg/apps/dashboard/serializers/apizones.py +26 -0
  5. django_cfg/apps/dashboard/serializers/base.py +16 -0
  6. django_cfg/apps/dashboard/serializers/charts.py +44 -0
  7. django_cfg/apps/dashboard/serializers/commands.py +26 -0
  8. django_cfg/apps/dashboard/serializers/overview.py +34 -0
  9. django_cfg/apps/dashboard/serializers/statistics.py +46 -0
  10. django_cfg/apps/dashboard/serializers/system.py +58 -0
  11. django_cfg/apps/dashboard/services/__init__.py +10 -1
  12. django_cfg/apps/dashboard/services/apizones_service.py +119 -0
  13. django_cfg/apps/dashboard/services/charts_service.py +266 -0
  14. django_cfg/apps/dashboard/services/commands_service.py +142 -0
  15. django_cfg/apps/dashboard/services/statistics_service.py +262 -104
  16. django_cfg/apps/dashboard/urls.py +25 -6
  17. django_cfg/apps/dashboard/views/__init__.py +23 -0
  18. django_cfg/apps/dashboard/views/activity_views.py +83 -0
  19. django_cfg/apps/dashboard/views/apizones_views.py +73 -0
  20. django_cfg/apps/dashboard/views/charts_views.py +159 -0
  21. django_cfg/apps/dashboard/views/commands_views.py +73 -0
  22. django_cfg/apps/dashboard/views/overview_views.py +92 -0
  23. django_cfg/apps/dashboard/views/statistics_views.py +105 -0
  24. django_cfg/apps/dashboard/views/system_views.py +73 -0
  25. django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/fetchers.ts.jinja +0 -1
  26. django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/function.ts.jinja +1 -1
  27. django_cfg/modules/django_unfold/callbacks/main.py +7 -6
  28. django_cfg/modules/django_unfold/dashboard.py +1 -36
  29. django_cfg/modules/django_unfold/models/config.py +102 -73
  30. django_cfg/modules/django_unfold/tailwind.py +31 -79
  31. django_cfg/pyproject.toml +1 -1
  32. django_cfg/static/frontend/admin/404.html +1 -1
  33. django_cfg/static/frontend/admin/500.html +1 -1
  34. django_cfg/static/frontend/admin/_next/static/chunks/23004-faae121bbfecc163.js +1 -0
  35. django_cfg/static/frontend/admin/_next/static/chunks/{20695.a7d37b6c40ad3f58.js → 43076.55dd23b6cd68edb0.js} +1 -1
  36. django_cfg/static/frontend/admin/_next/static/chunks/50314-3b9d15242191c8bc.js +1 -0
  37. django_cfg/static/frontend/admin/_next/static/chunks/{64330.2ef79bccd7d4e363.js → 64330.41858e98c0e5173b.js} +1 -1
  38. django_cfg/static/frontend/admin/_next/static/chunks/6766.8d01e44e83070e83.js +1 -0
  39. django_cfg/static/frontend/admin/_next/static/chunks/{96168.eb7fdb721b9cdb00.js → 96168.b7197f890097df6e.js} +1 -1
  40. django_cfg/static/frontend/admin/_next/static/chunks/pages/{404-c283223d1afd02a2.js → 404-cf71cd7b3cb005e5.js} +1 -1
  41. django_cfg/static/frontend/admin/_next/static/chunks/pages/{500-389d6d3e1f2f7fda.js → 500-ff19c7842e3df415.js} +1 -1
  42. django_cfg/static/frontend/admin/_next/static/chunks/pages/_app-f62e5528fbcbb6b3.js +272 -0
  43. django_cfg/static/frontend/admin/_next/static/chunks/pages/{_error-5291033275c26d09.js → _error-87f3fdc2aa131e77.js} +1 -1
  44. django_cfg/static/frontend/admin/_next/static/chunks/pages/{index-d7bc30185f52cbca.js → index-69f737d4802cc5b7.js} +1 -1
  45. django_cfg/static/frontend/admin/_next/static/chunks/pages/private/centrifugo-f24beb6ed3955aa8.js +1 -0
  46. django_cfg/static/frontend/admin/_next/static/chunks/pages/private/{profile-e93a65e8e7d9022b.js → profile-b8045f993287f1a7.js} +1 -1
  47. django_cfg/static/frontend/admin/_next/static/chunks/pages/private/{ui-669e8f2a785beba2.js → ui-373fff8b42878e64.js} +1 -1
  48. django_cfg/static/frontend/admin/_next/static/chunks/pages/private-fe9faa86ecdb0ce6.js +1 -0
  49. django_cfg/static/frontend/admin/_next/static/chunks/{webpack-92add5f95c66e349.js → webpack-7c456a65e96eb97e.js} +1 -1
  50. django_cfg/static/frontend/admin/_next/static/css/5f9a37b6e6a72303.css +3 -0
  51. django_cfg/static/frontend/admin/_next/static/wg0mGdXjT00H_1BUxoOSH/_buildManifest.js +1 -0
  52. django_cfg/static/frontend/admin/auth.html +1 -1
  53. django_cfg/static/frontend/admin/index.html +1 -1
  54. django_cfg/static/frontend/admin/legal/cookies.html +1 -1
  55. django_cfg/static/frontend/admin/legal/privacy.html +1 -1
  56. django_cfg/static/frontend/admin/legal/security.html +1 -1
  57. django_cfg/static/frontend/admin/legal/terms.html +1 -1
  58. django_cfg/static/frontend/admin/private/centrifugo.html +1 -1
  59. django_cfg/static/frontend/admin/private/profile.html +1 -1
  60. django_cfg/static/frontend/admin/private/ui.html +1 -1
  61. django_cfg/static/frontend/admin/private.html +1 -1
  62. django_cfg/templates/admin/index.html +237 -5
  63. django_cfg/templates/admin/sections/commands_section.html +5 -0
  64. django_cfg/templates/admin/sections/documentation_section.html +5 -0
  65. django_cfg/templates/admin/sections/overview_section.html +5 -0
  66. django_cfg/templates/admin/sections/stats_section.html +5 -0
  67. django_cfg/templates/admin/sections/system_section.html +5 -0
  68. django_cfg/templates/admin/sections/widgets_section.html +11 -0
  69. django_cfg/templates/unfold/layouts/skeleton.html +27 -0
  70. django_cfg/templatetags/django_cfg.py +53 -0
  71. {django_cfg-1.4.84.dist-info → django_cfg-1.4.86.dist-info}/METADATA +1 -1
  72. {django_cfg-1.4.84.dist-info → django_cfg-1.4.86.dist-info}/RECORD +76 -53
  73. django_cfg/apps/dashboard/api/__init__.py +0 -27
  74. django_cfg/apps/dashboard/api/serializers.py +0 -165
  75. django_cfg/apps/dashboard/api/viewsets.py +0 -257
  76. django_cfg/static/frontend/admin/_next/static/-Zk0eDB7OJOEFrFyR5BwZ/_buildManifest.js +0 -1
  77. django_cfg/static/frontend/admin/_next/static/chunks/43076-4be6a9794e9c3e8b.js +0 -1
  78. django_cfg/static/frontend/admin/_next/static/chunks/50314-79c02212788f1ec7.js +0 -1
  79. django_cfg/static/frontend/admin/_next/static/chunks/6766.d62fed7cd4761148.js +0 -1
  80. django_cfg/static/frontend/admin/_next/static/chunks/82296-a2c8d38f62224be5.js +0 -1
  81. django_cfg/static/frontend/admin/_next/static/chunks/pages/_app-f25bec36bbdc9625.js +0 -272
  82. django_cfg/static/frontend/admin/_next/static/chunks/pages/private/centrifugo-22532c65971225eb.js +0 -1
  83. django_cfg/static/frontend/admin/_next/static/chunks/pages/private-a8a9ba76f2c75354.js +0 -1
  84. django_cfg/static/frontend/admin/_next/static/css/78d677ac1677c210.css +0 -3
  85. /django_cfg/static/frontend/admin/_next/static/{-Zk0eDB7OJOEFrFyR5BwZ → wg0mGdXjT00H_1BUxoOSH}/_ssgManifest.js +0 -0
  86. {django_cfg-1.4.84.dist-info → django_cfg-1.4.86.dist-info}/WHEEL +0 -0
  87. {django_cfg-1.4.84.dist-info → django_cfg-1.4.86.dist-info}/entry_points.txt +0 -0
  88. {django_cfg-1.4.84.dist-info → django_cfg-1.4.86.dist-info}/licenses/LICENSE +0 -0
django_cfg/__init__.py CHANGED
@@ -32,7 +32,7 @@ Example:
32
32
  default_app_config = "django_cfg.apps.DjangoCfgConfig"
33
33
 
34
34
  # Version information
35
- __version__ = "1.4.84"
35
+ __version__ = "1.4.86"
36
36
  __license__ = "MIT"
37
37
 
38
38
  # Import registry for organized lazy loading
@@ -0,0 +1,55 @@
1
+ """
2
+ Dashboard Serializers
3
+
4
+ Organized by domain for better maintainability.
5
+ """
6
+
7
+ from .base import APIResponseSerializer
8
+ from .statistics import StatCardSerializer, UserStatisticsSerializer, AppStatisticsSerializer
9
+ from .system import SystemHealthSerializer, SystemHealthItemSerializer, SystemMetricsSerializer
10
+ from .activity import ActivityEntrySerializer, QuickActionSerializer
11
+ from .overview import DashboardOverviewSerializer
12
+ from .charts import (
13
+ ChartDataSerializer,
14
+ ChartDatasetSerializer,
15
+ ActivityTrackerDaySerializer,
16
+ RecentUserSerializer,
17
+ )
18
+ from .commands import CommandSerializer, CommandsSummarySerializer
19
+ from .apizones import APIZoneSerializer, APIZonesSummarySerializer
20
+
21
+ __all__ = [
22
+ # Base
23
+ 'APIResponseSerializer',
24
+
25
+ # Statistics
26
+ 'StatCardSerializer',
27
+ 'UserStatisticsSerializer',
28
+ 'AppStatisticsSerializer',
29
+
30
+ # System
31
+ 'SystemHealthSerializer',
32
+ 'SystemHealthItemSerializer',
33
+ 'SystemMetricsSerializer',
34
+
35
+ # Activity
36
+ 'ActivityEntrySerializer',
37
+ 'QuickActionSerializer',
38
+
39
+ # Overview
40
+ 'DashboardOverviewSerializer',
41
+
42
+ # Charts
43
+ 'ChartDataSerializer',
44
+ 'ChartDatasetSerializer',
45
+ 'ActivityTrackerDaySerializer',
46
+ 'RecentUserSerializer',
47
+
48
+ # Commands
49
+ 'CommandSerializer',
50
+ 'CommandsSummarySerializer',
51
+
52
+ # API Zones
53
+ 'APIZoneSerializer',
54
+ 'APIZonesSummarySerializer',
55
+ ]
@@ -0,0 +1,38 @@
1
+ """
2
+ Activity Serializers
3
+
4
+ Serializers for activity tracking and quick actions.
5
+ """
6
+
7
+ from rest_framework import serializers
8
+
9
+
10
+ class QuickActionSerializer(serializers.Serializer):
11
+ """
12
+ Serializer for quick action buttons.
13
+
14
+ Maps to QuickAction Pydantic model.
15
+ """
16
+
17
+ title = serializers.CharField(help_text="Action title")
18
+ description = serializers.CharField(help_text="Action description")
19
+ icon = serializers.CharField(help_text="Material icon name")
20
+ link = serializers.CharField(help_text="Action URL")
21
+ color = serializers.ChoiceField(
22
+ choices=['primary', 'success', 'warning', 'danger', 'secondary'],
23
+ default='primary',
24
+ help_text="Button color theme"
25
+ )
26
+ category = serializers.CharField(default='general', help_text="Action category")
27
+
28
+
29
+ class ActivityEntrySerializer(serializers.Serializer):
30
+ """Serializer for recent activity entries."""
31
+
32
+ id = serializers.IntegerField(help_text="Activity ID")
33
+ user = serializers.CharField(help_text="User who performed the action")
34
+ action = serializers.CharField(help_text="Action type (created, updated, deleted, etc.)")
35
+ resource = serializers.CharField(help_text="Resource affected")
36
+ timestamp = serializers.CharField(help_text="Activity timestamp (ISO format)")
37
+ icon = serializers.CharField(help_text="Material icon name")
38
+ color = serializers.CharField(help_text="Icon color")
@@ -0,0 +1,26 @@
1
+ """
2
+ API Zones Serializers
3
+
4
+ Serializers for OpenAPI zones/groups endpoints.
5
+ """
6
+
7
+ from rest_framework import serializers
8
+
9
+
10
+ class APIZoneSerializer(serializers.Serializer):
11
+ """OpenAPI zone/group serializer."""
12
+ name = serializers.CharField()
13
+ title = serializers.CharField()
14
+ description = serializers.CharField()
15
+ app_count = serializers.IntegerField()
16
+ endpoint_count = serializers.IntegerField()
17
+ status = serializers.CharField()
18
+ schema_url = serializers.CharField()
19
+ api_url = serializers.CharField()
20
+ apps = serializers.ListField(child=serializers.CharField())
21
+
22
+
23
+ class APIZonesSummarySerializer(serializers.Serializer):
24
+ """API zones summary serializer."""
25
+ zones = APIZoneSerializer(many=True)
26
+ summary = serializers.DictField()
@@ -0,0 +1,16 @@
1
+ """
2
+ Base Serializers
3
+
4
+ Common serializers used across dashboard endpoints.
5
+ """
6
+
7
+ from rest_framework import serializers
8
+
9
+
10
+ class APIResponseSerializer(serializers.Serializer):
11
+ """Standard API response wrapper."""
12
+
13
+ success = serializers.BooleanField(help_text="Operation success status")
14
+ message = serializers.CharField(required=False, help_text="Success message")
15
+ error = serializers.CharField(required=False, help_text="Error message")
16
+ data = serializers.DictField(required=False, help_text="Response data")
@@ -0,0 +1,44 @@
1
+ """
2
+ Charts Serializers
3
+
4
+ Serializers for chart data endpoints.
5
+ """
6
+
7
+ from rest_framework import serializers
8
+
9
+
10
+ class ChartDatasetSerializer(serializers.Serializer):
11
+ """Chart.js dataset serializer."""
12
+ label = serializers.CharField()
13
+ data = serializers.ListField(child=serializers.IntegerField())
14
+ backgroundColor = serializers.CharField()
15
+ borderColor = serializers.CharField()
16
+ tension = serializers.FloatField()
17
+ fill = serializers.BooleanField(required=False)
18
+
19
+
20
+ class ChartDataSerializer(serializers.Serializer):
21
+ """Chart.js data structure serializer."""
22
+ labels = serializers.ListField(child=serializers.CharField())
23
+ datasets = ChartDatasetSerializer(many=True)
24
+
25
+
26
+ class ActivityTrackerDaySerializer(serializers.Serializer):
27
+ """Activity tracker single day serializer."""
28
+ date = serializers.DateField()
29
+ count = serializers.IntegerField()
30
+ level = serializers.IntegerField()
31
+ color = serializers.CharField()
32
+ tooltip = serializers.CharField()
33
+
34
+
35
+ class RecentUserSerializer(serializers.Serializer):
36
+ """Recent user serializer."""
37
+ id = serializers.IntegerField()
38
+ username = serializers.CharField()
39
+ email = serializers.EmailField()
40
+ date_joined = serializers.CharField()
41
+ is_active = serializers.BooleanField()
42
+ is_staff = serializers.BooleanField()
43
+ is_superuser = serializers.BooleanField()
44
+ last_login = serializers.CharField(allow_null=True)
@@ -0,0 +1,26 @@
1
+ """
2
+ Commands Serializers
3
+
4
+ Serializers for Django management commands endpoints.
5
+ """
6
+
7
+ from rest_framework import serializers
8
+
9
+
10
+ class CommandSerializer(serializers.Serializer):
11
+ """Django management command serializer."""
12
+ name = serializers.CharField()
13
+ app = serializers.CharField()
14
+ help = serializers.CharField()
15
+ is_core = serializers.BooleanField()
16
+ is_custom = serializers.BooleanField()
17
+
18
+
19
+ class CommandsSummarySerializer(serializers.Serializer):
20
+ """Commands summary serializer."""
21
+ total_commands = serializers.IntegerField()
22
+ core_commands = serializers.IntegerField()
23
+ custom_commands = serializers.IntegerField()
24
+ categories = serializers.ListField(child=serializers.CharField())
25
+ commands = CommandSerializer(many=True)
26
+ categorized = serializers.DictField()
@@ -0,0 +1,34 @@
1
+ """
2
+ Overview Serializers
3
+
4
+ Serializers for dashboard overview endpoint.
5
+ """
6
+
7
+ from rest_framework import serializers
8
+
9
+
10
+ class DashboardOverviewSerializer(serializers.Serializer):
11
+ """
12
+ Main serializer for dashboard overview endpoint.
13
+ Uses DictField to avoid allOf generation in OpenAPI.
14
+ """
15
+
16
+ stat_cards = serializers.ListField(
17
+ child=serializers.DictField(),
18
+ help_text="Dashboard statistics cards"
19
+ )
20
+ system_health = serializers.ListField(
21
+ child=serializers.DictField(),
22
+ help_text="System health status"
23
+ )
24
+ quick_actions = serializers.ListField(
25
+ child=serializers.DictField(),
26
+ help_text="Quick action buttons"
27
+ )
28
+ recent_activity = serializers.ListField(
29
+ child=serializers.DictField(),
30
+ help_text="Recent activity entries"
31
+ )
32
+ system_metrics = serializers.DictField(help_text="System performance metrics")
33
+ user_statistics = serializers.DictField(help_text="User statistics")
34
+ timestamp = serializers.CharField(help_text="Data timestamp (ISO format)")
@@ -0,0 +1,46 @@
1
+ """
2
+ Statistics Serializers
3
+
4
+ Serializers for dashboard statistics endpoints.
5
+ """
6
+
7
+ from rest_framework import serializers
8
+
9
+
10
+ class StatCardSerializer(serializers.Serializer):
11
+ """
12
+ Serializer for dashboard statistics cards.
13
+
14
+ Maps to StatCard Pydantic model.
15
+ """
16
+
17
+ title = serializers.CharField(help_text="Card title")
18
+ value = serializers.CharField(help_text="Main value to display")
19
+ icon = serializers.CharField(help_text="Material icon name")
20
+ change = serializers.CharField(required=False, allow_null=True, help_text="Change indicator (e.g., '+12%')")
21
+ change_type = serializers.ChoiceField(
22
+ choices=['positive', 'negative', 'neutral'],
23
+ default='neutral',
24
+ help_text="Change type"
25
+ )
26
+ description = serializers.CharField(required=False, allow_null=True, help_text="Additional description")
27
+ color = serializers.CharField(default='primary', help_text="Card color theme")
28
+
29
+
30
+ class UserStatisticsSerializer(serializers.Serializer):
31
+ """Serializer for user statistics."""
32
+
33
+ total_users = serializers.IntegerField(help_text="Total number of users")
34
+ active_users = serializers.IntegerField(help_text="Active users (last 30 days)")
35
+ new_users = serializers.IntegerField(help_text="New users (last 7 days)")
36
+ superusers = serializers.IntegerField(help_text="Number of superusers")
37
+
38
+
39
+ class AppStatisticsSerializer(serializers.Serializer):
40
+ """Serializer for application-specific statistics."""
41
+
42
+ app_name = serializers.CharField(help_text="Application name")
43
+ statistics = serializers.DictField(
44
+ child=serializers.IntegerField(),
45
+ help_text="Application statistics"
46
+ )
@@ -0,0 +1,58 @@
1
+ """
2
+ System Serializers
3
+
4
+ Serializers for system health and metrics endpoints.
5
+ """
6
+
7
+ from rest_framework import serializers
8
+
9
+
10
+ class SystemHealthItemSerializer(serializers.Serializer):
11
+ """
12
+ Serializer for system health status items.
13
+
14
+ Maps to SystemHealthItem Pydantic model.
15
+ """
16
+
17
+ component = serializers.CharField(help_text="Component name")
18
+ status = serializers.ChoiceField(
19
+ choices=['healthy', 'warning', 'error', 'unknown'],
20
+ help_text="Health status"
21
+ )
22
+ description = serializers.CharField(help_text="Status description")
23
+ last_check = serializers.CharField(help_text="Last check time (ISO format)")
24
+ health_percentage = serializers.IntegerField(
25
+ required=False,
26
+ allow_null=True,
27
+ min_value=0,
28
+ max_value=100,
29
+ help_text="Health percentage (0-100)"
30
+ )
31
+
32
+
33
+ class SystemHealthSerializer(serializers.Serializer):
34
+ """Serializer for overall system health status."""
35
+
36
+ overall_status = serializers.ChoiceField(
37
+ choices=['healthy', 'warning', 'error', 'unknown'],
38
+ help_text="Overall system health status"
39
+ )
40
+ overall_health_percentage = serializers.IntegerField(
41
+ min_value=0,
42
+ max_value=100,
43
+ help_text="Overall health percentage"
44
+ )
45
+ components = SystemHealthItemSerializer(many=True, help_text="Health status of individual components")
46
+ timestamp = serializers.CharField(help_text="Check timestamp (ISO format)")
47
+
48
+
49
+ class SystemMetricsSerializer(serializers.Serializer):
50
+ """Serializer for system performance metrics."""
51
+
52
+ cpu_usage = serializers.FloatField(help_text="CPU usage percentage")
53
+ memory_usage = serializers.FloatField(help_text="Memory usage percentage")
54
+ disk_usage = serializers.FloatField(help_text="Disk usage percentage")
55
+ network_in = serializers.CharField(help_text="Network incoming bandwidth")
56
+ network_out = serializers.CharField(help_text="Network outgoing bandwidth")
57
+ response_time = serializers.CharField(help_text="Average response time")
58
+ uptime = serializers.CharField(help_text="System uptime")
@@ -7,5 +7,14 @@ Services are separated from views/API layer for better testability and reusabili
7
7
 
8
8
  from .statistics_service import StatisticsService
9
9
  from .system_health_service import SystemHealthService
10
+ from .charts_service import ChartsService
11
+ from .commands_service import CommandsService
12
+ from .apizones_service import APIZonesService
10
13
 
11
- __all__ = ['StatisticsService', 'SystemHealthService']
14
+ __all__ = [
15
+ 'StatisticsService',
16
+ 'SystemHealthService',
17
+ 'ChartsService',
18
+ 'CommandsService',
19
+ 'APIZonesService',
20
+ ]
@@ -0,0 +1,119 @@
1
+ """
2
+ API Zones Service
3
+
4
+ OpenAPI zones/groups management and introspection.
5
+ """
6
+
7
+ import logging
8
+ from typing import Any, Dict, List, Tuple
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class APIZonesService:
14
+ """
15
+ Service for OpenAPI zones (groups) management.
16
+
17
+ %%PRIORITY:MEDIUM%%
18
+ %%AI_HINT: Manages OpenAPI schema groups and endpoints%%
19
+
20
+ TAGS: openapi, api, zones, groups, service
21
+ """
22
+
23
+ def __init__(self):
24
+ """Initialize API zones service."""
25
+ self.logger = logger
26
+
27
+ def get_zones_data(self) -> Tuple[List[Dict[str, Any]], Dict[str, Any]]:
28
+ """
29
+ Get OpenAPI zones (groups) data.
30
+
31
+ Returns:
32
+ Tuple of (zones_list, summary_dict)
33
+
34
+ %%AI_HINT: Integrates with django_client module if available%%
35
+ """
36
+ try:
37
+ # Try to import django_client service
38
+ from django_cfg.modules.django_client.core import get_openapi_service
39
+
40
+ service = get_openapi_service()
41
+ if not service.config:
42
+ return [], {"total_apps": 0, "total_endpoints": 0, "total_zones": 0}
43
+
44
+ # Get groups (zones)
45
+ groups_dict = service.get_groups()
46
+ groups_list = list(groups_dict.values())
47
+ api_prefix = getattr(service.config, "api_prefix", "api")
48
+
49
+ zones_data = []
50
+ total_apps = 0
51
+ total_endpoints = 0
52
+
53
+ for group in groups_list:
54
+ # Handle both dict and object access
55
+ if isinstance(group, dict):
56
+ zone_name = group.get("name", "unknown")
57
+ title = group.get("title", zone_name.title())
58
+ description = group.get("description", f"{zone_name} zone")
59
+ apps = group.get("apps", [])
60
+ else:
61
+ zone_name = getattr(group, "name", "unknown")
62
+ title = getattr(group, "title", zone_name.title())
63
+ description = getattr(group, "description", f"{zone_name} zone")
64
+ apps = getattr(group, "apps", [])
65
+
66
+ # Estimate endpoint count
67
+ endpoint_count = len(apps) * 3
68
+
69
+ zones_data.append({
70
+ "name": zone_name,
71
+ "title": title,
72
+ "description": description,
73
+ "app_count": len(apps),
74
+ "endpoint_count": endpoint_count,
75
+ "status": "active" if apps else "empty",
76
+ "schema_url": f"/cfg/openapi/{zone_name}/schema/",
77
+ "api_url": f"/{api_prefix}/{zone_name}/",
78
+ "apps": apps,
79
+ })
80
+
81
+ total_apps += len(apps)
82
+ total_endpoints += endpoint_count
83
+
84
+ summary = {
85
+ "total_apps": total_apps,
86
+ "total_endpoints": total_endpoints,
87
+ "total_zones": len(zones_data),
88
+ }
89
+
90
+ return zones_data, summary
91
+
92
+ except ImportError:
93
+ self.logger.warning("django_client module not available")
94
+ return [], {"total_apps": 0, "total_endpoints": 0, "total_zones": 0}
95
+ except Exception as e:
96
+ self.logger.error(f"Error getting zones data: {e}")
97
+ return [], {"total_apps": 0, "total_endpoints": 0, "total_zones": 0}
98
+
99
+ def get_zones_summary(self) -> Dict[str, Any]:
100
+ """
101
+ Get summary of all API zones.
102
+
103
+ Returns:
104
+ Dictionary with zones list and summary statistics
105
+ """
106
+ try:
107
+ zones_list, summary = self.get_zones_data()
108
+
109
+ return {
110
+ "zones": zones_list,
111
+ "summary": summary,
112
+ }
113
+
114
+ except Exception as e:
115
+ self.logger.error(f"Error getting zones summary: {e}")
116
+ return {
117
+ "zones": [],
118
+ "summary": {"total_apps": 0, "total_endpoints": 0, "total_zones": 0},
119
+ }