django-admin-charts 1.3.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.
Files changed (88) hide show
  1. admin_tools_stats/__init__.py +20 -0
  2. admin_tools_stats/__pycache__/__init__.cpython-311.pyc +0 -0
  3. admin_tools_stats/admin.py +186 -0
  4. admin_tools_stats/app_label_renamer.py +93 -0
  5. admin_tools_stats/apps.py +6 -0
  6. admin_tools_stats/charts.py +0 -0
  7. admin_tools_stats/fixtures/10_dashboardstatscriteria_status.json +17 -0
  8. admin_tools_stats/fixtures/20_dashboardstatscriteria_call_type.json +26 -0
  9. admin_tools_stats/fixtures/initial_data.json +37 -0
  10. admin_tools_stats/forms.py +97 -0
  11. admin_tools_stats/management/commands/recalculate_charts.py +130 -0
  12. admin_tools_stats/migrations/0001_initial.py +188 -0
  13. admin_tools_stats/migrations/0002_auto_20190920_1058.py +95 -0
  14. admin_tools_stats/migrations/0003_auto_20191007_0950.py +90 -0
  15. admin_tools_stats/migrations/0004_dashboardstats_y_tick_format.py +24 -0
  16. admin_tools_stats/migrations/0005_auto_20200203_1537.py +71 -0
  17. admin_tools_stats/migrations/0006_auto_20200205_0944.py +109 -0
  18. admin_tools_stats/migrations/0007_auto_20200205_1054.py +62 -0
  19. admin_tools_stats/migrations/0008_auto_20200911_1400.py +35 -0
  20. admin_tools_stats/migrations/0009_auto_20200928_1003.py +26 -0
  21. admin_tools_stats/migrations/0010_dashboardstats_show_to_users.py +21 -0
  22. admin_tools_stats/migrations/0011_auto_20210204_1206.py +63 -0
  23. admin_tools_stats/migrations/0012_auto_20210207_0859.py +80 -0
  24. admin_tools_stats/migrations/0013_auto_20210221_1247.py +47 -0
  25. admin_tools_stats/migrations/0014_auto_20211122_1511.py +254 -0
  26. admin_tools_stats/migrations/0015_auto_20211209_0822.py +36 -0
  27. admin_tools_stats/migrations/0016_dashboardstats_cache_values.py +17 -0
  28. admin_tools_stats/migrations/0017_alter_dashboardstats_options_and_more.py +46 -0
  29. admin_tools_stats/migrations/0018_alter_dashboardstats_options.py +16 -0
  30. admin_tools_stats/migrations/0019_criteriatostatsm2m_recalculate.py +20 -0
  31. admin_tools_stats/migrations/0020_alter_dashboardstats_graph_key.py +28 -0
  32. admin_tools_stats/migrations/0021_auto_20230210_1102.py +40 -0
  33. admin_tools_stats/migrations/__init__.py +0 -0
  34. admin_tools_stats/models.py +1077 -0
  35. admin_tools_stats/modules.py +104 -0
  36. admin_tools_stats/south_migrations/0001_initial_migration.py +211 -0
  37. admin_tools_stats/south_migrations/0002_add_sum_field_name.py +121 -0
  38. admin_tools_stats/south_migrations/0003_add_operation_field_name.py +148 -0
  39. admin_tools_stats/south_migrations/__init__.py +0 -0
  40. admin_tools_stats/templates/admin/index.html +22 -0
  41. admin_tools_stats/templates/admin_tools_stats/admin_charts.js +83 -0
  42. admin_tools_stats/templates/admin_tools_stats/analytics.html +87 -0
  43. admin_tools_stats/templates/admin_tools_stats/analytics_chart.html +3 -0
  44. admin_tools_stats/templates/admin_tools_stats/analytics_user.html +5 -0
  45. admin_tools_stats/templates/admin_tools_stats/chart_container.html +10 -0
  46. admin_tools_stats/templates/admin_tools_stats/chart_containers.html +8 -0
  47. admin_tools_stats/templates/admin_tools_stats/chart_data.html +17 -0
  48. admin_tools_stats/templates/admin_tools_stats/chart_form.html +35 -0
  49. admin_tools_stats/templates/admin_tools_stats/modules/chart.html +18 -0
  50. admin_tools_stats/templates/include_nvd3.html +5 -0
  51. admin_tools_stats/templatetags/__init__.py +0 -0
  52. admin_tools_stats/templatetags/admin_chart_tags.py +56 -0
  53. admin_tools_stats/tests/__init__.py +0 -0
  54. admin_tools_stats/tests/test_admin.py +288 -0
  55. admin_tools_stats/tests/test_forms.py +18 -0
  56. admin_tools_stats/tests/test_models.py +1693 -0
  57. admin_tools_stats/tests/test_views.py +563 -0
  58. admin_tools_stats/tests/utils.py +41 -0
  59. admin_tools_stats/urls.py +27 -0
  60. admin_tools_stats/utils.py +37 -0
  61. admin_tools_stats/views.py +205 -0
  62. demoproject/.coveralls.yml +1 -0
  63. demoproject/.gitignore +1 -0
  64. demoproject/__init__.py +0 -0
  65. demoproject/dashboard.py +147 -0
  66. demoproject/demoproject/__init__.py +0 -0
  67. demoproject/demoproject/fixtures/auth_user.json +42 -0
  68. demoproject/demoproject/fixtures/test_data.json +118 -0
  69. demoproject/demoproject/migrations/0001_initial.py +33 -0
  70. demoproject/demoproject/migrations/0002_auto_20210221_0541.py +22 -0
  71. demoproject/demoproject/migrations/0003_testkid_author.py +26 -0
  72. demoproject/demoproject/migrations/0004_testkid_height.py +17 -0
  73. demoproject/demoproject/migrations/__init__.py +0 -0
  74. demoproject/demoproject/models.py +19 -0
  75. demoproject/demoproject/settings.py +260 -0
  76. demoproject/demoproject/templates/admin/base_site.html +22 -0
  77. demoproject/demoproject/templates/home.html +7 -0
  78. demoproject/demoproject/urls.py +17 -0
  79. demoproject/demoproject/views.py +12 -0
  80. demoproject/demoproject/wsgi.py +35 -0
  81. demoproject/manage.py +11 -0
  82. demoproject/menu.py +32 -0
  83. demoproject/requirements.txt +7 -0
  84. demoproject/test_settings.py +13 -0
  85. django_admin_charts-1.3.1.dist-info/METADATA +328 -0
  86. django_admin_charts-1.3.1.dist-info/RECORD +88 -0
  87. django_admin_charts-1.3.1.dist-info/WHEEL +5 -0
  88. django_admin_charts-1.3.1.dist-info/top_level.txt +2 -0
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ #
5
+ # This Source Code Form is subject to the terms of the Mozilla Public
6
+ # License, v. 2.0. If a copy of the MPL was not distributed with this file,
7
+ # You can obtain one at http://mozilla.org/MPL/2.0/.
8
+ #
9
+ # Copyright (C) 2011-2014 Star2Billing S.L.
10
+ #
11
+ # The Initial Developer of the Original Code is
12
+ # Arezqui Belaid <info@star2billing.com>
13
+ #
14
+
15
+ # edit also docs/source/conf.py and update requirements.txt
16
+ __version__ = "1.3.1"
17
+ __author__ = "Petr Dlouhy"
18
+ __contact__ = "petr.dlouhy@emai.cz"
19
+ __homepage__ = "https://github.com/PetrDlouhy"
20
+ __docformat__ = "restructuredtext"
@@ -0,0 +1,186 @@
1
+ #
2
+ # This Source Code Form is subject to the terms of the Mozilla Public
3
+ # License, v. 2.0. If a copy of the MPL was not distributed with this file,
4
+ # You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ #
6
+ # Copyright (C) 2011-2014 Star2Billing S.L.
7
+ #
8
+ # The Initial Developer of the Original Code is
9
+ # Arezqui Belaid <info@star2billing.com>
10
+ #
11
+
12
+ from django import forms
13
+ from django.contrib import admin
14
+ from django.urls import reverse
15
+ from django.utils.html import format_html
16
+ from django.utils.translation import gettext_lazy as _
17
+
18
+ from admin_tools_stats.app_label_renamer import AppLabelRenamer
19
+ from admin_tools_stats.models import (
20
+ CachedValue,
21
+ CriteriaToStatsM2M,
22
+ DashboardStats,
23
+ DashboardStatsCriteria,
24
+ )
25
+
26
+
27
+ AppLabelRenamer(native_app_label="admin_tools_stats", app_label=_("Admin Tools Stats")).main()
28
+
29
+
30
+ @admin.register(DashboardStatsCriteria)
31
+ class DashboardStatsCriteriaAdmin(admin.ModelAdmin):
32
+ """
33
+ Allows the administrator to view and modify certain attributes
34
+ of a DashboardStats.
35
+ """
36
+
37
+ list_display = (
38
+ "id",
39
+ "criteria_name",
40
+ "criteria_name",
41
+ "dynamic_criteria_field_name",
42
+ "criteria_dynamic_mapping_preview",
43
+ )
44
+ list_filter = ["created_date"]
45
+ readonly_fields = (
46
+ "created_date",
47
+ "updated_date",
48
+ )
49
+ search_fields = ("criteria_name",)
50
+ ordering = ("id",)
51
+ save_as = True
52
+
53
+
54
+ class DashboardStatsCriteriaInline(admin.TabularInline):
55
+ model = CriteriaToStatsM2M
56
+ readonly_fields = (
57
+ "criteria__dynamic_criteria_field_name",
58
+ "criteria__criteria_dynamic_mapping_preview",
59
+ )
60
+ fields = (
61
+ "criteria",
62
+ "order",
63
+ "use_as",
64
+ "default_option",
65
+ "choices_based_on_time_range",
66
+ "recalculate",
67
+ "count_limit",
68
+ "prefix",
69
+ "criteria__dynamic_criteria_field_name",
70
+ "criteria__criteria_dynamic_mapping_preview",
71
+ )
72
+ autocomplete_fields = ("criteria",)
73
+ extra = 0
74
+
75
+ def criteria__dynamic_criteria_field_name(self, obj):
76
+ return format_html(
77
+ "<strong>{}</strong>{}",
78
+ str(obj.prefix or ""),
79
+ str(obj.criteria.dynamic_criteria_field_name or ""),
80
+ )
81
+
82
+ def criteria__criteria_dynamic_mapping_preview(self, obj):
83
+ return obj.criteria.criteria_dynamic_mapping_preview()
84
+
85
+
86
+ class DashboardStatsForm(forms.ModelForm):
87
+ def __init__(self, *args, **kwargs):
88
+ super().__init__(*args, **kwargs)
89
+ if self.instance.pk:
90
+ self.fields["default_multiseries_criteria"].queryset = (
91
+ CriteriaToStatsM2M.objects.filter(
92
+ stats=self.instance,
93
+ )
94
+ )
95
+
96
+
97
+ @admin.register(DashboardStats)
98
+ class DashboardStatsAdmin(admin.ModelAdmin):
99
+ """
100
+ Allows the administrator to view and modify certain attributes
101
+ of a DashboardStats.
102
+ """
103
+
104
+ fieldsets = (
105
+ (
106
+ None,
107
+ {
108
+ "fields": (
109
+ "graph_key",
110
+ "graph_title",
111
+ ("model_app_name", "model_name", "date_field_name"),
112
+ ("operation_field_name", "distinct"),
113
+ ("user_field_name", "show_to_users"),
114
+ ("allowed_type_operation_field_name", "type_operation_field_name"),
115
+ ("allowed_chart_types", "default_chart_type"),
116
+ (
117
+ "allowed_time_scales",
118
+ "default_time_scale",
119
+ "default_time_period",
120
+ ),
121
+ "y_axis_format",
122
+ "default_multiseries_criteria",
123
+ "is_visible",
124
+ "cache_values",
125
+ ),
126
+ },
127
+ ),
128
+ )
129
+ list_display = (
130
+ "id",
131
+ "graph_key",
132
+ "analytics_link",
133
+ "graph_title",
134
+ "model_name",
135
+ "distinct",
136
+ "type_operation_field_name",
137
+ "is_visible",
138
+ "show_to_users",
139
+ "cache_values",
140
+ "created_date",
141
+ "date_field_name",
142
+ "operation_field_name",
143
+ "default_chart_type",
144
+ )
145
+ list_filter = [
146
+ "created_date",
147
+ "is_visible",
148
+ "show_to_users",
149
+ "cache_values",
150
+ ]
151
+ exclude = ("criteria",)
152
+ inlines = [DashboardStatsCriteriaInline]
153
+ save_as = True
154
+ form = DashboardStatsForm
155
+
156
+ def analytics_link(self, obj):
157
+ return format_html(
158
+ "<a href='{url}?show={key}' target='_blank'>A</a>",
159
+ url=reverse("chart-analytics"),
160
+ key=obj.graph_key,
161
+ )
162
+
163
+
164
+ @admin.register(CachedValue)
165
+ class CachedValueAdmin(admin.ModelAdmin):
166
+ list_display = (
167
+ "stats",
168
+ "date",
169
+ "value",
170
+ "operation",
171
+ "operation_field_name",
172
+ "filtered_value",
173
+ "time_scale",
174
+ "multiple_series_choice",
175
+ "is_final",
176
+ )
177
+ list_filter = (
178
+ "stats",
179
+ "operation",
180
+ "operation_field_name",
181
+ "filtered_value",
182
+ "time_scale",
183
+ "is_final",
184
+ "multiple_series_choice",
185
+ "stats__cache_values",
186
+ )
@@ -0,0 +1,93 @@
1
+ #
2
+ # This Source Code Form is subject to the terms of the Mozilla Public
3
+ # License, v. 2.0. If a copy of the MPL was not distributed with this file,
4
+ # You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ #
6
+ # Copyright (C) 2011-2014 Star2Billing S.L.
7
+ #
8
+ # The Initial Developer of the Original Code is
9
+ # Arezqui Belaid <info@star2billing.com>
10
+ #
11
+ from django.contrib import admin
12
+ from django.db.models.base import ModelBase
13
+ from django.urls import resolve
14
+
15
+
16
+ # TODO: Follow evolution of https://code.djangoproject.com/ticket/3591
17
+
18
+
19
+ # Source link : http://django-notes.blogspot.in/2011/07/django-app-name-breadcrumbs-l10n.html
20
+ class AppLabelRenamer(object):
21
+ """
22
+ Rename app label and app breadcrumbs in admin
23
+ """
24
+
25
+ def __init__(self, native_app_label, app_label):
26
+ self.native_app_label = native_app_label
27
+ self.app_label = app_label
28
+ self.module = ".".join([native_app_label, "models"])
29
+
30
+ class StringWithRealoadedTitle(str):
31
+ """
32
+ thanks to Ionel Maries Cristian for
33
+ http://ionelmc.wordpress.com/2011/06/24/custom-app-names-in-the-django-admin/
34
+ """
35
+
36
+ def __new__(cls, value, title):
37
+ instance = str.__new__(cls, value)
38
+ instance._title = title
39
+ return instance
40
+
41
+ def title(self):
42
+ return self._title
43
+
44
+ def __copy__(self):
45
+ return self
46
+
47
+ def __deepcopy__(self, memodict):
48
+ return self
49
+
50
+ def rename_app_label(self, f):
51
+ app_label = self.app_label
52
+
53
+ def rename_breadcrumbs(f):
54
+ def wrap(self, *args, **kwargs):
55
+ extra_context = kwargs.get("extra_context", {})
56
+ extra_context["app_label"] = app_label
57
+ kwargs["extra_context"] = extra_context
58
+ return f(self, *args, **kwargs)
59
+
60
+ return wrap
61
+
62
+ def wrap(model_or_iterable, admin_class=None, **option):
63
+ if isinstance(model_or_iterable, ModelBase):
64
+ model_or_iterable = [model_or_iterable]
65
+ for model in model_or_iterable:
66
+ if model.__module__ != self.module:
67
+ continue
68
+ if admin_class is None:
69
+ admin_class = type(model.__name__ + "Admin", (admin.ModelAdmin,), {})
70
+ admin_class.add_view = rename_breadcrumbs(admin_class.add_view)
71
+ admin_class.change_view = rename_breadcrumbs(admin_class.change_view)
72
+ admin_class.changelist_view = rename_breadcrumbs(admin_class.changelist_view)
73
+ model._meta.app_label = self.StringWithRealoadedTitle(
74
+ self.native_app_label, self.app_label
75
+ )
76
+ return f(model, admin_class, **option)
77
+
78
+ return wrap
79
+
80
+ def rename_app_index(self, f):
81
+ def wrap(request, app_label, extra_context=None):
82
+ requested_app_label = resolve(request.path).kwargs.get("app_label", "")
83
+ if requested_app_label and requested_app_label == self.native_app_label:
84
+ app_label = self.StringWithRealoadedTitle(self.native_app_label, self.app_label)
85
+ else:
86
+ app_label = requested_app_label
87
+ return f(request, app_label, extra_context=None)
88
+
89
+ return wrap
90
+
91
+ def main(self):
92
+ admin.site.register = self.rename_app_label(admin.site.register)
93
+ admin.site.app_index = self.rename_app_index(admin.site.app_index)
@@ -0,0 +1,6 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class AdminChartsConfig(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "admin_tools_stats"
File without changes
@@ -0,0 +1,17 @@
1
+ [
2
+ {
3
+ "pk":1,
4
+ "model":"admin_tools_stats.dashboardstatscriteria",
5
+ "fields":{
6
+ "criteria_fix_mapping":{
7
+ "is_active":"1"
8
+ },
9
+ "updated_date":"2011-09-01 09:06:12",
10
+ "criteria_name":"status",
11
+ "criteria_dynamic_mapping":{
12
+
13
+ },
14
+ "created_date":"2011-09-01 09:01:40"
15
+ }
16
+ }
17
+ ]
@@ -0,0 +1,26 @@
1
+ [
2
+ {
3
+ "pk":2,
4
+ "model":"admin_tools_stats.dashboardstatscriteria",
5
+ "fields":{
6
+ "criteria_fix_mapping":null,
7
+ "updated_date":"2011-09-02 05:51:41",
8
+ "criteria_name":"call_type",
9
+ "dynamic_criteria_field_name":"disposition",
10
+ "criteria_dynamic_mapping":{
11
+ "INVALIDARGS":"INVALIDARGS",
12
+ "BUSY":"BUSY",
13
+ "TORTURE":"TORTURE",
14
+ "ANSWER":"ANSWER",
15
+ "DONTCALL":"DONTCALL",
16
+ "FORBIDDEN":"FORBIDDEN",
17
+ "NOROUTE":"NOROUTE",
18
+ "CHANUNAVAIL":"CHANUNAVAIL",
19
+ "NOANSWER":"NOANSWER",
20
+ "CONGESTION":"CONGESTION",
21
+ "CANCEL":"CANCEL"
22
+ },
23
+ "created_date":"2011-09-02 05:51:41"
24
+ }
25
+ }
26
+ ]
@@ -0,0 +1,37 @@
1
+ [
2
+ {
3
+ "pk":1,
4
+ "model":"admin_tools_stats.dashboardstatscriteria",
5
+ "fields":{
6
+ "criteria_fix_mapping":{
7
+ "is_active":"1"
8
+ },
9
+ "updated_date":"2011-09-01 09:06:12",
10
+ "criteria_name":"status",
11
+ "dynamic_criteria_field_name":"",
12
+ "criteria_dynamic_mapping":{
13
+
14
+ },
15
+ "created_date":"2011-09-01 09:01:40"
16
+ }
17
+ },
18
+ {
19
+ "pk":1,
20
+ "model":"admin_tools_stats.dashboardstats",
21
+ "fields":{
22
+ "updated_date":"2011-09-01 09:11:56",
23
+ "is_visible":true,
24
+ "graph_key":"user_graph",
25
+ "graph_title":"User graph",
26
+ "criteria":[
27
+ 1
28
+ ],
29
+ "date_field_name":"date_joined",
30
+ "operation_field_name":"",
31
+ "type_operation_field_name":"",
32
+ "model_name":"User",
33
+ "created_date":"2011-09-01 08:56:45",
34
+ "model_app_name":"auth"
35
+ }
36
+ }
37
+ ]
@@ -0,0 +1,97 @@
1
+ from datetime import timedelta
2
+
3
+ from django import forms
4
+ from django.utils.timezone import now
5
+
6
+
7
+ class ChartSettingsForm(forms.Form):
8
+ def __init__(self, stats, user=None, *args, **kwargs):
9
+ super().__init__(*args, **kwargs)
10
+ for ch_filter in stats.criteriatostatsm2m_set.filter(use_as="chart_filter").order_by(
11
+ "order"
12
+ ):
13
+ dy_map = ch_filter.get_dynamic_choices(user=user)
14
+ if dy_map:
15
+ self.fields[f"select_box_dynamic_{ch_filter.id}"] = forms.ChoiceField(
16
+ choices=[("", "-------")] + list(dy_map.values()),
17
+ label=ch_filter.criteria.criteria_name,
18
+ initial=ch_filter.default_option,
19
+ )
20
+ self.fields[f"select_box_dynamic_{ch_filter.id}"].widget.attrs[
21
+ "class"
22
+ ] = "chart-input"
23
+
24
+ self.fields["graph_key"] = forms.CharField(
25
+ initial=stats.graph_key,
26
+ widget=forms.HiddenInput(attrs={"class": "hidden_graph_key"}),
27
+ )
28
+
29
+ multiple_series = stats.criteriatostatsm2m_set.filter(use_as="multiple_series")
30
+ if multiple_series.exists():
31
+ choices = (
32
+ multiple_series.select_related("stats__default_multiseries_criteria", "criteria")
33
+ .order_by("order")
34
+ .values_list("id", "criteria__criteria_name")
35
+ )
36
+ self.fields["select_box_multiple_series"] = forms.ChoiceField(
37
+ label="Divide",
38
+ choices=[("", "-------")] + list(choices),
39
+ initial=(
40
+ stats.default_multiseries_criteria.id
41
+ if stats.default_multiseries_criteria
42
+ else None
43
+ ),
44
+ )
45
+ self.fields["select_box_multiple_series"].widget.attrs[
46
+ "class"
47
+ ] = "chart-input select_box_multiple_series"
48
+
49
+ if len(stats.allowed_type_operation_field_name) > 1:
50
+ self.fields["select_box_operation"] = forms.ChoiceField(
51
+ choices=stats.allowed_type_operation_field_name_choices(),
52
+ label="Operation",
53
+ initial=stats.type_operation_field_name,
54
+ )
55
+ self.fields["select_box_operation"].widget.attrs["class"] = "chart-input"
56
+
57
+ operations_list = stats.get_operations_list()
58
+ if operations_list and len(operations_list) > 1:
59
+ self.fields["select_box_operation_field"] = forms.ChoiceField(
60
+ choices=[("", "(divide all)")] + [(o, o) for o in operations_list],
61
+ label="Field",
62
+ initial=operations_list[0],
63
+ )
64
+ self.fields["select_box_operation_field"].widget.attrs["class"] = "chart-input"
65
+
66
+ if len(stats.allowed_time_scales) > 1:
67
+ self.fields["select_box_interval"] = forms.ChoiceField(
68
+ choices=stats.allowed_time_scales_choices(),
69
+ label="Scale",
70
+ initial=stats.default_time_scale,
71
+ )
72
+ self.fields["select_box_interval"].widget.attrs["class"] = "chart-input"
73
+
74
+ if len(stats.allowed_chart_types) > 1:
75
+ self.fields["select_box_chart_type"] = forms.ChoiceField(
76
+ choices=stats.allowed_chart_types_choices(),
77
+ label="Chart",
78
+ initial=stats.default_chart_type,
79
+ )
80
+ self.fields["select_box_chart_type"].widget.attrs[
81
+ "class"
82
+ ] = "chart-input select_box_chart_type"
83
+
84
+ self.fields["time_since"] = forms.DateField(
85
+ label="Since",
86
+ initial=(now() - timedelta(days=stats.default_time_period)).strftime("%Y-%m-%d"),
87
+ widget=forms.TextInput(
88
+ attrs={"type": "date", "class": "chart-input select_box_date_since"}
89
+ ),
90
+ )
91
+ self.fields["time_until"] = forms.DateField(
92
+ label="Until",
93
+ initial=now().strftime("%Y-%m-%d"),
94
+ widget=forms.TextInput(
95
+ attrs={"type": "date", "class": "chart-input select_box_date_until"}
96
+ ),
97
+ )
@@ -0,0 +1,130 @@
1
+ from datetime import datetime, timedelta
2
+
3
+ from blenderhub.apps.accounts.models import UserProfile
4
+ from datetime_truncate import truncate
5
+ from django.core.management import BaseCommand
6
+
7
+ from admin_tools_stats.models import (
8
+ DashboardStats,
9
+ Interval,
10
+ get_charts_timezone,
11
+ truncate_ceiling,
12
+ )
13
+
14
+
15
+ class Command(BaseCommand):
16
+ help = "Recalculate all charts with cached values"
17
+
18
+ def add_arguments(self, parser):
19
+ parser.add_argument(
20
+ "graph_key",
21
+ nargs="*",
22
+ type=str,
23
+ help="Recalculate only specified charts",
24
+ )
25
+ parser.add_argument(
26
+ "--exclude",
27
+ help="Exclude graph keys from recalculation separated by comma",
28
+ )
29
+ parser.add_argument(
30
+ "--all-criteria",
31
+ help="Run for all multiseries criteria",
32
+ action="store_true",
33
+ )
34
+ parser.add_argument(
35
+ "--reload-all",
36
+ help="Recalculate all data in range",
37
+ action="store_true",
38
+ )
39
+ parser.add_argument(
40
+ "--dry-run",
41
+ help="Only print what would be recalculated",
42
+ action="store_true",
43
+ )
44
+ parser.add_argument(
45
+ "--time-ranges",
46
+ help="Override time ranges (hours, days, months, quarters, years); Separated by comma",
47
+ )
48
+ parser.add_argument(
49
+ "--periods-count",
50
+ help="Incerease recalculation of charts by more of default periods",
51
+ default=1,
52
+ type=int,
53
+ )
54
+
55
+ def get_all_multiseries_criteria(self, stats, options):
56
+ all_multiseries_criteria = list(
57
+ stats.criteriatostatsm2m_set.filter(use_as="multiple_series", recalculate=True)
58
+ )
59
+ all_multiseries_criteria.append("")
60
+ return all_multiseries_criteria
61
+
62
+ def handle(self, *args, **options):
63
+ stats_query = DashboardStats.objects.filter(cache_values=True, show_to_users=False)
64
+ if options.get("graph_key", None):
65
+ stats_query = stats_query.filter(graph_key__in=options["graph_key"])
66
+ if options.get("exclude", None):
67
+ stats_query = stats_query.exclude(graph_key__in=options["exclude"].split(","))
68
+
69
+ chart_string = "\n".join(
70
+ f"\t{k.ljust(30)}\t{v}"
71
+ for (k, v) in stats_query.values_list("graph_key", "graph_title")
72
+ )
73
+ print(f"recalculating charts: \n{chart_string}")
74
+ for stats in stats_query:
75
+ operation = stats.type_operation_field_name
76
+ user = UserProfile.objects.get(email="petr.dlouhy@email.cz")
77
+ configuration = {
78
+ "select_box_chart_type": stats.default_chart_type,
79
+ "reload": "True",
80
+ "reload_all": str(options["reload_all"]),
81
+ }
82
+ for criteria in stats.criteriatostatsm2m_set.filter(
83
+ use_as="chart_filter",
84
+ criteria__dynamic_criteria_field_name__isnull=False,
85
+ ):
86
+ configuration[f"select_box_dynamic_{criteria.id}"] = criteria.default_option
87
+
88
+ all_multiseries_criteria = self.get_all_multiseries_criteria(stats, options)
89
+
90
+ print(f"recalculating {stats} controls")
91
+ if not options["dry_run"]:
92
+ stats.get_control_form_raw(user=user)
93
+
94
+ for multiseries_criteria in all_multiseries_criteria:
95
+ if multiseries_criteria:
96
+ configuration["select_box_multiple_series"] = multiseries_criteria.id
97
+
98
+ if options.get("time_ranges", None):
99
+ time_scales = options["time_ranges"].split(",")
100
+ else:
101
+ time_scales = stats.allowed_time_scales
102
+
103
+ chart_tz = get_charts_timezone()
104
+ for operation_field in stats.operation_field_name.split(","):
105
+ for selected_interval in time_scales:
106
+ print(
107
+ f"recalculating chart {stats} with {multiseries_criteria} on "
108
+ f"{operation_field} criteria in {selected_interval}"
109
+ )
110
+ time_since = datetime.now() - timedelta(
111
+ days=stats.default_time_period
112
+ ) * options.get("periods_count", 1)
113
+ time_since = truncate(time_since, Interval(selected_interval).val())
114
+ time_since = time_since.astimezone(chart_tz)
115
+
116
+ time_until = datetime.now()
117
+ time_until = truncate_ceiling(time_until, Interval(selected_interval).val())
118
+ time_until = time_until.astimezone(chart_tz)
119
+
120
+ configuration["select_box_interval"] = selected_interval
121
+ if not options["dry_run"]:
122
+ stats.get_multi_time_series_cached(
123
+ configuration=configuration,
124
+ time_since=time_since,
125
+ time_until=time_until,
126
+ interval=Interval(selected_interval),
127
+ operation_choice=operation,
128
+ operation_field_choice=operation_field,
129
+ user=user,
130
+ )