wbwriter 1.54.23__py2.py3-none-any.whl → 1.59.10__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.
- wbwriter/admin.py +1 -3
- wbwriter/factories/article.py +4 -0
- wbwriter/factories/meta_information.py +1 -0
- wbwriter/migrations/0013_articletype_allow_empty_author_and_more.py +23 -0
- wbwriter/models/article.py +39 -76
- wbwriter/models/article_type.py +3 -1
- wbwriter/models/in_editor_template.py +1 -1
- wbwriter/models/meta_information.py +2 -2
- wbwriter/models/publication_models.py +4 -1
- wbwriter/pdf_generator.py +2 -2
- wbwriter/publication_parser.py +3 -4
- wbwriter/serializers/article.py +3 -7
- wbwriter/tests/signals.py +1 -0
- wbwriter/tests/test_filter.py +1 -0
- wbwriter/tests/test_model.py +2 -1
- wbwriter/tests/test_writer.py +1 -0
- wbwriter/urls.py +0 -1
- wbwriter/viewsets/buttons.py +1 -1
- wbwriter/viewsets/menu.py +0 -9
- {wbwriter-1.54.23.dist-info → wbwriter-1.59.10.dist-info}/METADATA +2 -2
- {wbwriter-1.54.23.dist-info → wbwriter-1.59.10.dist-info}/RECORD +22 -21
- {wbwriter-1.54.23.dist-info → wbwriter-1.59.10.dist-info}/WHEEL +1 -1
wbwriter/admin.py
CHANGED
|
@@ -51,7 +51,7 @@ class MetaInformationInstanceInline(admin.TabularInline):
|
|
|
51
51
|
|
|
52
52
|
@admin.register(ArticleType)
|
|
53
53
|
class ArticleTypeModelAdmin(admin.ModelAdmin):
|
|
54
|
-
list_display = ("id", "label", "slug")
|
|
54
|
+
list_display = ("id", "label", "slug", "can_be_published", "allow_empty_author")
|
|
55
55
|
|
|
56
56
|
ordering = ["slug"]
|
|
57
57
|
search_fields = ["id", "label", "slug"]
|
|
@@ -61,8 +61,6 @@ class ArticleTypeModelAdmin(admin.ModelAdmin):
|
|
|
61
61
|
"qa_reviewers",
|
|
62
62
|
]
|
|
63
63
|
|
|
64
|
-
# prepopulated_fields = {"slug": ("label",)}
|
|
65
|
-
|
|
66
64
|
|
|
67
65
|
@admin.register(Article)
|
|
68
66
|
class ArticleModelAdmin(CompareVersionAdmin, admin.ModelAdmin):
|
wbwriter/factories/article.py
CHANGED
|
@@ -43,6 +43,7 @@ def content() -> dict:
|
|
|
43
43
|
class ArticleFactory(factory.django.DjangoModelFactory):
|
|
44
44
|
class Meta:
|
|
45
45
|
model = Article
|
|
46
|
+
skip_postgeneration_save = True
|
|
46
47
|
|
|
47
48
|
name = factory.Sequence(lambda n: f"article {n}")
|
|
48
49
|
title = factory.Sequence(lambda n: f"title {n}")
|
|
@@ -78,8 +79,10 @@ class DependantArticleFactory(factory.django.DjangoModelFactory):
|
|
|
78
79
|
class ArticleTypeFactory(factory.django.DjangoModelFactory):
|
|
79
80
|
class Meta:
|
|
80
81
|
model = ArticleType
|
|
82
|
+
skip_postgeneration_save = True
|
|
81
83
|
|
|
82
84
|
label = factory.Sequence(lambda n: f"articletype {n}")
|
|
85
|
+
can_be_published = True
|
|
83
86
|
|
|
84
87
|
@factory.post_generation
|
|
85
88
|
def parsers(self, create, extracted, **kwargs):
|
|
@@ -109,6 +112,7 @@ class ArticleTypeFactory(factory.django.DjangoModelFactory):
|
|
|
109
112
|
class TemplateFactory(factory.django.DjangoModelFactory):
|
|
110
113
|
class Meta:
|
|
111
114
|
model = Template
|
|
115
|
+
skip_postgeneration_save = True
|
|
112
116
|
|
|
113
117
|
title = factory.Sequence(lambda n: f"template {n}")
|
|
114
118
|
template = factory.Faker("paragraph")
|
|
@@ -7,6 +7,7 @@ from wbwriter.models import MetaInformation, MetaInformationInstance
|
|
|
7
7
|
class MetaInformationFactory(factory.django.DjangoModelFactory):
|
|
8
8
|
class Meta:
|
|
9
9
|
model = MetaInformation
|
|
10
|
+
skip_postgeneration_save = True
|
|
10
11
|
|
|
11
12
|
name = factory.Sequence(lambda n: f"metainfo {n}")
|
|
12
13
|
key = factory.Sequence(lambda n: f"key {n}")
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Generated by Django 5.0.12 on 2025-12-02 10:19
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('wbwriter', '0012_i18n'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AddField(
|
|
14
|
+
model_name='articletype',
|
|
15
|
+
name='allow_empty_author',
|
|
16
|
+
field=models.BooleanField(default=False),
|
|
17
|
+
),
|
|
18
|
+
migrations.AddField(
|
|
19
|
+
model_name='articletype',
|
|
20
|
+
name='can_be_published',
|
|
21
|
+
field=models.BooleanField(default=True),
|
|
22
|
+
),
|
|
23
|
+
]
|
wbwriter/models/article.py
CHANGED
|
@@ -35,17 +35,18 @@ from wbcore.metadata.configs.display.instance_display.shortcuts import (
|
|
|
35
35
|
)
|
|
36
36
|
from wbcore.models import WBModel
|
|
37
37
|
from wbcore.utils.models import CloneMixin
|
|
38
|
+
from wbcore.workers import Queue
|
|
38
39
|
from weasyprint import CSS
|
|
39
40
|
|
|
40
41
|
from wbwriter.models.publication_models import Publication
|
|
41
42
|
from wbwriter.pdf_generator import PdfGenerator
|
|
42
|
-
from wbwriter.publication_parser import
|
|
43
|
+
from wbwriter.publication_parser import ParserValidationError
|
|
43
44
|
from wbwriter.typings import ArticleDTO
|
|
44
45
|
|
|
45
46
|
from .mixins import PublishableMixin
|
|
46
47
|
|
|
47
48
|
|
|
48
|
-
@shared_task
|
|
49
|
+
@shared_task(queue=Queue.HIGH_PRIORITY.value)
|
|
49
50
|
def generate_publications(article_id):
|
|
50
51
|
with suppress(Article.DoesNotExist):
|
|
51
52
|
article = Article.objects.get(id=article_id)
|
|
@@ -167,7 +168,7 @@ class DependantArticle(WBModel):
|
|
|
167
168
|
)
|
|
168
169
|
|
|
169
170
|
@classmethod
|
|
170
|
-
def get_endpoint_basename(
|
|
171
|
+
def get_endpoint_basename(cls):
|
|
171
172
|
return "wbwriter:dependantarticle"
|
|
172
173
|
|
|
173
174
|
@classmethod
|
|
@@ -770,16 +771,10 @@ class Article(CloneMixin, TagModelMixin, PublishableMixin, WBModel):
|
|
|
770
771
|
generate_publications.delay(self.id)
|
|
771
772
|
|
|
772
773
|
def can_publish(self) -> dict[str, str]:
|
|
773
|
-
"""Allow only one-off and deep-dive articles to be published."""
|
|
774
774
|
errors = dict()
|
|
775
775
|
if self.status not in [self.Status.APPROVED, self.Status.PUBLISHED]:
|
|
776
776
|
errors["status"] = [gettext_lazy("Status needs to be approved in order to allow publication")]
|
|
777
|
-
if not self.type or self.type.
|
|
778
|
-
"one-off-article",
|
|
779
|
-
"deep-dive-article",
|
|
780
|
-
"mid-year-review",
|
|
781
|
-
"year-s-favorites",
|
|
782
|
-
]:
|
|
777
|
+
if not self.type or not self.type.can_be_published:
|
|
783
778
|
errors["type"] = [gettext_lazy("unvalid type for publication")]
|
|
784
779
|
# We ensure the article's parser can be published
|
|
785
780
|
if article_type := self.type:
|
|
@@ -789,7 +784,7 @@ class Article(CloneMixin, TagModelMixin, PublishableMixin, WBModel):
|
|
|
789
784
|
self._build_dto(),
|
|
790
785
|
date.today(),
|
|
791
786
|
).is_valid()
|
|
792
|
-
except
|
|
787
|
+
except ParserValidationError as e:
|
|
793
788
|
errors["non_field_errors"] = e.errors
|
|
794
789
|
except ModuleNotFoundError:
|
|
795
790
|
errors["non_field_errors"] = [gettext_lazy("invalid parser")]
|
|
@@ -811,20 +806,6 @@ class Article(CloneMixin, TagModelMixin, PublishableMixin, WBModel):
|
|
|
811
806
|
# we delete existing publication in case the user unpublish it
|
|
812
807
|
Publication.objects.filter(object_id=self.id, content_type=ContentType.objects.get_for_model(self)).delete()
|
|
813
808
|
|
|
814
|
-
# @transition(
|
|
815
|
-
# status,
|
|
816
|
-
# source=[Status.APPROVED, Status.PUBLISHED],
|
|
817
|
-
# target=Status.DRAFT,
|
|
818
|
-
# permission=can_revise,
|
|
819
|
-
# custom={"_transition_button": revise_button},
|
|
820
|
-
# )
|
|
821
|
-
# def revise(self, by=None):
|
|
822
|
-
# """Move the article back to the draft state."""
|
|
823
|
-
# self.peer_reviewer_approved = False
|
|
824
|
-
# self.peer_reviewer = None
|
|
825
|
-
# if self.type.slug == "ddq":
|
|
826
|
-
# self.author = None
|
|
827
|
-
|
|
828
809
|
@transition(
|
|
829
810
|
status,
|
|
830
811
|
source=[Status.APPROVED],
|
|
@@ -867,10 +848,6 @@ class Article(CloneMixin, TagModelMixin, PublishableMixin, WBModel):
|
|
|
867
848
|
if not self.qa_reviewer:
|
|
868
849
|
self.qa_reviewer = self.roll_qa()
|
|
869
850
|
|
|
870
|
-
# if self.type and self.type.slug == "ddq":
|
|
871
|
-
# with suppress(Person.DoesNotExist):
|
|
872
|
-
# self.qa_reviewer = Person.objects.get(computed_str__icontains="Stefano Rodella")
|
|
873
|
-
|
|
874
851
|
create_dependencies(self)
|
|
875
852
|
super().save(*args, **kwargs)
|
|
876
853
|
|
|
@@ -1048,58 +1025,44 @@ class Article(CloneMixin, TagModelMixin, PublishableMixin, WBModel):
|
|
|
1048
1025
|
styles = []
|
|
1049
1026
|
pdf_content = None
|
|
1050
1027
|
if not self.content or "sections" not in self.content:
|
|
1051
|
-
|
|
1052
|
-
{"content": "<h1>There seems to be no content to render.</h1>"}
|
|
1053
|
-
)
|
|
1028
|
+
template_context = {"content": "<h1>There seems to be no content to render.</h1>"}
|
|
1054
1029
|
else:
|
|
1055
1030
|
content = self.content["sections"][self.content["sectionOrder"][0]]["content"]["content"]
|
|
1056
1031
|
resolved_content = resolve_content(content, extensions=["sane_lists"], article=self)
|
|
1057
1032
|
template_context = template.Context({"content": resolved_content})
|
|
1058
1033
|
if self.type:
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
if parsers.exists():
|
|
1089
|
-
parser = parsers.first().parser_class(self._build_dto(), datetime.date.today())
|
|
1090
|
-
if parser.is_valid(raise_exception=False):
|
|
1091
|
-
pdf_content = parser.get_file()
|
|
1092
|
-
else:
|
|
1093
|
-
template_context["content"] = f"""
|
|
1094
|
-
<p>To generate the PDF, please correct the following errors:</p>
|
|
1095
|
-
<ul>
|
|
1096
|
-
{"".join([f"<li>{error}</li>" for error in parser.errors])}
|
|
1097
|
-
</ul>
|
|
1098
|
-
"""
|
|
1099
|
-
html = template.Template(empty_template).render(template_context)
|
|
1034
|
+
parsers = self.type.parsers.all()
|
|
1035
|
+
if parsers.exists():
|
|
1036
|
+
parser = parsers.first().parser_class(self._build_dto(), datetime.date.today())
|
|
1037
|
+
if parser.is_valid(raise_exception=False):
|
|
1038
|
+
pdf_content = parser.get_file()
|
|
1039
|
+
else:
|
|
1040
|
+
template_context["content"] = f"""
|
|
1041
|
+
<p>To generate the PDF, please correct the following errors:</p>
|
|
1042
|
+
<ul>
|
|
1043
|
+
{"".join([f"<li>{error}</li>" for error in parser.errors])}
|
|
1044
|
+
</ul>
|
|
1045
|
+
"""
|
|
1046
|
+
elif self.template and self.template.template:
|
|
1047
|
+
html = template.Template(self.template.template).render(template_context)
|
|
1048
|
+
|
|
1049
|
+
styles = [CSS(string=style.style) for style in self.template.styles.all()]
|
|
1050
|
+
if header := self.template.header_template:
|
|
1051
|
+
styles += [CSS(string=style.style) for style in header.styles.all()]
|
|
1052
|
+
if footer := self.template.footer_template:
|
|
1053
|
+
styles += [CSS(string=style.style) for style in footer.styles.all()]
|
|
1054
|
+
|
|
1055
|
+
pdf_content = PdfGenerator(
|
|
1056
|
+
main_html=html,
|
|
1057
|
+
header_html=self.template.header_template.template,
|
|
1058
|
+
footer_html=self.template.footer_template.template,
|
|
1059
|
+
custom_css=styles,
|
|
1060
|
+
side_margin=self.template.side_margin,
|
|
1061
|
+
extra_vertical_margin=self.template.extra_vertical_margin,
|
|
1062
|
+
).render_pdf()
|
|
1100
1063
|
if not pdf_content:
|
|
1101
1064
|
pdf_content = PdfGenerator(
|
|
1102
|
-
main_html=
|
|
1065
|
+
main_html=template.Template(empty_template).render(template_context),
|
|
1103
1066
|
custom_css=styles,
|
|
1104
1067
|
side_margin=0,
|
|
1105
1068
|
extra_vertical_margin=0,
|
|
@@ -1175,7 +1138,7 @@ class Article(CloneMixin, TagModelMixin, PublishableMixin, WBModel):
|
|
|
1175
1138
|
return mapping
|
|
1176
1139
|
|
|
1177
1140
|
@classmethod
|
|
1178
|
-
def get_endpoint_basename(
|
|
1141
|
+
def get_endpoint_basename(cls):
|
|
1179
1142
|
return "wbwriter:article"
|
|
1180
1143
|
|
|
1181
1144
|
@classmethod
|
|
@@ -1191,7 +1154,7 @@ class Article(CloneMixin, TagModelMixin, PublishableMixin, WBModel):
|
|
|
1191
1154
|
return "{{ title }}"
|
|
1192
1155
|
|
|
1193
1156
|
|
|
1194
|
-
@shared_task
|
|
1157
|
+
@shared_task(queue=Queue.HIGH_PRIORITY.value)
|
|
1195
1158
|
def generate_pdf_as_task(article_id, user_id=None):
|
|
1196
1159
|
article = Article.objects.get(id=article_id)
|
|
1197
1160
|
user = get_user_model().objects.get(id=user_id) if user_id else None
|
wbwriter/models/article_type.py
CHANGED
|
@@ -30,9 +30,11 @@ class ArticleType(WBModel):
|
|
|
30
30
|
parsers = models.ManyToManyField("wbwriter.PublicationParser", related_name="publication_parsers", blank=True)
|
|
31
31
|
peer_reviewers = models.ManyToManyField("directory.Person", related_name="article_type_peer_reviewers")
|
|
32
32
|
qa_reviewers = models.ManyToManyField("directory.Person", related_name="article_type_qa_reviewers")
|
|
33
|
+
can_be_published = models.BooleanField(default=True)
|
|
34
|
+
allow_empty_author = models.BooleanField(default=False)
|
|
33
35
|
|
|
34
36
|
@classmethod
|
|
35
|
-
def get_endpoint_basename(
|
|
37
|
+
def get_endpoint_basename(cls):
|
|
36
38
|
return "wbwriter:articletyperepresentation"
|
|
37
39
|
|
|
38
40
|
@classmethod
|
|
@@ -24,7 +24,7 @@ class MetaInformation(WBModel):
|
|
|
24
24
|
verbose_name_plural = "Meta Information"
|
|
25
25
|
|
|
26
26
|
@classmethod
|
|
27
|
-
def get_endpoint_basename(
|
|
27
|
+
def get_endpoint_basename(cls):
|
|
28
28
|
return "wbwriter:metainformation"
|
|
29
29
|
|
|
30
30
|
@classmethod
|
|
@@ -60,7 +60,7 @@ class MetaInformationInstance(WBModel):
|
|
|
60
60
|
]
|
|
61
61
|
|
|
62
62
|
@classmethod
|
|
63
|
-
def get_endpoint_basename(
|
|
63
|
+
def get_endpoint_basename(cls):
|
|
64
64
|
return "wbwriter:metainformationinstance"
|
|
65
65
|
|
|
66
66
|
@classmethod
|
|
@@ -77,6 +77,9 @@ class Publication(TagModelMixin, models.Model):
|
|
|
77
77
|
object_id = models.PositiveIntegerField()
|
|
78
78
|
content_object = GenericForeignKey("content_type", "object_id")
|
|
79
79
|
|
|
80
|
+
def __str__(self) -> str:
|
|
81
|
+
return self.title
|
|
82
|
+
|
|
80
83
|
@classmethod
|
|
81
84
|
def create_or_update_from_parser_and_object(cls, parser, generic_object):
|
|
82
85
|
if hasattr(generic_object, "_build_dto") and callable(generic_object._build_dto):
|
|
@@ -91,7 +94,7 @@ class Publication(TagModelMixin, models.Model):
|
|
|
91
94
|
pub.save()
|
|
92
95
|
|
|
93
96
|
@classmethod
|
|
94
|
-
def get_endpoint_basename(
|
|
97
|
+
def get_endpoint_basename(cls):
|
|
95
98
|
return "wbwriter:publication"
|
|
96
99
|
|
|
97
100
|
@classmethod
|
wbwriter/pdf_generator.py
CHANGED
|
@@ -31,7 +31,7 @@ class PdfGenerator:
|
|
|
31
31
|
main_html,
|
|
32
32
|
header_html=None,
|
|
33
33
|
footer_html=None,
|
|
34
|
-
custom_css=
|
|
34
|
+
custom_css=None,
|
|
35
35
|
base_url=None,
|
|
36
36
|
side_margin=2,
|
|
37
37
|
extra_vertical_margin=30,
|
|
@@ -61,7 +61,7 @@ class PdfGenerator:
|
|
|
61
61
|
self.main_html = main_html
|
|
62
62
|
self.header_html = header_html
|
|
63
63
|
self.footer_html = footer_html
|
|
64
|
-
self.custom_css = custom_css
|
|
64
|
+
self.custom_css = custom_css if custom_css else []
|
|
65
65
|
self.base_url = base_url
|
|
66
66
|
self.side_margin = side_margin
|
|
67
67
|
self.extra_vertical_margin = extra_vertical_margin
|
wbwriter/publication_parser.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
|
-
from abc import ABC
|
|
4
3
|
from datetime import date
|
|
5
4
|
from io import BytesIO
|
|
6
5
|
from typing import Iterable
|
|
@@ -17,12 +16,12 @@ from wbwriter.pdf_generator import PdfGenerator
|
|
|
17
16
|
from .typings import ArticleDTO
|
|
18
17
|
|
|
19
18
|
|
|
20
|
-
class
|
|
19
|
+
class ParserValidationError(Exception):
|
|
21
20
|
def __init__(self, *args, errors=None, **kwargs): # real signature unknown
|
|
22
21
|
self.errors = errors
|
|
23
22
|
|
|
24
23
|
|
|
25
|
-
class PublicationParser
|
|
24
|
+
class PublicationParser: # pragma: no cover
|
|
26
25
|
def __init__(self, article: ArticleDTO, published_date: date):
|
|
27
26
|
self.article = article
|
|
28
27
|
self.published_date = published_date
|
|
@@ -64,7 +63,7 @@ class PublicationParser(ABC): # pragma: no cover
|
|
|
64
63
|
self._errors = []
|
|
65
64
|
is_valid = self._is_valid()
|
|
66
65
|
if self.errors and raise_exception:
|
|
67
|
-
raise
|
|
66
|
+
raise ParserValidationError(errors=self.errors)
|
|
68
67
|
return is_valid
|
|
69
68
|
|
|
70
69
|
def _get_additional_information(self) -> dict[str, any]:
|
wbwriter/serializers/article.py
CHANGED
|
@@ -7,6 +7,7 @@ from wbcore.contrib.directory.serializers import (
|
|
|
7
7
|
InternalUserProfileRepresentationSerializer,
|
|
8
8
|
)
|
|
9
9
|
from wbcore.contrib.documents.models import Document
|
|
10
|
+
from wbcore.contrib.i18n.serializers.mixins import ModelTranslateSerializerMixin
|
|
10
11
|
from wbcore.contrib.tags.serializers import TagSerializerMixin
|
|
11
12
|
from wbcore.serializers import (
|
|
12
13
|
HyperlinkField,
|
|
@@ -58,9 +59,6 @@ class DependantArticleModelSerializer(ModelSerializer):
|
|
|
58
59
|
)
|
|
59
60
|
|
|
60
61
|
|
|
61
|
-
from wbcore.contrib.i18n.serializers.mixins import ModelTranslateSerializerMixin
|
|
62
|
-
|
|
63
|
-
|
|
64
62
|
class ArticleModelSerializer(ModelTranslateSerializerMixin, TagSerializerMixin, ModelSerializer):
|
|
65
63
|
"""Serializes a subset of the fields of of the Article model to minimize
|
|
66
64
|
workload on serializing lists of instances.
|
|
@@ -323,15 +321,13 @@ class ArticleFullModelSerializer(ArticleModelSerializer):
|
|
|
323
321
|
if (
|
|
324
322
|
self.instance
|
|
325
323
|
and data.get("type")
|
|
326
|
-
and data.get("type").
|
|
324
|
+
and data.get("type").allow_empty_author
|
|
327
325
|
and (
|
|
328
326
|
("author" in data and not data.get("author"))
|
|
329
327
|
or ("author" not in data and self.instance.author is None)
|
|
330
328
|
)
|
|
331
329
|
):
|
|
332
|
-
raise serializers.ValidationError(
|
|
333
|
-
{"type": 'This must be set to "DDQ" as long as no author has been assigned!'}
|
|
334
|
-
)
|
|
330
|
+
raise serializers.ValidationError({"type": f'The type {data["type"]} does not allow empty author.'})
|
|
335
331
|
|
|
336
332
|
return data
|
|
337
333
|
|
wbwriter/tests/signals.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from django.dispatch import receiver
|
|
2
2
|
from dynamic_preferences.registries import global_preferences_registry
|
|
3
3
|
from wbcore.test.signals import custom_update_kwargs
|
|
4
|
+
|
|
4
5
|
from wbwriter.models import Article
|
|
5
6
|
from wbwriter.viewsets import ArticleModelViewSet, ReviewerArticleModelViewSet
|
|
6
7
|
|
wbwriter/tests/test_filter.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
from rest_framework.test import APIRequestFactory
|
|
3
3
|
from wbcore.contrib.authentication.factories import InternalUserFactory
|
|
4
|
+
|
|
4
5
|
from wbwriter.factories import ArticleFactory, MetaInformationInstanceFactory
|
|
5
6
|
from wbwriter.filters import MetaInformationInstanceFilter
|
|
6
7
|
from wbwriter.models import MetaInformationInstance
|
wbwriter/tests/test_model.py
CHANGED
|
@@ -5,6 +5,7 @@ from django.forms.models import model_to_dict
|
|
|
5
5
|
from django_fsm import has_transition_perm
|
|
6
6
|
from wbcore.contrib.authentication.factories import UserFactory
|
|
7
7
|
from wbcore.contrib.authentication.models import Permission, User
|
|
8
|
+
|
|
8
9
|
from wbwriter.factories import (
|
|
9
10
|
ArticleFactory,
|
|
10
11
|
ArticleTypeFactory,
|
|
@@ -422,7 +423,7 @@ class TestArticlePermissions:
|
|
|
422
423
|
status=Article.Status.APPROVED, type=ArticleTypeFactory(label="Mid Year Review")
|
|
423
424
|
)
|
|
424
425
|
article_d: Article = ArticleFactory(
|
|
425
|
-
status=Article.Status.APPROVED, type=ArticleTypeFactory(label="Unalligned Article")
|
|
426
|
+
status=Article.Status.APPROVED, type=ArticleTypeFactory(can_be_published=False, label="Unalligned Article")
|
|
426
427
|
)
|
|
427
428
|
assert article_a.can_be_published()
|
|
428
429
|
assert article_b.can_be_published()
|
wbwriter/tests/test_writer.py
CHANGED
wbwriter/urls.py
CHANGED
|
@@ -21,7 +21,6 @@ router.register(
|
|
|
21
21
|
|
|
22
22
|
router.register(r"article", viewsets.ArticleModelViewSet, basename="article")
|
|
23
23
|
router.register(r"articlerepresentation", viewsets.ArticleRepresentionViewSet, basename="articlerepresentation")
|
|
24
|
-
# router.register(r"ddq", viewsets.DDQArticleModelViewSet, basename="ddq")
|
|
25
24
|
router.register(r"review-article", viewsets.ReviewerArticleModelViewSet, basename="review-article")
|
|
26
25
|
router.register(
|
|
27
26
|
r"articletyperepresentation",
|
wbwriter/viewsets/buttons.py
CHANGED
|
@@ -46,7 +46,7 @@ class ArticleModelButtonConfig(ButtonViewConfig):
|
|
|
46
46
|
if (
|
|
47
47
|
instance.status == Article.Status.DRAFT
|
|
48
48
|
and instance.type
|
|
49
|
-
and instance.type.
|
|
49
|
+
and instance.type.allow_empty_author
|
|
50
50
|
and not instance.author
|
|
51
51
|
):
|
|
52
52
|
custom_instance_buttons.add(bt.ActionButton(key="edit", label="Edit", icon=WBIcon.EDIT.icon))
|
wbwriter/viewsets/menu.py
CHANGED
|
@@ -15,15 +15,6 @@ WRITER_MENU = Menu(
|
|
|
15
15
|
method=lambda request: is_internal_user(request.user), permissions=["wbwriter.view_article"]
|
|
16
16
|
),
|
|
17
17
|
),
|
|
18
|
-
# MenuItem(
|
|
19
|
-
# label="DDQ",
|
|
20
|
-
# endpoint="wbwriter:ddq-list",
|
|
21
|
-
# add=MenuItem(
|
|
22
|
-
# label="Create a DDQ related entry",
|
|
23
|
-
# endpoint="wbwriter:ddq-list",
|
|
24
|
-
# ),
|
|
25
|
-
# permission=ItemPermission(permissions=["wbwriter.view_article"]),
|
|
26
|
-
# ),
|
|
27
18
|
MenuItem(
|
|
28
19
|
label="Articles to review",
|
|
29
20
|
endpoint="wbwriter:review-article-list",
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
wbwriter/__init__.py,sha256=J-j-u0itpEFT6irdmWmixQqYMadNl1X91TxUmoiLHMI,22
|
|
2
|
-
wbwriter/admin.py,sha256=
|
|
2
|
+
wbwriter/admin.py,sha256=LhlE-voJ0DnxGCaiy2u2NoKfNrFZZ35iX8xV68BVw0U,3618
|
|
3
3
|
wbwriter/apps.py,sha256=UdEgUQGJaw3f6fkHlbL4kNC4CyksLOBL7ZpDBJmqD_U,91
|
|
4
4
|
wbwriter/dynamic_preferences_registry.py,sha256=yt3PJCuTv5TpHM2hQsTR-aNllRY20_pyo-wcS38DruU,594
|
|
5
|
-
wbwriter/pdf_generator.py,sha256=
|
|
6
|
-
wbwriter/publication_parser.py,sha256=
|
|
5
|
+
wbwriter/pdf_generator.py,sha256=ji1R5zjmrANIDMQ1bIosmfXwndlr5zkzejXDCbSFGHo,6664
|
|
6
|
+
wbwriter/publication_parser.py,sha256=aDZ5bO0a3LFxYbDRg59siZHuSvYrlYDPwq38vjSoLa4,9325
|
|
7
7
|
wbwriter/typings.py,sha256=-SsobxnS4-hCOL_44n-QcXXv1FuhZsQAwLj9BybtEVU,501
|
|
8
|
-
wbwriter/urls.py,sha256=
|
|
8
|
+
wbwriter/urls.py,sha256=VioMfCC1BJQ-7ODficPJ9hgybNdD5g2KLE7_qzHTt8E,2556
|
|
9
9
|
wbwriter/factories/__init__.py,sha256=NrHTM4vUVdLASCeSAYkklmBbtP2B5kQ5gwlZey5lp0o,364
|
|
10
|
-
wbwriter/factories/article.py,sha256=
|
|
11
|
-
wbwriter/factories/meta_information.py,sha256=
|
|
10
|
+
wbwriter/factories/article.py,sha256=extyBsRn_LdxaR4nAISzOAj8SeDeb76m0ya3GPKKCX8,5933
|
|
11
|
+
wbwriter/factories/meta_information.py,sha256=FbvbwrnrOYQuCI-0HYDQLC9ir3qk3ZZ4mG7PoPyzcLw,964
|
|
12
12
|
wbwriter/filters/__init__.py,sha256=2OTkcXa8veIWN-v_WV5mHvGuw3tIuJzh9qD2zaFrA4M,102
|
|
13
13
|
wbwriter/filters/article.py,sha256=c7YyAVf2XVwWf4Llghu0TtHcDweA4ATXms1nyTclW1A,1577
|
|
14
14
|
wbwriter/filters/metainformationinstance.py,sha256=gacS7nxEFrosP7AApXebZ73n4r7ST-ghsQzxIFS8UAA,857
|
|
@@ -23,20 +23,21 @@ wbwriter/migrations/0009_dependantarticle.py,sha256=OCxWFj68ToPelJJ_Hlrt3PXZy56z
|
|
|
23
23
|
wbwriter/migrations/0010_alter_article_options.py,sha256=S2CVb5wVXHpwok3gehyedMrZMVbwj75m6LkrVg5dhv8,520
|
|
24
24
|
wbwriter/migrations/0011_auto_20240103_0953.py,sha256=UnaHFJM7DVOSEfS0wm8rWEh9H2_wDcmKPYFRJYIx1Gs,1321
|
|
25
25
|
wbwriter/migrations/0012_i18n.py,sha256=24cClx1sWYh-VEo-IApTiSr5nNvV1CtINiaWRHFztMA,724
|
|
26
|
+
wbwriter/migrations/0013_articletype_allow_empty_author_and_more.py,sha256=stQ8frRr1_5fL7Y61i_s_UTyMrbbZT2dheohjymxwpA,560
|
|
26
27
|
wbwriter/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
28
|
wbwriter/models/__init__.py,sha256=opUUL36nRzxJypQNupPM8NxUj-u4uuB11IDn5XSkjVE,248
|
|
28
|
-
wbwriter/models/article.py,sha256=
|
|
29
|
-
wbwriter/models/article_type.py,sha256=
|
|
29
|
+
wbwriter/models/article.py,sha256=mTPXwEnzuy_KWaHzk7sRr9_WccT_j32madZfrOS9inE,44038
|
|
30
|
+
wbwriter/models/article_type.py,sha256=EwkZ0MQOaAXBWzoSOqJByWluPYyKe8n8uz0KKiYwPQg,2115
|
|
30
31
|
wbwriter/models/block.py,sha256=tn2sklo7s401Y0Ww5M09Y9Wv-yivEvAR1eM5raG7gtc,835
|
|
31
32
|
wbwriter/models/block_parameter.py,sha256=bHBp9bo5EEiZKTzhYfBwkidcS6kZVy81vtcAlx7xzck,524
|
|
32
|
-
wbwriter/models/in_editor_template.py,sha256=
|
|
33
|
-
wbwriter/models/meta_information.py,sha256=
|
|
33
|
+
wbwriter/models/in_editor_template.py,sha256=Z8UAzhkBWCyQDwwUDCbwlGfWBqs-vqKTWdJjvHYNq4c,3410
|
|
34
|
+
wbwriter/models/meta_information.py,sha256=yRhocySE1pH5vaaEcyHF0uAeAxUvGpjzE1_-PCVRnk8,3018
|
|
34
35
|
wbwriter/models/mixins.py,sha256=-1P0bQCS76H2D8Mj1Bwhzv3KsNwnkts9f75lGUrcxFU,249
|
|
35
|
-
wbwriter/models/publication_models.py,sha256=
|
|
36
|
+
wbwriter/models/publication_models.py,sha256=4VhN01k8HhGJkHToN8DBhpf2PQNDtbLJtH9dKk9kLrs,5349
|
|
36
37
|
wbwriter/models/style.py,sha256=b-CnU9K5D6adePdz4LpCdwXjK2750V3QnFv39wK-JLA,262
|
|
37
38
|
wbwriter/models/template.py,sha256=i6b_qmkUJIbrFqNaeld3gSjR7GLeU-EfKJiPsfjFA_Y,928
|
|
38
39
|
wbwriter/serializers/__init__.py,sha256=zPzGMOsxp8yEZ5m2sATEeqlZaktNWbXCx8oZefeiCa4,828
|
|
39
|
-
wbwriter/serializers/article.py,sha256=
|
|
40
|
+
wbwriter/serializers/article.py,sha256=M5X-WQkbUY0hBw3aQM4EkpqDs4z7TSHWxpiUGsg7bzY,13705
|
|
40
41
|
wbwriter/serializers/article_type.py,sha256=VmMVI0dreuX83GtwtzD0n3-tvmNMnbtc2KIqjAB7beI,426
|
|
41
42
|
wbwriter/serializers/in_editor_template.py,sha256=hZ0icXVJtiipFSzY3cF0p6X4_0XaKdeMYwjUhgPoDLI,1032
|
|
42
43
|
wbwriter/serializers/meta_information.py,sha256=zMNU6WYXJVO5L29Depm6FB-FnFMrnAttRI32PmNpt3w,2046
|
|
@@ -49,17 +50,17 @@ wbwriter/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
|
|
|
49
50
|
wbwriter/templatetags/writer.py,sha256=8bhOxG0U23A30KcAWcXJa3lOon_2acPZp49ZJMWAiBM,3051
|
|
50
51
|
wbwriter/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
52
|
wbwriter/tests/conftest.py,sha256=1vehReFFD4YZHYbOeCEFIuufIeC1QmbUV0mS4VmAXKU,982
|
|
52
|
-
wbwriter/tests/signals.py,sha256=
|
|
53
|
-
wbwriter/tests/test_filter.py,sha256=
|
|
54
|
-
wbwriter/tests/test_model.py,sha256=
|
|
55
|
-
wbwriter/tests/test_writer.py,sha256=
|
|
53
|
+
wbwriter/tests/signals.py,sha256=0EwprXt1WaEWbUQxYNamubWv-7t4HIcAD9f-hKgQUss,990
|
|
54
|
+
wbwriter/tests/test_filter.py,sha256=mbMV2nl5pMX4flXgSUMYdQkbi8Gp1Uo_5WkXbJlFCC0,2664
|
|
55
|
+
wbwriter/tests/test_model.py,sha256=c3X2x-xrsBcXtu7XVR6p5yupUAO5PIOwdqROz1FAyCQ,29433
|
|
56
|
+
wbwriter/tests/test_writer.py,sha256=KGGvXKUCbMoJTLUHedFzPp1Wd02iRUu_kWqmRwO6Ko0,1585
|
|
56
57
|
wbwriter/tests/tests.py,sha256=9quRn44Xi1XIKKIXKCBRmtSpARJowjm7_Edw40Mutdw,332
|
|
57
58
|
wbwriter/viewsets/__init__.py,sha256=FSoHcwxYukis44flBOwKaoa6fABkNI66lzaTXaYEZxk,722
|
|
58
59
|
wbwriter/viewsets/article.py,sha256=w5hig6Z3DulGbVp3RtPax0GHEEDLqw0n4LpS0BzYq60,9960
|
|
59
60
|
wbwriter/viewsets/article_type.py,sha256=wEeJXy3uPav0ZrV3sD1UHEpKdH37ZjwssKeNfaJJV4s,1503
|
|
60
|
-
wbwriter/viewsets/buttons.py,sha256=
|
|
61
|
+
wbwriter/viewsets/buttons.py,sha256=NR8gWk71eZC8mav76TXWzBtxglUw1_waSWncopFBPIA,2849
|
|
61
62
|
wbwriter/viewsets/in_editor_template.py,sha256=6ocFTerOYKrAoVTXKI8yEaDInGVpgiyI0tl1E4otusY,1713
|
|
62
|
-
wbwriter/viewsets/menu.py,sha256=
|
|
63
|
+
wbwriter/viewsets/menu.py,sha256=sSBZ5iLnH4t2kIEsV8-s2h0-VYvIbQOwY8Y-qUmrY3A,1141
|
|
63
64
|
wbwriter/viewsets/meta_information.py,sha256=W4Vi4BYCGUEZ9ncRY-Unj5jq3AqCMl27gafYD589VbQ,1588
|
|
64
65
|
wbwriter/viewsets/meta_information_instance.py,sha256=G03z5wNSg-04GA3gF1fejP0EPJiBGIHFhJsMzInBab4,1695
|
|
65
66
|
wbwriter/viewsets/publication.py,sha256=5z8Hbjt8iTBQ87H4WrJvDFVVWNWefrS0MIIzcgAiCyc,4033
|
|
@@ -77,6 +78,6 @@ wbwriter/viewsets/endpoints/meta_information.py,sha256=m7rVbfTNjsQNibmqtAG4DvXSJ
|
|
|
77
78
|
wbwriter/viewsets/titles/__init__.py,sha256=INLTvGeBi5Xro7i3FlJ2fesh-TjKbxRBlfsAyeg3h7k,131
|
|
78
79
|
wbwriter/viewsets/titles/publication_title_config.py,sha256=-39C_Enz8J1o-DWgRz89VFMV0r4MReKx-r-Tpf9MlVM,807
|
|
79
80
|
wbwriter/viewsets/titles/reviewer_article_title_config.py,sha256=YzRjuUwbe6vBwWhYLrCe6woGCpQ9c7AwkujKqPh5mLw,183
|
|
80
|
-
wbwriter-1.
|
|
81
|
-
wbwriter-1.
|
|
82
|
-
wbwriter-1.
|
|
81
|
+
wbwriter-1.59.10.dist-info/METADATA,sha256=wbqKvEReq3CtV1vX-SohfFaC2gmRoKw8fDyCqb3Lp_k,168
|
|
82
|
+
wbwriter-1.59.10.dist-info/WHEEL,sha256=aha0VrrYvgDJ3Xxl3db_g_MDIW-ZexDdrc_m-Hk8YY4,105
|
|
83
|
+
wbwriter-1.59.10.dist-info/RECORD,,
|