udata 7.0.4.dev27568__py2.py3-none-any.whl → 7.0.4.dev27598__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 (42) hide show
  1. udata/core/discussions/api.py +2 -4
  2. udata/core/discussions/models.py +23 -5
  3. udata/core/discussions/tasks.py +3 -1
  4. udata/core/spam/api.py +1 -2
  5. udata/core/spam/fields.py +1 -2
  6. udata/core/spam/models.py +6 -17
  7. udata/core/spam/tests/__init__.py +0 -0
  8. udata/core/spam/tests/test_spam.py +26 -0
  9. udata/harvest/backends/base.py +2 -2
  10. udata/harvest/models.py +1 -1
  11. udata/harvest/tasks.py +6 -3
  12. udata/harvest/tests/factories.py +1 -0
  13. udata/harvest/tests/test_models.py +1 -1
  14. udata/notifications/mattermost.py +2 -7
  15. udata/settings.py +1 -2
  16. udata/static/chunks/{11.c0ccea08914b6b41568e.js → 11.a23c110811a9ac943478.js} +3 -3
  17. udata/static/chunks/{11.c0ccea08914b6b41568e.js.map → 11.a23c110811a9ac943478.js.map} +1 -1
  18. udata/static/chunks/{13.526a25163ababaa44409.js → 13.0889e093f8664e38568c.js} +2 -2
  19. udata/static/chunks/{13.526a25163ababaa44409.js.map → 13.0889e093f8664e38568c.js.map} +1 -1
  20. udata/static/chunks/{16.7901839b4227881947f6.js → 16.f41599478d3e97ad9a30.js} +2 -2
  21. udata/static/chunks/{16.7901839b4227881947f6.js.map → 16.f41599478d3e97ad9a30.js.map} +1 -1
  22. udata/static/chunks/{19.471d5a2a08eef6e5338a.js → 19.2b534a26af8b17e9170b.js} +3 -3
  23. udata/static/chunks/{19.471d5a2a08eef6e5338a.js.map → 19.2b534a26af8b17e9170b.js.map} +1 -1
  24. udata/static/chunks/{5.98904a7a544eeb258e23.js → 5.bd822f0e9689f45bd582.js} +3 -3
  25. udata/static/chunks/{5.98904a7a544eeb258e23.js.map → 5.bd822f0e9689f45bd582.js.map} +1 -1
  26. udata/static/chunks/{6.e56975229e6065f68d2a.js → 6.16bb24fb8240f2746488.js} +3 -3
  27. udata/static/chunks/{6.e56975229e6065f68d2a.js.map → 6.16bb24fb8240f2746488.js.map} +1 -1
  28. udata/static/chunks/{9.534426728626f11f4571.js → 9.3e752966ff14e47e11f2.js} +2 -2
  29. udata/static/chunks/{9.534426728626f11f4571.js.map → 9.3e752966ff14e47e11f2.js.map} +1 -1
  30. udata/static/common.js +1 -1
  31. udata/static/common.js.map +1 -1
  32. udata/templates/mail/membership_refused.html +1 -1
  33. udata/templates/mail/membership_request.html +1 -1
  34. udata/templates/mail/new_member.html +2 -2
  35. udata/templates/mail/new_member.txt +1 -1
  36. udata/tests/test_discussions.py +4 -6
  37. {udata-7.0.4.dev27568.dist-info → udata-7.0.4.dev27598.dist-info}/METADATA +6 -1
  38. {udata-7.0.4.dev27568.dist-info → udata-7.0.4.dev27598.dist-info}/RECORD +42 -40
  39. {udata-7.0.4.dev27568.dist-info → udata-7.0.4.dev27598.dist-info}/LICENSE +0 -0
  40. {udata-7.0.4.dev27568.dist-info → udata-7.0.4.dev27598.dist-info}/WHEEL +0 -0
  41. {udata-7.0.4.dev27568.dist-info → udata-7.0.4.dev27598.dist-info}/entry_points.txt +0 -0
  42. {udata-7.0.4.dev27568.dist-info → udata-7.0.4.dev27598.dist-info}/top_level.txt +0 -0
