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.
Files changed (221) hide show
  1. educommon/__init__.py +0 -1
  2. educommon/about/ui/actions.py +16 -30
  3. educommon/about/ui/ui.py +3 -12
  4. educommon/about/utils.py +6 -5
  5. educommon/async_task/__init__.py +0 -1
  6. educommon/async_task/actions.py +18 -13
  7. educommon/async_task/apps.py +4 -0
  8. educommon/async_task/locker.py +2 -5
  9. educommon/async_task/migrations/0001_initial.py +55 -9
  10. educommon/async_task/migrations/0002_task_type_and_status_data.py +94 -89
  11. educommon/async_task/migrations/0003_alter_runningtask_options.py +0 -1
  12. educommon/async_task/models.py +9 -6
  13. educommon/async_task/tasks.py +11 -7
  14. educommon/async_task/ui.py +16 -35
  15. educommon/async_tasks/__init__.py +0 -1
  16. educommon/async_tasks/apps.py +4 -0
  17. educommon/async_tasks/locks.py +11 -21
  18. educommon/async_tasks/migrations/0001_initial.py +68 -8
  19. educommon/async_tasks/migrations/0002_load_initial_data.py +0 -1
  20. educommon/async_tasks/models.py +9 -29
  21. educommon/async_tasks/tasks.py +25 -54
  22. educommon/audit_log/__init__.py +1 -0
  23. educommon/audit_log/actions.py +27 -36
  24. educommon/audit_log/app_meta.py +7 -4
  25. educommon/audit_log/apps.py +44 -29
  26. educommon/audit_log/constants.py +7 -4
  27. educommon/audit_log/error_log/actions.py +1 -3
  28. educommon/audit_log/helpers.py +2 -4
  29. educommon/audit_log/management/commands/reinstall_audit_log.py +11 -7
  30. educommon/audit_log/migrations/0001_initial.py +91 -16
  31. educommon/audit_log/migrations/0002_install_audit_log.py +13 -13
  32. educommon/audit_log/migrations/0003_logproxy.py +1 -3
  33. educommon/audit_log/migrations/0004_reinstall_audit_log.py +1 -4
  34. educommon/audit_log/migrations/0005_postgresql_error.py +4 -2
  35. educommon/audit_log/migrations/0006_auto_20200806_1707.py +3 -4
  36. educommon/audit_log/migrations/0007_create_selective_tables_function.py +8 -5
  37. educommon/audit_log/migrations/0008_table_logged.py +0 -1
  38. educommon/audit_log/migrations/0009_reinstall_audit_log.py +0 -1
  39. educommon/audit_log/models.py +36 -42
  40. educommon/audit_log/permissions.py +11 -9
  41. educommon/audit_log/proxies.py +12 -23
  42. educommon/audit_log/ui.py +18 -15
  43. educommon/audit_log/utils/__init__.py +28 -60
  44. educommon/audit_log/utils/operations.py +16 -2
  45. educommon/auth/__init__.py +0 -3
  46. educommon/auth/rbac/__init__.py +2 -4
  47. educommon/auth/rbac/actions.py +148 -145
  48. educommon/auth/rbac/app_meta.py +9 -6
  49. educommon/auth/rbac/backends/base.py +2 -8
  50. educommon/auth/rbac/backends/caching.py +27 -37
  51. educommon/auth/rbac/backends/simple.py +1 -4
  52. educommon/auth/rbac/checker.py +1 -3
  53. educommon/auth/rbac/management/commands/rbac.py +6 -11
  54. educommon/auth/rbac/manager.py +18 -47
  55. educommon/auth/rbac/migrations/0001_initial.py +73 -12
  56. educommon/auth/rbac/migrations/0002_model_modifier_metaclass_fix.py +7 -6
  57. educommon/auth/rbac/migrations/0003_permission_hidden.py +1 -5
  58. educommon/auth/rbac/migrations/0004_auto_20171024_1245.py +26 -19
  59. educommon/auth/rbac/models.py +63 -68
  60. educommon/auth/rbac/permissions.py +6 -7
  61. educommon/auth/rbac/ui.py +83 -84
  62. educommon/auth/rbac/utils.py +10 -11
  63. educommon/auth/rbac/validators.py +4 -5
  64. educommon/auth/simple_auth/__init__.py +1 -5
  65. educommon/auth/simple_auth/actions.py +79 -92
  66. educommon/auth/simple_auth/app_meta.py +2 -9
  67. educommon/auth/simple_auth/checkers.py +3 -3
  68. educommon/auth/simple_auth/migrations/0001_initial.py +23 -4
  69. educommon/auth/simple_auth/validators.py +0 -1
  70. educommon/contingent/actions.py +7 -7
  71. educommon/contingent/app_meta.py +1 -4
  72. educommon/contingent/base.py +10 -15
  73. educommon/contingent/catalogs.py +424 -540
  74. educommon/contingent/contingent_plugin/actions.py +4 -15
  75. educommon/contingent/contingent_plugin/apps.py +10 -4
  76. educommon/contingent/contingent_plugin/migrations/0001_initial.py +5 -6
  77. educommon/contingent/contingent_plugin/migrations/0002_add_contingent_model_deleted.py +6 -11
  78. educommon/contingent/contingent_plugin/model_views.py +2 -12
  79. educommon/contingent/contingent_plugin/models.py +2 -7
  80. educommon/contingent/contingent_plugin/observer.py +14 -13
  81. educommon/contingent/contingent_plugin/plugin_meta.py +1 -3
  82. educommon/contingent/contingent_plugin/storage.py +8 -7
  83. educommon/contingent/contingent_plugin/utils.py +6 -6
  84. educommon/django/db/fields.py +72 -86
  85. educommon/django/db/migration/__init__.py +3 -7
  86. educommon/django/db/migration/operations.py +29 -51
  87. educommon/django/db/mixins/__init__.py +16 -10
  88. educommon/django/db/mixins/date_interval.py +47 -75
  89. educommon/django/db/mixins/validation.py +26 -26
  90. educommon/django/db/model_view/__init__.py +18 -22
  91. educommon/django/db/models.py +9 -8
  92. educommon/django/db/observer.py +9 -27
  93. educommon/django/db/partitioning/__init__.py +66 -92
  94. educommon/django/db/partitioning/management/commands/apply_partitioning.py +3 -13
  95. educommon/django/db/partitioning/management/commands/clear_table.py +18 -14
  96. educommon/django/db/partitioning/management/commands/split_table.py +18 -13
  97. educommon/django/db/routers.py +6 -15
  98. educommon/django/db/signals.py +149 -2
  99. educommon/django/db/utils.py +14 -19
  100. educommon/django/db/validators/__init__.py +1 -0
  101. educommon/django/db/validators/simple.py +72 -100
  102. educommon/django/storages/atcfs/api.py +39 -53
  103. educommon/django/storages/atcfs/app_meta.py +1 -1
  104. educommon/django/storages/atcfs/management/commands/atcfs_migrate.py +42 -55
  105. educommon/django/storages/atcfs/models.py +0 -3
  106. educommon/django/storages/atcfs/monkey_patching.py +18 -12
  107. educommon/django/storages/atcfs/storage.py +14 -23
  108. educommon/extjs/fields/input_params.py +15 -45
  109. educommon/importer/XLSReader.py +143 -241
  110. educommon/importer/__init__.py +86 -4
  111. educommon/importer/api.py +53 -84
  112. educommon/importer/constants.py +4 -14
  113. educommon/importer/loggers.py +16 -26
  114. educommon/importer/proxy.py +131 -176
  115. educommon/importer/proxy_import.py +11 -12
  116. educommon/importer/report.py +4 -6
  117. educommon/importer/ui.py +32 -26
  118. educommon/importer/validators.py +4 -7
  119. educommon/integration_entities/helpers.py +14 -18
  120. educommon/ioc/__init__.py +3 -6
  121. educommon/logger/loggers.py +10 -14
  122. educommon/m3/__init__.py +20 -38
  123. educommon/m3/extensions/__init__.py +1 -0
  124. educommon/m3/extensions/listeners/__init__.py +22 -38
  125. educommon/m3/extensions/listeners/delete_check/listeners.py +31 -41
  126. educommon/m3/extensions/listeners/delete_check/mixins.py +20 -25
  127. educommon/m3/extensions/listeners/delete_check/signals.py +2 -2
  128. educommon/m3/extensions/listeners/delete_check/ui.py +15 -14
  129. educommon/m3/extensions/listeners/delete_check/utils.py +9 -11
  130. educommon/m3/extensions/ui.py +15 -33
  131. educommon/m3/transaction_context.py +17 -19
  132. educommon/objectpack/actions.py +70 -88
  133. educommon/objectpack/apps.py +5 -0
  134. educommon/objectpack/filters.py +9 -15
  135. educommon/objectpack/ui.py +59 -77
  136. educommon/report/__init__.py +9 -5
  137. educommon/report/actions.py +29 -32
  138. educommon/report/constructor/__init__.py +5 -8
  139. educommon/report/constructor/app_meta.py +1 -3
  140. educommon/report/constructor/apps.py +1 -0
  141. educommon/report/constructor/base.py +33 -80
  142. educommon/report/constructor/builders/excel/_base.py +138 -286
  143. educommon/report/constructor/builders/excel/_header.py +2 -9
  144. educommon/report/constructor/builders/excel/product.py +13 -34
  145. educommon/report/constructor/builders/excel/with_merged_cells.py +18 -14
  146. educommon/report/constructor/config.py +2 -0
  147. educommon/report/constructor/editor/actions.py +101 -215
  148. educommon/report/constructor/editor/ui.py +71 -93
  149. educommon/report/constructor/exceptions.py +6 -12
  150. educommon/report/constructor/migrations/0001_initial.py +36 -44
  151. educommon/report/constructor/migrations/0002_report_filters.py +86 -72
  152. educommon/report/constructor/migrations/0003_reportfilter_exclude.py +5 -5
  153. educommon/report/constructor/migrations/0004_reportfilter_fields.py +22 -18
  154. educommon/report/constructor/migrations/0005_reportcolumn_visible.py +5 -4
  155. educommon/report/constructor/migrations/0006_reportsorting.py +21 -17
  156. educommon/report/constructor/migrations/0007_include_available_units.py +14 -14
  157. educommon/report/constructor/migrations/0008_auto_20170407_1318.py +4 -5
  158. educommon/report/constructor/migrations/0009_auto_20180405_0642.py +1 -4
  159. educommon/report/constructor/migrations/0010_add_aggregate_fields.py +7 -8
  160. educommon/report/constructor/mixins.py +14 -15
  161. educommon/report/constructor/models.py +76 -124
  162. educommon/report/constructor/utils.py +3 -8
  163. educommon/report/constructor/validators.py +1 -3
  164. educommon/report/reporter.py +25 -43
  165. educommon/report/utils.py +14 -40
  166. educommon/rest/actions.py +7 -11
  167. educommon/rest/context.py +6 -16
  168. educommon/rest/controllers.py +10 -10
  169. educommon/rest/mixins.py +29 -27
  170. educommon/secure_media/app_meta.py +9 -9
  171. educommon/utils/__init__.py +3 -2
  172. educommon/utils/caching.py +1 -3
  173. educommon/utils/conversion.py +1 -3
  174. educommon/utils/crypto.py +1 -2
  175. educommon/utils/date.py +13 -26
  176. educommon/utils/db/__init__.py +17 -26
  177. educommon/utils/db/postgresql.py +1 -4
  178. educommon/utils/fonts/__init__.py +3 -4
  179. educommon/utils/licence/__init__.py +5 -16
  180. educommon/utils/misc.py +9 -18
  181. educommon/utils/object_grid.py +55 -62
  182. educommon/utils/phone_number/modelfields.py +1 -3
  183. educommon/utils/phone_number/phone_number.py +5 -8
  184. educommon/utils/phone_number/validators.py +8 -23
  185. educommon/utils/plugins.py +15 -28
  186. educommon/utils/registry.py +2 -1
  187. educommon/utils/seqtools.py +1 -3
  188. educommon/utils/serializer.py +9 -16
  189. educommon/utils/storage.py +3 -2
  190. educommon/utils/system.py +1 -3
  191. educommon/utils/system_app/management/commands/delete_objects.py +17 -34
  192. educommon/utils/ui.py +87 -84
  193. educommon/utils/xml/__init__.py +2 -7
  194. educommon/utils/xml/resolver.py +1 -0
  195. educommon/ws_log/actions.py +31 -76
  196. educommon/ws_log/base.py +6 -20
  197. educommon/ws_log/migrations/0001_initial.py +25 -8
  198. educommon/ws_log/migrations/0002_auto_20160628_1334.py +0 -1
  199. educommon/ws_log/migrations/0003_add_fields_to_smev_logs.py +20 -4
  200. educommon/ws_log/migrations/0004_auto_20160727_1600.py +7 -6
  201. educommon/ws_log/migrations/0005_auto_20161130_1615.py +14 -4
  202. educommon/ws_log/migrations/0006_auto_20170327_1027.py +3 -2
  203. educommon/ws_log/migrations/0007_auto_20180607_1040.py +8 -9
  204. educommon/ws_log/migrations/0008_auto_20180713_1445.py +23 -10
  205. educommon/ws_log/migrations/0009_auto_20201130_1553.py +7 -2
  206. educommon/ws_log/models.py +21 -35
  207. educommon/ws_log/provider.py +2 -1
  208. educommon/ws_log/report.py +8 -13
  209. educommon/ws_log/smev/applications.py +12 -27
  210. educommon/ws_log/smev/exceptions.py +2 -3
  211. educommon/ws_log/ui.py +32 -32
  212. educommon/ws_log/utils.py +1 -3
  213. educommon-3.13.2.dist-info/METADATA +57 -0
  214. educommon-3.13.2.dist-info/RECORD +354 -0
  215. {educommon-3.12.0.dist-info → educommon-3.13.2.dist-info}/WHEEL +1 -1
  216. educommon/utils/patches.py +0 -27
  217. educommon/version.conf +0 -11
  218. educommon-3.12.0.dist-info/METADATA +0 -47
  219. educommon-3.12.0.dist-info/RECORD +0 -357
  220. educommon-3.12.0.dist-info/dependency_links.txt +0 -1
  221. {educommon-3.12.0.dist-info → educommon-3.13.2.dist-info}/top_level.txt +0 -0
