udata 9.1.2.dev30754__py2.py3-none-any.whl → 9.1.4.dev30983__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.
- tasks/helpers.py +6 -5
- udata/__init__.py +1 -1
- udata/api/__init__.py +2 -3
- udata/api/commands.py +0 -1
- udata/api/fields.py +22 -1
- udata/api_fields.py +22 -12
- udata/app.py +1 -1
- udata/assets.py +1 -1
- udata/auth/__init__.py +8 -12
- udata/commands/db.py +3 -3
- udata/commands/dcat.py +1 -1
- udata/commands/fixtures.py +110 -54
- udata/commands/init.py +2 -2
- udata/commands/tests/test_fixtures.py +71 -0
- udata/core/activity/tasks.py +1 -1
- udata/core/badges/models.py +0 -2
- udata/core/contact_point/api.py +1 -3
- udata/core/dataservices/tasks.py +1 -1
- udata/core/dataset/actions.py +2 -2
- udata/core/dataset/forms.py +0 -2
- udata/core/dataset/models.py +12 -10
- udata/core/dataset/rdf.py +1 -1
- udata/core/discussions/api.py +1 -1
- udata/core/discussions/models.py +2 -2
- udata/core/discussions/tasks.py +1 -1
- udata/core/organization/api.py +3 -4
- udata/core/organization/api_fields.py +2 -1
- udata/core/organization/apiv2.py +1 -1
- udata/core/reports/api.py +11 -0
- udata/core/reports/models.py +6 -1
- udata/core/reuse/metrics.py +1 -1
- udata/core/reuse/permissions.py +2 -1
- udata/core/reuse/search.py +2 -2
- udata/core/reuse/tasks.py +2 -2
- udata/core/spatial/commands.py +3 -3
- udata/core/spatial/factories.py +1 -1
- udata/core/spatial/forms.py +1 -1
- udata/core/spatial/models.py +2 -2
- udata/core/spatial/tests/test_models.py +1 -1
- udata/core/spatial/translations.py +3 -1
- udata/core/user/api.py +4 -4
- udata/core/user/metrics.py +1 -1
- udata/frontend/__init__.py +1 -1
- udata/harvest/actions.py +1 -1
- udata/harvest/backends/__init__.py +1 -1
- udata/harvest/tasks.py +0 -1
- udata/harvest/tests/factories.py +0 -2
- udata/harvest/tests/test_base_backend.py +0 -1
- udata/harvest/tests/test_dcat_backend.py +16 -17
- udata/migrations/2020-07-24-remove-s-from-scope-oauth.py +1 -1
- udata/migrations/2021-07-05-remove-unused-badges.py +0 -1
- udata/migrations/2023-02-08-rename-internal-dates.py +0 -2
- udata/migrations/2024-06-11-fix-reuse-datasets-references.py +0 -1
- udata/models/__init__.py +1 -0
- udata/routing.py +5 -0
- udata/search/commands.py +1 -1
- udata/search/query.py +1 -1
- udata/sentry.py +11 -2
- udata/settings.py +1 -0
- udata/tags.py +3 -3
- udata/tests/api/test_base_api.py +1 -1
- udata/tests/api/test_contact_points.py +4 -4
- udata/tests/api/test_datasets_api.py +10 -10
- udata/tests/api/test_organizations_api.py +39 -39
- udata/tests/api/test_reports_api.py +53 -3
- udata/tests/api/test_tags_api.py +2 -2
- udata/tests/api/test_transfer_api.py +1 -1
- udata/tests/apiv2/test_datasets.py +4 -4
- udata/tests/dataset/test_dataset_model.py +3 -3
- udata/tests/frontend/__init__.py +0 -2
- udata/tests/frontend/test_auth.py +0 -1
- udata/tests/organization/test_csv_adapter.py +0 -2
- udata/tests/organization/test_notifications.py +3 -3
- udata/tests/reuse/test_reuse_model.py +0 -1
- udata/tests/site/test_site_rdf.py +1 -3
- udata/tests/test_cors.py +0 -3
- udata/tests/test_owned.py +4 -4
- udata/tests/test_routing.py +1 -1
- udata/tests/test_tags.py +1 -1
- udata/tests/test_transfer.py +1 -2
- udata/tests/workers/test_jobs_commands.py +1 -1
- udata/utils.py +15 -14
- {udata-9.1.2.dev30754.dist-info → udata-9.1.4.dev30983.dist-info}/METADATA +20 -4
- {udata-9.1.2.dev30754.dist-info → udata-9.1.4.dev30983.dist-info}/RECORD +88 -89
- udata/commands/tests/fixtures.py +0 -44
- udata/tests/features/territories/commands.py +0 -9
- {udata-9.1.2.dev30754.dist-info → udata-9.1.4.dev30983.dist-info}/LICENSE +0 -0
- {udata-9.1.2.dev30754.dist-info → udata-9.1.4.dev30983.dist-info}/WHEEL +0 -0
- {udata-9.1.2.dev30754.dist-info → udata-9.1.4.dev30983.dist-info}/entry_points.txt +0 -0
- {udata-9.1.2.dev30754.dist-info → udata-9.1.4.dev30983.dist-info}/top_level.txt +0 -0
udata/core/dataset/actions.py
CHANGED
|
@@ -4,12 +4,12 @@ from datetime import datetime
|
|
|
4
4
|
from flask import current_app, render_template
|
|
5
5
|
|
|
6
6
|
from udata import i18n
|
|
7
|
-
from udata.models import Discussion, Message
|
|
7
|
+
from udata.models import Dataset, Discussion, Message
|
|
8
8
|
|
|
9
9
|
log = logging.getLogger(__name__)
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
def archive(dataset, comment=False):
|
|
12
|
+
def archive(dataset: Dataset, comment=False) -> None:
|
|
13
13
|
"""Archive a dataset"""
|
|
14
14
|
if dataset.archived:
|
|
15
15
|
log.warning("Dataset %s already archived, bumping date", dataset)
|
udata/core/dataset/forms.py
CHANGED
udata/core/dataset/models.py
CHANGED
|
@@ -256,8 +256,8 @@ class License(db.Document):
|
|
|
256
256
|
|
|
257
257
|
if license is None:
|
|
258
258
|
# Try to single match `slug` with a low Damerau-Levenshtein distance
|
|
259
|
-
computed = ((
|
|
260
|
-
candidates = [
|
|
259
|
+
computed = ((license_, rdlevenshtein(license_.slug, slug)) for license_ in cls.objects)
|
|
260
|
+
candidates = [license_ for license_, d in computed if d <= MAX_DISTANCE]
|
|
261
261
|
# If there is more that one match, we cannot determinate
|
|
262
262
|
# which one is closer to safely choose between candidates
|
|
263
263
|
if len(candidates) == 1:
|
|
@@ -265,8 +265,10 @@ class License(db.Document):
|
|
|
265
265
|
|
|
266
266
|
if license is None:
|
|
267
267
|
# Try to match `title` with a low Damerau-Levenshtein distance
|
|
268
|
-
computed = (
|
|
269
|
-
|
|
268
|
+
computed = (
|
|
269
|
+
(license_, rdlevenshtein(license_.title.lower(), text)) for license_ in cls.objects
|
|
270
|
+
)
|
|
271
|
+
candidates = [license_ for license_, d in computed if d <= MAX_DISTANCE]
|
|
270
272
|
# If there is more that one match, we cannot determinate
|
|
271
273
|
# which one is closer to safely choose between candidates
|
|
272
274
|
if len(candidates) == 1:
|
|
@@ -275,11 +277,11 @@ class License(db.Document):
|
|
|
275
277
|
if license is None:
|
|
276
278
|
# Try to single match `alternate_titles` with a low Damerau-Levenshtein distance
|
|
277
279
|
computed = (
|
|
278
|
-
(
|
|
279
|
-
for
|
|
280
|
-
for
|
|
280
|
+
(license_, rdlevenshtein(cls.slug.slugify(title_), slug))
|
|
281
|
+
for license_ in cls.objects
|
|
282
|
+
for title_ in license_.alternate_titles
|
|
281
283
|
)
|
|
282
|
-
candidates = [
|
|
284
|
+
candidates = [license_ for license_, distance_ in computed if distance_ <= MAX_DISTANCE]
|
|
283
285
|
# If there is more that one license matching, we cannot determinate
|
|
284
286
|
# which one is closer to safely choose between candidates
|
|
285
287
|
if len(set(candidates)) == 1:
|
|
@@ -998,7 +1000,7 @@ class ResourceSchema(object):
|
|
|
998
1000
|
f"Schemas catalog does not exist at {endpoint}"
|
|
999
1001
|
)
|
|
1000
1002
|
response.raise_for_status()
|
|
1001
|
-
except requests.exceptions.RequestException
|
|
1003
|
+
except requests.exceptions.RequestException:
|
|
1002
1004
|
log.exception(f"Error while getting schema catalog from {endpoint}")
|
|
1003
1005
|
schemas = cache.get(cache_key)
|
|
1004
1006
|
else:
|
|
@@ -1006,7 +1008,7 @@ class ResourceSchema(object):
|
|
|
1006
1008
|
cache.set(cache_key, schemas)
|
|
1007
1009
|
# no cached version or no content
|
|
1008
1010
|
if not schemas:
|
|
1009
|
-
log.error(
|
|
1011
|
+
log.error("No content found inc. from cache for schema catalog")
|
|
1010
1012
|
raise SchemasCacheUnavailableException("No content in cache for schema catalog")
|
|
1011
1013
|
|
|
1012
1014
|
return schemas
|
udata/core/dataset/rdf.py
CHANGED
|
@@ -387,7 +387,7 @@ def spatial_from_rdf(graph):
|
|
|
387
387
|
continue
|
|
388
388
|
|
|
389
389
|
if not polygons:
|
|
390
|
-
log.warning(
|
|
390
|
+
log.warning("No supported types found in the GeoJSON data.")
|
|
391
391
|
return None
|
|
392
392
|
|
|
393
393
|
spatial_coverage = SpatialCoverage(
|
udata/core/discussions/api.py
CHANGED
|
@@ -152,7 +152,7 @@ class DiscussionAPI(API):
|
|
|
152
152
|
|
|
153
153
|
@ns.route("/<id>/comments/<int:cidx>/spam", endpoint="discussion_comment_spam")
|
|
154
154
|
@ns.doc(delete={"id": "unspam"})
|
|
155
|
-
class
|
|
155
|
+
class DiscussionCommentSpamAPI(SpamAPIMixin):
|
|
156
156
|
def get_model(self, id, cidx):
|
|
157
157
|
discussion = Discussion.objects.get_or_404(id=id_or_404(id))
|
|
158
158
|
if len(discussion.discussion) <= cidx:
|
udata/core/discussions/models.py
CHANGED
|
@@ -20,7 +20,7 @@ class Message(SpamMixin, db.EmbeddedDocument):
|
|
|
20
20
|
return [self.content]
|
|
21
21
|
|
|
22
22
|
def spam_report_message(self, breadcrumb):
|
|
23
|
-
message =
|
|
23
|
+
message = "Spam potentiel dans le message"
|
|
24
24
|
if self.posted_by:
|
|
25
25
|
message += f" de [{self.posted_by.fullname}]({self.posted_by.external_url})"
|
|
26
26
|
|
|
@@ -34,7 +34,7 @@ class Message(SpamMixin, db.EmbeddedDocument):
|
|
|
34
34
|
discussion = breadcrumb[0]
|
|
35
35
|
if not isinstance(discussion, Discussion):
|
|
36
36
|
log.warning(
|
|
37
|
-
|
|
37
|
+
"`spam_report_message` called on message with a breadcrumb not containing a Discussion at index 0.",
|
|
38
38
|
extra={"breadcrumb": breadcrumb},
|
|
39
39
|
)
|
|
40
40
|
return message
|
udata/core/discussions/tasks.py
CHANGED
|
@@ -5,7 +5,7 @@ from udata.core.reuse.models import Reuse
|
|
|
5
5
|
from udata.i18n import lazy_gettext as _
|
|
6
6
|
from udata.tasks import connect, get_logger
|
|
7
7
|
|
|
8
|
-
from .models import Discussion
|
|
8
|
+
from .models import Discussion
|
|
9
9
|
from .signals import on_discussion_closed, on_new_discussion, on_new_discussion_comment
|
|
10
10
|
|
|
11
11
|
log = get_logger(__name__)
|
udata/core/organization/api.py
CHANGED
|
@@ -8,6 +8,8 @@ from udata.api.parsers import ModelApiParser
|
|
|
8
8
|
from udata.auth import admin_permission, current_user
|
|
9
9
|
from udata.core.badges import api as badges_api
|
|
10
10
|
from udata.core.badges.fields import badge_fields
|
|
11
|
+
from udata.core.contact_point.api import ContactPointApiParser
|
|
12
|
+
from udata.core.contact_point.api_fields import contact_point_page_fields
|
|
11
13
|
from udata.core.dataset.api import DatasetApiParser
|
|
12
14
|
from udata.core.dataset.api_fields import dataset_page_fields
|
|
13
15
|
from udata.core.dataset.models import Dataset
|
|
@@ -21,6 +23,7 @@ from udata.core.storages.api import (
|
|
|
21
23
|
parse_uploaded_image,
|
|
22
24
|
uploaded_image_fields,
|
|
23
25
|
)
|
|
26
|
+
from udata.models import ContactPoint
|
|
24
27
|
from udata.rdf import RDF_EXTENSIONS, graph_response, negociate_content
|
|
25
28
|
from udata.utils import multi_to_dict
|
|
26
29
|
|
|
@@ -207,10 +210,6 @@ class OrganizationBadgeAPI(API):
|
|
|
207
210
|
return badges_api.remove(org, badge_kind)
|
|
208
211
|
|
|
209
212
|
|
|
210
|
-
from udata.core.contact_point.api import ContactPointApiParser
|
|
211
|
-
from udata.core.contact_point.api_fields import contact_point_page_fields
|
|
212
|
-
from udata.models import ContactPoint
|
|
213
|
-
|
|
214
213
|
contact_point_parser = ContactPointApiParser()
|
|
215
214
|
|
|
216
215
|
|
|
@@ -41,7 +41,8 @@ org_ref_fields = api.inherit(
|
|
|
41
41
|
},
|
|
42
42
|
)
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
# This import is not at the top of the file to avoid circular imports
|
|
45
|
+
from udata.core.user.api_fields import user_ref_fields # noqa
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
def check_can_access_email():
|
udata/core/organization/apiv2.py
CHANGED
|
@@ -7,7 +7,7 @@ from udata.core.contact_point.api_fields import contact_point_fields
|
|
|
7
7
|
from udata.utils import multi_to_dict
|
|
8
8
|
|
|
9
9
|
from .api_fields import member_fields, org_fields, org_page_fields
|
|
10
|
-
from .permissions import EditOrganizationPermission
|
|
10
|
+
from .permissions import EditOrganizationPermission
|
|
11
11
|
from .search import OrganizationSearch
|
|
12
12
|
|
|
13
13
|
apiv2.inherit("OrganizationPage", org_page_fields)
|
udata/core/reports/api.py
CHANGED
|
@@ -4,6 +4,7 @@ from flask_login import current_user
|
|
|
4
4
|
|
|
5
5
|
from udata.api import API, api, fields
|
|
6
6
|
from udata.api_fields import patch
|
|
7
|
+
from udata.auth import admin_permission
|
|
7
8
|
|
|
8
9
|
from .constants import reports_reasons_translations
|
|
9
10
|
from .models import Report
|
|
@@ -16,6 +17,7 @@ class ReportsAPI(API):
|
|
|
16
17
|
@api.doc("list_reports")
|
|
17
18
|
@api.expect(Report.__index_parser__)
|
|
18
19
|
@api.marshal_with(Report.__page_fields__)
|
|
20
|
+
@api.secure(admin_permission)
|
|
19
21
|
def get(self):
|
|
20
22
|
query = Report.objects
|
|
21
23
|
|
|
@@ -37,6 +39,15 @@ class ReportsAPI(API):
|
|
|
37
39
|
return report, 201
|
|
38
40
|
|
|
39
41
|
|
|
42
|
+
@ns.route("/<report:report>/", endpoint="report")
|
|
43
|
+
class ReportAPI(API):
|
|
44
|
+
@api.doc("get_report")
|
|
45
|
+
@api.marshal_with(Report.__read_fields__)
|
|
46
|
+
@api.secure(admin_permission)
|
|
47
|
+
def get(self, report):
|
|
48
|
+
return report
|
|
49
|
+
|
|
50
|
+
|
|
40
51
|
@ns.route("/reasons/", endpoint="reports_reasons")
|
|
41
52
|
class ReportsReasonsAPI(API):
|
|
42
53
|
@api.doc("list_reports_reasons")
|
udata/core/reports/models.py
CHANGED
|
@@ -3,10 +3,11 @@ from datetime import datetime
|
|
|
3
3
|
from bson import DBRef
|
|
4
4
|
from mongoengine import DO_NOTHING, NULLIFY, signals
|
|
5
5
|
|
|
6
|
-
from udata.api_fields import field, generate_fields
|
|
6
|
+
from udata.api_fields import field, function_field, generate_fields
|
|
7
7
|
from udata.core.user.api_fields import user_ref_fields
|
|
8
8
|
from udata.core.user.models import User
|
|
9
9
|
from udata.mongo import db
|
|
10
|
+
from udata.uris import endpoint_for
|
|
10
11
|
|
|
11
12
|
from .constants import REPORT_REASONS_CHOICES, REPORTABLE_MODELS
|
|
12
13
|
|
|
@@ -45,6 +46,10 @@ class Report(db.Document):
|
|
|
45
46
|
readonly=True,
|
|
46
47
|
)
|
|
47
48
|
|
|
49
|
+
@function_field(description="Link to the API endpoint for this report")
|
|
50
|
+
def self_api_url(self):
|
|
51
|
+
return endpoint_for("api.report", report=self, _external=True)
|
|
52
|
+
|
|
48
53
|
@classmethod
|
|
49
54
|
def mark_as_deleted_soft_delete(cls, sender, document, **kwargs):
|
|
50
55
|
"""
|
udata/core/reuse/metrics.py
CHANGED
udata/core/reuse/permissions.py
CHANGED
|
@@ -3,10 +3,11 @@ from udata.core.organization.permissions import (
|
|
|
3
3
|
OrganizationAdminNeed,
|
|
4
4
|
OrganizationEditorNeed,
|
|
5
5
|
)
|
|
6
|
+
from udata.core.reuse.models import Reuse
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class ReuseEditPermission(Permission):
|
|
9
|
-
def __init__(self, reuse):
|
|
10
|
+
def __init__(self, reuse: Reuse) -> None:
|
|
10
11
|
needs = []
|
|
11
12
|
|
|
12
13
|
if reuse.organization:
|
udata/core/reuse/search.py
CHANGED
|
@@ -38,7 +38,7 @@ class ReuseSearch(ModelSearchAdapter):
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
@classmethod
|
|
41
|
-
def is_indexable(cls, reuse):
|
|
41
|
+
def is_indexable(cls, reuse: Reuse) -> bool:
|
|
42
42
|
return reuse.deleted is None and len(reuse.datasets) > 0 and not reuse.private
|
|
43
43
|
|
|
44
44
|
@classmethod
|
|
@@ -55,7 +55,7 @@ class ReuseSearch(ModelSearchAdapter):
|
|
|
55
55
|
return reuses.order_by(sort).skip(offset).limit(args["page_size"]), reuses.count()
|
|
56
56
|
|
|
57
57
|
@classmethod
|
|
58
|
-
def serialize(cls, reuse):
|
|
58
|
+
def serialize(cls, reuse: Reuse) -> dict:
|
|
59
59
|
organization = None
|
|
60
60
|
owner = None
|
|
61
61
|
if reuse.organization:
|
udata/core/reuse/tasks.py
CHANGED
|
@@ -10,7 +10,7 @@ log = get_logger(__name__)
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
@job("purge-reuses")
|
|
13
|
-
def purge_reuses(self):
|
|
13
|
+
def purge_reuses(self) -> None:
|
|
14
14
|
for reuse in Reuse.objects(deleted__ne=None):
|
|
15
15
|
log.info(f"Purging reuse {reuse}")
|
|
16
16
|
# Remove followers
|
|
@@ -32,7 +32,7 @@ def purge_reuses(self):
|
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
@task
|
|
35
|
-
def notify_new_reuse(reuse_id):
|
|
35
|
+
def notify_new_reuse(reuse_id: int) -> None:
|
|
36
36
|
reuse = Reuse.objects.get(pk=reuse_id)
|
|
37
37
|
for dataset in reuse.datasets:
|
|
38
38
|
if dataset.organization:
|
udata/core/spatial/commands.py
CHANGED
|
@@ -177,8 +177,8 @@ def migrate():
|
|
|
177
177
|
|
|
178
178
|
level_summary = "\n".join(
|
|
179
179
|
[
|
|
180
|
-
" - {0}: {1}".format(
|
|
181
|
-
for
|
|
180
|
+
" - {0}: {1}".format(geolevel.id, counter[geolevel.id])
|
|
181
|
+
for geolevel in GeoLevel.objects.order_by("admin_level")
|
|
182
182
|
]
|
|
183
183
|
)
|
|
184
184
|
summary = "\n".join(
|
|
@@ -188,7 +188,7 @@ def migrate():
|
|
|
188
188
|
Summary
|
|
189
189
|
=======
|
|
190
190
|
Processed {zones} zones in {datasets} datasets:\
|
|
191
|
-
""".format(
|
|
191
|
+
""".format(**counter)
|
|
192
192
|
),
|
|
193
193
|
level_summary,
|
|
194
194
|
]
|
udata/core/spatial/factories.py
CHANGED
|
@@ -2,7 +2,7 @@ import factory
|
|
|
2
2
|
from faker.providers import BaseProvider
|
|
3
3
|
from geojson.utils import generate_random
|
|
4
4
|
|
|
5
|
-
from udata.factories import
|
|
5
|
+
from udata.factories import ModelFactory
|
|
6
6
|
from udata.utils import faker_provider
|
|
7
7
|
|
|
8
8
|
from . import geoids
|
udata/core/spatial/forms.py
CHANGED
|
@@ -62,7 +62,7 @@ class GeomField(Field):
|
|
|
62
62
|
self.data = geojson.loads(value)
|
|
63
63
|
else:
|
|
64
64
|
self.data = geojson.GeoJSON.to_instance(value)
|
|
65
|
-
except:
|
|
65
|
+
except Exception:
|
|
66
66
|
self.data = None
|
|
67
67
|
log.exception("Unable to parse GeoJSON")
|
|
68
68
|
raise ValueError(self.gettext("Not a valid GeoJSON"))
|
udata/core/spatial/models.py
CHANGED
|
@@ -120,7 +120,7 @@ class GeoZone(WithMetrics, db.Document):
|
|
|
120
120
|
@cache.memoize()
|
|
121
121
|
def get_spatial_granularities(lang):
|
|
122
122
|
with language(lang):
|
|
123
|
-
return [(
|
|
123
|
+
return [(geolevel.id, _(geolevel.name)) for geolevel in GeoLevel.objects] + [
|
|
124
124
|
(id, str(label)) for id, label in BASE_GRANULARITIES
|
|
125
125
|
]
|
|
126
126
|
|
|
@@ -130,7 +130,7 @@ spatial_granularities = LocalProxy(lambda: get_spatial_granularities(get_locale(
|
|
|
130
130
|
|
|
131
131
|
@cache.cached(timeout=50, key_prefix="admin_levels")
|
|
132
132
|
def get_spatial_admin_levels():
|
|
133
|
-
return dict((
|
|
133
|
+
return dict((geolevel.id, geolevel.admin_level) for geolevel in GeoLevel.objects)
|
|
134
134
|
|
|
135
135
|
|
|
136
136
|
admin_levels = LocalProxy(get_spatial_admin_levels)
|
udata/core/user/api.py
CHANGED
|
@@ -4,7 +4,6 @@ from slugify import slugify
|
|
|
4
4
|
from udata.api import API, api
|
|
5
5
|
from udata.api.parsers import ModelApiParser
|
|
6
6
|
from udata.auth import admin_permission
|
|
7
|
-
from udata.core import storages
|
|
8
7
|
from udata.core.dataset.api_fields import community_resource_fields, dataset_fields
|
|
9
8
|
from udata.core.discussions.actions import discussions_for
|
|
10
9
|
from udata.core.discussions.api import discussion_fields
|
|
@@ -321,9 +320,10 @@ class UserAPI(API):
|
|
|
321
320
|
return "", 204
|
|
322
321
|
|
|
323
322
|
|
|
324
|
-
|
|
325
|
-
from udata.core.contact_point.
|
|
326
|
-
from udata.
|
|
323
|
+
# These imports are not at the top of the file to avoid circular imports
|
|
324
|
+
from udata.core.contact_point.api import ContactPointApiParser # noqa
|
|
325
|
+
from udata.core.contact_point.api_fields import contact_point_page_fields # noqa
|
|
326
|
+
from udata.models import ContactPoint # noqa
|
|
327
327
|
|
|
328
328
|
contact_point_parser = ContactPointApiParser()
|
|
329
329
|
|
udata/core/user/metrics.py
CHANGED
udata/frontend/__init__.py
CHANGED
udata/harvest/actions.py
CHANGED
|
@@ -177,7 +177,7 @@ def purge_jobs():
|
|
|
177
177
|
bucket = current_app.config.get("HARVEST_GRAPHS_S3_BUCKET")
|
|
178
178
|
if bucket is None:
|
|
179
179
|
log.error(
|
|
180
|
-
|
|
180
|
+
"Bucket isn't configured anymore, but jobs still exist with external filenames. Could not delete them."
|
|
181
181
|
)
|
|
182
182
|
break
|
|
183
183
|
|
udata/harvest/tasks.py
CHANGED
udata/harvest/tests/factories.py
CHANGED
|
@@ -3,8 +3,6 @@ import pytest
|
|
|
3
3
|
from factory.fuzzy import FuzzyChoice
|
|
4
4
|
from flask.signals import Namespace
|
|
5
5
|
|
|
6
|
-
from udata.core.dataset.factories import DatasetFactory
|
|
7
|
-
from udata.core.dataset.models import Dataset
|
|
8
6
|
from udata.factories import ModelFactory
|
|
9
7
|
|
|
10
8
|
from .. import backends
|
|
@@ -4,7 +4,6 @@ import re
|
|
|
4
4
|
import xml.etree.ElementTree as ET
|
|
5
5
|
from datetime import date
|
|
6
6
|
|
|
7
|
-
import boto3
|
|
8
7
|
import pytest
|
|
9
8
|
from flask import current_app
|
|
10
9
|
|
|
@@ -245,7 +244,7 @@ class DcatBackendTest:
|
|
|
245
244
|
|
|
246
245
|
datasets = {d.harvest.dct_identifier: d for d in Dataset.objects}
|
|
247
246
|
|
|
248
|
-
assert datasets["1"].schema
|
|
247
|
+
assert datasets["1"].schema is None
|
|
249
248
|
resources_by_title = {resource["title"]: resource for resource in datasets["1"].resources}
|
|
250
249
|
|
|
251
250
|
# Schema with wrong version are considered as external. Maybe we could change this in the future
|
|
@@ -253,8 +252,8 @@ class DcatBackendTest:
|
|
|
253
252
|
resources_by_title["Resource 1-2"].schema.url
|
|
254
253
|
== "https://schema.data.gouv.fr/schemas/etalab/schema-irve-statique/1337.42.0/schema-statique.json"
|
|
255
254
|
)
|
|
256
|
-
assert resources_by_title["Resource 1-2"].schema.name
|
|
257
|
-
assert resources_by_title["Resource 1-2"].schema.version
|
|
255
|
+
assert resources_by_title["Resource 1-2"].schema.name is None
|
|
256
|
+
assert resources_by_title["Resource 1-2"].schema.version is None
|
|
258
257
|
|
|
259
258
|
assert datasets["2"].schema.name == "RGF93 / Lambert-93 (EPSG:2154)"
|
|
260
259
|
assert (
|
|
@@ -266,17 +265,17 @@ class DcatBackendTest:
|
|
|
266
265
|
# Unknown schema are kept as they were provided
|
|
267
266
|
assert resources_by_title["Resource 2-1"].schema.name == "Example Schema"
|
|
268
267
|
assert resources_by_title["Resource 2-1"].schema.url == "https://example.org/schema.json"
|
|
269
|
-
assert resources_by_title["Resource 2-1"].schema.version
|
|
268
|
+
assert resources_by_title["Resource 2-1"].schema.version is None
|
|
270
269
|
|
|
271
|
-
assert resources_by_title["Resource 2-2"].schema
|
|
270
|
+
assert resources_by_title["Resource 2-2"].schema is None
|
|
272
271
|
|
|
273
|
-
assert datasets["3"].schema
|
|
272
|
+
assert datasets["3"].schema is None
|
|
274
273
|
resources_by_title = {resource["title"]: resource for resource in datasets["3"].resources}
|
|
275
274
|
|
|
276
275
|
# If there is just the URL, and it matches a known schema inside the catalog, only set the name and the version
|
|
277
276
|
# (discard the URL)
|
|
278
277
|
assert resources_by_title["Resource 3-1"].schema.name == "etalab/schema-irve-statique"
|
|
279
|
-
assert resources_by_title["Resource 3-1"].schema.url
|
|
278
|
+
assert resources_by_title["Resource 3-1"].schema.url is None
|
|
280
279
|
assert resources_by_title["Resource 3-1"].schema.version == "2.2.0"
|
|
281
280
|
|
|
282
281
|
job = HarvestJob.objects.order_by("-id").first()
|
|
@@ -321,7 +320,7 @@ class DcatBackendTest:
|
|
|
321
320
|
|
|
322
321
|
datasets = {d.harvest.dct_identifier: d for d in Dataset.objects}
|
|
323
322
|
|
|
324
|
-
assert datasets["1"].spatial
|
|
323
|
+
assert datasets["1"].spatial is None
|
|
325
324
|
assert datasets["2"].spatial.geom == {
|
|
326
325
|
"type": "MultiPolygon",
|
|
327
326
|
"coordinates": [
|
|
@@ -330,7 +329,7 @@ class DcatBackendTest:
|
|
|
330
329
|
[[[159, -25.0], [159, -11], [212, -11], [212, -25.0], [159, -25.0]]],
|
|
331
330
|
],
|
|
332
331
|
}
|
|
333
|
-
assert datasets["3"].spatial
|
|
332
|
+
assert datasets["3"].spatial is None
|
|
334
333
|
|
|
335
334
|
@pytest.mark.options(SCHEMA_CATALOG_URL="https://example.com/schemas")
|
|
336
335
|
def test_harvest_schemas(self, rmock):
|
|
@@ -345,7 +344,7 @@ class DcatBackendTest:
|
|
|
345
344
|
|
|
346
345
|
datasets = {d.harvest.dct_identifier: d for d in Dataset.objects}
|
|
347
346
|
|
|
348
|
-
assert datasets["1"].schema
|
|
347
|
+
assert datasets["1"].schema is None
|
|
349
348
|
resources_by_title = {resource["title"]: resource for resource in datasets["1"].resources}
|
|
350
349
|
|
|
351
350
|
# Schema with wrong version are considered as external. Maybe we could change this in the future
|
|
@@ -353,8 +352,8 @@ class DcatBackendTest:
|
|
|
353
352
|
resources_by_title["Resource 1-2"].schema.url
|
|
354
353
|
== "https://schema.data.gouv.fr/schemas/etalab/schema-irve-statique/1337.42.0/schema-statique.json"
|
|
355
354
|
)
|
|
356
|
-
assert resources_by_title["Resource 1-2"].schema.name
|
|
357
|
-
assert resources_by_title["Resource 1-2"].schema.version
|
|
355
|
+
assert resources_by_title["Resource 1-2"].schema.name is None
|
|
356
|
+
assert resources_by_title["Resource 1-2"].schema.version is None
|
|
358
357
|
|
|
359
358
|
assert datasets["2"].schema.name == "RGF93 / Lambert-93 (EPSG:2154)"
|
|
360
359
|
assert (
|
|
@@ -366,17 +365,17 @@ class DcatBackendTest:
|
|
|
366
365
|
# Unknown schema are kept as they were provided
|
|
367
366
|
assert resources_by_title["Resource 2-1"].schema.name == "Example Schema"
|
|
368
367
|
assert resources_by_title["Resource 2-1"].schema.url == "https://example.org/schema.json"
|
|
369
|
-
assert resources_by_title["Resource 2-1"].schema.version
|
|
368
|
+
assert resources_by_title["Resource 2-1"].schema.version is None
|
|
370
369
|
|
|
371
|
-
assert resources_by_title["Resource 2-2"].schema
|
|
370
|
+
assert resources_by_title["Resource 2-2"].schema is None
|
|
372
371
|
|
|
373
|
-
assert datasets["3"].schema
|
|
372
|
+
assert datasets["3"].schema is None
|
|
374
373
|
resources_by_title = {resource["title"]: resource for resource in datasets["3"].resources}
|
|
375
374
|
|
|
376
375
|
# If there is just the URL, and it matches a known schema inside the catalog, only set the name and the version
|
|
377
376
|
# (discard the URL)
|
|
378
377
|
assert resources_by_title["Resource 3-1"].schema.name == "etalab/schema-irve-statique"
|
|
379
|
-
assert resources_by_title["Resource 3-1"].schema.url
|
|
378
|
+
assert resources_by_title["Resource 3-1"].schema.url is None
|
|
380
379
|
assert resources_by_title["Resource 3-1"].schema.version == "2.2.0"
|
|
381
380
|
|
|
382
381
|
def test_simple_nested_attributes(self, rmock):
|
|
@@ -18,7 +18,7 @@ def migrate(db):
|
|
|
18
18
|
oauth_clients = db.oauth2_client
|
|
19
19
|
oauth_clients.update_many({}, {"$rename": {"scopes": "scope"}})
|
|
20
20
|
for client in oauth_clients.find():
|
|
21
|
-
if type(client["scope"])
|
|
21
|
+
if type(client["scope"]) is list:
|
|
22
22
|
scope_str = " ".join(client["scope"])
|
|
23
23
|
client["scope"] = scope_str
|
|
24
24
|
oauth_clients.save(client)
|
udata/models/__init__.py
CHANGED
|
@@ -22,6 +22,7 @@ from udata.core.jobs.models import * # noqa
|
|
|
22
22
|
from udata.core.tags.models import * # noqa
|
|
23
23
|
from udata.core.spam.models import * # noqa
|
|
24
24
|
from udata.core.reports.models import * # noqa
|
|
25
|
+
from udata.core.dataservices.models import * # noqa
|
|
25
26
|
|
|
26
27
|
from udata.features.transfer.models import * # noqa
|
|
27
28
|
from udata.features.territories.models import * # noqa
|
udata/routing.py
CHANGED
|
@@ -153,6 +153,10 @@ class ContactPointConverter(ModelConverter):
|
|
|
153
153
|
model = models.ContactPoint
|
|
154
154
|
|
|
155
155
|
|
|
156
|
+
class ReportConverter(ModelConverter):
|
|
157
|
+
model = models.Report
|
|
158
|
+
|
|
159
|
+
|
|
156
160
|
class TerritoryConverter(PathConverter):
|
|
157
161
|
DEFAULT_PREFIX = "fr" # TODO: make it a setting parameter
|
|
158
162
|
|
|
@@ -231,3 +235,4 @@ def init_app(app):
|
|
|
231
235
|
app.url_map.converters["post"] = PostConverter
|
|
232
236
|
app.url_map.converters["territory"] = TerritoryConverter
|
|
233
237
|
app.url_map.converters["contact_point"] = ContactPointConverter
|
|
238
|
+
app.url_map.converters["report"] = ReportConverter
|
udata/search/commands.py
CHANGED
|
@@ -93,7 +93,7 @@ def finalize_reindex(models, start):
|
|
|
93
93
|
r = requests.post(url, json=payload)
|
|
94
94
|
r.raise_for_status()
|
|
95
95
|
except Exception:
|
|
96
|
-
log.exception(
|
|
96
|
+
log.exception("Unable to set alias for index")
|
|
97
97
|
|
|
98
98
|
modified_since_reindex = 0
|
|
99
99
|
for adapter in iter_adapters():
|