wbwriter 1.54.17__tar.gz → 1.59.4__tar.gz
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-1.54.17 → wbwriter-1.59.4}/PKG-INFO +1 -1
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/admin.py +1 -3
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/factories/article.py +1 -0
- wbwriter-1.59.4/wbwriter/migrations/0013_articletype_allow_empty_author_and_more.py +23 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/models/article.py +36 -74
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/models/article_type.py +3 -1
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/models/in_editor_template.py +1 -1
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/models/meta_information.py +2 -2
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/models/publication_models.py +4 -1
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/pdf_generator.py +2 -2
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/publication_parser.py +3 -4
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/serializers/article.py +3 -7
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/tests/signals.py +1 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/tests/test_filter.py +1 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/tests/test_model.py +2 -1
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/tests/test_writer.py +1 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/urls.py +0 -1
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/buttons.py +1 -1
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/menu.py +0 -9
- {wbwriter-1.54.17 → wbwriter-1.59.4}/.gitignore +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/pyproject.toml +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/__init__.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/apps.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/dynamic_preferences_registry.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/factories/__init__.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/factories/meta_information.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/filters/__init__.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/filters/article.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/filters/metainformationinstance.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/locale/de/LC_MESSAGES/django.mo +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/locale/de/LC_MESSAGES/django.po +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/locale/en/LC_MESSAGES/django.mo +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/locale/en/LC_MESSAGES/django.po +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/locale/fr/LC_MESSAGES/django.mo +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/locale/fr/LC_MESSAGES/django.po +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/migrations/0001_initial_squashed_squashed_0008_alter_article_author_alter_article_feedback_contact_and_more.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/migrations/0009_dependantarticle.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/migrations/0010_alter_article_options.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/migrations/0011_auto_20240103_0953.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/migrations/0012_i18n.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/migrations/__init__.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/models/__init__.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/models/block.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/models/block_parameter.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/models/mixins.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/models/style.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/models/template.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/serializers/__init__.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/serializers/article_type.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/serializers/in_editor_template.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/serializers/meta_information.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/serializers/publication.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/templates/wbwriter/article_pdf.html +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/templates/wbwriter/templatetags/cite_article.html +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/templates/wbwriter/templatetags/table_of_contents.html +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/templates/wbwriter/templatetags/table_of_contents_articles.html +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/templatetags/__init__.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/templatetags/writer.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/tests/__init__.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/tests/conftest.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/tests/tests.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/typings.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/__init__.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/article.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/article_type.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/display/__init__.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/display/article.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/display/article_type.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/display/in_editor_template.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/display/meta_information.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/display/meta_information_instance.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/display/publication.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/documentation/article.md +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/endpoints/__init__.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/endpoints/article.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/endpoints/meta_information.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/in_editor_template.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/meta_information.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/meta_information_instance.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/publication.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/titles/__init__.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/titles/publication_title_config.py +0 -0
- {wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/titles/reviewer_article_title_config.py +0 -0
|
@@ -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):
|
|
@@ -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
|
+
]
|
|
@@ -39,7 +39,7 @@ from weasyprint import CSS
|
|
|
39
39
|
|
|
40
40
|
from wbwriter.models.publication_models import Publication
|
|
41
41
|
from wbwriter.pdf_generator import PdfGenerator
|
|
42
|
-
from wbwriter.publication_parser import
|
|
42
|
+
from wbwriter.publication_parser import ParserValidationError
|
|
43
43
|
from wbwriter.typings import ArticleDTO
|
|
44
44
|
|
|
45
45
|
from .mixins import PublishableMixin
|
|
@@ -167,7 +167,7 @@ class DependantArticle(WBModel):
|
|
|
167
167
|
)
|
|
168
168
|
|
|
169
169
|
@classmethod
|
|
170
|
-
def get_endpoint_basename(
|
|
170
|
+
def get_endpoint_basename(cls):
|
|
171
171
|
return "wbwriter:dependantarticle"
|
|
172
172
|
|
|
173
173
|
@classmethod
|
|
@@ -770,16 +770,10 @@ class Article(CloneMixin, TagModelMixin, PublishableMixin, WBModel):
|
|
|
770
770
|
generate_publications.delay(self.id)
|
|
771
771
|
|
|
772
772
|
def can_publish(self) -> dict[str, str]:
|
|
773
|
-
"""Allow only one-off and deep-dive articles to be published."""
|
|
774
773
|
errors = dict()
|
|
775
774
|
if self.status not in [self.Status.APPROVED, self.Status.PUBLISHED]:
|
|
776
775
|
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
|
-
]:
|
|
776
|
+
if not self.type or not self.type.can_be_published:
|
|
783
777
|
errors["type"] = [gettext_lazy("unvalid type for publication")]
|
|
784
778
|
# We ensure the article's parser can be published
|
|
785
779
|
if article_type := self.type:
|
|
@@ -789,7 +783,7 @@ class Article(CloneMixin, TagModelMixin, PublishableMixin, WBModel):
|
|
|
789
783
|
self._build_dto(),
|
|
790
784
|
date.today(),
|
|
791
785
|
).is_valid()
|
|
792
|
-
except
|
|
786
|
+
except ParserValidationError as e:
|
|
793
787
|
errors["non_field_errors"] = e.errors
|
|
794
788
|
except ModuleNotFoundError:
|
|
795
789
|
errors["non_field_errors"] = [gettext_lazy("invalid parser")]
|
|
@@ -811,20 +805,6 @@ class Article(CloneMixin, TagModelMixin, PublishableMixin, WBModel):
|
|
|
811
805
|
# we delete existing publication in case the user unpublish it
|
|
812
806
|
Publication.objects.filter(object_id=self.id, content_type=ContentType.objects.get_for_model(self)).delete()
|
|
813
807
|
|
|
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
808
|
@transition(
|
|
829
809
|
status,
|
|
830
810
|
source=[Status.APPROVED],
|
|
@@ -867,10 +847,6 @@ class Article(CloneMixin, TagModelMixin, PublishableMixin, WBModel):
|
|
|
867
847
|
if not self.qa_reviewer:
|
|
868
848
|
self.qa_reviewer = self.roll_qa()
|
|
869
849
|
|
|
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
850
|
create_dependencies(self)
|
|
875
851
|
super().save(*args, **kwargs)
|
|
876
852
|
|
|
@@ -1048,58 +1024,44 @@ class Article(CloneMixin, TagModelMixin, PublishableMixin, WBModel):
|
|
|
1048
1024
|
styles = []
|
|
1049
1025
|
pdf_content = None
|
|
1050
1026
|
if not self.content or "sections" not in self.content:
|
|
1051
|
-
|
|
1052
|
-
{"content": "<h1>There seems to be no content to render.</h1>"}
|
|
1053
|
-
)
|
|
1027
|
+
template_context = {"content": "<h1>There seems to be no content to render.</h1>"}
|
|
1054
1028
|
else:
|
|
1055
1029
|
content = self.content["sections"][self.content["sectionOrder"][0]]["content"]["content"]
|
|
1056
1030
|
resolved_content = resolve_content(content, extensions=["sane_lists"], article=self)
|
|
1057
1031
|
template_context = template.Context({"content": resolved_content})
|
|
1058
1032
|
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)
|
|
1033
|
+
parsers = self.type.parsers.all()
|
|
1034
|
+
if parsers.exists():
|
|
1035
|
+
parser = parsers.first().parser_class(self._build_dto(), datetime.date.today())
|
|
1036
|
+
if parser.is_valid(raise_exception=False):
|
|
1037
|
+
pdf_content = parser.get_file()
|
|
1038
|
+
else:
|
|
1039
|
+
template_context["content"] = f"""
|
|
1040
|
+
<p>To generate the PDF, please correct the following errors:</p>
|
|
1041
|
+
<ul>
|
|
1042
|
+
{"".join([f"<li>{error}</li>" for error in parser.errors])}
|
|
1043
|
+
</ul>
|
|
1044
|
+
"""
|
|
1045
|
+
elif self.template and self.template.template:
|
|
1046
|
+
html = template.Template(self.template.template).render(template_context)
|
|
1047
|
+
|
|
1048
|
+
styles = [CSS(string=style.style) for style in self.template.styles.all()]
|
|
1049
|
+
if header := self.template.header_template:
|
|
1050
|
+
styles += [CSS(string=style.style) for style in header.styles.all()]
|
|
1051
|
+
if footer := self.template.footer_template:
|
|
1052
|
+
styles += [CSS(string=style.style) for style in footer.styles.all()]
|
|
1053
|
+
|
|
1054
|
+
pdf_content = PdfGenerator(
|
|
1055
|
+
main_html=html,
|
|
1056
|
+
header_html=self.template.header_template.template,
|
|
1057
|
+
footer_html=self.template.footer_template.template,
|
|
1058
|
+
custom_css=styles,
|
|
1059
|
+
side_margin=self.template.side_margin,
|
|
1060
|
+
extra_vertical_margin=self.template.extra_vertical_margin,
|
|
1061
|
+
).render_pdf()
|
|
1100
1062
|
if not pdf_content:
|
|
1101
1063
|
pdf_content = PdfGenerator(
|
|
1102
|
-
main_html=
|
|
1064
|
+
main_html=template.Template(empty_template).render(template_context),
|
|
1103
1065
|
custom_css=styles,
|
|
1104
1066
|
side_margin=0,
|
|
1105
1067
|
extra_vertical_margin=0,
|
|
@@ -1175,7 +1137,7 @@ class Article(CloneMixin, TagModelMixin, PublishableMixin, WBModel):
|
|
|
1175
1137
|
return mapping
|
|
1176
1138
|
|
|
1177
1139
|
@classmethod
|
|
1178
|
-
def get_endpoint_basename(
|
|
1140
|
+
def get_endpoint_basename(cls):
|
|
1179
1141
|
return "wbwriter:article"
|
|
1180
1142
|
|
|
1181
1143
|
@classmethod
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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]:
|
|
@@ -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
|
|
|
@@ -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
|
|
|
@@ -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
|
|
@@ -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()
|
|
@@ -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",
|
|
@@ -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))
|
|
@@ -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",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/templates/wbwriter/templatetags/cite_article.html
RENAMED
|
File without changes
|
{wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/templates/wbwriter/templatetags/table_of_contents.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{wbwriter-1.54.17 → wbwriter-1.59.4}/wbwriter/viewsets/titles/reviewer_article_title_config.py
RENAMED
|
File without changes
|