@@ -14,10 +14,8 @@ from udata.core.user.api_fields import user_ref_fields
14
14
  from .forms import DiscussionCreateForm, DiscussionCommentForm
15
15
  from .models import Message, Discussion
16
16
  from .permissions import CloseDiscussionPermission
17
- from .signals import (
18
- on_new_discussion, on_new_discussion_comment, on_discussion_closed,
19
- on_discussion_deleted
20
- )
17
+ from .signals import on_discussion_deleted
18
+
21
19
 
22
20
  ns = api.namespace('discussions', 'Discussion related operations')
23
21
 
@@ -19,6 +19,23 @@ class Message(SpamMixin, db.EmbeddedDocument):
19
19
 
20
20
  def texts_to_check_for_spam(self):
21
21
  return [self.content]
22
+
23
+ def spam_report_message(self, breadcrumb):
24
+ message = f"Spam potentiel dans le message"
25
+ if self.posted_by:
26
+ message += f" de [{self.posted_by.fullname}]({self.posted_by.external_url})"
27
+
28
+ if len(breadcrumb) != 2:
29
+ log.warning(f"`spam_report_message` called on message with a breadcrumb of {len(breadcrumb)} elements.", extra={ 'breadcrumb': breadcrumb})
30
+ return message
31
+
32
+ discussion = breadcrumb[0]
33
+ if not isinstance(discussion, Discussion):
34
+ log.warning(f"`spam_report_message` called on message with a breadcrumb not containing a Discussion at index 0.", extra={ 'breadcrumb': breadcrumb})
35
+ return message
36
+
37
+ message += f" sur la discussion « [{discussion.title}]({discussion.external_url}) »"
38
+ return message
22
39
 
23
40
 
24
41
  class Discussion(SpamMixin, db.Document):
@@ -60,11 +77,12 @@ class Discussion(SpamMixin, db.Document):
60
77
  _anchor='discussion-{id}'.format(id=self.id),
61
78
  _external=True)
62
79
 
63
- def spam_report_title(self):
64
- return self.title
65
-
66
- def spam_report_link(self):
67
- return self.external_url
80
+ def spam_report_message(self, breadcrumb):
81
+ message = f"Spam potentiel sur la discussion « [{self.title}]({self.external_url}) »"
82
+ if self.user:
83
+ message += f" de [{self.user.fullname}]({self.user.external_url})"
84
+
85
+ return message
68
86
 
69
87
  @spam_protected()
70
88
  def signal_new(self):
@@ -16,8 +16,10 @@ log = get_logger(__name__)
16
16
  def owner_recipients(discussion):
17
17
  if getattr(discussion.subject, 'organization', None):
18
18
  return [m.user for m in discussion.subject.organization.members]
19
- else:
19
+ elif getattr(discussion.subject, 'owner', None):
20
20
  return [discussion.subject.owner]
21
+ else:
22
+ return []
21
23
 
22
24
 
23
25
  @connect(on_new_discussion, by_id=True)
udata/core/spam/api.py CHANGED
@@ -53,6 +53,5 @@ class SpamAPI(API):
53
53
  discussions = Discussion.objects(Q(spam__status=POTENTIAL_SPAM) | Q(discussion__spam__status=POTENTIAL_SPAM))
54
54
 
55
55
  return [{
56
- 'title': discussion.spam_report_title(),
57
- 'link': discussion.spam_report_link(),
56
+ 'message': discussion.spam_report_message([discussion]),
58
57
  } for discussion in discussions]
udata/core/spam/fields.py CHANGED
@@ -6,7 +6,6 @@ spam_fields = api.model('Spam', {
6
6
  })
7
7
 
