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
@@ -24,8 +24,7 @@ from django.db.models import (
24
24
  )
25
25
 
26
26
 
27
- def get_objects_from_fixture(file_path, file_type=None,
28
- use_natural_foreign_keys=False):
27
+ def get_objects_from_fixture(file_path, file_type=None, use_natural_foreign_keys=False):
29
28
  """Возвращает генератор объектов из файла фикстуры.
30
29
 
31
30
  :param basestring file_path: Путь к файлу с данными.
@@ -36,16 +35,14 @@ def get_objects_from_fixture(file_path, file_type=None,
36
35
  :rtype: generator
37
36
  """
38
37
  if file_type is None:
39
- file_type = file_path[file_path.rfind('.') + 1:]
38
+ file_type = file_path[file_path.rfind('.') + 1 :]
40
39
  if file_type not in ('json', 'xml', 'yaml'):
41
40
  raise ValueError('Неподдерживаемый тип файла ' + file_path)
42
41
 
43
42
  with open(file_path, 'r') as infile:
44
- with closing(deserialize(
45
- file_type,
46
- infile.read(),
47
- use_natural_foreign_keys=use_natural_foreign_keys
48
- )) as objects:
43
+ with closing(
44
+ deserialize(file_type, infile.read(), use_natural_foreign_keys=use_natural_foreign_keys)
45
+ ) as objects:
49
46
  for obj in objects:
50
47
  yield obj
51
48
 
@@ -69,11 +66,12 @@ def correct_sequence_value(model, field='id', conn=connection):
69
66
  cursor = conn.cursor()
70
67
 
71
68
  cursor.execute(
72
- "SELECT setval(pg_get_serial_sequence(%s,%s), %s)", (
69
+ 'SELECT setval(pg_get_serial_sequence(%s,%s), %s)',
70
+ (
73
71
  model._meta.db_table,
74
72
  model._meta.get_field(field).column,
75
73
  max_id,
76
- )
74
+ ),
77
75
  )
78
76
 
79
77
 
@@ -86,8 +84,7 @@ class LoadFixture(Operation):
86
84
 
87
85
  atomic = True
88
86
 
89
- def __init__(self, file_path, force=False, file_type=None,
90
- use_natural_foreign_keys=False):
87
+ def __init__(self, file_path, force=False, file_type=None, use_natural_foreign_keys=False):
91
88
  """Инициализация экземпляра класса.
92
89
 
93
90
  :param str file_path: Путь к файлу.
@@ -105,8 +102,9 @@ class LoadFixture(Operation):
105
102
  def state_forwards(self, app_label, state):
106
103
  pass
107
104
 
108
- def database_forwards(self, app_label, schema_editor, from_state,
109
- to_state):
105
+ def database_forwards(self, app_label, schema_editor, from_state, to_state):
106
+ """Выполняет миграцию для выбранного приложения."""
107
+
110
108
  @contextmanager
111
109
  def replace_model_loader():
112
110
  _get_model = python_serializer_module._get_model
@@ -142,8 +140,8 @@ class CorrectSequence(Operation):
142
140
  def state_forwards(self, app_label, state):
143
141
  pass
144
142
 
145
- def database_forwards(self, app_label, schema_editor, from_state,
146
- to_state):
143
+ def database_forwards(self, app_label, schema_editor, from_state, to_state):
144
+ """Выполняет миграцию для выбранного приложения."""
147
145
  db_alias = schema_editor.connection.alias
148
146
  model = to_state.apps.get_model(app_label, self.model_name)
149
147
  if self.allow_migrate_model(db_alias, model) or self.force:
@@ -155,8 +153,7 @@ class CreateSchema(Operation):
155
153
 
156
154
  reversible = True
157
155
 
158
- def __init__(self, schema_name, owner=None, aliases=None,
159
- cascade_drop=False):
156
+ def __init__(self, schema_name, owner=None, aliases=None, cascade_drop=False):
160
157
  """Инициализация экземпляра.
161
158
 
162
159
  :param schema_name: Имя схемы.
@@ -178,16 +175,16 @@ class CreateSchema(Operation):
178
175
  def state_backward(self, app_label, state):
179
176
  pass
180
177
 
