educommon 3.13.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 (220) 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 +4 -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.0.dist-info → educommon-3.13.2.dist-info}/METADATA +26 -14
  214. educommon-3.13.2.dist-info/RECORD +354 -0
  215. educommon/utils/patches.py +0 -27
  216. educommon/version.conf +0 -11
  217. educommon-3.13.0.dist-info/RECORD +0 -357
  218. educommon-3.13.0.dist-info/dependency_links.txt +0 -1
  219. {educommon-3.13.0.dist-info → educommon-3.13.2.dist-info}/WHEEL +0 -0
  220. {educommon-3.13.0.dist-info → educommon-3.13.2.dist-info}/top_level.txt +0 -0
@@ -8,24 +8,13 @@ from educommon.contingent.contingent_plugin.models import (
8
8
 
9
9
 
10
10
  class ContingentModelChangedPack(ObjectPack):
11
+ """Пак для отображения изменённых объектов контингента."""
11
12
 
12
13
  title = 'Измененные объекты контингента'
13
14
  model = ContingentModelChanged
14
15
 
15
- columns = [
16
- {
17
- 'data_index': 'content_type',
18
- 'header': 'Тип'
19
- },
20
- {
21
- 'data_index': 'content_object',
22
- 'header': 'Объект'
23
- }
24
- ]
16
+ columns = [{'data_index': 'content_type', 'header': 'Тип'}, {'data_index': 'content_object', 'header': 'Объект'}]
25
17
 
26
18
  def extend_menu(self, menu):
27
- return menu.SubMenu(
28
- 'Администрирование', menu.Item(
29
- self.title, self.list_window_action
30
- )
31
- )
19
+ """Добавляет пункт в подменю 'Администрирование'."""
20
+ return menu.SubMenu('Администрирование', menu.Item(self.title, self.list_window_action))
@@ -14,6 +14,10 @@ _VERSION = VERSION[:2]
14
14
 
15
15
 
16
16
  class ContingentPluginAppConfig(AppConfig):
17
+ """Конфигурация плагина контингента.
18
+
19
+ Отвечает за регистрацию представлений связанных моделей при старте приложения.
20
+ """
17
21
 
18
22
  name = __package__
19
23
 
@@ -27,10 +31,12 @@ class ContingentPluginAppConfig(AppConfig):
27
31
  )
28
32
 
29
33
  model_views = import_module(self.name + '.model_views')
30
- registries['related_objects'].register(
31
- *model_views.related_model_views
32
- )
34
+ registries['related_objects'].register(*model_views.related_model_views)
33
35
 
34
36
  def ready(self):
35
- super(ContingentPluginAppConfig, self).ready()
37
+ """Вызывается при готовности приложения.
38
+
39
+ Производит регистрацию представлений связанных моделей.
40
+ """
41
+ super().ready()
36
42
  self._register_related_objects_views()
