udata 9.0.1.dev29536__py2.py3-none-any.whl → 9.0.1.dev29541__py2.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.

Potentially problematic release.


This version of udata might be problematic. Click here for more details.

Files changed (26) hide show
  1. udata/commands/db.py +88 -48
  2. udata/db/__init__.py +0 -0
  3. udata/db/tasks.py +6 -0
  4. udata/static/chunks/{11.7266fef2dddc1db403d9.js → 11.ae54612e36c6d46f85db.js} +3 -3
  5. udata/static/chunks/{11.7266fef2dddc1db403d9.js.map → 11.ae54612e36c6d46f85db.js.map} +1 -1
  6. udata/static/chunks/{13.91b177d7d531fd55cf5d.js → 13.d8ccb992a49875966313.js} +2 -2
  7. udata/static/chunks/{13.91b177d7d531fd55cf5d.js.map → 13.d8ccb992a49875966313.js.map} +1 -1
  8. udata/static/chunks/{16.e866757bab9f6b0a3f1b.js → 16.4565605e68bab129a471.js} +2 -2
  9. udata/static/chunks/{16.e866757bab9f6b0a3f1b.js.map → 16.4565605e68bab129a471.js.map} +1 -1
  10. udata/static/chunks/{19.619b83ac597516dcd03e.js → 19.f993a75d5bfe2382548d.js} +3 -3
  11. udata/static/chunks/{19.619b83ac597516dcd03e.js.map → 19.f993a75d5bfe2382548d.js.map} +1 -1
  12. udata/static/chunks/{5.48417db6b33328fa9d6a.js → 5.cc2e7bf65ef32f9c8604.js} +3 -3
  13. udata/static/chunks/{5.48417db6b33328fa9d6a.js.map → 5.cc2e7bf65ef32f9c8604.js.map} +1 -1
  14. udata/static/chunks/{6.f84539bd4c419b36cc19.js → 6.cad898a38692eda28965.js} +3 -3
  15. udata/static/chunks/{6.f84539bd4c419b36cc19.js.map → 6.cad898a38692eda28965.js.map} +1 -1
  16. udata/static/chunks/{9.07503e7f7ec02919f696.js → 9.d5b992e9ef51921aeb57.js} +2 -2
  17. udata/static/chunks/{9.07503e7f7ec02919f696.js.map → 9.d5b992e9ef51921aeb57.js.map} +1 -1
  18. udata/static/common.js +1 -1
  19. udata/static/common.js.map +1 -1
  20. udata/tasks.py +1 -0
  21. {udata-9.0.1.dev29536.dist-info → udata-9.0.1.dev29541.dist-info}/METADATA +2 -1
  22. {udata-9.0.1.dev29536.dist-info → udata-9.0.1.dev29541.dist-info}/RECORD +26 -24
  23. {udata-9.0.1.dev29536.dist-info → udata-9.0.1.dev29541.dist-info}/LICENSE +0 -0
  24. {udata-9.0.1.dev29536.dist-info → udata-9.0.1.dev29541.dist-info}/WHEEL +0 -0
  25. {udata-9.0.1.dev29536.dist-info → udata-9.0.1.dev29541.dist-info}/entry_points.txt +0 -0
  26. {udata-9.0.1.dev29536.dist-info → udata-9.0.1.dev29541.dist-info}/top_level.txt +0 -0
udata/commands/db.py CHANGED
@@ -1,7 +1,10 @@
1
1
  import collections
2
+ from itertools import groupby
2
3
  import logging
3
4
  import os
5
+ import traceback
4
6
 
7
+ from bson import DBRef
5
8
  import click
6
9
  import mongoengine
7
10
 
@@ -135,8 +138,14 @@ def display_op(op):
135
138
  echo('{label:.<70} [{date}]'.format(label=label, date=timestamp))
136
139
  format_output(op['output'], success=op['success'], traceback=op.get('traceback'))
137
140
 
138
-
139
141
  def check_references(models_to_check):
142
+ # Cannot modify local scope from Python… :-(
143
+ class Log: errors = []
144
+
145
+ def print_and_save(text: str):
146
+ Log.errors.append(text.strip())
147
+ print(text)
148
+
140
149
  errors = collections.defaultdict(int)
141
150
 
142
151
  _models = []
@@ -147,7 +156,7 @@ def check_references(models_to_check):
147
156
  ]
148
157
 
149
158
  references = []
150
- for model in _models:
159
+ for model in set(_models):
151
160
  if model.__name__ == 'Activity':
152
161
  print(f'Skipping Activity model, scheduled for deprecation')
153
162
  continue
@@ -240,53 +249,84 @@ def check_references(models_to_check):
240
249
  print(f'- {reference["repr"]}({reference["destination"]}) — {reference["type"]}')
241
250
  print('')
242
251
 
