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
@@ -74,10 +74,10 @@ def get_related_fields(model, skip_func=None):
74
74
  if not related_model._meta.proxy:
75
75
  for field in related_model._meta.get_fields():
76
76
  if (
77
- field.concrete and
78
- isinstance(field, RelatedField) and
79
- issubclass(model, get_related(field).parent_model) and
80
- (not skip_func or not skip_func(field))
77
+ field.concrete
78
+ and isinstance(field, RelatedField)
79
+ and issubclass(model, get_related(field).parent_model)
80
+ and (not skip_func or not skip_func(field))
81
81
  ):
82
82
  yield field
83
83
 
@@ -134,10 +134,7 @@ def get_related_instances(obj, collapse=True, skip_func=None):
134
134
  """
135
135
 
136
136
  assert isinstance(obj, Model), type(obj)
137
- for model, fields in groupby(
138
- get_related_fields(obj.__class__, skip_func),
139
- lambda field: field.model
140
- ):
137
+ for model, fields in groupby(get_related_fields(obj.__class__, skip_func), lambda field: field.model):
141
138
  conditions = reduce(or_, (Q(**{field.name: obj}) for field in fields))
142
139
  for related_obj in model.objects.filter(conditions).iterator():
143
140
  if collapse:
@@ -145,13 +142,10 @@ def get_related_instances(obj, collapse=True, skip_func=None):
145
142
  else:
146
143
  for field in model._meta.get_fields():
147
144
  if (
148
- field.concrete and
149
- isinstance(field, RelatedField) and
150
- (
151
- field.related_model == obj.__class__ and
152
- getattr(related_obj, field.attname) == obj.pk
153
- ) and
154
- (not skip_func or not skip_func(field))
145
+ field.concrete
146
+ and isinstance(field, RelatedField)
147
+ and (field.related_model == obj.__class__ and getattr(related_obj, field.attname) == obj.pk)
148
+ and (not skip_func or not skip_func(field))
155
149
  ):
156
150
  yield related_obj
157
151
 
@@ -188,21 +182,18 @@ def get_non_block_relations(obj):
188
182
  """
189
183
 
190
184
  displayed_relations = dict()
191
- collector = Collector(
192
- using=router.db_for_write(obj.__class__, instance=obj)
193
- )
185
+ collector = Collector(using=router.db_for_write(obj.__class__, instance=obj))
194
186
  collector.collect((obj,))
195
187
  # Коллектор содержит ссылку на obj, удалим сам объект из коллектора
196
188
  collector.data[obj.__class__].remove(obj)
197
189
 
198
190
  for model, related_objects in collector.data.items():
199
- if all((
200
- getattr(model, 'display_related_error', True),
201
- related_objects
202
- )):
203
- key = '.'.join((
204
- model._meta.app_label,
205
- model._meta.object_name,
206
- ))
191
+ if all((getattr(model, 'display_related_error', True), related_objects)):
192
+ key = '.'.join(
193
+ (
194
+ model._meta.app_label,
195
+ model._meta.object_name,
196
+ )
197
+ )
207
198
  displayed_relations[key] = tuple(related_objects)
208
199
  return displayed_relations
@@ -19,10 +19,7 @@ def is_extension_exists(alias, name):
19
19
  :rtype: bool
20
20
  """
21
21
  with closing(connections[alias].cursor()) as cursor:
22
- cursor.execute(
23
- "SELECT 1 FROM pg_extension WHERE extname = %s",
24
- (name,)
25
- )
22
+ cursor.execute('SELECT 1 FROM pg_extension WHERE extname = %s', (name,))
26
23
  return cursor.fetchone() is not None
27
24
 
28
25
 
@@ -1,4 +1,5 @@
1
1
  """Средства для работы со шрифтами."""
2
+
2
3
  import os.path
3
4
 
4
5
  from PIL import (
@@ -51,6 +52,7 @@ def _split_word(font, word, max_width):
51
52
 
52
53
  :rtype: tuple: (<Часть слова, умещающаяся в max_width>, <остаток>).
53
54
  """
55
+
54
56
  def _split_index(index, max_index=None):