@@ -128,28 +128,17 @@ class FilterValuesProvider:
128
128
  """
129
129
 
130
130
  if column_descriptor.data_type == constants.CT_CHOICES:
131
- return get_field_value_by_display(
132
- column_descriptor.field,
133
- value
134
- ) if self._as_orm_filter else value
131
+ return get_field_value_by_display(column_descriptor.field, value) if self._as_orm_filter else value
135
132
  else:
136
133
  if isinstance(column_descriptor.field.formfield(), BooleanField):
137
- if (
138
- isinstance(value, str) and
139
- value.lower() in ('false', '0', 'нет')
140
- ):
134
+ if isinstance(value, str) and value.lower() in ('false', '0', 'нет'):
141
135
  return False if self._as_orm_filter else FALSE
142
136
 
143
- elif (
144
- isinstance(value, str) and
145
- value.lower() in ('true', '1', 'да')
146
- ):
137
+ elif isinstance(value, str) and value.lower() in ('true', '1', 'да'):
147
138
  return True if self._as_orm_filter else TRUE
148
139
 
149
140
  else:
150
- raise FilterError(
151
- report_filter, 'неправильно задано значение фильтра'
152
- )
141
+ raise FilterError(report_filter, 'неправильно задано значение фильтра')
153
142
 
154
143
  return column_descriptor.field.formfield().to_python(value)
155
144
 
@@ -168,33 +157,19 @@ class FilterValuesProvider:
168
157
  result = None
169
158
 
170
159
  elif report_filter.operator == constants.IN:
171
- result = tuple(
172
- self.to_python(value, report_filter, column_descriptor)
173
- for value in report_filter.values
174
- )
160
+ result = tuple(self.to_python(value, report_filter, column_descriptor) for value in report_filter.values)
175
161
 
176
162
  elif report_filter.operator == constants.BETWEEN:
177
- result = tuple(
178
- self.to_python(value, report_filter, column_descriptor)
179
- for value in report_filter.values
180
- )
163
+ result = tuple(self.to_python(value, report_filter, column_descriptor) for value in report_filter.values)
181
164
 
182
165
  if len(result) != 2:
183
- raise FilterError(
184
- report_filter, 'не правильно заданы границы диапазона'
185
- )
166
+ raise FilterError(report_filter, 'не правильно заданы границы диапазона')
186
167
 
187
168
  elif report_filter.values:
188
- result = self.to_python(
189
- report_filter.values[0],
190
- report_filter,
191
- column_descriptor
192
- )
169
+ result = self.to_python(report_filter.values[0], report_filter, column_descriptor)
193
170
 
194
171
  else:
195
- raise FilterError(
196
- report_filter, 'не указано значение для сравнения.'
197
- )
172
+ raise FilterError(report_filter, 'не указано значение для сравнения.')
198
173
 
199
174
  return result
200
175
 
@@ -204,10 +179,7 @@ def is_row(data):
204
179
 
205
180
  :rtype: bool
206
181
  """
