wbwriter 2.2.1__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.

Files changed (75) hide show
  1. wbwriter-2.2.1/.gitignore +181 -0
  2. wbwriter-2.2.1/PKG-INFO +8 -0
  3. wbwriter-2.2.1/pyproject.toml +32 -0
  4. wbwriter-2.2.1/wbwriter/__init__.py +1 -0
  5. wbwriter-2.2.1/wbwriter/admin.py +142 -0
  6. wbwriter-2.2.1/wbwriter/apps.py +5 -0
  7. wbwriter-2.2.1/wbwriter/dynamic_preferences_registry.py +15 -0
  8. wbwriter-2.2.1/wbwriter/factories/__init__.py +13 -0
  9. wbwriter-2.2.1/wbwriter/factories/article.py +181 -0
  10. wbwriter-2.2.1/wbwriter/factories/meta_information.py +29 -0
  11. wbwriter-2.2.1/wbwriter/filters/__init__.py +2 -0
  12. wbwriter-2.2.1/wbwriter/filters/article.py +47 -0
  13. wbwriter-2.2.1/wbwriter/filters/metainformationinstance.py +24 -0
  14. wbwriter-2.2.1/wbwriter/migrations/0001_initial_squashed_squashed_0008_alter_article_author_alter_article_feedback_contact_and_more.py +653 -0
  15. wbwriter-2.2.1/wbwriter/migrations/0009_dependantarticle.py +41 -0
  16. wbwriter-2.2.1/wbwriter/migrations/0010_alter_article_options.py +20 -0
  17. wbwriter-2.2.1/wbwriter/migrations/0011_auto_20240103_0953.py +39 -0
  18. wbwriter-2.2.1/wbwriter/migrations/__init__.py +0 -0
  19. wbwriter-2.2.1/wbwriter/models/__init__.py +9 -0
  20. wbwriter-2.2.1/wbwriter/models/article.py +1179 -0
  21. wbwriter-2.2.1/wbwriter/models/article_type.py +59 -0
  22. wbwriter-2.2.1/wbwriter/models/block.py +24 -0
  23. wbwriter-2.2.1/wbwriter/models/block_parameter.py +19 -0
  24. wbwriter-2.2.1/wbwriter/models/in_editor_template.py +102 -0
  25. wbwriter-2.2.1/wbwriter/models/meta_information.py +87 -0
  26. wbwriter-2.2.1/wbwriter/models/mixins.py +9 -0
  27. wbwriter-2.2.1/wbwriter/models/publication_models.py +170 -0
  28. wbwriter-2.2.1/wbwriter/models/style.py +13 -0
  29. wbwriter-2.2.1/wbwriter/models/template.py +34 -0
  30. wbwriter-2.2.1/wbwriter/pdf_generator.py +172 -0
  31. wbwriter-2.2.1/wbwriter/publication_parser.py +258 -0
  32. wbwriter-2.2.1/wbwriter/serializers/__init__.py +28 -0
  33. wbwriter-2.2.1/wbwriter/serializers/article.py +359 -0
  34. wbwriter-2.2.1/wbwriter/serializers/article_type.py +14 -0
  35. wbwriter-2.2.1/wbwriter/serializers/in_editor_template.py +37 -0
  36. wbwriter-2.2.1/wbwriter/serializers/meta_information.py +67 -0
  37. wbwriter-2.2.1/wbwriter/serializers/publication.py +82 -0
  38. wbwriter-2.2.1/wbwriter/templates/wbwriter/article_pdf.html +5 -0
  39. wbwriter-2.2.1/wbwriter/templates/wbwriter/templatetags/cite_article.html +1 -0
  40. wbwriter-2.2.1/wbwriter/templates/wbwriter/templatetags/table_of_contents.html +12 -0
  41. wbwriter-2.2.1/wbwriter/templates/wbwriter/templatetags/table_of_contents_articles.html +8 -0
  42. wbwriter-2.2.1/wbwriter/templatetags/__init__.py +0 -0
  43. wbwriter-2.2.1/wbwriter/templatetags/writer.py +72 -0
  44. wbwriter-2.2.1/wbwriter/tests/__init__.py +0 -0
  45. wbwriter-2.2.1/wbwriter/tests/conftest.py +32 -0
  46. wbwriter-2.2.1/wbwriter/tests/signals.py +23 -0
  47. wbwriter-2.2.1/wbwriter/tests/test_filter.py +58 -0
  48. wbwriter-2.2.1/wbwriter/tests/test_model.py +591 -0
  49. wbwriter-2.2.1/wbwriter/tests/test_writer.py +38 -0
  50. wbwriter-2.2.1/wbwriter/tests/tests.py +18 -0
  51. wbwriter-2.2.1/wbwriter/typings.py +23 -0
  52. wbwriter-2.2.1/wbwriter/urls.py +83 -0
  53. wbwriter-2.2.1/wbwriter/viewsets/__init__.py +22 -0
  54. wbwriter-2.2.1/wbwriter/viewsets/article.py +270 -0
  55. wbwriter-2.2.1/wbwriter/viewsets/article_type.py +49 -0
  56. wbwriter-2.2.1/wbwriter/viewsets/buttons.py +61 -0
  57. wbwriter-2.2.1/wbwriter/viewsets/display/__init__.py +6 -0
  58. wbwriter-2.2.1/wbwriter/viewsets/display/article.py +404 -0
  59. wbwriter-2.2.1/wbwriter/viewsets/display/article_type.py +27 -0
  60. wbwriter-2.2.1/wbwriter/viewsets/display/in_editor_template.py +39 -0
  61. wbwriter-2.2.1/wbwriter/viewsets/display/meta_information.py +37 -0
  62. wbwriter-2.2.1/wbwriter/viewsets/display/meta_information_instance.py +28 -0
  63. wbwriter-2.2.1/wbwriter/viewsets/display/publication.py +55 -0
  64. wbwriter-2.2.1/wbwriter/viewsets/documentation/article.md +1 -0
  65. wbwriter-2.2.1/wbwriter/viewsets/endpoints/__init__.py +2 -0
  66. wbwriter-2.2.1/wbwriter/viewsets/endpoints/article.py +12 -0
  67. wbwriter-2.2.1/wbwriter/viewsets/endpoints/meta_information.py +14 -0
  68. wbwriter-2.2.1/wbwriter/viewsets/in_editor_template.py +68 -0
  69. wbwriter-2.2.1/wbwriter/viewsets/menu.py +42 -0
  70. wbwriter-2.2.1/wbwriter/viewsets/meta_information.py +51 -0
  71. wbwriter-2.2.1/wbwriter/viewsets/meta_information_instance.py +48 -0
  72. wbwriter-2.2.1/wbwriter/viewsets/publication.py +117 -0
  73. wbwriter-2.2.1/wbwriter/viewsets/titles/__init__.py +2 -0
  74. wbwriter-2.2.1/wbwriter/viewsets/titles/publication_title_config.py +18 -0
  75. wbwriter-2.2.1/wbwriter/viewsets/titles/reviewer_article_title_config.py +6 -0
