wbnews 1.50.4__py2.py3-none-any.whl → 1.59.2__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.
- wbnews/filters/news.py +3 -1
- wbnews/import_export/handlers/news.py +7 -0
- wbnews/import_export/parsers/emails/news.py +0 -9
- wbnews/import_export/parsers/emails/utils.py +2 -2
- wbnews/import_export/parsers/rss/news.py +0 -7
- wbnews/locale/de/LC_MESSAGES/django.mo +0 -0
- wbnews/locale/de/LC_MESSAGES/django.po +93 -39
- wbnews/locale/de/LC_MESSAGES/django.po.translated +173 -0
- wbnews/locale/en/LC_MESSAGES/django.mo +0 -0
- wbnews/locale/en/LC_MESSAGES/django.po +159 -0
- wbnews/locale/fr/LC_MESSAGES/django.mo +0 -0
- wbnews/locale/fr/LC_MESSAGES/django.po +162 -0
- wbnews/migrations/0014_newsrelationship_unique_news_relationship.py +27 -0
- wbnews/models/llm/cleaned_news.py +26 -23
- wbnews/models/news.py +2 -1
- wbnews/models/relationships.py +3 -0
- wbnews/tests/parsers/test_emails.py +6 -6
- wbnews/tests/test_models.py +22 -12
- wbnews/utils.py +1 -1
- wbnews/viewsets/display.py +2 -0
- wbnews/viewsets/endpoints.py +3 -10
- {wbnews-1.50.4.dist-info → wbnews-1.59.2.dist-info}/METADATA +1 -2
- {wbnews-1.50.4.dist-info → wbnews-1.59.2.dist-info}/RECORD +24 -17
- {wbnews-1.50.4.dist-info → wbnews-1.59.2.dist-info}/WHEEL +1 -1
wbnews/filters/news.py
CHANGED
|
@@ -4,7 +4,7 @@ from wbnews.models import News, NewsRelationship, NewsSource
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class NewsFilterSet(wb_filters.FilterSet):
|
|
7
|
-
datetime = wb_filters.DateTimeRangeFilter(
|
|
7
|
+
datetime = wb_filters.DateTimeRangeFilter()
|
|
8
8
|
|
|
9
9
|
class Meta:
|
|
10
10
|
model = News
|
|
@@ -24,6 +24,8 @@ class NewsRelationshipFilterSet(wb_filters.FilterSet):
|
|
|
24
24
|
label_key=NewsSource.get_representation_label_key(),
|
|
25
25
|
method="filter_source",
|
|
26
26
|
)
|
|
27
|
+
content_type = wb_filters.CharFilter(method="fake_filter", hidden=True)
|
|
28
|
+
object_id = wb_filters.CharFilter(method="fake_filter", hidden=True)
|
|
27
29
|
|
|
28
30
|
def filter_datetime(self, queryset, name, value):
|
|
29
31
|
if value:
|
|
@@ -31,6 +31,13 @@ class NewsImportHandler(ImportExportHandler):
|
|
|
31
31
|
else:
|
|
32
32
|
data["guid"] = data["default_guid"]
|
|
33
33
|
|
|
34
|
+
# constrained fields to the max allowed size
|
|
35
|
+
if "title" in data:
|
|
36
|
+
data["title"] = data["title"][:500]
|
|
37
|
+
|
|
38
|
+
if "link" in data:
|
|
39
|
+
data["link"] = data["link"][:1024]
|
|
40
|
+
|
|
34
41
|
def _get_instance(self, data: Dict[str, Any], history: Optional[models.QuerySet] = None, **kwargs) -> models.Model:
|
|
35
42
|
default_guid = data.pop("default_guid")
|
|
36
43
|
instance = None
|
|
@@ -3,7 +3,6 @@ from contextlib import suppress
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
|
|
5
5
|
from django.conf.global_settings import LANGUAGES
|
|
6
|
-
from langdetect import detect, lang_detect_exception
|
|
7
6
|
from wbcore.utils.importlib import import_from_dotted_path
|
|
8
7
|
|
|
9
8
|
from .utils import EmlContentParser
|
|
@@ -37,12 +36,4 @@ def parse(import_source):
|
|
|
37
36
|
"source": parser.source,
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
# Language
|
|
41
|
-
try:
|
|
42
|
-
language = detect(data["description"])
|
|
43
|
-
if language in languages_dict:
|
|
44
|
-
data["language"] = language
|
|
45
|
-
except lang_detect_exception.LangDetectException:
|
|
46
|
-
pass
|
|
47
|
-
|
|
48
39
|
return {"data": [data]}
|
|
@@ -22,10 +22,10 @@ class EmlContentParser:
|
|
|
22
22
|
html = self.get_html(self.message)
|
|
23
23
|
return html.decode(self.encoding) if html else None
|
|
24
24
|
|
|
25
|
-
def get_html(
|
|
25
|
+
def get_html(self, parsed: message.Message) -> bytes | None:
|
|
26
26
|
if parsed.is_multipart():
|
|
27
27
|
for item in parsed.get_payload(): # type:message.Message
|
|
28
|
-
if html :=
|
|
28
|
+
if html := self.get_html(item):
|
|
29
29
|
return html
|
|
30
30
|
elif parsed.get_content_type() == "text/html":
|
|
31
31
|
return parsed.get_payload(decode=True)
|
|
@@ -4,7 +4,6 @@ from time import mktime
|
|
|
4
4
|
|
|
5
5
|
from django.conf.global_settings import LANGUAGES
|
|
6
6
|
from django.utils.html import strip_tags
|
|
7
|
-
from langdetect import detect, lang_detect_exception
|
|
8
7
|
|
|
9
8
|
languages_dict = dict(LANGUAGES)
|
|
10
9
|
|
|
@@ -43,12 +42,6 @@ def parse(import_source):
|
|
|
43
42
|
"link": entry.get("link", None),
|
|
44
43
|
"guid": entry.get("id", None),
|
|
45
44
|
}
|
|
46
|
-
try:
|
|
47
|
-
language = detect(entry["summary"])
|
|
48
|
-
if language in languages_dict:
|
|
49
|
-
res["language"] = language
|
|
50
|
-
except lang_detect_exception.LangDetectException:
|
|
51
|
-
pass
|
|
52
45
|
if published_parsed := entry.get("published_parsed", None):
|
|
53
46
|
updated = datetime.fromtimestamp(mktime(tuple(published_parsed)))
|
|
54
47
|
res["datetime"] = updated.strftime("%Y-%m-%dT%H:%M:%S")
|
|
Binary file
|
|
@@ -3,110 +3,164 @@
|
|
|
3
3
|
# This file is distributed under the same license as the PACKAGE package.
|
|
4
4
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
|
5
5
|
#
|
|
6
|
+
# Translators:
|
|
7
|
+
# Kevin Decoster, 2025
|
|
8
|
+
#
|
|
9
|
+
#, fuzzy
|
|
6
10
|
msgid ""
|
|
7
11
|
msgstr ""
|
|
8
|
-
"Project-Id-Version: \n"
|
|
12
|
+
"Project-Id-Version: PACKAGE VERSION\n"
|
|
9
13
|
"Report-Msgid-Bugs-To: \n"
|
|
10
|
-
"POT-Creation-Date:
|
|
11
|
-
"PO-Revision-Date:
|
|
12
|
-
"Last-Translator: \n"
|
|
13
|
-
"Language-Team: \n"
|
|
14
|
-
"Language: de\n"
|
|
14
|
+
"POT-Creation-Date: 2025-05-30 11:37+0200\n"
|
|
15
|
+
"PO-Revision-Date: 2025-05-30 09:40+0000\n"
|
|
16
|
+
"Last-Translator: Kevin Decoster, 2025\n"
|
|
17
|
+
"Language-Team: German (https://app.transifex.com/stainly/teams/171242/de/)\n"
|
|
15
18
|
"MIME-Version: 1.0\n"
|
|
16
19
|
"Content-Type: text/plain; charset=UTF-8\n"
|
|
17
20
|
"Content-Transfer-Encoding: 8bit\n"
|
|
21
|
+
"Language: de\n"
|
|
18
22
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
|
19
|
-
"X-Generator: Poedit 3.1.1\n"
|
|
20
23
|
|
|
21
|
-
#:
|
|
22
|
-
#:
|
|
24
|
+
#: models/news.py:51 viewsets/display.py:54 viewsets/display.py:100
|
|
25
|
+
#: viewsets/display.py:141
|
|
23
26
|
msgid "Datetime"
|
|
24
27
|
msgstr "Zeitpunkt"
|
|
25
28
|
|
|
26
|
-
#:
|
|
27
|
-
#:
|
|
29
|
+
#: models/news.py:52 serializers.py:21 serializers.py:76
|
|
30
|
+
#: viewsets/display.py:20 viewsets/display.py:55 viewsets/display.py:102
|
|
31
|
+
#: viewsets/display.py:142
|
|
28
32
|
msgid "Title"
|
|
29
33
|
msgstr "Titel"
|
|
30
34
|
|
|
31
|
-
#:
|
|
32
|
-
#:
|
|
35
|
+
#: models/news.py:54 serializers.py:24 serializers.py:77
|
|
36
|
+
#: viewsets/display.py:23 viewsets/display.py:57 viewsets/display.py:104
|
|
37
|
+
#: viewsets/display.py:143
|
|
33
38
|
msgid "Description"
|
|
34
39
|
msgstr "Beschreibung"
|
|
35
40
|
|
|
36
|
-
#:
|
|
41
|
+
#: models/news.py:55 serializers.py:78 viewsets/display.py:56
|
|
42
|
+
#: viewsets/display.py:103
|
|
37
43
|
msgid "Summary"
|
|
38
44
|
msgstr "Zusammenfassung"
|
|
39
45
|
|
|
40
|
-
#:
|
|
41
|
-
#: wbnews/viewsets/display.py:89
|
|
46
|
+
#: models/news.py:56 viewsets/display.py:60 viewsets/display.py:144
|
|
42
47
|
msgid "Language"
|
|
43
48
|
msgstr "Sprache"
|
|
44
49
|
|
|
45
|
-
#:
|
|
50
|
+
#: models/news.py:57
|
|
46
51
|
msgid "Link"
|
|
47
52
|
msgstr "Link"
|
|
48
53
|
|
|
49
|
-
#:
|
|
54
|
+
#: models/news.py:61 viewsets/display.py:59 viewsets/display.py:106
|
|
50
55
|
msgid "Source"
|
|
51
56
|
msgstr "Quelle"
|
|
52
57
|
|
|
53
|
-
#:
|
|
58
|
+
#: models/news.py:64
|
|
59
|
+
msgid "Mark as duplicate"
|
|
60
|
+
msgstr ""
|
|
61
|
+
|
|
62
|
+
#: models/relationships.py:9
|
|
63
|
+
msgid "Positive"
|
|
64
|
+
msgstr ""
|
|
65
|
+
|
|
66
|
+
#: models/relationships.py:10
|
|
67
|
+
msgid "Slightly Positive"
|
|
68
|
+
msgstr ""
|
|
69
|
+
|
|
70
|
+
#: models/relationships.py:11
|
|
71
|
+
msgid "Slightly Negative"
|
|
72
|
+
msgstr ""
|
|
73
|
+
|
|
74
|
+
#: models/relationships.py:12
|
|
75
|
+
msgid "Negative"
|
|
76
|
+
msgstr ""
|
|
77
|
+
|
|
78
|
+
#: serializers.py:22
|
|
54
79
|
msgid "Identifier"
|
|
55
80
|
msgstr "Identifikator"
|
|
56
81
|
|
|
57
|
-
#:
|
|
82
|
+
#: serializers.py:25 viewsets/display.py:22
|
|
58
83
|
msgid "Author"
|
|
59
84
|
msgstr "Autor*in"
|
|
60
85
|
|
|
61
|
-
#:
|
|
86
|
+
#: serializers.py:26
|
|
62
87
|
msgid "Updated"
|
|
63
88
|
msgstr "Geändert"
|
|
64
89
|
|
|
65
|
-
#:
|
|
90
|
+
#: serializers.py:79
|
|
91
|
+
msgid "Date"
|
|
92
|
+
msgstr ""
|
|
93
|
+
|
|
94
|
+
#: serializers.py:92 viewsets/menu.py:6 viewsets/titles.py:24
|
|
95
|
+
msgid "News"
|
|
96
|
+
msgstr "Neuigkeiten"
|
|
97
|
+
|
|
98
|
+
#: viewsets/buttons.py:14
|
|
66
99
|
msgid "Open News"
|
|
67
100
|
msgstr "Neuigkeiten Öffnen"
|
|
68
101
|
|
|
69
|
-
#:
|
|
102
|
+
#: viewsets/buttons.py:21 viewsets/buttons.py:22 viewsets/buttons.py:24
|
|
103
|
+
msgid "Reset relationships"
|
|
104
|
+
msgstr ""
|
|
105
|
+
|
|
106
|
+
#: viewsets/display.py:21
|
|
70
107
|
msgid "RSS feed"
|
|
71
108
|
msgstr "RSS Feed"
|
|
72
109
|
|
|
73
|
-
#:
|
|
110
|
+
#: viewsets/display.py:24
|
|
74
111
|
msgid "Last Update"
|
|
75
112
|
msgstr "Letztes Aktualisierung"
|
|
76
113
|
|
|
77
|
-
#:
|
|
78
|
-
#: wbnews/viewsets/titles.py:23 wbnews_config/menu.py:6
|
|
79
|
-
msgid "News"
|
|
80
|
-
msgstr "Neuigkeiten"
|
|
81
|
-
|
|
82
|
-
#: wbnews/viewsets/display.py:66
|
|
114
|
+
#: viewsets/display.py:61
|
|
83
115
|
msgid "Image"
|
|
84
116
|
msgstr "Bild"
|
|
85
117
|
|
|
86
|
-
#:
|
|
118
|
+
#: viewsets/display.py:93
|
|
119
|
+
msgid "Linked Object"
|
|
120
|
+
msgstr ""
|
|
121
|
+
|
|
122
|
+
#: viewsets/display.py:101
|
|
123
|
+
msgid "Analysis"
|
|
124
|
+
msgstr ""
|
|
125
|
+
|
|
126
|
+
#: viewsets/display.py:105
|
|
127
|
+
msgid "Important"
|
|
128
|
+
msgstr ""
|
|
129
|
+
|
|
130
|
+
#: viewsets/menu.py:11
|
|
131
|
+
msgid "News Relationships"
|
|
132
|
+
msgstr ""
|
|
133
|
+
|
|
134
|
+
#: viewsets/menu.py:17 viewsets/titles.py:9
|
|
87
135
|
msgid "Sources"
|
|
88
136
|
msgstr "Quellen"
|
|
89
137
|
|
|
90
|
-
#:
|
|
138
|
+
#: viewsets/menu.py:23
|
|
91
139
|
msgid "Create Source"
|
|
92
140
|
msgstr "Quelle Erstellen"
|
|
93
141
|
|
|
94
|
-
#:
|
|
142
|
+
#: viewsets/titles.py:13
|
|
95
143
|
#, python-brace-format
|
|
96
144
|
msgid "Source: {source}"
|
|
97
145
|
msgstr "Quelle: {source}"
|
|
98
146
|
|
|
99
|
-
#:
|
|
100
|
-
#, fuzzy
|
|
101
|
-
#| msgid "Source"
|
|
147
|
+
#: viewsets/titles.py:14
|
|
102
148
|
msgid "News Source"
|
|
103
|
-
msgstr "
|
|
149
|
+
msgstr ""
|
|
104
150
|
|
|
105
|
-
#:
|
|
151
|
+
#: viewsets/titles.py:19
|
|
106
152
|
msgid "News Flow"
|
|
107
153
|
msgstr "Nachrichtenfluss"
|
|
108
154
|
|
|
109
|
-
#:
|
|
155
|
+
#: viewsets/titles.py:30
|
|
110
156
|
#, python-brace-format
|
|
111
157
|
msgid "News from {source}"
|
|
112
158
|
msgstr "Neuigkeiten von {source}"
|
|
159
|
+
|
|
160
|
+
#: viewsets/titles.py:36
|
|
161
|
+
msgid "News Article for {}"
|
|
162
|
+
msgstr ""
|
|
163
|
+
|
|
164
|
+
#: viewsets/titles.py:37 viewsets/titles.py:44
|
|
165
|
+
msgid "News Article"
|
|
166
|
+
msgstr ""
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# SOME DESCRIPTIVE TITLE.
|
|
2
|
+
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
|
3
|
+
# This file is distributed under the same license as the PACKAGE package.
|
|
4
|
+
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
|
5
|
+
#
|
|
6
|
+
msgid ""
|
|
7
|
+
msgstr ""
|
|
8
|
+
"Project-Id-Version: \n"
|
|
9
|
+
"Report-Msgid-Bugs-To: \n"
|
|
10
|
+
"POT-Creation-Date: 2025-05-29 13:33+0200\n"
|
|
11
|
+
"PO-Revision-Date: 2022-10-17 14:51+0200\n"
|
|
12
|
+
"Last-Translator: \n"
|
|
13
|
+
"Language-Team: \n"
|
|
14
|
+
"Language: de\n"
|
|
15
|
+
"MIME-Version: 1.0\n"
|
|
16
|
+
"Content-Type: text/plain; charset=UTF-8\n"
|
|
17
|
+
"Content-Transfer-Encoding: 8bit\n"
|
|
18
|
+
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
|
19
|
+
"X-Generator: Poedit 3.1.1\n"
|
|
20
|
+
|
|
21
|
+
#: wbnews/models/news.py:51 wbnews/viewsets/display.py:54
|
|
22
|
+
#: wbnews/viewsets/display.py:100 wbnews/viewsets/display.py:141
|
|
23
|
+
msgid "Datetime"
|
|
24
|
+
msgstr "Zeitpunkt"
|
|
25
|
+
|
|
26
|
+
#: wbnews/models/news.py:52 wbnews/serializers.py:21 wbnews/serializers.py:76
|
|
27
|
+
#: wbnews/viewsets/display.py:20 wbnews/viewsets/display.py:55
|
|
28
|
+
#: wbnews/viewsets/display.py:102 wbnews/viewsets/display.py:142
|
|
29
|
+
msgid "Title"
|
|
30
|
+
msgstr "Titel"
|
|
31
|
+
|
|
32
|
+
#: wbnews/models/news.py:54 wbnews/serializers.py:24 wbnews/serializers.py:77
|
|
33
|
+
#: wbnews/viewsets/display.py:23 wbnews/viewsets/display.py:57
|
|
34
|
+
#: wbnews/viewsets/display.py:104 wbnews/viewsets/display.py:143
|
|
35
|
+
msgid "Description"
|
|
36
|
+
msgstr "Beschreibung"
|
|
37
|
+
|
|
38
|
+
#: wbnews/models/news.py:55 wbnews/serializers.py:78
|
|
39
|
+
#: wbnews/viewsets/display.py:56 wbnews/viewsets/display.py:103
|
|
40
|
+
msgid "Summary"
|
|
41
|
+
msgstr "Zusammenfassung"
|
|
42
|
+
|
|
43
|
+
#: wbnews/models/news.py:56 wbnews/viewsets/display.py:60
|
|
44
|
+
#: wbnews/viewsets/display.py:144
|
|
45
|
+
msgid "Language"
|
|
46
|
+
msgstr "Sprache"
|
|
47
|
+
|
|
48
|
+
#: wbnews/models/news.py:57
|
|
49
|
+
msgid "Link"
|
|
50
|
+
msgstr "Link"
|
|
51
|
+
|
|
52
|
+
#: wbnews/models/news.py:61 wbnews/viewsets/display.py:59
|
|
53
|
+
#: wbnews/viewsets/display.py:106
|
|
54
|
+
msgid "Source"
|
|
55
|
+
msgstr "Quelle"
|
|
56
|
+
|
|
57
|
+
#: wbnews/models/news.py:64
|
|
58
|
+
msgid "Mark as duplicate"
|
|
59
|
+
msgstr ""
|
|
60
|
+
|
|
61
|
+
#: wbnews/models/relationships.py:9
|
|
62
|
+
msgid "Positive"
|
|
63
|
+
msgstr ""
|
|
64
|
+
|
|
65
|
+
#: wbnews/models/relationships.py:10
|
|
66
|
+
msgid "Slightly Positive"
|
|
67
|
+
msgstr ""
|
|
68
|
+
|
|
69
|
+
#: wbnews/models/relationships.py:11
|
|
70
|
+
msgid "Slightly Negative"
|
|
71
|
+
msgstr ""
|
|
72
|
+
|
|
73
|
+
#: wbnews/models/relationships.py:12
|
|
74
|
+
msgid "Negative"
|
|
75
|
+
msgstr ""
|
|
76
|
+
|
|
77
|
+
#: wbnews/serializers.py:22
|
|
78
|
+
msgid "Identifier"
|
|
79
|
+
msgstr "Identifikator"
|
|
80
|
+
|
|
81
|
+
#: wbnews/serializers.py:25 wbnews/viewsets/display.py:22
|
|
82
|
+
msgid "Author"
|
|
83
|
+
msgstr "Autor*in"
|
|
84
|
+
|
|
85
|
+
#: wbnews/serializers.py:26
|
|
86
|
+
msgid "Updated"
|
|
87
|
+
msgstr "Geändert"
|
|
88
|
+
|
|
89
|
+
#: wbnews/serializers.py:79
|
|
90
|
+
#, fuzzy
|
|
91
|
+
#| msgid "Datetime"
|
|
92
|
+
msgid "Date"
|
|
93
|
+
msgstr "Zeitpunkt"
|
|
94
|
+
|
|
95
|
+
#: wbnews/serializers.py:92 wbnews/viewsets/menu.py:6
|
|
96
|
+
#: wbnews/viewsets/titles.py:24 wbnews_config/menu.py:5
|
|
97
|
+
msgid "News"
|
|
98
|
+
msgstr "Neuigkeiten"
|
|
99
|
+
|
|
100
|
+
#: wbnews/viewsets/buttons.py:14
|
|
101
|
+
msgid "Open News"
|
|
102
|
+
msgstr "Neuigkeiten Öffnen"
|
|
103
|
+
|
|
104
|
+
#: wbnews/viewsets/buttons.py:21 wbnews/viewsets/buttons.py:22
|
|
105
|
+
#: wbnews/viewsets/buttons.py:24
|
|
106
|
+
msgid "Reset relationships"
|
|
107
|
+
msgstr ""
|
|
108
|
+
|
|
109
|
+
#: wbnews/viewsets/display.py:21
|
|
110
|
+
msgid "RSS feed"
|
|
111
|
+
msgstr "RSS Feed"
|
|
112
|
+
|
|
113
|
+
#: wbnews/viewsets/display.py:24
|
|
114
|
+
msgid "Last Update"
|
|
115
|
+
msgstr "Letztes Aktualisierung"
|
|
116
|
+
|
|
117
|
+
#: wbnews/viewsets/display.py:61
|
|
118
|
+
msgid "Image"
|
|
119
|
+
msgstr "Bild"
|
|
120
|
+
|
|
121
|
+
#: wbnews/viewsets/display.py:93
|
|
122
|
+
msgid "Linked Object"
|
|
123
|
+
msgstr ""
|
|
124
|
+
|
|
125
|
+
#: wbnews/viewsets/display.py:101
|
|
126
|
+
msgid "Analysis"
|
|
127
|
+
msgstr ""
|
|
128
|
+
|
|
129
|
+
#: wbnews/viewsets/display.py:105
|
|
130
|
+
msgid "Important"
|
|
131
|
+
msgstr ""
|
|
132
|
+
|
|
133
|
+
#: wbnews/viewsets/menu.py:11
|
|
134
|
+
msgid "News Relationships"
|
|
135
|
+
msgstr ""
|
|
136
|
+
|
|
137
|
+
#: wbnews/viewsets/menu.py:17 wbnews/viewsets/titles.py:9
|
|
138
|
+
msgid "Sources"
|
|
139
|
+
msgstr "Quellen"
|
|
140
|
+
|
|
141
|
+
#: wbnews/viewsets/menu.py:23
|
|
142
|
+
msgid "Create Source"
|
|
143
|
+
msgstr "Quelle Erstellen"
|
|
144
|
+
|
|
145
|
+
#: wbnews/viewsets/titles.py:13
|
|
146
|
+
#, python-brace-format
|
|
147
|
+
msgid "Source: {source}"
|
|
148
|
+
msgstr "Quelle: {source}"
|
|
149
|
+
|
|
150
|
+
#: wbnews/viewsets/titles.py:14
|
|
151
|
+
#, fuzzy
|
|
152
|
+
#| msgid "Source"
|
|
153
|
+
msgid "News Source"
|
|
154
|
+
msgstr "Quelle"
|
|
155
|
+
|
|
156
|
+
#: wbnews/viewsets/titles.py:19
|
|
157
|
+
msgid "News Flow"
|
|
158
|
+
msgstr "Nachrichtenfluss"
|
|
159
|
+
|
|
160
|
+
#: wbnews/viewsets/titles.py:30
|
|
161
|
+
#, python-brace-format
|
|
162
|
+
msgid "News from {source}"
|
|
163
|
+
msgstr "Neuigkeiten von {source}"
|
|
164
|
+
|
|
165
|
+
#: wbnews/viewsets/titles.py:36
|
|
166
|
+
msgid "News Article for {}"
|
|
167
|
+
msgstr ""
|
|
168
|
+
|
|
169
|
+
#: wbnews/viewsets/titles.py:37 wbnews/viewsets/titles.py:44
|
|
170
|
+
#, fuzzy
|
|
171
|
+
#| msgid "Source"
|
|
172
|
+
msgid "News Article"
|
|
173
|
+
msgstr "Quelle"
|
|
Binary file
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# SOME DESCRIPTIVE TITLE.
|
|
2
|
+
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
|
3
|
+
# This file is distributed under the same license as the PACKAGE package.
|
|
4
|
+
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
|
5
|
+
#
|
|
6
|
+
msgid ""
|
|
7
|
+
msgstr ""
|
|
8
|
+
"Project-Id-Version: PACKAGE VERSION\n"
|
|
9
|
+
"Report-Msgid-Bugs-To: \n"
|
|
10
|
+
"POT-Creation-Date: 2025-05-30 11:37+0200\n"
|
|
11
|
+
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
|
12
|
+
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
|
13
|
+
"Language-Team: LANGUAGE <LL@li.org>\n"
|
|
14
|
+
"Language: \n"
|
|
15
|
+
"MIME-Version: 1.0\n"
|
|
16
|
+
"Content-Type: text/plain; charset=UTF-8\n"
|
|
17
|
+
"Content-Transfer-Encoding: 8bit\n"
|
|
18
|
+
|
|
19
|
+
#: models/news.py:51 viewsets/display.py:54 viewsets/display.py:100
|
|
20
|
+
#: viewsets/display.py:141
|
|
21
|
+
msgid "Datetime"
|
|
22
|
+
msgstr ""
|
|
23
|
+
|
|
24
|
+
#: models/news.py:52 serializers.py:21 serializers.py:76 viewsets/display.py:20
|
|
25
|
+
#: viewsets/display.py:55 viewsets/display.py:102 viewsets/display.py:142
|
|
26
|
+
msgid "Title"
|
|
27
|
+
msgstr ""
|
|
28
|
+
|
|
29
|
+
#: models/news.py:54 serializers.py:24 serializers.py:77 viewsets/display.py:23
|
|
30
|
+
#: viewsets/display.py:57 viewsets/display.py:104 viewsets/display.py:143
|
|
31
|
+
msgid "Description"
|
|
32
|
+
msgstr ""
|
|
33
|
+
|
|
34
|
+
#: models/news.py:55 serializers.py:78 viewsets/display.py:56
|
|
35
|
+
#: viewsets/display.py:103
|
|
36
|
+
msgid "Summary"
|
|
37
|
+
msgstr ""
|
|
38
|
+
|
|
39
|
+
#: models/news.py:56 viewsets/display.py:60 viewsets/display.py:144
|
|
40
|
+
msgid "Language"
|
|
41
|
+
msgstr ""
|
|
42
|
+
|
|
43
|
+
#: models/news.py:57
|
|
44
|
+
msgid "Link"
|
|
45
|
+
msgstr ""
|
|
46
|
+
|
|
47
|
+
#: models/news.py:61 viewsets/display.py:59 viewsets/display.py:106
|
|
48
|
+
msgid "Source"
|
|
49
|
+
msgstr ""
|
|
50
|
+
|
|
51
|
+
#: models/news.py:64
|
|
52
|
+
msgid "Mark as duplicate"
|
|
53
|
+
msgstr ""
|
|
54
|
+
|
|
55
|
+
#: models/relationships.py:9
|
|
56
|
+
msgid "Positive"
|
|
57
|
+
msgstr ""
|
|
58
|
+
|
|
59
|
+
#: models/relationships.py:10
|
|
60
|
+
msgid "Slightly Positive"
|
|
61
|
+
msgstr ""
|
|
62
|
+
|
|
63
|
+
#: models/relationships.py:11
|
|
64
|
+
msgid "Slightly Negative"
|
|
65
|
+
msgstr ""
|
|
66
|
+
|
|
67
|
+
#: models/relationships.py:12
|
|
68
|
+
msgid "Negative"
|
|
69
|
+
msgstr ""
|
|
70
|
+
|
|
71
|
+
#: serializers.py:22
|
|
72
|
+
msgid "Identifier"
|
|
73
|
+
msgstr ""
|
|
74
|
+
|
|
75
|
+
#: serializers.py:25 viewsets/display.py:22
|
|
76
|
+
msgid "Author"
|
|
77
|
+
msgstr ""
|
|
78
|
+
|
|
79
|
+
#: serializers.py:26
|
|
80
|
+
msgid "Updated"
|
|
81
|
+
msgstr ""
|
|
82
|
+
|
|
83
|
+
#: serializers.py:79
|
|
84
|
+
msgid "Date"
|
|
85
|
+
msgstr ""
|
|
86
|
+
|
|
87
|
+
#: serializers.py:92 viewsets/menu.py:6 viewsets/titles.py:24
|
|
88
|
+
msgid "News"
|
|
89
|
+
msgstr ""
|
|
90
|
+
|
|
91
|
+
#: viewsets/buttons.py:14
|
|
92
|
+
msgid "Open News"
|
|
93
|
+
msgstr ""
|
|
94
|
+
|
|
95
|
+
#: viewsets/buttons.py:21 viewsets/buttons.py:22 viewsets/buttons.py:24
|
|
96
|
+
msgid "Reset relationships"
|
|
97
|
+
msgstr ""
|
|
98
|
+
|
|
99
|
+
#: viewsets/display.py:21
|
|
100
|
+
msgid "RSS feed"
|
|
101
|
+
msgstr ""
|
|
102
|
+
|
|
103
|
+
#: viewsets/display.py:24
|
|
104
|
+
msgid "Last Update"
|
|
105
|
+
msgstr ""
|
|
106
|
+
|
|
107
|
+
#: viewsets/display.py:61
|
|
108
|
+
msgid "Image"
|
|
109
|
+
msgstr ""
|
|
110
|
+
|
|
111
|
+
#: viewsets/display.py:93
|
|
112
|
+
msgid "Linked Object"
|
|
113
|
+
msgstr ""
|
|
114
|
+
|
|
115
|
+
#: viewsets/display.py:101
|
|
116
|
+
msgid "Analysis"
|
|
117
|
+
msgstr ""
|
|
118
|
+
|
|
119
|
+
#: viewsets/display.py:105
|
|
120
|
+
msgid "Important"
|
|
121
|
+
msgstr ""
|
|
122
|
+
|
|
123
|
+
#: viewsets/menu.py:11
|
|
124
|
+
msgid "News Relationships"
|
|
125
|
+
msgstr ""
|
|
126
|
+
|
|
127
|
+
#: viewsets/menu.py:17 viewsets/titles.py:9
|
|
128
|
+
msgid "Sources"
|
|
129
|
+
msgstr ""
|
|
130
|
+
|
|
131
|
+
#: viewsets/menu.py:23
|
|
132
|
+
msgid "Create Source"
|
|
133
|
+
msgstr ""
|
|
134
|
+
|
|
135
|
+
#: viewsets/titles.py:13
|
|
136
|
+
#, python-brace-format
|
|
137
|
+
msgid "Source: {source}"
|
|
138
|
+
msgstr ""
|
|
139
|
+
|
|
140
|
+
#: viewsets/titles.py:14
|
|
141
|
+
msgid "News Source"
|
|
142
|
+
msgstr ""
|
|
143
|
+
|
|
144
|
+
#: viewsets/titles.py:19
|
|
145
|
+
msgid "News Flow"
|
|
146
|
+
msgstr ""
|
|
147
|
+
|
|
148
|
+
#: viewsets/titles.py:30
|
|
149
|
+
#, python-brace-format
|
|
150
|
+
msgid "News from {source}"
|
|
151
|
+
msgstr ""
|
|
152
|
+
|
|
153
|
+
#: viewsets/titles.py:36
|
|
154
|
+
msgid "News Article for {}"
|
|
155
|
+
msgstr ""
|
|
156
|
+
|
|
157
|
+
#: viewsets/titles.py:37 viewsets/titles.py:44
|
|
158
|
+
msgid "News Article"
|
|
159
|
+
msgstr ""
|
|
Binary file
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# SOME DESCRIPTIVE TITLE.
|
|
2
|
+
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
|
3
|
+
# This file is distributed under the same license as the PACKAGE package.
|
|
4
|
+
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
|
5
|
+
#
|
|
6
|
+
#, fuzzy
|
|
7
|
+
msgid ""
|
|
8
|
+
msgstr ""
|
|
9
|
+
"Project-Id-Version: PACKAGE VERSION\n"
|
|
10
|
+
"Report-Msgid-Bugs-To: \n"
|
|
11
|
+
"POT-Creation-Date: 2025-05-30 11:37+0200\n"
|
|
12
|
+
"PO-Revision-Date: 2025-05-30 09:40+0000\n"
|
|
13
|
+
"Language-Team: French (https://app.transifex.com/stainly/teams/171242/fr/)\n"
|
|
14
|
+
"MIME-Version: 1.0\n"
|
|
15
|
+
"Content-Type: text/plain; charset=UTF-8\n"
|
|
16
|
+
"Content-Transfer-Encoding: 8bit\n"
|
|
17
|
+
"Language: fr\n"
|
|
18
|
+
"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
|
|
19
|
+
|
|
20
|
+
#: models/news.py:51 viewsets/display.py:54 viewsets/display.py:100
|
|
21
|
+
#: viewsets/display.py:141
|
|
22
|
+
msgid "Datetime"
|
|
23
|
+
msgstr ""
|
|
24
|
+
|
|
25
|
+
#: models/news.py:52 serializers.py:21 serializers.py:76
|
|
26
|
+
#: viewsets/display.py:20 viewsets/display.py:55 viewsets/display.py:102
|
|
27
|
+
#: viewsets/display.py:142
|
|
28
|
+
msgid "Title"
|
|
29
|
+
msgstr ""
|
|
30
|
+
|
|
31
|
+
#: models/news.py:54 serializers.py:24 serializers.py:77
|
|
32
|
+
#: viewsets/display.py:23 viewsets/display.py:57 viewsets/display.py:104
|
|
33
|
+
#: viewsets/display.py:143
|
|
34
|
+
msgid "Description"
|
|
35
|
+
msgstr ""
|
|
36
|
+
|
|
37
|
+
#: models/news.py:55 serializers.py:78 viewsets/display.py:56
|
|
38
|
+
#: viewsets/display.py:103
|
|
39
|
+
msgid "Summary"
|
|
40
|
+
msgstr ""
|
|
41
|
+
|
|
42
|
+
#: models/news.py:56 viewsets/display.py:60 viewsets/display.py:144
|
|
43
|
+
msgid "Language"
|
|
44
|
+
msgstr ""
|
|
45
|
+
|
|
46
|
+
#: models/news.py:57
|
|
47
|
+
msgid "Link"
|
|
48
|
+
msgstr ""
|
|
49
|
+
|
|
50
|
+
#: models/news.py:61 viewsets/display.py:59 viewsets/display.py:106
|
|
51
|
+
msgid "Source"
|
|
52
|
+
msgstr ""
|
|
53
|
+
|
|
54
|
+
#: models/news.py:64
|
|
55
|
+
msgid "Mark as duplicate"
|
|
56
|
+
msgstr ""
|
|
57
|
+
|
|
58
|
+
#: models/relationships.py:9
|
|
59
|
+
msgid "Positive"
|
|
60
|
+
msgstr ""
|
|
61
|
+
|
|
62
|
+
#: models/relationships.py:10
|
|
63
|
+
msgid "Slightly Positive"
|
|
64
|
+
msgstr ""
|
|
65
|
+
|
|
66
|
+
#: models/relationships.py:11
|
|
67
|
+
msgid "Slightly Negative"
|
|
68
|
+
msgstr ""
|
|
69
|
+
|
|
70
|
+
#: models/relationships.py:12
|
|
71
|
+
msgid "Negative"
|
|
72
|
+
msgstr ""
|
|
73
|
+
|
|
74
|
+
#: serializers.py:22
|
|
75
|
+
msgid "Identifier"
|
|
76
|
+
msgstr ""
|
|
77
|
+
|
|
78
|
+
#: serializers.py:25 viewsets/display.py:22
|
|
79
|
+
msgid "Author"
|
|
80
|
+
msgstr ""
|
|
81
|
+
|
|
82
|
+
#: serializers.py:26
|
|
83
|
+
msgid "Updated"
|
|
84
|
+
msgstr ""
|
|
85
|
+
|
|
86
|
+
#: serializers.py:79
|
|
87
|
+
msgid "Date"
|
|
88
|
+
msgstr ""
|
|
89
|
+
|
|
90
|
+
#: serializers.py:92 viewsets/menu.py:6 viewsets/titles.py:24
|
|
91
|
+
msgid "News"
|
|
92
|
+
msgstr ""
|
|
93
|
+
|
|
94
|
+
#: viewsets/buttons.py:14
|
|
95
|
+
msgid "Open News"
|
|
96
|
+
msgstr ""
|
|
97
|
+
|
|
98
|
+
#: viewsets/buttons.py:21 viewsets/buttons.py:22 viewsets/buttons.py:24
|
|
99
|
+
msgid "Reset relationships"
|
|
100
|
+
msgstr ""
|
|
101
|
+
|
|
102
|
+
#: viewsets/display.py:21
|
|
103
|
+
msgid "RSS feed"
|
|
104
|
+
msgstr ""
|
|
105
|
+
|
|
106
|
+
#: viewsets/display.py:24
|
|
107
|
+
msgid "Last Update"
|
|
108
|
+
msgstr ""
|
|
109
|
+
|
|
110
|
+
#: viewsets/display.py:61
|
|
111
|
+
msgid "Image"
|
|
112
|
+
msgstr ""
|
|
113
|
+
|
|
114
|
+
#: viewsets/display.py:93
|
|
115
|
+
msgid "Linked Object"
|
|
116
|
+
msgstr ""
|
|
117
|
+
|
|
118
|
+
#: viewsets/display.py:101
|
|
119
|
+
msgid "Analysis"
|
|
120
|
+
msgstr ""
|
|
121
|
+
|
|
122
|
+
#: viewsets/display.py:105
|
|
123
|
+
msgid "Important"
|
|
124
|
+
msgstr ""
|
|
125
|
+
|
|
126
|
+
#: viewsets/menu.py:11
|
|
127
|
+
msgid "News Relationships"
|
|
128
|
+
msgstr ""
|
|
129
|
+
|
|
130
|
+
#: viewsets/menu.py:17 viewsets/titles.py:9
|
|
131
|
+
msgid "Sources"
|
|
132
|
+
msgstr ""
|
|
133
|
+
|
|
134
|
+
#: viewsets/menu.py:23
|
|
135
|
+
msgid "Create Source"
|
|
136
|
+
msgstr ""
|
|
137
|
+
|
|
138
|
+
#: viewsets/titles.py:13
|
|
139
|
+
#, python-brace-format
|
|
140
|
+
msgid "Source: {source}"
|
|
141
|
+
msgstr ""
|
|
142
|
+
|
|
143
|
+
#: viewsets/titles.py:14
|
|
144
|
+
msgid "News Source"
|
|
145
|
+
msgstr ""
|
|
146
|
+
|
|
147
|
+
#: viewsets/titles.py:19
|
|
148
|
+
msgid "News Flow"
|
|
149
|
+
msgstr ""
|
|
150
|
+
|
|
151
|
+
#: viewsets/titles.py:30
|
|
152
|
+
#, python-brace-format
|
|
153
|
+
msgid "News from {source}"
|
|
154
|
+
msgstr ""
|
|
155
|
+
|
|
156
|
+
#: viewsets/titles.py:36
|
|
157
|
+
msgid "News Article for {}"
|
|
158
|
+
msgstr ""
|
|
159
|
+
|
|
160
|
+
#: viewsets/titles.py:37 viewsets/titles.py:44
|
|
161
|
+
msgid "News Article"
|
|
162
|
+
msgstr ""
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Generated by Django 5.0.14 on 2025-05-06 14:44
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
from django.db.models import Count
|
|
5
|
+
from tqdm import tqdm
|
|
6
|
+
|
|
7
|
+
def handle_duplicated_relationship(apps, schema_editor):
|
|
8
|
+
NewsRelationship = apps.get_model("wbnews", "NewsRelationship")
|
|
9
|
+
qs = NewsRelationship.objects.values('content_type', 'object_id', 'news').annotate(c=Count('*')).filter(c__gt=1)
|
|
10
|
+
for row in tqdm(qs, total=qs.count()):
|
|
11
|
+
for rel in NewsRelationship.objects.filter(content_type=row['content_type'], news=row['news'], object_id=row["object_id"])[1:]:
|
|
12
|
+
rel.delete()
|
|
13
|
+
|
|
14
|
+
class Migration(migrations.Migration):
|
|
15
|
+
|
|
16
|
+
dependencies = [
|
|
17
|
+
('contenttypes', '0002_remove_content_type_name'),
|
|
18
|
+
('wbnews', '0013_alter_news_datetime'),
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
operations = [
|
|
22
|
+
migrations.RunPython(handle_duplicated_relationship),
|
|
23
|
+
migrations.AddConstraint(
|
|
24
|
+
model_name='newsrelationship',
|
|
25
|
+
constraint=models.UniqueConstraint(fields=('content_type', 'object_id', 'news'), name='unique_news_relationship'),
|
|
26
|
+
),
|
|
27
|
+
]
|
|
@@ -5,27 +5,7 @@ from pydantic import BaseModel, Field
|
|
|
5
5
|
from wbcore.contrib.ai.llm.config import LLMConfig
|
|
6
6
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
|
-
from wbnews.models import News
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def clean_news_prompt(news: "News"):
|
|
12
|
-
return [
|
|
13
|
-
SystemMessage(
|
|
14
|
-
content="I have an HTML email title and body, and I want to extract only the meaningful content in plain text format, removing all metadata, subscription links, and non-essential parts. The output should be HTML- and Markdown-free and should exclude any text related to links, subscription information, or common phrases like 'Unsubscribe' or 'View online'. Only retain the main email content but do not remove any information related to news."
|
|
15
|
-
),
|
|
16
|
-
HumanMessage(
|
|
17
|
-
content=f"Title: {news.title}\n\nDescription: {news.description}",
|
|
18
|
-
),
|
|
19
|
-
]
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def summarize_news_prompt(news: "News"):
|
|
23
|
-
return [
|
|
24
|
-
SystemMessage(content="Given this news description, please extract a short summary"),
|
|
25
|
-
HumanMessage(
|
|
26
|
-
content=f"Description: {news.description}",
|
|
27
|
-
),
|
|
28
|
-
]
|
|
8
|
+
from wbnews.models import News # noqa
|
|
29
9
|
|
|
30
10
|
|
|
31
11
|
class CleanNewsModel(BaseModel):
|
|
@@ -46,18 +26,41 @@ class SummarizedNewsModel(BaseModel):
|
|
|
46
26
|
)
|
|
47
27
|
|
|
48
28
|
|
|
29
|
+
def get_clean_news_config_query(instance):
|
|
30
|
+
return {"description": instance.description, "title": instance.title}
|
|
31
|
+
|
|
32
|
+
|
|
49
33
|
clean_news_config = LLMConfig["News"](
|
|
50
34
|
key="clean",
|
|
51
35
|
output_model=CleanNewsModel,
|
|
52
|
-
prompt=
|
|
36
|
+
prompt=[
|
|
37
|
+
SystemMessage(
|
|
38
|
+
content="I have an HTML email title and body, and I want to extract only the meaningful content in plain text format, removing all metadata, subscription links, and non-essential parts. The output should be HTML- and Markdown-free and should exclude any text related to links, subscription information, or common phrases like 'Unsubscribe' or 'View online'. Only retain the main email content but do not remove any information related to news."
|
|
39
|
+
),
|
|
40
|
+
HumanMessage(
|
|
41
|
+
content="Title: {title}\n\nDescription: {description}",
|
|
42
|
+
),
|
|
43
|
+
],
|
|
53
44
|
on_save=True,
|
|
54
45
|
on_condition=lambda n: n.source.clean_content,
|
|
46
|
+
query=get_clean_news_config_query,
|
|
55
47
|
)
|
|
56
48
|
|
|
49
|
+
|
|
50
|
+
def get_summarized_news_config_query(instance):
|
|
51
|
+
return {"description": instance.description}
|
|
52
|
+
|
|
53
|
+
|
|
57
54
|
summarized_news_config = LLMConfig["News"](
|
|
58
55
|
key="summarize",
|
|
59
56
|
output_model=SummarizedNewsModel,
|
|
60
|
-
prompt=
|
|
57
|
+
prompt=[
|
|
58
|
+
SystemMessage(content="Given this news description, please extract a short summary"),
|
|
59
|
+
HumanMessage(
|
|
60
|
+
content="Description: {description}",
|
|
61
|
+
),
|
|
62
|
+
],
|
|
61
63
|
on_save=True,
|
|
62
64
|
on_condition=lambda n: not n.summary,
|
|
65
|
+
query=get_summarized_news_config_query,
|
|
63
66
|
)
|
wbnews/models/news.py
CHANGED
|
@@ -81,7 +81,8 @@ class News(ImportMixin, WBModel):
|
|
|
81
81
|
"""
|
|
82
82
|
tasks = []
|
|
83
83
|
for sender, task_signature in create_news_relationships.send(sender=News, instance=self):
|
|
84
|
-
|
|
84
|
+
if not isinstance(task_signature, Signature):
|
|
85
|
+
raise AssertionError(self.errors["relationship_signal"].format(sender))
|
|
85
86
|
tasks.append(task_signature)
|
|
86
87
|
if tasks:
|
|
87
88
|
res = chord(tasks, create_relationship.s(self.id))
|
wbnews/models/relationships.py
CHANGED
|
@@ -40,3 +40,6 @@ class NewsRelationship(models.Model):
|
|
|
40
40
|
class Meta:
|
|
41
41
|
verbose_name = "News Relationship"
|
|
42
42
|
indexes = [models.Index(fields=["content_type", "object_id"])]
|
|
43
|
+
constraints = [
|
|
44
|
+
models.UniqueConstraint(name="unique_news_relationship", fields=["content_type", "object_id", "news"])
|
|
45
|
+
]
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
from unittest import
|
|
2
|
-
from unittest.mock import patch, PropertyMock
|
|
1
|
+
from unittest.mock import PropertyMock, patch
|
|
3
2
|
|
|
4
3
|
import pytest
|
|
5
4
|
|
|
@@ -7,19 +6,20 @@ from wbnews.import_export.parsers.emails.utils import EmlContentParser
|
|
|
7
6
|
|
|
8
7
|
|
|
9
8
|
class TestEmlContentParser:
|
|
10
|
-
|
|
11
9
|
@pytest.fixture
|
|
12
10
|
def content_parser(self):
|
|
13
|
-
parser = EmlContentParser(b
|
|
11
|
+
parser = EmlContentParser(b"")
|
|
14
12
|
parser.message = {"From": "main@acme.com"}
|
|
15
13
|
return parser
|
|
16
14
|
|
|
17
15
|
@patch.object(EmlContentParser, "text", new_callable=PropertyMock)
|
|
18
16
|
def test_source_from_in_text(self, mock_text, content_parser):
|
|
19
|
-
mock_text.return_value =
|
|
17
|
+
mock_text.return_value = (
|
|
18
|
+
"some random email content with a From field From: source name <email@test.com> and the rest of the email"
|
|
19
|
+
)
|
|
20
20
|
assert content_parser.source == {"title": "Source Name", "endpoint": "email@test.com", "type": "EMAIL"}
|
|
21
21
|
|
|
22
22
|
@patch.object(EmlContentParser, "text", new_callable=PropertyMock)
|
|
23
|
-
def
|
|
23
|
+
def test_source_from_in_text_alt(self, mock_text, content_parser):
|
|
24
24
|
mock_text.return_value = "some random email content without a From field"
|
|
25
25
|
assert content_parser.source == {"title": "Acme.Com", "endpoint": "main@acme.com", "type": "EMAIL"}
|
wbnews/tests/test_models.py
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
from datetime import timedelta,
|
|
2
|
-
from django.utils import timezone as django_timezone
|
|
1
|
+
from datetime import timedelta, timezone
|
|
3
2
|
from unittest.mock import patch
|
|
4
3
|
|
|
5
4
|
import pytest
|
|
5
|
+
from django.utils import timezone as django_timezone
|
|
6
|
+
from faker import Faker
|
|
6
7
|
|
|
7
8
|
from wbnews.models import News, NewsSource
|
|
8
|
-
from faker import Faker
|
|
9
9
|
|
|
10
10
|
fake = Faker()
|
|
11
11
|
|
|
12
|
+
|
|
12
13
|
@pytest.mark.django_db
|
|
13
14
|
class TestSource:
|
|
14
15
|
@pytest.mark.parametrize("news_source__title", ["source1"])
|
|
@@ -19,17 +20,19 @@ class TestSource:
|
|
|
19
20
|
ns1 = news_source_factory.create()
|
|
20
21
|
ns2 = news_source_factory.create()
|
|
21
22
|
|
|
22
|
-
assert NewsSource.source_dict_to_model({"id": ns1.id, "identifier": ns2.identifier}) == ns1
|
|
23
|
-
assert
|
|
24
|
-
|
|
23
|
+
assert NewsSource.source_dict_to_model({"id": ns1.id, "identifier": ns2.identifier}) == ns1 # priority to "id"
|
|
24
|
+
assert (
|
|
25
|
+
NewsSource.source_dict_to_model({"endpoint": ns1.endpoint, "identifier": ns2.identifier}) == ns2
|
|
26
|
+
) # priority to "identifier"
|
|
27
|
+
assert NewsSource.source_dict_to_model({"endpoint": ns2.endpoint}) == ns2 # exact match on endpoint
|
|
25
28
|
|
|
26
29
|
ns1.endpoint = ".*@test.com"
|
|
27
30
|
ns1.save()
|
|
28
|
-
assert NewsSource.source_dict_to_model({"endpoint": "abc@test.com"}) == ns1
|
|
31
|
+
assert NewsSource.source_dict_to_model({"endpoint": "abc@test.com"}) == ns1 # regex match on endpoint
|
|
29
32
|
|
|
30
33
|
new_source = NewsSource.source_dict_to_model({"endpoint": "abc@main_source.com", "title": "New Source"})
|
|
31
34
|
assert new_source not in [ns1, ns2]
|
|
32
|
-
assert new_source.endpoint == ".*@main_source\.com"
|
|
35
|
+
assert new_source.endpoint == r".*@main_source\.com"
|
|
33
36
|
assert new_source.title == "New Source"
|
|
34
37
|
assert new_source.author == "Main Source"
|
|
35
38
|
|
|
@@ -45,24 +48,31 @@ class TestNews:
|
|
|
45
48
|
|
|
46
49
|
def test_get_default_guid(self):
|
|
47
50
|
assert News.get_default_guid("This is a title", None) == "this-is-a-title"
|
|
48
|
-
assert
|
|
51
|
+
assert (
|
|
52
|
+
News.get_default_guid("This is a title", "http://mylink.com") == "http://mylink.com"
|
|
53
|
+
) # link takes precendence
|
|
49
54
|
assert News.get_default_guid("a" * 24, None, max_length=20) == "a" * 20
|
|
50
55
|
|
|
51
56
|
def test_future_news(self, news_factory):
|
|
52
57
|
# ensure a future datetime always default to now
|
|
53
58
|
now = django_timezone.now()
|
|
54
59
|
future_news = news_factory.create(datetime=now + timedelta(days=1))
|
|
55
|
-
assert (future_news.datetime - now).seconds < 1
|
|
60
|
+
assert (future_news.datetime - now).seconds < 1 # we do that to account for clock difference
|
|
56
61
|
|
|
57
62
|
@patch("wbnews.models.news.detect_near_duplicates")
|
|
58
63
|
def test_handle_duplicates(self, mock_fct, news_factory):
|
|
59
64
|
val_date = fake.date_time(tzinfo=timezone.utc)
|
|
60
|
-
n0 = news_factory.create(
|
|
65
|
+
n0 = news_factory.create(
|
|
66
|
+
datetime=val_date - timedelta(days=1)
|
|
67
|
+
) # we exclude this news from the duplicate search
|
|
61
68
|
n1 = news_factory.create(datetime=val_date)
|
|
62
69
|
n2 = news_factory.create(datetime=val_date)
|
|
63
70
|
n3 = news_factory.create(datetime=val_date)
|
|
64
71
|
|
|
65
|
-
mock_fct.return_value = [
|
|
72
|
+
mock_fct.return_value = [
|
|
73
|
+
n0.id,
|
|
74
|
+
n3.id,
|
|
75
|
+
] # n0 is considered as duplicate but does not fall within the specified daterange so it will not be marked
|
|
66
76
|
News.handle_duplicates(val_date, val_date)
|
|
67
77
|
|
|
68
78
|
n3.refresh_from_db()
|
wbnews/utils.py
CHANGED
|
@@ -11,7 +11,7 @@ logger = logging.getLogger("news")
|
|
|
11
11
|
|
|
12
12
|
def _get_similarity_matrix_df(data: dict[int, str]) -> pd.DataFrame:
|
|
13
13
|
# Convert texts to TF-IDF vectors
|
|
14
|
-
ids, texts = zip(*data.items())
|
|
14
|
+
ids, texts = zip(*data.items(), strict=False)
|
|
15
15
|
vectorizer = TfidfVectorizer()
|
|
16
16
|
tfidf_matrix = vectorizer.fit_transform(texts)
|
|
17
17
|
# Compute pairwise cosine similarity...
|
wbnews/viewsets/display.py
CHANGED
wbnews/viewsets/endpoints.py
CHANGED
|
@@ -7,23 +7,19 @@ class NewsEndpointConfig(EndpointViewConfig):
|
|
|
7
7
|
def get_endpoint(self, **kwargs):
|
|
8
8
|
return None
|
|
9
9
|
|
|
10
|
-
def get_list_endpoint(self, **kwargs):
|
|
11
|
-
return reverse("wbnews:news-list", request=self.request)
|
|
12
|
-
|
|
13
10
|
def get_instance_endpoint(self, **kwargs):
|
|
14
|
-
return self.
|
|
11
|
+
return reverse("wbnews:news-list", request=self.request)
|
|
15
12
|
|
|
16
13
|
|
|
17
14
|
class NewsSourceEndpointConfig(NewsEndpointConfig):
|
|
18
|
-
|
|
19
|
-
return reverse("wbnews:source-news-list", args=[self.view.kwargs["source_id"]], request=self.request)
|
|
15
|
+
pass
|
|
20
16
|
|
|
21
17
|
|
|
22
18
|
class NewsRelationshipEndpointConfig(EndpointViewConfig):
|
|
23
19
|
def get_endpoint(self, **kwargs):
|
|
24
20
|
return reverse("wbnews:newsrelationship-list", args=[], request=self.request)
|
|
25
21
|
|
|
26
|
-
def
|
|
22
|
+
def get_create_endpoint(self, **kwargs):
|
|
27
23
|
params = {}
|
|
28
24
|
if ct := self.view.content_type:
|
|
29
25
|
params["content_type"] = ct.id
|
|
@@ -31,9 +27,6 @@ class NewsRelationshipEndpointConfig(EndpointViewConfig):
|
|
|
31
27
|
params["object_id"] = object_id
|
|
32
28
|
return get_urlencode_endpoint(self.get_endpoint(**kwargs), params)
|
|
33
29
|
|
|
34
|
-
def get_create_endpoint(self, **kwargs):
|
|
35
|
-
return self.get_list_endpoint()
|
|
36
|
-
|
|
37
30
|
# def get_instance_endpoint(self, **kwargs):
|
|
38
31
|
# return reverse("wbnews:news-list", args=[], request=self.request)
|
|
39
32
|
#
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wbnews
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.59.2
|
|
4
4
|
Summary: A workbench module for managing news.
|
|
5
5
|
Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
|
|
6
6
|
Requires-Dist: feedparser==6.*
|
|
7
|
-
Requires-Dist: langdetect==1.*
|
|
8
7
|
Requires-Dist: wbcore
|
|
@@ -7,22 +7,28 @@ wbnews/serializers.py,sha256=KihExQ_GS1fNZnojryEcfU32xlTiPn-K2WGO0Zc84Cw,4732
|
|
|
7
7
|
wbnews/signals.py,sha256=eqipwffwJnDQWUZ9VTKr5Jp-OMXLmVSNwoIsawnCKvM,192
|
|
8
8
|
wbnews/tasks.py,sha256=ce9JbXKnJnf7La74h0LPwm3yrygHNe1BKZju_4m1itw,384
|
|
9
9
|
wbnews/urls.py,sha256=2Gs9RGU8x6tNOLJiuG17n_ik82QVb8jlw7bU07Lk_S4,1008
|
|
10
|
-
wbnews/utils.py,sha256=
|
|
10
|
+
wbnews/utils.py,sha256=h4TdMbUL0qQ2h3o3jEpGuGwT90TWiryVRtBXc7avLW0,1955
|
|
11
11
|
wbnews/filters/__init__.py,sha256=FXJcPsLQePCU-fzjpIu44Dsdu9vCckLHr0Kr-8Z_-C4,59
|
|
12
|
-
wbnews/filters/news.py,sha256=
|
|
12
|
+
wbnews/filters/news.py,sha256=glG45wx_Jxee57H43oqCCJIjQumrNUh8pVXiD0hBM6E,1662
|
|
13
13
|
wbnews/fixtures/wbnews.yaml,sha256=cDu1UWYwIFxz-hdivW7rxLYsNOweBm4GdN1GDsycN90,173164
|
|
14
14
|
wbnews/import_export/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
wbnews/import_export/backends/__init__.py,sha256=_2HnB9uCuGhQDNg-Z0V2uIvKn26LtRBXAxUBoNetBIo,30
|
|
16
16
|
wbnews/import_export/backends/news.py,sha256=QqMeAWYh4U3W4Sng0HlHx11RoR1LV_WZmRUr58dYrVg,1461
|
|
17
17
|
wbnews/import_export/handlers/__init__.py,sha256=zOeENt9cpEcSLG9ZaVb4otmbTnLAa0XdTPsUjD11dXs,36
|
|
18
|
-
wbnews/import_export/handlers/news.py,sha256=
|
|
18
|
+
wbnews/import_export/handlers/news.py,sha256=cdrtWIP3XOU8NMYZKF8VLYH5qHmPLboUm_GwNAvO2ew,2181
|
|
19
19
|
wbnews/import_export/parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
20
|
wbnews/import_export/parsers/emails/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
-
wbnews/import_export/parsers/emails/news.py,sha256=
|
|
22
|
-
wbnews/import_export/parsers/emails/utils.py,sha256=
|
|
21
|
+
wbnews/import_export/parsers/emails/news.py,sha256=8fSgjSPuSmdfGKV1IxLMWriqGhDAG-tiZUyAgWpjZ_M,1236
|
|
22
|
+
wbnews/import_export/parsers/emails/utils.py,sha256=k7R1HZx18FKXRq10COARFgHjjBadsWe1DY3AHCrwHRs,2207
|
|
23
23
|
wbnews/import_export/parsers/rss/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
-
wbnews/import_export/parsers/rss/news.py,sha256=
|
|
25
|
-
wbnews/locale/de/LC_MESSAGES/django.
|
|
24
|
+
wbnews/import_export/parsers/rss/news.py,sha256=H89avqOU_AUAyHiIXd_tfoMbcqy67GgDQ2-ucihDpks,2076
|
|
25
|
+
wbnews/locale/de/LC_MESSAGES/django.mo,sha256=M70N6el-I7EymxHLw9n773Bimbztf8s8equWa9SydBs,1283
|
|
26
|
+
wbnews/locale/de/LC_MESSAGES/django.po,sha256=furtxh-NgkodwE4dv0uXaRtWF2z3NxWtEw64tH9OMN4,3494
|
|
27
|
+
wbnews/locale/de/LC_MESSAGES/django.po.translated,sha256=ILRIDL_vON91Do5QhZr5N85Y4vsOAIHsaU8QX0FiEA4,3962
|
|
28
|
+
wbnews/locale/en/LC_MESSAGES/django.mo,sha256=UXCQbz2AxBvh-IQ7bGgjoBnijo8h9DfE9107A-2Mgkk,337
|
|
29
|
+
wbnews/locale/en/LC_MESSAGES/django.po,sha256=K7SPoXdLZHyOOAoIOEydgJwl1qdQMrK3uapd4EMrVIg,3125
|
|
30
|
+
wbnews/locale/fr/LC_MESSAGES/django.mo,sha256=t4lh3zX7kshbDAFzXa5HU_YGPXkPzKqODNXL2MeZ5KQ,429
|
|
31
|
+
wbnews/locale/fr/LC_MESSAGES/django.po,sha256=kRBjvETEy0GMPqX0H6EArGqrtzhYXwswjUW-JDajlbM,3232
|
|
26
32
|
wbnews/migrations/0001_initial_squashed_0005_alter_news_import_source.py,sha256=4qxqfpAYVeU16GsWaj7kUbtOk0ZLzuECTfzhUfmni2A,14596
|
|
27
33
|
wbnews/migrations/0006_alter_news_language.py,sha256=necqSWKi20sHLok-RO9He3vnmMlmmdx07AiN3lnZPBM,4638
|
|
28
34
|
wbnews/migrations/0007_auto_20240103_0955.py,sha256=YzkH_LSWH_8qdw_BrKaTN5vqLNCPnjMlJZxs03Xbbvw,1478
|
|
@@ -32,27 +38,28 @@ wbnews/migrations/0010_newsrelationship_important.py,sha256=seK-bFIzXa6uzja0gPnR
|
|
|
32
38
|
wbnews/migrations/0011_newsrelationship_content_object_repr.py,sha256=oJUhx__5WI6TjPGPqqEqBgb3yDluc02hhh05nXTjbHw,428
|
|
33
39
|
wbnews/migrations/0012_alter_news_unique_together_news_identifier_and_more.py,sha256=SLtQiREJYMwOytGGLqaCYr_9pSeD_XD5ax7lXSETT3w,3111
|
|
34
40
|
wbnews/migrations/0013_alter_news_datetime.py,sha256=q4Gbxo25jClsv9b1kfTqBLmpPNHAq7LxOgEZeLYYRk4,497
|
|
41
|
+
wbnews/migrations/0014_newsrelationship_unique_news_relationship.py,sha256=aZUHLMKRMaXHJrrbnV9O2ZJRDVjbpkb9NjVPB7VKVW0,1073
|
|
35
42
|
wbnews/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
43
|
wbnews/models/__init__.py,sha256=KKIcQbpCPCVNJUPJs2MtpN9Q2Wb34Qdk-C7w6AAOy7w,99
|
|
37
|
-
wbnews/models/news.py,sha256=
|
|
38
|
-
wbnews/models/relationships.py,sha256=
|
|
44
|
+
wbnews/models/news.py,sha256=yRbOpVG69yssvJPzv9bZJGMoFcb9n2uLaG2mz2j8oxM,5203
|
|
45
|
+
wbnews/models/relationships.py,sha256=bcQbRa-ObS9h67pIuIrodD63RnkzbaOaRT4L6ctBoI0,1841
|
|
39
46
|
wbnews/models/sources.py,sha256=N3-rBg4myeGG_stHvKiNnSoH0yS-LS_Zr_UeKPMBr5s,2629
|
|
40
47
|
wbnews/models/utils.py,sha256=1JQxV4WxmmFGl7MdVtJoEeao3vnqqVwCi7mhSToaUvM,600
|
|
41
|
-
wbnews/models/llm/cleaned_news.py,sha256=
|
|
48
|
+
wbnews/models/llm/cleaned_news.py,sha256=PA0McsgMmR0TOZUNWs55sBnsCXGbaC61VtVFbJ8vx28,2226
|
|
42
49
|
wbnews/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
43
50
|
wbnews/tests/conftest.py,sha256=UEZOYH5Av-Cqqq5t8WQCR7QJdNnajhthBPpF8AnwQjY,186
|
|
44
|
-
wbnews/tests/test_models.py,sha256=
|
|
51
|
+
wbnews/tests/test_models.py,sha256=op79cYyz9f5dX1uRBztw-E0J4HYAY8K-7cz4kBCN0qk,3278
|
|
45
52
|
wbnews/tests/test_utils.py,sha256=PsyHH_F_y-Uy3mdVgDINH19bPEd1bmZT_UQ7yu87BNU,278
|
|
46
53
|
wbnews/tests/tests.py,sha256=OADY-vbKZBe0bjjVEO1KNzRYAP2JA9mWkO9pZuh1TSs,280
|
|
47
54
|
wbnews/tests/parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
|
-
wbnews/tests/parsers/test_emails.py,sha256=
|
|
55
|
+
wbnews/tests/parsers/test_emails.py,sha256=dStdTPI7Ns1jSw5PDbDEc84FJaoNPnBHC4BskWDDMxY,1073
|
|
49
56
|
wbnews/viewsets/__init__.py,sha256=2OhRiJy0MVK9TdMpFmS9x9jl_gM-CjBxUoUHlZB3_8U,531
|
|
50
57
|
wbnews/viewsets/buttons.py,sha256=6m_IdRYCQgTH4mD51DUoOa8Vx1y1-2zPQydUdvL_nF4,1788
|
|
51
|
-
wbnews/viewsets/display.py,sha256=
|
|
52
|
-
wbnews/viewsets/endpoints.py,sha256=
|
|
58
|
+
wbnews/viewsets/display.py,sha256=fwMZLlwxHcNHM4AppwYpYTOicLD_pXaQ4QeFeIl4whk,5315
|
|
59
|
+
wbnews/viewsets/endpoints.py,sha256=2slGDJin_SA2heWsIaMdNTUN46FqZJvusbY1jhAaeZM,1165
|
|
53
60
|
wbnews/viewsets/menu.py,sha256=XTShfTIykN9t7oclosPfFVb1r6o46QyglEEe1C7QCMk,979
|
|
54
61
|
wbnews/viewsets/titles.py,sha256=iMyiGBMBpzng8s2ySVLqEOwueHRnAoEuc75dt9nCPjc,1367
|
|
55
62
|
wbnews/viewsets/views.py,sha256=SMCDT6bJMQblyqX5cQ7X3qDe0zxTVWh1q7Aqj0fBo2Q,6795
|
|
56
|
-
wbnews-1.
|
|
57
|
-
wbnews-1.
|
|
58
|
-
wbnews-1.
|
|
63
|
+
wbnews-1.59.2.dist-info/METADATA,sha256=2XmQqal4PTp0zfuGKmHVbScPKO4YMeLgkPJt9IuKnLY,215
|
|
64
|
+
wbnews-1.59.2.dist-info/WHEEL,sha256=aha0VrrYvgDJ3Xxl3db_g_MDIW-ZexDdrc_m-Hk8YY4,105
|
|
65
|
+
wbnews-1.59.2.dist-info/RECORD,,
|