8
8
  potential_spam_fields = api.model('PotentialSpam', {
9
- 'title': fields.String(readonly=True),
10
- 'link': fields.String(readonly=True),
9
+ 'message': fields.String(readonly=True),
11
10
  })
12
11
 
udata/core/spam/models.py CHANGED
@@ -97,7 +97,7 @@ class SpamMixin(object):
97
97
 
98
98
  # Language detection is not working well with texts of a few words.
99
99
  if SpamMixin.allowed_langs() and len(text) > SpamMixin.minimum_string_length_for_lang_check():
100
- lang = detect(text)
100
+ lang = detect(text.lower())
101
101
  if lang not in SpamMixin.allowed_langs():
102
102
  self.spam.status = POTENTIAL_SPAM
103
103
  self._report(text=text, breadcrumb=breadcrumb, reason=f"not allowed language \"{lang}\"")
@@ -143,11 +143,8 @@ class SpamMixin(object):
143
143
  def embeds_to_check_for_spam(self):
144
144
  return []
145
145
 
146
- def spam_report_title(self):
147
- return type(self).__name__
148
-
149
- def spam_report_link(self):
150
- return None
146
+ def spam_report_message(self):
147
+ return f"Spam potentiel sur {type(self).__name__}"
151
148
 
152
149
  def _report(self, text, breadcrumb, reason):
153
150
  base_model = breadcrumb[0]
@@ -158,22 +155,14 @@ class SpamMixin(object):
158
155
  if document != base_model:
159
156
  return
160
157
 
161
- # Note that all the chain should be a SpamMixin, maybe we could filter out if it's not the case here…
162
- title = " → ".join(map(lambda o: o.spam_report_title(), breadcrumb))
163
-
164
- # Select the first link in the embed list (for example message doesn't have a link, so check if discussion
165
- # have one)
166
- for object in reversed(breadcrumb):
167
- link = object.spam_report_link()
168
- if link:
169
- break
158
+ message = self.spam_report_message(breadcrumb)
170
159
 
171
- on_new_potential_spam.send(self, title=title, link=link, text=text, reason=reason)
160
+ on_new_potential_spam.send(self, message=message, text=text, reason=reason)
172
161
 
173
162
  # We clean the listener here. Not sure if it's necessary either.
174
163
  signals.post_save.disconnect(report_after_save)
175
164
 
176
- # For things like `spam_report_link` we often need the ID of the document so we
165
+ # For `spam_report_message` we often need the ID of the document so we
177
166
  # must report after saving to have the ID available.
178
167
  # By default the signal is weak so it is dropped at the end of this function and it's
179
168
  # never called. We disconnect the signal in `report_after_save` to avoid leaks.
File without changes
@@ -0,0 +1,26 @@
1
+ import logging
2
+ import pytest
3
+ from udata.tests import TestCase
4
+ from udata.tests.helpers import assert_not_emit
5
+ from udata.models import db
6
+
7
+ from ..models import POTENTIAL_SPAM, SpamMixin
8
+
9
+
10
+ log = logging.getLogger(__name__)
11
+
12
+ class TestModel(SpamMixin, db.Document):
13
+ text = db.StringField(required=True)
14
+ _created = True
15
+
16
+ def texts_to_check_for_spam(self):
17
+ return [self.text]
18
+
19
+
20
+ class SpamTest(TestCase):
21
+ @pytest.mark.options(SPAM_WORDS=['spam'], SPAM_ALLOWED_LANGS=['fr'])
22
+ def test_uppercase_lang_detect(self):
23
+ model = TestModel(text="DONNEES DE RECENSEMENT - MARCHES PUBLICS")
24
+ model.detect_spam()
25
+ self.assertNotEqual(model.spam.status, POTENTIAL_SPAM)
26
+
@@ -156,7 +156,7 @@ class BaseBackend(object):
156
156
  self.job.errors.append(error)
157
157
  self.job.status = 'failed'
158
158
  self.end()
159
- return
159
+ return None
160
160
  except Exception as e:
161
161
  self.job.status = 'failed'