@@ -12,7 +12,6 @@ from django.db import (
12
12
 
13
13
 
14
14
  class Migration(migrations.Migration):
15
-
16
15
  initial = True
17
16
 
18
17
  dependencies = [
@@ -23,10 +22,7 @@ class Migration(migrations.Migration):
23
22
  # первым в списке выполняемых миграций. Начиная с версии 1.10 данный
24
23
  # способ не подходит ввиду ввода проверки на корректность плана миграций
25
24
  if VERSION < (1, 10):
26
- run_before = [
27
- (app_name.split('.')[-1], '__first__')
28
- for app_name in settings.PROJECT_APPS
29
- ]
25
+ run_before = [(app_name.split('.')[-1], '__first__') for app_name in settings.PROJECT_APPS]
30
26
 
31
27
  operations = [
32
28
  migrations.CreateModel(
@@ -34,7 +30,10 @@ class Migration(migrations.Migration):
34
30
  fields=[
35
31
  ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
36
32
  ('object_id', models.PositiveIntegerField()),
37
- ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
33
+ (
34
+ 'content_type',
35
+ models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType'),
36
+ ),
38
37
  ],
39
38
  ),
40
39
  migrations.AlterUniqueTogether(
@@ -6,7 +6,6 @@ from django.db import (
6
6
 
7
7
 
8
8
  class Migration(migrations.Migration):
9
-
10
9
  dependencies = [
11
10
  ('contenttypes', '0002_remove_content_type_name'),
12
11
  ('contingent_plugin', '0001_initial'),
@@ -16,17 +15,13 @@ class Migration(migrations.Migration):
16
15
  migrations.CreateModel(
17
16
  name='ContingentModelDeleted',
18
17
  fields=[
19
- ('id',
20
- models.AutoField(auto_created=True,
21
- primary_key=True,
22
- serialize=False,
23
- verbose_name='ID')),
18
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
24
19
  ('object_id', models.PositiveIntegerField()),
25
- ('data',
26
- models.TextField(verbose_name='Данные об удалённом объекте')),
27
- ('content_type',
28
- models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
29
- to='contenttypes.ContentType')),
20
+ ('data', models.TextField(verbose_name='Данные об удалённом объекте')),
21
+ (
22
+ 'content_type',
23
+ models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType'),
24
+ ),
30
25
  ],
31
26
  ),
32
27
  migrations.AlterUniqueTogether(
@@ -9,21 +9,11 @@ related_model_views = (
9
9
  HtmlTableView(
10
10
  model='contingent_plugin.ContingentModelChanged',
11
11
  description=Text('Измененная модель'),
12
- columns=(
13
- dict(
14
- header=Text('Идентификатор объекта'),
15
- data=AttrValue('object_id')
16
- ),
17
- )
12
+ columns=(dict(header=Text('Идентификатор объекта'), data=AttrValue('object_id')),),
18
13
  ),
19
14
  HtmlTableView(
20
15
  model='contingent_plugin.ContingentModelDeleted',
21
16
  description=Text('Удаленная модель'),
22
- columns=(
23
- dict(
24
- header=Text('Идентификатор объекта'),
25
- data=AttrValue('object_id')
26
- ),
27
- )
17
+ columns=(dict(header=Text('Идентификатор объекта'), data=AttrValue('object_id')),),
28
18
  ),
29
19
  )
@@ -13,7 +13,6 @@ from m3_django_compat.models import (
13
13
 
14
14
 
15
15
  class ContingentModelChanged(models.Model):
16
-
17
16
  """Данные об измененных моделях."""
18
17
 
19
18
  content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
@@ -54,14 +53,10 @@ if VERSION >= (1, 10):
54
53
  value = False
55
54
  index = 0
56
55
  for ind, migration_tuple in enumerate(plan):
57
- if (migration_tuple[0].name == '0001_initial') and (
58
- migration_tuple[0].app_label == 'contingent_plugin'
59
- ):
56
+ if (migration_tuple[0].name == '0001_initial') and (migration_tuple[0].app_label == 'contingent_plugin'):
60
57
  value = migration_tuple
61
58
 
62
- if (
63
- migration_tuple[0].name == '0002_remove_content_type_name'
64
- ) and (
59
+ if (migration_tuple[0].name == '0002_remove_content_type_name') and (
65
60
  migration_tuple[0].app_label == 'contenttypes'
66
61
  ):
67
62
  index = ind
@@ -15,10 +15,7 @@ from educommon.utils.storage import (
15
15
  )
16
16
 
17
17
 
18
- class ContingentFieldsObserver(
19
- OriginalObjectMixin,
20
- ModelObserverBase
21
- ):
18
+ class ContingentFieldsObserver(OriginalObjectMixin, ModelObserverBase):
22
19
  """Фиксирует изменения в полях моделей.
23
20
 
24
21
  Ссылки на измененные объекты сохраняются в модели
@@ -39,7 +36,7 @@ class ContingentFieldsObserver(
39
36
  """
40
37
 
41
38
  def __init__(self, model_fields=None):
42
- super(ContingentFieldsObserver, self).__init__()
39
+ super().__init__()
43
40
 
44
41
  self.model_fields = model_fields
45
42
 
@@ -50,7 +47,7 @@ class ContingentFieldsObserver(
50
47
  """Проверяет наличие указаных полей model_fields в моделях."""
51
48
  for (app_label, model_name), fields in model_fields.items():
52
49
  model = get_model(app_label, model_name)
53
- for field_name in (fields(model) if callable(fields) else fields):
50
+ for field_name in fields(model) if callable(fields) else fields:
54
51
  model._meta.get_field(field_name)
55
52
 
56
53
  self.__checked = True
@@ -58,7 +55,9 @@ class ContingentFieldsObserver(
58
55
  return True
59
56
 
60
57
  def _is_observable(self, model):
58
+ """Проверяет, указана ли модель в конфигурации наблюдаемых полей."""
61
59
  key = model._meta.app_label, model.__name__
60
+
62
61
  return key in self.model_fields
63
62
 
64
63
  def observe(self, model):
@@ -73,6 +72,7 @@ class ContingentFieldsObserver(
73
72
  """
74
73
  key = model._meta.app_label, model.__name__
75
74
  fields = self.model_fields.get(key, ())
75
+
76
76
  return fields(model) if callable(fields) else fields
77
77
 
78
78
  def _has_changes(self, original, instance):
@@ -102,15 +102,14 @@ class ContingentFieldsObserver(
102
102
  ContentType = get_model('contenttypes', 'ContentType')
103
103
  content_type = ContentType.objects.get_for_model(instance)
104
104
 
105
- ContingentModelChanged = get_model(
106
- 'contingent_plugin', 'ContingentModelChanged'
107
- )
105
+ ContingentModelChanged = get_model('contingent_plugin', 'ContingentModelChanged')
108
106
  ContingentModelChanged.objects.get_or_create(
109
107
  content_type=content_type,
110
108
  object_id=instance.pk,
111
109
  )
112
110
 
113
111
  def post_save(self, context, instance, sender, *args, **kwargs):
112
+ """Обработчик сигнала post_save для наблюдаемых моделей."""
114
113
  if not self.__checked:
115
114
  assert self._is_model_fields_valid(self.model_fields)
116
115
 
@@ -118,9 +117,7 @@ class ContingentFieldsObserver(
118
117
  self._fix_changed_object(instance)
119
118
 
120
119
 
121
- class PreDeletionDataSavingObserver(
122
- OriginalObjectMixin, ModelOnlyObserverMixin, ModelObserverBase
123
- ):
120
+ class PreDeletionDataSavingObserver(OriginalObjectMixin, ModelOnlyObserverMixin, ModelObserverBase):
124
121
  """Класс для перехвата и сохранения объекта модели перед удалением."""
125
122
 
126
123
  def __init__(self, storage, observables=None):
@@ -136,10 +133,14 @@ class PreDeletionDataSavingObserver(
136
133
  assert isinstance(storage, AbstractInstanceStorage)
137
134
  self._storage = storage
138
135
 
139
- super(PreDeletionDataSavingObserver, self).__init__()
136
+ super().__init__()
140
137
  if observables:
141
138
  for model in observables:
142
139
  self.observe(model)
143
140
 
144
141
  def pre_delete(self, instance, context, **kwargs):
142
+ """Сохраняет данные объекта перед его удалением.
143
+
144
+ Вызывается автоматически перед удалением объекта модели.
145
+ """
145
146
  self._storage.save(instance, context=context, **kwargs)
@@ -1,4 +1,2 @@
1
1
  def connect_plugin(settings, plugin_settings):
2
- settings['INSTALLED_APPS'].append(
3
- __package__ + '.apps.ContingentPluginAppConfig'
4
- )
2
+ settings['INSTALLED_APPS'].append(__package__ + '.apps.ContingentPluginAppConfig')
@@ -7,13 +7,10 @@ from educommon.utils.storage import (
7
7
  )
8
8
 
9
9
 
10
- ContingentModelDeleted = get_model(
11
- 'contingent_plugin', 'ContingentModelDeleted')
10
+ ContingentModelDeleted = get_model('contingent_plugin', 'ContingentModelDeleted')
12
11
 
13
12
 
14
- class ContingentDeletedInstancesDataStorage(
15
- AbstractInstanceDataStorage
16
- ):
13
+ class ContingentDeletedInstancesDataStorage(AbstractInstanceDataStorage):
17
14
  """Класс для сохранения данных объекта модели для Контингента.
18
15
 
19
16
  Предполагается использование объекта этого класса вместе с классом
@@ -40,15 +37,18 @@ class ContingentDeletedInstancesDataStorage(
40
37
  :rtype: ContentType
41
38
  """
42
39
  ContentType = get_model('contenttypes', 'ContentType')
40
+
43
41
  return ContentType.objects.get_for_model(instance)
44
42
 
45
43
  def _is_instance_already_saved(self, instance, **kwargs):
44
+ """Проверяет, сохранён ли уже объект как удалённый."""
46
45
  return ContingentModelDeleted.objects.filter(
47
46
  content_type=self._get_instance_content_type(instance),
48
47
  object_id=instance.pk,
49
48
  ).exists()
50
49
 
51
50
  def _save_instance_data(self, instance, **kwargs):
51
+ """Сохраняет данные удаляемого объекта в модель ContingentModelDeleted."""
52
52
  instance_data = self._get_instance_data(instance)
53
53
  if instance_data is not None:
54
54
  ContingentModelDeleted.objects.get_or_create(
@@ -67,6 +67,7 @@ class ContingentDeletedInstancesDataStorage(
67
67
  pass
68
68
 
69
69
  def save(self, instance, **kwargs):
70
- super(ContingentDeletedInstancesDataStorage, self).save(
71
- instance, **kwargs)
70
+ """Сохраняет данные и вызывает удаление зависимостей после сохранения."""
71
+ super().save(instance, **kwargs)
72
+
72
73
  self.delete_some_objects_after_saving(instance, **kwargs)
@@ -73,8 +73,7 @@ def get_params_from_deleted_model(model, object_id):
73
73
  :raise: ApplicationLogicException
74
74
  """
75
75
  obj = ContingentModelDeleted.objects.filter(
76
- content_type=ContentType.objects.get_for_model(model),
77
- object_id=object_id
76
+ content_type=ContentType.objects.get_for_model(model), object_id=object_id
78
77
  ).first()
79
78
 
80
79
  if not obj:
@@ -102,6 +101,7 @@ def get_param_value_from_deleted_model(model, object_id, param_name):
102
101
  """
103
102
  params = get_params_from_deleted_model(model, object_id)
104
103
  param_value = params.get(param_name)
104
+
105
105
  return param_value
106
106
 
107
107
 
@@ -130,10 +130,10 @@ def get_new_param_tuples(params_tuples, model):
130
130
  result_tuples = []
131
131
  for params_tuple in params_tuples:
132
132
  param_name = params_tuple[0]
133
- new_function = partial(
134
- get_param_value_from_deleted_model, model, param_name=param_name)
133
+ new_function = partial(get_param_value_from_deleted_model, model, param_name=param_name)
135
134
  param_tuple = (param_name, ('object_id', new_function), None, None)
136
135
  result_tuples.append(param_tuple)
136
+
137
137
  return tuple(result_tuples)
138
138
 
139
139
 
@@ -152,11 +152,11 @@ def get_original_and_deleted_instances_info(model, query_config):
152
152
  :rtype: Tuple[Tuple[QuerySet, tuple], Tuple[QuerySet, tuple]]
153
153
  """
154
154
  model_content_type = ContentType.objects.get_for_model(model)
155
- new_query = ContingentModelDeleted.objects.filter(
156
- content_type=model_content_type)
155
+ new_query = ContingentModelDeleted.objects.filter(content_type=model_content_type)
157
156
 
158
157
  _, params_tuples = query_config
159
158
  new_param_tuples = get_new_param_tuples(params_tuples, model)
160
159
 
161
160
  deleted_objects_query_config = new_query, new_param_tuples
161
+
162
162
  return query_config, deleted_objects_query_config
@@ -1,4 +1,5 @@
1
1
  """Кастомные поля моделей Django"""
2
+
2
3
  from datetime import (
3
4
  datetime,
4
5
  timedelta,
@@ -61,188 +62,173 @@ class SingleErrorDecimalField(fields.DecimalField):
61
62
 
62
63
  @cached_property
63
64
  def validators(self):
64
- """
65
- Переопрелеление стандартного валидатора Decimal.
65
+ """Переопрелеление стандартного валидатора Decimal.
66
66
 
67
67
  :return: Список валидаторов.
68
68
  """
69
69
  validators = super(SingleErrorDecimalField, self).validators
70
+
70
71
  validators.pop()
71
- validators.append(
72
- simple.SingleErrorDecimalValidator(
73
- self.max_digits, self.decimal_places
74
- )
75
- )
72
+ validators.append(simple.SingleErrorDecimalValidator(self.max_digits, self.decimal_places))
73
+
76
74
  return validators
77
75
 
78
76
 
79
77
  class FIOField(fields.CharField, IMaskRegexField):
78
+ """Поле для ввода ФИО с маской и валидацией.
79
+
80
+ Разрешены только буквы, пробелы и дефисы.
81
+ """
82
+
80
83
  _mask_re = r'^[а-яА-ЯёЁa-zA-Z\s-]*$'
81
84
 
82
- default_validators = [
83
- simple.FIOValidator()
84
- ]
85
+ default_validators = [simple.FIOValidator()]
85
86
 
86
87
 
87
88
  class RangedDateField(fields.DateField):
88
89
  """Поле, реализующее валидаторы по умолчанию для границ периода"""
89
90
 
90
- def __init__(self, minimum_date=datetime(1917, 1, 1).date(),
91
- maximum_date=None, **kwargs):
92
- super(RangedDateField, self).__init__(**kwargs)
93
- self.validators.append(simple.date_range_validator(
94
- minimum=minimum_date, maximum=maximum_date))
91
+ def __init__(self, minimum_date=datetime(1, 1, 1).date(), maximum_date=None, **kwargs):
92
+ super().__init__(**kwargs)
93
+ self.validators.append(simple.date_range_validator(minimum=minimum_date, maximum=maximum_date))
95
94
 
96
95
 
97
96
  class LastNameField(FIOField):
98
97
  """Расширение поля ФИО для фамилии"""
99
98
 
100
99
  def __init__(self, verbose_name='Фамилия', max_length=30, **kwargs):
101
- super(LastNameField, self).__init__(
102
- verbose_name=verbose_name, max_length=max_length, **kwargs)
100
+ super().__init__(verbose_name=verbose_name, max_length=max_length, **kwargs)
103
101
 
104
102
 
105
103
  class FirstNameField(FIOField):
106
104
  """Расширение поля ФИО для имени"""
107
105
 
108
106
  def __init__(self, verbose_name='Имя', max_length=30, **kwargs):
109
- super(FirstNameField, self).__init__(
110
- verbose_name=verbose_name, max_length=max_length, **kwargs)
107
+ super().__init__(verbose_name=verbose_name, max_length=max_length, **kwargs)
111
108
 
112
109
 
113
110
  class MiddleNameField(FIOField):
114
111
  """Расширение поля ФИО для отчества"""
115
112
 
116
- def __init__(self, verbose_name='Отчество',
117
- null=True, blank=True, max_length=30, **kwargs):
118
- super(MiddleNameField, self).__init__(
119
- verbose_name=verbose_name, null=null, blank=blank,
120
- max_length=max_length, **kwargs)
113
+ def __init__(self, verbose_name='Отчество', null=True, blank=True, max_length=30, **kwargs):
114
+ super().__init__(verbose_name=verbose_name, null=null, blank=blank, max_length=max_length, **kwargs)
121
115
 
122
116
 
123
117
  class SNILSField(fields.CharField, IMaskRegexField):
118
+ """Поле модели для ввода СНИЛС с маской и валидацией."""
124
119
 
125
120
  _mask_re = r'^[-\s\d]{0,14}$'
126
121
 
127
- default_validators = [
128
- simple.SNILSValidator()
129
- ]
122
+ default_validators = [simple.SNILSValidator()]
130
123
 
131
- def __init__(
132
- self, verbose_name='СНИЛС', **kwargs
133
- ):
124
+ def __init__(self, verbose_name='СНИЛС', **kwargs):
134
125
  kwargs.setdefault('max_length', 14)
135
- super(SNILSField, self).__init__(
136
- verbose_name=verbose_name, **kwargs)
126
+
127
+ super().__init__(verbose_name=verbose_name, **kwargs)
137
128
 
138
129
 
139
130
  class BirthDateField(RangedDateField):
140
- def __init__(self, minimum_date=datetime(1917, 1, 1).date(),
141
- maximum_date=date_difference_as_callable(timedelta(days=1)),
142
- verbose_name='Дата рождения', **kwargs):
143
- super(BirthDateField, self).__init__(
144
- minimum_date=minimum_date, maximum_date=maximum_date,
145
- verbose_name=verbose_name, **kwargs)
131
+ """Поле даты рождения с ограничением по минимальной и максимальной дате."""
132
+
133
+ def __init__(
134
+ self,
135
+ minimum_date=datetime(1, 1, 1).date(),
136
+ maximum_date=date_difference_as_callable(timedelta(days=1)),
137
+ verbose_name='Дата рождения',
138
+ **kwargs,
139
+ ):
140
+ super().__init__(minimum_date=minimum_date, maximum_date=maximum_date, verbose_name=verbose_name, **kwargs)
146
141
 
147
142
 
148
143
  class DocumentSeriesField(fields.CharField, IMaskRegexField):
144
+ """Поле серии документа с маской и соответствующим валидатором."""
145
+
149
146
  _mask_re = r'^[a-zA-Zа-яА-ЯёЁ\d\s|\-|\.|\,|\\|\/]*$'
150
147
 
151
- default_validators = [
152
- simple.DocumentSeriesValidator()
153
- ]
148
+ default_validators = [simple.DocumentSeriesValidator()]
154
149
 
155
- def __init__(
156
- self, verbose_name='Серия документа', **kwargs
157
- ):
158
- super(DocumentSeriesField, self).__init__(
159
- verbose_name=verbose_name, **kwargs)
150
+ def __init__(self, verbose_name='Серия документа', **kwargs):
151
+ super().__init__(verbose_name=verbose_name, **kwargs)
160
152
 
161
153
 
162
154
  class DocumentNumberField(fields.CharField, IMaskRegexField):
155
+ """Поле номера документа с маской и соответствующим валидатором."""
156
+
163
157
  _mask_re = r'^[a-zA-Zа-яА-ЯёЁ\d\s|\-|\.|\,|\\|\/]*$'
164
158
 
165
- default_validators = [
166
- simple.DocumentNumberValidator()
167
- ]
159
+ default_validators = [simple.DocumentNumberValidator()]
168
160
 
169
- def __init__(
170
- self, verbose_name='Номер документа', **kwargs
171
- ):
172
- super(DocumentNumberField, self).__init__(
173
- verbose_name=verbose_name, **kwargs)
161
+ def __init__(self, verbose_name='Номер документа', **kwargs):
162
+ super().__init__(verbose_name=verbose_name, **kwargs)
174
163
 
175
164
 
176
165
  class PassportSeriesField(DocumentSeriesField):
166
+ """Поле серии паспорта с числовой маской и ограничением по длине."""
167
+
177
168
  _mask_re = r'^\d{0,4}$'
178
169
 
179
- default_validators = [
180
- simple.PassportSeriesValidator()
181
- ]
170
+ default_validators = [simple.PassportSeriesValidator()]
182
171
 
183
- def __init__(
184
- self, verbose_name='Серия паспорта', **kwargs
185
- ):
172
+ def __init__(self, verbose_name='Серия паспорта', **kwargs):
186
173
  kwargs.setdefault('max_length', 4)
187
- super(PassportSeriesField, self).__init__(
188
- verbose_name=verbose_name, **kwargs)
174
+
175
+ super().__init__(verbose_name=verbose_name, **kwargs)
189
176
 
190
177
 
191
178
  class PassportNumberField(DocumentNumberField):
179
+ """Поле номера паспорта с числовой маской и ограничением по длине."""
180
+
192
181
  _mask_re = r'^\d{0,6}$'
193
182
 
194
- default_validators = [
195
- simple.PassportNumberValidator()
196
- ]
183
+ default_validators = [simple.PassportNumberValidator()]
197
184
 
198
- def __init__(
199
- self, verbose_name='Номер паспорта', **kwargs
200
- ):
185
+ def __init__(self, verbose_name='Номер паспорта', **kwargs):
201
186
  kwargs.setdefault('max_length', 6)
202
- super(PassportNumberField, self).__init__(
203
- verbose_name=verbose_name, **kwargs)
187
+
188
+ super().__init__(verbose_name=verbose_name, **kwargs)
204
189
 
205
190
 
206
191
  class INNField(fields.CharField, IMaskRegexField):
192
+ """Поле ИНН с маской и встроенной валидацией."""
193
+
207
194
  _mask_re = r'^\d{0,12}$'
208
195
 
209
- default_validators = [
210
- simple.inn_validator
211
- ]
196
+ default_validators = [simple.inn_validator]
212
197
 
213
198
  def __init__(self, verbose_name='ИНН', **kwargs):
214
199
  kwargs.setdefault('max_length', 12)
215
- super(INNField, self).__init__(verbose_name=verbose_name, **kwargs)
200
+
201
+ super().__init__(verbose_name=verbose_name, **kwargs)
216
202
 
217
203
 
218
204
  class KPPField(fields.CharField, IMaskRegexField):
205
+ """Поле КПП с маской, без дублирования ошибок превышения длины."""
206
+
219
207
  _mask_re = r'^\d{0,9}$'
220
208
 
221
- default_validators = [
222
- simple.kpp_validator
223
- ]
209
+ default_validators = [simple.kpp_validator]
224
210
 
225
211
  def __init__(self, verbose_name='КПП', **kwargs):
226
212
  kwargs.setdefault('max_length', 9)
227
- super(KPPField, self).__init__(
228
- verbose_name=verbose_name, **kwargs)
213
+
214
+ super().__init__(verbose_name=verbose_name, **kwargs)
215
+
229
216
  # из за стандартного валидатора дублируются сообщения об ошибке
230
217
  # привышения длинны поля
231
218
  try:
232
- self.validators.remove(
233
- validators.MaxLengthValidator(self.max_length)
234
- )
219
+ self.validators.remove(validators.MaxLengthValidator(self.max_length))
235
220
  except ValueError:
236
221
  pass
237
222
 
238
223
 
239
224
  class OGRNField(fields.CharField, IMaskRegexField):
225
+ """Поле ОГРН с маской и встроенной валидацией."""
226
+
240
227
  _mask_re = r'^\d{0,15}$'
241
228
 
242
- default_validators = [
243
- simple.ogrn_validator
244
- ]
229
+ default_validators = [simple.ogrn_validator]
245
230
 
246
231
  def __init__(self, verbose_name='ОГРН', **kwargs):
247
232
  kwargs.setdefault('max_length', 15)
248
- super(OGRNField, self).__init__(verbose_name=verbose_name, **kwargs)
233
+
234
+ super().__init__(verbose_name=verbose_name, **kwargs)
@@ -7,8 +7,7 @@ from operator import (
7
7
  )
8
8
 
9
9
 
10
- def date_difference_as_callable(
11
- diff, date_fn=datetime.date.today, operator_=sub):
10
+ def date_difference_as_callable(diff, date_fn=datetime.date.today, operator_=sub):
12
11
  """Функция, возвращающая callable-объект, не принимающий аргументов и
13
12
  возвращающий дату.
14
13
  Используется для валидации дат, когда нужно проверить не точную дату, а
@@ -24,13 +23,10 @@ def date_difference_as_callable(
24
23
  :return:
25
24
  :rtype: Callable[[], datetime.date]
26
25
  """
27
- return partial(
28
- _get_time_difference_from_callable,
29
- operator_=operator_, date_fn=date_fn, diff=diff)
26
+ return partial(_get_time_difference_from_callable, operator_=operator_, date_fn=date_fn, diff=diff)
30
27
 
31
28
 
32
- def _get_time_difference_from_callable(
33
- operator_, date_fn, diff):
29
+ def _get_time_difference_from_callable(operator_, date_fn, diff):
34
30
  """Вспомогательная функция для сериализации валидатора при генерации
35
31
  файла-миграции. Нужен для того, чтобы при генерации миграции функция
36
32
  date_fn не разворачивалась в точное значение, а оставалась функцией.