181
- def database_forwards(self, app_label, schema_editor, from_state,
182
- to_state):
178
+ def database_forwards(self, app_label, schema_editor, from_state, to_state):
179
+ """Выполняет миграцию для выбранного приложения."""
183
180
  if not self.aliases or schema_editor.connection.alias in self.aliases:
184
181
  sql = 'CREATE SCHEMA IF NOT EXISTS ' + self.schema_name
185
182
  if self.owner:
186
183
  sql += ' AUTHORIZATION ' + self.owner
187
184
  schema_editor.execute(sql)
188
185
 
189
- def database_backwards(self, app_label, schema_editor, from_state,
190
- to_state):
186
+ def database_backwards(self, app_label, schema_editor, from_state, to_state):
187
+ """Откатывает миграцию для выбранного приложения."""
191
188
  if not self.aliases or schema_editor.connection.alias in self.aliases:
192
189
  sql = 'DROP SCHEMA IF EXISTS ' + self.schema_name
193
190
  if self.cascade_drop:
@@ -196,41 +193,25 @@ class CreateSchema(Operation):
196
193
 
197
194
 
198
195
  class _AnotherAppMixin:
199
-
200
196
  def __init__(self, *args, **kwargs):
201
197
  self.__app_label = kwargs.pop('app_label', None)
202
198
 
203
199
  super().__init__(*args, **kwargs)
204
200
 
205
201
  def state_forwards(self, app_label, state):
206
- super().state_forwards(
207
- self.__app_label or app_label,
208
- state
209
- )
210
-
211
- def database_forwards(
212
- self, app_label, schema_editor, from_state, to_state
213
- ):
214
- super().database_forwards(
215
- self.__app_label or app_label,
216
- schema_editor,
217
- from_state,
218
- to_state
219
- )
220
-
221
- def database_backwards(
222
- self, app_label, schema_editor, from_state, to_state
223
- ):
224
- super().database_backwards(
225
- self.__app_label or app_label,
226
- schema_editor,
227
- from_state,
228
- to_state
229
- )
202
+ """Применяет изменение к заданному приложению или текущему."""
203
+ super().state_forwards(self.__app_label or app_label, state)
230
204
 
205
+ def database_forwards(self, app_label, schema_editor, from_state, to_state):
206
+ """Выполняет миграцию для выбранного приложения."""
207
+ super().database_forwards(self.__app_label or app_label, schema_editor, from_state, to_state)
208
+
209
+ def database_backwards(self, app_label, schema_editor, from_state, to_state):
210
+ """Откатывает миграцию для выбранного приложения."""
211
+ super().database_backwards(self.__app_label or app_label, schema_editor, from_state, to_state)
231
212
 
232
- class AddField(_AnotherAppMixin, AddField):
233
213
 
214
+ class AddField(_AnotherAppMixin, AddField):
234
215
  """Операция добавления поля в модель с поддержкой других приложений.
235
216
 
236
217
  Имя приложения можно указать в аргументе :arg:`app_label`.
@@ -238,7 +219,6 @@ class AddField(_AnotherAppMixin, AddField):
238
219
 
239
220
 
240
221
  class AlterField(_AnotherAppMixin, AlterField):
241
-
242
222
  """Операция изменения поля в модели с поддержкой других приложений.
243
223
 
244
224
  Имя приложения можно указать в аргументе :arg:`app_label`.
@@ -246,7 +226,6 @@ class AlterField(_AnotherAppMixin, AlterField):
246
226
 
247
227
 
248
228
  class RemoveField(_AnotherAppMixin, RemoveField):
249
-
250
229
  """Операция удаления поля в модели с поддержкой других приложений.
251
230
 
252
231
  Имя приложения можно указать в аргументе :arg:`app_label`.
@@ -254,7 +233,6 @@ class RemoveField(_AnotherAppMixin, RemoveField):
254
233
 
255
234
 
256
235
  class RenameField(_AnotherAppMixin, RenameField):
257
-
258
236
  """Операция переименования поля в модели с поддержкой других приложений.
259
237
 
260
238
  Имя приложения можно указать в аргументе :arg:`app_label`.
@@ -1,4 +1,5 @@
1
1
  """Классы-примеси для моделей Django."""
2
+
2
3
  from django.db import (
3
4
  models,
4
5
  )
