udata 10.1.5.dev34548__py2.py3-none-any.whl → 10.1.5.dev34612__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/__init__.py +6 -9
- udata/auth/forms.py +9 -9
- udata/commands/serve.py +2 -4
- udata/core/badges/fields.py +1 -1
- udata/core/dataset/models.py +7 -4
- udata/core/discussions/api.py +1 -1
- udata/core/spatial/models.py +4 -6
- udata/forms/fields.py +8 -8
- udata/frontend/markdown.py +8 -9
- udata/harvest/tests/test_base_backend.py +2 -2
- udata/harvest/tests/test_dcat_backend.py +1 -1
- udata/harvest/tests/test_filters.py +1 -1
- udata/i18n.py +1 -1
- udata/mongo/taglist_field.py +1 -1
- udata/static/chunks/{11.83535504cd650ea08f65.js → 11.b6f741fcc366abfad9c4.js} +3 -3
- udata/static/chunks/{11.83535504cd650ea08f65.js.map → 11.b6f741fcc366abfad9c4.js.map} +1 -1
- udata/static/chunks/{13.39e106d56f794ebd06a0.js → 13.2d06442dd9a05d9777b5.js} +2 -2
- udata/static/chunks/{13.39e106d56f794ebd06a0.js.map → 13.2d06442dd9a05d9777b5.js.map} +1 -1
- udata/static/chunks/{17.70cbb4a91b002338007e.js → 17.e8e4caaad5cb0cc0bacc.js} +2 -2
- udata/static/chunks/{17.70cbb4a91b002338007e.js.map → 17.e8e4caaad5cb0cc0bacc.js.map} +1 -1
- udata/static/chunks/{19.df16abde17a42033a7f8.js → 19.f03a102365af4315f9db.js} +3 -3
- udata/static/chunks/{19.df16abde17a42033a7f8.js.map → 19.f03a102365af4315f9db.js.map} +1 -1
- udata/static/chunks/{8.462bb3029de008497675.js → 8.778091d55cd8ea39af6b.js} +2 -2
- udata/static/chunks/{8.462bb3029de008497675.js.map → 8.778091d55cd8ea39af6b.js.map} +1 -1
- udata/static/common.js +1 -1
- udata/static/common.js.map +1 -1
- udata/templates/macros/forms.html +2 -2
- udata/tests/api/test_topics_api.py +17 -2
- udata/tests/frontend/test_markdown.py +28 -5
- udata/tests/workers/test_jobs_commands.py +1 -1
- udata/translations/ar/LC_MESSAGES/udata.mo +0 -0
- udata/translations/de/LC_MESSAGES/udata.mo +0 -0
- udata/translations/es/LC_MESSAGES/udata.mo +0 -0
- udata/translations/fr/LC_MESSAGES/udata.mo +0 -0
- udata/translations/it/LC_MESSAGES/udata.mo +0 -0
- udata/translations/pt/LC_MESSAGES/udata.mo +0 -0
- udata/translations/sr/LC_MESSAGES/udata.mo +0 -0
- udata/uris.py +5 -1
- {udata-10.1.5.dev34548.dist-info → udata-10.1.5.dev34612.dist-info}/METADATA +109 -98
- {udata-10.1.5.dev34548.dist-info → udata-10.1.5.dev34612.dist-info}/RECORD +44 -44
- {udata-10.1.5.dev34548.dist-info → udata-10.1.5.dev34612.dist-info}/WHEEL +1 -1
- {udata-10.1.5.dev34548.dist-info → udata-10.1.5.dev34612.dist-info}/LICENSE +0 -0
- {udata-10.1.5.dev34548.dist-info → udata-10.1.5.dev34612.dist-info}/entry_points.txt +0 -0
- {udata-10.1.5.dev34548.dist-info → udata-10.1.5.dev34612.dist-info}/top_level.txt +0 -0
tasks/__init__.py
CHANGED
|
@@ -41,21 +41,18 @@ def clean(ctx, node=False, translations=False, all=False):
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
@task
|
|
44
|
-
def update(ctx
|
|
44
|
+
def update(ctx):
|
|
45
45
|
"""Perform a development update"""
|
|
46
46
|
msg = "Update all dependencies"
|
|
47
|
-
if migrate:
|
|
48
|
-
msg += " and migrate data"
|
|
49
47
|
header(msg)
|
|
50
48
|
info("Updating Python dependencies")
|
|
51
49
|
with ctx.cd(ROOT):
|
|
52
|
-
ctx.run("pi3 install -e .")
|
|
53
50
|
ctx.run("pip install -r requirements/develop.pip")
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
for requirement_file in ["install", "test", "develop", "doc", "report"]:
|
|
52
|
+
ctx.run(
|
|
53
|
+
f"pip-compile requirements/{requirement_file}.in --output-file=requirements/{requirement_file}.pip --upgrade"
|
|
54
|
+
)
|
|
55
|
+
# TODO: Add javascript dependencies update
|
|
59
56
|
|
|
60
57
|
|
|
61
58
|
@task
|
udata/auth/forms.py
CHANGED
|
@@ -24,17 +24,17 @@ class ExtendedRegisterForm(RegisterForm):
|
|
|
24
24
|
],
|
|
25
25
|
)
|
|
26
26
|
|
|
27
|
-
def validate(self):
|
|
27
|
+
def validate(self, **kwargs):
|
|
28
28
|
# no register allowed when read only mode is on
|
|
29
|
-
if not super().validate() or current_app.config.get("READ_ONLY_MODE"):
|
|
29
|
+
if not super().validate(**kwargs) or current_app.config.get("READ_ONLY_MODE"):
|
|
30
30
|
return False
|
|
31
31
|
|
|
32
32
|
return True
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
class ExtendedLoginForm(LoginForm):
|
|
36
|
-
def validate(self):
|
|
37
|
-
if not super().validate():
|
|
36
|
+
def validate(self, **kwargs):
|
|
37
|
+
if not super().validate(**kwargs):
|
|
38
38
|
return False
|
|
39
39
|
|
|
40
40
|
if self.user.password_rotation_demanded:
|
|
@@ -45,8 +45,8 @@ class ExtendedLoginForm(LoginForm):
|
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
class ExtendedResetPasswordForm(ResetPasswordForm):
|
|
48
|
-
def validate(self):
|
|
49
|
-
if not super().validate():
|
|
48
|
+
def validate(self, **kwargs):
|
|
49
|
+
if not super().validate(**kwargs):
|
|
50
50
|
return False
|
|
51
51
|
|
|
52
52
|
if self.user.password_rotation_demanded:
|
|
@@ -65,15 +65,15 @@ class ChangeEmailForm(Form):
|
|
|
65
65
|
)
|
|
66
66
|
submit = fields.SubmitField(_("Change email"))
|
|
67
67
|
|
|
68
|
-
def validate(self):
|
|
69
|
-
if not super().validate():
|
|
68
|
+
def validate(self, **kwargs):
|
|
69
|
+
if not super().validate(**kwargs):
|
|
70
70
|
return False
|
|
71
71
|
|
|
72
72
|
self.user = current_user
|
|
73
73
|
|
|
74
74
|
if self.user.email.strip() == self.new_email.data.strip():
|
|
75
75
|
self.new_email.errors.append(
|
|
76
|
-
"Your new email must be different than your
|
|
76
|
+
"Your new email must be different than your previous email"
|
|
77
77
|
)
|
|
78
78
|
return False
|
|
79
79
|
return True
|
udata/commands/serve.py
CHANGED
|
@@ -18,15 +18,13 @@ log = logging.getLogger(__name__)
|
|
|
18
18
|
"-r/-nr",
|
|
19
19
|
"--reload/--no-reload",
|
|
20
20
|
default=None,
|
|
21
|
-
help="Enable or disable the reloader. By default the reloader "
|
|
22
|
-
"is active if debug is enabled.",
|
|
21
|
+
help="Enable or disable the reloader. By default the reloader is active if debug is enabled.",
|
|
23
22
|
)
|
|
24
23
|
@click.option(
|
|
25
24
|
"-d/-nd",
|
|
26
25
|
"--debugger/--no-debugger",
|
|
27
26
|
default=None,
|
|
28
|
-
help="Enable or disable the debugger. By default the debugger "
|
|
29
|
-
"is active if debug is enabled.",
|
|
27
|
+
help="Enable or disable the debugger. By default the debugger is active if debug is enabled.",
|
|
30
28
|
)
|
|
31
29
|
@click.option(
|
|
32
30
|
"--eager-loading/--lazy-loader",
|
udata/core/badges/fields.py
CHANGED
udata/core/dataset/models.py
CHANGED
|
@@ -5,6 +5,7 @@ from pydoc import locate
|
|
|
5
5
|
from typing import Self
|
|
6
6
|
from urllib.parse import urlparse
|
|
7
7
|
|
|
8
|
+
import Levenshtein
|
|
8
9
|
import requests
|
|
9
10
|
from blinker import signal
|
|
10
11
|
from dateutil.parser import parse as parse_dt
|
|
@@ -13,7 +14,6 @@ from mongoengine import DynamicEmbeddedDocument
|
|
|
13
14
|
from mongoengine import ValidationError as MongoEngineValidationError
|
|
14
15
|
from mongoengine.fields import DateTimeField
|
|
15
16
|
from mongoengine.signals import post_save, pre_init, pre_save
|
|
16
|
-
from stringdist import rdlevenshtein
|
|
17
17
|
from werkzeug.utils import cached_property
|
|
18
18
|
|
|
19
19
|
from udata.api_fields import field
|
|
@@ -281,7 +281,9 @@ class License(db.Document):
|
|
|
281
281
|
|
|
282
282
|
if license is None:
|
|
283
283
|
# Try to single match `slug` with a low Damerau-Levenshtein distance
|
|
284
|
-
computed = (
|
|
284
|
+
computed = (
|
|
285
|
+
(license_, Levenshtein.distance(license_.slug, slug)) for license_ in cls.objects
|
|
286
|
+
)
|
|
285
287
|
candidates = [license_ for license_, d in computed if d <= MAX_DISTANCE]
|
|
286
288
|
# If there is more that one match, we cannot determinate
|
|
287
289
|
# which one is closer to safely choose between candidates
|
|
@@ -291,7 +293,8 @@ class License(db.Document):
|
|
|
291
293
|
if license is None:
|
|
292
294
|
# Try to match `title` with a low Damerau-Levenshtein distance
|
|
293
295
|
computed = (
|
|
294
|
-
(license_,
|
|
296
|
+
(license_, Levenshtein.distance(license_.title.lower(), text))
|
|
297
|
+
for license_ in cls.objects
|
|
295
298
|
)
|
|
296
299
|
candidates = [license_ for license_, d in computed if d <= MAX_DISTANCE]
|
|
297
300
|
# If there is more that one match, we cannot determinate
|
|
@@ -302,7 +305,7 @@ class License(db.Document):
|
|
|
302
305
|
if license is None:
|
|
303
306
|
# Try to single match `alternate_titles` with a low Damerau-Levenshtein distance
|
|
304
307
|
computed = (
|
|
305
|
-
(license_,
|
|
308
|
+
(license_, Levenshtein.distance(cls.slug.slugify(title_), slug))
|
|
306
309
|
for license_ in cls.objects
|
|
307
310
|
for title_ in license_.alternate_titles
|
|
308
311
|
)
|
udata/core/discussions/api.py
CHANGED
|
@@ -129,7 +129,7 @@ class DiscussionAPI(API):
|
|
|
129
129
|
@api.doc("comment_discussion")
|
|
130
130
|
@api.expect(comment_discussion_fields)
|
|
131
131
|
@api.response(
|
|
132
|
-
403, "Not allowed to close this discussion
|
|
132
|
+
403, "Not allowed to close this discussion OR can't add comments on a closed discussion"
|
|
133
133
|
)
|
|
134
134
|
@api.marshal_with(discussion_fields)
|
|
135
135
|
def post(self, id):
|
udata/core/spatial/models.py
CHANGED
|
@@ -170,9 +170,7 @@ class SpatialCoverage(db.EmbeddedDocument):
|
|
|
170
170
|
return [zone for zone in self.zones if zone.handled_level]
|
|
171
171
|
|
|
172
172
|
def clean(self):
|
|
173
|
-
if
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if self.geom:
|
|
178
|
-
raise db.ValidationError("The spatial coverage already has a Geometry")
|
|
173
|
+
if self.zones and self.geom:
|
|
174
|
+
raise db.ValidationError(
|
|
175
|
+
"The spatial coverage cannot contains a Geozone and a Geometry"
|
|
176
|
+
)
|
udata/forms/fields.py
CHANGED
|
@@ -363,11 +363,11 @@ class SelectField(FieldHelper, fields.SelectField):
|
|
|
363
363
|
|
|
364
364
|
def iter_choices(self):
|
|
365
365
|
localized_choices = [
|
|
366
|
-
(value, self.localize_label(label), selected)
|
|
367
|
-
for value, label, selected in super(SelectField, self).iter_choices()
|
|
366
|
+
(value, self.localize_label(label), selected, render_kw)
|
|
367
|
+
for value, label, selected, render_kw in super(SelectField, self).iter_choices()
|
|
368
368
|
]
|
|
369
|
-
for value, label, selected in sorted(localized_choices, key=lambda c: c[1]):
|
|
370
|
-
yield (value, label, selected)
|
|
369
|
+
for value, label, selected, render_kw in sorted(localized_choices, key=lambda c: c[1]):
|
|
370
|
+
yield (value, label, selected, render_kw)
|
|
371
371
|
|
|
372
372
|
@property
|
|
373
373
|
def choices(self):
|
|
@@ -390,11 +390,11 @@ class SelectMultipleField(FieldHelper, fields.SelectMultipleField):
|
|
|
390
390
|
|
|
391
391
|
def iter_choices(self):
|
|
392
392
|
localized_choices = [
|
|
393
|
-
(value, self._(label) if label else "", selected)
|
|
394
|
-
for value, label, selected in super(SelectMultipleField, self).iter_choices()
|
|
393
|
+
(value, self._(label) if label else "", selected, render_kw)
|
|
394
|
+
for value, label, selected, render_kw in super(SelectMultipleField, self).iter_choices()
|
|
395
395
|
]
|
|
396
|
-
for value, label, selected in sorted(localized_choices, key=lambda c: c[1]):
|
|
397
|
-
yield (value, label, selected)
|
|
396
|
+
for value, label, selected, render_kw in sorted(localized_choices, key=lambda c: c[1]):
|
|
397
|
+
yield (value, label, selected, render_kw)
|
|
398
398
|
|
|
399
399
|
|
|
400
400
|
class TagField(Field):
|
udata/frontend/markdown.py
CHANGED
|
@@ -5,6 +5,7 @@ from urllib.parse import urlparse
|
|
|
5
5
|
import bleach
|
|
6
6
|
import html2text
|
|
7
7
|
import mistune
|
|
8
|
+
from bleach.css_sanitizer import CSSSanitizer
|
|
8
9
|
from bleach.linkifier import LinkifyFilter
|
|
9
10
|
from flask import Markup, current_app, request
|
|
10
11
|
from jinja2.filters import do_striptags, do_truncate
|
|
@@ -55,11 +56,6 @@ def nofollow_callback(attrs, new=False):
|
|
|
55
56
|
return attrs
|
|
56
57
|
|
|
57
58
|
|
|
58
|
-
class Renderer(mistune.Renderer):
|
|
59
|
-
def table(self, header, body):
|
|
60
|
-
return ("<table>\n<thead>\n%s</thead>\n" "<tbody>\n%s</tbody>\n</table>\n") % (header, body)
|
|
61
|
-
|
|
62
|
-
|
|
63
59
|
class UdataCleaner(bleach.Cleaner):
|
|
64
60
|
def __init__(self, source_tooltip=False) -> None:
|
|
65
61
|
callbacks = [nofollow_callback]
|
|
@@ -67,9 +63,11 @@ class UdataCleaner(bleach.Cleaner):
|
|
|
67
63
|
callbacks.append(source_tooltip_callback)
|
|
68
64
|
|
|
69
65
|
super().__init__(
|
|
70
|
-
tags=current_app.config["MD_ALLOWED_TAGS"],
|
|
66
|
+
tags=set(current_app.config["MD_ALLOWED_TAGS"]),
|
|
71
67
|
attributes=current_app.config["MD_ALLOWED_ATTRIBUTES"],
|
|
72
|
-
|
|
68
|
+
css_sanitizer=CSSSanitizer(
|
|
69
|
+
allowed_css_properties=current_app.config["MD_ALLOWED_STYLES"]
|
|
70
|
+
),
|
|
73
71
|
protocols=current_app.config["MD_ALLOWED_PROTOCOLS"],
|
|
74
72
|
strip_comments=False,
|
|
75
73
|
filters=[
|
|
@@ -83,8 +81,9 @@ class UDataMarkdown(object):
|
|
|
83
81
|
|
|
84
82
|
def __init__(self, app):
|
|
85
83
|
app.jinja_env.filters.setdefault("markdown", self.__call__)
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
self.markdown = mistune.create_markdown(
|
|
85
|
+
escape=False, hard_wrap=True, plugins=["table", "strikethrough"]
|
|
86
|
+
)
|
|
88
87
|
|
|
89
88
|
def __call__(self, stream, source_tooltip=False, wrap=True):
|
|
90
89
|
if not stream:
|
|
@@ -207,7 +207,7 @@ class BaseBackendTest:
|
|
|
207
207
|
|
|
208
208
|
dataset = Dataset.objects.first()
|
|
209
209
|
|
|
210
|
-
|
|
210
|
+
assert_equal_dates(dataset.last_modified_internal, last_modified)
|
|
211
211
|
assert_equal_dates(dataset.harvest.last_update, datetime.utcnow())
|
|
212
212
|
|
|
213
213
|
def test_dont_overwrite_last_modified_even_if_set_to_same(self, mocker):
|
|
@@ -222,7 +222,7 @@ class BaseBackendTest:
|
|
|
222
222
|
|
|
223
223
|
dataset = Dataset.objects.first()
|
|
224
224
|
|
|
225
|
-
|
|
225
|
+
assert_equal_dates(dataset.last_modified_internal, last_modified)
|
|
226
226
|
assert_equal_dates(dataset.harvest.last_update, datetime.utcnow())
|
|
227
227
|
|
|
228
228
|
def test_autoarchive(self, app):
|
|
@@ -512,7 +512,7 @@ class DcatBackendTest:
|
|
|
512
512
|
|
|
513
513
|
dataset = Dataset.objects.get(harvest__dct_identifier="1")
|
|
514
514
|
# test html abstract description support
|
|
515
|
-
assert dataset.description == "# h1 title\n\n## h2 title\n\n
|
|
515
|
+
assert dataset.description == "# h1 title\n\n## h2 title\n\n**and bold text**"
|
|
516
516
|
# test DCAT periodoftime
|
|
517
517
|
assert dataset.temporal_coverage is not None
|
|
518
518
|
assert dataset.temporal_coverage.start == date(2016, 1, 1)
|
|
@@ -62,7 +62,7 @@ class FiltersTest:
|
|
|
62
62
|
hashes = {
|
|
63
63
|
"md5": "bd8668597bfba2d1843441d7199bea65",
|
|
64
64
|
"sha1": "f2f0249827f501286b4713683e526d541d2cc7e2",
|
|
65
|
-
"sha256": ("
|
|
65
|
+
"sha256": ("c4373e1d81eb44882bf9ff539d0e5ffaf03a114abf9306591117d781966268f9"),
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
for type, value in hashes.items():
|
udata/i18n.py
CHANGED
|
@@ -134,7 +134,7 @@ def check_config(cfg):
|
|
|
134
134
|
default_language = cfg["DEFAULT_LANGUAGE"]
|
|
135
135
|
if default_language not in cfg.get("LANGUAGES", []):
|
|
136
136
|
raise ConfigError(
|
|
137
|
-
"You are using a DEFAULT_LANGUAGE {0} not defined
|
|
137
|
+
"You are using a DEFAULT_LANGUAGE {0} not defined into LANGUAGES".format(
|
|
138
138
|
default_language
|
|
139
139
|
)
|
|
140
140
|
)
|
udata/mongo/taglist_field.py
CHANGED
|
@@ -35,7 +35,7 @@ class TagListField(ListField):
|
|
|
35
35
|
if not tags.MIN_TAG_LENGTH <= len(tag) <= tags.MAX_TAG_LENGTH:
|
|
36
36
|
self.error(
|
|
37
37
|
_(
|
|
38
|
-
'Tag "%(tag)s" must be between %(min)d
|
|
38
|
+
'Tag "%(tag)s" must be between %(min)d and %(max)d characters long.',
|
|
39
39
|
min=tags.MIN_TAG_LENGTH,
|
|
40
40
|
max=tags.MAX_TAG_LENGTH,
|
|
41
41
|
tag=tag,
|