55
57
  """Рекурсивно разбивает слово на две части.
56
58
 
@@ -59,14 +61,12 @@ def _split_word(font, word, max_width):
59
61
  больше максимальной.
60
62
  """
61
63
  index = int(index)
62
- assert index > 0, ('Ширины строки не хватает, '
63
- 'чтобы поместить один символ')
64
+ assert index > 0, 'Ширины строки не хватает, чтобы поместить один символ'
64
65
 
65
66
  # определяем ширину слова в пикселях
66
67
  width, _ = font.getsize(word[:index])
67
68
 
68
69
  if width > max_width:
69
-
70
70
  if index == max_index:
71
71
  # Если индексы равны, значит предыдущее условие было
72
72
  # width < max_width, а предыдущий index на единицу меньше.
@@ -77,7 +77,6 @@ def _split_word(font, word, max_width):
77
77
  return _split_index(index // 2, index)
78
78
 
79
79
  elif width < max_width:
80
-
81
80
  if max_index is None:
82
81
  # Нас обманули, слово влазит в одну строку.
83
82
  return index
@@ -23,8 +23,7 @@ class LicenceError(Exception):
23
23
  class Licence:
24
24
  """Базовый класс лицензии."""
25
25
 
26
- def __init__(self, licence_file_path, schema_file_path, config,
27
- params_root='//BarsLicence/LicenceData'):
26
+ def __init__(self, licence_file_path, schema_file_path, config, params_root='//BarsLicence/LicenceData'):
28
27
  """Инициализация экземпляра класса.
29
28
 
30
29
  :param basestring licence_file_path: Путь к файлу лицензии.
@@ -67,14 +66,10 @@ class Licence:
67
66
  существует, либо к нему нет доступа.
68
67
  """
69
68
  if not os.path.exists(file_path):
70
- raise ImproperlyConfigured(
71
- 'Licence file not found: ' + file_path
72
- )
69
+ raise ImproperlyConfigured(f'Licence file not found: {file_path}')
73
70
 
74
71
  if not os.access(file_path, os.R_OK):
75
- raise ImproperlyConfigured(
76
- "Can't read licence file: " + file_path
77
- )
72
+ raise ImproperlyConfigured(f"Can't read licence file: {file_path}")
78
73
 
79
74
  @cached_property
80
75
  def _params_elements(self):
@@ -97,14 +92,10 @@ class Licence:
97
92
  schema_uri=schema_uri,
98
93
  )
99
94
  except etree.XMLSyntaxError as error:
100
- raise LicenceError(
101
- 'Error parsing licence XML document {}:\n{}'
102
- .format(self.licence_file_path, str(error))
103
- )
95
+ raise LicenceError('Error parsing licence XML document {}:\n{}'.format(self.licence_file_path, str(error)))
104
96
 
105
97
  result = document_tree.xpath(self._params_root)[0]
106
98
 
107
-
108
99
  # поскольку промежуточный результат в памяти хранить необязательно,
109
100
  # а необходим он только в 2 местах, кладем его в список 2 раза, после
110
101
  # этого достаем в каждом и не храним в памяти промежуточные результаты
@@ -131,9 +122,7 @@ class Licence:
131
122
  """
132
123
  params_root_element = self._params_elements.pop()
133
124
 
134
- return set(
135
- params_root_element.xpath('plugins/plugin/attribute::name')
136
- )
125
+ return set(params_root_element.xpath('plugins/plugin/attribute::name'))
137
126
 
138
127
  def __getattr__(self, attr_name):
139
128
  return self._params[attr_name]
educommon/utils/misc.py CHANGED
@@ -19,7 +19,7 @@ class cached_property(property):
19
19
  """
20
20
 
21
21
  def __init__(self, method):
22
- super(cached_property, self).__init__(method)
22
+ super().__init__(method)
23
23
 
24
24
  self.__doc__ = method.__doc__
25
25
 
@@ -66,11 +66,7 @@ def get_nested_attr(obj, attr, default=Undefined):
66
66
  if default is not Undefined:
67
67
  return default
68
68
  else:
69
- raise AttributeError(
70
- "'{}' object has no attribute '{}'".format(
71
- type(obj), nested_attribute
72
- )
73
- )
69
+ raise AttributeError("'{}' object has no attribute '{}'".format(type(obj), nested_attribute))
74
70
 