@@ -12,7 +13,6 @@ from m3_django_compat.exceptions import (
12
13
 
13
14
 
14
15
  class DeferredActionsMixin(models.Model):
15
-
16
16
  """Класс-примесь для выполнения отложенных действий в моделях.
17
17
 
18
18
  Позволяет выполнять действия, оформленные в виде callable-объектов,
@@ -36,7 +36,7 @@ class DeferredActionsMixin(models.Model):
36
36
  abstract = True
37
37
 
38
38
  def __init__(self, *args, **kwargs): # noqa: D107
39
- super(DeferredActionsMixin, self).__init__(*args, **kwargs)
39
+ super().__init__(*args, **kwargs)
40
40
 
41
41
  self.__pre_save_actions = []
42
42
  self.__post_save_actions = []
@@ -74,7 +74,9 @@ class DeferredActionsMixin(models.Model):
74
74
  соответственно.
75
75
  """
76
76
  self.__execute_actions(self.__pre_save_actions)
77
- super(DeferredActionsMixin, self).save(*args, **kwargs)
77
+
78
+ super().save(*args, **kwargs)
79
+
78
80
  self.__execute_actions(self.__post_save_actions)
79
81
 
80
82
  @atomic
@@ -86,8 +88,11 @@ class DeferredActionsMixin(models.Model):
86
88
  соответственно.
87
89
  """
88
90
  self.__execute_actions(self.__pre_delete_actions)
89
- result = super(DeferredActionsMixin, self).delete(*args, **kwargs)
91
+
92
+ result = super().delete(*args, **kwargs)
93
+
90
94
  self.__execute_actions(self.__post_delete_actions)
95
+
91
96
  return result
92
97
 
93
98
  @atomic
@@ -99,13 +104,15 @@ class DeferredActionsMixin(models.Model):
99
104
  соответственно.
100
105
  """
101
106
  self.__execute_actions(self.__pre_delete_actions)
102
- result = super(DeferredActionsMixin, self).safe_delete(*args, **kwargs)
107
+
108
+ result = super().safe_delete(*args, **kwargs)
109
+
103
110
  self.__execute_actions(self.__post_delete_actions)
111
+
104
112
  return result
105
113
 
106
114
 
107
115
  class DeleteOnSaveMixin(models.Model):
108
-
109
116
  """Примесь для удаления объектов до/после сохранения модели.
110
117
 
111
118
  Функционал данной примеси актуален при валидации моделей. В некоторых
@@ -120,7 +127,7 @@ class DeleteOnSaveMixin(models.Model):
120
127
  """
121
128
 
122
129
  def __init__(self, *args, **kwargs):
123
- super(DeleteOnSaveMixin, self).__init__(*args, **kwargs)
130
+ super().__init__(*args, **kwargs)
124
131
 
125
132
  # Список объектов, подлежащих удалению после сохранения self
126
133
  self.__objects_for_delete_before_save = set()
@@ -176,7 +183,7 @@ class DeleteOnSaveMixin(models.Model):
176
183
 
177
184
  delete_objects(self.__objects_for_delete_before_save)
178
185
 
179
- super(DeleteOnSaveMixin, self).save(*args, **kwargs)
186
+ super().save(*args, **kwargs)
180
187
 
181
188
  delete_objects(self.__objects_for_delete_after_save)
182
189
 
@@ -185,7 +192,6 @@ class DeleteOnSaveMixin(models.Model):
185
192
 
186
193
 
187
194
  class StringFieldsCleanerMixin(models.Model):
188
-
189
195
  """Примесь для удаления из строковых полей модели лишних пробелов.
190
196
 
191
197
  Во всех текстовых полях модели удаляет пробельные символы в начале и конце
@@ -222,7 +228,7 @@ class StringFieldsCleanerMixin(models.Model):
222
228
 
223
229
  setattr(self, field.attname, field_value)
224
230
 
225
- return super(StringFieldsCleanerMixin, self).clean_fields(exclude)
231
+ return super().clean_fields(exclude)
226
232
 
227
233
  class Meta:
228
234
  abstract = True
@@ -30,12 +30,12 @@ from educommon.utils.date import (
30
30
 
31
31
 
32
32
  class BaseIntervalMeta(models.base.ModelBase):
33
-
34
33
  """Базовый метакласс для примесей *IntervalMixin.
35
34
 
36
35
  Добавляет к создаваемой модели поля, содержащие границы интервала. Имена
37
36
  полей задаются в атрибуте interval_field_names.
38
37
  """
38
+
39
39
  # тип поля модели, определяется в потомке
40
40
  _interval_bound_field_type = None
41
41
 
@@ -62,42 +62,34 @@ class BaseIntervalMeta(models.base.ModelBase):
62
62
  return result
63
63
 
64
64
  def __new__(cls, name, bases, attrs):
65
- if (
66
- cls._mixin_stay(bases) and
67
- (
68
- # поля с границами интервала создаются только если модель не
69
- # абстрактная.
70
- 'Meta' not in attrs or
71
- not hasattr(attrs['Meta'], 'abstract') or
72
- not attrs['Meta'].abstract
73
- )
65
+ if cls._mixin_stay(bases) and (
66
+ # поля с границами интервала создаются только если модель не
67
+ # абстрактная.
68
+ 'Meta' not in attrs or not hasattr(attrs['Meta'], 'abstract') or not attrs['Meta'].abstract
74
69
  ):
75
- interval_field_names = cls.__get_attr(
76
- 'interval_field_names', attrs, bases, cls.interval_field_names
77
- )
70
+ interval_field_names = cls.__get_attr('interval_field_names', attrs, bases, cls.interval_field_names)
78
71
 
79
72
  # Добавление полей, хранящих границы интервала
80
- for field_name, verbose_name in zip(
81
- interval_field_names, ('Начало интервала', 'Конец интервала')
82
- ):
73
+ for field_name, verbose_name in zip(interval_field_names, ('Начало интервала', 'Конец интервала')):
83
74
  field = cls.__get_attr(field_name, attrs, bases)
84
75
  if field is Undefined:
85
76
  attrs[field_name] = cls._interval_bound_field_type(
86
77
  name=field_name,
87
- blank=True, null=True,
78
+ blank=True,
79
+ null=True,
88
80
  verbose_name=verbose_name,
89
81
  )
90
82
 
91
- return super(BaseIntervalMeta, cls).__new__(cls, name, bases, attrs)
83
+ return super().__new__(cls, name, bases, attrs)
92
84
 
93
85
 
94
86
  class DateIntervalMeta(BaseIntervalMeta):
95
-
96
87
  """Метакласс для примеси DateIntervalMixin.
97
88
 
98
89
  Добавляет к создаваемой модели поля дат, содержащие границы интервала.
99
90
  Имена полей задаются в атрибуте interval_field_names.
100
91
  """
92
+
101
93
  # тип полей модели, которые будет созданы
102
94
  _interval_bound_field_type = models.DateField
103
95
  # названия полей по умолчанию
@@ -109,19 +101,16 @@ class DateIntervalMeta(BaseIntervalMeta):
109
101
 
110
102
  :rtype bool
111
103
  """
112
- return (
113
- 'DateIntervalMixin' in globals() and
114
- any(issubclass(base, DateIntervalMixin) for base in bases)
115
- )
104
+ return 'DateIntervalMixin' in globals() and any(issubclass(base, DateIntervalMixin) for base in bases)
116
105
 
117
106
 
118
107
  class DateTimeIntervalMeta(BaseIntervalMeta):
119
-
120
108
  """Метакласс для примеси DateTimeIntervalMixin.
121
109
 
122
110
  Добавляет к создаваемой модели поля дат со временем, содержащие
123
111
  границы интервала. Имена полей задаются в атрибуте interval_field_names.
124
112
  """
113
+
125
114
  # тип полей модели, которые будет созданы
126
115
  _interval_bound_field_type = models.DateTimeField
127
116
  # названия полей по умолчанию
@@ -133,14 +122,10 @@ class DateTimeIntervalMeta(BaseIntervalMeta):
133
122
 
134
123
  :rtype bool
135
124
  """
136
- return (
137
- 'DateTimeIntervalMixin' in globals() and
138
- any(issubclass(base, DateTimeIntervalMixin) for base in bases)
139
- )
125
+ return 'DateTimeIntervalMixin' in globals() and any(issubclass(base, DateTimeIntervalMixin) for base in bases)
140
126
 
141
127
 
142
128
  class BaseIntervalMixin(models.Model):
143
-
144
129
  """Базовый класс для примесей к моделям, добавляющих интервал дат.
145
130
 
146
131
  Содержит два поля: date_from (дата начала интервала) и date_to (дата
@@ -165,18 +150,17 @@ class BaseIntervalMixin(models.Model):
165
150
 
166
151
  Используется для проверки пересечения диапазонов.
167
152
  """
153
+
168
154
  @classmethod
169
155
  def get_current_date(cls):
170
156
  """Возвращает текущую дату.
171
157
 
172
158
  :rtype: datetime.datetime or datetime.date
173
159
  """
174
- raise NotImplemented
160
+ raise NotImplementedError
175
161
 
176
162
  @classmethod
177
- def get_date_in_intervals_filter(cls, day=None, lookup=None,
178
- include_lower_bound=True,
179
- include_upper_bound=True):
163
+ def get_date_in_intervals_filter(cls, day=None, lookup=None, include_lower_bound=True, include_upper_bound=True):
180
164
  """Возвращает фильтр для выборки записей по указанной дате.
181
165
 
182
166
  Условия фильтра включают записи, в интервал которых входит
@@ -225,6 +209,7 @@ class BaseIntervalMixin(models.Model):
225
209
  to_filter |= Q(**{to_lookup + '__isnull': True})
226
210
 
227
211
  result = from_filter & to_filter
212
+
228
213
  return result
229
214
 
230
215
  @classmethod
@@ -233,12 +218,7 @@ class BaseIntervalMixin(models.Model):
233
218
 
234
219
  @classmethod
235
220
  def get_intersection_daterange_filter(
236
- cls,
237
- date_begin=None,
238
- date_end=None,
239
- lookup=None,
240
- include_lower_bound=True,
241
- include_upper_bound=True
221
+ cls, date_begin=None, date_end=None, lookup=None, include_lower_bound=True, include_upper_bound=True
242
222
  ):
243
223
  """Метод возвращает фильтр для выборки записей, попадающих в интервал.
244
224
 
@@ -268,11 +248,7 @@ class BaseIntervalMixin(models.Model):
268
248
 
269
249
  .. code-block:: python
270
250
  :caption: Пример использования
271
- query = model.objects.filter(
272
- model.get_intersection_daterange_filter(
273
- date_begin, date_end
274
- )
275
- )
251
+ query = model.objects.filter(model.get_intersection_daterange_filter(date_begin, date_end))
276
252
 
277
253
  :params date_begin: Дата начала интервала.
278
254
  :type date_begin: datetime.datetime
@@ -305,9 +281,7 @@ class BaseIntervalMixin(models.Model):
305
281
  # Предполагается, что в интервалах дат дата окончания находится
306
282
  # справа по оси времени от даты начала или равна. Если будет наоборот,
307
283
  # то результат может оказаться не верным
308
- assert date_end >= date_begin, (
309
- 'Дата окончания должна быть больше или равна дате начала'
310
- )
284
+ assert date_end >= date_begin, 'Дата окончания должна быть больше или равна дате начала'
311
285
 
312
286
  from_name, to_name = cls.interval_field_names
313
287
  if lookup is not None:
@@ -331,6 +305,7 @@ class BaseIntervalMixin(models.Model):
331
305
  to_filter |= Q(**{to_lookup + '__isnull': True})
332
306
 
333
307
  result = from_filter & to_filter
308
+
334
309
  return result
335
310
 
336
311
  def is_date_in_interval(self, day):
@@ -343,12 +318,19 @@ class BaseIntervalMixin(models.Model):
343
318
  assert isinstance(day, (date, datetime)), type(day)
344
319
  date_from, date_to = self.interval_range
345
320
  result = (
346
- date_from is None and date_to is None or
347
- date_from is None and date_to is not None and day <= date_to or
348
- date_from is not None and date_to is None and day >= date_from or
349
- date_from is not None and date_to is not None and
350
- date_from <= day <= date_to
321
+ date_from is None
322
+ and date_to is None
323
+ or date_from is None
324
+ and date_to is not None
325
+ and day <= date_to
326
+ or date_from is not None
327
+ and date_to is None
328
+ and day >= date_from
329
+ or date_from is not None
330
+ and date_to is not None
331
+ and date_from <= day <= date_to
351
332
  )
333
+
352
334
  return result
353
335
 
354
336
  def is_intersected_with(self, date_from, date_to):
@@ -400,6 +382,7 @@ class BaseIntervalMixin(models.Model):
400
382
  opts.get_field(date_from_name).verbose_name.capitalize(),
401
383
  opts.get_field(date_to_name).verbose_name.lower(),
402
384
  )
385
+
403
386
  return result
404
387
 
405
388
  def interval_intersected_error_message(self, others=None):
@@ -419,9 +402,9 @@ class BaseIntervalMixin(models.Model):
419
402
 
420
403
  date_from = getattr(self, date_from_name)
421
404
 
422
- prev_intervals = self.__class__.objects.filter(
423
- **{'{}__lt'.format(date_to_name): date_from}
424
- ).order_by('-{}'.format(date_to_name))
405
+ prev_intervals = self.__class__.objects.filter(**{'{}__lt'.format(date_to_name): date_from}).order_by(
406
+ '-{}'.format(date_to_name)
407
+ )
425
408
 
426
409
  return prev_intervals[0] if prev_intervals else None
427
410
 
@@ -466,9 +449,9 @@ class BaseIntervalMixin(models.Model):
466
449
 
467
450
  date_to = getattr(self, date_to_name)
468
451
 
469
- next_intervals = self.__class__.objects.filter(
470
- **{'{}__gt'.format(date_from_name): date_to}
471
- ).order_by(date_from_name)
452
+ next_intervals = self.__class__.objects.filter(**{'{}__gt'.format(date_from_name): date_to}).order_by(
453
+ date_from_name
454
+ )
472
455
 
473
456
  return next_intervals[0] if next_intervals else None
474
457
 
@@ -477,28 +460,20 @@ class BaseIntervalMixin(models.Model):
477
460
  errors = {}
478
461
 
479
462
  try:
480
- super(BaseIntervalMixin, self).clean()
463
+ super().clean()
481
464
  except ValidationError as error:
482
465
  errors = error.update_error_dict(errors)
483
466
 
484
467
  date_from, date_to = self.interval_range
485
468
 
486
469
  # Дата начала интервала должна быть раньше даты окончания
487
- if (
488
- date_from is not None and
489
- date_to is not None and
490
- date_from > date_to
491
- ):
492
- errors.setdefault(NON_FIELD_ERRORS, []).append(
493
- self.interval_dates_error_message()
494
- )
470
+ if date_from is not None and date_to is not None and date_from > date_to:
471
+ errors.setdefault(NON_FIELD_ERRORS, []).append(self.interval_dates_error_message())
495
472
  else:
496
473
  # Проверка пересения интервалов
497
474
  others = self.get_intersected_query()
498
475
  if others:
499
- errors.setdefault(NON_FIELD_ERRORS, []).append(
500
- self.interval_intersected_error_message(others)
501
- )
476
+ errors.setdefault(NON_FIELD_ERRORS, []).append(self.interval_intersected_error_message(others))
502
477
 
503
478
  if errors:
504
479
  raise ValidationError(errors)
@@ -582,7 +557,6 @@ class DateTimeIntervalMixin(BaseIntervalMixin, metaclass=DateTimeIntervalMeta):
582
557
 
583
558
 
584
559
  class ActualObjectsManager(Manager):
585
-
586
560
  """Менеджер для интервальной модели.
587
561
 
588
562
  Отфильтровывает все объекты, в интервал которых входит текущая дата.
@@ -591,9 +565,7 @@ class ActualObjectsManager(Manager):
591
565
  def contribute_to_class(self, model, name):
592
566
  assert issubclass(model, BaseIntervalMixin), type(model)
593
567
 
594
- super(ActualObjectsManager, self).contribute_to_class(model, name)
568
+ super().contribute_to_class(model, name)
595
569
 
596
570
  def get_queryset(self):
597
- return super(ActualObjectsManager, self).get_queryset().filter(
598
- self.model.get_date_in_intervals_filter()
599
- )
571
+ return super().get_queryset().filter(self.model.get_date_in_intervals_filter())