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.
- udata/api_fields.py +48 -7
- udata/core/reports/api.py +2 -2
- udata/core/reports/constants.py +5 -1
- udata/core/reports/models.py +26 -11
- udata/static/chunks/{11.e9b9ca1f3e03d4020377.js → 11.f6ed628667c44bbad757.js} +3 -3
- udata/static/chunks/{11.e9b9ca1f3e03d4020377.js.map → 11.f6ed628667c44bbad757.js.map} +1 -1
- udata/static/chunks/{13.038c0d9aa0dfa0181c4b.js → 13.a535d3f0dc1690137ebf.js} +2 -2
- udata/static/chunks/{13.038c0d9aa0dfa0181c4b.js.map → 13.a535d3f0dc1690137ebf.js.map} +1 -1
- udata/static/chunks/{16.0baa2b64a74a2dcde25c.js → 16.e499a69d451e8620b796.js} +2 -2
- udata/static/chunks/{16.0baa2b64a74a2dcde25c.js.map → 16.e499a69d451e8620b796.js.map} +1 -1
- udata/static/chunks/{19.350a9f150b074b4ecefa.js → 19.7d96d075450c6a7f843e.js} +3 -3
- udata/static/chunks/{19.350a9f150b074b4ecefa.js.map → 19.7d96d075450c6a7f843e.js.map} +1 -1
- udata/static/chunks/{5.6ebbce2b9b3e696d3da5.js → 5.a1a6164f9161d24acda1.js} +3 -3
- udata/static/chunks/{5.6ebbce2b9b3e696d3da5.js.map → 5.a1a6164f9161d24acda1.js.map} +1 -1
- udata/static/chunks/{6.d8a5f7b017bcbd083641.js → 6.7d18ddefd50912448615.js} +3 -3
- udata/static/chunks/{6.d8a5f7b017bcbd083641.js.map → 6.7d18ddefd50912448615.js.map} +1 -1
- udata/static/chunks/{9.ba6c5ed42a4b3d1de13a.js → 9.47a0060ef619922385ad.js} +2 -2
- udata/static/chunks/{9.ba6c5ed42a4b3d1de13a.js.map → 9.47a0060ef619922385ad.js.map} +1 -1
- udata/static/common.js +1 -1
- udata/static/common.js.map +1 -1
- udata/tests/api/test_reports_api.py +44 -20
- {udata-9.1.2.dev30454.dist-info → udata-9.1.2.dev30483.dist-info}/METADATA +3 -1
- {udata-9.1.2.dev30454.dist-info → udata-9.1.2.dev30483.dist-info}/RECORD +27 -27
- {udata-9.1.2.dev30454.dist-info → udata-9.1.2.dev30483.dist-info}/LICENSE +0 -0
- {udata-9.1.2.dev30454.dist-info → udata-9.1.2.dev30483.dist-info}/WHEEL +0 -0
- {udata-9.1.2.dev30454.dist-info → udata-9.1.2.dev30483.dist-info}/entry_points.txt +0 -0
- {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
|
-
|
|
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(
|
|
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.
|
|
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
|
|
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.
|
|
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
|
|
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
|
-
|
|
29
|
+
if current_user.is_authenticated:
|
|
30
|
+
report.by = current_user._get_current_object()
|
|
31
31
|
|
|
32
32
|
try:
|
|
33
33
|
report.save()
|
udata/core/reports/constants.py
CHANGED
|
@@ -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]
|
udata/core/reports/models.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
|
|
3
|
-
from
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
|
|
53
|
-
).update(
|
|
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:
|