75
71
  return nested_object
76
72
 
@@ -78,7 +74,7 @@ def get_nested_attr(obj, attr, default=Undefined):
78
74
  def md5sum(filepath):
79
75
  """Возвращает контрольную сумму MD5 указанного файла.
80
76
 
81
- :param basestring filepath: Путь к файлу.
77
+ :param str filepath: Путь к файлу.
82
78
 
83
79
  :rtype: str
84
80
  """
@@ -113,9 +109,10 @@ def get_mime_type_for_extension(extension):
113
109
  :rtype: str
114
110
  """
115
111
  import mimetypes
112
+
116
113
  mimetypes.init()
117
114
  if not extension.startswith('.'):
118
- extension = '.%s' % extension
115
+ extension = f'.{extension}'
119
116
  return mimetypes.types_map.get(extension.lower())
120
117
 
121
118
 
@@ -131,19 +128,13 @@ def message_to_sentry(message=None, extra=None, tag=None, level=logging.INFO):
131
128
  from sentry_sdk import (
132
129
  capture_message,
133
130
  )
134
- capture_message(
135
- message=message,
136
- level=level,
137
- extras=extra,
138
- tags={'tags': tag} if tag else None
139
- )
131
+
132
+ capture_message(message=message, level=level, extras=extra, tags={'tags': tag} if tag else None)
140
133
  else:
141
134
  from raven.contrib.django.raven_compat.models import (
142
135
  client as sentry_client,
143
136
  )
137
+
144
138
  sentry_client.captureMessage(
145
- message=message,
146
- data={'level': level},
147
- extra=extra,
148
- tags={'tags': tag} if tag else None
139
+ message=message, data={'level': level}, extra=extra, tags={'tags': tag} if tag else None
149
140
  )
@@ -1,4 +1,5 @@
1
1
  """Утилиты для ObjectGrid."""
2
+
2
3
  import json
3
4
 
4
5
  from m3.actions.urls import (
@@ -31,18 +32,14 @@ def add_action_button(label, grid, action, icon_cls, index=None):
31
32
  add_action_button('Печать', grid, self.print_action, Icons.PRINTER)
32
33
  """
33
34
  action_url = get_url(action)
34
- button = ext.ExtButton(
35
- text=label,
36
- handler=_get_action_handler(action_url),
37
- icon_cls=icon_cls)
35
+ button = ext.ExtButton(text=label, handler=_get_action_handler(action_url), icon_cls=icon_cls)
38
36
  if index is not None:
39
37
  grid.top_bar.items.insert(index, button)
40
38
  else:
41
39
  grid.top_bar.items.append(button)
42
40
 
43
41
 
44
- def add_one_row_button(label, grid, action, icon_cls,
45
- dbl_clicked=False, index=None):
42
+ def add_one_row_button(label, grid, action, icon_cls, dbl_clicked=False, index=None):
46
43
  """
47
44
  Добавление кнопки в тулбар и popup меню ObjectGrid'а.
48
45
 
@@ -81,8 +78,7 @@ def add_one_row_button(label, grid, action, icon_cls,
81
78
  grid.handler_dblclick = grid.dblclick_handler
82
79
 
83
80
 
84
- def add_multi_row_button(label, grid, action, icon_cls,
85
- confirm_required=False, index=None):
81
+ def add_multi_row_button(label, grid, action, icon_cls, confirm_required=False, index=None):
86
82
  """
87
83
  Добавление кнопки в тулбар и popup меню ObjectGrid'а.
88
84
 
@@ -137,27 +133,27 @@ def _get_multirow_params(label, action, icon_cls, confirm_required):
137
133
 
138
134
 
139
135
  def _get_action_handler(url):
140
- return """
141
- function(){
142
- onObjGridAction(objGrid, '%s');
143
- }
144
- """ % url
136
+ return f"""
137
+ function() {{
138
+ onObjGridAction(objGrid, '{url}');
139
+ }}
140
+ """
145
141
 
146
142
 
147
143
  def _get_one_row_handler(action_name, url):
