django-cfg 1.4.119__py3-none-any.whl → 1.5.1__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 (84) hide show
  1. django_cfg/__init__.py +8 -4
  2. django_cfg/apps/centrifugo/admin/centrifugo_log.py +33 -71
  3. django_cfg/apps/grpc/__init__.py +9 -0
  4. django_cfg/apps/grpc/admin/__init__.py +11 -0
  5. django_cfg/apps/grpc/admin/config.py +89 -0
  6. django_cfg/apps/grpc/admin/grpc_request_log.py +252 -0
  7. django_cfg/apps/grpc/apps.py +28 -0
  8. django_cfg/apps/grpc/auth/__init__.py +9 -0
  9. django_cfg/apps/grpc/auth/jwt_auth.py +295 -0
  10. django_cfg/apps/grpc/interceptors/__init__.py +19 -0
  11. django_cfg/apps/grpc/interceptors/errors.py +241 -0
  12. django_cfg/apps/grpc/interceptors/logging.py +270 -0
  13. django_cfg/apps/grpc/interceptors/metrics.py +306 -0
  14. django_cfg/apps/grpc/interceptors/request_logger.py +515 -0
  15. django_cfg/apps/grpc/management/__init__.py +1 -0
  16. django_cfg/apps/grpc/management/commands/__init__.py +0 -0
  17. django_cfg/apps/grpc/management/commands/rungrpc.py +302 -0
  18. django_cfg/apps/grpc/managers/__init__.py +10 -0
  19. django_cfg/apps/grpc/managers/grpc_request_log.py +310 -0
  20. django_cfg/apps/grpc/migrations/0001_initial.py +69 -0
  21. django_cfg/apps/grpc/migrations/0002_rename_django_cfg__service_4c4a8e_idx_django_cfg__service_584308_idx_and_more.py +38 -0
  22. django_cfg/apps/grpc/migrations/__init__.py +0 -0
  23. django_cfg/apps/grpc/models/__init__.py +9 -0
  24. django_cfg/apps/grpc/models/grpc_request_log.py +219 -0
  25. django_cfg/apps/grpc/serializers/__init__.py +23 -0
  26. django_cfg/apps/grpc/serializers/health.py +18 -0
  27. django_cfg/apps/grpc/serializers/requests.py +18 -0
  28. django_cfg/apps/grpc/serializers/services.py +50 -0
  29. django_cfg/apps/grpc/serializers/stats.py +22 -0
  30. django_cfg/apps/grpc/services/__init__.py +16 -0
  31. django_cfg/apps/grpc/services/base.py +375 -0
  32. django_cfg/apps/grpc/services/discovery.py +415 -0
  33. django_cfg/apps/grpc/urls.py +23 -0
  34. django_cfg/apps/grpc/utils/__init__.py +13 -0
  35. django_cfg/apps/grpc/utils/proto_gen.py +423 -0
  36. django_cfg/apps/grpc/views/__init__.py +9 -0
  37. django_cfg/apps/grpc/views/monitoring.py +497 -0
  38. django_cfg/apps/maintenance/admin/api_key_admin.py +7 -8
  39. django_cfg/apps/maintenance/admin/site_admin.py +5 -4
  40. django_cfg/apps/payments/admin/balance_admin.py +26 -36
  41. django_cfg/apps/payments/admin/payment_admin.py +65 -85
  42. django_cfg/apps/payments/admin/withdrawal_admin.py +65 -100
  43. django_cfg/apps/tasks/admin/task_log.py +20 -47
  44. django_cfg/apps/urls.py +7 -1
  45. django_cfg/config.py +106 -0
  46. django_cfg/core/base/config_model.py +6 -0
  47. django_cfg/core/builders/apps_builder.py +3 -0
  48. django_cfg/core/generation/integration_generators/grpc_generator.py +318 -0
  49. django_cfg/core/generation/orchestrator.py +10 -0
  50. django_cfg/models/api/grpc/__init__.py +59 -0
  51. django_cfg/models/api/grpc/config.py +364 -0
  52. django_cfg/modules/base.py +15 -0
  53. django_cfg/modules/django_admin/__init__.py +2 -0
  54. django_cfg/modules/django_admin/base/pydantic_admin.py +2 -2
  55. django_cfg/modules/django_admin/config/__init__.py +2 -0
  56. django_cfg/modules/django_admin/config/field_config.py +24 -0
  57. django_cfg/modules/django_admin/utils/__init__.py +41 -3
  58. django_cfg/modules/django_admin/utils/badges/__init__.py +13 -0
  59. django_cfg/modules/django_admin/utils/{badges.py → badges/status_badges.py} +3 -3
  60. django_cfg/modules/django_admin/utils/displays/__init__.py +13 -0
  61. django_cfg/modules/django_admin/utils/{displays.py → displays/data_displays.py} +2 -2
  62. django_cfg/modules/django_admin/utils/html/__init__.py +26 -0
  63. django_cfg/modules/django_admin/utils/html/badges.py +47 -0
  64. django_cfg/modules/django_admin/utils/html/base.py +167 -0
  65. django_cfg/modules/django_admin/utils/html/code.py +87 -0
  66. django_cfg/modules/django_admin/utils/html/composition.py +198 -0
  67. django_cfg/modules/django_admin/utils/html/formatting.py +231 -0
  68. django_cfg/modules/django_admin/utils/html/keyvalue.py +219 -0
  69. django_cfg/modules/django_admin/utils/html/markdown_integration.py +108 -0
  70. django_cfg/modules/django_admin/utils/html/progress.py +127 -0
  71. django_cfg/modules/django_admin/utils/html_builder.py +55 -408
  72. django_cfg/modules/django_admin/utils/markdown/__init__.py +21 -0
  73. django_cfg/modules/django_admin/widgets/registry.py +42 -0
  74. django_cfg/modules/django_unfold/navigation.py +28 -0
  75. django_cfg/pyproject.toml +3 -5
  76. django_cfg/registry/modules.py +6 -0
  77. {django_cfg-1.4.119.dist-info → django_cfg-1.5.1.dist-info}/METADATA +10 -1
  78. {django_cfg-1.4.119.dist-info → django_cfg-1.5.1.dist-info}/RECORD +83 -34
  79. django_cfg/modules/django_admin/utils/CODE_BLOCK_DOCS.md +0 -396
  80. /django_cfg/modules/django_admin/utils/{mermaid_plugin.py → markdown/mermaid_plugin.py} +0 -0
  81. /django_cfg/modules/django_admin/utils/{markdown_renderer.py → markdown/renderer.py} +0 -0
  82. {django_cfg-1.4.119.dist-info → django_cfg-1.5.1.dist-info}/WHEEL +0 -0
  83. {django_cfg-1.4.119.dist-info → django_cfg-1.5.1.dist-info}/entry_points.txt +0 -0
  84. {django_cfg-1.4.119.dist-info → django_cfg-1.5.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,219 @@
1
+ """
2
+ Key-value display elements for Django Admin.
3
+
4
+ Provides utilities for displaying key-value pairs, breakdowns, and lists.
5
+ """
6
+
7
+ from typing import Any, List, Optional
8
+
9
+ from django.utils.html import escape, format_html
10
+ from django.utils.safestring import SafeString, mark_safe
11
+
12
+
13
+ class KeyValueElements:
14
+ """Key-value display utilities."""
15
+
16
+ @staticmethod
17
+ def icon(icon_name: str, size: str = "xs") -> SafeString:
18
+ """Helper to get icon (internal use)."""
19
+ from .base import BaseElements
20
+ return BaseElements.icon(icon_name, size)
21
+
22
+ @staticmethod
23
+ def text(content: Any, variant: Optional[str] = None, size: Optional[str] = None) -> SafeString:
24
+ """Helper to get styled text (internal use)."""
25
+ from .base import BaseElements
26
+ return BaseElements.text(content, variant=variant, size=size)
27
+
28
+ @staticmethod
29
+ def key_value(
30
+ key: str,
31
+ value: Any,
32
+ icon: Optional[str] = None,
33
+ indent: bool = False,
34
+ divider: bool = False,
35
+ value_variant: Optional[str] = None,
36
+ value_size: Optional[str] = None
37
+ ) -> SafeString:
38
+ """
39
+ Render single key-value pair.
40
+
41
+ Args:
42
+ key: Key text
43
+ value: Value (can be SafeString from other html methods)
44
+ icon: Material icon name
45
+ indent: Indent the item
46
+ divider: Show divider above
47
+ value_variant: Color variant for value ('success', 'warning', etc)
48
+ value_size: Size for value ('sm', 'base', 'lg')
49
+
50
+ Usage:
51
+ html.key_value('Total', '100 BTC')
52
+ html.key_value('Available', '60 BTC', icon=Icons.CHECK_CIRCLE, indent=True)
53
+ html.key_value('Price', '$1,234', divider=True, value_variant='success', value_size='lg')
54
+
55
+ Returns:
56
+ SafeString with key-value HTML
57
+ """
58
+ # Classes
59
+ classes = ['mb-2']
60
+ if indent:
61
+ classes.append('ml-5')
62
+ if divider:
63
+ classes.append('mt-4 pt-2 border-t border-base-200 dark:border-base-700')
64
+
65
+ # Wrap value if variant or size specified
66
+ if value_variant or value_size:
67
+ value = KeyValueElements.text(value, variant=value_variant, size=value_size)
68
+
69
+ # Build parts
70
+ parts = []
71
+ parts.append(f'<div class="{" ".join(classes)}">')
72
+
73
+ # Icon
74
+ if icon:
75
+ parts.append(str(KeyValueElements.icon(icon, size="xs")))
76
+ parts.append(' ')
77
+
78
+ # Key
79
+ parts.append(f'<span class="font-semibold text-font-default-light dark:text-font-default-dark">{escape(key)}:</span> ')
80
+
81
+ # Value
82
+ parts.append(str(value))
83
+
84
+ parts.append('</div>')
85
+
86
+ return mark_safe(''.join(parts))
87
+
88
+ @staticmethod
89
+ def divider(css_class: str = "my-2") -> SafeString:
90
+ """
91
+ Render horizontal divider line.
92
+
93
+ Args:
94
+ css_class: CSS classes for the hr element
95
+
96
+ Usage:
97
+ html.breakdown(
98
+ section1,
99
+ html.divider(),
100
+ section2,
101
+ )
102
+
103
+ Returns:
104
+ SafeString with hr element
105
+ """
106
+ return format_html('<hr class="{}">', css_class)
107
+
108
+ @staticmethod
109
+ def breakdown(*items: SafeString) -> SafeString:
110
+ """
111
+ Combine multiple key-value pairs into a breakdown section.
112
+
113
+ Args:
114
+ *items: Variable number of SafeStrings (from html.key_value())
115
+
116
+ Usage:
117
+ html.breakdown(
118
+ html.key_value('Total', total_val),
119
+ html.key_value('Available', avail_val, icon=Icons.CHECK_CIRCLE, indent=True),
120
+ html.key_value('Locked', locked_val, icon=Icons.LOCK, indent=True),
121
+ html.key_value('Price', price, divider=True) if has_price else None,
122
+ html.key_value('Total Value', usd_val, value_variant='success', value_size='lg') if has_price else None,
123
+ )
124
+
125
+ Returns:
126
+ SafeString with combined breakdown HTML
127
+ """
128
+ # Filter out None values
129
+ filtered = [str(item) for item in items if item is not None]
130
+
131
+ return mark_safe(''.join(filtered))
132
+
133
+ @staticmethod
134
+ def key_value_list(
135
+ items: List[dict],
136
+ layout: str = "vertical",
137
+ key_width: Optional[str] = None,
138
+ spacing: str = "normal"
139
+ ) -> SafeString:
140
+ """
141
+ Render key-value pairs as a formatted list.
142
+
143
+ Args:
144
+ items: List of dicts with 'key', 'value', and optional keys:
145
+ - icon: Material icon name
146
+ - indent: Boolean for indentation
147
+ - value_class: Tailwind classes for value
148
+ - divider: Boolean to show divider above
149
+ - size: 'sm', 'base', 'lg'
150
+ layout: "vertical" or "horizontal"
151
+ key_width: Fixed width for keys (e.g., "100px") for alignment
152
+ spacing: "tight", "normal", "relaxed"
153
+
154
+ Usage:
155
+ # Simple key-value list
156
+ html.key_value_list([
157
+ {'key': 'Total', 'value': '100 BTC', 'size': 'lg'},
158
+ {'key': 'Available', 'value': '60 BTC', 'indent': True},
159
+ {'key': 'Locked', 'value': '40 BTC', 'indent': True},
160
+ ])
161
+
162
+ # With icons and styling
163
+ html.key_value_list([
164
+ {'key': 'Available', 'value': '60 BTC', 'icon': Icons.CHECK_CIRCLE},
165
+ {'key': 'Total Value', 'value': '$1,234.56', 'value_class': 'text-success-600 text-lg', 'divider': True},
166
+ ])
167
+
168
+ Returns:
169
+ SafeString with formatted key-value list HTML
170
+ """
171
+ spacing_map = {
172
+ 'tight': 'mb-1',
173
+ 'normal': 'mb-2',
174
+ 'relaxed': 'mb-3'
175
+ }
176
+ spacing_class = spacing_map.get(spacing, 'mb-2')
177
+
178
+ parts = []
179
+ for item in items:
180
+ key = item.get('key', '')
181
+ value = item.get('value', '')
182
+ icon = item.get('icon', '')
183
+ indent = item.get('indent', False)
184
+ value_class = item.get('value_class', '')
185
+ divider = item.get('divider', False)
186
+ size = item.get('size', 'base')
187
+
188
+ # Icon HTML
189
+ icon_html = ""
190
+ if icon:
191
+ icon_html = f'{KeyValueElements.icon(icon, size="xs")} '
192
+
193
+ # Size classes
194
+ size_map = {
195
+ 'sm': 'text-sm',
196
+ 'base': 'text-base',
197
+ 'lg': 'text-lg'
198
+ }
199
+ size_class = size_map.get(size, 'text-base')
200
+
201
+ # Classes
202
+ indent_class = 'ml-5' if indent else ''
203
+ divider_class = 'mt-4 pt-2 border-t border-base-200 dark:border-base-700' if divider else ''
204
+
205
+ # Build item HTML
206
+ item_html = format_html(
207
+ '<div class="{} {} {} {}">{}<span class="font-semibold">{}:</span> <span class="{}">{}</span></div>',
208
+ spacing_class,
209
+ indent_class,
210
+ divider_class,
211
+ size_class,
212
+ icon_html,
213
+ escape(key),
214
+ value_class,
215
+ value # Already SafeString from html.number()
216
+ )
217
+ parts.append(item_html)
218
+
219
+ return mark_safe(''.join(str(p) for p in parts))
@@ -0,0 +1,108 @@
1
+ """
2
+ Markdown integration for HTML builder.
3
+
4
+ Provides thin wrapper methods that delegate to MarkdownRenderer.
5
+ """
6
+
7
+ from pathlib import Path
8
+ from typing import Optional, Union
9
+
10
+ from django.utils.safestring import SafeString
11
+
12
+
13
+ class MarkdownIntegration:
14
+ """Markdown rendering integration for HtmlBuilder."""
15
+
16
+ @staticmethod
17
+ def markdown(
18
+ text: str,
19
+ css_class: str = "",
20
+ max_height: Optional[str] = None,
21
+ enable_plugins: bool = True
22
+ ) -> SafeString:
23
+ """
24
+ Render markdown text to beautifully styled HTML.
25
+
26
+ Delegates to MarkdownRenderer.render_markdown() for actual rendering.
27
+
28
+ Args:
29
+ text: Markdown content
30
+ css_class: Additional CSS classes
31
+ max_height: Max height with scrolling (e.g., "400px", "20rem")
32
+ enable_plugins: Enable mistune plugins (tables, strikethrough, etc.)
33
+
34
+ Usage:
35
+ # Simple markdown rendering
36
+ html.markdown("# Hello\\n\\nThis is **bold** text")
37
+
38
+ # With custom styling
39
+ html.markdown(obj.description, css_class="my-custom-class")
40
+
41
+ # With max height for long content
42
+ html.markdown(obj.documentation, max_height="500px")
43
+
44
+ Returns:
45
+ SafeString with rendered HTML
46
+ """
47
+ # Import here to avoid circular dependency
48
+ from ..markdown.renderer import MarkdownRenderer
49
+
50
+ return MarkdownRenderer.render_markdown(
51
+ text=text,
52
+ css_class=css_class,
53
+ max_height=max_height,
54
+ enable_plugins=enable_plugins
55
+ )
56
+
57
+ @staticmethod
58
+ def markdown_docs(
59
+ content: Union[str, Path],
60
+ collapsible: bool = True,
61
+ title: str = "Documentation",
62
+ icon: str = "description",
63
+ max_height: Optional[str] = "500px",
64
+ enable_plugins: bool = True,
65
+ default_open: bool = False
66
+ ) -> SafeString:
67
+ """
68
+ Render markdown documentation from string or file with collapsible UI.
69
+
70
+ Auto-detects whether content is a file path or markdown string.
71
+
72
+ Args:
73
+ content: Markdown string or path to .md file
74
+ collapsible: Wrap in collapsible details/summary
75
+ title: Title for collapsible section
76
+ icon: Material icon name for title
77
+ max_height: Max height for scrolling
78
+ enable_plugins: Enable markdown plugins
79
+ default_open: Open by default if collapsible
80
+
81
+ Usage:
82
+ # From string with collapse
83
+ html.markdown_docs(obj.description, title="Description")
84
+
85
+ # From file
86
+ html.markdown_docs("docs/api.md", title="API Documentation")
87
+
88
+ # Simple, no collapse
89
+ html.markdown_docs(obj.notes, collapsible=False)
90
+
91
+ # Open by default
92
+ html.markdown_docs(obj.readme, default_open=True)
93
+
94
+ Returns:
95
+ Rendered markdown with beautiful Tailwind styling
96
+ """
97
+ # Import here to avoid circular dependency
98
+ from ..markdown.renderer import MarkdownRenderer
99
+
100
+ return MarkdownRenderer.render(
101
+ content=content,
102
+ collapsible=collapsible,
103
+ title=title,
104
+ icon=icon,
105
+ max_height=max_height,
106
+ enable_plugins=enable_plugins,
107
+ default_open=default_open
108
+ )
@@ -0,0 +1,127 @@
1
+ """
2
+ Progress bar elements for Django Admin.
3
+
4
+ Provides progress bar rendering with multiple colored segments.
5
+ """
6
+
7
+ from django.utils.html import escape
8
+ from django.utils.safestring import SafeString, mark_safe
9
+
10
+
11
+ class ProgressElements:
12
+ """Progress bar display elements."""
13
+
14
+ @staticmethod
15
+ def segment(percentage: float, variant: str = 'primary', label: str = ''):
16
+ """
17
+ Create progress bar segment with named parameters.
18
+
19
+ Args:
20
+ percentage: Percentage value (0-100)
21
+ variant: Color variant ('success', 'warning', 'danger', 'info', 'primary')
22
+ label: Label text
23
+
24
+ Usage:
25
+ html.segment(percentage=60, variant='success', label='Available')
26
+
27
+ Returns:
28
+ dict with segment data
29
+ """
30
+ return {'percentage': percentage, 'variant': variant, 'label': label}
31
+
32
+ @staticmethod
33
+ def progress_bar(
34
+ *segments,
35
+ width: str = "w-full max-w-xs",
36
+ height: str = "h-6",
37
+ show_labels: bool = True,
38
+ rounded: bool = True
39
+ ) -> SafeString:
40
+ """
41
+ Render progress bar with multiple colored segments using Tailwind.
42
+
43
+ Args:
44
+ *segments: Variable number of segment dicts (from html.segment())
45
+ width: Tailwind width classes (default: "w-full max-w-xs")
46
+ height: Tailwind height class (default: "h-6" = 24px for visibility)
47
+ show_labels: Show percentage labels below the bar
48
+ rounded: Use rounded corners
49
+
50
+ Usage:
51
+ html.progress_bar(
52
+ html.segment(percentage=60, variant='success', label='Available'),
53
+ html.segment(percentage=40, variant='warning', label='Locked')
54
+ )
55
+
56
+ Returns:
57
+ SafeString with progress bar HTML
58
+ """
59
+ # Standard Tailwind colors with dark mode (работают всегда!)
60
+ # Progress bars need visible contrast
61
+ variant_bg_map = {
62
+ 'success': 'bg-green-600 dark:bg-green-500',
63
+ 'warning': 'bg-yellow-600 dark:bg-yellow-500',
64
+ 'danger': 'bg-red-600 dark:bg-red-500',
65
+ 'info': 'bg-blue-600 dark:bg-blue-500',
66
+ 'primary': 'bg-primary-600 dark:bg-primary-500',
67
+ 'secondary': 'bg-gray-400 dark:bg-gray-500',
68
+ }
69
+
70
+ # Standard Tailwind text colors with dark mode
71
+ variant_text_map = {
72
+ 'success': 'text-green-700 dark:text-green-300',
73
+ 'warning': 'text-yellow-700 dark:text-yellow-300',
74
+ 'danger': 'text-red-700 dark:text-red-300',
75
+ 'info': 'text-blue-700 dark:text-blue-300',
76
+ 'primary': 'text-primary-700 dark:text-primary-300',
77
+ 'secondary': 'text-gray-600 dark:text-gray-400',
78
+ }
79
+
80
+ # Build segments HTML
81
+ segments_html = []
82
+ for seg in segments:
83
+ pct = seg['percentage']
84
+ variant = seg['variant']
85
+ bg_class = variant_bg_map.get(variant, 'bg-base-200 dark:bg-base-700')
86
+
87
+ if pct > 0:
88
+ segments_html.append(
89
+ f'<div class="{bg_class}" style="width: {pct}%; height: 100%;"></div>'
90
+ )
91
+
92
+ # Build labels HTML
93
+ labels_html = ""
94
+ if show_labels:
95
+ label_parts = []
96
+ for seg in segments:
97
+ pct = seg['percentage']
98
+ variant = seg['variant']
99
+ label = seg['label']
100
+ text_class = variant_text_map.get(variant, 'text-base-600')
101
+
102
+ if pct > 0 or label:
103
+ label_parts.append(
104
+ f'<span class="{text_class}">{escape(label)}: {pct:.1f}%</span>'
105
+ )
106
+
107
+ if label_parts:
108
+ labels_html = (
109
+ f'<div class="flex justify-between mt-1 text-xs">'
110
+ f'{"".join(label_parts)}'
111
+ f'</div>'
112
+ )
113
+
114
+ # Rounded class
115
+ rounded_class = 'rounded-lg' if rounded else ''
116
+
117
+ # Combine
118
+ html = (
119
+ f'<div class="{width}">'
120
+ f'<div class="bg-base-100 dark:bg-base-800 {height} {rounded_class} overflow-hidden flex">'
121
+ f'{"".join(segments_html)}'
122
+ f'</div>'
123
+ f'{labels_html}'
124
+ f'</div>'
125
+ )
126
+
127
+ return mark_safe(html)