207
- return (
208
- isinstance(data, (tuple, list)) and
209
- any(not isinstance(cell, (tuple, list)) for cell in data)
210
- )
182
+ return isinstance(data, (tuple, list)) and any(not isinstance(cell, (tuple, list)) for cell in data)
211
183
 
212
184
 
213
185
  def is_block(data):
@@ -215,11 +187,7 @@ def is_block(data):
215
187
 
216
188
  :rtype: bool
217
189
  """
218
- return (
219
- data and
220
- isinstance(data, (tuple, list)) and
221
- all(isinstance(row, (tuple, list)) for row in data)
222
- )
190
+ return data and isinstance(data, (tuple, list)) and all(isinstance(row, (tuple, list)) for row in data)
223
191
 
224
192
 
225
193
  def get_data_width(data):
@@ -255,9 +223,7 @@ class _FilterBuilder:
255
223
  educommon.report.constructor.base.ColumnDescriptor
256
224
  """
257
225
  assert isinstance(report_filter, ReportFilter), type(report_filter)
258
- assert isinstance(column_descriptor, ColumnDescriptor), type(
259
- column_descriptor
260
- )
226
+ assert isinstance(column_descriptor, ColumnDescriptor), type(column_descriptor)
261
227
 
262
228
  self._report_filter = report_filter
263
229
  self._column_descriptor = column_descriptor
@@ -271,10 +237,7 @@ class _FilterBuilder:
271
237
 
272
238
  :param str field_lookup: lookup-выражение для доступа к значению поля