148
- return """
149
- function(){
150
- onObjGridOneRecordAction(objGrid, '%s', '%s');
151
- }
152
- """ % (action_name, url)
144
+ return f"""
145
+ function() {{
146
+ onObjGridOneRecordAction(objGrid, '{action_name}', '{url}');
147
+ }}
148
+ """
153
149
 
154
150
 
155
151
  def _get_multi_row_handler(action_name, url, confirm_required):
156
- return """
157
- function(){
158
- onObjGridMultiRecordAction(objGrid, '%s', '%s', %s);
159
- }
160
- """ % (action_name, url, int(bool(confirm_required)))
152
+ return f"""
153
+ function() {{
154
+ onObjGridMultiRecordAction(objGrid, '{action_name}', '{url}', {int(bool(confirm_required))});
155
+ }}
156
+ """
161
157
 
162
158
 
163
159
  def column_style_renderer(styles_map, default_style=''):
@@ -166,13 +162,15 @@ def column_style_renderer(styles_map, default_style=''):
166
162
  :param styles_map: словарь карты стилей по значению в колонке
167
163
  :param default_style: стиль по-умолчанию
168
164
  """
169
- func = (
170
- """function (value, metaData, record, rowIndex, colIndex, store) {
171
- var styles_map = Ext.util.JSON.decode('%(styles_map)s');
172
- metaData.style += styles_map[value] || '%(default_style)s';
173
- return value;
174
- }""") % {'styles_map': styles_map, 'default_style': default_style}
175
- return func
165
+ styles_map = json.dumps(styles_map)
166
+
167
+ return f"""
168
+ function (value, metaData, record, rowIndex, colIndex, store) {{
169
+ var styles_map = Ext.util.JSON.decode('{styles_map}');
170
+ metaData.style += styles_map[value] || '{default_style}';
171
+ return value;
172
+ }}
173
+ """
176
174
 
177
175
 
178
176
  def set_grid_initial(grid, initializers):
@@ -194,12 +192,12 @@ def set_grid_initial(grid, initializers):
194
192
  ))
195
193
  """
196
194
  grid_initializers = ''.join(initializers)
197
- grid._listeners['added'] = """
198
- function() {
199
- var grid = Ext.getCmp('%(client_id)s');
200
- %(initializers)s
201
- }
202
- """ % {'initializers': grid_initializers, 'client_id': grid.client_id}
195
+ grid._listeners['added'] = f"""
196
+ function() {{
197
+ var grid = Ext.getCmp('{grid.client_id}');
198
+ {grid_initializers}
199
+ }}
200
+ """
203
201
 
204
202
 
205
203
  def styling_grid_rows(data_index, styles_map, default_style=''):
@@ -209,18 +207,16 @@ def styling_grid_rows(data_index, styles_map, default_style=''):
209
207
  :param styles_map: словарь карты стилей по значению в колонке
210
208
  :param default_style: стиль по-умолчанию
211
209
  """
212
- return """
213
- var styles_map = Ext.util.JSON.decode('%(styles_map)s');
214
- grid.getView().getRowClass = function(record, rowIndex, rp, ds){
210
+ styles_map = json.dumps(styles_map)
211
+
212
+ return f"""
213
+ var styles_map = Ext.util.JSON.decode('{styles_map}');
214
+ grid.getView().getRowClass = function(record, rowIndex, rp, ds) {{
215
215
  return styles_map[
216
- record.json.%(data_index)s
217
- ] || '%(default_style)s';
218
- };
219
- """ % {
220
- 'data_index': data_index,
221
- 'styles_map': json.dumps(styles_map),
222
- 'default_style': default_style,
223
- }
216
+ record.json.{data_index}
217
+ ] || '{default_style}';
218
+ }};
219
+ """
224
220
 
225
221
 
226
222
  def add_tooltip_to_grid_rows(delegate: str, column_with_text: str) -> str:
@@ -229,26 +225,23 @@ def add_tooltip_to_grid_rows(delegate: str, column_with_text: str) -> str:
229
225
  :param delegate: css-класс строк к которым добавляется подсказка
230
226
  :param column_with_text: наименование колонки содержащей текст подсказки
231
227
  """
232
- return """
228
+ return f"""
233
229
  var view = grid.getView();
234
230
  var store = grid.getStore();
