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.
- udata/core/discussions/api.py +2 -4
- udata/core/discussions/models.py +23 -5
- udata/core/discussions/tasks.py +3 -1
- udata/core/spam/api.py +1 -2
- udata/core/spam/fields.py +1 -2
- udata/core/spam/models.py +6 -17
- udata/core/spam/tests/__init__.py +0 -0
- udata/core/spam/tests/test_spam.py +26 -0
- udata/harvest/backends/base.py +2 -2
- udata/harvest/models.py +1 -1
- udata/harvest/tasks.py +6 -3
- udata/harvest/tests/factories.py +1 -0
- udata/harvest/tests/test_models.py +1 -1
- udata/notifications/mattermost.py +2 -7
- udata/settings.py +1 -2
- udata/static/chunks/{11.c0ccea08914b6b41568e.js → 11.a23c110811a9ac943478.js} +3 -3
- udata/static/chunks/{11.c0ccea08914b6b41568e.js.map → 11.a23c110811a9ac943478.js.map} +1 -1
- udata/static/chunks/{13.526a25163ababaa44409.js → 13.0889e093f8664e38568c.js} +2 -2
- udata/static/chunks/{13.526a25163ababaa44409.js.map → 13.0889e093f8664e38568c.js.map} +1 -1
- udata/static/chunks/{16.7901839b4227881947f6.js → 16.f41599478d3e97ad9a30.js} +2 -2
- udata/static/chunks/{16.7901839b4227881947f6.js.map → 16.f41599478d3e97ad9a30.js.map} +1 -1
- udata/static/chunks/{19.471d5a2a08eef6e5338a.js → 19.2b534a26af8b17e9170b.js} +3 -3
- udata/static/chunks/{19.471d5a2a08eef6e5338a.js.map → 19.2b534a26af8b17e9170b.js.map} +1 -1
- udata/static/chunks/{5.98904a7a544eeb258e23.js → 5.bd822f0e9689f45bd582.js} +3 -3
- udata/static/chunks/{5.98904a7a544eeb258e23.js.map → 5.bd822f0e9689f45bd582.js.map} +1 -1
- udata/static/chunks/{6.e56975229e6065f68d2a.js → 6.16bb24fb8240f2746488.js} +3 -3
- udata/static/chunks/{6.e56975229e6065f68d2a.js.map → 6.16bb24fb8240f2746488.js.map} +1 -1
- udata/static/chunks/{9.534426728626f11f4571.js → 9.3e752966ff14e47e11f2.js} +2 -2
- udata/static/chunks/{9.534426728626f11f4571.js.map → 9.3e752966ff14e47e11f2.js.map} +1 -1
- udata/static/common.js +1 -1
- udata/static/common.js.map +1 -1
- udata/templates/mail/membership_refused.html +1 -1
- udata/templates/mail/membership_request.html +1 -1
- udata/templates/mail/new_member.html +2 -2
- udata/templates/mail/new_member.txt +1 -1
- udata/tests/test_discussions.py +4 -6
- {udata-7.0.4.dev27568.dist-info → udata-7.0.4.dev27598.dist-info}/METADATA +6 -1
- {udata-7.0.4.dev27568.dist-info → udata-7.0.4.dev27598.dist-info}/RECORD +42 -40
- {udata-7.0.4.dev27568.dist-info → udata-7.0.4.dev27598.dist-info}/LICENSE +0 -0
- {udata-7.0.4.dev27568.dist-info → udata-7.0.4.dev27598.dist-info}/WHEEL +0 -0
- {udata-7.0.4.dev27568.dist-info → udata-7.0.4.dev27598.dist-info}/entry_points.txt +0 -0
- {udata-7.0.4.dev27568.dist-info → udata-7.0.4.dev27598.dist-info}/top_level.txt +0 -0
udata/core/discussions/api.py
CHANGED
|
@@ -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
|
-
|
|
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
|
|
udata/core/discussions/models.py
CHANGED
|
@@ -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
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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):
|
udata/core/discussions/tasks.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
'
|
|
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
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
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
+
|
udata/harvest/backends/base.py
CHANGED
|
@@ -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
|
|
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
|
-
|
|
30
|
-
backend.finalize()
|
|
33
|
+
|
|
31
34
|
|
|
32
35
|
|
|
33
36
|
@task(ignore_result=False, route='low.harvest')
|
udata/harvest/tests/factories.py
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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
|