273
239
  """
274
- if (
275
- not self._report_filter.case_sensitive and
276
- self._column_descriptor.data_type == constants.CT_TEXT
277
- ):
240
+ if not self._report_filter.case_sensitive and self._column_descriptor.data_type == constants.CT_TEXT:
278
241
  field_lookup += {
279
242
  constants.EQ: '__iexact',
280
243
  constants.CONTAINS: '__icontains',
@@ -303,16 +266,14 @@ class _FilterBuilder:
303
266
  values = True
304
267
 
305
268
  elif (
306
- not self._report_filter.case_sensitive and
307
- self._column_descriptor.data_type == constants.CT_TEXT and
308
- self._report_filter.operator == constants.IN
269
+ not self._report_filter.case_sensitive
270
+ and self._column_descriptor.data_type == constants.CT_TEXT
271
+ and self._report_filter.operator == constants.IN
309
272
  ):
310
273
  values = tuple(v.lower() for v in self._report_filter.values)
311
274
 
312
275
  else:
313
- values = FilterValuesProvider()(
314
- self._report_filter, self._column_descriptor
315
- )
276
+ values = FilterValuesProvider()(self._report_filter, self._column_descriptor)
316
277
 
317
278
  return values
318
279
 
@@ -327,18 +288,17 @@ class _FilterBuilder:
327
288
 
328
289
  if self._report_filter.operator == constants.IS_NULL:
329
290
  if self._column_descriptor.data_type == constants.CT_TEXT:
330
- result = Q(
331
- Q(**{field_lookup + '__isnull': True}) |
332
- Q(**{field_lookup: ''})
333
- )
291
+ result = Q(Q(**{f'{field_lookup}__isnull': True}) | Q(**{field_lookup: ''}))
334
292
  else:
335
- result = Q(**{field_lookup + '__isnull': True})
293
+ result = Q(**{f'{field_lookup}__isnull': True})
336
294
 
337
295
  else:
338
296
  try:
339
- result = Q(**{
340
- self._get_lookup(field_lookup): filter_values,
341
- })
297
+ result = Q(
298
+ **{
299
+ self._get_lookup(field_lookup): filter_values,
300
+ }
301
+ )
342
302
  except ValueError as error:
343
303
  raise FilterError(self._report_filter, str(error))
344
304
 
@@ -362,9 +322,7 @@ class _FilterGroupBuilder:
362
322
  educommon.report.constructor.models.ReportFilterGroup
363
323
  """
364
324
  assert isinstance(data_filterer, _DataFilterer), type(data_filterer)
365
- assert isinstance(filter_group, ReportFilterGroup), type(
366
- filter_group
367
- )
325
+ assert isinstance(filter_group, ReportFilterGroup), type(filter_group)
368
326
 
369
327
  self._data_filterer = data_filterer
370
328
  self._filter_group = filter_group
@@ -382,13 +340,9 @@ class _FilterGroupBuilder:
382
340
 
383
341
  for report_filter in self._data_filterer.filters_by_id.values():
384
342
  if report_filter.group_id == self._filter_group.id:
385
- column_descriptor = data_source.get_column_descriptor(
386
- report_filter.column.name
387
- )
343
+ column_descriptor = data_source.get_column_descriptor(report_filter.column.name)
388
344
  if isinstance(column_descriptor.field, models.Field):
389
- filter_builder = _FilterBuilder(
390
- report_filter, column_descriptor
391
- )
345
+ filter_builder = _FilterBuilder(report_filter, column_descriptor)
392
346
  yield filter_builder.get_orm_filter()
393
347
 
394
348
  def get_orm_filter(self):
@@ -402,18 +356,12 @@ class _FilterGroupBuilder:
402
356
  }
403
357
 
404
358
  if self._filter_group.operator not in operators:
405
- raise ApplicationLogicException(
406
- 'Неподдерживаемый оператор: {}'
407
- .format(self._filter_group.operator)
408
- )
359
+ raise ApplicationLogicException('Неподдерживаемый оператор: {}'.format(self._filter_group.operator))
409
360
 
410
361
  orm_filters = tuple(self._get_nested_orm_filters())
411
362
 
412
363
  if orm_filters:
413
- result = reduce(
414
- operators[self._filter_group.operator],
415
- orm_filters
416
- )
364
+ result = reduce(operators[self._filter_group.operator], orm_filters)
417
365
  else:
418
366
  result = Q()
419
367
 
@@ -429,8 +377,7 @@ class _DataFilterer:
429
377
  2. Фильтрация полученных данных в приложении.
430
378
  """
431
379
 
432
- def __init__(self, report_template, data_source_descriptor,
433
- report_columns, ignored_columns_ids):
380
+ def __init__(self, report_template, data_source_descriptor, report_columns, ignored_columns_ids):
434
381
  """Инициализация экземпляра класса.
435
382
 
436
383
  :param report_template: Шаблон фильтра.
@@ -447,9 +394,7 @@ class _DataFilterer:
447
394
  :param ignored_columns_ids: Колонки исключенные из отчета.
448
395
  :type: tuple
449
396
  """
450
- assert isinstance(report_template, ReportTemplate), type(
451
- report_template
452
- )
397
+ assert isinstance(report_template, ReportTemplate), type(report_template)
453
398
 
454
399
  self._report_template = report_template
455
400
  self.data_source_descriptor = data_source_descriptor
@@ -464,14 +409,9 @@ class _DataFilterer:
464
409
  """
465
410
  query = ReportFilter.objects.filter(
466
411
  group__report_template=self._report_template,
467
- ).exclude(
468
- column_id__in=self._ignored_columns_ids
469
- )
412
+ ).exclude(column_id__in=self._ignored_columns_ids)
470
413
 
471
- return {
472
- report_filter.id: report_filter
473
- for report_filter in query
474
- }
414
+ return {report_filter.id: report_filter for report_filter in query}
475
415
 
476
416
  def get_orm_filters(self):
477
417
  """Возвращает фильтры для формирования SQL-запроса.
@@ -486,9 +426,7 @@ class _DataFilterer:
486
426
 
487
427
  :rtype: django.db.models.Q
488
428
  """
489
- filter_group = self._report_template.filter_groups.filter(
490
- parent__isnull=True
491
- ).first()
429
+ filter_group = self._report_template.filter_groups.filter(parent__isnull=True).first()
492
430
 
493
431
  if filter_group:
494
432
  return _FilterGroupBuilder(self, filter_group).get_orm_filter()
@@ -501,84 +439,82 @@ class _DataFilterer:
501
439
  :rtype: callable
