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
wbwriter/urls.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from django.urls import include, path
|
|
2
|
+
from wbcore.routers import WBCoreRouter
|
|
3
|
+
from wbwriter import viewsets
|
|
4
|
+
|
|
5
|
+
router = WBCoreRouter()
|
|
6
|
+
router.register(r"publication", viewsets.PublicationModelViewSet, basename="publication")
|
|
7
|
+
router.register(
|
|
8
|
+
r"publicationrepresentation", viewsets.PublicationRepresentationViewSet, basename="publicationrepresentation"
|
|
9
|
+
)
|
|
10
|
+
router.register(
|
|
11
|
+
r"publicationparserrepresentation",
|
|
12
|
+
viewsets.PublicationParserRepresentationViewSet,
|
|
13
|
+
basename="publicationparserrepresentation",
|
|
14
|
+
)
|
|
15
|
+
router.register(
|
|
16
|
+
r"publicationparser",
|
|
17
|
+
viewsets.PublicationParserModelViewSet,
|
|
18
|
+
basename="publicationparser",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
router.register(r"article", viewsets.ArticleModelViewSet, basename="article")
|
|
22
|
+
router.register(r"articlerepresentation", viewsets.ArticleRepresentionViewSet, basename="articlerepresentation")
|
|
23
|
+
# router.register(r"ddq", viewsets.DDQArticleModelViewSet, basename="ddq")
|
|
24
|
+
router.register(r"review-article", viewsets.ReviewerArticleModelViewSet, basename="review-article")
|
|
25
|
+
router.register(
|
|
26
|
+
r"articletyperepresentation",
|
|
27
|
+
viewsets.ArticleTypeRepresentationViewSet,
|
|
28
|
+
basename="articletyperepresentation",
|
|
29
|
+
)
|
|
30
|
+
router.register(
|
|
31
|
+
r"articletype",
|
|
32
|
+
viewsets.ArticleTypeModelViewSet,
|
|
33
|
+
basename="articletype",
|
|
34
|
+
)
|
|
35
|
+
router.register(
|
|
36
|
+
r"in-editor-template",
|
|
37
|
+
viewsets.InEditorTemplateModelViewSet,
|
|
38
|
+
basename="in-editor-template",
|
|
39
|
+
)
|
|
40
|
+
router.register(
|
|
41
|
+
r"in-editor-template-representation",
|
|
42
|
+
viewsets.InEditorTemplateRepresentationViewSet,
|
|
43
|
+
basename="in-editor-template-representation",
|
|
44
|
+
)
|
|
45
|
+
router.register(
|
|
46
|
+
r"metainformation",
|
|
47
|
+
viewsets.MetaInformationRepresentationViewSet,
|
|
48
|
+
basename="metainformation",
|
|
49
|
+
)
|
|
50
|
+
router.register(
|
|
51
|
+
r"metainformationinstance",
|
|
52
|
+
viewsets.MetaInformationInstanceModelViewSet,
|
|
53
|
+
basename="metainformationinstance",
|
|
54
|
+
)
|
|
55
|
+
router.register(
|
|
56
|
+
r"dependantarticle",
|
|
57
|
+
viewsets.DependantArticleModelViewSet,
|
|
58
|
+
basename="dependantarticle",
|
|
59
|
+
)
|
|
60
|
+
article_router = WBCoreRouter()
|
|
61
|
+
article_router.register(
|
|
62
|
+
r"metainformationinstancearticle",
|
|
63
|
+
viewsets.MetaInformationInstanceModelViewSet,
|
|
64
|
+
basename="metainformationinstancearticle",
|
|
65
|
+
)
|
|
66
|
+
article_router.register(
|
|
67
|
+
r"dependantarticle",
|
|
68
|
+
viewsets.DependantArticleModelViewSet,
|
|
69
|
+
basename="dependantarticle-article",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
used_article_router = WBCoreRouter()
|
|
73
|
+
used_article_router.register(
|
|
74
|
+
r"usedarticle",
|
|
75
|
+
viewsets.DependantArticleModelViewSet,
|
|
76
|
+
basename="usedarticle-article",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
urlpatterns = [
|
|
80
|
+
path("", include(router.urls)),
|
|
81
|
+
path("article/<int:article_id>/", include(article_router.urls)),
|
|
82
|
+
path("article/<int:used_article_id>/", include(used_article_router.urls)),
|
|
83
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from .article import (
|
|
2
|
+
ArticleModelViewSet,
|
|
3
|
+
ReviewerArticleModelViewSet,
|
|
4
|
+
DependantArticleModelViewSet,
|
|
5
|
+
ArticleRepresentionViewSet,
|
|
6
|
+
)
|
|
7
|
+
from .article_type import ArticleTypeModelViewSet, ArticleTypeRepresentationViewSet
|
|
8
|
+
from .in_editor_template import (
|
|
9
|
+
InEditorTemplateModelViewSet,
|
|
10
|
+
InEditorTemplateRepresentationViewSet,
|
|
11
|
+
)
|
|
12
|
+
from .meta_information import MetaInformationRepresentationViewSet
|
|
13
|
+
from .meta_information_instance import (
|
|
14
|
+
MetaInformationInstanceModelViewSet,
|
|
15
|
+
MetaInformationInstanceRepresentationViewSet,
|
|
16
|
+
)
|
|
17
|
+
from .publication import (
|
|
18
|
+
PublicationModelViewSet,
|
|
19
|
+
PublicationParserModelViewSet,
|
|
20
|
+
PublicationParserRepresentationViewSet,
|
|
21
|
+
PublicationRepresentationViewSet,
|
|
22
|
+
)
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
from django.contrib.contenttypes.models import ContentType
|
|
2
|
+
from django.db.models import Q, QuerySet
|
|
3
|
+
from django.shortcuts import get_object_or_404
|
|
4
|
+
from django.utils.functional import cached_property
|
|
5
|
+
from rest_framework import status
|
|
6
|
+
from rest_framework.decorators import action
|
|
7
|
+
from rest_framework.response import Response
|
|
8
|
+
from reversion.views import RevisionMixin
|
|
9
|
+
from wbcore.contrib.authentication.authentication import JWTCookieAuthentication
|
|
10
|
+
from wbcore.contrib.directory.models import Person
|
|
11
|
+
from wbcore.utils.views import CloneMixin
|
|
12
|
+
from wbcore.viewsets import ModelViewSet, RepresentationViewSet
|
|
13
|
+
from wbwriter.filters.article import ArticleFilter
|
|
14
|
+
from wbwriter.models import Article, can_access_article
|
|
15
|
+
from wbwriter.models.article import (
|
|
16
|
+
DependantArticle,
|
|
17
|
+
can_administrate_article,
|
|
18
|
+
can_edit_article_author,
|
|
19
|
+
can_edit_article_content,
|
|
20
|
+
can_edit_article_meta_data,
|
|
21
|
+
can_edit_article_type,
|
|
22
|
+
generate_pdf_as_task,
|
|
23
|
+
)
|
|
24
|
+
from wbwriter.serializers import (
|
|
25
|
+
ArticleFullModelSerializer,
|
|
26
|
+
ArticleRepresentionSerializer,
|
|
27
|
+
DependantArticleModelSerializer,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
from .buttons import ArticleModelButtonConfig
|
|
31
|
+
from .display import ArticleDisplayConfig, DependantArticleDisplayConfig
|
|
32
|
+
from .endpoints import (
|
|
33
|
+
DependantArticleEndpointViewConfig,
|
|
34
|
+
ReviewerArticleModelEndpointConfig,
|
|
35
|
+
)
|
|
36
|
+
from .titles import ReviewerArticleTitleConfig
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ArticleRepresentionViewSet(RepresentationViewSet):
|
|
40
|
+
queryset = Article.objects.all()
|
|
41
|
+
serializer_class = ArticleRepresentionSerializer
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class DependantArticleModelViewSet(ModelViewSet):
|
|
45
|
+
queryset = DependantArticle.objects.all()
|
|
46
|
+
serializer_class = DependantArticleModelSerializer
|
|
47
|
+
display_config_class = DependantArticleDisplayConfig
|
|
48
|
+
endpoint_config_class = DependantArticleEndpointViewConfig
|
|
49
|
+
|
|
50
|
+
def get_queryset(self) -> QuerySet[DependantArticle]:
|
|
51
|
+
queryset = super().get_queryset()
|
|
52
|
+
if article_id := self.kwargs.get("article_id", None):
|
|
53
|
+
queryset = queryset.filter(article_id=article_id)
|
|
54
|
+
|
|
55
|
+
if used_article_id := self.kwargs.get("used_article_id", None):
|
|
56
|
+
queryset = queryset.filter(dependant_article_id=used_article_id)
|
|
57
|
+
|
|
58
|
+
return queryset.select_related("article", "dependant_article")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ArticleInstancePermissionMixin:
|
|
62
|
+
@cached_property
|
|
63
|
+
def profile(self):
|
|
64
|
+
user = self.request.user
|
|
65
|
+
return user.profile
|
|
66
|
+
|
|
67
|
+
@cached_property
|
|
68
|
+
def instance(self) -> Article | None:
|
|
69
|
+
if "pk" in self.kwargs:
|
|
70
|
+
return self.get_object()
|
|
71
|
+
|
|
72
|
+
@cached_property
|
|
73
|
+
def can_edit_article_author(self) -> bool:
|
|
74
|
+
if self.instance:
|
|
75
|
+
return can_edit_article_author(self.instance, self.request.user)
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
@cached_property
|
|
79
|
+
def can_edit_article_meta_data(self) -> bool:
|
|
80
|
+
if self.instance:
|
|
81
|
+
return can_edit_article_meta_data(self.instance, self.request.user)
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
@cached_property
|
|
85
|
+
def can_edit_article_type(self) -> bool:
|
|
86
|
+
return can_edit_article_type(self.instance, self.request.user)
|
|
87
|
+
|
|
88
|
+
@cached_property
|
|
89
|
+
def can_edit_article_content(self) -> bool:
|
|
90
|
+
if self.instance:
|
|
91
|
+
return can_edit_article_content(self.instance, self.request.user)
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
@cached_property
|
|
95
|
+
def can_administrate_article(self) -> bool:
|
|
96
|
+
if self.instance:
|
|
97
|
+
return can_administrate_article(self.instance, self.request.user)
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class ArticleModelViewSet(ArticleInstancePermissionMixin, CloneMixin, RevisionMixin, ModelViewSet):
|
|
102
|
+
# LIST_DOCUMENTATION = "wbwriter/viewsets/documentation/article.md"
|
|
103
|
+
|
|
104
|
+
serializer_class = ArticleFullModelSerializer
|
|
105
|
+
queryset = Article.objects.all()
|
|
106
|
+
|
|
107
|
+
button_config_class = ArticleModelButtonConfig
|
|
108
|
+
display_config_class = ArticleDisplayConfig
|
|
109
|
+
filterset_class = ArticleFilter
|
|
110
|
+
|
|
111
|
+
search_fields = (
|
|
112
|
+
"tags__title",
|
|
113
|
+
"type__label",
|
|
114
|
+
"type__slug",
|
|
115
|
+
"content",
|
|
116
|
+
"title",
|
|
117
|
+
"name",
|
|
118
|
+
)
|
|
119
|
+
ordering = ("-modified",)
|
|
120
|
+
ordering_fields = (
|
|
121
|
+
"title",
|
|
122
|
+
"status",
|
|
123
|
+
"type",
|
|
124
|
+
"created",
|
|
125
|
+
"modified",
|
|
126
|
+
"id",
|
|
127
|
+
"author",
|
|
128
|
+
"reviewer",
|
|
129
|
+
"peer_reviewer",
|
|
130
|
+
"qa_reviewer",
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
@cached_property
|
|
134
|
+
def content_type(self):
|
|
135
|
+
return ContentType.objects.get(app_label="wbwriter", model="article")
|
|
136
|
+
|
|
137
|
+
def get_queryset(self):
|
|
138
|
+
if self.request.user.has_perm("wbwriter.administrate_article") or self.profile.is_internal:
|
|
139
|
+
qs = Article.objects.all()
|
|
140
|
+
else:
|
|
141
|
+
qs = Article.objects.filter(is_private=False)
|
|
142
|
+
return qs.select_related(
|
|
143
|
+
"type", "feedback_contact", "reviewer", "peer_reviewer", "qa_reviewer", "author"
|
|
144
|
+
).prefetch_related("tags")
|
|
145
|
+
|
|
146
|
+
@action(detail=True, methods=["POST"], authentication_classes=[JWTCookieAuthentication])
|
|
147
|
+
def generate_pdf(self, request, pk):
|
|
148
|
+
"""
|
|
149
|
+
NOTE: JWTCookieAuthentication only works, if the frontend is served under the same URL as the backend due to
|
|
150
|
+
the cookie.
|
|
151
|
+
"""
|
|
152
|
+
article = get_object_or_404(Article, pk=pk)
|
|
153
|
+
|
|
154
|
+
if not can_access_article(article, request.user):
|
|
155
|
+
return Response({}, status=status.HTTP_403_FORBIDDEN)
|
|
156
|
+
|
|
157
|
+
generate_pdf_as_task.delay(pk, user_id=request.user.id)
|
|
158
|
+
return Response(
|
|
159
|
+
{"__notification": {"title": "PDF is going to be created"}},
|
|
160
|
+
status=status.HTTP_200_OK,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
@action(detail=True, methods=["POST"], authentication_classes=[JWTCookieAuthentication])
|
|
164
|
+
def edit(self, request, pk):
|
|
165
|
+
"""Sets the current user to be the author of the specified article."""
|
|
166
|
+
article = get_object_or_404(Article, pk=pk)
|
|
167
|
+
article.author = request.user.profile
|
|
168
|
+
article.save()
|
|
169
|
+
return Response(
|
|
170
|
+
{"__notification": {"title": f'You are now author of "{article.title}".'}},
|
|
171
|
+
status=status.HTTP_200_OK,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
@action(detail=True, methods=["POST"], authentication_classes=[JWTCookieAuthentication])
|
|
175
|
+
def reroll_peer(self, request, pk):
|
|
176
|
+
"""Rolls for a new peer reviewer."""
|
|
177
|
+
article = get_object_or_404(Article, pk=pk)
|
|
178
|
+
article.reroll_peer()
|
|
179
|
+
article.save()
|
|
180
|
+
return Response(
|
|
181
|
+
{"__notification": {"title": f'The new peer reviewer is "{article.peer_reviewer}".'}},
|
|
182
|
+
status=status.HTTP_200_OK,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
@action(detail=True, methods=["POST"], authentication_classes=[JWTCookieAuthentication])
|
|
186
|
+
def reroll_qa(self, request, pk):
|
|
187
|
+
"""Rolls for a new QA reviewer."""
|
|
188
|
+
article = get_object_or_404(Article, pk=pk)
|
|
189
|
+
article.reroll_qa()
|
|
190
|
+
article.save()
|
|
191
|
+
return Response(
|
|
192
|
+
{"__notification": {"title": f'The new QA reviewer is "{article.qa_reviewer}".'}},
|
|
193
|
+
status=status.HTTP_200_OK,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
@action(detail=True, methods=["POST"], authentication_classes=[JWTCookieAuthentication])
|
|
197
|
+
def reroll_peer_and_qa(self, request, pk):
|
|
198
|
+
"""Rolls for a new peer and a new QA reviewer."""
|
|
199
|
+
article = get_object_or_404(Article, pk=pk)
|
|
200
|
+
article.reroll_peer_and_qa()
|
|
201
|
+
article.save()
|
|
202
|
+
return Response(
|
|
203
|
+
{"__notification": {"title": f'You are now author of "{article.title}".'}},
|
|
204
|
+
status=status.HTTP_200_OK,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
@action(detail=True, methods=["POST"], authentication_classes=[JWTCookieAuthentication])
|
|
208
|
+
def assign_new_author(self, request, pk):
|
|
209
|
+
"""Assigns a new author."""
|
|
210
|
+
article = get_object_or_404(Article, pk=pk)
|
|
211
|
+
author_id = request.POST.get("author")
|
|
212
|
+
author = get_object_or_404(Person, pk=author_id)
|
|
213
|
+
article.author = author
|
|
214
|
+
article.save()
|
|
215
|
+
return Response(
|
|
216
|
+
{"__notification": {"title": f"The new author is {author}."}},
|
|
217
|
+
status=status.HTTP_200_OK,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class ReviewerArticleModelViewSet(ArticleModelViewSet):
|
|
222
|
+
# LIST_DOCUMENTATION = "wbwriter/viewsets/documentation/article.md"
|
|
223
|
+
|
|
224
|
+
# serializer_class = ArticleModelSerializer
|
|
225
|
+
queryset = Article.objects.all()
|
|
226
|
+
title_config_class = ReviewerArticleTitleConfig
|
|
227
|
+
|
|
228
|
+
button_config_class = ArticleModelButtonConfig
|
|
229
|
+
display_config_class = ArticleDisplayConfig
|
|
230
|
+
endpoint_config_class = ReviewerArticleModelEndpointConfig
|
|
231
|
+
|
|
232
|
+
search_fields = (
|
|
233
|
+
"tags__title",
|
|
234
|
+
"type__label",
|
|
235
|
+
"type__slug",
|
|
236
|
+
"content",
|
|
237
|
+
"title",
|
|
238
|
+
"name",
|
|
239
|
+
)
|
|
240
|
+
filter_fields = {
|
|
241
|
+
"title": ["icontains"],
|
|
242
|
+
"status": ["exact"],
|
|
243
|
+
"tags": ["exact"],
|
|
244
|
+
"type": ["exact", "icontains"],
|
|
245
|
+
"author": ["exact"],
|
|
246
|
+
"reviewer": ["exact"],
|
|
247
|
+
"peer_reviewer": ["exact"],
|
|
248
|
+
"qa_reviewer": ["exact"],
|
|
249
|
+
"created": ["exact", "lt", "lte", "gt", "gte"],
|
|
250
|
+
"modified": ["exact", "lt", "lte", "gt", "gte"],
|
|
251
|
+
}
|
|
252
|
+
ordering = ("-modified",)
|
|
253
|
+
ordering_fields = ("title", "status", "type", "created", "modified")
|
|
254
|
+
|
|
255
|
+
def get_queryset(self):
|
|
256
|
+
return Article.objects.filter(
|
|
257
|
+
Q(reviewer=self.profile, status=Article.Status.FEEDBACK)
|
|
258
|
+
| Q(peer_reviewer=self.profile, status=Article.Status.PEER_REVIEW)
|
|
259
|
+
| Q(qa_reviewer=self.profile, status=Article.Status.QA_REVIEW)
|
|
260
|
+
| Q(author=self.profile, status=Article.Status.AUTHOR_APPROVAL)
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
def get_serializer_class(self):
|
|
264
|
+
# TODO: Create a unique endpoint for creating instances so that we
|
|
265
|
+
# can assign a "new instance" to have better control over what
|
|
266
|
+
# serializer we want to use.
|
|
267
|
+
# if self.kwargs.get("pk"):
|
|
268
|
+
# return ArticleFullModelSerializer
|
|
269
|
+
# return ArticleModelSerializer
|
|
270
|
+
return ArticleFullModelSerializer
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from wbcore.viewsets import ModelViewSet, RepresentationViewSet
|
|
2
|
+
from wbwriter.models import ArticleType
|
|
3
|
+
from wbwriter.serializers import (
|
|
4
|
+
ArticleTypeModelSerializer,
|
|
5
|
+
ArticleTypeRepresentationSerializer,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
from .display import ArticleTypeDisplayConfig
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ArticleTypeModelViewSet(ModelViewSet):
|
|
12
|
+
ENDPOINT = "wbwriter:article-type-list"
|
|
13
|
+
|
|
14
|
+
serializer_class = ArticleTypeModelSerializer
|
|
15
|
+
# queryset = ArticleType.objects.all()
|
|
16
|
+
|
|
17
|
+
display_config_class = ArticleTypeDisplayConfig
|
|
18
|
+
|
|
19
|
+
search_fields = ("label", "slug")
|
|
20
|
+
filter_fields = {"label": ["icontains", "exact"], "slug": ["icontains", "exact"]}
|
|
21
|
+
ordering_fields = ("label",)
|
|
22
|
+
|
|
23
|
+
def get_queryset(self):
|
|
24
|
+
request = self.request
|
|
25
|
+
user = request.user
|
|
26
|
+
profile = user.profile
|
|
27
|
+
|
|
28
|
+
if user.is_superuser or profile.is_internal:
|
|
29
|
+
return ArticleType.objects.all()
|
|
30
|
+
# TODO: What to do with customers?
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ArticleTypeRepresentationViewSet(RepresentationViewSet):
|
|
34
|
+
serializer_class = ArticleTypeRepresentationSerializer
|
|
35
|
+
queryset = ArticleType.objects.all()
|
|
36
|
+
|
|
37
|
+
search_fields = ("label", "slug")
|
|
38
|
+
filter_fields = {"label": ["icontains", "exact"], "slug": ["icontains", "exact"]}
|
|
39
|
+
ordering_fields = ("label",)
|
|
40
|
+
|
|
41
|
+
def get_queryset(self):
|
|
42
|
+
request = self.request
|
|
43
|
+
user = request.user
|
|
44
|
+
profile = user.profile
|
|
45
|
+
|
|
46
|
+
if user.is_superuser or profile.is_internal:
|
|
47
|
+
return ArticleType.objects.all()
|
|
48
|
+
|
|
49
|
+
return ArticleType.objects.none()
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from wbcore import serializers
|
|
2
|
+
from wbcore.contrib.directory.models import Person
|
|
3
|
+
from wbcore.contrib.directory.serializers import (
|
|
4
|
+
InternalUserProfileRepresentationSerializer,
|
|
5
|
+
)
|
|
6
|
+
from wbcore.contrib.icons import WBIcon
|
|
7
|
+
from wbcore.metadata.configs import buttons as bt
|
|
8
|
+
from wbcore.metadata.configs.buttons.view_config import ButtonViewConfig
|
|
9
|
+
from wbcore.metadata.configs.display.instance_display.shortcuts import (
|
|
10
|
+
create_simple_display,
|
|
11
|
+
)
|
|
12
|
+
from wbwriter.models.article import Article
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ArticleModelButtonConfig(ButtonViewConfig):
|
|
16
|
+
def get_custom_list_instance_buttons(self) -> set:
|
|
17
|
+
return {bt.HyperlinkButton(key="preview", label="Preview PDF", icon=WBIcon.VIEW.icon)}
|
|
18
|
+
|
|
19
|
+
def get_custom_instance_buttons(self):
|
|
20
|
+
class AssignAuthorSerializer(serializers.Serializer):
|
|
21
|
+
author = serializers.PrimaryKeyRelatedField(queryset=Person.objects.filter_only_internal())
|
|
22
|
+
_author = InternalUserProfileRepresentationSerializer(source="author")
|
|
23
|
+
|
|
24
|
+
custom_instance_buttons = {
|
|
25
|
+
bt.ActionButton(key="generate_pdf", label="Generate PDF", icon=WBIcon.DOCUMENT.icon),
|
|
26
|
+
list(self.get_custom_list_instance_buttons())[0],
|
|
27
|
+
bt.WidgetButton(key="publications", label="Publications", icon=WBIcon.NOTEBOOK.icon),
|
|
28
|
+
bt.ActionButton(key="reroll_peer", label="Assign new peer reviewer", icon=WBIcon.REFRESH.icon),
|
|
29
|
+
bt.ActionButton(key="reroll_qa", label="Assign new QA reviewer", icon=WBIcon.REFRESH.icon),
|
|
30
|
+
bt.ActionButton(
|
|
31
|
+
key="reroll_peer_and_qa", label="Assign new peer and QA reviewers", icon=WBIcon.REFRESH.icon
|
|
32
|
+
),
|
|
33
|
+
bt.ActionButton(
|
|
34
|
+
key="assign_new_author",
|
|
35
|
+
label="Assign another author",
|
|
36
|
+
icon=WBIcon.PEOPLE.icon,
|
|
37
|
+
serializer=AssignAuthorSerializer,
|
|
38
|
+
instance_display=create_simple_display([["author"]]),
|
|
39
|
+
),
|
|
40
|
+
}
|
|
41
|
+
# Make sure that the "Edit" button only shows up on Status "draft" and
|
|
42
|
+
# only if the article has no author assigned, and is of Type "DDQ".
|
|
43
|
+
if self.view.kwargs.get("pk", None):
|
|
44
|
+
instance = self.view.get_object()
|
|
45
|
+
if (
|
|
46
|
+
instance.status == Article.Status.DRAFT
|
|
47
|
+
and instance.type
|
|
48
|
+
and instance.type.slug == "ddq"
|
|
49
|
+
and not instance.author
|
|
50
|
+
):
|
|
51
|
+
custom_instance_buttons.add(bt.ActionButton(key="edit", label="Edit", icon=WBIcon.EDIT.icon))
|
|
52
|
+
|
|
53
|
+
return custom_instance_buttons
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class PublicationButtonConfig(ButtonViewConfig):
|
|
57
|
+
def get_custom_instance_buttons(self):
|
|
58
|
+
return {bt.HyperlinkButton(key="pdf_file", label="PDF", icon=WBIcon.NOTEBOOK.icon)}
|
|
59
|
+
|
|
60
|
+
def get_custom_list_instance_buttons(self):
|
|
61
|
+
return self.get_custom_instance_buttons()
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
from .article import ArticleDisplayConfig, DependantArticleDisplayConfig
|
|
2
|
+
from .article_type import ArticleTypeDisplayConfig
|
|
3
|
+
from .in_editor_template import InEditorTemplateDisplayConfig
|
|
4
|
+
from .meta_information import MetaInformationDisplayConfig
|
|
5
|
+
from .meta_information_instance import MetaInformationInstanceDisplayConfig
|
|
6
|
+
from .publication import PublicationDisplayConfig, PublicationParserDisplayConfig
|