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
@@ -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(ReportTemplate, self).simple_clean(errors)
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, blank=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
- total = models.PositiveSmallIntegerField(
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
- ('report_template', 'name'),
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
- (self.by_value == self.total) or
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
- self.report_template.data_source_name
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(ReportColumn, self).simple_clean(errors)
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, null=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 or
282
- not report_filter.group_id or
283
- not report_filter.column_id or
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(ReportFilterGroup, self).simple_clean(errors)
274
+ super().simple_clean(errors)
303
275
  # ---------------------------------------------------------------------
304
276
  # Все столбцы в фильтрах группы должны быть в том же шаблоне, что и
305
277
  # данная группа.
306
278
 
307
279
  if (
308
- self.pk and
309
- self.report_template_id and
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 and self.report_template_id and
323
- self.parent and self.parent.report_template_id and
324
- self.report_template_id != self.parent.report_template_id
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, null=True,
340
+ blank=True,
341
+ null=True,
369
342
  ),
370
- blank=True, null=True,
343
+ blank=True,
344
+ null=True,
371
345
  )
372
346
  comment = models.TextField(
373
347
  'Описание фильтра',
374
- blank=True, null=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(col=self.column, invert__op=invert__op,
392
- operator=self.get_operator_display().lower(),
393
- value=', '.join(self.values), case=case)
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
- ('column', 'index'),
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(ReportFilter, self).simple_clean(errors)
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
- self.operator not in (constants.IN, constants.BETWEEN,) and
427
- (
428
- not self.values or
429
- len(self.values) > 1
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
- self.group and self.column and
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
- self.group.report_template.data_source_name
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
- col=self.column, direction=self.get_direction_display().lower()
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))
@@ -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
- Объект занимающийся комплексным построением отчета на основе simple_report:
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 (self.template_file_path is not None and
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 (self.template_file_path is not None and
109
- self.template_file_path.startswith('./')):
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
- base_name, os.path.extsep, self.extension.strip('.'))
123
- auto_report_path = os.path.join(
124
- report_dir, rel_sub_path, auto_report_name)
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
- Возвращает кортеж из абс. пути отчёта и url для скачивания.
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
  # настройка формата отчета