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.

Files changed (70) hide show
  1. wbwriter/__init__.py +1 -0
  2. wbwriter/admin.py +142 -0
  3. wbwriter/apps.py +5 -0
  4. wbwriter/dynamic_preferences_registry.py +15 -0
  5. wbwriter/factories/__init__.py +13 -0
  6. wbwriter/factories/article.py +181 -0
  7. wbwriter/factories/meta_information.py +29 -0
  8. wbwriter/filters/__init__.py +2 -0
  9. wbwriter/filters/article.py +47 -0
  10. wbwriter/filters/metainformationinstance.py +24 -0
  11. wbwriter/migrations/0001_initial_squashed_squashed_0008_alter_article_author_alter_article_feedback_contact_and_more.py +653 -0
  12. wbwriter/migrations/0009_dependantarticle.py +41 -0
  13. wbwriter/migrations/0010_alter_article_options.py +20 -0
  14. wbwriter/migrations/0011_auto_20240103_0953.py +39 -0
  15. wbwriter/migrations/__init__.py +0 -0
  16. wbwriter/models/__init__.py +9 -0
  17. wbwriter/models/article.py +1179 -0
  18. wbwriter/models/article_type.py +59 -0
  19. wbwriter/models/block.py +24 -0
  20. wbwriter/models/block_parameter.py +19 -0
  21. wbwriter/models/in_editor_template.py +102 -0
  22. wbwriter/models/meta_information.py +87 -0
  23. wbwriter/models/mixins.py +9 -0
  24. wbwriter/models/publication_models.py +170 -0
  25. wbwriter/models/style.py +13 -0
  26. wbwriter/models/template.py +34 -0
  27. wbwriter/pdf_generator.py +172 -0
  28. wbwriter/publication_parser.py +258 -0
  29. wbwriter/serializers/__init__.py +28 -0
  30. wbwriter/serializers/article.py +359 -0
  31. wbwriter/serializers/article_type.py +14 -0
  32. wbwriter/serializers/in_editor_template.py +37 -0
  33. wbwriter/serializers/meta_information.py +67 -0
  34. wbwriter/serializers/publication.py +82 -0
  35. wbwriter/templatetags/__init__.py +0 -0
  36. wbwriter/templatetags/writer.py +72 -0
  37. wbwriter/tests/__init__.py +0 -0
  38. wbwriter/tests/conftest.py +32 -0
  39. wbwriter/tests/signals.py +23 -0
  40. wbwriter/tests/test_filter.py +58 -0
  41. wbwriter/tests/test_model.py +591 -0
  42. wbwriter/tests/test_writer.py +38 -0
  43. wbwriter/tests/tests.py +18 -0
  44. wbwriter/typings.py +23 -0
  45. wbwriter/urls.py +83 -0
  46. wbwriter/viewsets/__init__.py +22 -0
  47. wbwriter/viewsets/article.py +270 -0
  48. wbwriter/viewsets/article_type.py +49 -0
  49. wbwriter/viewsets/buttons.py +61 -0
  50. wbwriter/viewsets/display/__init__.py +6 -0
  51. wbwriter/viewsets/display/article.py +404 -0
  52. wbwriter/viewsets/display/article_type.py +27 -0
  53. wbwriter/viewsets/display/in_editor_template.py +39 -0
  54. wbwriter/viewsets/display/meta_information.py +37 -0
  55. wbwriter/viewsets/display/meta_information_instance.py +28 -0
  56. wbwriter/viewsets/display/publication.py +55 -0
  57. wbwriter/viewsets/endpoints/__init__.py +2 -0
  58. wbwriter/viewsets/endpoints/article.py +12 -0
  59. wbwriter/viewsets/endpoints/meta_information.py +14 -0
  60. wbwriter/viewsets/in_editor_template.py +68 -0
  61. wbwriter/viewsets/menu.py +42 -0
  62. wbwriter/viewsets/meta_information.py +51 -0
  63. wbwriter/viewsets/meta_information_instance.py +48 -0
  64. wbwriter/viewsets/publication.py +117 -0
  65. wbwriter/viewsets/titles/__init__.py +2 -0
  66. wbwriter/viewsets/titles/publication_title_config.py +18 -0
  67. wbwriter/viewsets/titles/reviewer_article_title_config.py +6 -0
  68. wbwriter-2.2.1.dist-info/METADATA +8 -0
  69. wbwriter-2.2.1.dist-info/RECORD +70 -0
  70. wbwriter-2.2.1.dist-info/WHEEL +5 -0
