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.
Files changed (38) hide show
  1. wbnews/admin.py +4 -1
  2. wbnews/factories.py +7 -5
  3. wbnews/filters/__init__.py +1 -1
  4. wbnews/filters/news.py +39 -2
  5. wbnews/import_export/backends/news.py +3 -3
  6. wbnews/import_export/handlers/news.py +35 -3
  7. wbnews/import_export/parsers/emails/news.py +2 -11
  8. wbnews/import_export/parsers/emails/utils.py +16 -12
  9. wbnews/import_export/parsers/rss/news.py +3 -9
  10. wbnews/locale/de/LC_MESSAGES/django.mo +0 -0
  11. wbnews/locale/de/LC_MESSAGES/django.po +92 -39
  12. wbnews/locale/de/LC_MESSAGES/django.po.translated +173 -0
  13. wbnews/locale/en/LC_MESSAGES/django.mo +0 -0
  14. wbnews/locale/en/LC_MESSAGES/django.po +159 -0
  15. wbnews/locale/fr/LC_MESSAGES/django.mo +0 -0
  16. wbnews/locale/fr/LC_MESSAGES/django.po +161 -0
  17. wbnews/migrations/0012_alter_news_unique_together_news_identifier_and_more.py +91 -0
  18. wbnews/migrations/0013_alter_news_datetime.py +19 -0
  19. wbnews/migrations/0014_newsrelationship_unique_news_relationship.py +27 -0
  20. wbnews/models/llm/cleaned_news.py +26 -23
  21. wbnews/models/news.py +37 -22
  22. wbnews/models/relationships.py +20 -1
  23. wbnews/models/sources.py +35 -5
  24. wbnews/models/utils.py +15 -0
  25. wbnews/serializers.py +16 -7
  26. wbnews/tasks.py +17 -0
  27. wbnews/tests/parsers/__init__.py +0 -0
  28. wbnews/tests/parsers/test_emails.py +25 -0
  29. wbnews/tests/test_models.py +65 -0
  30. wbnews/tests/test_utils.py +7 -0
  31. wbnews/utils.py +57 -0
  32. wbnews/viewsets/display.py +25 -29
  33. wbnews/viewsets/endpoints.py +11 -6
  34. wbnews/viewsets/views.py +5 -4
  35. {wbnews-1.46.12.dist-info → wbnews-1.60.1.dist-info}/METADATA +1 -2
  36. wbnews-1.60.1.dist-info/RECORD +65 -0
  37. {wbnews-1.46.12.dist-info → wbnews-1.60.1.dist-info}/WHEEL +1 -1
  38. 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", "url")
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 = faker.url()
15
+ image = factory.Faker("url")
16
16
  description = factory.Faker("sentence", nb_words=32)
17
- author = faker.name()
18
- url = factory.Faker("url")
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.Sequence(lambda n: f"news_{n}")
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 = faker.url()
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",)
@@ -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"], "datetime": ["lte", "gte"], "source": ["exact"], "language": ["exact"]}
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.url
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, url__isnull=False)
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.url)
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
- data["source"] = self.model.source_dict_to_model(data["source"])
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
- return self.model.objects.filter(source=data["source"], datetime=data["datetime"], title=data["title"]).first()
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 langdetect import detect, lang_detect_exception
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 = import_from_dotted_path(html_parser_path)
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(cls, parsed: message.Message) -> bytes | None:
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 := cls.get_html(item):
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
- name, email = parseaddr(self.message["From"])
51
- if not name:
52
- name = "Generic"
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
- email = "generic"
55
- source = {
56
- "title": f"{name} Research Email",
57
- "identifier": "research-email-" + email.lower(),
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["url"] = d["feed"]["link"]
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: 2022-10-17 14:50+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"
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
- #: wbnews/models.py:45 wbnews/viewsets/display.py:59
22
- #: wbnews/viewsets/display.py:86
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
- #: wbnews/models.py:46 wbnews/serializers.py:17 wbnews/viewsets/display.py:12
27
- #: wbnews/viewsets/display.py:60 wbnews/viewsets/display.py:87
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
- #: wbnews/models.py:47 wbnews/serializers.py:20 wbnews/viewsets/display.py:15
32
- #: wbnews/viewsets/display.py:62 wbnews/viewsets/display.py:88
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
- #: wbnews/models.py:48 wbnews/viewsets/display.py:61
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
- #: wbnews/models.py:49 wbnews/viewsets/display.py:65
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
- #: wbnews/models.py:50
49
+ #: models/news.py:58
46
50
  msgid "Link"
47
51
  msgstr "Link"
48
52
 
49
- #: wbnews/models.py:54 wbnews/viewsets/display.py:64
53
+ #: models/news.py:62 viewsets/display.py:59 viewsets/display.py:106
50
54
  msgid "Source"
51
55
  msgstr "Quelle"
52
56
 
53
- #: wbnews/serializers.py:18
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
- #: wbnews/serializers.py:21 wbnews/viewsets/display.py:14
81
+ #: serializers.py:25 viewsets/display.py:22
58
82
  msgid "Author"
59
83
  msgstr "Autor*in"
60
84
 
61
- #: wbnews/serializers.py:22
85
+ #: serializers.py:26
62
86
  msgid "Updated"
63
87
  msgstr "Geändert"
64
88
 
65
- #: wbnews/viewsets/buttons.py:8
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
- #: wbnews/viewsets/display.py:13
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
- #: wbnews/viewsets/display.py:16
109
+ #: viewsets/display.py:24
74
110
  msgid "Last Update"
75
111
  msgstr "Letztes Aktualisierung"
76
112
 
77
- #: wbnews/viewsets/display.py:33 wbnews/viewsets/menu.py:6
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
- #: wbnews/viewsets/menu.py:11 wbnews/viewsets/titles.py:8
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
- #: wbnews/viewsets/menu.py:15
137
+ #: viewsets/menu.py:23
91
138
  msgid "Create Source"
92
139
  msgstr "Quelle Erstellen"
93
140
 
94
- #: wbnews/viewsets/titles.py:12
141
+ #: viewsets/titles.py:13
95
142
  #, python-brace-format
96
143
  msgid "Source: {source}"
97
144
  msgstr "Quelle: {source}"
98
145
 
99
- #: wbnews/viewsets/titles.py:13
100
- #, fuzzy
101
- #| msgid "Source"
146
+ #: viewsets/titles.py:14
102
147
  msgid "News Source"
103
- msgstr "Quelle"
148
+ msgstr ""
104
149
 
105
- #: wbnews/viewsets/titles.py:18
150
+ #: viewsets/titles.py:19
106
151
  msgid "News Flow"
107
152
  msgstr "Nachrichtenfluss"
108
153
 
109
- #: wbnews/viewsets/titles.py:29
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