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.
- wbwriter/__init__.py +1 -0
- wbwriter/admin.py +142 -0
- wbwriter/apps.py +5 -0
- wbwriter/dynamic_preferences_registry.py +15 -0
- wbwriter/factories/__init__.py +13 -0
- wbwriter/factories/article.py +181 -0
- wbwriter/factories/meta_information.py +29 -0
- wbwriter/filters/__init__.py +2 -0
- wbwriter/filters/article.py +47 -0
- wbwriter/filters/metainformationinstance.py +24 -0
- wbwriter/migrations/0001_initial_squashed_squashed_0008_alter_article_author_alter_article_feedback_contact_and_more.py +653 -0
- wbwriter/migrations/0009_dependantarticle.py +41 -0
- wbwriter/migrations/0010_alter_article_options.py +20 -0
- wbwriter/migrations/0011_auto_20240103_0953.py +39 -0
- wbwriter/migrations/__init__.py +0 -0
- wbwriter/models/__init__.py +9 -0
- wbwriter/models/article.py +1179 -0
- wbwriter/models/article_type.py +59 -0
- wbwriter/models/block.py +24 -0
- wbwriter/models/block_parameter.py +19 -0
- wbwriter/models/in_editor_template.py +102 -0
- wbwriter/models/meta_information.py +87 -0
- wbwriter/models/mixins.py +9 -0
- wbwriter/models/publication_models.py +170 -0
- wbwriter/models/style.py +13 -0
- wbwriter/models/template.py +34 -0
- wbwriter/pdf_generator.py +172 -0
- wbwriter/publication_parser.py +258 -0
- wbwriter/serializers/__init__.py +28 -0
- wbwriter/serializers/article.py +359 -0
- wbwriter/serializers/article_type.py +14 -0
- wbwriter/serializers/in_editor_template.py +37 -0
- wbwriter/serializers/meta_information.py +67 -0
- wbwriter/serializers/publication.py +82 -0
- wbwriter/templatetags/__init__.py +0 -0
- wbwriter/templatetags/writer.py +72 -0
- wbwriter/tests/__init__.py +0 -0
- wbwriter/tests/conftest.py +32 -0
- wbwriter/tests/signals.py +23 -0
- wbwriter/tests/test_filter.py +58 -0
- wbwriter/tests/test_model.py +591 -0
- wbwriter/tests/test_writer.py +38 -0
- wbwriter/tests/tests.py +18 -0
- wbwriter/typings.py +23 -0
- wbwriter/urls.py +83 -0
- wbwriter/viewsets/__init__.py +22 -0
- wbwriter/viewsets/article.py +270 -0
- wbwriter/viewsets/article_type.py +49 -0
- wbwriter/viewsets/buttons.py +61 -0
- wbwriter/viewsets/display/__init__.py +6 -0
- wbwriter/viewsets/display/article.py +404 -0
- wbwriter/viewsets/display/article_type.py +27 -0
- wbwriter/viewsets/display/in_editor_template.py +39 -0
- wbwriter/viewsets/display/meta_information.py +37 -0
- wbwriter/viewsets/display/meta_information_instance.py +28 -0
- wbwriter/viewsets/display/publication.py +55 -0
- wbwriter/viewsets/endpoints/__init__.py +2 -0
- wbwriter/viewsets/endpoints/article.py +12 -0
- wbwriter/viewsets/endpoints/meta_information.py +14 -0
- wbwriter/viewsets/in_editor_template.py +68 -0
- wbwriter/viewsets/menu.py +42 -0
- wbwriter/viewsets/meta_information.py +51 -0
- wbwriter/viewsets/meta_information_instance.py +48 -0
- wbwriter/viewsets/publication.py +117 -0
- wbwriter/viewsets/titles/__init__.py +2 -0
- wbwriter/viewsets/titles/publication_title_config.py +18 -0
- wbwriter/viewsets/titles/reviewer_article_title_config.py +6 -0
- wbwriter-2.2.1.dist-info/METADATA +8 -0
- wbwriter-2.2.1.dist-info/RECORD +70 -0
- 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()
|