educommon 3.12.0__py3-none-any.whl → 3.13.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.
- educommon/__init__.py +0 -1
- educommon/about/ui/actions.py +16 -30
- educommon/about/ui/ui.py +3 -12
- educommon/about/utils.py +6 -5
- educommon/async_task/__init__.py +0 -1
- educommon/async_task/actions.py +18 -13
- educommon/async_task/apps.py +4 -0
- educommon/async_task/locker.py +2 -5
- educommon/async_task/migrations/0001_initial.py +55 -9
- educommon/async_task/migrations/0002_task_type_and_status_data.py +94 -89
- educommon/async_task/migrations/0003_alter_runningtask_options.py +0 -1
- educommon/async_task/models.py +9 -6
- educommon/async_task/tasks.py +11 -7
- educommon/async_task/ui.py +16 -35
- educommon/async_tasks/__init__.py +0 -1
- educommon/async_tasks/apps.py +4 -0
- educommon/async_tasks/locks.py +11 -21
- educommon/async_tasks/migrations/0001_initial.py +68 -8
- educommon/async_tasks/migrations/0002_load_initial_data.py +0 -1
- educommon/async_tasks/models.py +9 -29
- educommon/async_tasks/tasks.py +25 -54
- educommon/audit_log/__init__.py +1 -0
- educommon/audit_log/actions.py +27 -36
- educommon/audit_log/app_meta.py +7 -4
- educommon/audit_log/apps.py +44 -29
- educommon/audit_log/constants.py +7 -4
- educommon/audit_log/error_log/actions.py +1 -3
- educommon/audit_log/helpers.py +2 -4
- educommon/audit_log/management/commands/reinstall_audit_log.py +11 -7
- educommon/audit_log/migrations/0001_initial.py +91 -16
- educommon/audit_log/migrations/0002_install_audit_log.py +13 -13
- educommon/audit_log/migrations/0003_logproxy.py +1 -3
- educommon/audit_log/migrations/0004_reinstall_audit_log.py +1 -4
- educommon/audit_log/migrations/0005_postgresql_error.py +4 -2
- educommon/audit_log/migrations/0006_auto_20200806_1707.py +3 -4
- educommon/audit_log/migrations/0007_create_selective_tables_function.py +8 -5
- educommon/audit_log/migrations/0008_table_logged.py +0 -1
- educommon/audit_log/migrations/0009_reinstall_audit_log.py +0 -1
- educommon/audit_log/models.py +36 -42
- educommon/audit_log/permissions.py +11 -9
- educommon/audit_log/proxies.py +12 -23
- educommon/audit_log/ui.py +18 -15
- educommon/audit_log/utils/__init__.py +28 -60
- educommon/audit_log/utils/operations.py +16 -2
- educommon/auth/__init__.py +0 -3
- educommon/auth/rbac/__init__.py +2 -4
- educommon/auth/rbac/actions.py +148 -145
- educommon/auth/rbac/app_meta.py +9 -6
- educommon/auth/rbac/backends/base.py +2 -8
- educommon/auth/rbac/backends/caching.py +27 -37
- educommon/auth/rbac/backends/simple.py +1 -4
- educommon/auth/rbac/checker.py +1 -3
- educommon/auth/rbac/management/commands/rbac.py +6 -11
- educommon/auth/rbac/manager.py +18 -47
- educommon/auth/rbac/migrations/0001_initial.py +73 -12
- educommon/auth/rbac/migrations/0002_model_modifier_metaclass_fix.py +7 -6
- educommon/auth/rbac/migrations/0003_permission_hidden.py +1 -5
- educommon/auth/rbac/migrations/0004_auto_20171024_1245.py +26 -19
- educommon/auth/rbac/models.py +63 -68
- educommon/auth/rbac/permissions.py +6 -7
- educommon/auth/rbac/ui.py +83 -84
- educommon/auth/rbac/utils.py +10 -11
- educommon/auth/rbac/validators.py +4 -5
- educommon/auth/simple_auth/__init__.py +1 -5
- educommon/auth/simple_auth/actions.py +79 -92
- educommon/auth/simple_auth/app_meta.py +2 -9
- educommon/auth/simple_auth/checkers.py +3 -3
- educommon/auth/simple_auth/migrations/0001_initial.py +23 -4
- educommon/auth/simple_auth/validators.py +0 -1
- educommon/contingent/actions.py +7 -7
- educommon/contingent/app_meta.py +1 -4
- educommon/contingent/base.py +10 -15
- educommon/contingent/catalogs.py +424 -540
- educommon/contingent/contingent_plugin/actions.py +4 -15
- educommon/contingent/contingent_plugin/apps.py +10 -4
- educommon/contingent/contingent_plugin/migrations/0001_initial.py +5 -6
- educommon/contingent/contingent_plugin/migrations/0002_add_contingent_model_deleted.py +6 -11
- educommon/contingent/contingent_plugin/model_views.py +2 -12
- educommon/contingent/contingent_plugin/models.py +2 -7
- educommon/contingent/contingent_plugin/observer.py +14 -13
- educommon/contingent/contingent_plugin/plugin_meta.py +1 -3
- educommon/contingent/contingent_plugin/storage.py +8 -7
- educommon/contingent/contingent_plugin/utils.py +6 -6
- educommon/django/db/fields.py +72 -86
- educommon/django/db/migration/__init__.py +3 -7
- educommon/django/db/migration/operations.py +29 -51
- educommon/django/db/mixins/__init__.py +16 -10
- educommon/django/db/mixins/date_interval.py +47 -75
- educommon/django/db/mixins/validation.py +26 -26
- educommon/django/db/model_view/__init__.py +18 -22
- educommon/django/db/models.py +9 -8
- educommon/django/db/observer.py +9 -27
- educommon/django/db/partitioning/__init__.py +66 -92
- educommon/django/db/partitioning/management/commands/apply_partitioning.py +3 -13
- educommon/django/db/partitioning/management/commands/clear_table.py +18 -14
- educommon/django/db/partitioning/management/commands/split_table.py +18 -13
- educommon/django/db/routers.py +6 -15
- educommon/django/db/signals.py +149 -2
- educommon/django/db/utils.py +14 -19
- educommon/django/db/validators/__init__.py +1 -0
- educommon/django/db/validators/simple.py +72 -100
- educommon/django/storages/atcfs/api.py +39 -53
- educommon/django/storages/atcfs/app_meta.py +1 -1
- educommon/django/storages/atcfs/management/commands/atcfs_migrate.py +42 -55
- educommon/django/storages/atcfs/models.py +0 -3
- educommon/django/storages/atcfs/monkey_patching.py +18 -12
- educommon/django/storages/atcfs/storage.py +14 -23
- educommon/extjs/fields/input_params.py +15 -45
- educommon/importer/XLSReader.py +143 -241
- educommon/importer/__init__.py +86 -4
- educommon/importer/api.py +53 -84
- educommon/importer/constants.py +4 -14
- educommon/importer/loggers.py +16 -26
- educommon/importer/proxy.py +131 -176
- educommon/importer/proxy_import.py +11 -12
- educommon/importer/report.py +4 -6
- educommon/importer/ui.py +32 -26
- educommon/importer/validators.py +4 -7
- educommon/integration_entities/helpers.py +14 -18
- educommon/ioc/__init__.py +3 -6
- educommon/logger/loggers.py +10 -14
- educommon/m3/__init__.py +20 -38
- educommon/m3/extensions/__init__.py +1 -0
- educommon/m3/extensions/listeners/__init__.py +22 -38
- educommon/m3/extensions/listeners/delete_check/listeners.py +31 -41
- educommon/m3/extensions/listeners/delete_check/mixins.py +20 -25
- educommon/m3/extensions/listeners/delete_check/signals.py +2 -2
- educommon/m3/extensions/listeners/delete_check/ui.py +15 -14
- educommon/m3/extensions/listeners/delete_check/utils.py +9 -11
- educommon/m3/extensions/ui.py +15 -33
- educommon/m3/transaction_context.py +17 -19
- educommon/objectpack/actions.py +70 -88
- educommon/objectpack/apps.py +5 -0
- educommon/objectpack/filters.py +9 -15
- educommon/objectpack/ui.py +59 -77
- educommon/report/__init__.py +9 -5
- educommon/report/actions.py +29 -32
- educommon/report/constructor/__init__.py +5 -8
- educommon/report/constructor/app_meta.py +1 -3
- educommon/report/constructor/apps.py +1 -0
- educommon/report/constructor/base.py +33 -80
- educommon/report/constructor/builders/excel/_base.py +138 -286
- educommon/report/constructor/builders/excel/_header.py +2 -9
- educommon/report/constructor/builders/excel/product.py +13 -34
- educommon/report/constructor/builders/excel/with_merged_cells.py +18 -14
- educommon/report/constructor/config.py +2 -0
- educommon/report/constructor/editor/actions.py +101 -215
- educommon/report/constructor/editor/ui.py +71 -93
- educommon/report/constructor/exceptions.py +6 -12
- educommon/report/constructor/migrations/0001_initial.py +36 -44
- educommon/report/constructor/migrations/0002_report_filters.py +86 -72
- educommon/report/constructor/migrations/0003_reportfilter_exclude.py +5 -5
- educommon/report/constructor/migrations/0004_reportfilter_fields.py +22 -18
- educommon/report/constructor/migrations/0005_reportcolumn_visible.py +5 -4
- educommon/report/constructor/migrations/0006_reportsorting.py +21 -17
- educommon/report/constructor/migrations/0007_include_available_units.py +14 -14
- educommon/report/constructor/migrations/0008_auto_20170407_1318.py +4 -5
- educommon/report/constructor/migrations/0009_auto_20180405_0642.py +1 -4
- educommon/report/constructor/migrations/0010_add_aggregate_fields.py +7 -8
- educommon/report/constructor/mixins.py +14 -15
- educommon/report/constructor/models.py +76 -124
- educommon/report/constructor/utils.py +3 -8
- educommon/report/constructor/validators.py +1 -3
- educommon/report/reporter.py +25 -43
- educommon/report/utils.py +14 -40
- educommon/rest/actions.py +7 -11
- educommon/rest/context.py +6 -16
- educommon/rest/controllers.py +10 -10
- educommon/rest/mixins.py +29 -27
- educommon/secure_media/app_meta.py +9 -9
- educommon/utils/__init__.py +3 -2
- educommon/utils/caching.py +1 -3
- educommon/utils/conversion.py +1 -3
- educommon/utils/crypto.py +1 -2
- educommon/utils/date.py +13 -26
- educommon/utils/db/__init__.py +17 -26
- educommon/utils/db/postgresql.py +1 -4
- educommon/utils/fonts/__init__.py +3 -4
- educommon/utils/licence/__init__.py +5 -16
- educommon/utils/misc.py +9 -18
- educommon/utils/object_grid.py +55 -62
- educommon/utils/phone_number/modelfields.py +1 -3
- educommon/utils/phone_number/phone_number.py +5 -8
- educommon/utils/phone_number/validators.py +8 -23
- educommon/utils/plugins.py +15 -28
- educommon/utils/registry.py +2 -1
- educommon/utils/seqtools.py +1 -3
- educommon/utils/serializer.py +9 -16
- educommon/utils/storage.py +3 -2
- educommon/utils/system.py +1 -3
- educommon/utils/system_app/management/commands/delete_objects.py +17 -34
- educommon/utils/ui.py +87 -84
- educommon/utils/xml/__init__.py +2 -7
- educommon/utils/xml/resolver.py +1 -0
- educommon/ws_log/actions.py +31 -76
- educommon/ws_log/base.py +6 -20
- educommon/ws_log/migrations/0001_initial.py +25 -8
- educommon/ws_log/migrations/0002_auto_20160628_1334.py +0 -1
- educommon/ws_log/migrations/0003_add_fields_to_smev_logs.py +20 -4
- educommon/ws_log/migrations/0004_auto_20160727_1600.py +7 -6
- educommon/ws_log/migrations/0005_auto_20161130_1615.py +14 -4
- educommon/ws_log/migrations/0006_auto_20170327_1027.py +3 -2
- educommon/ws_log/migrations/0007_auto_20180607_1040.py +8 -9
- educommon/ws_log/migrations/0008_auto_20180713_1445.py +23 -10
- educommon/ws_log/migrations/0009_auto_20201130_1553.py +7 -2
- educommon/ws_log/models.py +21 -35
- educommon/ws_log/provider.py +2 -1
- educommon/ws_log/report.py +8 -13
- educommon/ws_log/smev/applications.py +12 -27
- educommon/ws_log/smev/exceptions.py +2 -3
- educommon/ws_log/ui.py +32 -32
- educommon/ws_log/utils.py +1 -3
- educommon-3.13.2.dist-info/METADATA +57 -0
- educommon-3.13.2.dist-info/RECORD +354 -0
- {educommon-3.12.0.dist-info → educommon-3.13.2.dist-info}/WHEEL +1 -1
- educommon/utils/patches.py +0 -27
- educommon/version.conf +0 -11
- educommon-3.12.0.dist-info/METADATA +0 -47
- educommon-3.12.0.dist-info/RECORD +0 -357
- educommon-3.12.0.dist-info/dependency_links.txt +0 -1
- {educommon-3.12.0.dist-info → educommon-3.13.2.dist-info}/top_level.txt +0 -0
@@ -59,9 +59,7 @@ class ReportTemplate(CascadeDeleteMixin, BaseModel):
|
|
59
59
|
choices=FORMAT_CHOICES,
|
60
60
|
default=USER_DEFINED,
|
61
61
|
)
|
62
|
-
include_available_units = models.BooleanField(
|
63
|
-
'Отображать данные по дочерним организациям', default=False
|
64
|
-
)
|
62
|
+
include_available_units = models.BooleanField('Отображать данные по дочерним организациям', default=False)
|
65
63
|
|
66
64
|
class Meta:
|
67
65
|
verbose_name = 'Отчет'
|
@@ -78,26 +76,23 @@ class ReportTemplate(CascadeDeleteMixin, BaseModel):
|
|
78
76
|
query = query.exclude(pk=self.pk)
|
79
77
|
|
80
78
|
if query.exists():
|
81
|
-
errors['title'].append(
|
82
|
-
'Шаблон отчета с подобным именем уже существует.'
|
83
|
-
)
|
79
|
+
errors['title'].append('Шаблон отчета с подобным именем уже существует.')
|
84
80
|
# ---------------------------------------------------------------------
|
85
|
-
super(
|
81
|
+
super().simple_clean(errors)
|
86
82
|
|
87
83
|
@cached_property
|
88
84
|
def data_source(self):
|
89
85
|
if self.data_source_name not in registry:
|
90
86
|
raise ValidationError(
|
91
|
-
'В шаблоне отчета указан несуществующий источник данных ({}).'
|
92
|
-
.format(self.data_source_name)
|
87
|
+
'В шаблоне отчета указан несуществующий источник данных ({}).'.format(self.data_source_name)
|
93
88
|
)
|
94
|
-
data_source = registry.get(
|
95
|
-
self.data_source_name
|
96
|
-
).get_data_source_descriptor()
|
89
|
+
data_source = registry.get(self.data_source_name).get_data_source_descriptor()
|
97
90
|
return data_source
|
98
91
|
|
99
92
|
def __str__(self):
|
100
93
|
return 'Шаблон отчета: {}'.format(self.title)
|
94
|
+
|
95
|
+
|
101
96
|
# -----------------------------------------------------------------------------
|
102
97
|
|
103
98
|
|
@@ -129,63 +124,38 @@ class ReportColumn(BaseModel):
|
|
129
124
|
title = models.CharField(
|
130
125
|
'Отображаемое имя',
|
131
126
|
max_length=300,
|
132
|
-
null=True,
|
133
|
-
)
|
134
|
-
by_value = models.PositiveSmallIntegerField(
|
135
|
-
'Промежуточный итог',
|
136
|
-
choices=constants.BY_VALUE_CHOICES,
|
127
|
+
null=True,
|
137
128
|
blank=True,
|
138
|
-
null=True
|
139
129
|
)
|
140
|
-
|
141
|
-
'
|
142
|
-
choices=constants.TOTAL_CHOICES,
|
143
|
-
blank=True,
|
144
|
-
null=True
|
130
|
+
by_value = models.PositiveSmallIntegerField(
|
131
|
+
'Промежуточный итог', choices=constants.BY_VALUE_CHOICES, blank=True, null=True
|
145
132
|
)
|
133
|
+
total = models.PositiveSmallIntegerField('Итог', choices=constants.TOTAL_CHOICES, blank=True, null=True)
|
146
134
|
|
147
135
|
cascade_delete_for = (report_template,)
|
148
136
|
|
149
137
|
class Meta:
|
150
138
|
verbose_name = 'Столбец отчета'
|
151
139
|
verbose_name_plural = 'Столбцы отчетов'
|
152
|
-
unique_together = (
|
153
|
-
|
154
|
-
)
|
155
|
-
ordering = (
|
156
|
-
'index',
|
157
|
-
)
|
140
|
+
unique_together = (('report_template', 'name'),)
|
141
|
+
ordering = ('index',)
|
158
142
|
|
159
143
|
def _clean_aggregator_type(self, errors):
|
160
144
|
"""Проверяет на соответствие типов итогов."""
|
161
|
-
if not (
|
162
|
-
(
|
163
|
-
(any(not bool(item) for item in (self.by_value, self.total)))
|
164
|
-
):
|
165
|
-
errors['by_value'].append(
|
166
|
-
'Тип для "Промежуточный итог" и "Итог" должен совпадать.'
|
167
|
-
)
|
145
|
+
if not ((self.by_value == self.total) or (any(not bool(item) for item in (self.by_value, self.total)))):
|
146
|
+
errors['by_value'].append('Тип для "Промежуточный итог" и "Итог" должен совпадать.')
|
168
147
|
|
169
148
|
def _clean_aggregator_data_type(self, errors):
|
170
149
|
"""Проверяет тип данных для итогов."""
|
171
150
|
# Определяем тип данных в колонке.
|
172
|
-
data_source_descriptor = registry.get(
|
173
|
-
|
174
|
-
).get_data_source_descriptor()
|
175
|
-
column_descriptor = data_source_descriptor.get_column_descriptor(
|
176
|
-
self.name
|
177
|
-
)
|
151
|
+
data_source_descriptor = registry.get(self.report_template.data_source_name).get_data_source_descriptor()
|
152
|
+
column_descriptor = data_source_descriptor.get_column_descriptor(self.name)
|
178
153
|
is_num_data_type = column_descriptor.data_type == constants.CT_NUMBER
|
179
154
|
# Определяем тип Промежуточного итога.
|
180
|
-
is_sum_by_value =
|
181
|
-
self.by_value and self.by_value == constants.BY_VALUE_SUM
|
182
|
-
)
|
155
|
+
is_sum_by_value = self.by_value and self.by_value == constants.BY_VALUE_SUM
|
183
156
|
# Определяем тип Итога.
|
184
157
|
is_total_sum = self.total and self.total == constants.TOTAL_SUM
|
185
|
-
aggregator_info = (
|
186
|
-
('by_value', is_sum_by_value),
|
187
|
-
('total', is_total_sum)
|
188
|
-
)
|
158
|
+
aggregator_info = (('by_value', is_sum_by_value), ('total', is_total_sum))
|
189
159
|
for aggregator_type, is_sum in aggregator_info:
|
190
160
|
if is_sum and not is_num_data_type:
|
191
161
|
errors[aggregator_type].append(
|
@@ -199,10 +169,12 @@ class ReportColumn(BaseModel):
|
|
199
169
|
self._clean_aggregator_type(errors)
|
200
170
|
self._clean_aggregator_data_type(errors)
|
201
171
|
|
202
|
-
super(
|
172
|
+
super().simple_clean(errors)
|
203
173
|
|
204
174
|
def __str__(self):
|
205
175
|
return self.title
|
176
|
+
|
177
|
+
|
206
178
|
# -----------------------------------------------------------------------------
|
207
179
|
|
208
180
|
|
@@ -239,7 +211,8 @@ class ReportFilterGroup(MPTTModel, BaseModel):
|
|
239
211
|
)
|
240
212
|
parent = TreeForeignKey(
|
241
213
|
'self',
|
242
|
-
blank=True,
|
214
|
+
blank=True,
|
215
|
+
null=True,
|
243
216
|
verbose_name='Родительская группа',
|
244
217
|
related_name='nested_groups',
|
245
218
|
on_delete=models.CASCADE,
|
@@ -278,10 +251,10 @@ class ReportFilterGroup(MPTTModel, BaseModel):
|
|
278
251
|
report_filter = instance
|
279
252
|
|
280
253
|
if (
|
281
|
-
not report_filter.pk
|
282
|
-
not report_filter.group_id
|
283
|
-
not report_filter.column_id
|
284
|
-
not report_filter.group.report_template_id
|
254
|
+
not report_filter.pk
|
255
|
+
or not report_filter.group_id
|
256
|
+
or not report_filter.column_id
|
257
|
+
or not report_filter.group.report_template_id
|
285
258
|
):
|
286
259
|
return
|
287
260
|
|
@@ -293,43 +266,42 @@ class ReportFilterGroup(MPTTModel, BaseModel):
|
|
293
266
|
if column.report_template_id != group.report_template_id:
|
294
267
|
errors['column'].append(
|
295
268
|
'Шаблон, к которому относится столбец фильтра "{}" '
|
296
|
-
'отличается от шаблона, к которому относится группа фильтров.'
|
297
|
-
.format(column.name)
|
269
|
+
'отличается от шаблона, к которому относится группа фильтров.'.format(column.name)
|
298
270
|
)
|
299
271
|
# ---------------------------------------------------------------------
|
300
272
|
|
301
273
|
def simple_clean(self, errors):
|
302
|
-
super(
|
274
|
+
super().simple_clean(errors)
|
303
275
|
# ---------------------------------------------------------------------
|
304
276
|
# Все столбцы в фильтрах группы должны быть в том же шаблоне, что и
|
305
277
|
# данная группа.
|
306
278
|
|
307
279
|
if (
|
308
|
-
self.pk
|
309
|
-
self.report_template_id
|
310
|
-
self.filters.exclude(
|
280
|
+
self.pk
|
281
|
+
and self.report_template_id
|
282
|
+
and self.filters.exclude(
|
311
283
|
column__report_template=self.report_template_id,
|
312
284
|
).exists()
|
313
285
|
):
|
314
|
-
errors['report_template'].append(
|
315
|
-
'В данной группе есть фильтры, ссылающиеся на столбцы из '
|
316
|
-
'другого шаблона.'
|
317
|
-
)
|
286
|
+
errors['report_template'].append('В данной группе есть фильтры, ссылающиеся на столбцы из другого шаблона.')
|
318
287
|
# ---------------------------------------------------------------------
|
319
288
|
# Шаблоны данной группы и родительской должны совпадать.
|
320
289
|
|
321
290
|
if (
|
322
|
-
self.pk
|
323
|
-
|
324
|
-
|
291
|
+
self.pk
|
292
|
+
and self.report_template_id
|
293
|
+
and self.parent
|
294
|
+
and self.parent.report_template_id
|
295
|
+
and self.report_template_id != self.parent.report_template_id
|
325
296
|
):
|
326
297
|
errors['parent'].append(
|
327
|
-
'Шаблон данной группы фильтров ("{}") отличается от шаблона '
|
328
|
-
'родительской группы ("{}").'.format(
|
298
|
+
'Шаблон данной группы фильтров ("{}") отличается от шаблона родительской группы ("{}").'.format(
|
329
299
|
self.report_template.title,
|
330
300
|
self.parent.report_template.title,
|
331
301
|
)
|
332
302
|
)
|
303
|
+
|
304
|
+
|
333
305
|
# -----------------------------------------------------------------------------
|
334
306
|
|
335
307
|
|
@@ -365,13 +337,16 @@ class ReportFilter(BaseModel):
|
|
365
337
|
values = ArrayField(
|
366
338
|
models.TextField(
|
367
339
|
'Значение',
|
368
|
-
blank=True,
|
340
|
+
blank=True,
|
341
|
+
null=True,
|
369
342
|
),
|
370
|
-
blank=True,
|
343
|
+
blank=True,
|
344
|
+
null=True,
|
371
345
|
)
|
372
346
|
comment = models.TextField(
|
373
347
|
'Описание фильтра',
|
374
|
-
blank=True,
|
348
|
+
blank=True,
|
349
|
+
null=True,
|
375
350
|
)
|
376
351
|
|
377
352
|
cascade_delete_for = (group, column)
|
@@ -382,76 +357,52 @@ class ReportFilter(BaseModel):
|
|
382
357
|
"<значение>", <без учета|с учетом> регистра`
|
383
358
|
:return:
|
384
359
|
"""
|
385
|
-
template =
|
386
|
-
'Поле "{col}"{invert__op:10}{operator} '
|
387
|
-
'"{value}", {case} регистра'
|
388
|
-
)
|
360
|
+
template = 'Поле "{col}"{invert__op:10}{operator} "{value}", {case} регистра'
|
389
361
|
invert__op = ' не ' if self.exclude else ' '
|
390
362
|
case = 'с учетом' if self.case_sensitive else 'без учета'
|
391
|
-
return template.format(
|
392
|
-
|
393
|
-
|
363
|
+
return template.format(
|
364
|
+
col=self.column,
|
365
|
+
invert__op=invert__op,
|
366
|
+
operator=self.get_operator_display().lower(),
|
367
|
+
value=', '.join(self.values),
|
368
|
+
case=case,
|
369
|
+
)
|
394
370
|
|
395
371
|
class Meta:
|
396
372
|
verbose_name = 'Фильтр'
|
397
373
|
verbose_name_plural = 'Фильтры'
|
398
|
-
unique_together = (
|
399
|
-
|
400
|
-
)
|
401
|
-
ordering = (
|
402
|
-
'index',
|
403
|
-
)
|
374
|
+
unique_together = (('column', 'index'),)
|
375
|
+
ordering = ('index',)
|
404
376
|
|
405
377
|
def simple_clean(self, errors):
|
406
|
-
super(
|
378
|
+
super().simple_clean(errors)
|
407
379
|
# ---------------------------------------------------------------------
|
408
380
|
# Значение должно быть указано для всех операторов, кроме IS_NULL.
|
409
381
|
|
410
382
|
if self.operator == constants.IS_NULL:
|
411
383
|
self.values = None
|
412
384
|
|
413
|
-
elif (
|
414
|
-
self.operator == constants.BETWEEN and
|
415
|
-
(
|
416
|
-
not self.values or
|
417
|
-
len(self.values) != 2
|
418
|
-
)
|
419
|
-
):
|
385
|
+
elif self.operator == constants.BETWEEN and (not self.values or len(self.values) != 2):
|
420
386
|
errors['values'].append(
|
421
|
-
'Для оператора "{}" должно быть указано два значения.'
|
422
|
-
.format(self.get_operator_display())
|
387
|
+
'Для оператора "{}" должно быть указано два значения.'.format(self.get_operator_display())
|
423
388
|
)
|
424
389
|
|
425
|
-
elif (
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
)
|
431
|
-
):
|
432
|
-
errors['values'].append(
|
433
|
-
'Должно быть указано только одно значение.'
|
434
|
-
)
|
390
|
+
elif self.operator not in (
|
391
|
+
constants.IN,
|
392
|
+
constants.BETWEEN,
|
393
|
+
) and (not self.values or len(self.values) > 1):
|
394
|
+
errors['values'].append('Должно быть указано только одно значение.')
|
435
395
|
|
436
396
|
elif not self.values:
|
437
397
|
errors['values'].append('Не указано значение для сравнения.')
|
438
398
|
# ---------------------------------------------------------------------
|
439
399
|
|
440
|
-
if
|
441
|
-
|
442
|
-
self.group.report_template_id != self.column.report_template_id
|
443
|
-
):
|
444
|
-
errors['column'].append(
|
445
|
-
'Группа и колонка принадлежат разным шаблонам.'
|
446
|
-
)
|
400
|
+
if self.group and self.column and self.group.report_template_id != self.column.report_template_id:
|
401
|
+
errors['column'].append('Группа и колонка принадлежат разным шаблонам.')
|
447
402
|
# ---------------------------------------------------------------------
|
448
403
|
|
449
|
-
data_source_descriptor = registry.get(
|
450
|
-
|
451
|
-
).get_data_source_descriptor()
|
452
|
-
column_descriptor = data_source_descriptor.get_column_descriptor(
|
453
|
-
self.column.name
|
454
|
-
)
|
404
|
+
data_source_descriptor = registry.get(self.group.report_template.data_source_name).get_data_source_descriptor()
|
405
|
+
column_descriptor = data_source_descriptor.get_column_descriptor(self.column.name)
|
455
406
|
data_type = column_descriptor.data_type
|
456
407
|
valid_operators = constants.VALID_OPERATORS[data_type]
|
457
408
|
if self.operator and self.operator not in valid_operators:
|
@@ -461,6 +412,8 @@ class ReportFilter(BaseModel):
|
|
461
412
|
column_descriptor.get_full_title(),
|
462
413
|
)
|
463
414
|
)
|
415
|
+
|
416
|
+
|
464
417
|
# -----------------------------------------------------------------------------
|
465
418
|
|
466
419
|
|
@@ -487,11 +440,10 @@ class ReportSorting(BaseModel):
|
|
487
440
|
ordering = ('index',)
|
488
441
|
|
489
442
|
def __str__(self):
|
490
|
-
return '{col} ({direction})'.format(
|
491
|
-
|
492
|
-
|
443
|
+
return '{col} ({direction})'.format(col=self.column, direction=self.get_direction_display().lower())
|
444
|
+
|
445
|
+
|
493
446
|
# -----------------------------------------------------------------------------
|
494
447
|
|
495
448
|
|
496
|
-
post_clean.connect(ReportFilterGroup.check_filter, ReportFilter,
|
497
|
-
dispatch_uid='ReportFilterGroup.check_filter')
|
449
|
+
post_clean.connect(ReportFilterGroup.check_filter, ReportFilter, dispatch_uid='ReportFilterGroup.check_filter')
|
@@ -72,6 +72,7 @@ def get_field(model, name):
|
|
72
72
|
from educommon.report.constructor.base import (
|
73
73
|
ExtraField,
|
74
74
|
)
|
75
|
+
|
75
76
|
field = params['field']
|
76
77
|
field.model = model if isclass(model) else model.__class__
|
77
78
|
field.name = name
|
@@ -116,10 +117,7 @@ def get_nested_field(model, field_name):
|
|
116
117
|
try:
|
117
118
|
return get_nested_field(attr_value, nested_attr)
|
118
119
|
except FieldDoesNotExist:
|
119
|
-
raise FieldDoesNotExist(
|
120
|
-
"{} has not field named '{}'"
|
121
|
-
.format(model.__name__, field_name)
|
122
|
-
)
|
120
|
+
raise FieldDoesNotExist("{} has not field named '{}'".format(model.__name__, field_name))
|
123
121
|
else:
|
124
122
|
return attr_value
|
125
123
|
|
@@ -174,10 +172,7 @@ def get_field_value_by_display(field, display_value):
|
|
174
172
|
if display.lower() == display_value.lower():
|
175
173
|
return value
|
176
174
|
|
177
|
-
raise ValueError(
|
178
|
-
'Значения "{}" нет среди вариантов выбора.'
|
179
|
-
.format(display_value)
|
180
|
-
)
|
175
|
+
raise ValueError('Значения "{}" нет среди вариантов выбора.'.format(display_value))
|
181
176
|
|
182
177
|
|
183
178
|
def get_data_type(field):
|
@@ -13,6 +13,4 @@ def validate_data_source_name(value):
|
|
13
13
|
Источник данных должен быть зарегистрирован в реестре.
|
14
14
|
"""
|
15
15
|
if value not in registry:
|
16
|
-
raise ValidationError(
|
17
|
-
'Источник данных "{}" не существует.'.format(value)
|
18
|
-
)
|
16
|
+
raise ValidationError('Источник данных "{}" не существует.'.format(value))
|
educommon/report/reporter.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
"Модуль с построителями отчетов"
|
2
|
+
|
2
3
|
import os
|
3
4
|
import sys
|
4
5
|
import uuid
|
@@ -42,8 +43,8 @@ def get_url(filename):
|
|
42
43
|
|
43
44
|
|
44
45
|
class SimpleReporter:
|
45
|
-
"""
|
46
|
-
|
46
|
+
"""Объект занимающийся комплексным построением отчета на основе simple_report.
|
47
|
+
|
47
48
|
1) инстанцированием и загрузкой данных провайдера
|
48
49
|
2) инстанцированием билдера
|
49
50
|
3) построением отчета
|
@@ -55,6 +56,7 @@ class SimpleReporter:
|
|
55
56
|
reporter = MySimpleReporter(provider_params, builder_params)
|
56
57
|
report_url = reporter.make_report()
|
57
58
|
"""
|
59
|
+
|
58
60
|
# доступные форматы
|
59
61
|
_available_extensions = ['.xls', '.xlsx', '.docx']
|
60
62
|
|
@@ -96,8 +98,7 @@ class SimpleReporter:
|
|
96
98
|
:type default_base_name: str
|
97
99
|
:returns: str - полный путь к шаблону
|
98
100
|
"""
|
99
|
-
if
|
100
|
-
os.path.isabs(self.template_file_path)):
|
101
|
+
if self.template_file_path is not None and os.path.isabs(self.template_file_path):
|
101
102
|
return self.template_file_path
|
102
103
|
|
103
104
|
report_file_path = sys.modules[self.__module__].__file__
|
@@ -105,27 +106,23 @@ class SimpleReporter:
|
|
105
106
|
report_dir = os.path.dirname(report_file_path)
|
106
107
|
|
107
108
|
rel_sub_path = ''
|
108
|
-
if
|
109
|
-
|
110
|
-
rel_sub_path = os.path.relpath(
|
111
|
-
os.path.dirname(self.template_file_path))
|
109
|
+
if self.template_file_path is not None and self.template_file_path.startswith('./'):
|
110
|
+
rel_sub_path = os.path.relpath(os.path.dirname(self.template_file_path))
|
112
111
|
|
113
112
|
if self.template_file_path is None:
|
114
113
|
base_name, _ = os.path.splitext(os.path.basename(report_dir))
|
115
114
|
else:
|
116
|
-
base_name, _ = os.path.splitext(
|
117
|
-
os.path.basename(self.template_file_path))
|
115
|
+
base_name, _ = os.path.splitext(os.path.basename(self.template_file_path))
|
118
116
|
if not base_name:
|
119
117
|
base_name = default_base_name
|
120
118
|
|
121
|
-
auto_report_name = '{0}{1}{2}'.format(
|
122
|
-
|
123
|
-
|
124
|
-
|
119
|
+
auto_report_name = '{0}{1}{2}'.format(base_name, os.path.extsep, self.extension.strip('.'))
|
120
|
+
auto_report_path = os.path.join(report_dir, rel_sub_path, auto_report_name)
|
121
|
+
|
122
|
+
assert os.path.isfile(auto_report_path), "Report template '{0}' not found at {1}".format(
|
123
|
+
auto_report_name, auto_report_path
|
124
|
+
)
|
125
125
|
|
126
|
-
assert os.path.isfile(auto_report_path), (
|
127
|
-
"Report template '{0}' not found at {1}".format(
|
128
|
-
auto_report_name, auto_report_path))
|
129
126
|
return auto_report_path
|
130
127
|
|
131
128
|
def set_up_report(self):
|
@@ -133,8 +130,7 @@ class SimpleReporter:
|
|
133
130
|
template_path = self.get_template()
|
134
131
|
|
135
132
|
if self.extension == '.xls':
|
136
|
-
report = SpreadsheetReport(
|
137
|
-
template_path, wrapper=DocumentXLS, type=FileConverter.XLS)
|
133
|
+
report = SpreadsheetReport(template_path, wrapper=DocumentXLS, type=FileConverter.XLS)
|
138
134
|
elif self.extension == '.xlsx':
|
139
135
|
report = SpreadsheetReport(template_path)
|
140
136
|
elif self.extension == '.docx':
|
@@ -145,8 +141,8 @@ class SimpleReporter:
|
|
145
141
|
return report
|
146
142
|
|
147
143
|
def set_file_and_url(self):
|
148
|
-
"""
|
149
|
-
|
144
|
+
"""Возвращает кортеж из абс. пути отчёта и url для скачивания.
|
145
|
+
|
150
146
|
Не требует переопределения.
|
151
147
|
"""
|
152
148
|
title = self.builder_params.get('title')
|
@@ -158,8 +154,7 @@ class SimpleReporter:
|
|
158
154
|
return (out_file_path, out_file_url)
|
159
155
|
|
160
156
|
def create_provider(self):
|
161
|
-
"""
|
162
|
-
Кастомный метод для создания экземпляра класса провайдера.
|
157
|
+
"""Кастомный метод для создания экземпляра класса провайдера.
|
163
158
|
|
164
159
|
Используется в случае необходимости явного вызова конструктора
|
165
160
|
провайдера, например, для композитного провайдера.
|
@@ -167,9 +162,7 @@ class SimpleReporter:
|
|
167
162
|
"""
|
168
163
|
|
169
164
|
def init_provider(self, data_provider):
|
170
|
-
"""
|
171
|
-
Инициализирует дата-провайдер с параметрами self.provider_params
|
172
|
-
"""
|
165
|
+
"""Инициализирует дата-провайдер с параметрами self.provider_params."""
|
173
166
|
data_provider.init(**self.provider_params)
|
174
167
|
|
175
168
|
def create_builder(self, data_provider, report):
|
@@ -179,16 +172,10 @@ class SimpleReporter:
|
|
179
172
|
|
180
173
|
:returns: билдер с параметрами self.builder_params
|
181
174
|
"""
|
182
|
-
return self.builder_class(
|
183
|
-
data_provider,
|
184
|
-
adapter=self.adapter_class,
|
185
|
-
report=report,
|
186
|
-
params=self.builder_params
|
187
|
-
)
|
175
|
+
return self.builder_class(data_provider, adapter=self.adapter_class, report=report, params=self.builder_params)
|
188
176
|
|
189
177
|
def get_data_provider(self):
|
190
|
-
"""
|
191
|
-
Создание провайдера и взятие данных.
|
178
|
+
"""Создание провайдера и взятие данных.
|
192
179
|
|
193
180
|
:return: Provider с уже сформированным результатом
|
194
181
|
"""
|
@@ -205,8 +192,7 @@ class SimpleReporter:
|
|
205
192
|
return data_provider
|
206
193
|
|
207
194
|
def _get_report_builder(self, data_provider, report):
|
208
|
-
"""
|
209
|
-
Создание билдера и построение отчета.
|
195
|
+
"""Создание билдера и построение отчета.
|
210
196
|
|
211
197
|
:param data_provider: Provider с уже сформированным результатом.
|
212
198
|
:param report: Отчет форматов .xls, .xlsx или .docx
|
@@ -217,8 +203,7 @@ class SimpleReporter:
|
|
217
203
|
return report_builder.build()
|
218
204
|
|
219
205
|
def create_dir(self, out_file):
|
220
|
-
"""
|
221
|
-
Создание директории для сохранения файла
|
206
|
+
"""Создание директории для сохранения файла.
|
222
207
|
|
223
208
|
:param out_file: str, путь к файлу.
|
224
209
|
:raise: OsError
|
@@ -232,8 +217,7 @@ class SimpleReporter:
|
|
232
217
|
os.makedirs(cat)
|
233
218
|
|
234
219
|
def build_report(self, report, out_file, params):
|
235
|
-
"""
|
236
|
-
Построение отчета.
|
220
|
+
"""Построение отчета.
|
237
221
|
|
238
222
|
:param report: Отчет форматов .xls, .xlsx или .docx
|
239
223
|
:param out_file: str, путь к файлу.
|
@@ -247,9 +231,7 @@ class SimpleReporter:
|
|
247
231
|
report.build(out_file)
|
248
232
|
|
249
233
|
def make_report(self):
|
250
|
-
"""
|
251
|
-
Основной метод, выполняющий построение отчета
|
252
|
-
"""
|
234
|
+
"""Основной метод, выполняющий построение отчета."""
|
253
235
|
# получение абс. пути отчёта
|
254
236
|
out_file, out_url = self.set_file_and_url()
|
255
237
|
# настройка формата отчета
|