502
440
  """
503
441
  data_source = self.data_source_descriptor
504
- column_descriptor = data_source.get_column_descriptor(
505
- report_filter.column.name
506
- )
507
- filter_values = FilterValuesProvider(as_orm_filter=False)(
508
- report_filter, column_descriptor
509
- )
442
+ column_descriptor = data_source.get_column_descriptor(report_filter.column.name)
443
+ filter_values = FilterValuesProvider(as_orm_filter=False)(report_filter, column_descriptor)
510
444
 
511
445
  if report_filter.operator == constants.LE:
446
+
512
447
  def function(value):
513
448
  return value is not None and value <= filter_values
514
449
 
515
450
  elif report_filter.operator == constants.LT:
451
+
516
452
  def function(value):
517
453
  return value is not None and value < filter_values
518
454
 
519
455
  elif report_filter.operator == constants.EQ:
456
+
520
457
  def function(value):
521
458
  return value == filter_values
522
459
 
523
460
  elif report_filter.operator == constants.GT:
461
+
524
462
  def function(value):
525
463
  return value is not None and value > filter_values
526
464
 
527
465
  elif report_filter.operator == constants.GE:
466
+
528
467
  def function(value):
529
468
  return value is not None and value >= filter_values
530
469
 
531
470
  elif report_filter.operator == constants.IS_NULL:
471
+
532
472
  def function(value):
533
473
  return value is None or value == ''
534
474
 
535
475
  elif report_filter.operator == constants.CONTAINS:
536
- assert isinstance(filter_values, str), type(
537
- filter_values
538
- )
476
+ assert isinstance(filter_values, str), type(filter_values)
539
477
 
540
478
  if report_filter.case_sensitive:
479
+
541
480
  def function(value):
542
481
  return value is not None and filter_values in value
543
482
  else:
483
+
544
484
  def function(value):
545
- return value is not None and any(
546
- v.lower() in value
547
- for v in filter_values
548
- )
485
+ return value is not None and any(v.lower() in value for v in filter_values)
549
486
 
550
487
  elif report_filter.operator == constants.STARTS_WITH:
551
- assert isinstance(filter_values, str), type(
552
- filter_values
553
- )
488
+ assert isinstance(filter_values, str), type(filter_values)
554
489
 
555
490
  if report_filter.case_sensitive:
491
+
556
492
  def function(value):
557
493
  return value is not None and value.startswith(filter_values)
558
494
  else:
495
+
559
496
  def function(value):
560
497
  return value is not None and value.lower().startswith(filter_values.lower())
561
498
 
562
499
  elif report_filter.operator == constants.ENDS_WITH:
563
- assert isinstance(filter_values, str), type(
564
- filter_values
565
- )
500
+ assert isinstance(filter_values, str), type(filter_values)
566
501
 
567
502
  if report_filter.case_sensitive:
503
+
568
504
  def function(value):
569
505
  return value is not None and value.endswith(filter_values)
570
506
  else:
507
+
571
508
  def function(value):
572
509
  return value is not None and value.lower().endswith(filter_values.lower())
573
510
 
574
511
  elif report_filter.operator == constants.BETWEEN:
575
512
  if report_filter.case_sensitive:
513
+
576
514
  def function(value):
577
- return (
578
- value is not None and
579
- filter_values[0] <= value <= filter_values[1]
580
- )
515
+ return value is not None and filter_values[0] <= value <= filter_values[1]
581
516
  else:
517
+
582
518
  def function(value):
583
519
  if isinstance(value, str):
584
520
  value = value.lower()
@@ -586,28 +522,22 @@ class _DataFilterer:
586
522
  else:
587
523
  values = filter_values
588
524
 
589
- return (
590
- value is not None and
591
- values[0] <= value <= values[1]
592
- )
525
+ return value is not None and values[0] <= value <= values[1]
593
526
 
594
527
  elif report_filter.operator == constants.IN:
595
528
  assert isinstance(filter_values, tuple), type(filter_values)
596
529
 
597
530
  if report_filter.case_sensitive:
531
+
598
532
  def function(value):
599
533
  return value in filter_values
600
534
  else:
535
+
601
536
  def function(value):
602
537
  return value.lower() in (v.lower() for v in filter_values)
603
538
 
604
539
  else:
605
- raise FilterError(
606
- report_filter,
607
- 'Неподдерживаемый оператор ({})'.format(
608
- report_filter.operator
609
- )
610
- )
540
+ raise FilterError(report_filter, 'Неподдерживаемый оператор ({})'.format(report_filter.operator))
611
541
 
612
542
  if report_filter.exclude:
613
543
  return lambda value: not function(value)
@@ -626,14 +556,11 @@ class _DataFilterer:
626
556
  втором случае --- будут удалены записи во внутренних блоках.
627
557
  """
628
558
  filter_functions = {
629
- report_filter.column_id: self._get_function_for_filter(
630
- report_filter
631
- )
559
+ report_filter.column_id: self._get_function_for_filter(report_filter)
632
560
  for report_filter in self.filters_by_id.values()
633
561
  }
634
562
  filter_functions_by_column = tuple(
635
- filter_functions.get(report_column.pk)
636
- for report_column in self._report_columns
563
+ filter_functions.get(report_column.pk) for report_column in self._report_columns
637
564
  )
638
565
 
639
566
  def filter_function(row_data, column_functions=None):
@@ -648,8 +575,7 @@ class _DataFilterer:
648
575
  column_functions = column_functions[cell_width:]
649
576
 
650
577
  if is_block(cell):
651
- filtered_block = _filter_block_or_row(
652
- cell, column_filter_functions)
578
+ filtered_block = _filter_block_or_row(cell, column_filter_functions)
653
579
  if filtered_block is None:
654
580
  return None
655
581
  result.append(filtered_block)
@@ -657,10 +583,7 @@ class _DataFilterer:
657
583
  # Простое значение (не строка или блок)
658
584
  assert not is_row(cell), cell
659
585
  column_filter_function = column_filter_functions[0]
660
- if (
661
- column_filter_function and
662
- not column_filter_function(cell)
663
- ):
586
+ if column_filter_function and not column_filter_function(cell):
664
587
  return None
665
588
  result.append(cell)
666
589
 
@@ -670,8 +593,7 @@ class _DataFilterer:
670
593
  """Возвращет отфильтрованный блок/строку или None."""
671
594
  if is_row(block_or_row):
672
595
  return filter_function(block_or_row, column_filter_functions)
673
- filtered = [_filter_block_or_row(item, column_filter_functions)
674
- for item in block_or_row]
596
+ filtered = [_filter_block_or_row(item, column_filter_functions) for item in block_or_row]
675
597
  filtered = [item for item in filtered if item is not None]