162
162
  error = HarvestError(message=safe_unicode(e))
@@ -164,7 +164,7 @@ class BaseBackend(object):
164
164
  self.end()
165
165
  msg = 'Initialization failed for "{0.name}" ({0.backend})'
166
166
  log.exception(msg.format(self.source))
167
- return
167
+ return None
168
168
 
169
169
  if self.max_items:
170
170
  self.job.items = self.job.items[:self.max_items]
udata/harvest/models.py CHANGED
@@ -94,7 +94,7 @@ class HarvestSource(db.Owned, db.Document):
94
94
  populate_from='name', update=True)
95
95
  description = db.StringField()
96
96
  url = db.StringField(required=True)
97
- backend = db.StringField()
97
+ backend = db.StringField(required=True)
98
98
  config = db.DictField()
99
99
  periodic_task = db.ReferenceField('PeriodicTask',
100
100
  reverse_delete_rule=db.NULLIFY)
udata/harvest/tasks.py CHANGED
@@ -19,15 +19,18 @@ def harvest(self, ident):
19
19
  Backend = backends.get(current_app, source.backend)
20
20
  backend = Backend(source)
21
21
  items = backend.perform_initialization()
22
- if items > 0:
22
+ if items is None:
23
+ pass
24
+ elif items == 0:
25
+ backend.finalize()
26
+ else:
23
27
  finalize = harvest_job_finalize.s(backend.job.id)
24
28
  items = [
25
29
  harvest_job_item.s(backend.job.id, item.remote_id)
26
30
  for item in backend.job.items
27
31
  ]
28
32
  chord(items)(finalize)
29
- elif items == 0:
30
- backend.finalize()
33
+
31
34
 
32
35
 
33
36
  @task(ignore_result=False, route='low.harvest')
@@ -22,6 +22,7 @@ class HarvestSourceFactory(ModelFactory):
22
22
  name = factory.Faker('name')
23
23
  url = factory.Faker('url')
24
24
  description = factory.Faker('text')
25
+ backend = 'factory'
25
26
 
26
27
 
27
28
  class HarvestJobFactory(ModelFactory):
@@ -12,7 +12,7 @@ log = logging.getLogger(__name__)
12
12
  @pytest.mark.usefixtures('clean_db')
13
13
  class HarvestSourceTest:
14
14
  def test_defaults(self):
15
- source = HarvestSource.objects.create(name='Test', url=faker.url())
15
+ source = HarvestSource.objects.create(name='Test', url=faker.url(), backend='factory')
16
16
  assert source.name == 'Test'
17
17
  assert source.slug == 'test'
18
18
 
@@ -5,16 +5,11 @@ from flask import current_app
5
5
 
6
6
  @on_new_potential_spam.connect
7
7
  def notify_potential_spam(sender, **kwargs):
8
- title = kwargs.get('title', 'no title')
9
- link = kwargs.get('link')
8
+ message = kwargs.get('message')
10
9
  reason = kwargs.get('reason')
11
10
  text = kwargs.get('text')
12
11
 
13
- message = ':warning: @all Spam potentiel sur '
14
- if link:
15
- message += f'[{title}]({link})'
16
- else:
17
- message += title
12
+ message = f':warning: @all {message}'
18
13
 
19
14
  if reason:
20
15
  message += f' ({reason})'
udata/settings.py CHANGED
@@ -470,8 +470,7 @@ class Testing(object):
470
470
  WTF_CSRF_ENABLED = False
471
471
  AUTO_INDEX = False
472
472
  CELERY_TASK_ALWAYS_EAGER = True
473
- # TODO: ideally, this should be set to True in order to reveal exceptions in delayed tasks
474
- CELERY_TASK_EAGER_PROPAGATES = False
473
+ CELERY_TASK_EAGER_PROPAGATES = True
475
474
  TEST_WITH_PLUGINS = False
476
475
  PLUGINS = []
477
476
  TEST_WITH_THEME = False