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
@@ -1,4 +1,5 @@
1
1
  """Инструменты для сериализации моделей."""
2
+
2
3
  from collections import (
3
4
  defaultdict,
4
5
  )
@@ -120,19 +121,15 @@ class SerializeQueue:
120
121
  attname = field.attname
121
122
  if attname.endswith('_id'):
122
123
  attname = attname.replace('_id', '')
123
- attname = '%s__in' % attname
124
+ attname = f'{attname}__in'
124
125
  # добавление фильтра вхождения
125
126
  objects = self._include_conditions.get(rel_model._meta.object_name)
126
127
  if objects:
127
- query_set = query_set.filter(
128
- **{attname: objects}
129
- )
128
+ query_set = query_set.filter(**{attname: objects})
130
129
  # добавление фильтра исключения
131
130
  objects = self._exclude_conditions.get(rel_model._meta.object_name)
132
131
  if objects:
133
- query_set = query_set.exclude(
134
- **{attname: objects}
135
- )
132
+ query_set = query_set.exclude(**{attname: objects})
136
133
 
137
134
  return query_set
138
135
 
@@ -144,14 +141,12 @@ class SerializeQueue:
144
141
  :return: список кортежей вида [(поле, ссылочная модель), ...]
145
142
  """
146
143
  return [
147
- (field, get_related(field).parent_model) for
148
- field in model._meta.fields
149
- if isinstance(field, ForeignKey)
144
+ (field, get_related(field).parent_model) for field in model._meta.fields if isinstance(field, ForeignKey)
150
145
  ]
151
146
 
152
147
  @staticmethod
153
148
  def related_objects(obj):
154
- """ Возвращает список кверисетов зависимых моделей
149
+ """Возвращает список кверисетов зависимых моделей
155
150
 
156
151
  :param obj:
157
152
  :return:
@@ -163,11 +158,9 @@ class SerializeQueue:
163
158
  _attname = rel.field.attname
164
159
  if _attname.endswith('_id'):
165
160
  _attname = _attname.replace('_id', '')
166
- _attname = '%s__pk' % _attname
161
+ _attname = f'{_attname}__pk'
167
162
  # Пытаемся определить наличие зависимых объектов:
168
- rel_qs = rel.field.model.objects.filter(
169
- **{str(_attname): obj.id}
170
- )
163
+ rel_qs = rel.field.model.objects.filter(**{str(_attname): obj.id})
171
164
  if rel_qs.exists():
172
165
  result.append(rel_qs)
173
166
 
@@ -216,6 +209,6 @@ class SerializeQueue:
216
209
 
217
210
  :param filename: имя файла
218
211
  """
219
- data = serializers.serialize("json", self._objects, indent=4)
212
+ data = serializers.serialize('json', self._objects, indent=4)
220
213
  with open(filename, 'wb') as json_out:
221
214
  json_out.write(data)
@@ -68,8 +68,9 @@ class AbstractInstanceDataStorage(AbstractInstanceStorage):
68
68
 
69
69
  :param instance: Объект модели
70
70
  """
71
- if (not self.registry.is_model_has_handler(instance.__class__) or
72
- self._is_instance_already_saved(instance, **kwargs)):
71
+ if not self.registry.is_model_has_handler(instance.__class__) or self._is_instance_already_saved(
72
+ instance, **kwargs
73
+ ):
73
74
  return
74
75
 
75
76
  self._save_instance_data(instance)
educommon/utils/system.py CHANGED
@@ -57,9 +57,7 @@ def get_postgresql_version(connection):
57
57
  """
58
58
  with connection.cursor() as cursor:
59
59
  if cursor.db.vendor != 'postgresql':
60
- raise RuntimeError(
61
- 'Only PostgreSQL RDBMS supported, not ' + cursor.db.vendor
62
- )
60
+ raise RuntimeError(f'Only PostgreSQL RDBMS supported, not {cursor.db.vendor}')
63
61
 