235
- grid.on('render', function(grid) {
236
- grid.tooltip = new Ext.ToolTip({
231
+ grid.on('render', function(grid) {{
232
+ grid.tooltip = new Ext.ToolTip({{
237
233
  target: view.mainBody,
238
- delegate: '%(delegate)s',
239
- listeners: {
240
- show: function updateTipBody(tooltip) {
234
+ delegate: '{delegate}',
235
+ listeners: {{
236
+ show: function updateTipBody(tooltip) {{
241
237
  var rowIndex = view.findRowIndex(tooltip.triggerElement);
242
- var text = store.getAt(rowIndex).data['%(column)s'];
238
+ var text = store.getAt(rowIndex).data['{column_with_text}'];
243
239
  tooltip.update(text);
244
- }
245
- }
246
- });
247
- });
248
- """ % {
249
- 'delegate': delegate,
250
- 'column': column_with_text,
251
- }
240
+ }}
241
+ }}
242
+ }});
243
+ }});
244
+ """
252
245
 
253
246
 
254
247
  def boolean_column_renderer():
@@ -69,9 +69,7 @@ class PhoneField(CharField):
69
69
  kwargs.setdefault('max_length', 31)
70
70
  super().__init__(*args, **kwargs)
71
71
 
72
- self.validators.append(
73
- PHONE_FIELD_TYPE_TO_VALIDATOR.get(phone_type, validate_common_phone_number)
74
- )
72
+ self.validators.append(PHONE_FIELD_TYPE_TO_VALIDATOR.get(phone_type, validate_common_phone_number))
75
73
 
76
74
  def to_python(self, value):
77
75
  """Преобразование входящего значения в ожидаемый тип Python."""
@@ -11,12 +11,12 @@ from educommon.utils.phone_number.enums import (
11
11
 
12
12
 
13
13
  PHONE_REGEX = re.compile(
14
- r'^(?:' # группа с кодом страны и кодом региона
15
- r'(\+[1-6,90]{1,3}|\+?7|\+?8)?-?\s*' # опциональный код страны (+XXX, для России может быть +7, 7, 8)
14
+ r'^(?:' # группа с кодом страны и кодом региона
15
+ r'(\+[1-6,90]{1,3}|\+?7|\+?8)?-?\s*' # опциональный код страны (+XXX, для России может быть +7, 7, 8)
16
16
  r'\(?(\d{3}(?:[-\s]?\d{0,2}(?=[)-.\s]))?)\)?[-.\s]*' # оцпиональный код региона/города в необязательных скобках
17
- r')?' # группа с кодом страны и кодом региона может отсутствовать
17
+ r')?' # группа с кодом страны и кодом региона может отсутствовать
18
18
  r'(\d{1,2}[-.\s]*\d?)[-.\s]*(\d[-.\s]*\d)[-.\s]*(\d[-.\s]*\d)$' # абонентский номер в формате XXX-XX-XX
19
- # первая группа может состоять от 1 до 3 цифр
19
+ # первая группа может состоять от 1 до 3 цифр
20
20
  )
21
21
 
22
22
  # российский мобильный номер вида +7 (9XX) XXX-XX-XX
@@ -177,10 +177,7 @@ class PhoneNumber:
177
177
  @property
178
178
  def is_russia(self) -> bool:
179
179
  """Признак, что номер российский."""
180
- return (
181
- self._cleaned.startswith(RU_PHONE_CODE)
182
- or (self.is_valid and not self.region_code)
183
- )
180
+ return self._cleaned.startswith(RU_PHONE_CODE) or (self.is_valid and not self.region_code)
184
181
 
185
182
  @property
186
183
  def cleaned(self) -> str:
@@ -18,8 +18,7 @@ def validate_common_phone_number(phone_number: Union[str, PhoneNumber]):
18
18
 
19
19
  if isinstance(phone_number, PhoneNumber) and not phone_number.is_valid:
20
20
  raise ValidationError(
21
- 'Неверный формат! Примеры допустимых форматов: '
22
- '+XXX (XXX) XXX XX XX, 8 (XXXXX) X-XX-XX, XXX-XX-XX',
21
+ 'Неверный формат! Примеры допустимых форматов: +XXX (XXX) XXX XX XX, 8 (XXXXX) X-XX-XX, XXX-XX-XX',
23
22
  code='invalid',
24
23
  )
25
24
 
@@ -28,14 +27,9 @@ def validate_e164_phone_number(phone_number: Union[str, PhoneNumber]):
28
27
  """Валидация номера телефона в международном формате."""
29
28
  phone_number = to_python(phone_number)
30
29
 
31
- if (
32
- isinstance(phone_number, PhoneNumber)
33
- and not phone_number.is_valid
34
- or not phone_number.is_e164
35
- ):
30
+ if isinstance(phone_number, PhoneNumber) and not phone_number.is_valid or not phone_number.is_e164:
36
31
  raise ValidationError(
37
- 'Неверный формат! Примеры допустимых форматов: '
38
- '+XXX (XXX) XXX XX XX, +7 (XXX) XXX-XX-XX, 8 (XXXXX) X-XX-XX',
32
+ 'Неверный формат! Примеры допустимых форматов: +XXX (XXX) XXX XX XX, +7 (XXX) XXX-XX-XX, 8 (XXXXX) X-XX-XX',
39
33
  code='invalid',
40
34
  )
41
35
 
@@ -44,14 +38,9 @@ def validate_ru_phone_number(phone_number: Union[str, PhoneNumber]):
44
38
  """Валидация российского номера телефона в общем формате."""
45
39
  phone_number = to_python(phone_number)
46
40
 
47
- if (
48
- isinstance(phone_number, PhoneNumber)
49
- and not phone_number.is_valid
50
- or not phone_number.is_russia
51
- ):
41
+ if isinstance(phone_number, PhoneNumber) and not phone_number.is_valid or not phone_number.is_russia:
52
42
  raise ValidationError(
53
- 'Неверный формат! Примеры допустимых форматов: '
54
- '+7 (XXX) XXX-XX-XX, 8 (XXXXX) X-XX-XX, XXX-XX-XX',
43
+ 'Неверный формат! Примеры допустимых форматов: +7 (XXX) XXX-XX-XX, 8 (XXXXX) X-XX-XX, XXX-XX-XX',
55
44
  code='invalid',
56
45
  )
57
46
 
@@ -66,8 +55,7 @@ def validate_ru_e164_phone_number(phone_number: Union[str, PhoneNumber]):
66
55
  or not (phone_number.is_russia and phone_number.is_e164)
67
56
  ):
68
57
  raise ValidationError(
69
- 'Неверный формат! Примеры допустимых форматов: '
70
- '+7 (XXX) XXX-XX-XX, 8 (XXXXX) X-XX-XX, (XXX) XXX-XX-XX',
58
+ 'Неверный формат! Примеры допустимых форматов: +7 (XXX) XXX-XX-XX, 8 (XXXXX) X-XX-XX, (XXX) XXX-XX-XX',
71
59
  code='invalid',
72
60
  )
73
61
 
@@ -80,13 +68,10 @@ def validate_ru_mobile_phone_number(phone_number: Union[str, PhoneNumber]):
80
68
  isinstance(phone_number, PhoneNumber)
81
69
  and not phone_number.is_valid
82
70
  or not (
83
- phone_number.region_code
84
- and len(phone_number.region_code) == 3
85
- and phone_number.region_code.startswith('9')
71
+ phone_number.region_code and len(phone_number.region_code) == 3 and phone_number.region_code.startswith('9')
86
72
  )
87
73
  ):
88
74
  raise ValidationError(
89
- 'Неверный формат! Примеры допустимых форматов: '
90
- '+7 (9XX) XXX-XX-XX, 7 (9XX) XXX XX XX, 89XXXXXXXXX',
75
+ 'Неверный формат! Примеры допустимых форматов: +7 (9XX) XXX-XX-XX, 7 (9XX) XXX XX XX, 89XXXXXXXXX',
91
76
  code='invalid',
92
77
  )
@@ -29,7 +29,6 @@ __all__ = ['extender_for']
29
29
 
30
30
 
31
31
  class _ExtenderRegistry(metaclass=SingletonMeta):
32
-
33
32
  """Реестр расширителей классов.
34
33
 
35
34
  Обеспечивает работу декораторов
@@ -90,13 +89,7 @@ def _function_types():
90
89
  WrapperDescriptorType,
91
90
  )
92
91
 
93
- return (
94
- FunctionType,
95
- MethodType,
96
- WrapperDescriptorType,
97
- MethodWrapperType,
98
- MethodDescriptorType
99
- )
92
+ return (FunctionType, MethodType, WrapperDescriptorType, MethodWrapperType, MethodDescriptorType)
100
93
 
101
94
 
102
95
  _function_types = lru_cache(maxsize=1)(_function_types)
@@ -172,10 +165,8 @@ def _extendable(func):
172
165
  :caption: Пример использования
173
166
 
174
167
  class EditWindow(BaseEditWindow):
175
-
176
168
  @extendable
177
- def set_params(self, params):
178
- ...
169
+ def set_params(self, params): ...
179
170
  """
180
171
  assert isinstance(func, _function_types()), func
181
172
 
@@ -184,12 +175,13 @@ def _extendable(func):
184
175
  from m3_django_compat import (
185
176
  WrapperDescriptorType,
186
177
  )
187
- unbound_types = (FunctionType, WrapperDescriptorType,)
188
178
 
189
- assert all((
190
- isinstance(func, unbound_types),
191
- not isclass(self))
192
- ), 'Нельзя расширить staticmethod и classmethod'
179
+ unbound_types = (
180
+ FunctionType,
181
+ WrapperDescriptorType,
182
+ )
183
+
184
+ assert all((isinstance(func, unbound_types), not isclass(self))), 'Нельзя расширить staticmethod и classmethod'
193
185
 
194
186
  result = func(self, *args, **kwargs)
195
187
 
@@ -222,9 +214,9 @@ def get_plugin_apps():
222
214
  app_config
223
215
  for app_config in apps.get_app_configs()
224
216
  if (
225
- hasattr(app_config.module, 'plugin_meta') and
226
- hasattr(app_config.module.plugin_meta, 'connect_plugin') and
227
- callable(app_config.module.plugin_meta.connect_plugin)
217
+ hasattr(app_config.module, 'plugin_meta')
218
+ and hasattr(app_config.module.plugin_meta, 'connect_plugin')
219
+ and callable(app_config.module.plugin_meta.connect_plugin)
228
220
  )
229
221
  )
230
222
 
@@ -250,20 +242,15 @@ def init_plugin(package, settings=None, config=None):
250
242
  {}, # globals
251
243
  {}, # locals
252
244
  ['connect_plugin'], # from list
253
- 0 # absolute import
245
+ 0, # absolute import
254
246
  )
255
247
  except ImportError:
256
- raise ImproperlyConfigured(
257
- '{} is not plugin application.'.format(package)
258
- )
248
+ raise ImproperlyConfigured('{} is not plugin application.'.format(package))
259
249
 
260
250
  meta_module.__package__ = package
261
251
 
262
- if (not hasattr(meta_module, 'connect_plugin') or
263
- not callable(meta_module.connect_plugin)):
252
+ if not hasattr(meta_module, 'connect_plugin') or not callable(meta_module.connect_plugin):
264
253
  raise ImproperlyConfigured(
265
- '{} is not a plugin application '
266
- '(module {} has not connect_plugin function)'
267
- .format(package, meta_module)
254
+ '{} is not a plugin application (module {} has not connect_plugin function)'.format(package, meta_module)
268
255
  )
269
256
  meta_module.connect_plugin(settings, config or {})
@@ -10,7 +10,8 @@ class ModelHandlerRegistry:
10
10
  assert isinstance(handlers, dict)
11
11
  assert all(map(callable, handlers.values()))
12
12
 
13
- super(ModelHandlerRegistry, self).__init__()
13
+ super().__init__()
14
+
14
15
  self._handlers = handlers
15
16
 
16
17
  def get_model_handler(self, model):
@@ -17,7 +17,5 @@ def make_chunks(
17
17
 
18
18
  for first in iterator:
19
19
  yield (
20
- list(chain([first], islice(iterator, size - 1)))
21
- if is_list
22
- else chain([first], islice(iterator, size - 1))
20
+ list(chain([first], islice(iterator, size - 1))) if is_list else chain([first], islice(iterator, size - 1))
23
21
  )