wbnews 1.46.12__py2.py3-none-any.whl → 1.58.0__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 +0 -9
  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 +93 -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 +162 -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 +35 -21
  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 +15 -6
  26. wbnews/tasks.py +16 -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 +4 -3
  35. {wbnews-1.46.12.dist-info → wbnews-1.58.0.dist-info}/METADATA +1 -2
  36. wbnews-1.58.0.dist-info/RECORD +65 -0
  37. wbnews-1.46.12.dist-info/RECORD +0 -50
  38. {wbnews-1.46.12.dist-info → wbnews-1.58.0.dist-info}/WHEEL +0 -0
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,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]}
@@ -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,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: 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"
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
- #: wbnews/models.py:45 wbnews/viewsets/display.py:59
22
- #: wbnews/viewsets/display.py:86
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
- #: wbnews/models.py:46 wbnews/serializers.py:17 wbnews/viewsets/display.py:12
27
- #: wbnews/viewsets/display.py:60 wbnews/viewsets/display.py:87
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
- #: wbnews/models.py:47 wbnews/serializers.py:20 wbnews/viewsets/display.py:15
32
- #: wbnews/viewsets/display.py:62 wbnews/viewsets/display.py:88
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
- #: wbnews/models.py:48 wbnews/viewsets/display.py:61
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
- #: wbnews/models.py:49 wbnews/viewsets/display.py:65
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
- #: wbnews/models.py:50
50
+ #: models/news.py:57
46
51
  msgid "Link"
47
52
  msgstr "Link"
48
53
 
49
- #: wbnews/models.py:54 wbnews/viewsets/display.py:64
54
+ #: models/news.py:61 viewsets/display.py:59 viewsets/display.py:106
50
55
  msgid "Source"
51
56
  msgstr "Quelle"
52
57
 
53
- #: wbnews/serializers.py:18
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
- #: wbnews/serializers.py:21 wbnews/viewsets/display.py:14
82
+ #: serializers.py:25 viewsets/display.py:22
58
83
  msgid "Author"
59
84
  msgstr "Autor*in"
60
85
 
61
- #: wbnews/serializers.py:22
86
+ #: serializers.py:26
62
87
  msgid "Updated"
63
88
  msgstr "Geändert"
64
89
 
65
- #: wbnews/viewsets/buttons.py:8
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
- #: wbnews/viewsets/display.py:13
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
- #: wbnews/viewsets/display.py:16
110
+ #: viewsets/display.py:24
74
111
  msgid "Last Update"
75
112
  msgstr "Letztes Aktualisierung"
76
113
 
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
114
+ #: viewsets/display.py:61
83
115
  msgid "Image"
84
116
  msgstr "Bild"
85
117
 
86
- #: wbnews/viewsets/menu.py:11 wbnews/viewsets/titles.py:8
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
- #: wbnews/viewsets/menu.py:15
138
+ #: viewsets/menu.py:23
91
139
  msgid "Create Source"
92
140
  msgstr "Quelle Erstellen"
93
141
 
94
- #: wbnews/viewsets/titles.py:12
142
+ #: viewsets/titles.py:13
95
143
  #, python-brace-format
96
144
  msgid "Source: {source}"
97
145
  msgstr "Quelle: {source}"
98
146
 
99
- #: wbnews/viewsets/titles.py:13
100
- #, fuzzy
101
- #| msgid "Source"
147
+ #: viewsets/titles.py:14
102
148
  msgid "News Source"
103
- msgstr "Quelle"
149
+ msgstr ""
104
150
 
105
- #: wbnews/viewsets/titles.py:18
151
+ #: viewsets/titles.py:19
106
152
  msgid "News Flow"
107
153
  msgstr "Nachrichtenfluss"
108
154
 
109
- #: wbnews/viewsets/titles.py:29
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