wbwriter 2.2.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.

Potentially problematic release.


This version of wbwriter might be problematic. Click here for more details.

Files changed (70) hide show
  1. wbwriter/__init__.py +1 -0
  2. wbwriter/admin.py +142 -0
  3. wbwriter/apps.py +5 -0
  4. wbwriter/dynamic_preferences_registry.py +15 -0
  5. wbwriter/factories/__init__.py +13 -0
  6. wbwriter/factories/article.py +181 -0
  7. wbwriter/factories/meta_information.py +29 -0
  8. wbwriter/filters/__init__.py +2 -0
  9. wbwriter/filters/article.py +47 -0
  10. wbwriter/filters/metainformationinstance.py +24 -0
  11. wbwriter/migrations/0001_initial_squashed_squashed_0008_alter_article_author_alter_article_feedback_contact_and_more.py +653 -0
  12. wbwriter/migrations/0009_dependantarticle.py +41 -0
  13. wbwriter/migrations/0010_alter_article_options.py +20 -0
  14. wbwriter/migrations/0011_auto_20240103_0953.py +39 -0
  15. wbwriter/migrations/__init__.py +0 -0
  16. wbwriter/models/__init__.py +9 -0
  17. wbwriter/models/article.py +1179 -0
  18. wbwriter/models/article_type.py +59 -0
  19. wbwriter/models/block.py +24 -0
  20. wbwriter/models/block_parameter.py +19 -0
  21. wbwriter/models/in_editor_template.py +102 -0
  22. wbwriter/models/meta_information.py +87 -0
  23. wbwriter/models/mixins.py +9 -0
  24. wbwriter/models/publication_models.py +170 -0
  25. wbwriter/models/style.py +13 -0
  26. wbwriter/models/template.py +34 -0
  27. wbwriter/pdf_generator.py +172 -0
  28. wbwriter/publication_parser.py +258 -0
  29. wbwriter/serializers/__init__.py +28 -0
  30. wbwriter/serializers/article.py +359 -0
  31. wbwriter/serializers/article_type.py +14 -0
  32. wbwriter/serializers/in_editor_template.py +37 -0
  33. wbwriter/serializers/meta_information.py +67 -0
  34. wbwriter/serializers/publication.py +82 -0
  35. wbwriter/templatetags/__init__.py +0 -0
  36. wbwriter/templatetags/writer.py +72 -0
  37. wbwriter/tests/__init__.py +0 -0
  38. wbwriter/tests/conftest.py +32 -0
  39. wbwriter/tests/signals.py +23 -0
  40. wbwriter/tests/test_filter.py +58 -0
  41. wbwriter/tests/test_model.py +591 -0
  42. wbwriter/tests/test_writer.py +38 -0
  43. wbwriter/tests/tests.py +18 -0
  44. wbwriter/typings.py +23 -0
  45. wbwriter/urls.py +83 -0
  46. wbwriter/viewsets/__init__.py +22 -0
  47. wbwriter/viewsets/article.py +270 -0
  48. wbwriter/viewsets/article_type.py +49 -0
  49. wbwriter/viewsets/buttons.py +61 -0
  50. wbwriter/viewsets/display/__init__.py +6 -0
  51. wbwriter/viewsets/display/article.py +404 -0
  52. wbwriter/viewsets/display/article_type.py +27 -0
  53. wbwriter/viewsets/display/in_editor_template.py +39 -0
  54. wbwriter/viewsets/display/meta_information.py +37 -0
  55. wbwriter/viewsets/display/meta_information_instance.py +28 -0
  56. wbwriter/viewsets/display/publication.py +55 -0
  57. wbwriter/viewsets/endpoints/__init__.py +2 -0
  58. wbwriter/viewsets/endpoints/article.py +12 -0
  59. wbwriter/viewsets/endpoints/meta_information.py +14 -0
  60. wbwriter/viewsets/in_editor_template.py +68 -0
  61. wbwriter/viewsets/menu.py +42 -0
  62. wbwriter/viewsets/meta_information.py +51 -0
  63. wbwriter/viewsets/meta_information_instance.py +48 -0
  64. wbwriter/viewsets/publication.py +117 -0
  65. wbwriter/viewsets/titles/__init__.py +2 -0
  66. wbwriter/viewsets/titles/publication_title_config.py +18 -0
  67. wbwriter/viewsets/titles/reviewer_article_title_config.py +6 -0
  68. wbwriter-2.2.1.dist-info/METADATA +8 -0
  69. wbwriter-2.2.1.dist-info/RECORD +70 -0
  70. wbwriter-2.2.1.dist-info/WHEEL +5 -0
