udata 9.1.2.dev30454__py2.py3-none-any.whl → 9.1.2.dev30483__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 (27) hide show
  1. udata/api_fields.py +48 -7
  2. udata/core/reports/api.py +2 -2
  3. udata/core/reports/constants.py +5 -1
  4. udata/core/reports/models.py +26 -11
  5. udata/static/chunks/{11.e9b9ca1f3e03d4020377.js → 11.f6ed628667c44bbad757.js} +3 -3
  6. udata/static/chunks/{11.e9b9ca1f3e03d4020377.js.map → 11.f6ed628667c44bbad757.js.map} +1 -1
  7. udata/static/chunks/{13.038c0d9aa0dfa0181c4b.js → 13.a535d3f0dc1690137ebf.js} +2 -2
  8. udata/static/chunks/{13.038c0d9aa0dfa0181c4b.js.map → 13.a535d3f0dc1690137ebf.js.map} +1 -1
  9. udata/static/chunks/{16.0baa2b64a74a2dcde25c.js → 16.e499a69d451e8620b796.js} +2 -2
  10. udata/static/chunks/{16.0baa2b64a74a2dcde25c.js.map → 16.e499a69d451e8620b796.js.map} +1 -1
  11. udata/static/chunks/{19.350a9f150b074b4ecefa.js → 19.7d96d075450c6a7f843e.js} +3 -3
  12. udata/static/chunks/{19.350a9f150b074b4ecefa.js.map → 19.7d96d075450c6a7f843e.js.map} +1 -1
  13. udata/static/chunks/{5.6ebbce2b9b3e696d3da5.js → 5.a1a6164f9161d24acda1.js} +3 -3
  14. udata/static/chunks/{5.6ebbce2b9b3e696d3da5.js.map → 5.a1a6164f9161d24acda1.js.map} +1 -1
  15. udata/static/chunks/{6.d8a5f7b017bcbd083641.js → 6.7d18ddefd50912448615.js} +3 -3
  16. udata/static/chunks/{6.d8a5f7b017bcbd083641.js.map → 6.7d18ddefd50912448615.js.map} +1 -1
  17. udata/static/chunks/{9.ba6c5ed42a4b3d1de13a.js → 9.47a0060ef619922385ad.js} +2 -2
  18. udata/static/chunks/{9.ba6c5ed42a4b3d1de13a.js.map → 9.47a0060ef619922385ad.js.map} +1 -1
  19. udata/static/common.js +1 -1
  20. udata/static/common.js.map +1 -1
  21. udata/tests/api/test_reports_api.py +44 -20
  22. {udata-9.1.2.dev30454.dist-info → udata-9.1.2.dev30483.dist-info}/METADATA +3 -1
  23. {udata-9.1.2.dev30454.dist-info → udata-9.1.2.dev30483.dist-info}/RECORD +27 -27
  24. {udata-9.1.2.dev30454.dist-info → udata-9.1.2.dev30483.dist-info}/LICENSE +0 -0
  25. {udata-9.1.2.dev30454.dist-info → udata-9.1.2.dev30483.dist-info}/WHEEL +0 -0
  26. {udata-9.1.2.dev30454.dist-info → udata-9.1.2.dev30483.dist-info}/entry_points.txt +0 -0
  27. {udata-9.1.2.dev30454.dist-info → udata-9.1.2.dev30483.dist-info}/top_level.txt +0 -0
udata/api_fields.py CHANGED
@@ -5,8 +5,17 @@ from bson import ObjectId
5
5
 
6
6
  import udata.api.fields as custom_restx_fields
7
7
  from udata.api import api
8
+ from udata.mongo.engine import db
8
9
  from udata.mongo.errors import FieldValidationError
9
10
 
11
+ lazy_reference = api.model(
12
+ "LazyReference",
13
+ {
14
+ "class": restx_fields.Raw(attribute=lambda ref: ref.document_type.__name__),
15
+ "id": restx_fields.Raw(attribute=lambda ref: ref.pk),
16
+ },
17
+ )
18
+
10
19
 
11
20
  def convert_db_to_field(key, field, info={}):
