django-cfg 1.4.120__py3-none-any.whl → 1.5.2__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.
- django_cfg/__init__.py +8 -4
- django_cfg/apps/centrifugo/admin/centrifugo_log.py +33 -71
- django_cfg/apps/dashboard/TRANSACTION_FIX.md +73 -0
- django_cfg/apps/dashboard/serializers/__init__.py +0 -12
- django_cfg/apps/dashboard/serializers/activity.py +1 -1
- django_cfg/apps/dashboard/services/__init__.py +0 -2
- django_cfg/apps/dashboard/services/charts_service.py +4 -3
- django_cfg/apps/dashboard/services/statistics_service.py +11 -2
- django_cfg/apps/dashboard/services/system_health_service.py +64 -106
- django_cfg/apps/dashboard/urls.py +0 -2
- django_cfg/apps/dashboard/views/__init__.py +0 -2
- django_cfg/apps/dashboard/views/commands_views.py +3 -6
- django_cfg/apps/dashboard/views/overview_views.py +14 -13
- django_cfg/apps/grpc/__init__.py +9 -0
- django_cfg/apps/grpc/admin/__init__.py +11 -0
- django_cfg/apps/{tasks → grpc}/admin/config.py +32 -41
- django_cfg/apps/grpc/admin/grpc_request_log.py +252 -0
- django_cfg/apps/grpc/apps.py +28 -0
- django_cfg/apps/grpc/auth/__init__.py +9 -0
- django_cfg/apps/grpc/auth/jwt_auth.py +295 -0
- django_cfg/apps/grpc/interceptors/__init__.py +19 -0
- django_cfg/apps/grpc/interceptors/errors.py +241 -0
- django_cfg/apps/grpc/interceptors/logging.py +270 -0
- django_cfg/apps/grpc/interceptors/metrics.py +306 -0
- django_cfg/apps/grpc/interceptors/request_logger.py +515 -0
- django_cfg/apps/grpc/management/__init__.py +1 -0
- django_cfg/apps/grpc/management/commands/rungrpc.py +302 -0
- django_cfg/apps/grpc/managers/__init__.py +10 -0
- django_cfg/apps/grpc/managers/grpc_request_log.py +310 -0
- django_cfg/apps/grpc/migrations/0001_initial.py +69 -0
- django_cfg/apps/grpc/migrations/0002_rename_django_cfg__service_4c4a8e_idx_django_cfg__service_584308_idx_and_more.py +38 -0
- django_cfg/apps/grpc/models/__init__.py +9 -0
- django_cfg/apps/grpc/models/grpc_request_log.py +219 -0
- django_cfg/apps/grpc/serializers/__init__.py +23 -0
- django_cfg/apps/grpc/serializers/health.py +18 -0
- django_cfg/apps/grpc/serializers/requests.py +18 -0
- django_cfg/apps/grpc/serializers/services.py +50 -0
- django_cfg/apps/grpc/serializers/stats.py +22 -0
- django_cfg/apps/grpc/services/__init__.py +16 -0
- django_cfg/apps/grpc/services/base.py +375 -0
- django_cfg/apps/grpc/services/discovery.py +415 -0
- django_cfg/apps/grpc/urls.py +23 -0
- django_cfg/apps/grpc/utils/__init__.py +13 -0
- django_cfg/apps/grpc/utils/proto_gen.py +423 -0
- django_cfg/apps/grpc/views/__init__.py +9 -0
- django_cfg/apps/grpc/views/monitoring.py +497 -0
- django_cfg/apps/knowbase/apps.py +2 -2
- django_cfg/apps/maintenance/admin/api_key_admin.py +7 -9
- django_cfg/apps/maintenance/admin/site_admin.py +5 -4
- django_cfg/apps/newsletter/admin/newsletter_admin.py +12 -11
- django_cfg/apps/payments/admin/balance_admin.py +26 -36
- django_cfg/apps/payments/admin/payment_admin.py +65 -85
- django_cfg/apps/payments/admin/withdrawal_admin.py +65 -100
- django_cfg/apps/rq/__init__.py +9 -0
- django_cfg/apps/rq/apps.py +80 -0
- django_cfg/apps/rq/management/__init__.py +1 -0
- django_cfg/apps/rq/management/commands/__init__.py +1 -0
- django_cfg/apps/rq/management/commands/rqscheduler.py +31 -0
- django_cfg/apps/rq/management/commands/rqstats.py +33 -0
- django_cfg/apps/rq/management/commands/rqworker.py +31 -0
- django_cfg/apps/rq/management/commands/rqworker_pool.py +27 -0
- django_cfg/apps/rq/serializers/__init__.py +40 -0
- django_cfg/apps/rq/serializers/health.py +60 -0
- django_cfg/apps/rq/serializers/job.py +100 -0
- django_cfg/apps/rq/serializers/queue.py +80 -0
- django_cfg/apps/rq/serializers/schedule.py +178 -0
- django_cfg/apps/rq/serializers/testing.py +139 -0
- django_cfg/apps/rq/serializers/worker.py +58 -0
- django_cfg/apps/rq/services/__init__.py +25 -0
- django_cfg/apps/rq/services/config_helper.py +233 -0
- django_cfg/apps/rq/services/models/README.md +417 -0
- django_cfg/apps/rq/services/models/__init__.py +30 -0
- django_cfg/apps/rq/services/models/event.py +123 -0
- django_cfg/apps/rq/services/models/job.py +99 -0
- django_cfg/apps/rq/services/models/queue.py +92 -0
- django_cfg/apps/rq/services/models/worker.py +104 -0
- django_cfg/apps/rq/services/rq_converters.py +183 -0
- django_cfg/apps/rq/tasks/__init__.py +23 -0
- django_cfg/apps/rq/tasks/demo_tasks.py +284 -0
- django_cfg/apps/rq/urls.py +54 -0
- django_cfg/apps/rq/views/__init__.py +19 -0
- django_cfg/apps/rq/views/jobs.py +882 -0
- django_cfg/apps/rq/views/monitoring.py +248 -0
- django_cfg/apps/rq/views/queues.py +261 -0
- django_cfg/apps/rq/views/schedule.py +400 -0
- django_cfg/apps/rq/views/testing.py +761 -0
- django_cfg/apps/rq/views/workers.py +195 -0
- django_cfg/apps/urls.py +13 -8
- django_cfg/config.py +106 -0
- django_cfg/core/base/config_model.py +16 -26
- django_cfg/core/builders/apps_builder.py +7 -11
- django_cfg/core/generation/integration_generators/__init__.py +3 -6
- django_cfg/core/generation/integration_generators/django_rq.py +80 -0
- django_cfg/core/generation/integration_generators/grpc_generator.py +318 -0
- django_cfg/core/generation/orchestrator.py +15 -15
- django_cfg/core/integration/display/startup.py +6 -20
- django_cfg/mixins/__init__.py +2 -0
- django_cfg/mixins/superadmin_api.py +59 -0
- django_cfg/models/__init__.py +3 -3
- django_cfg/models/api/grpc/__init__.py +59 -0
- django_cfg/models/api/grpc/config.py +364 -0
- django_cfg/models/django/__init__.py +3 -3
- django_cfg/models/django/django_rq.py +621 -0
- django_cfg/models/django/revolution_legacy.py +1 -1
- django_cfg/modules/base.py +19 -6
- django_cfg/modules/django_admin/base/pydantic_admin.py +2 -2
- django_cfg/modules/django_admin/config/background_task_config.py +4 -4
- django_cfg/modules/django_admin/utils/__init__.py +41 -3
- django_cfg/modules/django_admin/utils/badges/__init__.py +13 -0
- django_cfg/modules/django_admin/utils/{badges.py → badges/status_badges.py} +3 -3
- django_cfg/modules/django_admin/utils/displays/__init__.py +13 -0
- django_cfg/modules/django_admin/utils/{displays.py → displays/data_displays.py} +2 -2
- django_cfg/modules/django_admin/utils/html/__init__.py +26 -0
- django_cfg/modules/django_admin/utils/html/badges.py +47 -0
- django_cfg/modules/django_admin/utils/html/base.py +167 -0
- django_cfg/modules/django_admin/utils/html/code.py +87 -0
- django_cfg/modules/django_admin/utils/html/composition.py +205 -0
- django_cfg/modules/django_admin/utils/html/formatting.py +231 -0
- django_cfg/modules/django_admin/utils/html/keyvalue.py +219 -0
- django_cfg/modules/django_admin/utils/html/markdown_integration.py +108 -0
- django_cfg/modules/django_admin/utils/html/progress.py +127 -0
- django_cfg/modules/django_admin/utils/html_builder.py +55 -408
- django_cfg/modules/django_admin/utils/markdown/__init__.py +21 -0
- django_cfg/modules/django_unfold/navigation.py +21 -18
- django_cfg/pyproject.toml +4 -6
- django_cfg/registry/core.py +4 -7
- django_cfg/registry/modules.py +6 -0
- django_cfg/static/frontend/admin.zip +0 -0
- django_cfg/templates/admin/constance/includes/results_list.html +73 -0
- django_cfg/templates/admin/index.html +187 -62
- django_cfg/templatetags/django_cfg.py +61 -1
- {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/METADATA +12 -4
- {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/RECORD +140 -96
- django_cfg/apps/dashboard/permissions.py +0 -48
- django_cfg/apps/dashboard/serializers/django_q2.py +0 -50
- django_cfg/apps/dashboard/services/django_q2_service.py +0 -159
- django_cfg/apps/dashboard/views/django_q2_views.py +0 -79
- django_cfg/apps/tasks/__init__.py +0 -64
- django_cfg/apps/tasks/admin/__init__.py +0 -4
- django_cfg/apps/tasks/admin/task_log.py +0 -265
- django_cfg/apps/tasks/apps.py +0 -15
- django_cfg/apps/tasks/filters/__init__.py +0 -10
- django_cfg/apps/tasks/filters/task_log.py +0 -121
- django_cfg/apps/tasks/migrations/0001_initial.py +0 -196
- django_cfg/apps/tasks/migrations/0002_delete_tasklog.py +0 -16
- django_cfg/apps/tasks/models/__init__.py +0 -4
- django_cfg/apps/tasks/models/task_log.py +0 -246
- django_cfg/apps/tasks/serializers/__init__.py +0 -28
- django_cfg/apps/tasks/serializers/task_log.py +0 -249
- django_cfg/apps/tasks/services/__init__.py +0 -10
- django_cfg/apps/tasks/services/client/__init__.py +0 -7
- django_cfg/apps/tasks/services/client/client.py +0 -234
- django_cfg/apps/tasks/services/config_helper.py +0 -63
- django_cfg/apps/tasks/services/sync.py +0 -204
- django_cfg/apps/tasks/urls.py +0 -16
- django_cfg/apps/tasks/views/__init__.py +0 -10
- django_cfg/apps/tasks/views/task_log.py +0 -41
- django_cfg/apps/tasks/views/task_log_base.py +0 -41
- django_cfg/apps/tasks/views/task_log_overview.py +0 -100
- django_cfg/apps/tasks/views/task_log_related.py +0 -41
- django_cfg/apps/tasks/views/task_log_stats.py +0 -91
- django_cfg/apps/tasks/views/task_log_timeline.py +0 -81
- django_cfg/core/generation/integration_generators/django_q2.py +0 -133
- django_cfg/core/generation/integration_generators/tasks.py +0 -88
- django_cfg/models/django/django_q2.py +0 -514
- django_cfg/models/tasks/__init__.py +0 -49
- django_cfg/models/tasks/backends.py +0 -122
- django_cfg/models/tasks/config.py +0 -209
- django_cfg/models/tasks/utils.py +0 -162
- django_cfg/modules/django_admin/utils/CODE_BLOCK_DOCS.md +0 -396
- django_cfg/modules/django_q2/README.md +0 -140
- django_cfg/modules/django_q2/__init__.py +0 -8
- django_cfg/modules/django_q2/apps.py +0 -107
- django_cfg/modules/django_q2/management/commands/__init__.py +0 -0
- django_cfg/modules/django_q2/management/commands/sync_django_q_schedules.py +0 -74
- /django_cfg/apps/{tasks/migrations → grpc/management/commands}/__init__.py +0 -0
- /django_cfg/{modules/django_q2/management → apps/grpc/migrations}/__init__.py +0 -0
- /django_cfg/modules/django_admin/utils/{mermaid_plugin.py → markdown/mermaid_plugin.py} +0 -0
- /django_cfg/modules/django_admin/utils/{markdown_renderer.py → markdown/renderer.py} +0 -0
- {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -18,7 +18,7 @@ class BackgroundTaskConfig(BaseModel):
|
|
|
18
18
|
```python
|
|
19
19
|
background_config = BackgroundTaskConfig(
|
|
20
20
|
enabled=True,
|
|
21
|
-
task_runner='
|
|
21
|
+
task_runner='django_rq',
|
|
22
22
|
batch_size=100,
|
|
23
23
|
timeout=300,
|
|
24
24
|
)
|
|
@@ -35,9 +35,9 @@ class BackgroundTaskConfig(BaseModel):
|
|
|
35
35
|
description="Enable background task processing"
|
|
36
36
|
)
|
|
37
37
|
|
|
38
|
-
task_runner: Literal['
|
|
39
|
-
'
|
|
40
|
-
description="Task runner to use for background operations"
|
|
38
|
+
task_runner: Literal['django_rq', 'sync'] = Field(
|
|
39
|
+
'sync',
|
|
40
|
+
description="Task runner to use for background operations (django_rq or sync)"
|
|
41
41
|
)
|
|
42
42
|
|
|
43
43
|
batch_size: int = Field(
|
|
@@ -1,17 +1,46 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Display and
|
|
2
|
+
Display and utility modules for Django Admin.
|
|
3
|
+
|
|
4
|
+
Refactored structure with logical organization:
|
|
5
|
+
- badges/ - Status badges, progress badges, counter badges
|
|
6
|
+
- displays/ - User displays, money displays, datetime displays
|
|
7
|
+
- html/ - HTML building utilities (organized by functionality)
|
|
8
|
+
- markdown/ - Markdown rendering with Mermaid support
|
|
9
|
+
- decorators.py - Admin field decorators
|
|
10
|
+
- html_builder.py - Backward-compatible facade for self.html.* API
|
|
3
11
|
"""
|
|
4
12
|
|
|
13
|
+
# Badges
|
|
5
14
|
from .badges import CounterBadge, ProgressBadge, StatusBadge
|
|
15
|
+
|
|
16
|
+
# Decorators
|
|
6
17
|
from .decorators import (
|
|
7
18
|
annotated_field,
|
|
8
19
|
badge_field,
|
|
9
20
|
computed_field,
|
|
10
21
|
currency_field,
|
|
11
22
|
)
|
|
23
|
+
|
|
24
|
+
# Displays
|
|
12
25
|
from .displays import DateTimeDisplay, MoneyDisplay, UserDisplay
|
|
26
|
+
|
|
27
|
+
# HTML Builders (organized by functionality)
|
|
28
|
+
from .html import (
|
|
29
|
+
BadgeElements,
|
|
30
|
+
BaseElements,
|
|
31
|
+
CodeElements,
|
|
32
|
+
CompositionElements,
|
|
33
|
+
FormattingElements,
|
|
34
|
+
KeyValueElements,
|
|
35
|
+
MarkdownIntegration,
|
|
36
|
+
ProgressElements,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# HtmlBuilder - Backward-compatible facade
|
|
13
40
|
from .html_builder import HtmlBuilder
|
|
14
|
-
|
|
41
|
+
|
|
42
|
+
# Markdown
|
|
43
|
+
from .markdown import MarkdownRenderer
|
|
15
44
|
|
|
16
45
|
__all__ = [
|
|
17
46
|
# Display utilities
|
|
@@ -22,7 +51,16 @@ __all__ = [
|
|
|
22
51
|
"StatusBadge",
|
|
23
52
|
"ProgressBadge",
|
|
24
53
|
"CounterBadge",
|
|
25
|
-
# HTML
|
|
54
|
+
# HTML Builders (modular)
|
|
55
|
+
"BaseElements",
|
|
56
|
+
"CodeElements",
|
|
57
|
+
"BadgeElements",
|
|
58
|
+
"CompositionElements",
|
|
59
|
+
"FormattingElements",
|
|
60
|
+
"KeyValueElements",
|
|
61
|
+
"ProgressElements",
|
|
62
|
+
"MarkdownIntegration",
|
|
63
|
+
# HTML Builder (backward-compatible facade)
|
|
26
64
|
"HtmlBuilder",
|
|
27
65
|
# Markdown Renderer
|
|
28
66
|
"MarkdownRenderer",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Badge utilities for Django Admin.
|
|
3
|
+
|
|
4
|
+
Provides StatusBadge, ProgressBadge, and CounterBadge classes.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .status_badges import CounterBadge, ProgressBadge, StatusBadge
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"StatusBadge",
|
|
11
|
+
"ProgressBadge",
|
|
12
|
+
"CounterBadge",
|
|
13
|
+
]
|
|
@@ -9,9 +9,9 @@ from django.contrib.humanize.templatetags.humanize import intcomma
|
|
|
9
9
|
from django.utils.html import escape, format_html
|
|
10
10
|
from django.utils.safestring import SafeString
|
|
11
11
|
|
|
12
|
-
from
|
|
13
|
-
from
|
|
14
|
-
from
|
|
12
|
+
from ...icons import Icons
|
|
13
|
+
from ...models.badge_models import StatusBadgeConfig
|
|
14
|
+
from ...models.base import BadgeVariant
|
|
15
15
|
|
|
16
16
|
logger = logging.getLogger(__name__)
|
|
17
17
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Display utilities for Django Admin.
|
|
3
|
+
|
|
4
|
+
Provides UserDisplay, MoneyDisplay, and DateTimeDisplay classes.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .data_displays import DateTimeDisplay, MoneyDisplay, UserDisplay
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"UserDisplay",
|
|
11
|
+
"MoneyDisplay",
|
|
12
|
+
"DateTimeDisplay",
|
|
13
|
+
]
|
|
@@ -12,8 +12,8 @@ from django.utils import timezone
|
|
|
12
12
|
from django.utils.html import escape, format_html
|
|
13
13
|
from django.utils.safestring import SafeString
|
|
14
14
|
|
|
15
|
-
from
|
|
16
|
-
from
|
|
15
|
+
from ...icons import Icons
|
|
16
|
+
from ...models.display_models import DateTimeDisplayConfig, MoneyDisplayConfig, UserDisplayConfig
|
|
17
17
|
|
|
18
18
|
logger = logging.getLogger(__name__)
|
|
19
19
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HTML builder module - organized and modular.
|
|
3
|
+
|
|
4
|
+
Exports all HTML building classes for easy access.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .badges import BadgeElements
|
|
8
|
+
from .base import BaseElements
|
|
9
|
+
from .code import CodeElements
|
|
10
|
+
from .composition import CompositionElements
|
|
11
|
+
from .formatting import FormattingElements
|
|
12
|
+
from .keyvalue import KeyValueElements
|
|
13
|
+
from .markdown_integration import MarkdownIntegration
|
|
14
|
+
from .progress import ProgressElements
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
# Core elements
|
|
18
|
+
"BaseElements",
|
|
19
|
+
"CodeElements",
|
|
20
|
+
"BadgeElements",
|
|
21
|
+
"CompositionElements",
|
|
22
|
+
"FormattingElements",
|
|
23
|
+
"KeyValueElements",
|
|
24
|
+
"ProgressElements",
|
|
25
|
+
"MarkdownIntegration",
|
|
26
|
+
]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Badge elements for Django Admin.
|
|
3
|
+
|
|
4
|
+
Provides badge rendering with variants and icons.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
from django.utils.html import escape, format_html
|
|
10
|
+
from django.utils.safestring import SafeString
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BadgeElements:
|
|
14
|
+
"""Badge display elements."""
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def badge(text: any, variant: str = "primary", icon: Optional[str] = None) -> SafeString:
|
|
18
|
+
"""
|
|
19
|
+
Render badge with optional icon.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
text: Badge text
|
|
23
|
+
variant: primary, success, warning, danger, info, secondary
|
|
24
|
+
icon: Optional Material Icon
|
|
25
|
+
|
|
26
|
+
Usage:
|
|
27
|
+
html.badge("Active", variant="success", icon=Icons.CHECK_CIRCLE)
|
|
28
|
+
"""
|
|
29
|
+
variant_classes = {
|
|
30
|
+
'success': 'bg-success-100 text-success-800 dark:bg-success-900 dark:text-success-200',
|
|
31
|
+
'warning': 'bg-warning-100 text-warning-800 dark:bg-warning-900 dark:text-warning-200',
|
|
32
|
+
'danger': 'bg-danger-100 text-danger-800 dark:bg-danger-900 dark:text-danger-200',
|
|
33
|
+
'info': 'bg-info-100 text-info-800 dark:bg-info-900 dark:text-info-200',
|
|
34
|
+
'primary': 'bg-primary-100 text-primary-800 dark:bg-primary-900 dark:text-primary-200',
|
|
35
|
+
'secondary': 'bg-base-100 text-font-default-light dark:bg-base-800 dark:text-font-default-dark',
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
css_classes = variant_classes.get(variant, variant_classes['primary'])
|
|
39
|
+
|
|
40
|
+
icon_html = ""
|
|
41
|
+
if icon:
|
|
42
|
+
icon_html = format_html('<span class="material-symbols-outlined text-xs mr-1">{}</span>', icon)
|
|
43
|
+
|
|
44
|
+
return format_html(
|
|
45
|
+
'<span class="inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium {}">{}{}</span>',
|
|
46
|
+
css_classes, icon_html, escape(str(text))
|
|
47
|
+
)
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Basic HTML elements for Django Admin.
|
|
3
|
+
|
|
4
|
+
Provides fundamental HTML building blocks: icons, spans, text, divs, links, and empty placeholders.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Optional
|
|
8
|
+
|
|
9
|
+
from django.utils.html import escape, format_html
|
|
10
|
+
from django.utils.safestring import SafeString
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BaseElements:
|
|
14
|
+
"""Basic HTML building blocks."""
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def icon(icon_name: str, size: str = "xs", css_class: str = "") -> SafeString:
|
|
18
|
+
"""
|
|
19
|
+
Render Material Icon.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
icon_name: Icon name from Icons class
|
|
23
|
+
size: xs, sm, base, lg, xl
|
|
24
|
+
css_class: Additional CSS classes
|
|
25
|
+
"""
|
|
26
|
+
size_classes = {
|
|
27
|
+
'xs': 'text-xs',
|
|
28
|
+
'sm': 'text-sm',
|
|
29
|
+
'base': 'text-base',
|
|
30
|
+
'lg': 'text-lg',
|
|
31
|
+
'xl': 'text-xl'
|
|
32
|
+
}
|
|
33
|
+
size_class = size_classes.get(size, 'text-xs')
|
|
34
|
+
classes = f"material-symbols-outlined {size_class}"
|
|
35
|
+
if css_class:
|
|
36
|
+
classes += f" {css_class}"
|
|
37
|
+
|
|
38
|
+
return format_html('<span class="{}">{}</span>', classes, icon_name)
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def span(text: Any, css_class: str = "") -> SafeString:
|
|
42
|
+
"""
|
|
43
|
+
Render text in span with optional CSS class.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
text: Text to display
|
|
47
|
+
css_class: CSS classes
|
|
48
|
+
"""
|
|
49
|
+
if css_class:
|
|
50
|
+
return format_html('<span class="{}">{}</span>', css_class, escape(str(text)))
|
|
51
|
+
return format_html('<span>{}</span>', escape(str(text)))
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def text(
|
|
55
|
+
content: Any,
|
|
56
|
+
variant: Optional[str] = None,
|
|
57
|
+
size: Optional[str] = None,
|
|
58
|
+
weight: Optional[str] = None,
|
|
59
|
+
muted: bool = False
|
|
60
|
+
) -> SafeString:
|
|
61
|
+
"""
|
|
62
|
+
Render styled text with semantic variants.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
content: Text content (can be SafeString from other html methods)
|
|
66
|
+
variant: Color variant - 'success', 'warning', 'danger', 'info', 'primary'
|
|
67
|
+
size: Size - 'xs', 'sm', 'base', 'lg', 'xl', '2xl'
|
|
68
|
+
weight: Font weight - 'normal', 'medium', 'semibold', 'bold'
|
|
69
|
+
muted: Use muted/subtle color
|
|
70
|
+
|
|
71
|
+
Usage:
|
|
72
|
+
# Success text
|
|
73
|
+
html.text("$1,234.56", variant="success", size="lg")
|
|
74
|
+
|
|
75
|
+
# Muted small text
|
|
76
|
+
html.text("(12.5%)", muted=True, size="sm")
|
|
77
|
+
|
|
78
|
+
# Combined with other methods
|
|
79
|
+
total = html.number(1234.56, prefix="$")
|
|
80
|
+
html.text(total, variant="success", size="lg")
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
SafeString with styled text
|
|
84
|
+
"""
|
|
85
|
+
classes = []
|
|
86
|
+
|
|
87
|
+
# Variant colors
|
|
88
|
+
if variant:
|
|
89
|
+
variant_classes = {
|
|
90
|
+
'success': 'text-success-600 dark:text-success-400',
|
|
91
|
+
'warning': 'text-warning-600 dark:text-warning-400',
|
|
92
|
+
'danger': 'text-danger-600 dark:text-danger-400',
|
|
93
|
+
'info': 'text-info-600 dark:text-info-400',
|
|
94
|
+
'primary': 'text-primary-600 dark:text-primary-400',
|
|
95
|
+
}
|
|
96
|
+
classes.append(variant_classes.get(variant, ''))
|
|
97
|
+
|
|
98
|
+
# Muted
|
|
99
|
+
if muted:
|
|
100
|
+
classes.append('text-font-subtle-light dark:text-font-subtle-dark')
|
|
101
|
+
|
|
102
|
+
# Size
|
|
103
|
+
if size:
|
|
104
|
+
size_classes = {
|
|
105
|
+
'xs': 'text-xs',
|
|
106
|
+
'sm': 'text-sm',
|
|
107
|
+
'base': 'text-base',
|
|
108
|
+
'lg': 'text-lg',
|
|
109
|
+
'xl': 'text-xl',
|
|
110
|
+
'2xl': 'text-2xl',
|
|
111
|
+
}
|
|
112
|
+
classes.append(size_classes.get(size, ''))
|
|
113
|
+
|
|
114
|
+
# Weight
|
|
115
|
+
if weight:
|
|
116
|
+
weight_classes = {
|
|
117
|
+
'normal': 'font-normal',
|
|
118
|
+
'medium': 'font-medium',
|
|
119
|
+
'semibold': 'font-semibold',
|
|
120
|
+
'bold': 'font-bold',
|
|
121
|
+
}
|
|
122
|
+
classes.append(weight_classes.get(weight, ''))
|
|
123
|
+
|
|
124
|
+
css_class = ' '.join(filter(None, classes))
|
|
125
|
+
|
|
126
|
+
if css_class:
|
|
127
|
+
return format_html('<span class="{}">{}</span>', css_class, content)
|
|
128
|
+
return format_html('<span>{}</span>', content)
|
|
129
|
+
|
|
130
|
+
@staticmethod
|
|
131
|
+
def div(content: Any, css_class: str = "") -> SafeString:
|
|
132
|
+
"""
|
|
133
|
+
Render content in div with optional CSS class.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
content: Content to display (can be SafeString)
|
|
137
|
+
css_class: CSS classes
|
|
138
|
+
"""
|
|
139
|
+
if css_class:
|
|
140
|
+
return format_html('<div class="{}">{}</div>', css_class, content)
|
|
141
|
+
return format_html('<div>{}</div>', content)
|
|
142
|
+
|
|
143
|
+
@staticmethod
|
|
144
|
+
def link(url: str, text: str, css_class: str = "", target: str = "") -> SafeString:
|
|
145
|
+
"""
|
|
146
|
+
Render link.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
url: URL
|
|
150
|
+
text: Link text
|
|
151
|
+
css_class: CSS classes
|
|
152
|
+
target: Target attribute (_blank, _self, etc)
|
|
153
|
+
"""
|
|
154
|
+
if target:
|
|
155
|
+
return format_html(
|
|
156
|
+
'<a href="{}" class="{}" target="{}">{}</a>',
|
|
157
|
+
url, css_class, target, escape(text)
|
|
158
|
+
)
|
|
159
|
+
return format_html('<a href="{}" class="{}">{}</a>', url, css_class, escape(text))
|
|
160
|
+
|
|
161
|
+
@staticmethod
|
|
162
|
+
def empty(text: str = "—") -> SafeString:
|
|
163
|
+
"""Render empty/placeholder value."""
|
|
164
|
+
return format_html(
|
|
165
|
+
'<span class="text-font-subtle-light dark:text-font-subtle-dark">{}</span>',
|
|
166
|
+
escape(text)
|
|
167
|
+
)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Code display elements for Django Admin.
|
|
3
|
+
|
|
4
|
+
Provides inline code and code block rendering with syntax highlighting support.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Optional
|
|
8
|
+
|
|
9
|
+
from django.utils.html import escape, format_html
|
|
10
|
+
from django.utils.safestring import SafeString
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CodeElements:
|
|
14
|
+
"""Code display elements."""
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def code(text: Any, css_class: str = "") -> SafeString:
|
|
18
|
+
"""
|
|
19
|
+
Render inline code.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
text: Code text
|
|
23
|
+
css_class: Additional CSS classes
|
|
24
|
+
|
|
25
|
+
Usage:
|
|
26
|
+
html.code("/path/to/file")
|
|
27
|
+
html.code("command --arg value")
|
|
28
|
+
"""
|
|
29
|
+
base_classes = "font-mono text-xs bg-base-100 dark:bg-base-800 px-1.5 py-0.5 rounded"
|
|
30
|
+
classes = f"{base_classes} {css_class}".strip()
|
|
31
|
+
|
|
32
|
+
return format_html(
|
|
33
|
+
'<code class="{}">{}</code>',
|
|
34
|
+
classes,
|
|
35
|
+
escape(str(text))
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def code_block(
|
|
40
|
+
text: Any,
|
|
41
|
+
language: Optional[str] = None,
|
|
42
|
+
max_height: Optional[str] = None,
|
|
43
|
+
variant: str = "default"
|
|
44
|
+
) -> SafeString:
|
|
45
|
+
"""
|
|
46
|
+
Render code block with optional syntax highlighting and scrolling.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
text: Code content
|
|
50
|
+
language: Programming language (json, python, bash, etc.) - for future syntax highlighting
|
|
51
|
+
max_height: Max height with scrolling (e.g., "400px", "20rem")
|
|
52
|
+
variant: Color variant - default, warning, danger, success, info
|
|
53
|
+
|
|
54
|
+
Usage:
|
|
55
|
+
html.code_block(json.dumps(data, indent=2), language="json")
|
|
56
|
+
html.code_block(stdout, max_height="400px")
|
|
57
|
+
html.code_block(stderr, max_height="400px", variant="warning")
|
|
58
|
+
"""
|
|
59
|
+
# Variant-specific styles
|
|
60
|
+
variant_classes = {
|
|
61
|
+
'default': 'bg-base-50 dark:bg-base-900 border-base-200 dark:border-base-700',
|
|
62
|
+
'warning': 'bg-warning-50 dark:bg-warning-900/20 border-warning-200 dark:border-warning-700',
|
|
63
|
+
'danger': 'bg-danger-50 dark:bg-danger-900/20 border-danger-200 dark:border-danger-700',
|
|
64
|
+
'success': 'bg-success-50 dark:bg-success-900/20 border-success-200 dark:border-success-700',
|
|
65
|
+
'info': 'bg-info-50 dark:bg-info-900/20 border-info-200 dark:border-info-700',
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
variant_class = variant_classes.get(variant, variant_classes['default'])
|
|
69
|
+
|
|
70
|
+
# Base styles
|
|
71
|
+
base_classes = f"font-mono text-xs whitespace-pre-wrap break-words border rounded-md p-3 {variant_class}"
|
|
72
|
+
|
|
73
|
+
# Add max-height and overflow if specified
|
|
74
|
+
style = ""
|
|
75
|
+
if max_height:
|
|
76
|
+
style = f'style="max-height: {max_height}; overflow-y: auto;"'
|
|
77
|
+
|
|
78
|
+
# Add language class for potential syntax highlighting
|
|
79
|
+
lang_class = f"language-{language}" if language else ""
|
|
80
|
+
|
|
81
|
+
return format_html(
|
|
82
|
+
'<pre class="{} {}" {}><code>{}</code></pre>',
|
|
83
|
+
base_classes,
|
|
84
|
+
lang_class,
|
|
85
|
+
style,
|
|
86
|
+
escape(str(text))
|
|
87
|
+
)
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Composition elements for Django Admin.
|
|
3
|
+
|
|
4
|
+
Provides methods for composing multiple elements together: inline, icon_text, header.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Optional, Union
|
|
8
|
+
|
|
9
|
+
from django.utils.html import escape, format_html
|
|
10
|
+
from django.utils.safestring import SafeString
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CompositionElements:
|
|
14
|
+
"""Element composition utilities."""
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def icon(icon_name: str, size: str = "xs", css_class: str = "") -> SafeString:
|
|
18
|
+
"""
|
|
19
|
+
Render Material Icon (helper for internal use).
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
icon_name: Icon name
|
|
23
|
+
size: Icon size
|
|
24
|
+
css_class: Additional CSS classes
|
|
25
|
+
"""
|
|
26
|
+
from .base import BaseElements
|
|
27
|
+
return BaseElements.icon(icon_name, size, css_class)
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def icon_text(icon_or_text: Union[str, Any], text: Any = None,
|
|
31
|
+
icon_size: str = "xs", separator: str = " ") -> SafeString:
|
|
32
|
+
"""
|
|
33
|
+
Render icon with text or emoji with text.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
icon_or_text: Icon from Icons class, emoji, or text if text param is None
|
|
37
|
+
text: Optional text to display after icon
|
|
38
|
+
icon_size: Icon size (xs, sm, base, lg, xl)
|
|
39
|
+
separator: Separator between icon and text
|
|
40
|
+
|
|
41
|
+
Usage:
|
|
42
|
+
html.icon_text(Icons.EDIT, 5) # Icon with number
|
|
43
|
+
html.icon_text("📝", 5) # Emoji with number
|
|
44
|
+
html.icon_text("Active") # Just text
|
|
45
|
+
"""
|
|
46
|
+
if text is None:
|
|
47
|
+
# Just text
|
|
48
|
+
return format_html('<span>{}</span>', escape(str(icon_or_text)))
|
|
49
|
+
|
|
50
|
+
# Check if it's a Material Icon (from Icons class) or emoji
|
|
51
|
+
icon_str = str(icon_or_text)
|
|
52
|
+
|
|
53
|
+
# Detect if it's emoji by checking for non-ASCII characters
|
|
54
|
+
is_emoji = any(ord(c) > 127 for c in icon_str)
|
|
55
|
+
|
|
56
|
+
if is_emoji or icon_str in ['📝', '💬', '🛒', '👤', '📧', '🔔', '⚙️', '🔧', '📊', '🎯']:
|
|
57
|
+
# Emoji
|
|
58
|
+
icon_html = escape(icon_str)
|
|
59
|
+
else:
|
|
60
|
+
# Material Icon
|
|
61
|
+
icon_html = CompositionElements.icon(icon_str, size=icon_size)
|
|
62
|
+
|
|
63
|
+
# DON'T escape SafeString - it's already safe HTML!
|
|
64
|
+
from django.utils.safestring import SafeString
|
|
65
|
+
if isinstance(text, SafeString):
|
|
66
|
+
text_html = text
|
|
67
|
+
else:
|
|
68
|
+
text_html = escape(str(text))
|
|
69
|
+
|
|
70
|
+
return format_html('{}{}<span>{}</span>', icon_html, separator, text_html)
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def inline(*items, separator: str = " | ",
|
|
74
|
+
size: str = "small", css_class: str = "") -> SafeString:
|
|
75
|
+
"""
|
|
76
|
+
Render items inline with separator.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
*items: Variable number of SafeString/str items to join (filters out None values)
|
|
80
|
+
separator: Separator between items
|
|
81
|
+
size: small, medium, large
|
|
82
|
+
css_class: Additional CSS classes
|
|
83
|
+
|
|
84
|
+
Usage:
|
|
85
|
+
html.inline(
|
|
86
|
+
html.icon_text(Icons.EDIT, 5),
|
|
87
|
+
html.icon_text(Icons.CHAT, 10),
|
|
88
|
+
separator=" | "
|
|
89
|
+
)
|
|
90
|
+
"""
|
|
91
|
+
# Filter out None values
|
|
92
|
+
filtered_items = [item for item in items if item is not None]
|
|
93
|
+
|
|
94
|
+
if not filtered_items:
|
|
95
|
+
return format_html('<span class="text-font-subtle-light dark:text-font-subtle-dark">—</span>')
|
|
96
|
+
|
|
97
|
+
size_classes = {
|
|
98
|
+
'small': 'text-xs',
|
|
99
|
+
'medium': 'text-sm',
|
|
100
|
+
'large': 'text-base'
|
|
101
|
+
}
|
|
102
|
+
size_class = size_classes.get(size, 'text-xs')
|
|
103
|
+
|
|
104
|
+
classes = size_class
|
|
105
|
+
if css_class:
|
|
106
|
+
classes += f" {css_class}"
|
|
107
|
+
|
|
108
|
+
# Convert items to strings, keeping SafeString as-is
|
|
109
|
+
from django.utils.safestring import SafeString, mark_safe
|
|
110
|
+
processed_items = []
|
|
111
|
+
for item in filtered_items:
|
|
112
|
+
if isinstance(item, (SafeString, str)):
|
|
113
|
+
processed_items.append(item)
|
|
114
|
+
else:
|
|
115
|
+
processed_items.append(escape(str(item)))
|
|
116
|
+
|
|
117
|
+
# Join with separator - str() doesn't lose SafeString when joined then mark_safe'd
|
|
118
|
+
joined = mark_safe(separator.join(str(item) for item in processed_items))
|
|
119
|
+
|
|
120
|
+
return format_html('<span class="{}">{}</span>', classes, joined)
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
def header(
|
|
124
|
+
title: str,
|
|
125
|
+
subtitle: Optional[str] = None,
|
|
126
|
+
initials: Optional[str] = None,
|
|
127
|
+
avatar_variant: str = "primary"
|
|
128
|
+
) -> SafeString:
|
|
129
|
+
"""
|
|
130
|
+
Render header with avatar/initials, title and subtitle.
|
|
131
|
+
|
|
132
|
+
Creates a horizontal layout with circular avatar badge and text content.
|
|
133
|
+
Common pattern for displaying users, accounts, entities with identity.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
title: Main title text
|
|
137
|
+
subtitle: Optional subtitle text (smaller, muted)
|
|
138
|
+
initials: Optional initials for avatar (e.g., "AB", "JD")
|
|
139
|
+
avatar_variant: Color variant for avatar badge (primary, success, info, etc.)
|
|
140
|
+
|
|
141
|
+
Usage:
|
|
142
|
+
# User with avatar
|
|
143
|
+
html.header(
|
|
144
|
+
title="John Doe",
|
|
145
|
+
subtitle="john@example.com • Admin",
|
|
146
|
+
initials="JD"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Account with info
|
|
150
|
+
html.header(
|
|
151
|
+
title="Trading Account",
|
|
152
|
+
subtitle="Binance • SPOT • user@email.com",
|
|
153
|
+
initials="TA",
|
|
154
|
+
avatar_variant="success"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Simple title only
|
|
158
|
+
html.header(title="Item Name")
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
SafeString with header component HTML
|
|
162
|
+
"""
|
|
163
|
+
# Avatar/initials badge
|
|
164
|
+
avatar_html = ""
|
|
165
|
+
if initials:
|
|
166
|
+
avatar_html = format_html(
|
|
167
|
+
'<span class="inline-flex items-center justify-center rounded-full w-8 h-8 text-xs font-semibold {} mr-3">{}</span>',
|
|
168
|
+
CompositionElements._get_avatar_classes(avatar_variant),
|
|
169
|
+
escape(initials)
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# Title
|
|
173
|
+
title_html = format_html(
|
|
174
|
+
'<div class="font-medium text-sm text-font-default-light dark:text-font-default-dark">{}</div>',
|
|
175
|
+
escape(title)
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# Subtitle
|
|
179
|
+
subtitle_html = ""
|
|
180
|
+
if subtitle:
|
|
181
|
+
subtitle_html = format_html(
|
|
182
|
+
'<div class="text-xs text-font-subtle-light dark:text-font-subtle-dark mt-0.5">{}</div>',
|
|
183
|
+
escape(subtitle)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
# Combine
|
|
187
|
+
return format_html(
|
|
188
|
+
'<div class="flex items-center">{}<div>{}{}</div></div>',
|
|
189
|
+
avatar_html,
|
|
190
|
+
title_html,
|
|
191
|
+
subtitle_html
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
@staticmethod
|
|
195
|
+
def _get_avatar_classes(variant: str) -> str:
|
|
196
|
+
"""Get CSS classes for avatar badge variant."""
|
|
197
|
+
variant_classes = {
|
|
198
|
+
'success': 'bg-success-100 text-success-800 dark:bg-success-900 dark:text-success-200',
|
|
199
|
+
'warning': 'bg-warning-100 text-warning-800 dark:bg-warning-900 dark:text-warning-200',
|
|
200
|
+
'danger': 'bg-danger-100 text-danger-800 dark:bg-danger-900 dark:text-danger-200',
|
|
201
|
+
'info': 'bg-info-100 text-info-800 dark:bg-info-900 dark:text-info-200',
|
|
202
|
+
'primary': 'bg-primary-100 text-primary-800 dark:bg-primary-900 dark:text-primary-200',
|
|
203
|
+
'secondary': 'bg-base-100 text-font-default-light dark:bg-base-800 dark:text-font-default-dark',
|
|
204
|
+
}
|
|
205
|
+
return variant_classes.get(variant, variant_classes['primary'])
|