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 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):
@@ -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
+ ]
@@ -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 ParserValidationException
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(self):
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.slug not in [
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 ParserValidationException as e:
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
- html = template.Template(empty_template).render(
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
- if "ddq" in self.type.slug:
1060
- # TODO: Replace self.content with the raw_contents equivalent.
1061
-
1062
- # If there is a template defined, then render that one and use its styles.
1063
- if self.template and self.template.template:
1064
- html = template.Template(self.template.template).render(template_context)
1065
-
1066
- styles = [CSS(string=style.style) for style in self.template.styles.all()]
1067
- if header := self.template.header_template:
1068
- styles += [CSS(string=style.style) for style in header.styles.all()]
1069
- if footer := self.template.footer_template:
1070
- styles += [CSS(string=style.style) for style in footer.styles.all()]
1071
-
1072
- pdf_content = PdfGenerator(
1073
- main_html=html,
1074
- header_html=self.template.header_template.template,
1075
- footer_html=self.template.footer_template.template,
1076
- custom_css=styles,
1077
- side_margin=self.template.side_margin,
1078
- extra_vertical_margin=self.template.extra_vertical_margin,
1079
- ).render_pdf()
1080
-
1081
- elif (
1082
- "one-off" in self.type.slug
1083
- or "deep-dive" in self.type.slug
1084
- or "mid-year" in self.type.slug
1085
- or "year-s-favorites" in self.type.slug
1086
- ):
1087
- parsers = self.type.parsers.all()
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=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(self):
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
@@ -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(self):
37
+ def get_endpoint_basename(cls):
36
38
  return "wbwriter:articletyperepresentation"
37
39
 
38
40
  @classmethod
@@ -86,7 +86,7 @@ class InEditorTemplate(WBModel):
86
86
  return self.title
87
87
 
88
88
  @classmethod
89
- def get_endpoint_basename(self):
89
+ def get_endpoint_basename(cls):
90
90
  return "wbwriter:in-editor-template"
91
91
 
92
92
  @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(self):
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(self):
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(self):
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
@@ -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 ParserValidationException(Exception):
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(ABC): # pragma: no cover
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 ParserValidationException(errors=self.errors)
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").slug != "ddq"
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
 
@@ -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()
@@ -1,4 +1,5 @@
1
1
  import pytest
2
+
2
3
  from wbwriter.templatetags.writer import cite_article, table_of_contents
3
4
 
4
5
 
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",
@@ -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.slug == "ddq"
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wbwriter
3
- Version: 1.54.23
3
+ Version: 1.59.10
4
4
  Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
5
- Requires-Dist: reportlab==3.*
5
+ Requires-Dist: pillow==12.*
6
6
  Requires-Dist: wbcore
@@ -1,14 +1,14 @@
1
1
  wbwriter/__init__.py,sha256=J-j-u0itpEFT6irdmWmixQqYMadNl1X91TxUmoiLHMI,22
2
- wbwriter/admin.py,sha256=rnIiwWBB5I8QUYGSsLMrPn9weBqLt10eXhIYEFoPP9w,3626
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=AYjcVaCnrV2D4lmCSOJUGCPq4wqH9fPFJoCDkNUV0j4,6640
6
- wbwriter/publication_parser.py,sha256=rGFX7_wwZt8NDXQYH1GYUChdQ05AAuT8f6Ct5Y5ndCw,9358
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=gUVYDOuPjX15apIC-N1caJnL6DfOYHwjCnZkIpNI3W4,2631
8
+ wbwriter/urls.py,sha256=VioMfCC1BJQ-7ODficPJ9hgybNdD5g2KLE7_qzHTt8E,2556
9
9
  wbwriter/factories/__init__.py,sha256=NrHTM4vUVdLASCeSAYkklmBbtP2B5kQ5gwlZey5lp0o,364
10
- wbwriter/factories/article.py,sha256=Ugk2nz99_BP4E3FgvVdNjglkalx3dakU42oULxPQJN4,5785
11
- wbwriter/factories/meta_information.py,sha256=DrjYxvkQ9F7dY6mwbT7Xqg24_7Ge8FdiYegHpPMLFCM,924
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=l5iSkD6uHTlYP94DyMlKnMABjZQZSNoR7-KelC6GH4c,45487
29
- wbwriter/models/article_type.py,sha256=hofB200Y11LFanpICNgWYDa5-Qg-K4X2fXv6qccBP_0,1999
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=1LqnxkAw9Iyh8EmjWnf3O3TUmneng0Zp4OTFpGgSphg,3411
33
- wbwriter/models/meta_information.py,sha256=8Ipozrv8ndsBUZKIAc-Qcg57xr5IMpjfoSG-DbL_0qQ,3020
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=k6lmyEux7GegtSmBn-X7xXfrFZRASkahXxYt9S1EFuA,5293
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=bBtRghL3zZIE0i9gratSk2Yj9vy9fkLfsxd43kyWUIA,13752
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=ay1kQIaHT7a_kymGimdq1MWj_8UjNqlYW-oyN13WK6c,989
53
- wbwriter/tests/test_filter.py,sha256=dhXiMsjSkwsTdsHMZt2cUt3QzCECvKgN3te5DLee5qM,2663
54
- wbwriter/tests/test_model.py,sha256=kTz68WYfR65x8G9UCXbRRswwQVIPNCQFHRLso-XEd_k,29408
55
- wbwriter/tests/test_writer.py,sha256=XRmfaH-o4ItekFjw1dnIyMzw7veTCw6KH_PouQgFRRQ,1584
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=6ClY6ddqiPheulgz2j_kw80YqjMgXBt1G4_sSiYE-Tg,2844
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=SE-y6PIeamISF_DOJVCYtTKbTVTZQs0Hk5aSrBYib-Q,1472
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.54.23.dist-info/METADATA,sha256=DXZYD-zfxrv0bu_wfythvEJhXo8oXpaEHt4qXiBwvGU,170
81
- wbwriter-1.54.23.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
82
- wbwriter-1.54.23.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any