12
21
  """
@@ -60,6 +69,10 @@ def convert_db_to_field(key, field, info={}):
60
69
  )
61
70
  constructor_read = lambda **kwargs: restx_fields.List(field_read, **kwargs)
62
71
  constructor_write = lambda **kwargs: restx_fields.List(field_write, **kwargs)
72
+ elif isinstance(
73
+ field, (mongo_fields.GenericReferenceField, mongoengine.fields.GenericLazyReferenceField)
74
+ ):
75
+ constructor = lambda **kwargs: restx_fields.Nested(lazy_reference, **kwargs)
63
76
  elif isinstance(field, mongo_fields.ReferenceField):
64
77
  # For reference we accept while writing a String representing the ID of the referenced model.
65
78
  # For reading, if the user supplied a `nested_fields` (RestX model), we use it to convert
@@ -289,8 +302,21 @@ def patch(obj, request):
289
302
  ):
290
303
  # TODO `wrap_primary_key` do Mongo request, do a first pass to fetch all documents before calling it (to avoid multiple queries).
291
304
  value = [wrap_primary_key(key, model_attribute.field, id) for id in value]
292
- if isinstance(model_attribute, mongoengine.fields.ReferenceField):
305
+ elif isinstance(model_attribute, mongoengine.fields.ReferenceField):
293
306
  value = wrap_primary_key(key, model_attribute, value)
307
+ elif isinstance(
308
+ model_attribute,
309
+ (
310
+ mongoengine.fields.GenericReferenceField,
311
+ mongoengine.fields.GenericLazyReferenceField,
312
+ ),
313
+ ):
314
+ value = wrap_primary_key(
315
+ key,
316
+ model_attribute,
317
+ value["id"],
318
+ document_type=db.resolve_model(value["class"]),
319
+ )
294
320
 
295
321
  info = getattr(model_attribute, "__additional_field_info__", {})
296
322
 
@@ -305,24 +331,39 @@ def patch(obj, request):
305
331
  return obj
306
332
 
307
333
 
308
- def wrap_primary_key(field_name: str, foreign_field: mongoengine.fields.ReferenceField, value: str):
334
+ def wrap_primary_key(
335
+ field_name: str,
336
+ foreign_field: mongoengine.fields.ReferenceField | mongoengine.fields.GenericReferenceField,
337
+ value: str,
338
+ document_type: type = None,
339
+ ):
309
340
  """
310
341
  We need to wrap the `String` inside an `ObjectId` most of the time. If the foreign ID is a `String` we need to get
311
342
  a `DBRef` from the database.
312
343
 
313
344
  TODO: we only check the document reference if the ID is a `String` field (not in the case of a classic `ObjectId`).