64
62
  return (
65
63
  cursor.db.pg_version // 10000,
@@ -1,4 +1,5 @@
1
1
  """Management-команда для удаления обьектов."""
2
+
2
3
  import sys
3
4
  from collections import (
4
5
  defaultdict,
@@ -39,7 +40,7 @@ def call_custom_command(command_name, *args, **options):
39
40
  try:
40
41
  app_name = get_commands()[command_name]
41
42
  except KeyError:
42
- raise CommandError("Unknown command: %r" % command_name)
43
+ raise CommandError(f'Unknown command: {command_name!r}')
43
44
 
44
45
  if isinstance(app_name, BaseCommand):
45
46
  # If the command is already loaded, use it directly.
@@ -52,16 +53,15 @@ def call_custom_command(command_name, *args, **options):
52
53
  parser = command.create_parser('', command_name)
53
54
  # Use the `dest` option name from the parser option
54
55
  opt_mapping = {
55
- sorted(s_opt.option_strings)[0].lstrip('-').replace(
56
- '-', '_'): s_opt.dest
57
- for s_opt in parser._actions if s_opt.option_strings
56
+ sorted(s_opt.option_strings)[0].lstrip('-').replace('-', '_'): s_opt.dest
57
+ for s_opt in parser._actions
58
+ if s_opt.option_strings
58
59
  }
59
- arg_options = {opt_mapping.get(key, key): value
60
- for key, value in options.items()}
60
+ arg_options = {opt_mapping.get(key, key): value for key, value in options.items()}
61
61
  options, un_options = parser.parse_known_args(args)
62
62
  for opt in un_options:
63
63
  if not isinstance(opt, str):
64
- opt = opt.decode(sys.stdout.encoding)
64
+ opt = str(opt)
65
65
  com = opt.split('=')
66
66
  com = com[0]
67
67
  if '__in' in com:
@@ -92,7 +92,7 @@ class Command(BaseCommand):
92
92
  options, un_options = parser.parse_known_args(argv[2:])
93
93
  for opt in un_options:
94
94
  if not isinstance(opt, str):
95
- opt = opt.decode(sys.stdout.encoding)
95
+ opt = str(opt)
96
96
  com = opt.split('=')
97
97
  com = com[0]
98
98
  if '__in' in com:
@@ -116,21 +116,15 @@ class Command(BaseCommand):
116
116
  if isinstance(e, SystemCheckError):
117
117
  self.stderr.write(str(e), lambda x: x)
118
118
  else:
119
- self.stderr.write('%s: %s' % (e.__class__.__name__, e))
119
+ self.stderr.write(f'{e.__class__.__name__}: {e}')
120
120
  sys.exit(1)
121
121
  finally:
122
122
  connections.close_all()
123
123
 
124
124
  def add_arguments(self, parser):
125
- parser.add_argument(
126
- '--count', action='count', help='Количество удаляемых обьектов'
127
- )
128
- parser.add_argument(
129
- '--model', type=str, required=True, help='Модель для удаления'
130
- )
131
- parser.add_argument(
132
- '--data', action='count', help='Вернуть json удаляемых обьектов'
133
- )
125
+ parser.add_argument('--count', action='count', help='Количество удаляемых обьектов')
126
+ parser.add_argument('--model', type=str, required=True, help='Модель для удаления')
127
+ parser.add_argument('--data', action='count', help='Вернуть json удаляемых обьектов')
134
128
 
135
129
  @staticmethod
136
130
  def find_filter(options, ignore_opt=()):
@@ -177,10 +171,7 @@ class Command(BaseCommand):
177
171
  try:
178
172
  model = apps.get_model(app_name, model_name)
179
173
  except LookupError:
180
- message = (
181
- 'Модели {} в приложении '
182
- '{} не найдено.'.format(model_name, app_name)
183
- )
174
+ message = 'Модели {} в приложении {} не найдено.'.format(model_name, app_name)
184
175
  return model, message
185
176
  for app_config in apps.get_app_configs():
186
177
  try:
@@ -191,10 +182,7 @@ class Command(BaseCommand):
191
182
 
192
183
  if len(models) > 1:
193
184
  model = None
194
- message = (
195
- 'Невозможно однозначно определить модель, '
196
- 'найдены похожие модели: \n'
197
- )
185
+ message = 'Невозможно однозначно определить модель, найдены похожие модели: \n'
198
186
  message += '\n'.join(str(model._meta) for model in models)
199
187
  elif not model:
200
188
  message = 'Модели {} не найдено.'.format(model_name)
@@ -206,9 +194,7 @@ class Command(BaseCommand):
206
194
  for del_obj in deleted_objects:
207
195
  for model, model_objects in del_obj.get_related_objects():
208
196
  for o in model_objects:
209
- uniq_dict[
210
- model.__name__ + ' ' + model._meta.verbose_name
211
- ].add(o)
197
+ uniq_dict[model.__name__ + ' ' + model._meta.verbose_name].add(o)
212
198
  for model, objs in uniq_dict.items():
213
199
  display_mes = '{} {}'.format(model, len(objs))
214
200
  self.stdout.write(display_mes)
@@ -220,8 +206,7 @@ class Command(BaseCommand):
220
206
  for rel_model, rel_objs in instance.get_related_objects():
221
207
  for obj in rel_objs:
222
208
  objs.append(obj)
223
- data = serializers.serialize(
224
- 'json', objs)
209
+ data = serializers.serialize('json', objs)
225
210
  self.stdout.write(data)
226
211
 
227
212
  def handle(self, *args, **options):
@@ -234,9 +219,7 @@ class Command(BaseCommand):
234
219
  if message:
235
220
  raise CommandError(message)
236
221
  # опции которые будут игнорироваться в фильтре
237
- ignore_opt = (
238
- 'count', 'model', 'data', 'verbosity', 'stdout', 'skip_checks'
239
- )
222
+ ignore_opt = ('count', 'model', 'data', 'verbosity', 'stdout', 'skip_checks')
240
223
  lookup = self.find_filter(options, ignore_opt)
241
224
  deleted_objects = model.objects.filter(**lookup)
242
225
 
educommon/utils/ui.py CHANGED
@@ -1,10 +1,20 @@
1
1
  """Утилиты для работы с элементами управления (интерфейсами)."""
2
+ from __future__ import (
3
+ annotations,
4
+ )
5
+
2
6
  import inspect
3
7
  import os
4
8
  from datetime import (
9
+ date,
5
10
  datetime,
6
11
  time,
7
12
  )
13
+ from typing import (
14
+ TYPE_CHECKING,
15
+ Callable,
16
+ Type,
17
+ )
8
18
 
9
19
  from django.conf import (
10
20
  settings,
@@ -58,26 +68,29 @@ from educommon.utils.misc import (
58
68
  )
59
69
 
60
70
 
71
+ if TYPE_CHECKING:
72
+ from django.db.models import (
73
+ Model,
74
+ )
75
+
76
+ from objectpack.filters import (
77
+ FilterGroup,
78
+ )
79
+
80
+
61
81
  def anchor100(*elements):
62
82
  """Установка anchor='100%' для перечня компонент."""
63
- return list(
64
- obj_anchor100(element)
65
- for element in elements
66
- )
83
+ return list(obj_anchor100(element) for element in elements)
67
84
 
68
85
 
69
86
  def deny_blank(*elements):
70
87
  """Установка allow_blank=False для перечня компонент."""
71
- return list(
72
- obj_deny_blank(element)
73
- for element in elements
74
- )
88
+ return list(obj_deny_blank(element) for element in elements)
75
89
 
76
90
 
77
91
  def make_button(title, icon_cls, event, client_id):
78
92
  """Создает кнопку, оповещающую компонент с client_id на событие event."""
79
- handler = "function() {Ext.getCmp('%s').fireEvent('%s');}" % (
80
- client_id, event)
93
+ handler = f'function() {{Ext.getCmp("{client_id}").fireEvent("{event}");}}'
81
94
 
82
95
  return ext.ExtButton(text=title, icon_cls=icon_cls, handler=handler)
83
96
 
@@ -107,7 +120,7 @@ class ChoicesFilter(CustomFilter):
107
120
  self._choices = choices
108
121
  kwargs['xtype'] = 'combo'
109
122
 
110
- super(ChoicesFilter, self).__init__(*args, **kwargs)
123
+ super().__init__(*args, **kwargs)
111
124
 
112
125
  def get_script(self):
113
126
  """Генерация кода компонента."""
@@ -126,12 +139,8 @@ class ChoicesFilter(CustomFilter):
126
139
 
127
140
 
128
141
  class ColumnFilterWithDefaultValue(_FilterByField):
129
-
130
142
  def get_script(self):
131
- control = _create_control_for_field(
132
- self.field,
133
- **self._field_fabric_params
134
- )
143
+ control = _create_control_for_field(self.field, **self._field_fabric_params)
135
144
  control._put_config_value('filterName', self._uid)
136
145
  control._put_config_value('tooltip', self._tooltip or control.label)
137
146
  control.name = self._uid
@@ -142,8 +151,7 @@ class ColumnFilterWithDefaultValue(_FilterByField):
142
151
  return [control.render()]
143
152
 
144
153
 
145
- def reconfigure_grid_by_access(grid, can_add=False, can_edit=False,
146
- can_delete=False, can_view=True):
154
+ def reconfigure_grid_by_access(grid, can_add=False, can_edit=False, can_delete=False, can_view=True):
147
155
  """Перенастраивает грид в зависимости от прав доступа.
148
156
 
149
157
  :param grid: Перенастраиваемый грид.
@@ -172,8 +180,7 @@ def reconfigure_grid_by_access(grid, can_add=False, can_edit=False,
172
180
  grid.url_delete = None
173
181
 
174
182
 
175
- def reconfigure_object_tree_by_access(grid, can_add=False, can_edit=False,
176
- can_delete=False, can_view=True):
183
+ def reconfigure_object_tree_by_access(grid, can_add=False, can_edit=False, can_delete=False, can_view=True):
177
184
  """Перенастраивает древовидный грид в зависимости от прав доступа.
178
185
 
179
186
  :param grid: Перенастраиваемый грид.
@@ -212,16 +219,14 @@ class FilterByField(_FilterByField):
212
219
  if parser:
213
220
  self.parsers_map = list(self.parsers_map)
214
221
  self.parsers_map.append(parser)
215
- super(FilterByField, self).__init__(*args, **kwargs)
222
+
223
+ super().__init__(*args, **kwargs)
216
224
 
217
225
  def create_control(self):
218
226
  if self._control_creator is not None:
219
227
  return self._control_creator()
220
228
 
221
- return _create_control_for_field(
222
- self.field,
223
- **self._field_fabric_params
224
- )
229
+ return _create_control_for_field(self.field, **self._field_fabric_params)
225
230
 
226
231
  def get_control(self):
227
232
  control = self.create_control()
@@ -243,75 +248,84 @@ class DatetimeFilterCreator:
243
248
  Поддерживает значения по умолчанию.
244
249
  """
245
250
 
246
- def __init__(self, model, field_name,
247
- get_from=lambda: None, get_to=lambda: None):
251
+ def __init__(
252
+ self,
253
+ model: Type[Model],
254
+ field_name: str,
255
+ get_from: Callable[[], None | date] = lambda: None,
256
+ get_to: Callable[[], None | date] = lambda: None,
257
+ min_value: Callable[[], None | date] = lambda: None,
258
+ max_value: Callable[[], None | date] = lambda: None,
259
+ ) -> None:
248
260
  """Фильтр по интервалу для datetime поля.
249
261
 
250
- :param django.db.models.Model model: модель для фильтра.
251
- :param str field_name: имя поля модели.
252
- :param callable get_from: возвращает дату по умолчанию для фильтра "С".
253
- :param callable get_to: возвращает дату по умолчанию для фильтра "По".
262
+ Args:
263
+ model: Модель для фильтра.
264
+ field_name: Имя поля модели.
265
+ get_from: Дата по умолчанию для фильтра "С".
266
+ get_to: Дата по умолчанию для фильтра "По".
267
+ min_value: Минимально возможная дата.
268
+ max_value: Максимально возможная дата.
254
269
 
255
270
  Значения по умолчанию передаются в качестве callable, чтобы они
256
271
  вычислялись во время создания js. То есть, если в фильтре должна быть
257
272
  текущая дата, а пак с колонками был создан вчера, пользователь увидит
258
273
  в фильтре сегодняшнюю дату, а не вчерашнюю.
259
274
  """
275
+
260
276
  self.model = model
261
277
  self.field_name = field_name
262
278
 
263
279
  assert callable(get_from)
264
280
  assert callable(get_to)
281
+ assert callable(min_value)
282
+ assert callable(max_value)
283
+
265
284
  self.defaults = {
266
285
  'from': get_from,
267
286
  'to': get_to,
287
+ 'min': min_value if min_value() else lambda: date(1, 1, 1),
288
+ 'max': max_value,
268
289
  }
269
290
 
270
291
  @cached_property
271
- def filter(self):
292
+ def filter(self) -> FilterGroup:
272
293
  """Фильтр для колонки.
273
294
 
274
- :return Группа колоночных фильтров для грида
275
- :rtype objectpack.filters.FilterGroup
295
+ Returns:
296
+ Группа колоночных фильтров для грида.
276
297
  """
298
+
277
299
  observer = ioc.get('observer')
278
300
 
279
- return (
280
- FilterByField(
281
- self.model,
282
- model_register=observer,
283
- control_creator=lambda: ext.ExtDateField(
284
- value=self.defaults['from'](),
285
- ),
286
- field_name=self.field_name,
287
- lookup=lambda dt: Q(**{
288
- self.field_name + '__gte': datetime.combine(dt, time(0))
289
- }),
290
- tooltip='С',
291
- ) & FilterByField(
292
- self.model,
293
- model_register=observer,
294
- control_creator=lambda: ext.ExtDateField(
295
- value=self.defaults['to'](),
296
- ),
297
- field_name=self.field_name,
298
- lookup=lambda dt: Q(**{
299
- self.field_name + '__lte': datetime.combine(
300
- dt, time(
301
- hour=23, minute=59, second=59, microsecond=999999
302
- )
303
- )
304
- }),
305
- tooltip='По',
306
- )
301
+ return FilterByField(
302
+ self.model,
303
+ model_register=observer,
304
+ control_creator=lambda: ext.ExtDateField(
305
+ value=self.defaults['from'](),
306
+ min_value=self.defaults['min'](),
307
+ max_value=self.defaults['max'](),
308
+ ),
309
+ field_name=self.field_name,
310
+ lookup=lambda dt: Q(**{f'{self.field_name}__gte': datetime.combine(dt, time.min)}),
311
+ tooltip='С',
312
+ ) & FilterByField(
313
+ self.model,
314
+ model_register=observer,
315
+ control_creator=lambda: ext.ExtDateField(
316
+ value=self.defaults['to'](),
317
+ min_value=self.defaults['min'](),
318
+ max_value=self.defaults['max'](),
319
+ ),
320
+ field_name=self.field_name,
321
+ lookup=lambda dt: Q(**{f'{self.field_name}__lte': datetime.combine(dt, time.max)}),
322
+ tooltip='По',
307
323
  )
308
324
 
309
325
  @property
310
- def base_params(self):
311
- """Базовые параметры для store грида.
326
+ def base_params(self) -> dict[str, str]:
327
+ """Базовые параметры для store грида."""
312
328
 
313
- :rtype: dict
314
- """
315
329
  result = {}
316
330
 
317
331
  value = self.defaults['from']()
@@ -321,6 +335,7 @@ class DatetimeFilterCreator:
321
335
  value = self.defaults['to']()
322
336
  if value is not None:
323
337
  result[self.filter._items[1]._uid] = str(value)
338
+
324
339
  return result
325
340
 
326
341
 
@@ -335,7 +350,7 @@ def switch_window_in_read_only_mode(window):
335
350
  assert isinstance(window, BaseEditWindow), type(window)
336
351
 
337
352
  if window.title.endswith(': Редактирование'):
338
- window.title = window.title[:-len('Редактирование')] + 'Просмотр'
353
+ window.title = window.title[: -len('Редактирование')] + 'Просмотр'
339
354
 
340
355
  window.buttons.remove(window.save_btn)
341
356
  window.cancel_btn.text = 'Закрыть'
@@ -358,16 +373,9 @@ def local_template(file_name):
358
373
  root_package_name = frame.f_globals['__name__'].rsplit('.', 2)[0]
359
374
  module = __import__(root_package_name)
360
375
 
361
- TEMPLATE_DIRS = set(
362
- path
363
- for config in settings.TEMPLATES
364
- for path in config.get('DIRS', ())
365
- )
376
+ TEMPLATE_DIRS = set(path for config in settings.TEMPLATES for path in config.get('DIRS', ()))
366
377
 
367
- assert any(
368
- os.path.dirname(path) in TEMPLATE_DIRS
369
- for path in module.__path__
370
- ), (
378
+ assert any(os.path.dirname(path) in TEMPLATE_DIRS for path in module.__path__), (
371
379
  '{} package path must be in TEMPLATES config.'.format(module.__path__),
372
380
  TEMPLATE_DIRS,
373
381
  )
@@ -377,14 +385,13 @@ def local_template(file_name):
377
385
 
378
386
  for path in TEMPLATE_DIRS:
379
387
  if module_path.startswith(path):
380
- module_path = module_path[len(path) + 1:]
388
+ module_path = module_path[len(path) + 1 :]
381
389
  break
382
390
 
383
391
  return os.path.join(module_path, file_name)
384
392
 
385
393
 
386
394
  class FilterByTextField(FilterByField):
387
-
388
395
  """Фильтр для поля TextField, с ExtStringField в качестве контрола.
389
396
 
390
397
  Возвращает контрол однострочного текстового поля
@@ -396,10 +403,7 @@ class FilterByTextField(FilterByField):
396
403
  # Работаем только с TextField
397
404
  assert isinstance(self.field, TextField)
398
405
 
399
- return ext.ExtStringField(
400
- max_length=self.field.max_length,
401
- **self._field_fabric_params
402
- )
406
+ return ext.ExtStringField(max_length=self.field.max_length, **self._field_fabric_params)
403
407
 
404
408
 
405
409
  def append_template_globals(comp, template):
@@ -417,8 +421,7 @@ def append_template_globals(comp, template):
417
421
  :type template: str or unicode
418
422
  """
419
423
  if not isinstance(comp, BaseExtComponent):
420
- raise ApplicationLogicException(
421
- 'Component has no attribute template_globals')
424
+ raise ApplicationLogicException('Component has no attribute template_globals')
422
425
  if isinstance(comp.template_globals, str):
423
426
  # если template_globals - пустая строка, просто заменяем ее
424
427
  if len(comp.template_globals) == 0:
@@ -62,9 +62,7 @@ def parse_xml(xml):
62
62
 
63
63
 
64
64
  def get_text(elements):
65
- """Возвращает текст первого элемента найденного
66
- с помощью make_xpath_query
67
- """
65
+ """Возвращает текст первого элемента найденного с помощью make_xpath_query."""
68
66
  return elements[0].text if elements else ''
69
67
 
70
68
 
@@ -74,9 +72,6 @@ def make_xpath_query(*tags):
74
72
  :param tags: Имена тэгов XML-документа в порядке иерархии (без учета
75
73
  пространств имен).
76
74
  """
77
- result = '/' + ''.join(
78
- "/*[local-name()='{}']".format(tag)
79
- for tag in tags
80
- )
75
+ result = '/' + ''.join("/*[local-name()='{}']".format(tag) for tag in tags)
81
76
 
82
77
  return result
@@ -29,6 +29,7 @@ class Resolver(etree.Resolver):
29
29
  .. code-block:: python
30
30
 
31
31
  from lxml import etree
32
+
32
33
  parser = etree.XMLParser(load_dtd=True)
33
34
  parser.resolvers.add(Resolver())
34
35
  """