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.

Files changed (44) hide show
  1. tasks/__init__.py +6 -9
  2. udata/auth/forms.py +9 -9
  3. udata/commands/serve.py +2 -4
  4. udata/core/badges/fields.py +1 -1
  5. udata/core/dataset/models.py +7 -4
  6. udata/core/discussions/api.py +1 -1
  7. udata/core/spatial/models.py +4 -6
  8. udata/forms/fields.py +8 -8
  9. udata/frontend/markdown.py +8 -9
  10. udata/harvest/tests/test_base_backend.py +2 -2
  11. udata/harvest/tests/test_dcat_backend.py +1 -1
  12. udata/harvest/tests/test_filters.py +1 -1
  13. udata/i18n.py +1 -1
  14. udata/mongo/taglist_field.py +1 -1
  15. udata/static/chunks/{11.83535504cd650ea08f65.js → 11.b6f741fcc366abfad9c4.js} +3 -3
  16. udata/static/chunks/{11.83535504cd650ea08f65.js.map → 11.b6f741fcc366abfad9c4.js.map} +1 -1
  17. udata/static/chunks/{13.39e106d56f794ebd06a0.js → 13.2d06442dd9a05d9777b5.js} +2 -2
  18. udata/static/chunks/{13.39e106d56f794ebd06a0.js.map → 13.2d06442dd9a05d9777b5.js.map} +1 -1
  19. udata/static/chunks/{17.70cbb4a91b002338007e.js → 17.e8e4caaad5cb0cc0bacc.js} +2 -2
  20. udata/static/chunks/{17.70cbb4a91b002338007e.js.map → 17.e8e4caaad5cb0cc0bacc.js.map} +1 -1
  21. udata/static/chunks/{19.df16abde17a42033a7f8.js → 19.f03a102365af4315f9db.js} +3 -3
  22. udata/static/chunks/{19.df16abde17a42033a7f8.js.map → 19.f03a102365af4315f9db.js.map} +1 -1
  23. udata/static/chunks/{8.462bb3029de008497675.js → 8.778091d55cd8ea39af6b.js} +2 -2
  24. udata/static/chunks/{8.462bb3029de008497675.js.map → 8.778091d55cd8ea39af6b.js.map} +1 -1
  25. udata/static/common.js +1 -1
  26. udata/static/common.js.map +1 -1
  27. udata/templates/macros/forms.html +2 -2
  28. udata/tests/api/test_topics_api.py +17 -2
  29. udata/tests/frontend/test_markdown.py +28 -5
  30. udata/tests/workers/test_jobs_commands.py +1 -1
  31. udata/translations/ar/LC_MESSAGES/udata.mo +0 -0
  32. udata/translations/de/LC_MESSAGES/udata.mo +0 -0
  33. udata/translations/es/LC_MESSAGES/udata.mo +0 -0
  34. udata/translations/fr/LC_MESSAGES/udata.mo +0 -0
  35. udata/translations/it/LC_MESSAGES/udata.mo +0 -0
  36. udata/translations/pt/LC_MESSAGES/udata.mo +0 -0
  37. udata/translations/sr/LC_MESSAGES/udata.mo +0 -0
  38. udata/uris.py +5 -1
  39. {udata-10.1.5.dev34548.dist-info → udata-10.1.5.dev34612.dist-info}/METADATA +109 -98
  40. {udata-10.1.5.dev34548.dist-info → udata-10.1.5.dev34612.dist-info}/RECORD +44 -44
  41. {udata-10.1.5.dev34548.dist-info → udata-10.1.5.dev34612.dist-info}/WHEEL +1 -1
  42. {udata-10.1.5.dev34548.dist-info → udata-10.1.5.dev34612.dist-info}/LICENSE +0 -0
  43. {udata-10.1.5.dev34548.dist-info → udata-10.1.5.dev34612.dist-info}/entry_points.txt +0 -0
  44. {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, migrate=False):
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
- info("Updating JavaScript dependencies")
55
- ctx.run("npm install")
56
- if migrate:
57
- info("Migrating database")
58
- ctx.run("udata db migrate")
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 " "previous email"
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",
@@ -4,7 +4,7 @@ badge_fields = api.model(
4
4
  "Badge",
5
5
  {
6
6
  "kind": fields.String(
7
- description=("Kind of badge (certified, etc), " "specific to each model"), required=True
7
+ description=("Kind of badge (certified, etc), specific to each model"), required=True
8
8
  ),
9
9
  },
10
10
  )
@@ -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 = ((license_, rdlevenshtein(license_.slug, slug)) for license_ in cls.objects)
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_, rdlevenshtein(license_.title.lower(), text)) for license_ in cls.objects
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_, rdlevenshtein(cls.slug.slugify(title_), slug))
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
  )
@@ -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 " "OR can't add comments on a closed 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):
@@ -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 "geom" in self._get_changed_fields():
174
- if self.zones:
175
- raise db.ValidationError("The spatial coverage already has a Geozone")
176
- if "zones" in self._get_changed_fields():
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):
@@ -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
- styles=current_app.config["MD_ALLOWED_STYLES"],
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
- renderer = Renderer(escape=False, hard_wrap=True)
87
- self.markdown = mistune.Markdown(renderer=renderer)
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
- assert dataset.last_modified_internal == last_modified
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
- assert dataset.last_modified_internal == last_modified
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 **and bold text**"
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": ("c4373e1d81eb44882bf9ff539d0e5f" "faf03a114abf9306591117d781966268f9"),
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 " "into LANGUAGES".format(
137
+ "You are using a DEFAULT_LANGUAGE {0} not defined into LANGUAGES".format(
138
138
  default_language
139
139
  )
140
140
  )
@@ -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 ' "and %(max)d characters long.",
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,