243
- for reference in references:
244
- print(f'- {reference["repr"]}({reference["destination"]}) {reference["type"]}...')
245
- query = {f'{reference["name"]}__ne': None}
246
- qs = reference['model'].objects(**query).no_cache().all()
247
- try:
248
- for obj in qs:
249
- if reference['type'] == 'direct':
250
- try:
251
- _ = getattr(obj, reference['name'])
252
- except mongoengine.errors.DoesNotExist:
253
- errors[reference["repr"]] += 1
254
- elif reference['type'] == 'list':
255
- for sub in getattr(obj, reference['name']):
256
- try:
257
- _ = sub.id
258
- except mongoengine.errors.DoesNotExist:
259
- errors[reference["repr"]] += 1
260
- elif reference['type'] == 'embed_list':
261
- p1, p2 = reference['name'].split('__')
262
- for sub in getattr(obj, p1):
263
- try:
264
- getattr(sub, p2)
265
- except mongoengine.errors.DoesNotExist:
266
- errors[reference["repr"]] += 1
267
- elif reference['type'] == 'embed':
268
- p1, p2 = reference['name'].split('__')
269
- sub = getattr(obj, p1)
270
- try:
271
- getattr(sub, p2)
272
- except mongoengine.errors.DoesNotExist:
273
- errors[reference["repr"]] += 1
274
- elif reference['type'] == 'embed_list_ref':
275
- p1, p2 = reference['name'].split('__')
276
- sub = getattr(getattr(obj, p1), p2)
277
- for obj in sub:
278
- try:
279
- obj.id
280
- except mongoengine.errors.DoesNotExist:
281
- errors[reference["repr"]] += 1
282
- else:
283
- print(f'Unknown ref type {reference["type"]}')
284
- print('Errors:', errors[reference["repr"]])
285
- except mongoengine.errors.FieldDoesNotExist as e:
286
- print('[ERROR]', e)
287
-
288
- print(f'\n Total errors: {sum(errors.values())}')
252
+ total = 0
253
+ for model, model_references in groupby(references, lambda i: i["model"]):
254
+ model_references = list(model_references)
255
+ count = model.objects.count()
256
+ print(f'- doing {count} {model.__name__}…')
257
+ errors[model] = {}
258
+
259
+ qs = model.objects().no_cache().all()
260
+ with click.progressbar(qs, length=count) as models:
261
+ for obj in models:
262
+ for reference in model_references:
263
+ key = f'\t- {reference["repr"]}({reference["destination"]}) {reference["type"]}…'
264
+ if key not in errors[model]:
265
+ errors[model][key] = 0
289
266
 
267
+ try:
268
+ if reference['type'] == 'direct':
269
+ try:
270
+ _ = getattr(obj, reference['name'])
271
+ except mongoengine.errors.DoesNotExist:
272
+ errors[model][key] += 1
273
+ print_and_save(f'\t{model.__name__}#{obj.id} have a broken reference for `{reference["name"]}`')
274
+ elif reference['type'] == 'list':
275
+ attr_list = getattr(obj, reference['name'], [])
276
+ for i, sub in enumerate(attr_list):
277
+ # If it's still an instance of DBRef it means that it failed to
278
+ # dereference the ID.
279
+ if isinstance(sub, DBRef):
280
+ errors[model][key] += 1
281
+ print_and_save(f'\t{model.__name__}#{obj.id} have a broken reference for {reference["name"]}[{i}]')
282
+ elif reference['type'] == 'embed_list':
283
+ p1, p2 = reference['name'].split('__')
284
+ attr_list = getattr(obj, p1, [])
285
+ for i, sub in enumerate(attr_list):
286
+ try:
287
+ getattr(sub, p2)
288
+ except mongoengine.errors.DoesNotExist:
289
+ errors[model][key] += 1
290
+ print_and_save(f'\t{model.__name__}#{obj.id} have a broken reference for {p1}[{i}].{p2}')
291
+ elif reference['type'] == 'embed':
292
+ p1, p2 = reference['name'].split('__')
293
+ sub = getattr(obj, p1)
294
+ if sub is None: continue
295
+ try:
296
+ getattr(sub, p2)
297
+ except mongoengine.errors.DoesNotExist:
298
+ errors[model][key] += 1
299
+ print_and_save(f'\t{model.__name__}#{obj.id} have a broken reference for {p1}.{p2}')
300
+ elif reference['type'] == 'embed_list_ref':
301
+ p1, p2 = reference['name'].split('__')
302
+ a = getattr(obj, p1)
303
+ if a is None: continue
304
+ sub = getattr(a, p2, [])
305
+ for i, child in enumerate(sub):
306
+ # If it's still an instance of DBRef it means that it failed to
307
+ # dereference the ID.
308
+ if isinstance(child, DBRef):
309
+ errors[model][key] += 1
310
+ print_and_save(f'\t{model.__name__}#{obj.id} have a broken reference for {p1}.{p2}[{i}]')
311
+ else:
312
+ print_and_save(f'Unknown ref type {reference["type"]}')
313
+ except mongoengine.errors.FieldDoesNotExist as e:
314
+ print_and_save(f'[ERROR for {model.__name__} {obj.id}] {traceback.format_exc()}')
315
+
316
+ for key, nb_errors in errors[model].items():
317
+ print(f'{key}: {nb_errors}')
318
+ total += nb_errors
319
+
320
+ print(f'\n Total errors: {total}')
321
+
322
+ if total > 0:
323
+ try:
324
+ import sentry_sdk
325
+ with sentry_sdk.push_scope() as scope:
326
+ scope.set_extra("errors", Log.errors)
327
+ sentry_sdk.capture_message(f"{total} integrity errors", "fatal")
328
+ except ImportError:
329
+ print("`sentry_sdk` not installed. The errors weren't reported")
290
330
 
291
331
  @grp.command()
292
332
  @click.option('--models', multiple=True, default=[], help='Model(s) to check')
udata/db/__init__.py ADDED
File without changes
udata/db/tasks.py ADDED
@@ -0,0 +1,6 @@
1
+ from udata.commands.db import check_references
2
+ from udata.tasks import job
3
+
4
+ @job('check-integrity')
5
+ def check_integrity(self):
6
+ check_references()