django-spire 0.23.12__py3-none-any.whl → 0.23.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_spire/consts.py +1 -1
- django_spire/contrib/generic_views/modal_views.py +6 -1
- django_spire/core/static/django_spire/css/app-import.css +5 -0
- django_spire/core/static/django_spire/css/app-printing.css +31 -0
- django_spire/core/static/django_spire/css/bootstrap-extension.css +14 -2
- django_spire/core/tag/mixins.py +1 -1
- django_spire/core/templates/django_spire/page/full_page.html +2 -2
- django_spire/core/templates/django_spire/table/base.html +1 -1
- django_spire/core/templates/django_spire/table/element/row.html +20 -16
- django_spire/knowledge/migrations/0009_alter_collection_tags_alter_entry_tags.py +24 -0
- django_spire/metric/__init__.py +0 -0
- django_spire/metric/apps.py +19 -0
- django_spire/metric/report/__init__.py +0 -0
- django_spire/metric/report/admin.py +8 -0
- django_spire/metric/report/apps.py +25 -0
- django_spire/metric/report/auth/__init__.py +0 -0
- django_spire/metric/report/auth/controller.py +17 -0
- django_spire/metric/report/auth/tests/__init__.py +0 -0
- django_spire/metric/report/auth/tests/test_controller.py +86 -0
- django_spire/metric/report/enums.py +14 -0
- django_spire/metric/report/migrations/0001_initial.py +28 -0
- django_spire/metric/report/migrations/__init__.py +0 -0
- django_spire/metric/report/models.py +24 -0
- django_spire/metric/report/querysets.py +26 -0
- django_spire/metric/report/registry.py +38 -0
- django_spire/metric/report/report.py +187 -0
- django_spire/metric/report/templates/django_spire/metric/report/element/report_sub_navigation_element.html +34 -0
- django_spire/metric/report/templates/django_spire/metric/report/form/report_form.html +67 -0
- django_spire/metric/report/templates/django_spire/metric/report/page/report_page.html +37 -0
- django_spire/metric/report/templates/django_spire/metric/report/print/report_print.html +73 -0
- django_spire/metric/report/tests/__init__.py +0 -0
- django_spire/metric/report/tests/test_urls/__init__.py +0 -0
- django_spire/metric/report/tests/test_urls/test_page_urls.py +16 -0
- django_spire/metric/report/tools.py +11 -0
- django_spire/metric/report/urls/__init__.py +10 -0
- django_spire/metric/report/urls/page_urls.py +14 -0
- django_spire/metric/report/views/__init__.py +0 -0
- django_spire/metric/report/views/page_views.py +100 -0
- django_spire/metric/urls/__init__.py +10 -0
- django_spire/settings.py +3 -0
- {django_spire-0.23.12.dist-info → django_spire-0.23.13.dist-info}/METADATA +1 -1
- {django_spire-0.23.12.dist-info → django_spire-0.23.13.dist-info}/RECORD +45 -14
- {django_spire-0.23.12.dist-info → django_spire-0.23.13.dist-info}/WHEEL +0 -0
- {django_spire-0.23.12.dist-info → django_spire-0.23.13.dist-info}/licenses/LICENSE.md +0 -0
- {django_spire-0.23.12.dist-info → django_spire-0.23.13.dist-info}/top_level.txt +0 -0
django_spire/consts.py
CHANGED
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
|
+
from django.contrib import messages
|
|
5
6
|
from django.http import HttpResponseRedirect
|
|
6
7
|
from django.template.response import TemplateResponse
|
|
7
8
|
|
|
@@ -26,7 +27,8 @@ def dispatch_modal_delete_form_content(
|
|
|
26
27
|
# Present and past tense of verb
|
|
27
28
|
verbs: tuple[str, str] = ('delete', 'deleted'),
|
|
28
29
|
return_url: str | None = None,
|
|
29
|
-
template: str = 'django_spire/modal/content/dispatch_modal_delete_confirmation_content.html'
|
|
30
|
+
template: str = 'django_spire/modal/content/dispatch_modal_delete_confirmation_content.html',
|
|
31
|
+
show_success_message: bool = False
|
|
30
32
|
) -> HttpResponseRedirect | TemplateResponse:
|
|
31
33
|
if context_data is None:
|
|
32
34
|
context_data = {}
|
|
@@ -45,6 +47,9 @@ def dispatch_modal_delete_form_content(
|
|
|
45
47
|
auto_add_activity=auto_add_activity
|
|
46
48
|
)
|
|
47
49
|
|
|
50
|
+
if show_success_message:
|
|
51
|
+
messages.success(request, f'Successfully deleted {model_name}.')
|
|
52
|
+
|
|
48
53
|
else:
|
|
49
54
|
show_form_errors(request, form)
|
|
50
55
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
@media print {
|
|
2
|
+
.hide-on-print {
|
|
3
|
+
display: none !important;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
@page {
|
|
7
|
+
margin: 1cm !important;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
div {
|
|
11
|
+
page-break-inside: auto !important;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.page-break-before {
|
|
15
|
+
page-break-before: always !important;
|
|
16
|
+
display: block;
|
|
17
|
+
height: 1px;
|
|
18
|
+
clear: both;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.table-hover > tbody > tr:hover > * {
|
|
22
|
+
--bs-table-bg-state: var(--app-table-row-color) !important;
|
|
23
|
+
background-color: var(--app-table-row-color) !important;
|
|
24
|
+
color: var(--app-default-text-color) !important;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.table-print {
|
|
30
|
+
font-family: monospace !important;
|
|
31
|
+
}
|
|
@@ -188,11 +188,23 @@ h1, h2, h3, h4, h5, h6 {
|
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
.fs--2 {
|
|
191
|
-
font-size: .
|
|
191
|
+
font-size: .70rem !important;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.fs--3 {
|
|
195
|
+
font-size: .55rem !important;
|
|
192
196
|
}
|
|
193
197
|
|
|
194
198
|
.fs-7 {
|
|
195
|
-
font-size: .
|
|
199
|
+
font-size: .80rem !important;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.fs-8 {
|
|
203
|
+
font-size: .70rem !important;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.fs-8 {
|
|
207
|
+
font-size: .55rem !important;
|
|
196
208
|
}
|
|
197
209
|
|
|
198
210
|
.fw-400 {
|
django_spire/core/tag/mixins.py
CHANGED
|
@@ -7,7 +7,7 @@ from django_spire.core.tag.tools import simplify_and_weight_tag_set_to_dict, sim
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class TagModelMixin(models.Model):
|
|
10
|
-
tags = models.ManyToManyField(Tag, related_name='+',
|
|
10
|
+
tags = models.ManyToManyField(Tag, related_name='+', blank=True, editable=False)
|
|
11
11
|
|
|
12
12
|
class Meta:
|
|
13
13
|
abstract = True
|
|
@@ -149,14 +149,14 @@
|
|
|
149
149
|
}
|
|
150
150
|
}"
|
|
151
151
|
>
|
|
152
|
-
<div class="d-none d-lg-block pe-1 side-navigation">
|
|
152
|
+
<div class="d-none d-lg-block pe-1 side-navigation hide-on-print">
|
|
153
153
|
{% block full_page_side_navigation %}
|
|
154
154
|
{% include 'django_spire/navigation/side_navigation.html' %}
|
|
155
155
|
{% endblock %}
|
|
156
156
|
</div>
|
|
157
157
|
|
|
158
158
|
<div class="container-fluid d-flex flex-column">
|
|
159
|
-
<div class="row sticky-top">
|
|
159
|
+
<div class="row sticky-top hide-on-print">
|
|
160
160
|
<div class="col-12">
|
|
161
161
|
{% block full_page_top_navigation %}
|
|
162
162
|
{% include 'django_spire/navigation/top_navigation.html' %}
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
{% block scroll_container %}
|
|
142
142
|
<div
|
|
143
143
|
class="position-relative table-container flex-grow-1"
|
|
144
|
-
style="min-height: 200px; overflow-x: auto; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;"
|
|
144
|
+
style="min-height: {{ table_height|default:'200px;' }} overflow-x: auto; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;"
|
|
145
145
|
x-ref="scroll_container"
|
|
146
146
|
:data-table-id="table_id"
|
|
147
147
|
>
|
|
@@ -74,23 +74,27 @@
|
|
|
74
74
|
@select-all-rows.window="handle_select_all_rows($event)"
|
|
75
75
|
@toggle-row-state.window="handle_toggle_row_state($event)"
|
|
76
76
|
>
|
|
77
|
-
|
|
78
|
-
<
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
{% block table_checkbox %}
|
|
78
|
+
<td>
|
|
79
|
+
<input
|
|
80
|
+
:checked="is_checked"
|
|
81
|
+
@change="handle_checkbox_change()"
|
|
82
|
+
type="checkbox"
|
|
83
|
+
>
|
|
84
|
+
</td>
|
|
85
|
+
{% endblock %}
|
|
84
86
|
|
|
85
|
-
|
|
86
|
-
<
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
87
|
+
{% block table_child_toggle %}
|
|
88
|
+
<td>
|
|
89
|
+
<template x-if="has_children">
|
|
90
|
+
<i
|
|
91
|
+
:class="is_open ? 'bi-chevron-down' : 'bi-chevron-right'"
|
|
92
|
+
@click="$dispatch('toggle-row', { row_id: row_id, table_id: table_id })"
|
|
93
|
+
class="bi cursor-pointer"
|
|
94
|
+
></i>
|
|
95
|
+
</template>
|
|
96
|
+
</td>
|
|
97
|
+
{% endblock %}
|
|
94
98
|
|
|
95
99
|
{% block table_cell %}{% endblock %}
|
|
96
100
|
</tr>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Generated by Django 5.2.8 on 2026-01-11 18:54
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('django_spire_core', '0001_initial'),
|
|
10
|
+
('django_spire_knowledge', '0008_collection_tags_entry_tags'),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.AlterField(
|
|
15
|
+
model_name='collection',
|
|
16
|
+
name='tags',
|
|
17
|
+
field=models.ManyToManyField(blank=True, editable=False, related_name='+', to='django_spire_core.tag'),
|
|
18
|
+
),
|
|
19
|
+
migrations.AlterField(
|
|
20
|
+
model_name='entry',
|
|
21
|
+
name='tags',
|
|
22
|
+
field=models.ManyToManyField(blank=True, editable=False, related_name='+', to='django_spire_core.tag'),
|
|
23
|
+
),
|
|
24
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.apps import AppConfig
|
|
4
|
+
|
|
5
|
+
from django_spire.utils import check_required_apps
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MetricConfig(AppConfig):
|
|
9
|
+
default_auto_field = 'django.db.models.BigAutoField'
|
|
10
|
+
label = 'metric'
|
|
11
|
+
name = 'django_spire.metric'
|
|
12
|
+
|
|
13
|
+
REQUIRED_APPS = ('django_spire_core',)
|
|
14
|
+
|
|
15
|
+
URLPATTERNS_INCLUDE = 'django_spire.metric.urls'
|
|
16
|
+
URLPATTERNS_NAMESPACE = 'metric'
|
|
17
|
+
|
|
18
|
+
def ready(self) -> None:
|
|
19
|
+
check_required_apps(self.label)
|
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.apps import AppConfig
|
|
4
|
+
|
|
5
|
+
from django_spire.utils import check_required_apps
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ReportConfig(AppConfig):
|
|
9
|
+
default_auto_field = 'django.db.models.BigAutoField'
|
|
10
|
+
label = 'django_spire_metric_report'
|
|
11
|
+
name = 'django_spire.metric.report'
|
|
12
|
+
|
|
13
|
+
MODEL_PERMISSIONS = (
|
|
14
|
+
{
|
|
15
|
+
'name': 'report',
|
|
16
|
+
'verbose_name': 'Report',
|
|
17
|
+
'model_class_path': 'django_spire.metric.report.models.ReportRun',
|
|
18
|
+
'is_proxy_model': False,
|
|
19
|
+
},
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
REQUIRED_APPS = ('django_spire_core',)
|
|
23
|
+
|
|
24
|
+
def ready(self) -> None:
|
|
25
|
+
check_required_apps(self.label)
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.auth.controller.controller import BaseAuthController
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BaseReportAuthController(BaseAuthController):
|
|
7
|
+
def can_add(self):
|
|
8
|
+
return self.request.user.has_perm('django_spire_metric_report.add_reportrun')
|
|
9
|
+
|
|
10
|
+
def can_change(self):
|
|
11
|
+
return self.request.user.has_perm('django_spire_metric_report.change_reportrun')
|
|
12
|
+
|
|
13
|
+
def can_delete(self):
|
|
14
|
+
return self.request.user.has_perm('django_spire_metric_report.delete_reportrun')
|
|
15
|
+
|
|
16
|
+
def can_view(self):
|
|
17
|
+
return self.request.user.has_perm('django_spire_metric_report.view_reportrun')
|
|
File without changes
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.contrib.auth.models import Permission
|
|
4
|
+
from django.contrib.contenttypes.models import ContentType
|
|
5
|
+
from django.test import RequestFactory
|
|
6
|
+
|
|
7
|
+
from django_spire.auth.user.tests.factories import create_user
|
|
8
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
9
|
+
from django_spire.metric.report.auth.controller import BaseReportAuthController
|
|
10
|
+
from django_spire.metric.report.models import ReportRun
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BaseReportAuthControllerTests(BaseTestCase):
|
|
14
|
+
def setUp(self):
|
|
15
|
+
super().setUp()
|
|
16
|
+
self.factory = RequestFactory()
|
|
17
|
+
self.user = create_user(username='test_auth_user')
|
|
18
|
+
self.request = self.factory.get('/')
|
|
19
|
+
self.request.user = self.user
|
|
20
|
+
|
|
21
|
+
content_type = ContentType.objects.get_for_model(ReportRun)
|
|
22
|
+
|
|
23
|
+
self.view_permission = Permission.objects.get(
|
|
24
|
+
codename='view_reportrun',
|
|
25
|
+
content_type=content_type
|
|
26
|
+
)
|
|
27
|
+
self.add_permission = Permission.objects.get(
|
|
28
|
+
codename='add_reportrun',
|
|
29
|
+
content_type=content_type
|
|
30
|
+
)
|
|
31
|
+
self.change_permission = Permission.objects.get(
|
|
32
|
+
codename='change_reportrun',
|
|
33
|
+
content_type=content_type
|
|
34
|
+
)
|
|
35
|
+
self.delete_permission = Permission.objects.get(
|
|
36
|
+
codename='delete_reportrun',
|
|
37
|
+
content_type=content_type
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def _refresh_user_and_request(self):
|
|
41
|
+
self.user = type(self.user).objects.get(pk=self.user.pk)
|
|
42
|
+
self.request.user = self.user
|
|
43
|
+
|
|
44
|
+
def test_can_view_without_permission(self):
|
|
45
|
+
controller = BaseReportAuthController(self.request)
|
|
46
|
+
assert controller.can_view() is False
|
|
47
|
+
|
|
48
|
+
def test_can_view_with_permission(self):
|
|
49
|
+
self.user.user_permissions.add(self.view_permission)
|
|
50
|
+
self._refresh_user_and_request()
|
|
51
|
+
|
|
52
|
+
controller = BaseReportAuthController(self.request)
|
|
53
|
+
assert controller.can_view() is True
|
|
54
|
+
|
|
55
|
+
def test_can_add_without_permission(self):
|
|
56
|
+
controller = BaseReportAuthController(self.request)
|
|
57
|
+
assert controller.can_add() is False
|
|
58
|
+
|
|
59
|
+
def test_can_add_with_permission(self):
|
|
60
|
+
self.user.user_permissions.add(self.add_permission)
|
|
61
|
+
self._refresh_user_and_request()
|
|
62
|
+
|
|
63
|
+
controller = BaseReportAuthController(self.request)
|
|
64
|
+
assert controller.can_add() is True
|
|
65
|
+
|
|
66
|
+
def test_can_change_without_permission(self):
|
|
67
|
+
controller = BaseReportAuthController(self.request)
|
|
68
|
+
assert controller.can_change() is False
|
|
69
|
+
|
|
70
|
+
def test_can_change_with_permission(self):
|
|
71
|
+
self.user.user_permissions.add(self.change_permission)
|
|
72
|
+
self._refresh_user_and_request()
|
|
73
|
+
|
|
74
|
+
controller = BaseReportAuthController(self.request)
|
|
75
|
+
assert controller.can_change() is True
|
|
76
|
+
|
|
77
|
+
def test_can_delete_without_permission(self):
|
|
78
|
+
controller = BaseReportAuthController(self.request)
|
|
79
|
+
assert controller.can_delete() is False
|
|
80
|
+
|
|
81
|
+
def test_can_delete_with_permission(self):
|
|
82
|
+
self.user.user_permissions.add(self.delete_permission)
|
|
83
|
+
self._refresh_user_and_request()
|
|
84
|
+
|
|
85
|
+
controller = BaseReportAuthController(self.request)
|
|
86
|
+
assert controller.can_delete() is True
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Generated by Django 5.2.10 on 2026-01-08 20:41
|
|
2
|
+
|
|
3
|
+
import django.utils.timezone
|
|
4
|
+
from django.db import migrations, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
initial = True
|
|
10
|
+
|
|
11
|
+
dependencies = [
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
operations = [
|
|
15
|
+
migrations.CreateModel(
|
|
16
|
+
name='ReportRun',
|
|
17
|
+
fields=[
|
|
18
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
19
|
+
('report_key_stack', models.TextField()),
|
|
20
|
+
('datetime', models.DateTimeField(default=django.utils.timezone.now)),
|
|
21
|
+
],
|
|
22
|
+
options={
|
|
23
|
+
'verbose_name': 'Report Run',
|
|
24
|
+
'verbose_name_plural': 'Report Runs',
|
|
25
|
+
'db_table': 'django_spire_metric_report_run',
|
|
26
|
+
},
|
|
27
|
+
),
|
|
28
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
from django.utils.timezone import now
|
|
3
|
+
|
|
4
|
+
from django_spire.metric.report.querysets import ReportRunQuerySet
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ReportRun(models.Model):
|
|
8
|
+
report_key_stack = models.TextField()
|
|
9
|
+
datetime = models.DateTimeField(default=now)
|
|
10
|
+
|
|
11
|
+
objects = ReportRunQuerySet.as_manager()
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def report_button_text(self) -> str:
|
|
15
|
+
return self.report_key_stack.split('|')[-1]
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def report_key_stack_verbose(self):
|
|
19
|
+
return self.report_key_stack.replace('|', ' > ')
|
|
20
|
+
|
|
21
|
+
class Meta:
|
|
22
|
+
verbose_name = 'Report Run'
|
|
23
|
+
verbose_name_plural = 'Report Runs'
|
|
24
|
+
db_table = 'django_spire_metric_report_run'
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from django.db.models import QuerySet, Count
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ReportRunQuerySet(QuerySet):
|
|
12
|
+
def by_popular(self):
|
|
13
|
+
return (
|
|
14
|
+
self
|
|
15
|
+
.values('report_key_stack')
|
|
16
|
+
.annotate(
|
|
17
|
+
run_count=Count('report_key_stack')
|
|
18
|
+
)
|
|
19
|
+
.order_by('-run_count')
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
def by_top_ten(self):
|
|
23
|
+
return self.by_popular()[:10]
|
|
24
|
+
|
|
25
|
+
def run_count(self, report_key_stack: str) -> int:
|
|
26
|
+
return self.filter(report_key_stack=report_key_stack).count()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from typing import Self
|
|
2
|
+
|
|
3
|
+
from django_spire.metric.report.report import BaseReport
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ReportRegistry:
|
|
7
|
+
category: str | None = None
|
|
8
|
+
report_names_classes: dict[str, BaseReport] = {}
|
|
9
|
+
report_registries: list[Self] = []
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
for report_registry in self.report_registries:
|
|
13
|
+
self.add_registry(report_registry)
|
|
14
|
+
|
|
15
|
+
def add_registry(
|
|
16
|
+
self,
|
|
17
|
+
report_registry: Self
|
|
18
|
+
):
|
|
19
|
+
if report_registry.category is None:
|
|
20
|
+
message = 'Report Registry category is required'
|
|
21
|
+
raise ValueError(message)
|
|
22
|
+
|
|
23
|
+
# if report_registry.category in self.report_names_classes:
|
|
24
|
+
# message = f'Report Registry category "{report_registry.category}" already exists'
|
|
25
|
+
# raise ValueError(message)
|
|
26
|
+
|
|
27
|
+
self.report_names_classes[report_registry.category] = report_registry.report_names_classes
|
|
28
|
+
|
|
29
|
+
def get_report_from_key_stack(self, report_key_stack: str) -> BaseReport | None:
|
|
30
|
+
current = self.report_names_classes
|
|
31
|
+
|
|
32
|
+
for key in report_key_stack.split('|'):
|
|
33
|
+
if isinstance(current, dict) and key in current:
|
|
34
|
+
current = current[key]
|
|
35
|
+
else:
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
return current()
|