@@ -0,0 +1,181 @@
1
+ ~
2
+
3
+ # Docker volumes
4
+ volumes/
5
+ # Poetry auth file
6
+ auth.toml
7
+
8
+ media/*
9
+ media/
10
+ mediafiles/
11
+ mediafiles/*
12
+ test/*
13
+ staticfiles/*
14
+ staticfiles/
15
+ #
16
+ # Byte-compiled / optimized / DLL files
17
+ __pycache__/
18
+ *.py[cod]
19
+ *$py.class
20
+
21
+ # C extensions
22
+ *.so
23
+
24
+ # Distribution / packaging
25
+ .Python
26
+ build/
27
+ develop-eggs/
28
+ dist/
29
+ info/
30
+ downloads/
31
+ eggs/
32
+ .eggs/
33
+ lib/
34
+ lib64/
35
+ parts/
36
+ sdist/
37
+ var/
38
+ wheels/
39
+ share/python-wheels/
40
+ *.egg-info/
41
+ .installed.cfg
42
+ *.egg
43
+ MANIFEST
44
+
45
+ # PyInstaller
46
+ # Usually these files are written by a python script from a template
47
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
48
+ *.manifest
49
+ *.spec
50
+
51
+ # Installer logs
52
+ pip-log.txt
53
+ pip-delete-this-directory.txt
54
+
55
+ # Unit test / coverage reports
56
+ htmlcov/
57
+ .tox/
58
+ .nox/
59
+ .coverage
60
+ .coverage.*
61
+ .cache
62
+ .dccache
63
+ nosetests.xml
64
+ coverage.xml
65
+ *.cover
66
+ *.py,cover
67
+ .hypothesis/
68
+ .pytest_cache/
69
+ cover/
70
+ report.xml
71
+ */report.xml
72
+
73
+ # Translations
74
+ *.mo
75
+ *.pot
76
+
77
+ # Django stuff:
78
+ *.log
79
+ local_settings.py
80
+ *.sqlite3
81
+ db.sqlite3-journal
82
+
83
+ # Flask stuff:
84
+ instance/
85
+ .webassets-cache
86
+
87
+ # Scrapy stuff:
88
+ .scrapy
89
+
90
+ # Sphinx documentation
91
+ docs/_build/
92
+
93
+ # PyBuilder
94
+ .pybuilder/
95
+ target/
96
+
97
+ # Jupyter Notebook
98
+ .ipynb_checkpoints
99
+ *.ipynb
100
+ # IPython
101
+ profile_default/
102
+ ipython_config.py
103
+
104
+ # pyenv
105
+ # For a library or package, you might want to ignore these files since the code is
106
+ # intended to run in multiple environments; otherwise, check them in:
107
+ # .python-version
108
+
109
+ # pipenv
110
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
111
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
112
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
113
+ # install all needed dependencies.
114
+ #Pipfile.lock
115
+
116
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
117
+ __pypackages__/
118
+
119
+ # Celery stuff
120
+ celerybeat-schedule
121
+ celerybeat.pid
122
+
123
+ # SageMath parsed files
124
+ *.sage.py
125
+
126
+ # Environments
127
+ .env
128
+ .envrc
129
+ .venv
130
+ env/
131
+ venv/
132
+ ENV/
133
+ env.bak/
134
+ venv.bak/
135
+ .vscode/
136
+ .idea/
137
+ .idea.bkp/
138
+
139
+ # Spyder project settings
140
+ .spyderproject
141
+ .spyproject
142
+
143
+ # Rope project settings
144
+ .ropeproject
145
+
146
+ # mkdocs documentation
147
+ /site
148
+
149
+ # mypy
150
+ .mypy_cache/
151
+ .dmypy.json
152
+ dmypy.json
153
+
154
+ # Pyre type checker
155
+ .pyre/
156
+
157
+ # pytype static type analyzer
158
+ .pytype/
159
+ crm/
160
+ # Cython debug symbols
161
+ cython_debug/
162
+
163
+ # Gitlab Runner
164
+ builds
165
+ builds/
166
+
167
+ # Integrator Office 365 : reverse proxy tunnel for outlook365
168
+ ngrok
169
+ */ngrok
170
+ /modules/**/system/
171
+
172
+ /modules/wbmailing/files/*
173
+ /modules/wbmailing/mailing/*
174
+
175
+ /projects/*/requirements.txt
176
+ public
177
+
178
+ # Ignore archive localization generated folder
179
+ backend/modules/**/archive/*
180
+ **/**/requirements.txt
181
+ CHANGELOG-*
@@ -0,0 +1,8 @@
1
+ Metadata-Version: 2.3
2
+ Name: wbwriter
3
+ Version: 2.2.1
4
+ Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
5
+ Requires-Dist: pillow==9.1.*
6
+ Requires-Dist: reportlab==3.*
7
+ Requires-Dist: wbcore
8
+ Requires-Dist: weasyprint==57.*
@@ -0,0 +1,32 @@
1
+ [project]
2
+ name = "wbwriter"
3
+ description = ""
4
+ authors = [{ name = "Christopher Wittlinger", email = "c.wittlinger@stainly.com"}]
5
+ dynamic = ["version"]
6
+
7
+ dependencies = [
8
+ "wbcore",
9
+ "pillow == 9.1.*",
10
+ "reportlab == 3.*",
11
+ "weasyprint == 57.*",
12
+ ]
13
+
14
+ [tool.uv.sources]
15
+ wbcore = { workspace = true }
16
+
17
+ [tool.uv]
18
+ package = true
19
+
20
+ [tool.hatch.version]
21
+ path = "../../pyproject.toml"
22
+
23
+ [tool.hatch.build.targets.sdist]
24
+ include = ["wbwriter/*"]
25
+
26
+ [tool.hatch.build.targets.wheel]
27
+ packages = ["wbwriter"]
28
+ only-packages = true
29
+
30
+ [build-system]
31
+ requires = ["hatchling"]
32
+ build-backend = "hatchling.build"
@@ -0,0 +1 @@
1
+ __version__ = "1.0.0"
@@ -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"]
@@ -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
+ }