676
598
  return filtered or None
677
599
 
@@ -722,9 +644,7 @@ class _DataSorter:
722
644
  столбцы были добавлены в шаблон отчета до того, как были удалены
723
645
  из моделей Системы).
724
646
  """
725
- assert isinstance(report_template, ReportTemplate), type(
726
- report_template
727
- )
647
+ assert isinstance(report_template, ReportTemplate), type(report_template)
728
648
 
729
649
  self._report_template = report_template
730
650
  self._ignored_columns_ids = ignored_columns_ids
@@ -732,11 +652,11 @@ class _DataSorter:
732
652
  @cached_property
733
653
  def _params(self):
734
654
  data = enumerate(
735
- self._report_template.columns.exclude(
736
- id__in=self._ignored_columns_ids
737
- ).order_by(
655
+ self._report_template.columns.exclude(id__in=self._ignored_columns_ids)
656
+ .order_by(
738
657
  'index',
739
- ).values_list(
658
+ )
659
+ .values_list(
740
660
  'sorting__index',
741
661
  'sorting__direction',
742
662
  )
@@ -775,10 +695,7 @@ class _DataSorter:
775
695
  values[sort_index] = _OrderInverter(cell)
776
696
  index += 1
777
697
 
778
- result = tuple(
779
- values[sort_index]
780
- for sort_index in sorted(values)
781
- )
698
+ result = tuple(values[sort_index] for sort_index in sorted(values))
782
699
 
783
700
  return result
784
701
 
@@ -806,8 +723,7 @@ class _DataSorter:
806
723
  index += 1
807
724
 
808
725
  nullable_rows = filter(self._is_row_nullable, rows)
809
- non_nullable_rows = filter(
810
- lambda _row: not self._is_row_nullable(_row), rows)
726
+ non_nullable_rows = filter(lambda _row: not self._is_row_nullable(_row), rows)
811
727
 
812
728
  sorted_rows = sorted(
813
729
  non_nullable_rows,
@@ -870,15 +786,12 @@ class DataLoader:
870
786
  (
871
787
  ('СДЮШОР1', 'Бокс'),
872
788
  ('СДЮШОР1', 'Фехтование'),
873
- )
789
+ ),
874
790
  )
875
791
  """
876
792
 
877
- def __init__(self, report_template, data_source, report_columns,
878
- ignored_columns_ids, user):
879
- assert isinstance(report_template, ReportTemplate), type(
880
- report_template
881
- )
793
+ def __init__(self, report_template, data_source, report_columns, ignored_columns_ids, user):
794
+ assert isinstance(report_template, ReportTemplate), type(report_template)
882
795
  self._report_template = report_template
883
796
  self._data_source = data_source
884
797
  self._report_columns = report_columns
@@ -889,9 +802,7 @@ class DataLoader:
889
802
  def _get_column_count(columns):
890
803
  # Для иерархии столбцов возвращает количество занимаемых столбцов.
891
804
  if columns:
892
- return sum(map(
893
- DataLoader._get_column_count, columns.values()
894
- ))
805
+ return sum(map(DataLoader._get_column_count, columns.values()))
895
806
  else:
896
807
  return 1
897
808
 
@@ -924,9 +835,9 @@ class DataLoader:
924
835
  attr_value = getattr(obj, attr_name)
925
836
  except AttributeError:
926
837
  if (
927
- hasattr(obj, 'report_constructor_params') and
928
- 'extra' in obj.report_constructor_params and
929
- attr_name in obj.report_constructor_params['extra']
838
+ hasattr(obj, 'report_constructor_params')
839
+ and 'extra' in obj.report_constructor_params
840
+ and attr_name in obj.report_constructor_params['extra']
930
841
  ):
931
842
  # Дополнительное поле.
932
843
  extra = obj.report_constructor_params['extra']
@@ -940,34 +851,24 @@ class DataLoader:
940
851
  else:
941
852
  raise
942
853
 
943
- if (
944
- attr_value is not None and
945
- isinstance(field, (models.BooleanField,
946
- models.NullBooleanField))
947
- ):
948
- attr_value = (
949
- TRUE if attr_value else FALSE
950
- )
854
+ if attr_value is not None and isinstance(field, (models.BooleanField, models.NullBooleanField)):
855
+ attr_value = TRUE if attr_value else FALSE
951
856
  else:
952
857
  attr_value = obj
953
858
  descriptor = None
954
859
 
955
860
  # Обратная связь или M2M. Такие данные выводятся в виде кортежа.