@@ -0,0 +1,67 @@
1
+ from wbcore.serializers import ModelSerializer, RepresentationSerializer
2
+ from wbwriter.models import MetaInformation, MetaInformationInstance
3
+
4
+ from .article_type import ArticleTypeRepresentationSerializer
5
+
6
+
7
+ class MetaInformationModelSerializer(ModelSerializer):
8
+ """Serializes all fields of the MetaInformation model."""
9
+
10
+ _article_type = ArticleTypeRepresentationSerializer(source="article_type", many=True)
11
+
12
+ class Meta:
13
+ model = MetaInformation
14
+ fields = (
15
+ "id",
16
+ "article_type",
17
+ "_article_type",
18
+ "name",
19
+ "key",
20
+ "meta_information_type",
21
+ "boolean_default",
22
+ )
23
+
24
+
25
+ class MetaInformationRepresentationSerializer(RepresentationSerializer):
26
+ """Serializes the name and the key of the MetaInformation model."""
27
+
28
+ class Meta:
29
+ model = MetaInformation
30
+ fields = ("id", "name", "key")
31
+
32
+
33
+ class MetaInformationInstanceModelSerializer(ModelSerializer):
34
+ # from wbwriter.serializers import ArticleFullModelSerializer
35
+
36
+ # _article = ArticleFullModelSerializer(source="article")
37
+ _meta_information = MetaInformationRepresentationSerializer(source="meta_information")
38
+
39
+ class Meta:
40
+ model = MetaInformationInstance
41
+ read_only_fields = ("meta_information",)
42
+ fields = (
43
+ "id",
44
+ "meta_information",
45
+ "_meta_information",
46
+ # "article",
47
+ # "_article",
48
+ "boolean_value",
49
+ )
50
+
51
+
52
+ class MetaInformationInstanceRepresentationSerializer(RepresentationSerializer):
53
+ # from wbwriter.serializers import ArticleFullModelSerializer
54
+
55
+ # _article = ArticleFullModelSerializer(source="article")
56
+ _meta_information = MetaInformationRepresentationSerializer(source="meta_information")
57
+
58
+ class Meta:
59
+ model = MetaInformationInstance
60
+ fields = (
61
+ "id",
62
+ "meta_information",
63
+ "_meta_information",
64
+ # "article",
65
+ # "_article",
66
+ "boolean_value",
67
+ )
@@ -0,0 +1,82 @@
1
+ from contextlib import suppress
2
+
3
+ from wbcore.contrib.directory.serializers import PersonRepresentationSerializer
4
+ from wbcore.contrib.tags.serializers import TagSerializerMixin
5
+ from wbcore.serializers import (
6
+ CharField,
7
+ HyperlinkField,
8
+ ModelSerializer,
9
+ RepresentationSerializer,
10
+ TextAreaField,
11
+ register_resource,
12
+ )
13
+ from wbwriter.models import Publication, PublicationParser
14
+
15
+
16
+ class PublicationParserRepresentationSerializer(RepresentationSerializer):
17
+ """Serializes the ID and the title of the Publication Parser model."""
18
+
19
+ _detail = HyperlinkField(reverse_name="wbwriter:publicationparser-detail")
20
+
21
+ class Meta:
22
+ model = PublicationParser
23
+ fields = ("id", "title", "_detail")
24
+
25
+
26
+ class PublicationModelSerializer(TagSerializerMixin, ModelSerializer):
27
+ """Serializes the all fields of the Publication model except for the slug field."""
28
+
29
+ description = TextAreaField()
30
+ title = CharField(read_only=True)
31
+ _author = PersonRepresentationSerializer(source="author")
32
+ author = CharField(read_only=True)
33
+ _parser = PublicationParserRepresentationSerializer(source="parser")
34
+
35
+ @register_resource()
36
+ def pdf_file(self, instance, request, user):
37
+ with suppress(ValueError):
38
+ if instance.content_file:
39
+ return {"pdf_file": instance.content_file.url}
40
+ return {}
41
+
42
+ class Meta:
43
+ model = Publication
44
+ fields = (
45
+ "id",
46
+ "title",
47
+ "target",
48
+ "author",
49
+ "_author",
50
+ "created",
51
+ "modified",
52
+ "content",
53
+ "description",
54
+ "teaser_image",
55
+ "thumbnail_image",
56
+ "tags",
57
+ "_tags",
58
+ "additional_information",
59
+ "_additional_resources",
60
+ "parser",
61
+ "_parser",
62
+ "content_type",
63
+ "object_id",
64
+ )
65
+ ordering = ("-modified", "title", "target")
66
+
67
+
68
+ class PublicationRepresentationSerializer(RepresentationSerializer):
69
+ """Serializes the ID and the title of the Publication model."""
70
+
71
+ _detail = HyperlinkField(reverse_name="wbwriter:publication-detail")
72
+
73
+ class Meta:
74
+ model = Publication
75
+ fields = ("id", "title", "target", "modified", "_detail")
76
+ ordering = ("-modified", "title", "target")
77
+
78
+
79
+ class PublicationParserSerializer(ModelSerializer):
80
+ class Meta:
81
+ model = PublicationParser
82
+ fields = ("id", "title", "slug", "parser_path")
File without changes
@@ -0,0 +1,72 @@
1
+ from django import template
2
+ from django.core.exceptions import ValidationError
3
+ from django.shortcuts import get_object_or_404
4
+ from django.template import Context, Template
5
+ from slugify import slugify
6
+ from wbcore.markdown.template import load_registered_templatetags
7
+ from wbwriter.models import Article
8
+
9
+ register = template.Library()
10
+
11
+
12
+ @register.inclusion_tag("wbwriter/templatetags/table_of_contents.html", takes_context=True)
13
+ def table_of_contents(context, title="Table of contents", custom_enumeration=True):
14
+ if "article" in context and context.get("article"):
15
+ article = context.get("article")
16
+ article_toc = article.get_article_structure()
17
+ return {"title": title, "custom_enumeration": custom_enumeration, **article_toc}
18
+
19
+ return {"not_working": "<i>Table of content is only rendered in the exported PDF Version.</i>"}
20
+
21
+
22
+ @register.simple_tag(takes_context=True)
23
+ def load_article(context, article_id, with_title=True, include_in_toc=True, enumeration_from_toc=True):
24
+ prefix = ""
25
+ level = 1
26
+
27
+ article = get_object_or_404(Article, id=article_id)
28
+ # Render the content
29
+ template = ""
30
+ for section in article.content["sectionOrder"]:
31
+ template = article.content["sections"][section]["content"]["content"]
32
+
33
+ loaded_template_tags = load_registered_templatetags()
34
+ if with_title:
35
+ if main_article := context.get("article"):
36
+ if structure := main_article.article_structure.get(str(article_id)):
37
+ prefix = structure.get("enumerator", "")
38
+ level = structure.get("level", 1)
39
+
40
+ anchor = "<a name='" + slugify(article.name) + "'></a>"
41
+ template = f"<h1 class='header-{level}'>{prefix} {article.title}{anchor}</h1>\n" + template
42
+ return Template(loaded_template_tags + template).render(Context(context))
43
+
44
+
45
+ @register.inclusion_tag("wbwriter/templatetags/cite_article.html", takes_context=True)
46
+ def cite_article(context, article_id, text="(see {e} {t})"):
47
+ if "article" in context and context.get("article"):
48
+ main_article = context.get("article")
49
+ prefix = ""
50
+ if structure := main_article.article_structure.get(str(article_id)):
51
+ prefix = structure.get("enumerator", "")
52
+ structure.get("level", 1)
53
+ article = get_object_or_404(Article, id=article_id)
54
+ return {"anchor": slugify(article.name), "text": text.format(e=prefix, t=article.title)}
55
+
56
+ return {"not_working": "(article citations only work in a rendered pdf.)"}
57
+
58
+
59
+ def block_templatetag(block):
60
+ def templatetag(context, *args):
61
+ parameters = block.parameters.all().count()
62
+ if parameters != len(args):
63
+ raise ValidationError({"non_field_errors": "Not all fields were send to the server."})
64
+ return block.render(args)
65
+
66
+ return templatetag
67
+
68
+
69
+ # TODO do we need the following three lines of code? (13.07.2022 / CL)
70
+ # with suppress(ProgrammingError):
71
+ # for block in Block.objects.all():
72
+ # register.simple_tag(takes_context=True, name=block.key)(block_templatetag(block))
File without changes
@@ -0,0 +1,32 @@
1
+ from django.apps import apps
2
+ from django.db import connection
3
+ from django.db.models.signals import pre_migrate
4
+ from pytest_factoryboy import register
5
+ from wbcore.contrib.authentication.factories import UserFactory
6
+ from wbcore.contrib.directory.factories import PersonFactory
7
+ from wbcore.contrib.geography.tests.signals import app_pre_migration
8
+ from wbwriter.factories import (
9
+ ArticleFactory,
10
+ ArticleTypeFactory,
11
+ BlockFactory,
12
+ InEditorTemplateFactory,
13
+ MetaInformationFactory,
14
+ MetaInformationInstanceFactory,
15
+ PublicationParserFactory,
16
+ TemplateFactory,
17
+ )
18
+
19
+ register(UserFactory)
20
+ register(ArticleFactory)
21
+ register(ArticleTypeFactory)
22
+ register(BlockFactory)
23
+ register(InEditorTemplateFactory)
24
+ register(PersonFactory)
25
+ register(PublicationParserFactory)
26
+ register(TemplateFactory)
27
+ register(MetaInformationFactory)
28
+ register(MetaInformationInstanceFactory)
29
+
30
+ from .signals import *
31
+
32
+ pre_migrate.connect(app_pre_migration, sender=apps.get_app_config("wbwriter"))
@@ -0,0 +1,23 @@
1
+ from django.dispatch import receiver
2
+ from dynamic_preferences.registries import global_preferences_registry
3
+ from wbcore.test.signals import custom_update_kwargs
4
+ from wbwriter.models import Article
5
+ from wbwriter.viewsets import ArticleModelViewSet, ReviewerArticleModelViewSet
6
+
7
+
8
+ @receiver(custom_update_kwargs, sender=ReviewerArticleModelViewSet)
9
+ def receive_kwargs_reviewer_article(sender, *args, **kwargs):
10
+ if (obj := kwargs.get("obj_factory")) and (user := kwargs.get("user")):
11
+ obj.reviewer = user.profile
12
+ obj.status = Article.Status.FEEDBACK
13
+ obj.author.employers.set({global_preferences_registry.manager()["directory__main_company"]})
14
+ obj.save()
15
+ return {}
16
+
17
+
18
+ @receiver(custom_update_kwargs, sender=ArticleModelViewSet)
19
+ def receive_kwargs_article(sender, *args, **kwargs):
20
+ if obj := kwargs.get("obj_factory"):
21
+ obj.author.employers.set({global_preferences_registry.manager()["directory__main_company"]})
22
+ obj.save()
23
+ return {}
@@ -0,0 +1,58 @@
1
+ import pytest
2
+ from rest_framework.test import APIRequestFactory
3
+ from wbcore.contrib.authentication.factories import InternalUserFactory
4
+ from wbwriter.factories import ArticleFactory, MetaInformationInstanceFactory
5
+ from wbwriter.filters import MetaInformationInstanceFilter
6
+ from wbwriter.models import MetaInformationInstance
7
+ from wbwriter.viewsets import ArticleModelViewSet
8
+
9
+
10
+ @pytest.mark.django_db
11
+ class TestArticleFilters:
12
+ def test_get_my_articles_true(self):
13
+ employee_a = InternalUserFactory().profile
14
+ employee_b = InternalUserFactory().profile
15
+ user_a = employee_a.user_account
16
+ ArticleFactory(author=user_a.profile)
17
+ ArticleFactory(author=employee_b)
18
+ ArticleFactory(author=employee_b, reviewer=user_a.profile)
19
+
20
+ request = APIRequestFactory().get("")
21
+ request.user = user_a
22
+ mvs = ArticleModelViewSet(kwargs={})
23
+ qs = mvs.get_serializer_class().Meta.model.objects.all()
24
+ filtered_qs = mvs.filterset_class(request=request).get_my_articles(qs, "", True)
25
+ assert filtered_qs.count() == 2
26
+ assert filtered_qs.count() < qs.count()
27
+
28
+ def test_get_my_articles_false(self):
29
+ employee_a = InternalUserFactory.create().profile
30
+ employee_b = InternalUserFactory.create().profile
31
+ user_a = employee_a.user_account
32
+ ArticleFactory(author=user_a.profile)
33
+ ArticleFactory(author=employee_b)
34
+ ArticleFactory(author=employee_b, reviewer=user_a.profile)
35
+
36
+ request = APIRequestFactory().get("")
37
+ request.user = user_a
38
+ mvs = ArticleModelViewSet(kwargs={})
39
+ qs = mvs.get_serializer_class().Meta.model.objects.all()
40
+ filtered_qs = mvs.filterset_class(request=request).get_my_articles(qs, "", False)
41
+ assert filtered_qs.count() == qs.count()
42
+
43
+ def test_filter_for_article_with_id(self):
44
+ article = ArticleFactory()
45
+ MetaInformationInstanceFactory(article=article)
46
+ MetaInformationInstanceFactory.create_batch(4)
47
+ qs = MetaInformationInstance.objects.all()
48
+ filtered_qs = MetaInformationInstanceFilter.filter_for_article(self, queryset=qs, name="", value=article.id)
49
+ assert filtered_qs.count() == 1
50
+ assert filtered_qs.count() < qs.count()
51
+
52
+ def test_filter_for_article_no_id(self):
53
+ article = ArticleFactory()
54
+ MetaInformationInstanceFactory(article=article)
55
+ MetaInformationInstanceFactory.create_batch(4)
56
+ qs = MetaInformationInstance.objects.all()
57
+ filtered_qs = MetaInformationInstanceFilter.filter_for_article(self, queryset=qs, name="", value=None)
58
+ assert filtered_qs.count() == qs.count()