314
345
  """
315
- document_type = foreign_field.document_type()
316
- id_field_name = document_type.__class__._meta["id_field"]
346
+ document_type = document_type or foreign_field.document_type().__class__
347
+ id_field_name = document_type._meta["id_field"]
317
348
 
318
- id_field = getattr(document_type.__class__, id_field_name)
349
+ id_field = getattr(document_type, id_field_name)
319
350
 
320
351
  # Get the foreign document from MongoDB because the othewise it fails during read
321
352
  # Also useful to get a DBRef for non ObjectId references (see below)
322
- foreign_document = document_type.__class__.objects(**{id_field_name: value}).first()
353
+ foreign_document = document_type.objects(**{id_field_name: value}).first()
323
354
  if foreign_document is None:
324
355
  raise FieldValidationError(field=field_name, message=f"Unknown reference '{value}'")
325
356
 
357
+ # GenericReferenceField only accepts document (not dbref / objectid)
358
+ if isinstance(
359
+ foreign_field,
360
+ (
361
+ mongoengine.fields.GenericReferenceField,
362
+ mongoengine.fields.GenericLazyReferenceField,
363
+ ),
364
+ ):
365
+ return foreign_document
366
+
326
367
  if isinstance(id_field, mongoengine.fields.ObjectIdField):
327
368
  return ObjectId(value)
328
369
  elif isinstance(id_field, mongoengine.fields.StringField):
@@ -336,5 +377,5 @@ def wrap_primary_key(field_name: str, foreign_field: mongoengine.fields.Referenc
336
377
  return foreign_document.to_dbref()
337
378
  else:
338
379
  raise ValueError(
339
- f"Unknown ID field type {id_field.__class__} for {document_type.__class__} (ID field name is {id_field_name}, value was {value})"
380
+ f"Unknown ID field type {id_field.__class__} for {document_type} (ID field name is {id_field_name}, value was {value})"
340
381
  )
udata/core/reports/api.py CHANGED
@@ -21,13 +21,13 @@ class ReportsAPI(API):
21
21
 
22
22
  return Report.apply_sort_filters_and_pagination(query)
23
23
 
24
- @api.secure
25
24
  @api.doc("create_report", responses={400: "Validation error"})
26
25
  @api.expect(Report.__write_fields__)
27
26
  @api.marshal_with(Report.__read_fields__, code=201)
28
27
  def post(self):
29
28
  report = patch(Report(), request)
30
- report.by = current_user._get_current_object()
29
+ if current_user.is_authenticated:
30
+ report.by = current_user._get_current_object()
31
31
 
32
32
  try:
33
33
  report.save()
@@ -1,4 +1,8 @@
1
+ from udata.core.dataservices.models import Dataservice
1
2
  from udata.core.dataset.models import Dataset
3
+ from udata.core.discussions.models import Discussion
4
+ from udata.core.organization.models import Organization
5
+ from udata.core.reuse.models import Reuse
2
6
  from udata.i18n import lazy_gettext as _
3
7
 
4
8
  REASON_PERSONAL_DATA = "personal_data"
@@ -25,4 +29,4 @@ def reports_reasons_translations() -> list:
25
29
 
26
30
 
27
31
  REPORT_REASONS_CHOICES: list[str] = [item["value"] for item in reports_reasons_translations()]
28
- REPORTABLE_MODELS = [Dataset]
32
+ REPORTABLE_MODELS = [Dataset, Reuse, Discussion, Organization, Dataservice]
@@ -1,6 +1,7 @@
1
1
  from datetime import datetime
2
2
 
3
- from mongoengine import NULLIFY, signals
3
+ from bson import DBRef
4
+ from mongoengine import DO_NOTHING, NULLIFY, signals
4
5
 
5
6
  from udata.api_fields import field, generate_fields
6
7
  from udata.core.user.api_fields import user_ref_fields
@@ -20,9 +21,13 @@ class Report(db.Document):
20
21
  allow_null=True,
21
22
  )
22
23
 
23
- object_type = field(db.StringField(choices=[m.__name__ for m in REPORTABLE_MODELS]))
24
- object_id = field(db.ObjectIdField())
25
- object_deleted_at = field(
24
+ # Here we use the lazy version of `GenericReferenceField` because we could point to a
25
+ # non existant model (if it was deleted we want to keep the report data).
26
+ subject = field(
27
+ db.GenericLazyReferenceField(reverse_delete_rule=DO_NOTHING, choices=REPORTABLE_MODELS)
28
+ )
29
+
30
+ subject_deleted_at = field(
26
31
  db.DateTimeField(),
27
32
  allow_null=True,
28
33
  readonly=True,
@@ -42,15 +47,25 @@ class Report(db.Document):
42
47
 
43
48
  @classmethod
44
49
  def mark_as_deleted_soft_delete(cls, sender, document, **kwargs):
45
- if document.deleted:
46
- Report.objects(
47
- object_type=sender.__name__, object_id=document.id, object_deleted_at=None
48
- ).update(object_deleted_at=datetime.utcnow)
50
+ """
51
+ Called when updating a model (maybe updating the `deleted` date)
52
+ Some documents like Discussion do not have a `deleted` attribute.
53
+ """
54
+ if hasattr(document, "deleted") and document.deleted:
55
+ Report.objects(subject=document, subject_deleted_at=None).update(
56
+ subject_deleted_at=datetime.utcnow
57
+ )
49
58
 
50
- def mark_as_deleted_hard_delete(cls, document, **kwargs):
59
+ @classmethod
60
+ def mark_as_deleted_hard_delete(cls, sender, document, **kwargs):
61
+ """
62
+ Call when really deleting a model from the database.
63
+ """
64
+ # Here we are forced to do a manual `DBRef(sender.__name__.lower(), document.id)`
65
+ # because the document doesn't exist anymore…
51
66
  Report.objects(
52
- object_type=document.__class__.__name__, object_id=document.id, object_deleted_at=None
53
- ).update(object_deleted_at=datetime.utcnow)
67
+ subject=DBRef(sender.__name__.lower(), document.id), subject_deleted_at=None
68
+ ).update(subject_deleted_at=datetime.utcnow)
54
69
 
55
70
 
56
71
  for model in REPORTABLE_MODELS: