udata 10.4.2.dev35451__py2.py3-none-any.whl → 10.4.2.dev35475__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/core/activity/__init__.py +1 -0
- udata/core/dataservices/api.py +43 -0
- udata/core/dataset/api.py +44 -0
- udata/core/post/api.py +34 -0
- udata/core/reuse/api.py +42 -1
- udata/core/topic/activities.py +36 -0
- udata/core/topic/models.py +23 -15
- udata/static/chunks/{11.51d706fb9521c16976bc.js → 11.0f04e49a40a0a381bcce.js} +3 -3
- udata/static/chunks/{11.51d706fb9521c16976bc.js.map → 11.0f04e49a40a0a381bcce.js.map} +1 -1
- udata/static/chunks/{19.a348a5fff8fe2801e52a.js → 19.8da42e8359d72afc2618.js} +3 -3
- udata/static/chunks/{19.a348a5fff8fe2801e52a.js.map → 19.8da42e8359d72afc2618.js.map} +1 -1
- udata/static/chunks/{5.0652a860afda96795a53.js → 5.0fa1408dae4e76b87b2e.js} +3 -3
- udata/static/chunks/{5.0652a860afda96795a53.js.map → 5.0fa1408dae4e76b87b2e.js.map} +1 -1
- udata/static/chunks/{6.92d7c2ec6d20005774ef.js → 6.d663709d877baa44a71e.js} +3 -3
- udata/static/chunks/{6.92d7c2ec6d20005774ef.js.map → 6.d663709d877baa44a71e.js.map} +1 -1
- udata/static/chunks/{8.462bb3029de008497675.js → 8.494b003a94383b142c18.js} +2 -2
- udata/static/chunks/{8.462bb3029de008497675.js.map → 8.494b003a94383b142c18.js.map} +1 -1
- udata/static/common.js +1 -1
- udata/static/common.js.map +1 -1
- udata/tests/api/test_dataservices_api.py +53 -0
- udata/tests/api/test_datasets_api.py +53 -0
- udata/tests/api/test_reuses_api.py +54 -0
- udata/tests/dataset/test_dataset_model.py +49 -0
- udata/tests/test_topics.py +19 -0
- {udata-10.4.2.dev35451.dist-info → udata-10.4.2.dev35475.dist-info}/METADATA +5 -3
- {udata-10.4.2.dev35451.dist-info → udata-10.4.2.dev35475.dist-info}/RECORD +30 -29
- {udata-10.4.2.dev35451.dist-info → udata-10.4.2.dev35475.dist-info}/LICENSE +0 -0
- {udata-10.4.2.dev35451.dist-info → udata-10.4.2.dev35475.dist-info}/WHEEL +0 -0
- {udata-10.4.2.dev35451.dist-info → udata-10.4.2.dev35475.dist-info}/entry_points.txt +0 -0
- {udata-10.4.2.dev35451.dist-info → udata-10.4.2.dev35475.dist-info}/top_level.txt +0 -0
udata/core/activity/__init__.py
CHANGED
udata/core/dataservices/api.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
+
from typing import List
|
|
2
3
|
|
|
3
4
|
import mongoengine
|
|
4
5
|
from bson import ObjectId
|
|
6
|
+
from feedgenerator.django.utils.feedgenerator import Atom1Feed
|
|
5
7
|
from flask import make_response, redirect, request, url_for
|
|
6
8
|
from flask_login import current_user
|
|
7
9
|
|
|
@@ -10,6 +12,9 @@ from udata.api_fields import patch
|
|
|
10
12
|
from udata.core.dataservices.permissions import OwnablePermission
|
|
11
13
|
from udata.core.dataset.models import Dataset
|
|
12
14
|
from udata.core.followers.api import FollowAPI
|
|
15
|
+
from udata.core.site.models import current_site
|
|
16
|
+
from udata.frontend.markdown import md
|
|
17
|
+
from udata.i18n import gettext as _
|
|
13
18
|
from udata.rdf import RDF_EXTENSIONS, graph_response, negociate_content
|
|
14
19
|
|
|
15
20
|
from .models import Dataservice
|
|
@@ -49,6 +54,44 @@ class DataservicesAPI(API):
|
|
|
49
54
|
return dataservice, 201
|
|
50
55
|
|
|
51
56
|
|
|
57
|
+
@ns.route("/recent.atom", endpoint="recent_dataservices_atom_feed")
|
|
58
|
+
class DataservicesAtomFeedAPI(API):
|
|
59
|
+
@api.doc("recent_dataservices_atom_feed")
|
|
60
|
+
def get(self):
|
|
61
|
+
feed = Atom1Feed(
|
|
62
|
+
_("Latest APIs"), description=None, feed_url=request.url, link=request.url_root
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
dataservices: List[Dataservice] = (
|
|
66
|
+
Dataservice.objects.visible()
|
|
67
|
+
.order_by("-created_at_internal")
|
|
68
|
+
.limit(current_site.feed_size)
|
|
69
|
+
)
|
|
70
|
+
for dataservice in dataservices:
|
|
71
|
+
author_name = None
|
|
72
|
+
author_uri = None
|
|
73
|
+
if dataservice.organization:
|
|
74
|
+
author_name = dataservice.organization.name
|
|
75
|
+
author_uri = dataservice.organization.external_url
|
|
76
|
+
elif dataservice.owner:
|
|
77
|
+
author_name = dataservice.owner.fullname
|
|
78
|
+
author_uri = dataservice.owner.external_url
|
|
79
|
+
feed.add_item(
|
|
80
|
+
dataservice.title,
|
|
81
|
+
unique_id=dataservice.id,
|
|
82
|
+
description=dataservice.description,
|
|
83
|
+
content=md(dataservice.description),
|
|
84
|
+
author_name=author_name,
|
|
85
|
+
author_link=author_uri,
|
|
86
|
+
link=dataservice.url_for(external=True),
|
|
87
|
+
updateddate=dataservice.metadata_modified_at,
|
|
88
|
+
pubdate=dataservice.created_at,
|
|
89
|
+
)
|
|
90
|
+
response = make_response(feed.writeString("utf-8"))
|
|
91
|
+
response.headers["Content-Type"] = "application/atom+xml"
|
|
92
|
+
return response
|
|
93
|
+
|
|
94
|
+
|
|
52
95
|
@ns.route("/<dataservice:dataservice>/", endpoint="dataservice")
|
|
53
96
|
class DataserviceAPI(API):
|
|
54
97
|
@api.doc("get_dataservice")
|
udata/core/dataset/api.py
CHANGED
|
@@ -20,9 +20,11 @@ These changes might lead to backward compatibility breakage meaning:
|
|
|
20
20
|
import logging
|
|
21
21
|
import os
|
|
22
22
|
from datetime import datetime
|
|
23
|
+
from typing import List
|
|
23
24
|
|
|
24
25
|
import mongoengine
|
|
25
26
|
from bson.objectid import ObjectId
|
|
27
|
+
from feedgenerator.django.utils.feedgenerator import Atom1Feed
|
|
26
28
|
from flask import abort, current_app, make_response, redirect, request, url_for
|
|
27
29
|
from flask_restx.inputs import boolean
|
|
28
30
|
from flask_security import current_user
|
|
@@ -39,8 +41,11 @@ from udata.core.dataset.models import CHECKSUM_TYPES
|
|
|
39
41
|
from udata.core.followers.api import FollowAPI
|
|
40
42
|
from udata.core.organization.models import Organization
|
|
41
43
|
from udata.core.reuse.models import Reuse
|
|
44
|
+
from udata.core.site.models import current_site
|
|
42
45
|
from udata.core.storages.api import handle_upload, upload_parser
|
|
43
46
|
from udata.core.topic.models import Topic
|
|
47
|
+
from udata.frontend.markdown import md
|
|
48
|
+
from udata.i18n import gettext as _
|
|
44
49
|
from udata.linkchecker.checker import check_resource
|
|
45
50
|
from udata.rdf import RDF_EXTENSIONS, graph_response, negociate_content
|
|
46
51
|
from udata.utils import get_by
|
|
@@ -292,6 +297,45 @@ class DatasetListAPI(API):
|
|
|
292
297
|
return dataset, 201
|
|
293
298
|
|
|
294
299
|
|
|
300
|
+
@ns.route("/recent.atom", endpoint="recent_datasets_atom_feed")
|
|
301
|
+
class DatasetsAtomFeedAPI(API):
|
|
302
|
+
@api.doc("recent_datasets_atom_feed")
|
|
303
|
+
def get(self):
|
|
304
|
+
feed = Atom1Feed(
|
|
305
|
+
_("Latest datasets"),
|
|
306
|
+
description=None,
|
|
307
|
+
feed_url=request.url,
|
|
308
|
+
link=request.url_root,
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
datasets: List[Dataset] = (
|
|
312
|
+
Dataset.objects.visible().order_by("-created_at_internal").limit(current_site.feed_size)
|
|
313
|
+
)
|
|
314
|
+
for dataset in datasets:
|
|
315
|
+
author_name = None
|
|
316
|
+
author_uri = None
|
|
317
|
+
if dataset.organization:
|
|
318
|
+
author_name = dataset.organization.name
|
|
319
|
+
author_uri = dataset.organization.external_url
|
|
320
|
+
elif dataset.owner:
|
|
321
|
+
author_name = dataset.owner.fullname
|
|
322
|
+
author_uri = dataset.owner.external_url
|
|
323
|
+
feed.add_item(
|
|
324
|
+
dataset.title,
|
|
325
|
+
unique_id=dataset.id,
|
|
326
|
+
description=dataset.description,
|
|
327
|
+
content=md(dataset.description),
|
|
328
|
+
author_name=author_name,
|
|
329
|
+
author_link=author_uri,
|
|
330
|
+
link=dataset.external_url,
|
|
331
|
+
updateddate=dataset.last_modified,
|
|
332
|
+
pubdate=dataset.created_at,
|
|
333
|
+
)
|
|
334
|
+
response = make_response(feed.writeString("utf-8"))
|
|
335
|
+
response.headers["Content-Type"] = "application/atom+xml"
|
|
336
|
+
return response
|
|
337
|
+
|
|
338
|
+
|
|
295
339
|
@ns.route("/<dataset:dataset>/", endpoint="dataset", doc=common_doc)
|
|
296
340
|
@api.response(404, "Dataset not found")
|
|
297
341
|
@api.response(410, "Dataset has been deleted")
|
udata/core/post/api.py
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
from feedgenerator.django.utils.feedgenerator import Atom1Feed
|
|
5
|
+
from flask import make_response, request
|
|
2
6
|
|
|
3
7
|
from udata.api import API, api, fields
|
|
4
8
|
from udata.auth import Permission as AdminPermission
|
|
@@ -11,6 +15,8 @@ from udata.core.storages.api import (
|
|
|
11
15
|
uploaded_image_fields,
|
|
12
16
|
)
|
|
13
17
|
from udata.core.user.api_fields import user_ref_fields
|
|
18
|
+
from udata.frontend.markdown import md
|
|
19
|
+
from udata.i18n import gettext as _
|
|
14
20
|
|
|
15
21
|
from .forms import PostForm
|
|
16
22
|
from .models import Post
|
|
@@ -105,6 +111,34 @@ class PostsAPI(API):
|
|
|
105
111
|
return form.save(), 201
|
|
106
112
|
|
|
107
113
|
|
|
114
|
+
@ns.route("/recent.atom", endpoint="recent_posts_atom_feed")
|
|
115
|
+
class PostsAtomFeedAPI(API):
|
|
116
|
+
@api.doc("recent_posts_atom_feed")
|
|
117
|
+
def get(self):
|
|
118
|
+
feed = Atom1Feed(
|
|
119
|
+
_("Latests posts"),
|
|
120
|
+
description=None,
|
|
121
|
+
feed_url=request.url,
|
|
122
|
+
link=request.url_root,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
posts: List[Post] = Post.objects().published().order_by("-published").limit(15)
|
|
126
|
+
for post in posts:
|
|
127
|
+
feed.add_item(
|
|
128
|
+
post.name,
|
|
129
|
+
unique_id=post.id,
|
|
130
|
+
description=post.headline,
|
|
131
|
+
content=md(post.content),
|
|
132
|
+
author_name="data.gouv.fr",
|
|
133
|
+
link=post.external_url,
|
|
134
|
+
updateddate=post.last_modified,
|
|
135
|
+
pubdate=post.published,
|
|
136
|
+
)
|
|
137
|
+
response = make_response(feed.writeString("utf-8"))
|
|
138
|
+
response.headers["Content-Type"] = "application/atom+xml"
|
|
139
|
+
return response
|
|
140
|
+
|
|
141
|
+
|
|
108
142
|
@ns.route("/<post:post>/", endpoint="post")
|
|
109
143
|
@api.response(404, "Object not found")
|
|
110
144
|
@api.param("post", "The post ID or slug")
|
udata/core/reuse/api.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
+
from typing import List
|
|
2
3
|
|
|
3
4
|
import mongoengine
|
|
4
5
|
from bson.objectid import ObjectId
|
|
5
|
-
from
|
|
6
|
+
from feedgenerator.django.utils.feedgenerator import Atom1Feed
|
|
7
|
+
from flask import make_response, request
|
|
6
8
|
from flask_login import current_user
|
|
7
9
|
|
|
8
10
|
from udata.api import API, api, errors
|
|
@@ -20,6 +22,8 @@ from udata.core.storages.api import (
|
|
|
20
22
|
parse_uploaded_image,
|
|
21
23
|
uploaded_image_fields,
|
|
22
24
|
)
|
|
25
|
+
from udata.frontend.markdown import md
|
|
26
|
+
from udata.i18n import gettext as _
|
|
23
27
|
from udata.models import Dataset
|
|
24
28
|
from udata.utils import id_or_404
|
|
25
29
|
|
|
@@ -130,6 +134,43 @@ class ReuseListAPI(API):
|
|
|
130
134
|
return patch_and_save(reuse, request), 201
|
|
131
135
|
|
|
132
136
|
|
|
137
|
+
@ns.route("/recent.atom", endpoint="recent_reuses_atom_feed")
|
|
138
|
+
class ReusesAtomFeedAPI(API):
|
|
139
|
+
@api.doc("recent_reuses_atom_feed")
|
|
140
|
+
def get(self):
|
|
141
|
+
feed = Atom1Feed(
|
|
142
|
+
_("Latests reuses"),
|
|
143
|
+
description=None,
|
|
144
|
+
feed_url=request.url,
|
|
145
|
+
link=request.url_root,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
reuses: List[Reuse] = Reuse.objects.visible().order_by("-created_at").limit(15)
|
|
149
|
+
for reuse in reuses:
|
|
150
|
+
author_name = None
|
|
151
|
+
author_uri = None
|
|
152
|
+
if reuse.organization:
|
|
153
|
+
author_name = reuse.organization.name
|
|
154
|
+
author_uri = reuse.organization.external_url
|
|
155
|
+
elif reuse.owner:
|
|
156
|
+
author_name = reuse.owner.fullname
|
|
157
|
+
author_uri = reuse.owner.external_url
|
|
158
|
+
feed.add_item(
|
|
159
|
+
reuse.title,
|
|
160
|
+
unique_id=reuse.id,
|
|
161
|
+
description=reuse.description,
|
|
162
|
+
content=md(reuse.description),
|
|
163
|
+
author_name=author_name,
|
|
164
|
+
author_link=author_uri,
|
|
165
|
+
link=reuse.external_url,
|
|
166
|
+
updateddate=reuse.last_modified,
|
|
167
|
+
pubdate=reuse.created_at,
|
|
168
|
+
)
|
|
169
|
+
response = make_response(feed.writeString("utf-8"))
|
|
170
|
+
response.headers["Content-Type"] = "application/atom+xml"
|
|
171
|
+
return response
|
|
172
|
+
|
|
173
|
+
|
|
133
174
|
@ns.route("/<reuse:reuse>/", endpoint="reuse", doc=common_doc)
|
|
134
175
|
@api.response(404, "Reuse not found")
|
|
135
176
|
@api.response(410, "Reuse has been deleted")
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from flask_security import current_user
|
|
2
|
+
|
|
3
|
+
from udata.i18n import lazy_gettext as _
|
|
4
|
+
from udata.models import Activity, Topic, db
|
|
5
|
+
|
|
6
|
+
__all__ = ("UserCreatedTopic", "UserUpdatedTopic", "TopicRelatedActivity")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TopicRelatedActivity(object):
|
|
10
|
+
related_to = db.ReferenceField("Topic")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class UserCreatedTopic(TopicRelatedActivity, Activity):
|
|
14
|
+
key = "topic:created"
|
|
15
|
+
icon = "fa fa-plus"
|
|
16
|
+
badge_type = "success"
|
|
17
|
+
label = _("created a topic")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class UserUpdatedTopic(TopicRelatedActivity, Activity):
|
|
21
|
+
key = "topic:updated"
|
|
22
|
+
icon = "fa fa-pencil"
|
|
23
|
+
label = _("updated a topic")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@Topic.on_create.connect
|
|
27
|
+
def on_user_created_topic(topic):
|
|
28
|
+
if current_user and current_user.is_authenticated:
|
|
29
|
+
UserCreatedTopic.emit(topic, topic.organization)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@Topic.on_update.connect
|
|
33
|
+
def on_user_updated_topic(topic, **kwargs):
|
|
34
|
+
changed_fields = kwargs.get("changed_fields", [])
|
|
35
|
+
if current_user and current_user.is_authenticated:
|
|
36
|
+
UserUpdatedTopic.emit(topic, topic.organization, changed_fields)
|
udata/core/topic/models.py
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
from blinker import Signal
|
|
1
2
|
from flask import url_for
|
|
2
|
-
from mongoengine.signals import pre_save
|
|
3
|
+
from mongoengine.signals import post_save, pre_save
|
|
3
4
|
|
|
5
|
+
from udata.api_fields import field
|
|
6
|
+
from udata.core.activity.models import Auditable
|
|
4
7
|
from udata.core.owned import Owned, OwnedQuerySet
|
|
5
8
|
from udata.models import SpatialCoverage, db
|
|
6
9
|
from udata.search import reindex
|
|
@@ -8,24 +11,24 @@ from udata.search import reindex
|
|
|
8
11
|
__all__ = ("Topic",)
|
|
9
12
|
|
|
10
13
|
|
|
11
|
-
class Topic(db.
|
|
12
|
-
name = db.StringField(required=True)
|
|
13
|
-
slug =
|
|
14
|
-
max_length=255, required=True, populate_from="name", update=True, follow=True
|
|
14
|
+
class Topic(db.Datetimed, Auditable, db.Document, Owned):
|
|
15
|
+
name = field(db.StringField(required=True))
|
|
16
|
+
slug = field(
|
|
17
|
+
db.SlugField(max_length=255, required=True, populate_from="name", update=True, follow=True),
|
|
18
|
+
auditable=False,
|
|
15
19
|
)
|
|
16
|
-
description = db.StringField()
|
|
17
|
-
tags = db.ListField(db.StringField())
|
|
18
|
-
color = db.IntField()
|
|
20
|
+
description = field(db.StringField())
|
|
21
|
+
tags = field(db.ListField(db.StringField()))
|
|
22
|
+
color = field(db.IntField())
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
reuses = db.ListField(db.LazyReferenceField("Reuse", reverse_delete_rule=db.PULL))
|
|
24
|
+
datasets = field(db.ListField(db.LazyReferenceField("Dataset", reverse_delete_rule=db.PULL)))
|
|
25
|
+
reuses = field(db.ListField(db.LazyReferenceField("Reuse", reverse_delete_rule=db.PULL)))
|
|
23
26
|
|
|
24
|
-
featured = db.BooleanField(default=False)
|
|
25
|
-
private = db.BooleanField()
|
|
26
|
-
extras = db.ExtrasField()
|
|
27
|
+
featured = field(db.BooleanField(default=False), auditable=False)
|
|
28
|
+
private = field(db.BooleanField())
|
|
29
|
+
extras = field(db.ExtrasField(), auditable=False)
|
|
27
30
|
|
|
28
|
-
spatial = db.EmbeddedDocumentField(SpatialCoverage)
|
|
31
|
+
spatial = field(db.EmbeddedDocumentField(SpatialCoverage))
|
|
29
32
|
|
|
30
33
|
meta = {
|
|
31
34
|
"indexes": ["$name", "created_at", "slug"] + Owned.meta["indexes"],
|
|
@@ -34,6 +37,10 @@ class Topic(db.Document, Owned, db.Datetimed):
|
|
|
34
37
|
"queryset_class": OwnedQuerySet,
|
|
35
38
|
}
|
|
36
39
|
|
|
40
|
+
after_save = Signal()
|
|
41
|
+
on_create = Signal()
|
|
42
|
+
on_update = Signal()
|
|
43
|
+
|
|
37
44
|
def __str__(self):
|
|
38
45
|
return self.name
|
|
39
46
|
|
|
@@ -60,3 +67,4 @@ class Topic(db.Document, Owned, db.Datetimed):
|
|
|
60
67
|
|
|
61
68
|
|
|
62
69
|
pre_save.connect(Topic.pre_save, sender=Topic)
|
|
70
|
+
post_save.connect(Topic.post_save, sender=Topic)
|