educommon 3.16.0__py3-none-any.whl → 3.18.0__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.
- educommon/audit_log/management/commands/audit_log_migrate_data.py +357 -0
- educommon/audit_log/migrations/0010_alter_auditlog_time.py +24 -0
- educommon/audit_log/models.py +4 -1
- educommon/audit_log/utils/__init__.py +32 -0
- {educommon-3.16.0.dist-info → educommon-3.18.0.dist-info}/METADATA +1 -1
- {educommon-3.16.0.dist-info → educommon-3.18.0.dist-info}/RECORD +8 -6
- {educommon-3.16.0.dist-info → educommon-3.18.0.dist-info}/WHEEL +0 -0
- {educommon-3.16.0.dist-info → educommon-3.18.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,357 @@
|
|
1
|
+
"""
|
2
|
+
Команда переноса данных из локального AuditLog'а в educommon'овский.
|
3
|
+
"""
|
4
|
+
import sys
|
5
|
+
from datetime import (
|
6
|
+
datetime as dt,
|
7
|
+
)
|
8
|
+
from typing import (
|
9
|
+
TYPE_CHECKING,
|
10
|
+
Generator,
|
11
|
+
Optional,
|
12
|
+
Type,
|
13
|
+
)
|
14
|
+
|
15
|
+
from dateutil.relativedelta import (
|
16
|
+
relativedelta,
|
17
|
+
)
|
18
|
+
from django.apps import (
|
19
|
+
apps,
|
20
|
+
)
|
21
|
+
from django.core.management.base import (
|
22
|
+
BaseCommand,
|
23
|
+
OutputWrapper,
|
24
|
+
)
|
25
|
+
from django.db.models import (
|
26
|
+
ManyToManyField,
|
27
|
+
Model,
|
28
|
+
)
|
29
|
+
|
30
|
+
from educommon.audit_log.models import (
|
31
|
+
AuditLog,
|
32
|
+
Table,
|
33
|
+
)
|
34
|
+
from educommon.audit_log.utils import (
|
35
|
+
get_model_by_table,
|
36
|
+
)
|
37
|
+
|
38
|
+
|
39
|
+
if TYPE_CHECKING:
|
40
|
+
from django.db.models.query import (
|
41
|
+
QuerySet,
|
42
|
+
)
|
43
|
+
|
44
|
+
|
45
|
+
PROJECT_LOCAL_AUDIT_LOG_APP = {
|
46
|
+
'eduschl': 'web_edu_audit_log',
|
47
|
+
'edussuz': 'audit_log_ssuz',
|
48
|
+
'edukndg': 'audit_log_kndg'
|
49
|
+
}
|
50
|
+
LOG_OPERATION_MAP = {
|
51
|
+
'N': AuditLog.OPERATION_CREATE,
|
52
|
+
'I': AuditLog.OPERATION_CREATE,
|
53
|
+
'U': AuditLog.OPERATION_UPDATE,
|
54
|
+
'D': AuditLog.OPERATION_DELETE,
|
55
|
+
}
|
56
|
+
LOCAL_AUDIT_LOG_MODEL_NAME = 'Log'
|
57
|
+
DEFAULT_DATE_YEAR_RANGE = 1
|
58
|
+
|
59
|
+
|
60
|
+
class BulkSaver:
|
61
|
+
"""Контекстный менеджер для группового сохранения записей."""
|
62
|
+
|
63
|
+
def __init__(self, bulk_size: int) -> None:
|
64
|
+
self._bulk_size = bulk_size
|
65
|
+
self._bulk_list = []
|
66
|
+
|
67
|
+
def __enter__(self):
|
68
|
+
return self
|
69
|
+
|
70
|
+
def __exit__(self, *exc):
|
71
|
+
self._bulk_save()
|
72
|
+
|
73
|
+
def save(self, audit_log: AuditLog):
|
74
|
+
self._bulk_list.append(audit_log)
|
75
|
+
if len(self._bulk_list) >= self._bulk_size:
|
76
|
+
self._bulk_save()
|
77
|
+
|
78
|
+
def _bulk_save(self):
|
79
|
+
AuditLog.objects.bulk_create(self._bulk_list)
|
80
|
+
self._bulk_list.clear()
|
81
|
+
|
82
|
+
|
83
|
+
class LogMigrator:
|
84
|
+
"""Класс для миграции логов из локального AuditLog'а в educommon'овский."""
|
85
|
+
|
86
|
+
def __init__(self, stdout: OutputWrapper):
|
87
|
+
"""Инициализация."""
|
88
|
+
self._stdout = stdout
|
89
|
+
|
90
|
+
@staticmethod
|
91
|
+
def get_model(model_name: str) -> Optional[Type[Model]]:
|
92
|
+
"""Bозвращает класс модели по имени."""
|
93
|
+
for mod in apps.get_models():
|
94
|
+
if mod.__name__ == model_name and mod.__module__.find('django') == -1:
|
95
|
+
return mod
|
96
|
+
|
97
|
+
return None
|
98
|
+
|
99
|
+
def _get_educommon_table(self, model_name: str, create: bool = False) -> tuple[Optional[Table], str]:
|
100
|
+
"""Возвращает таблицу, отслеживаемую системой аудита."""
|
101
|
+
model_cls = self.get_model(model_name)
|
102
|
+
|
103
|
+
if not model_cls:
|
104
|
+
return None, f'Не найдено Django моделей с именем {model_name}'
|
105
|
+
try:
|
106
|
+
return Table.objects.get(name=model_cls._meta.db_table), ''
|
107
|
+
except Table.DoesNotExist:
|
108
|
+
is_loggable_mixin_use = getattr(model_cls, 'need_to_log', False)
|
109
|
+
|
110
|
+
if not is_loggable_mixin_use:
|
111
|
+
return None, (
|
112
|
+
f'В таблицу {model_name} не добавлен LoggableModelMixin'
|
113
|
+
)
|
114
|
+
|
115
|
+
if create:
|
116
|
+
return Table.objects.create(
|
117
|
+
name=model_cls._meta.db_table,
|
118
|
+
schema='public',
|
119
|
+
logged=True,
|
120
|
+
), ''
|
121
|
+
|
122
|
+
return None, (
|
123
|
+
f'В educommon не найдено таблицы с именем {model_cls._meta.db_table}. '
|
124
|
+
'Таблицу можно создать, указав флаг force_create_educommon_table'
|
125
|
+
)
|
126
|
+
|
127
|
+
@staticmethod
|
128
|
+
def _get_fields_map(table: Table) -> dict[str, str]:
|
129
|
+
"""Возвращает словарь соответствия поля модели и его названия."""
|
130
|
+
return {
|
131
|
+
field.name: field.attname
|
132
|
+
for field in get_model_by_table(table)._meta.get_fields()
|
133
|
+
if field.concrete and not isinstance(field, ManyToManyField)
|
134
|
+
}
|
135
|
+
|
136
|
+
def _get_local_audit_log_query(
|
137
|
+
self,
|
138
|
+
project: str,
|
139
|
+
model_name: str,
|
140
|
+
date_from: dt,
|
141
|
+
date_to: dt
|
142
|
+
) -> tuple[Optional['QuerySet'], str]:
|
143
|
+
"""Возвращает QuerySet с локальными логами."""
|
144
|
+
if not (audit_log_app_name := PROJECT_LOCAL_AUDIT_LOG_APP.get(project)):
|
145
|
+
return None, 'Неизвестное название проекта.'
|
146
|
+
|
147
|
+
audit_log_model = apps.get_model(audit_log_app_name, LOCAL_AUDIT_LOG_MODEL_NAME)
|
148
|
+
|
149
|
+
if not audit_log_model:
|
150
|
+
return None, 'Не найдена локальная модель логов.'
|
151
|
+
|
152
|
+
return audit_log_model.objects.filter(
|
153
|
+
model=model_name,
|
154
|
+
date__lt=date_to,
|
155
|
+
date__gte=date_from,
|
156
|
+
), ''
|
157
|
+
|
158
|
+
@staticmethod
|
159
|
+
def _prepare_local_logs(query: 'QuerySet') -> Generator[tuple, None, None]:
|
160
|
+
"""Подготавливает данные логов для дальнейшего использования."""
|
161
|
+
query = query.values_list(
|
162
|
+
'user_id',
|
163
|
+
'date',
|
164
|
+
'model_id',
|
165
|
+
'object_json',
|
166
|
+
'ip',
|
167
|
+
'operation',
|
168
|
+
).order_by(
|
169
|
+
'date',
|
170
|
+
)
|
171
|
+
for user_id, date_, model_id, object_json, ip, operation in query.iterator():
|
172
|
+
yield user_id, date_, model_id, ip, operation, object_json
|
173
|
+
|
174
|
+
@staticmethod
|
175
|
+
def _prepare_object_dict(fields_map: dict, object_dict: Optional[dict]) -> dict[str, str]:
|
176
|
+
"""Подготавливает данные о объекте для AuditLog.
|
177
|
+
|
178
|
+
В основном требуется только для переименования ForeignKey-полей вида
|
179
|
+
`period` в `period_id`
|
180
|
+
"""
|
181
|
+
if not object_dict:
|
182
|
+
return {}
|
183
|
+
|
184
|
+
filled_field_names = set(fields_map).intersection(object_dict)
|
185
|
+
return {
|
186
|
+
fields_map[field_name]: object_dict[field_name]
|
187
|
+
for field_name in filled_field_names
|
188
|
+
}
|
189
|
+
|
190
|
+
def process(
|
191
|
+
self,
|
192
|
+
model_name: str,
|
193
|
+
project: str,
|
194
|
+
bulk_save_size: int,
|
195
|
+
date_from: Optional[dt] = None,
|
196
|
+
date_to: Optional[dt] = None,
|
197
|
+
force_create_educommon_table: bool = False,
|
198
|
+
):
|
199
|
+
"""Перенос записей из локального AuditLog'а в educommon."""
|
200
|
+
table, error = self._get_educommon_table(model_name, force_create_educommon_table)
|
201
|
+
if error:
|
202
|
+
self._stdout.write(error)
|
203
|
+
return
|
204
|
+
|
205
|
+
fields_map = self._get_fields_map(table)
|
206
|
+
|
207
|
+
first_audit_log_date = self._get_first_audit_log_date(table)
|
208
|
+
date_to = dt.combine((date_to or dt.today()), dt.max.time())
|
209
|
+
|
210
|
+
date_to = min(date_to, first_audit_log_date)
|
211
|
+
date_from = date_from or (date_to - relativedelta(years=DEFAULT_DATE_YEAR_RANGE))
|
212
|
+
|
213
|
+
self._stdout.write(
|
214
|
+
f'Поиск записей в локальном Auditlog с {date_from} по {date_to}... ',
|
215
|
+
ending='',
|
216
|
+
)
|
217
|
+
|
218
|
+
local_audit_log_query, error = self._get_local_audit_log_query(
|
219
|
+
project=project,
|
220
|
+
model_name=model_name,
|
221
|
+
date_from=date_from,
|
222
|
+
date_to=date_to,
|
223
|
+
)
|
224
|
+
if error:
|
225
|
+
self._stdout.write(error)
|
226
|
+
return
|
227
|
+
|
228
|
+
total_count = local_audit_log_query.count()
|
229
|
+
self._stdout.write(f'Найдено {total_count} запись(ей).')
|
230
|
+
|
231
|
+
local_logs = self._prepare_local_logs(local_audit_log_query)
|
232
|
+
self._stdout.write(f'Подготовка к работе... (занимает некоторое время)', ending='\r',)
|
233
|
+
with BulkSaver(bulk_save_size) as bulk:
|
234
|
+
for count, (user_id, date_, object_id, ip, operation, data_dict) in enumerate(local_logs, start=1):
|
235
|
+
object_dict = self._prepare_object_dict(fields_map, data_dict[0].get('fields', {}))
|
236
|
+
|
237
|
+
operation = LOG_OPERATION_MAP[operation]
|
238
|
+
|
239
|
+
if operation == AuditLog.OPERATION_UPDATE:
|
240
|
+
changes = object_dict
|
241
|
+
else:
|
242
|
+
# Если объект быз создан или удалён, то изменений нет.
|
243
|
+
changes = {}
|
244
|
+
|
245
|
+
bulk.save(AuditLog(
|
246
|
+
user_id=user_id,
|
247
|
+
ip=ip,
|
248
|
+
time=date_,
|
249
|
+
table_id=table.id,
|
250
|
+
data=object_dict,
|
251
|
+
changes=changes,
|
252
|
+
object_id=object_id,
|
253
|
+
operation=operation,
|
254
|
+
))
|
255
|
+
|
256
|
+
self._stdout.write(
|
257
|
+
f'Обработано {(count / total_count) * 100:5.2f}% запись(ей)... (Последняя от {date_})',
|
258
|
+
ending='\r',
|
259
|
+
)
|
260
|
+
|
261
|
+
def _get_first_audit_log_date(self, table: str) -> dt:
|
262
|
+
"""Возвращает первую дату/время появления audit_log по переданной таблице.
|
263
|
+
|
264
|
+
В случае отсуствия логов прекращает выполнение команды за отсутствием данных для переноса.
|
265
|
+
"""
|
266
|
+
first_log = AuditLog.objects.filter(
|
267
|
+
table=table,
|
268
|
+
).order_by('time').first()
|
269
|
+
|
270
|
+
if not first_log:
|
271
|
+
self._stdout.write('Не найдены локальные логи переданной модели. Завершение команды.')
|
272
|
+
sys.exit()
|
273
|
+
|
274
|
+
return first_log.time
|
275
|
+
|
276
|
+
|
277
|
+
class Command(BaseCommand):
|
278
|
+
"""Команда переноса данных из локального AuditLog'а в educommon'овский."""
|
279
|
+
|
280
|
+
help = (
|
281
|
+
'Команда для переноса данных из локального AuditLog`а в educommon.audit_log.models.AuditLog.\n'
|
282
|
+
'Пример использования:\n'
|
283
|
+
'audit_log_migrate_data --project eduschl --model_name Mark --date_from 10.10.2024'
|
284
|
+
)
|
285
|
+
|
286
|
+
@staticmethod
|
287
|
+
def _get_date(date_string: str) -> dt:
|
288
|
+
return dt.strptime(date_string, '%d.%m.%Y')
|
289
|
+
|
290
|
+
def add_arguments(self, parser):
|
291
|
+
parser.add_argument(
|
292
|
+
'--model_name',
|
293
|
+
type=str,
|
294
|
+
help='Название модели из локального лога',
|
295
|
+
)
|
296
|
+
parser.add_argument(
|
297
|
+
'--project',
|
298
|
+
type=str,
|
299
|
+
help='Наименование (код) продукта, в котором применяется команда',
|
300
|
+
)
|
301
|
+
parser.add_argument(
|
302
|
+
'--date_from',
|
303
|
+
type=self._get_date,
|
304
|
+
required=False,
|
305
|
+
help=(
|
306
|
+
'Дата, с которой будут переноситься логи в формате ДД.ММ.ГГГГ. '
|
307
|
+
'Значение по умолчанию - на год раньше даты, указанной в --date_to'
|
308
|
+
),
|
309
|
+
)
|
310
|
+
parser.add_argument(
|
311
|
+
'--date_to',
|
312
|
+
type=self._get_date,
|
313
|
+
required=False,
|
314
|
+
help=(
|
315
|
+
'Дата, по которую будут переноситься логи в формате ДД.ММ.ГГГГ.'
|
316
|
+
'Значение по умолчанию - текущая дата.'
|
317
|
+
),
|
318
|
+
)
|
319
|
+
parser.add_argument(
|
320
|
+
'--force_create_educommon_table',
|
321
|
+
action='store_true',
|
322
|
+
default=False,
|
323
|
+
help=(
|
324
|
+
'Создание отслеживаемой таблицы (educommon.audit_log.models.Table), '
|
325
|
+
'если она еще не была создана'
|
326
|
+
),
|
327
|
+
)
|
328
|
+
parser.add_argument(
|
329
|
+
'--bulk_save_size',
|
330
|
+
type=int,
|
331
|
+
default=500,
|
332
|
+
help='По сколько записей за раз будет сохраняться. По умолчанию 500',
|
333
|
+
)
|
334
|
+
|
335
|
+
def handle(
|
336
|
+
self,
|
337
|
+
model_name: str,
|
338
|
+
project: str,
|
339
|
+
date_from: Optional[dt],
|
340
|
+
date_to: Optional[dt],
|
341
|
+
force_create_educommon_table: bool,
|
342
|
+
bulk_save_size: int,
|
343
|
+
*args,
|
344
|
+
**options,
|
345
|
+
):
|
346
|
+
"""Выполнение переноса записей."""
|
347
|
+
self.stdout.write('Начало работы команды.')
|
348
|
+
LogMigrator(self.stdout).process(
|
349
|
+
model_name=model_name,
|
350
|
+
project=project,
|
351
|
+
date_from=date_from,
|
352
|
+
date_to=date_to,
|
353
|
+
force_create_educommon_table=force_create_educommon_table,
|
354
|
+
bulk_save_size=bulk_save_size,
|
355
|
+
)
|
356
|
+
|
357
|
+
self.stdout.write(self.style.SUCCESS('Выполнение команды завершено.'))
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Generated by Django 3.2.24 on 2025-05-16 12:04
|
2
|
+
|
3
|
+
from django.db import (
|
4
|
+
migrations,
|
5
|
+
models,
|
6
|
+
)
|
7
|
+
from django.utils import (
|
8
|
+
timezone,
|
9
|
+
)
|
10
|
+
|
11
|
+
|
12
|
+
class Migration(migrations.Migration):
|
13
|
+
|
14
|
+
dependencies = [
|
15
|
+
('audit_log', '0009_reinstall_audit_log'),
|
16
|
+
]
|
17
|
+
|
18
|
+
operations = [
|
19
|
+
migrations.AlterField(
|
20
|
+
model_name='auditlog',
|
21
|
+
name='time',
|
22
|
+
field=models.DateTimeField(db_index=True, default=timezone.now, verbose_name='Дата, время'),
|
23
|
+
),
|
24
|
+
]
|
educommon/audit_log/models.py
CHANGED
@@ -41,6 +41,9 @@ from django.db.backends.signals import (
|
|
41
41
|
from django.dispatch.dispatcher import (
|
42
42
|
receiver,
|
43
43
|
)
|
44
|
+
from django.utils import (
|
45
|
+
timezone,
|
46
|
+
)
|
44
47
|
|
45
48
|
from educommon.audit_log.utils import (
|
46
49
|
get_audit_log_context,
|
@@ -115,7 +118,7 @@ class AuditLog(ReadOnlyMixin, BaseModel):
|
|
115
118
|
verbose_name='Тип пользователя',
|
116
119
|
)
|
117
120
|
ip = models.GenericIPAddressField(null=True, verbose_name='IP адрес')
|
118
|
-
time = models.DateTimeField(
|
121
|
+
time = models.DateTimeField(default=timezone.now, db_index=True, verbose_name='Дата, время')
|
119
122
|
table = models.ForeignKey(
|
120
123
|
Table,
|
121
124
|
verbose_name='Таблица',
|
@@ -40,6 +40,7 @@ from django.db.transaction import (
|
|
40
40
|
from django.http import (
|
41
41
|
HttpRequest,
|
42
42
|
)
|
43
|
+
|
43
44
|
from m3_django_compat import (
|
44
45
|
get_related,
|
45
46
|
)
|
@@ -52,6 +53,9 @@ from educommon.audit_log.constants import (
|
|
52
53
|
PG_LOCK_ID,
|
53
54
|
SQL_FILES_DIR,
|
54
55
|
)
|
56
|
+
from educommon.logger import (
|
57
|
+
error as logger_error,
|
58
|
+
)
|
55
59
|
from educommon.utils.misc import (
|
56
60
|
cached_property,
|
57
61
|
)
|
@@ -99,6 +103,19 @@ def get_need_to_log_table_names() -> Set[str]:
|
|
99
103
|
return table_names
|
100
104
|
|
101
105
|
|
106
|
+
def get_table_names_for_app_labels(app_labels: Iterable[str]) -> Set[str]:
|
107
|
+
"""Возвращает множество с именами таблиц для указанных приложений."""
|
108
|
+
tables = set()
|
109
|
+
for app_label in app_labels:
|
110
|
+
try:
|
111
|
+
app_config = apps.get_app_config(app_label)
|
112
|
+
tables.update(model._meta.db_table for model in app_config.get_models())
|
113
|
+
except LookupError:
|
114
|
+
continue
|
115
|
+
|
116
|
+
return tables
|
117
|
+
|
118
|
+
|
102
119
|
def update_or_create_tables(need_to_log_table_names: Iterable[str]) -> bool:
|
103
120
|
"""Создаёт записи Table для отслеживаемых таблиц, либо меняет флаг logged.
|
104
121
|
|
@@ -109,6 +126,21 @@ def update_or_create_tables(need_to_log_table_names: Iterable[str]) -> bool:
|
|
109
126
|
need_to_log_table_names = set(need_to_log_table_names)
|
110
127
|
existed_table_names = set(Table.objects.filter(schema='public').values_list('name', flat=True))
|
111
128
|
|
129
|
+
# Таблицы сервисных приложений, которые не должны отслеживаться аудит-логом
|
130
|
+
allowed_service_apps_labels = getattr(settings, 'ALLOWED_SERVICE_APPS_LABELS', [])
|
131
|
+
service_table_names = get_table_names_for_app_labels(allowed_service_apps_labels)
|
132
|
+
|
133
|
+
# Проверка конфликтных таблиц: должны логироваться, но находятся в сервисных приложениях
|
134
|
+
conflicting_tables = need_to_log_table_names & service_table_names
|
135
|
+
if conflicting_tables:
|
136
|
+
tables = ', '.join(sorted(conflicting_tables))
|
137
|
+
error_msg = (
|
138
|
+
f'Невозможно включить логирование для сервисных таблиц: {tables}. '
|
139
|
+
'Исключите их из отслеживания или перенесите таблицы в основную БД.'
|
140
|
+
)
|
141
|
+
logger_error(error_msg)
|
142
|
+
raise ValueError(error_msg)
|
143
|
+
|
112
144
|
to_create_table_names = need_to_log_table_names - existed_table_names
|
113
145
|
to_disable_table_names = existed_table_names - need_to_log_table_names
|
114
146
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: educommon
|
3
|
-
Version: 3.
|
3
|
+
Version: 3.18.0
|
4
4
|
Summary: Общая кодовая база для проектов БЦ Образование
|
5
5
|
Author-email: BARS Group <education_dev@bars-open.ru>
|
6
6
|
Project-URL: Homepage, https://stash.bars-open.ru/projects/EDUBASE/repos/educommon/browse
|
@@ -48,7 +48,7 @@ educommon/audit_log/apps.py,sha256=A27iSAEWAVr7LmBmexVDrbWm3eQ-zFq1FYGQRClMJVM,6
|
|
48
48
|
educommon/audit_log/constants.py,sha256=8C1SCiwD1s_ED9C3Hh50LMAnToGvZmF21nrv18hRLEg,903
|
49
49
|
educommon/audit_log/helpers.py,sha256=Dp1DvuzVkhD-P37yoXGd1tvXKPJz2ZQzROS97VURrcY,727
|
50
50
|
educommon/audit_log/middleware.py,sha256=HkxBh-1RQJnhKqckkXaMbFjJ34WgZGJssbk04wiS3ts,1140
|
51
|
-
educommon/audit_log/models.py,sha256=
|
51
|
+
educommon/audit_log/models.py,sha256=wWVJAb7EsluER4FiZQjk3Povw5N__F-X9iCcZNFNjGY,13112
|
52
52
|
educommon/audit_log/permissions.py,sha256=_0ntlLKUbJ1kYR2_YpgOxYMJNopZkrdIgxgPiHgL780,1273
|
53
53
|
educommon/audit_log/proxies.py,sha256=IVZSKpeTDTV8AXdblLo1u9yOmecPdzSvVNrgmf-sxBQ,8750
|
54
54
|
educommon/audit_log/routers.py,sha256=FF3KLvf6_WWFuZ9VRI8AZyKDfWp_lyd0i6bcVFfIehQ,204
|
@@ -57,6 +57,7 @@ educommon/audit_log/error_log/__init__.py,sha256=lfAIm5GTGQ1-kRFxH1s0agSt2Oeguwj
|
|
57
57
|
educommon/audit_log/error_log/actions.py,sha256=-KSy3RrBsbPWCML-gl5Hl5UGQdEsPZhoKrHq16LRmm8,2305
|
58
58
|
educommon/audit_log/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
59
59
|
educommon/audit_log/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
60
|
+
educommon/audit_log/management/commands/audit_log_migrate_data.py,sha256=J_ZIpamGVBcotc-6cYsz37AJADEEyuiUs-VVyKON47o,12695
|
60
61
|
educommon/audit_log/management/commands/reinstall_audit_log.py,sha256=HHpUeQwG_SuSsaxwZIq66piNhip_GHHQmAzpEzOFgaM,3573
|
61
62
|
educommon/audit_log/migrations/0001_initial.py,sha256=HDhvBNyVSx_NlFmyA-t_ooFo_TiKf0UHNCZp1GOpLA8,6115
|
62
63
|
educommon/audit_log/migrations/0002_install_audit_log.py,sha256=kAhtd1Xz8b6g33wmxBQyRBJIl-LGJdOc7yFy5yhoRJ8,2825
|
@@ -67,10 +68,11 @@ educommon/audit_log/migrations/0006_auto_20200806_1707.py,sha256=as1GDzH6UBF8_8g
|
|
67
68
|
educommon/audit_log/migrations/0007_create_selective_tables_function.py,sha256=h65nZHknxORAArKXTtblaRHH9iEmUMT8t4dAtEp6ii0,871
|
68
69
|
educommon/audit_log/migrations/0008_table_logged.py,sha256=mvBPtLGxgtwOIImIZ616yifmcUsEFbCjWo5NkhTX3Q4,476
|
69
70
|
educommon/audit_log/migrations/0009_reinstall_audit_log.py,sha256=c95H2yamWyrCoGDby6M_VGh41p2MuftC_xjmk_nqqQ8,286
|
71
|
+
educommon/audit_log/migrations/0010_alter_auditlog_time.py,sha256=XmujmdgNADMy4OhL4O7CIK6KDKtW2sSyfxLYWEGmtvI,509
|
70
72
|
educommon/audit_log/migrations/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
71
73
|
educommon/audit_log/sql/configure_audit_log.sql,sha256=M3QxNKTZbn-uNRxGDvNxE9iJh1EOQUTIho7rvc3yhlY,1511
|
72
74
|
educommon/audit_log/sql/install_audit_log.sql,sha256=SHrZ7WaYxawUKQEpZnj9k4HTU25NvBlxX_POqZ95HU0,14107
|
73
|
-
educommon/audit_log/utils/__init__.py,sha256=
|
75
|
+
educommon/audit_log/utils/__init__.py,sha256=14BHRbKdnvHNgomM-R__GqLk1N32ww_GN2xy81Ph_gs,16388
|
74
76
|
educommon/audit_log/utils/operations.py,sha256=skxL7wE4Jx1XlNdPx-Pl3SfiZ1G9jBmcZrXKSQDUGzw,2555
|
75
77
|
educommon/auth/__init__.py,sha256=xkGJgqQ5QaEU86n6tJV77ux-2bMTAKbjpVYBCDhcS0E,79
|
76
78
|
educommon/auth/rbac/__init__.py,sha256=guO7sOX6e1Z-dqptsqXjbnMdgbSdKp2suDKJa5_pdVU,317
|
@@ -349,7 +351,7 @@ educommon/ws_log/smev/exceptions.py,sha256=VNfzNHlj5Pz8D4979d_msTkxC-RQVoMctsgoJ
|
|
349
351
|
educommon/ws_log/templates/report/smev_logs.xlsx,sha256=nnYgB0Z_ix8HoxsRICjsZfFRQBdra-5Gd8nWhCxTjYg,10439
|
350
352
|
educommon/ws_log/templates/ui-js/smev-logs-list-window.js,sha256=AGup3D8GTJSY9WdDPj0zBJeYQBFOmGgcbxPOJbKK-nY,513
|
351
353
|
educommon/ws_log/templates/ui-js/smev-logs-report-setting-window.js,sha256=nQ7QYK9frJcE7g7kIt6INg9TlEEJAPPayBJgRaoTePA,1103
|
352
|
-
educommon-3.
|
353
|
-
educommon-3.
|
354
|
-
educommon-3.
|
355
|
-
educommon-3.
|
354
|
+
educommon-3.18.0.dist-info/METADATA,sha256=cKDmyFTkng12PZ-N2rVp62H3esRyCz9mHRLwenDTlLU,2380
|
355
|
+
educommon-3.18.0.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
|
356
|
+
educommon-3.18.0.dist-info/top_level.txt,sha256=z5fbW7bz_0V1foUm_FGcZ9_MTpW3N1dBN7-kEmMowl4,10
|
357
|
+
educommon-3.18.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|