956
- if (
957
- attr_name and
958
- (
959
- isinstance(field, ForeignObjectRel) and
960
- hasattr(descriptor, 'related_manager_cls') and
961
- isinstance(attr_value, descriptor.related_manager_cls) or
962
- isinstance(field, models.ManyToManyField) and
963
- isinstance(attr_value, Manager)
964
- )
861
+ if attr_name and (
862
+ isinstance(field, ForeignObjectRel)
863
+ and hasattr(descriptor, 'related_manager_cls')
864
+ and isinstance(attr_value, descriptor.related_manager_cls)
865
+ or isinstance(field, models.ManyToManyField)
866
+ and isinstance(attr_value, Manager)
965
867
  ):
966
868
  objects = attr_value.all()
967
869
  if objects:
968
870
  related_obj_data_gen = (
969
- list(DataLoader._get_object_data(related_obj, None, nested))
970
- for related_obj in objects
871
+ list(DataLoader._get_object_data(related_obj, None, nested)) for related_obj in objects
971
872
  )
972
873
  yield list(itertools.takewhile(bool, related_obj_data_gen))
973
874
  else:
@@ -977,9 +878,7 @@ class DataLoader:
977
878
  elif nested:
978
879
  if attr_value:
979
880
  for k, v in nested.items():
980
- for object_data in (
981
- DataLoader._get_object_data(attr_value, k, v)
982
- ):
881
+ for object_data in DataLoader._get_object_data(attr_value, k, v):
983
882
  yield object_data
984
883
  else:
985
884
  for _ in range(DataLoader._get_column_count(nested)):
@@ -991,22 +890,14 @@ class DataLoader:
991
890
  inner_values = []
992
891
  for inner_value in attr_value:
993
892
  if getattr(field.base_field, 'flatchoices', False):
994
- inner_values.append(
995
- DataLoader._get_field_display(
996
- field.base_field, inner_value
997
- )
998
- )
893
+ inner_values.append(DataLoader._get_field_display(field.base_field, inner_value))
999
894
  else:
1000
895
  inner_values.append(inner_value)
1001
- yield list(
1002
- (str(value),) for value in inner_values
1003
- )
896
+ yield list((str(value),) for value in inner_values)
1004
897
 
1005
898
  # Поле с choices.
1006
899
  elif getattr(field, 'flatchoices', False):
1007
- yield DataLoader._get_field_display(
1008
- field, attr_value
1009
- )
900
+ yield DataLoader._get_field_display(field, attr_value)
1010
901
 
1011
902
  # Поле с данными.
1012
903
  else:
@@ -1015,10 +906,7 @@ class DataLoader:
1015
906
  @cached_property
1016
907
  def _data_filterer(self):
1017
908
  """Фильтратор данных отчета."""
1018
- return _DataFilterer(
1019
- self._report_template, self._data_source, self._report_columns,
1020
- self._ignored_columns_ids
1021
- )
909
+ return _DataFilterer(self._report_template, self._data_source, self._report_columns, self._ignored_columns_ids)
1022
910
 
1023
911
  @cached_property
1024
912
  def _data_aggregator(self):
@@ -1027,30 +915,27 @@ class DataLoader:
1027
915
 
1028
916
  def _available_units_filter(self, query):
1029
917
  """Фильтратор данных отчета по доступности учреждений."""
1030
- return self._data_source.add_source_filter(
1031
- query, self._report_template.include_available_units,
1032
- self._user
1033
- )
918
+ return self._data_source.add_source_filter(query, self._report_template.include_available_units, self._user)
1034
919
 
1035
920
  def _get_objects(self):
1036
921
  result = self._data_source.model.objects.all()
1037
922
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1038
923
  # Добавление фильтров в запрос.
1039
924
 
1040
- if ReportFilter.objects.filter(
1041
- column__report_template=self._report_template,
1042
- ).exclude(
1043
- column_id__in=self._ignored_columns_ids
1044
- ).exists():
925
+ if (
926
+ ReportFilter.objects.filter(
927
+ column__report_template=self._report_template,
928
+ )
929
+ .exclude(column_id__in=self._ignored_columns_ids)
930
+ .exists()
931
+ ):
1045
932
  # Является ли модель древовидной(mptt - Modified Preorder Tree Traversal)
1046
933
  if hasattr(self._data_source.model, '_mptt_meta'):
1047
- result = result.filter(
1048
- self._data_filterer.get_orm_filters()
1049
- ).distinct(self._data_source.model._mptt_meta.tree_id_attr)
934
+ result = result.filter(self._data_filterer.get_orm_filters()).distinct(
935
+ self._data_source.model._mptt_meta.tree_id_attr
936
+ )
1050
937
  else:
1051
- result = result.filter(
1052
- self._data_filterer.get_orm_filters()
1053
- ).distinct('pk')
938
+ result = result.filter(self._data_filterer.get_orm_filters()).distinct('pk')
1054
939
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1055
940
  # Добавление параметров включения вложенных учреждений.
1056
941
 
@@ -1063,9 +948,7 @@ class DataLoader:
1063
948
  filter_function = self._data_filterer.get_filter_function()
1064
949
 
1065
950
  for obj in self._get_objects():
1066
- row_data = list(
1067
- DataLoader._get_object_data(obj, None, columns_hierarchy)
1068
- )
951
+ row_data = list(DataLoader._get_object_data(obj, None, columns_hierarchy))
1069
952
  if row_data and any(row_data):
1070
953
  row_data = filter_function(row_data)
1071
954
  if row_data:
@@ -1073,16 +956,16 @@ class DataLoader:
1073
956
  yield row_data
1074
957
 
1075
958
  def __iter__(self):
1076
- column_names = self._report_template.columns.filter(
1077
- visible=True,
1078
- ).exclude(
1079
- pk__in=self._ignored_columns_ids
1080
- ).values_list('name', flat=True)
959
+ column_names = (
960
+ self._report_template.columns.filter(
961
+ visible=True,
962
+ )
963
+ .exclude(pk__in=self._ignored_columns_ids)
964
+ .values_list('name', flat=True)
965
+ )
1081
966
 
1082
967
  if not column_names:
1083
- raise ReportConstructorException(
1084
- 'В шаблоне нет ни одного отображаемого столбца.'
1085
- )
968
+ raise ReportConstructorException('В шаблоне нет ни одного отображаемого столбца.')
1086
969
 
1087
970
  columns_hierarchy = get_columns_hierarchy(*column_names)
1088
971
 
@@ -1108,9 +991,7 @@ class ReportBuilderBase(metaclass=abc.ABCMeta):
1108
991
 
1109
992
  :param str file_path: Путь к файлу отчета.
1110
993
  """
1111
- assert isinstance(report_template, ReportTemplate), type(
1112
- report_template
1113
- )
994
+ assert isinstance(report_template, ReportTemplate), type(report_template)
1114
995
 
1115
996
  if report_template.data_source_name not in registry:
1116
997
  raise DataSourceParamsNotFound(report_template.data_source_name)
@@ -1133,11 +1014,7 @@ class ReportBuilderBase(metaclass=abc.ABCMeta):
1133
1014
 
1134
1015
  :rtype: tuple
1135
1016
  """
1136
- return tuple(
1137
- self._report_template.columns.exclude(
1138
- pk__in=self._ignored_columns_ids
1139
- )
1140
- )
1017
+ return tuple(self._report_template.columns.exclude(pk__in=self._ignored_columns_ids))
1141
1018
 
1142
1019
  @cached_property
1143
1020
  def _ignored_columns_ids(self):
@@ -1146,8 +1023,7 @@ class ReportBuilderBase(metaclass=abc.ABCMeta):
1146
1023
  :rtype: set
1147
1024
  """
1148
1025
  ignored_columns = set()
1149
- for col_id, col_name in self._report_template.columns.values_list(
1150
- 'id', 'name'):
1026
+ for col_id, col_name in self._report_template.columns.values_list('id', 'name'):
1151
1027
  if self._data_source.is_column_ignored(col_name):
1152
1028
  ignored_columns.add(col_id)
1153
1029
 
@@ -1161,20 +1037,21 @@ class ReportBuilderBase(metaclass=abc.ABCMeta):
1161
1037
  """
1162
1038
  return list(
1163
1039
  DataLoader(
1164
- self._report_template, self._data_source, self._report_columns,
1165
- self._ignored_columns_ids, self._user
1040
+ self._report_template, self._data_source, self._report_columns, self._ignored_columns_ids, self._user
1166
1041
  )
1167
1042
  )
1168
1043
 
1169
1044
  @cached_property
1170
1045
  def _workbook(self):
1171
1046
  """Книга Excel, в которой формируется отчет."""
1172
- result = Workbook(self._file_path, dict(
1173
- default_date_format='dd.mm.yyyy',
1174
- ))
1047
+ result = Workbook(
1048
+ self._file_path,
1049
+ dict(
1050
+ default_date_format='dd.mm.yyyy',
1051
+ ),
1052
+ )
1175
1053
 
1176
- for cell_format in (result.default_date_format,
1177
- result.default_url_format):
1054
+ for cell_format in (result.default_date_format, result.default_url_format):
1178
1055
  cell_format.set_border()
1179
1056
  cell_format.set_align('vcenter')
1180
1057
 
@@ -1267,9 +1144,8 @@ class ColumnCounter(BaseColumnAggregator):
1267
1144
  title = 'Количество'
1268
1145
 
1269
1146
  def __init__(self, column_index, by_value, total, **kwargs):
1270
- super(ColumnCounter, self).__init__(
1271
- column_index, by_value, total, **kwargs
1272
- )
1147
+ super(ColumnCounter, self).__init__(column_index, by_value, total, **kwargs)
1148
+
1273
1149
  # Требуется ли вывод в итоге количества уникальных значений.
1274
1150
  self.__is_total_unique = kwargs.get('total_unique', False)
1275
1151
 
@@ -1318,21 +1194,14 @@ def get_column_aggregator_info(column):
1318
1194
  :return: Тип агрегатора, (промежуточный итог, итог, количество уникальных)
1319
1195
  :rtype: tuple
1320
1196
  """
1321
- if (
1322
- column.by_value == BY_VALUE_COUNT or
1323
- column.total in (TOTAL_COUNT, TOTAL_UNIQUE_COUNT)
1324
- ):
1197
+ if column.by_value == BY_VALUE_COUNT or column.total in (TOTAL_COUNT, TOTAL_UNIQUE_COUNT):
1325
1198
  return COUNT, (
1326
1199
  column.by_value == BY_VALUE_COUNT,
1327
1200
  column.total == TOTAL_COUNT,
1328
- column.total == TOTAL_UNIQUE_COUNT
1201
+ column.total == TOTAL_UNIQUE_COUNT,
1329
1202
  )
1330
1203
  elif column.by_value == BY_VALUE_SUM or column.total == TOTAL_SUM:
1331
- return SUM, (
1332
- column.by_value == BY_VALUE_SUM,
1333
- column.total == TOTAL_SUM,
1334
- None
1335
- )
1204
+ return SUM, (column.by_value == BY_VALUE_SUM, column.total == TOTAL_SUM, None)
1336
1205
  else:
1337
1206
  return None, (None, None, None)
1338
1207
 
@@ -1353,16 +1222,11 @@ class DataAggregator:
1353
1222
 
1354
1223
  def set_aggregator(self, aggregator):
1355
1224
  """Добавляет агрегатор."""
1356
- self._aggregators[aggregator.type] = dict(
1357
- cls=aggregator,
1358
- instances=dict()
1359
- )
1225
+ self._aggregators[aggregator.type] = dict(cls=aggregator, instances=dict())
1360
1226
 
1361
1227
  def get_column_aggregator(self, column, column_idx):
1362
1228
  """Возвращает агрегатор для колонки."""
1363
- aggregator_type, (by_value, total, total_unique) = (
1364
- get_column_aggregator_info(column)
1365
- )
1229
+ aggregator_type, (by_value, total, total_unique) = get_column_aggregator_info(column)
1366
1230
  aggregator = self._aggregators.get(aggregator_type)
1367
1231
  if not aggregator:
1368
1232
  return
@@ -1413,7 +1277,8 @@ class DataAggregator:
1413
1277
  aggregator.column_index,
1414
1278
  aggregator.data.items(),
1415
1279
  )
1416
- for aggregator in aggregators if aggregator.is_by_value
1280
+ for aggregator in aggregators
1281
+ if aggregator.is_by_value
1417
1282
  )
1418
1283
  for title, col_idx, data in aggregators_data:
1419
1284
  for idx, (value, value_data) in enumerate(data):
@@ -1423,24 +1288,16 @@ class DataAggregator:
1423
1288
  row_data = self.get_empty_row()
1424
1289
  rows.append(row_data)
1425
1290
 
1426
- row_data[col_idx] = (
1427
- '{} "{}": {}'.format(title, value, value_data)
1428
- )
1291
+ row_data[col_idx] = '{} "{}": {}'.format(title, value, value_data)
1429
1292
 
1430
1293
  def _extend_rows_by_count(self, rows):
1431
1294
  """Добавляет строки с количеством элементов."""
1432
- counters = sorted(
1433
- self._aggregators[COUNT]['instances'].values(),
1434
- key=lambda c: c.column_index
1435
- )
1295
+ counters = sorted(self._aggregators[COUNT]['instances'].values(), key=lambda c: c.column_index)
1436
1296
  self._extend_rows(counters, rows)
1437
1297
 
1438
1298
  def _extend_rows_by_sum(self, rows):
1439
1299
  """Добавляет строки с суммой элементов."""
1440
- summators = sorted(
1441
- self._aggregators[SUM]['instances'].values(),
1442
- key=lambda c: c.column_index
1443
- )
1300
+ summators = sorted(self._aggregators[SUM]['instances'].values(), key=lambda c: c.column_index)
1444
1301
  self._extend_rows(summators, rows)
1445
1302
 
1446
1303
  def get_total_row(self):
@@ -1449,14 +1306,9 @@ class DataAggregator:
1449
1306
  for aggregator in self._aggregators.values():
1450
1307
  for instance in aggregator['instances'].values():
1451
1308
  if instance.is_total:
1452
- row[instance.column_index] = 'Итог({}): {}'.format(
1453
- instance.title,
1454
- instance.total
1455
- )
1309
+ row[instance.column_index] = 'Итог({}): {}'.format(instance.title, instance.total)
1456
1310
  elif instance.is_total_unique:
1457
- row[instance.column_index] = (
1458
- 'Итог(Уникальных): {}'.format(instance.total_unique)
1459
- )
1311
+ row[instance.column_index] = 'Итог(Уникальных): {}'.format(instance.total_unique)
1460
1312
 
1461
1313
  return row
1462
1314