wbwriter/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "1.0.0"
wbwriter/admin.py ADDED
@@ -0,0 +1,142 @@
1
+ from django.contrib import admin
2
+ from reversion_compare.admin import CompareVersionAdmin
3
+ from wbwriter.models import (
4
+ Article,
5
+ ArticleType,
6
+ Block,
7
+ BlockParameter,
8
+ DependantArticle,
9
+ InEditorTemplate,
10
+ MetaInformation,
11
+ MetaInformationInstance,
12
+ Publication,
13
+ PublicationParser,
14
+ Style,
15
+ Template,
16
+ )
17
+
18
+
19
+ class UsesArticleInline(admin.TabularInline):
20
+ verbose_name = "Uses Article"
21
+ fields = ["dependant_article"]
22
+ model = DependantArticle
23
+ fk_name = "article"
24
+ extra = 0
25
+
26
+
27
+ class UsedArticleInline(admin.TabularInline):
28
+ verbose_name = "Used in Article"
29
+ fields = ["article"]
30
+ model = DependantArticle
31
+ fk_name = "dependant_article"
32
+ extra = 0
33
+
34
+
35
+ @admin.register(MetaInformation)
36
+ class MetaInformationModelAdmin(admin.ModelAdmin):
37
+ pass
38
+
39
+
40
+ @admin.register(MetaInformationInstance)
41
+ class MetaInformationInstanceModelAdmin(admin.ModelAdmin):
42
+ pass
43
+
44
+
45
+ class MetaInformationInstanceInline(admin.TabularInline):
46
+ model = MetaInformationInstance
47
+ extra = 0
48
+ list_display = ("meta_information", "boolean_value")
49
+
50
+
51
+ @admin.register(ArticleType)
52
+ class ArticleTypeModelAdmin(admin.ModelAdmin):
53
+ list_display = ("id", "label", "slug")
54
+
55
+ ordering = ["slug"]
56
+ search_fields = ["id", "label", "slug"]
57
+
58
+ autocomplete_fields = [
59
+ "peer_reviewers",
60
+ "qa_reviewers",
61
+ ]
62
+
63
+ # prepopulated_fields = {"slug": ("label",)}
64
+
65
+
66
+ @admin.register(Article)
67
+ class ArticleModelAdmin(CompareVersionAdmin, admin.ModelAdmin):
68
+ list_display = ("id", "title", "slug")
69
+
70
+ autocomplete_fields = [
71
+ "author",
72
+ "reviewer",
73
+ "peer_reviewer",
74
+ "qa_reviewer",
75
+ ]
76
+
77
+ inlines = [UsesArticleInline, UsedArticleInline, MetaInformationInstanceInline]
78
+
79
+ def reversion_register(self, model, **options):
80
+ options = {
81
+ # Foreign keys that will be observed.
82
+ # "follow": ("author", "reviewer", "peer_reviewer", "qa_reviewer"),
83
+ # Fields to ignore when creating versions
84
+ "exclude": ("created", "modified", "published"),
85
+ "ignore_duplicates": True,
86
+ }
87
+ super().reversion_register(model, **options)
88
+
89
+
90
+ @admin.register(Style)
91
+ class StyleModelAdmin(admin.ModelAdmin):
92
+ list_display = ("id", "title")
93
+
94
+
95
+ @admin.register(InEditorTemplate)
96
+ class InEditorTemplateModelAdmin(admin.ModelAdmin):
97
+ list_display = ("uuid", "title", "is_stand_alone_template")
98
+
99
+ ordering = ["uuid", "title", "is_stand_alone_template"]
100
+ search_fields = ["uuid", "title", "description"]
101
+
102
+
103
+ @admin.register(Template)
104
+ class TemplateModelAdmin(admin.ModelAdmin):
105
+ list_display = ("id", "title")
106
+
107
+
108
+ class BlockParameterInline(admin.StackedInline):
109
+ model = BlockParameter
110
+ extra = 0
111
+
112
+
113
+ @admin.register(Block)
114
+ class BlockModelAdmin(admin.ModelAdmin):
115
+ list_display = ("title",)
116
+
117
+ inlines = (BlockParameterInline,)
118
+
119
+
120
+ def rerender_publication(modeladmin, request, queryset):
121
+ for pub in queryset:
122
+ Publication.create_or_update_from_parser_and_object(pub.parser, pub.content_object)
123
+
124
+
125
+ @admin.register(Publication)
126
+ class PublicationModelAdmin(CompareVersionAdmin, admin.ModelAdmin):
127
+ list_display = ("id", "title", "modified", "created")
128
+ list_filter = ("created", "modified")
129
+
130
+ ordering = ["title", "author", "created", "modified"]
131
+ search_fields = ["title", "author__computed_str"]
132
+
133
+ actions = [rerender_publication]
134
+
135
+
136
+ @admin.register(PublicationParser)
137
+ class PublicationParserModelAdmin(admin.ModelAdmin):
138
+ list_display = ("title",)
139
+ list_filter = ("title",)
140
+
141
+ ordering = ["title", "parser_path"]
142
+ search_fields = ["title", "slug"]
wbwriter/apps.py ADDED
@@ -0,0 +1,5 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class WbwriterConfig(AppConfig):
5
+ name = "wbwriter"
@@ -0,0 +1,15 @@
1
+ from dynamic_preferences.preferences import Section
2
+ from dynamic_preferences.registries import global_preferences_registry
3
+ from dynamic_preferences.types import IntegerPreference
4
+
5
+ general = Section("wbwriter")
6
+
7
+
8
+ @global_preferences_registry.register
9
+ class ReviewerRollDaysRangePreference(IntegerPreference):
10
+ section = general
11
+ name = "reviewer_roll_days_range"
12
+ default = 31
13
+
14
+ verbose_name = "Reviewer roll days range"
15
+ help_text = "The number of days that the reviewer-picking-algorithm looks into the past to count the reviews a member of the reviewer groups has been given."
@@ -0,0 +1,13 @@
1
+ from .article import (
2
+ AbstractPublicationFactory,
3
+ ArticleFactory,
4
+ ArticleTypeFactory,
5
+ BlockFactory,
6
+ BlockParameterFactory,
7
+ InEditorTemplateFactory,
8
+ PublicationFactory,
9
+ PublicationParserFactory,
10
+ TemplateFactory,
11
+ DependantArticleFactory,
12
+ )
13
+ from .meta_information import MetaInformationFactory, MetaInformationInstanceFactory
@@ -0,0 +1,181 @@
1
+ import random
2
+
3
+ import factory
4
+ from django.contrib.contenttypes.models import ContentType
5
+ from wbcore.contrib.authentication.factories.users import InternalUserFactory
6
+ from wbcore.contrib.directory.factories.entries import PersonFactory
7
+ from wbwriter.models import (
8
+ Article,
9
+ ArticleType,
10
+ Block,
11
+ BlockParameter,
12
+ DependantArticle,
13
+ InEditorTemplate,
14
+ Publication,
15
+ PublicationParser,
16
+ Template,
17
+ )
18
+ from wbwriter.publication_parser import PublicationParser as PublicationParserClass
19
+
20
+
21
+ class Parser(PublicationParserClass):
22
+ def is_valid(self):
23
+ return True
24
+
25
+
26
+ def content() -> dict:
27
+ return {
28
+ "sectionOrder": ["section-1"], # A list of section keys that will be used in the `sections` dict.
29
+ "sections": {
30
+ "section-1": {
31
+ "id": "section-1",
32
+ "templateID": None, # `null` means no InEditorTemplate instance, otherwise the ID of the instance.
33
+ "configuration": None, # `null` means no config. This is otherwise a dict: dict[str, Any]
34
+ "content": { # This is a dict: dict[str, str]. The actual content depends on the chosen template and user input.
35
+ "content": "lorem ipsum dolor...",
36
+ },
37
+ }
38
+ },
39
+ }
40
+
41
+
42
+ class ArticleFactory(factory.django.DjangoModelFactory):
43
+ class Meta:
44
+ model = Article
45
+
46
+ name = factory.Sequence(lambda n: f"article {n}")
47
+ title = factory.Sequence(lambda n: f"title {n}")
48
+ content = content()
49
+ type = factory.SubFactory("wbwriter.factories.article.ArticleTypeFactory")
50
+ author = factory.LazyAttribute(lambda o: InternalUserFactory.create().profile)
51
+ feedback_contact = factory.LazyAttribute(lambda o: InternalUserFactory.create().profile)
52
+ reviewer = factory.SubFactory("wbcore.contrib.directory.factories.entries.PersonFactory")
53
+ peer_reviewer = factory.LazyAttribute(lambda o: InternalUserFactory.create().profile)
54
+ peer_reviewer_approved = True
55
+ qa_reviewer = factory.LazyAttribute(lambda o: InternalUserFactory.create().profile)
56
+ template = factory.SubFactory("wbwriter.factories.article.TemplateFactory")
57
+ status = Article.Status.DRAFT
58
+
59
+ @factory.post_generation
60
+ def tags(self, create, extracted, **kwargs):
61
+ if not create:
62
+ return
63
+
64
+ if extracted:
65
+ for tag in extracted:
66
+ self.tags.add(tag)
67
+
68
+
69
+ class DependantArticleFactory(factory.django.DjangoModelFactory):
70
+ class Meta:
71
+ model = DependantArticle
72
+
73
+ article = factory.SubFactory(ArticleFactory)
74
+ dependant_article = factory.SubFactory(ArticleFactory)
75
+
76
+
77
+ class ArticleTypeFactory(factory.django.DjangoModelFactory):
78
+ class Meta:
79
+ model = ArticleType
80
+
81
+ label = factory.Sequence(lambda n: f"articletype {n}")
82
+
83
+ @factory.post_generation
84
+ def parsers(self, create, extracted, **kwargs):
85
+ self.parsers.add(PublicationParserFactory.create())
86
+
87
+ @factory.post_generation
88
+ def peer_reviewers(self, create, extracted, **kwargs):
89
+ if not create:
90
+ return
91
+
92
+ self.peer_reviewers.add(PersonFactory())
93
+ if extracted:
94
+ for peer in extracted:
95
+ self.peer_reviewers.add(peer)
96
+
97
+ @factory.post_generation
98
+ def qa_reviewers(self, create, extracted, **kwargs):
99
+ if not create:
100
+ return
101
+
102
+ self.qa_reviewers.add(PersonFactory())
103
+ if extracted:
104
+ for peer in extracted:
105
+ self.qa_reviewers.add(peer)
106
+
107
+
108
+ class TemplateFactory(factory.django.DjangoModelFactory):
109
+ class Meta:
110
+ model = Template
111
+
112
+ title = factory.Sequence(lambda n: f"template {n}")
113
+ template = factory.Faker("paragraph")
114
+
115
+ @factory.post_generation
116
+ def styles(self, create, extracted, **kwargs):
117
+ if not create:
118
+ return
119
+
120
+ if extracted:
121
+ for style in extracted:
122
+ self.styles.add(style)
123
+
124
+
125
+ class BlockFactory(factory.django.DjangoModelFactory):
126
+ class Meta:
127
+ model = Block
128
+
129
+ title = factory.Sequence(lambda n: f"block {n}")
130
+ html = factory.Faker("paragraph")
131
+
132
+
133
+ class BlockParameterFactory(factory.django.DjangoModelFactory):
134
+ class Meta:
135
+ model = BlockParameter
136
+
137
+ block = factory.SubFactory(BlockFactory)
138
+ title = factory.Sequence(lambda n: f"blockparameter {n}")
139
+ order = random.randint(1, 9999)
140
+
141
+
142
+ class InEditorTemplateFactory(factory.django.DjangoModelFactory):
143
+ class Meta:
144
+ model = InEditorTemplate
145
+
146
+ uuid = factory.Sequence(lambda n: f"uid {n}")
147
+ title = factory.Sequence(lambda n: f"title {n}")
148
+ description = factory.Faker("paragraph")
149
+ style = factory.Faker("paragraph")
150
+ template = factory.Faker("paragraph")
151
+
152
+
153
+ class AbstractPublicationFactory(factory.django.DjangoModelFactory):
154
+ object_id = factory.SelfAttribute("content_object.id")
155
+ content_type = factory.LazyAttribute(lambda o: ContentType.objects.get_for_model(o.content_object))
156
+
157
+ class Meta:
158
+ exclude = ["content_object"]
159
+ abstract = True
160
+
161
+
162
+ class PublicationParserFactory(factory.django.DjangoModelFactory):
163
+ class Meta:
164
+ model = PublicationParser
165
+ django_get_or_create = ("parser_path",)
166
+
167
+ title = factory.Sequence(lambda n: f"publicationparser {n}")
168
+ parser_path = "wbwriter.factories.article"
169
+
170
+
171
+ class PublicationFactory(AbstractPublicationFactory):
172
+ class Meta:
173
+ model = Publication
174
+
175
+ title = factory.Sequence(lambda n: f"publication {n}")
176
+ # author = factory.SubFactory("wbcore.contrib.directory.factories.PersonFactory")
177
+ content = factory.Faker("paragraph")
178
+ parser = factory.SubFactory(PublicationParserFactory)
179
+ content_object = factory.SubFactory(ArticleFactory)
180
+ description = factory.Faker("paragraph")
181
+ target = factory.Faker("pystr")
@@ -0,0 +1,29 @@
1
+ import factory
2
+ from wbwriter.factories.article import ArticleTypeFactory
3
+ from wbwriter.models import MetaInformation, MetaInformationInstance
4
+
5
+
6
+ class MetaInformationFactory(factory.django.DjangoModelFactory):
7
+ class Meta:
8
+ model = MetaInformation
9
+
10
+ name = factory.Sequence(lambda n: f"metainfo {n}")
11
+ key = factory.Sequence(lambda n: f"key {n}")
12
+
13
+ @factory.post_generation
14
+ def article_type(self, create, extracted, **kwargs):
15
+ if not create:
16
+ return
17
+
18
+ self.article_type.add(ArticleTypeFactory())
19
+ if extracted:
20
+ for _type in extracted:
21
+ self.article_type.add(_type)
22
+
23
+
24
+ class MetaInformationInstanceFactory(factory.django.DjangoModelFactory):
25
+ class Meta:
26
+ model = MetaInformationInstance
27
+
28
+ article = factory.SubFactory("wbwriter.factories.article.ArticleFactory")
29
+ meta_information = factory.SubFactory(MetaInformationFactory)
@@ -0,0 +1,2 @@
1
+ from .article import ArticleFilter
2
+ from .metainformationinstance import MetaInformationInstanceFilter
@@ -0,0 +1,47 @@
1
+ import django_filters
2
+ from django.db.models import Q
3
+ from wbcore import filters
4
+ from wbwriter.models import Article, ArticleType
5
+
6
+
7
+ class ArticleFilter(filters.FilterSet):
8
+ show_only_involved_articles = filters.BooleanFilter(
9
+ default=True,
10
+ label="Show only my articles",
11
+ method="get_my_articles",
12
+ )
13
+ type = filters.ModelMultipleChoiceFilter(
14
+ label="Types",
15
+ queryset=ArticleType.objects.all(),
16
+ endpoint=ArticleType.get_representation_endpoint(),
17
+ value_key=ArticleType.get_representation_value_key(),
18
+ label_key=ArticleType.get_representation_label_key(),
19
+ )
20
+
21
+ def get_my_articles(self, queryset, name, value):
22
+ if value:
23
+ request = self.request
24
+ user = request.user
25
+ profile = user.profile
26
+ return queryset.filter(
27
+ Q(author=profile) | Q(reviewer=profile) | Q(peer_reviewer=profile) | Q(qa_reviewer=profile)
28
+ )
29
+ return queryset
30
+
31
+ status = filters.MultipleChoiceFilter(
32
+ label="Status", choices=Article.Status.choices, widget=django_filters.widgets.CSVWidget
33
+ )
34
+
35
+ class Meta:
36
+ model = Article
37
+ fields = {
38
+ "title": ["icontains"],
39
+ "tags": ["exact"],
40
+ "author": ["exact"],
41
+ "reviewer": ["exact"],
42
+ "peer_reviewer": ["exact"],
43
+ "qa_reviewer": ["exact"],
44
+ "created": ["exact", "lt", "lte", "gt", "gte"],
45
+ "modified": ["exact", "lt", "lte", "gt", "gte"],
46
+ "is_private": ["exact"],
47
+ }
@@ -0,0 +1,24 @@
1
+ from wbcore import filters as wb_filters
2
+ from wbwriter.models.meta_information import MetaInformationInstance
3
+
4
+
5
+ class MetaInformationInstanceFilter(wb_filters.FilterSet):
6
+ article = wb_filters.ModelChoiceFilter(
7
+ label="Article",
8
+ queryset=MetaInformationInstance.objects.all(),
9
+ endpoint=MetaInformationInstance.get_representation_endpoint(),
10
+ value_key=MetaInformationInstance.get_representation_value_key(),
11
+ label_key=MetaInformationInstance.get_representation_label_key(),
12
+ method="filter_for_article",
13
+ )
14
+
15
+ def filter_for_article(self, queryset, name, value):
16
+ if value:
17
+ return MetaInformationInstance.objects.filter(article__id=value)
18
+ return queryset
19
+
20
+ class Meta:
21
+ model = MetaInformationInstance
22
+ fields = {
23
+ "article": ["exact"],
24
+ }