wbnews 1.46.12__py2.py3-none-any.whl → 1.60.1__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/admin.py +4 -1
- wbnews/factories.py +7 -5
- wbnews/filters/__init__.py +1 -1
- wbnews/filters/news.py +39 -2
- wbnews/import_export/backends/news.py +3 -3
- wbnews/import_export/handlers/news.py +35 -3
- wbnews/import_export/parsers/emails/news.py +2 -11
- wbnews/import_export/parsers/emails/utils.py +16 -12
- wbnews/import_export/parsers/rss/news.py +3 -9
- wbnews/locale/de/LC_MESSAGES/django.mo +0 -0
- wbnews/locale/de/LC_MESSAGES/django.po +92 -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 +161 -0
- wbnews/migrations/0012_alter_news_unique_together_news_identifier_and_more.py +91 -0
- wbnews/migrations/0013_alter_news_datetime.py +19 -0
- wbnews/migrations/0014_newsrelationship_unique_news_relationship.py +27 -0
- wbnews/models/llm/cleaned_news.py +26 -23
- wbnews/models/news.py +37 -22
- wbnews/models/relationships.py +20 -1
- wbnews/models/sources.py +35 -5
- wbnews/models/utils.py +15 -0
- wbnews/serializers.py +16 -7
- wbnews/tasks.py +17 -0
- wbnews/tests/parsers/__init__.py +0 -0
- wbnews/tests/parsers/test_emails.py +25 -0
- wbnews/tests/test_models.py +65 -0
- wbnews/tests/test_utils.py +7 -0
- wbnews/utils.py +57 -0
- wbnews/viewsets/display.py +25 -29
- wbnews/viewsets/endpoints.py +11 -6
- wbnews/viewsets/views.py +5 -4
- {wbnews-1.46.12.dist-info → wbnews-1.60.1.dist-info}/METADATA +1 -2
- wbnews-1.60.1.dist-info/RECORD +65 -0
- {wbnews-1.46.12.dist-info → wbnews-1.60.1.dist-info}/WHEEL +1 -1
- wbnews-1.46.12.dist-info/RECORD +0 -50
wbnews/admin.py
CHANGED
|
@@ -20,8 +20,11 @@ class NewsAdmin(admin.ModelAdmin):
|
|
|
20
20
|
|
|
21
21
|
list_filter = ("source",)
|
|
22
22
|
|
|
23
|
+
def get_queryset(self, request):
|
|
24
|
+
return News.all_objects.select_related("source")
|
|
25
|
+
|
|
23
26
|
|
|
24
27
|
@admin.register(NewsSource)
|
|
25
28
|
class NewsSourceAdmin(admin.ModelAdmin):
|
|
26
|
-
search_fields = ("type", "title", "identifier", "description", "author", "
|
|
29
|
+
search_fields = ("type", "title", "identifier", "description", "author", "endpoint")
|
|
27
30
|
list_filter = ("type",)
|
wbnews/factories.py
CHANGED
|
@@ -12,10 +12,10 @@ faker = Factory.create()
|
|
|
12
12
|
class NewsSourceFactory(factory.django.DjangoModelFactory):
|
|
13
13
|
title = factory.Sequence(lambda n: f"source_{n}")
|
|
14
14
|
identifier = factory.Sequence(lambda n: f"http://myurl_{n}.com")
|
|
15
|
-
image =
|
|
15
|
+
image = factory.Faker("url")
|
|
16
16
|
description = factory.Faker("sentence", nb_words=32)
|
|
17
|
-
author =
|
|
18
|
-
|
|
17
|
+
author = factory.Faker("name")
|
|
18
|
+
endpoint = factory.Faker("url")
|
|
19
19
|
|
|
20
20
|
class Meta:
|
|
21
21
|
model = NewsSource
|
|
@@ -23,12 +23,14 @@ class NewsSourceFactory(factory.django.DjangoModelFactory):
|
|
|
23
23
|
|
|
24
24
|
class NewsFactory(factory.django.DjangoModelFactory):
|
|
25
25
|
datetime = factory.LazyFunction(timezone.now)
|
|
26
|
-
title = factory.
|
|
26
|
+
title = factory.Faker("sentence", nb_words=32)
|
|
27
27
|
description = factory.Faker("sentence", nb_words=32)
|
|
28
28
|
summary = factory.Faker("sentence", nb_words=32)
|
|
29
29
|
language = factory.Iterator(langs)
|
|
30
|
-
link =
|
|
30
|
+
link = factory.Faker("url")
|
|
31
|
+
guid = factory.LazyAttribute(lambda o: News.get_default_guid(o.title, o.link))
|
|
31
32
|
source = factory.SubFactory(NewsSourceFactory)
|
|
32
33
|
|
|
33
34
|
class Meta:
|
|
34
35
|
model = News
|
|
36
|
+
django_get_or_create = ("guid",)
|
wbnews/filters/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
from .news import NewsFilterSet
|
|
1
|
+
from .news import NewsFilterSet, NewsRelationshipFilterSet
|
wbnews/filters/news.py
CHANGED
|
@@ -1,9 +1,46 @@
|
|
|
1
1
|
from wbcore import filters as wb_filters
|
|
2
2
|
|
|
3
|
-
from wbnews.models import News
|
|
3
|
+
from wbnews.models import News, NewsRelationship, NewsSource
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class NewsFilterSet(wb_filters.FilterSet):
|
|
7
|
+
datetime = wb_filters.DateTimeRangeFilter()
|
|
8
|
+
|
|
7
9
|
class Meta:
|
|
8
10
|
model = News
|
|
9
|
-
fields = {"title": ["icontains"], "
|
|
11
|
+
fields = {"title": ["icontains"], "source": ["exact"], "language": ["exact"]}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class NewsRelationshipFilterSet(wb_filters.FilterSet):
|
|
15
|
+
datetime = wb_filters.DateTimeRangeFilter(method="filter_datetime")
|
|
16
|
+
title = wb_filters.CharFilter(lookup_expr="icontains")
|
|
17
|
+
description = wb_filters.CharFilter(lookup_expr="icontains")
|
|
18
|
+
summary = wb_filters.CharFilter(lookup_expr="icontains")
|
|
19
|
+
source = wb_filters.ModelMultipleChoiceFilter(
|
|
20
|
+
label="Source",
|
|
21
|
+
queryset=NewsSource.objects.all(),
|
|
22
|
+
endpoint=NewsSource.get_representation_endpoint(),
|
|
23
|
+
value_key=NewsSource.get_representation_value_key(),
|
|
24
|
+
label_key=NewsSource.get_representation_label_key(),
|
|
25
|
+
method="filter_source",
|
|
26
|
+
)
|
|
27
|
+
content_type = wb_filters.CharFilter(method="fake_filter", hidden=True)
|
|
28
|
+
object_id = wb_filters.CharFilter(method="fake_filter", hidden=True)
|
|
29
|
+
|
|
30
|
+
def filter_datetime(self, queryset, name, value):
|
|
31
|
+
if value:
|
|
32
|
+
return queryset.filter(news__datetime__gte=value.lower, news__datetime__lte=value.upper)
|
|
33
|
+
return queryset
|
|
34
|
+
|
|
35
|
+
def filter_source(self, queryset, name, value):
|
|
36
|
+
if value:
|
|
37
|
+
return queryset.filter(news__source__in=value)
|
|
38
|
+
return queryset
|
|
39
|
+
|
|
40
|
+
class Meta:
|
|
41
|
+
model = NewsRelationship
|
|
42
|
+
fields = {
|
|
43
|
+
"analysis": ["icontains"],
|
|
44
|
+
"sentiment": ["exact"],
|
|
45
|
+
"important": ["exact"],
|
|
46
|
+
}
|
|
@@ -15,17 +15,17 @@ from wbnews.models import NewsSource
|
|
|
15
15
|
@register("News RSS Backend", save_data_in_import_source=True)
|
|
16
16
|
class DataBackend(AbstractDataBackend):
|
|
17
17
|
def is_object_valid(self, obj: "NewsSource") -> bool:
|
|
18
|
-
return obj.type == NewsSource.Type.RSS and obj.is_active and obj.
|
|
18
|
+
return obj.type == NewsSource.Type.RSS and obj.is_active and obj.endpoint
|
|
19
19
|
|
|
20
20
|
def get_default_queryset(self) -> QuerySet["NewsSource"]:
|
|
21
|
-
return NewsSource.objects.filter(type=NewsSource.Type.RSS, is_active=True,
|
|
21
|
+
return NewsSource.objects.filter(type=NewsSource.Type.RSS, is_active=True, endpoint__isnull=False)
|
|
22
22
|
|
|
23
23
|
def get_files(
|
|
24
24
|
self, execution_time: datetime, queryset=None, **kwargs
|
|
25
25
|
) -> Generator[tuple[str, BytesIO], None, None] | None:
|
|
26
26
|
if queryset is not None:
|
|
27
27
|
for source in queryset:
|
|
28
|
-
data = feedparser.parse(source.
|
|
28
|
+
data = feedparser.parse(source.endpoint)
|
|
29
29
|
if not data.get("bozo_exception"):
|
|
30
30
|
data["news_source"] = source.id
|
|
31
31
|
content_file = BytesIO()
|
|
@@ -1,24 +1,56 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
-
from typing import Any, Dict, Optional
|
|
2
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional
|
|
3
3
|
|
|
4
4
|
import pytz
|
|
5
|
+
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
|
|
5
6
|
from django.db import models
|
|
6
7
|
from django.utils import timezone
|
|
8
|
+
from slugify import slugify
|
|
7
9
|
from wbcore.contrib.io.imports import ImportExportHandler
|
|
8
10
|
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from wbnews.models import News
|
|
13
|
+
|
|
9
14
|
|
|
10
15
|
class NewsImportHandler(ImportExportHandler):
|
|
11
16
|
MODEL_APP_LABEL = "wbnews.News"
|
|
17
|
+
model: "News"
|
|
12
18
|
|
|
13
19
|
def _deserialize(self, data: Dict[str, Any]):
|
|
14
|
-
|
|
20
|
+
from wbnews.models.sources import NewsSource
|
|
21
|
+
|
|
22
|
+
data["source"] = NewsSource.source_dict_to_model(data["source"])
|
|
15
23
|
if parsed_datetime := data.get("datetime", None):
|
|
16
24
|
data["datetime"] = pytz.utc.localize(datetime.strptime(parsed_datetime, "%Y-%m-%dT%H:%M:%S"))
|
|
17
25
|
else:
|
|
18
26
|
data["datetime"] = timezone.now()
|
|
19
27
|
|
|
28
|
+
data["default_guid"] = self.model.get_default_guid(data["title"], data.get("link", None))
|
|
29
|
+
if guid := data.get("guid", None):
|
|
30
|
+
data["guid"] = slugify(guid)
|
|
31
|
+
else:
|
|
32
|
+
data["guid"] = data["default_guid"]
|
|
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
|
+
|
|
20
41
|
def _get_instance(self, data: Dict[str, Any], history: Optional[models.QuerySet] = None, **kwargs) -> models.Model:
|
|
21
|
-
|
|
42
|
+
default_guid = data.pop("default_guid")
|
|
43
|
+
instance = None
|
|
44
|
+
try:
|
|
45
|
+
instance = self.model.all_objects.get(models.Q(guid=data["guid"]) | models.Q(guid=default_guid))
|
|
46
|
+
except MultipleObjectsReturned:
|
|
47
|
+
instance = self.model.all_objects.get(models.Q(guid=data["guid"]))
|
|
48
|
+
except ObjectDoesNotExist:
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
if instance:
|
|
52
|
+
self.import_source.log += f"\nFound existing news {instance.id} (guid: {instance.guid})"
|
|
53
|
+
return instance
|
|
22
54
|
|
|
23
55
|
def _create_instance(self, data: Dict[str, Any], **kwargs) -> models.Model:
|
|
24
56
|
self.import_source.log += "\nCreate News."
|
|
@@ -3,8 +3,7 @@ from contextlib import suppress
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
|
|
5
5
|
from django.conf.global_settings import LANGUAGES
|
|
6
|
-
from
|
|
7
|
-
from wbcore.utils.importlib import import_from_dotted_path
|
|
6
|
+
from django.utils.module_loading import import_string
|
|
8
7
|
|
|
9
8
|
from .utils import EmlContentParser
|
|
10
9
|
|
|
@@ -27,7 +26,7 @@ def parse(import_source):
|
|
|
27
26
|
# If source define a custom html parser, we import it and convert the returned html
|
|
28
27
|
if html_parser_path := import_source.source.import_parameters.get("html_parser", None):
|
|
29
28
|
with suppress(ModuleNotFoundError):
|
|
30
|
-
html_parser =
|
|
29
|
+
html_parser = import_string(html_parser_path)
|
|
31
30
|
html = html_parser(html)
|
|
32
31
|
|
|
33
32
|
data = {
|
|
@@ -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]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import re
|
|
1
2
|
from email import message, parser
|
|
2
3
|
from email.utils import parseaddr, parsedate_to_datetime
|
|
3
4
|
|
|
@@ -21,10 +22,10 @@ class EmlContentParser:
|
|
|
21
22
|
html = self.get_html(self.message)
|
|
22
23
|
return html.decode(self.encoding) if html else None
|
|
23
24
|
|
|
24
|
-
def get_html(
|
|
25
|
+
def get_html(self, parsed: message.Message) -> bytes | None:
|
|
25
26
|
if parsed.is_multipart():
|
|
26
27
|
for item in parsed.get_payload(): # type:message.Message
|
|
27
|
-
if html :=
|
|
28
|
+
if html := self.get_html(item):
|
|
28
29
|
return html
|
|
29
30
|
elif parsed.get_content_type() == "text/html":
|
|
30
31
|
return parsed.get_payload(decode=True)
|
|
@@ -47,15 +48,18 @@ class EmlContentParser:
|
|
|
47
48
|
|
|
48
49
|
@property
|
|
49
50
|
def source(self) -> dict[str, any]:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
match = re.search(r"From:(.*)<([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})>", self.text)
|
|
52
|
+
# we search first for forwarding info
|
|
53
|
+
if match:
|
|
54
|
+
name = match.group(1)
|
|
55
|
+
email = match.group(2)
|
|
56
|
+
else:
|
|
57
|
+
# otherwise we default to the From attribute
|
|
58
|
+
name, email = parseaddr(self.message["From"])
|
|
59
|
+
|
|
53
60
|
if not email:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"author": name,
|
|
59
|
-
"url": email,
|
|
60
|
-
}
|
|
61
|
+
raise ValueError("Couldn't find valid source data")
|
|
62
|
+
if not name:
|
|
63
|
+
name = email
|
|
64
|
+
source = {"title": name.split("@")[-1].strip().title(), "endpoint": email, "type": "EMAIL"}
|
|
61
65
|
return source
|
|
@@ -4,13 +4,12 @@ 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
|
|
|
11
10
|
|
|
12
11
|
def _get_source(d):
|
|
13
|
-
source = {}
|
|
12
|
+
source = {"type": "RSS"}
|
|
14
13
|
if source_id := d.get("news_source"):
|
|
15
14
|
source["id"] = source_id
|
|
16
15
|
else:
|
|
@@ -23,7 +22,7 @@ def _get_source(d):
|
|
|
23
22
|
if "href" in d["feed"]:
|
|
24
23
|
source["identifier"] = d["feed"]["href"]
|
|
25
24
|
if "link" in d["feed"]:
|
|
26
|
-
source["
|
|
25
|
+
source["endpoint"] = d["feed"]["link"]
|
|
27
26
|
return source
|
|
28
27
|
|
|
29
28
|
|
|
@@ -41,13 +40,8 @@ def parse(import_source):
|
|
|
41
40
|
"source": source,
|
|
42
41
|
"title": strip_tags(entry.get("title", "")).strip(),
|
|
43
42
|
"link": entry.get("link", None),
|
|
43
|
+
"guid": entry.get("id", None),
|
|
44
44
|
}
|
|
45
|
-
try:
|
|
46
|
-
language = detect(entry["summary"])
|
|
47
|
-
if language in languages_dict:
|
|
48
|
-
res["language"] = language
|
|
49
|
-
except lang_detect_exception.LangDetectException:
|
|
50
|
-
pass
|
|
51
45
|
if published_parsed := entry.get("published_parsed", None):
|
|
52
46
|
updated = datetime.fromtimestamp(mktime(tuple(published_parsed)))
|
|
53
47
|
res["datetime"] = updated.strftime("%Y-%m-%dT%H:%M:%S")
|
|
Binary file
|
|
@@ -3,110 +3,163 @@
|
|
|
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
|
+
#
|
|
6
9
|
msgid ""
|
|
7
10
|
msgstr ""
|
|
8
|
-
"Project-Id-Version: \n"
|
|
11
|
+
"Project-Id-Version: PACKAGE VERSION\n"
|
|
9
12
|
"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"
|
|
13
|
+
"POT-Creation-Date: 2026-01-16 14:04+0100\n"
|
|
14
|
+
"PO-Revision-Date: 2025-05-30 09:40+0000\n"
|
|
15
|
+
"Last-Translator: Kevin Decoster, 2025\n"
|
|
16
|
+
"Language-Team: German (https://app.transifex.com/stainly/teams/171242/de/)\n"
|
|
15
17
|
"MIME-Version: 1.0\n"
|
|
16
18
|
"Content-Type: text/plain; charset=UTF-8\n"
|
|
17
19
|
"Content-Transfer-Encoding: 8bit\n"
|
|
20
|
+
"Language: de\n"
|
|
18
21
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
|
19
|
-
"X-Generator: Poedit 3.1.1\n"
|
|
20
22
|
|
|
21
|
-
#:
|
|
22
|
-
#:
|
|
23
|
+
#: models/news.py:52 viewsets/display.py:54 viewsets/display.py:100
|
|
24
|
+
#: viewsets/display.py:141
|
|
23
25
|
msgid "Datetime"
|
|
24
26
|
msgstr "Zeitpunkt"
|
|
25
27
|
|
|
26
|
-
#:
|
|
27
|
-
#:
|
|
28
|
+
#: models/news.py:53 serializers.py:21 serializers.py:76
|
|
29
|
+
#: viewsets/display.py:20 viewsets/display.py:55 viewsets/display.py:102
|
|
30
|
+
#: viewsets/display.py:142
|
|
28
31
|
msgid "Title"
|
|
29
32
|
msgstr "Titel"
|
|
30
33
|
|
|
31
|
-
#:
|
|
32
|
-
#:
|
|
34
|
+
#: models/news.py:55 serializers.py:24 serializers.py:77
|
|
35
|
+
#: viewsets/display.py:23 viewsets/display.py:57 viewsets/display.py:104
|
|
36
|
+
#: viewsets/display.py:143
|
|
33
37
|
msgid "Description"
|
|
34
38
|
msgstr "Beschreibung"
|
|
35
39
|
|
|
36
|
-
#:
|
|
40
|
+
#: models/news.py:56 serializers.py:78 viewsets/display.py:56
|
|
41
|
+
#: viewsets/display.py:103
|
|
37
42
|
msgid "Summary"
|
|
38
43
|
msgstr "Zusammenfassung"
|
|
39
44
|
|
|
40
|
-
#:
|
|
41
|
-
#: wbnews/viewsets/display.py:89
|
|
45
|
+
#: models/news.py:57 viewsets/display.py:60 viewsets/display.py:144
|
|
42
46
|
msgid "Language"
|
|
43
47
|
msgstr "Sprache"
|
|
44
48
|
|
|
45
|
-
#:
|
|
49
|
+
#: models/news.py:58
|
|
46
50
|
msgid "Link"
|
|
47
51
|
msgstr "Link"
|
|
48
52
|
|
|
49
|
-
#:
|
|
53
|
+
#: models/news.py:62 viewsets/display.py:59 viewsets/display.py:106
|
|
50
54
|
msgid "Source"
|
|
51
55
|
msgstr "Quelle"
|
|
52
56
|
|
|
53
|
-
#:
|
|
57
|
+
#: models/news.py:65
|
|
58
|
+
msgid "Mark as duplicate"
|
|
59
|
+
msgstr ""
|
|
60
|
+
|
|
61
|
+
#: models/relationships.py:9
|
|
62
|
+
msgid "Positive"
|
|
63
|
+
msgstr ""
|
|
64
|
+
|
|
65
|
+
#: models/relationships.py:10
|
|
66
|
+
msgid "Slightly Positive"
|
|
67
|
+
msgstr ""
|
|
68
|
+
|
|
69
|
+
#: models/relationships.py:11
|
|
70
|
+
msgid "Slightly Negative"
|
|
71
|
+
msgstr ""
|
|
72
|
+
|
|
73
|
+
#: models/relationships.py:12
|
|
74
|
+
msgid "Negative"
|
|
75
|
+
msgstr ""
|
|
76
|
+
|
|
77
|
+
#: serializers.py:22
|
|
54
78
|
msgid "Identifier"
|
|
55
79
|
msgstr "Identifikator"
|
|
56
80
|
|
|
57
|
-
#:
|
|
81
|
+
#: serializers.py:25 viewsets/display.py:22
|
|
58
82
|
msgid "Author"
|
|
59
83
|
msgstr "Autor*in"
|
|
60
84
|
|
|
61
|
-
#:
|
|
85
|
+
#: serializers.py:26
|
|
62
86
|
msgid "Updated"
|
|
63
87
|
msgstr "Geändert"
|
|
64
88
|
|
|
65
|
-
#:
|
|
89
|
+
#: serializers.py:79
|
|
90
|
+
msgid "Date"
|
|
91
|
+
msgstr ""
|
|
92
|
+
|
|
93
|
+
#: serializers.py:92 viewsets/menu.py:6 viewsets/titles.py:24
|
|
94
|
+
msgid "News"
|
|
95
|
+
msgstr "Neuigkeiten"
|
|
96
|
+
|
|
97
|
+
#: viewsets/buttons.py:14
|
|
66
98
|
msgid "Open News"
|
|
67
99
|
msgstr "Neuigkeiten Öffnen"
|
|
68
100
|
|
|
69
|
-
#:
|
|
101
|
+
#: viewsets/buttons.py:21 viewsets/buttons.py:22 viewsets/buttons.py:24
|
|
102
|
+
msgid "Reset relationships"
|
|
103
|
+
msgstr ""
|
|
104
|
+
|
|
105
|
+
#: viewsets/display.py:21
|
|
70
106
|
msgid "RSS feed"
|
|
71
107
|
msgstr "RSS Feed"
|
|
72
108
|
|
|
73
|
-
#:
|
|
109
|
+
#: viewsets/display.py:24
|
|
74
110
|
msgid "Last Update"
|
|
75
111
|
msgstr "Letztes Aktualisierung"
|
|
76
112
|
|
|
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
|
|
113
|
+
#: viewsets/display.py:61
|
|
83
114
|
msgid "Image"
|
|
84
115
|
msgstr "Bild"
|
|
85
116
|
|
|
86
|
-
#:
|
|
117
|
+
#: viewsets/display.py:93
|
|
118
|
+
msgid "Linked Object"
|
|
119
|
+
msgstr ""
|
|
120
|
+
|
|
121
|
+
#: viewsets/display.py:101
|
|
122
|
+
msgid "Analysis"
|
|
123
|
+
msgstr ""
|
|
124
|
+
|
|
125
|
+
#: viewsets/display.py:105
|
|
126
|
+
msgid "Important"
|
|
127
|
+
msgstr ""
|
|
128
|
+
|
|
129
|
+
#: viewsets/menu.py:11
|
|
130
|
+
msgid "News Relationships"
|
|
131
|
+
msgstr ""
|
|
132
|
+
|
|
133
|
+
#: viewsets/menu.py:17 viewsets/titles.py:9
|
|
87
134
|
msgid "Sources"
|
|
88
135
|
msgstr "Quellen"
|
|
89
136
|
|
|
90
|
-
#:
|
|
137
|
+
#: viewsets/menu.py:23
|
|
91
138
|
msgid "Create Source"
|
|
92
139
|
msgstr "Quelle Erstellen"
|
|
93
140
|
|
|
94
|
-
#:
|
|
141
|
+
#: viewsets/titles.py:13
|
|
95
142
|
#, python-brace-format
|
|
96
143
|
msgid "Source: {source}"
|
|
97
144
|
msgstr "Quelle: {source}"
|
|
98
145
|
|
|
99
|
-
#:
|
|
100
|
-
#, fuzzy
|
|
101
|
-
#| msgid "Source"
|
|
146
|
+
#: viewsets/titles.py:14
|
|
102
147
|
msgid "News Source"
|
|
103
|
-
msgstr "
|
|
148
|
+
msgstr ""
|
|
104
149
|
|
|
105
|
-
#:
|
|
150
|
+
#: viewsets/titles.py:19
|
|
106
151
|
msgid "News Flow"
|
|
107
152
|
msgstr "Nachrichtenfluss"
|
|
108
153
|
|
|
109
|
-
#:
|
|
154
|
+
#: viewsets/titles.py:30
|
|
110
155
|
#, python-brace-format
|
|
111
156
|
msgid "News from {source}"
|
|
112
157
|
msgstr "Neuigkeiten von {source}"
|
|
158
|
+
|
|
159
|
+
#: viewsets/titles.py:36
|
|
160
|
+
msgid "News Article for {}"
|
|
161
|
+
msgstr ""
|
|
162
|
+
|
|
163
|
+
#: viewsets/titles.py:37 viewsets/titles.py:44
|
|
164
|
+
msgid "News Article"
|
|
165
|
+
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
|